成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

看完離編寫高性能的JavaScript又近了一步

Nekron / 1847人閱讀

摘要:加上內(nèi)部函數(shù)被返回,被其他對(duì)象引用,形成了閉包,因此對(duì)應(yīng)的變量對(duì)象存在于閉包函數(shù)的作用域鏈中。因此訪問次數(shù)越多,費(fèi)用越高,頁面性能就會(huì)受到很大影響。盡管還主動(dòng)執(zhí)行了一次操作,曲線也沒有下降。

副標(biāo)題:常見的JavaScript內(nèi)存泄露

這是關(guān)于JavaScript內(nèi)存泄露相關(guān)的序列文章中一篇。由于時(shí)間有限更新進(jìn)度會(huì)有點(diǎn)慢,但會(huì)持續(xù)更新的。自己也在學(xué)習(xí)中,難免對(duì)某些知識(shí)點(diǎn)的理解不是很正確,所以才將文章放置github上,一是想與大家分享,二是方便持續(xù)更新,三是便于實(shí)時(shí)修正錯(cuò)誤點(diǎn)。也希望看本文的各位同學(xué)能多提issues,我會(huì)根據(jù)提的意見不斷完善文章。最后希望各位能從文章中有所收獲----->? enjoy reading, enjoy life ?
??最新內(nèi)容請(qǐng)以github上的為準(zhǔn)?? 序列文章鏈接

JavaScript內(nèi)存那點(diǎn)事

常見的JavaScript內(nèi)存泄露

IE<8循環(huán)引用導(dǎo)致的內(nèi)存泄露

內(nèi)存泄露之jQuery.cache

內(nèi)存泄露之Listeners

requestAnimationFrame

什么是內(nèi)存泄露
內(nèi)存泄漏指由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存。內(nèi)存泄漏并非指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,由于設(shè)計(jì)錯(cuò)誤,導(dǎo)致在釋放該段內(nèi)存之前就失去了對(duì)該段內(nèi)存的控制,從而造成了內(nèi)存的浪費(fèi)。內(nèi)存泄漏通常情況下只能由獲得程序源代碼的程序員才能分析出來。然而,有不少人習(xí)慣于把任何不需要的內(nèi)存使用的增加描述為內(nèi)存泄漏,即使嚴(yán)格意義上來說這是不準(zhǔn)確的?!獁ikipedia

??注:下文中標(biāo)注的CG是Chrome瀏覽器中Devtools的【Collect garbage】按鈕縮寫,表示回收垃圾操作。

意外的全局變量

JavaScript對(duì)未聲明變量的處理方式:在全局對(duì)象上創(chuàng)建該變量的引用(即全局對(duì)象上的屬性,不是變量,因?yàn)樗芡ㄟ^delete刪除)。如果在瀏覽器中,全局對(duì)象就是window對(duì)象。

如果未聲明的變量緩存大量的數(shù)據(jù),會(huì)導(dǎo)致這些數(shù)據(jù)只有在窗口關(guān)閉或重新刷新頁面時(shí)才能被釋放。這樣會(huì)造成意外的內(nèi)存泄漏。

function foo(arg) {
    bar = "this is a hidden global variable with a large of data";
}

等同于:

function foo(arg) {
    window.bar = "this is an explicit global variable with a large of data";
}

另外,通過this創(chuàng)建意外的全局變量:

function foo() {
    this.variable = "potential accidental global";
}

// 當(dāng)在全局作用域中調(diào)用foo函數(shù),此時(shí)this指向的是全局對(duì)象(window),而不是"undefined"
foo();
解決方法:

在JavaScript文件中添加"use strict",開啟嚴(yán)格模式,可以有效地避免上述問題。

function foo(arg) {
    "use strict" // 在foo函數(shù)作用域內(nèi)開啟嚴(yán)格模式
    bar = "this is an explicit global variable with a large of data";// 報(bào)錯(cuò):因?yàn)閎ar還沒有被聲明
}

如果需要在一個(gè)函數(shù)中使用全局變量,可以像如下代碼所示,在window上明確聲明:

function foo(arg) {
    window.bar = "this is a explicit global variable with a large of data";
}

這樣不僅可讀性高,而且后期維護(hù)也方便

談到全局變量,需要注意那些用來臨時(shí)存儲(chǔ)大量數(shù)據(jù)的全局變量,確保在處理完這些數(shù)據(jù)后將其設(shè)置為null或重新賦值。全局變量也常用來做cache,一般cache都是為了性能優(yōu)化才用到的,為了性能,最好對(duì)cache的大小做個(gè)上限限制。因?yàn)閏ache是不能被回收的,越高cache會(huì)導(dǎo)致越高的內(nèi)存消耗。
console.log

console.log:向web開發(fā)控制臺(tái)打印一條消息,常用來在開發(fā)時(shí)調(diào)試分析。有時(shí)在開發(fā)時(shí),需要打印一些對(duì)象信息,但發(fā)布時(shí)卻忘記去掉console.log語句,這可能造成內(nèi)存泄露。

在傳遞給console.log的對(duì)象是不能被垃圾回收 ??,因?yàn)樵诖a運(yùn)行之后需要在開發(fā)工具能查看對(duì)象信息。所以最好不要在生產(chǎn)環(huán)境中console.log任何對(duì)象。

實(shí)例------>demos/log.html




  
  
  
  Leaker



  
  


這里結(jié)合Chrome的Devtools–>Performance做一些分析,操作步驟如下:

??注:最好在隱藏窗口中進(jìn)行分析工作,避免瀏覽器插件影響分析結(jié)果

開啟【Performance】項(xiàng)的記錄

執(zhí)行一次CG,創(chuàng)建基準(zhǔn)參考線

連續(xù)單擊【click】按鈕三次,新建三個(gè)Leaker對(duì)象

執(zhí)行一次CG

停止記錄

可以看出【JS Heap】線最后沒有降回到基準(zhǔn)參考線的位置,顯然存在沒有被回收的內(nèi)存。如果將代碼修改為:

    !function () {
      function Leaker() {
        this.init();
      };
      Leaker.prototype = {
        init: function () {
          this.name = (Array(100000)).join("*");
        },

        destroy: function () {
          // do something....
        }
      };
      document.querySelector("input").addEventListener("click", function () {
        new Leaker();
      }, false);
    }()

去掉console.log("Leaking an object %o: %o", (new Date()), this);語句。重復(fù)上述的操作步驟,分析結(jié)果如下:

從對(duì)比分析結(jié)果可知,console.log打印的對(duì)象是不會(huì)被垃圾回收器回收的。因此最好不要在頁面中console.log任何大對(duì)象,這樣可能會(huì)影響頁面的整體性能,特別在生產(chǎn)環(huán)境中。除了console.log外,另外還有console.dir、console.error、console.warn等都存在類似的問題,這些細(xì)節(jié)需要特別的關(guān)注。

closures(閉包)

當(dāng)一個(gè)函數(shù)A返回一個(gè)內(nèi)聯(lián)函數(shù)B,即使函數(shù)A執(zhí)行完,函數(shù)B也能訪問函數(shù)A作用域內(nèi)的變量,這就是一個(gè)閉包——————本質(zhì)上閉包是將函數(shù)內(nèi)部和外部連接起來的一座橋梁。

function foo(message) {
    function closure() {
        console.log(message)
    };
    return closure;
}

// 使用
var bar = foo("hello closure!");
bar()// 返回 "hello closure!"

在函數(shù)foo內(nèi)創(chuàng)建的函數(shù)closure對(duì)象是不能被回收掉的,因?yàn)樗蝗肿兞縝ar引用,處于一直可訪問狀態(tài)。通過執(zhí)行bar()可以打印出hello closure!。如果想釋放掉可以將bar = null即可。

由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過多。

實(shí)例------>demos/closures.html




  
  
  
  Closure



  

不斷單擊【click】按鈕

這里結(jié)合Chrome的Devtools->Memory工具進(jìn)行分析,操作步驟如下:

??注:最好在隱藏窗口中進(jìn)行分析工作,避免瀏覽器插件影響分析結(jié)果

選中【Record allocation timeline】選項(xiàng)

執(zhí)行一次CG

單擊【start】按鈕開始記錄堆分析

連續(xù)單擊【click】按鈕十多次

停止記錄堆分析

上圖中藍(lán)色柱形條表示隨著時(shí)間新分配的內(nèi)存。選中其中某條藍(lán)色柱形條,過濾出對(duì)應(yīng)新分配的對(duì)象:

查看對(duì)象的詳細(xì)信息:

從圖可知,在返回的閉包作用鏈(Scopes)中攜帶有它所在函數(shù)的作用域,作用域中還包含一個(gè)str字段。而str字段并沒有在返回getData()中使用過。為什么會(huì)存在在作用域中,按理應(yīng)該被GC回收掉, why

原因是在相同作用域內(nèi)創(chuàng)建的多個(gè)內(nèi)部函數(shù)對(duì)象是共享同一個(gè)變量對(duì)象(variable object)。如果創(chuàng)建的內(nèi)部函數(shù)沒有被其他對(duì)象引用,不管內(nèi)部函數(shù)是否引用外部函數(shù)的變量和函數(shù),在外部函數(shù)執(zhí)行完,對(duì)應(yīng)變量對(duì)象便會(huì)被銷毀。反之,如果內(nèi)部函數(shù)中存在有對(duì)外部函數(shù)變量或函數(shù)的訪問(可以不是被引用的內(nèi)部函數(shù)),并且存在某個(gè)或多個(gè)內(nèi)部函數(shù)被其他對(duì)象引用,那么就會(huì)形成閉包,外部函數(shù)的變量對(duì)象就會(huì)存在于閉包函數(shù)的作用域鏈中。這樣確保了閉包函數(shù)有權(quán)訪問外部函數(shù)的所有變量和函數(shù)。了解了問題產(chǎn)生的原因,便可以對(duì)癥下藥了。對(duì)代碼做如下修改:

    function f() {
      var str = Array(10000).join("#");
      var foo = {
        name: "foo"
      }
      function unused() {
        var message = "it is only a test message";
        // str = "unused: " + str; //刪除該條語句
      }
      function getData() {
        return "data";
      }
      return getData;
    }

    var list = [];
    
    document.querySelector("#click_button").addEventListener("click", function () {
      list.push(f());
    }, false);

getData()和unused()內(nèi)部函數(shù)共享f函數(shù)對(duì)應(yīng)的變量對(duì)象,因?yàn)閡nused()內(nèi)部函數(shù)訪問了f作用域內(nèi)str變量,所以str字段存在于f變量對(duì)象中。加上getData()內(nèi)部函數(shù)被返回,被其他對(duì)象引用,形成了閉包,因此對(duì)應(yīng)的f變量對(duì)象存在于閉包函數(shù)的作用域鏈中。這里只要將函數(shù)unused中str = "unused: " + str;語句刪除便可解決問題。

查看一下閉包信息:

DOM泄露

在JavaScript中,DOM操作是非常耗時(shí)的。因?yàn)镴avaScript/ECMAScript引擎獨(dú)立于渲染引擎,而DOM是位于渲染引擎,相互訪問需要消耗一定的資源。如Chrome瀏覽器中DOM位于WebCore,而JavaScript/ECMAScript位于V8中。假如將JavaScript/ECMAScript、DOM分別想象成兩座孤島,兩島之間通過一座收費(fèi)橋連接,過橋需要交納一定“過橋費(fèi)”。JavaScript/ECMAScript每次訪問DOM時(shí),都需要交納“過橋費(fèi)”。因此訪問DOM次數(shù)越多,費(fèi)用越高,頁面性能就會(huì)受到很大影響。了解更多??

為了減少DOM訪問次數(shù),一般情況下,當(dāng)需要多次訪問同一個(gè)DOM方法或?qū)傩詴r(shí),會(huì)將DOM引用緩存到一個(gè)局部變量中。但如果在執(zhí)行某些刪除、更新操作后,可能會(huì)忘記釋放掉代碼中對(duì)應(yīng)的DOM引用,這樣會(huì)造成DOM內(nèi)存泄露。

實(shí)例------>demos/dom.html



  
  
  
  Dom-Leakage


  
  

  

  

這里結(jié)合Chrome瀏覽器的Devtools–>Performance做一些分析,操作步驟如下:

??注:最好在隱藏窗口中進(jìn)行分析工作,避免瀏覽器插件影響分析結(jié)果

開啟【Performance】項(xiàng)的記錄

執(zhí)行一次CG,創(chuàng)建基準(zhǔn)參考線

連續(xù)單擊【add】按鈕6次,增加6個(gè)文本節(jié)點(diǎn)到pre元素中

單擊【remove】按鈕,刪除剛增加6個(gè)文本節(jié)點(diǎn)和pre元元素

執(zhí)行一次CG

停止記錄堆分析

從分析結(jié)果圖可知,雖然6次add操作增加6個(gè)Node,但是remove操作并沒有讓Nodes節(jié)點(diǎn)數(shù)下降,即remove操作失敗。盡管還主動(dòng)執(zhí)行了一次CG操作,Nodes曲線也沒有下降。因此可以斷定內(nèi)存泄露了!那問題來了,如何去查找問題的原因呢?這里可以通過Chrome瀏覽器的Devtools–>Memory進(jìn)行診斷分析,執(zhí)行如下操作步驟:

??注:最好在隱藏窗口中進(jìn)行分析工作,避免瀏覽器插件影響分析結(jié)果

選中【Take heap snapshot】選項(xiàng)

連續(xù)單擊【add】按鈕6次,增加6個(gè)文本節(jié)點(diǎn)到pre元素中

單擊【Take snapshot】按鈕,執(zhí)行一次堆快照

單擊【remove】按鈕,刪除剛增加6個(gè)文本節(jié)點(diǎn)和pre元元素

單擊【Take snapshot】按鈕,執(zhí)行一次堆快照

選中生成的第二個(gè)快照?qǐng)?bào)告,并將視圖由"Summary"切換到"Comparison"對(duì)比模式,在[class filter]過濾輸入框中輸入關(guān)鍵字:Detached

從分析結(jié)果圖可知,導(dǎo)致整個(gè)pre元素和6個(gè)文本節(jié)點(diǎn)無法別回收的原因是:代碼中存在全局變量wrapper對(duì)pre元素的引用。知道了產(chǎn)生的問題原因,便可對(duì)癥下藥了。對(duì)代碼做如下就修改:

    // 因?yàn)橐啻斡玫絧re.wrapper、div.container、input.remove、input.add節(jié)點(diǎn),將其緩存到本地變量中
    var wrapper = document.querySelector(".wrapper");
    var container = document.querySelector(".container");
    var removeBtn = document.querySelector(".remove");
    var addBtn = document.querySelector(".add");
    var counter = 0;
    var once = true;
    // 方法
    var hide = function(target){
      target.style.display = "none";
    }
    var show = function(target){
      target.style.display = "inline-block";
    }
    // 回調(diào)函數(shù)
    var removeCallback = function(){
      removeBtn.removeEventListener("click", removeCallback, false);
      addBtn.removeEventListener("click", addCallback, false);
      hide(addBtn);
      hide(removeBtn);
      container.removeChild(wrapper);
     
      wrapper = null;//在執(zhí)行刪除操作時(shí),將wrapper對(duì)pre節(jié)點(diǎn)的引用釋放掉
    }
    var addCallback = function(){
      wrapper.appendChild(document.createTextNode("	" + ++counter + ":a new line text
"));
      // 顯示刪除操作按鈕
      if(once){
        show(removeBtn);
        once = false;
      }
    }
    // 綁定事件
    removeBtn.addEventListener("click", removeCallback, false);
    addBtn.addEventListener("click", addCallback, false);

在執(zhí)行刪除操作時(shí),將wrapper對(duì)pre節(jié)點(diǎn)的引用釋放掉,即在刪除邏輯中增加wrapper = null;語句。再次在Devtools–>Performance中重復(fù)上述操作:

小試牛刀------>demos/dom_practice.html

再來看看網(wǎng)上的一個(gè)實(shí)例,代碼如下:




  
  
  
  Practice


  

整個(gè)過程如下圖所演示:

有興趣的同學(xué)可以使用Chrome的Devtools工具,驗(yàn)證一下分析結(jié)果,實(shí)踐很重要~~~

timers

在JavaScript常用setInterval()來實(shí)現(xiàn)一些動(dòng)畫效果。當(dāng)然也可以使用鏈?zhǔn)?b>setTimeout()調(diào)用模式來實(shí)現(xiàn):

setTimeout(function() {
  // do something. . . .
  setTimeout(arguments.callee, interval);
}, interval);

如果在不需要setInterval()時(shí),沒有通過clearInterval()方法移除,那么setInterval()會(huì)不停地調(diào)用函數(shù),直到調(diào)用clearInterval()或窗口關(guān)閉。如果鏈?zhǔn)?b>setTimeout()調(diào)用模式?jīng)]有給出終止邏輯,也會(huì)一直運(yùn)行下去。因此再不需要重復(fù)定時(shí)器時(shí),確保對(duì)定時(shí)器進(jìn)行清除,避免占用系統(tǒng)資源。另外,在使用setInterval()setTimeout()來實(shí)現(xiàn)動(dòng)畫時(shí),無法確保定時(shí)器按照指定的時(shí)間間隔來執(zhí)行動(dòng)畫。為了能在JavaScript中創(chuàng)建出平滑流暢的動(dòng)畫,瀏覽器為JavaScript動(dòng)畫添加了一個(gè)新API-requestAnimationFrame()。關(guān)于setInterval、setTimeout與requestAnimationFrame實(shí)現(xiàn)動(dòng)畫上的區(qū)別?猛擊?

實(shí)例------>demos/timers.html

如下通過setInterval()實(shí)現(xiàn)一個(gè)clock的小實(shí)例,不過代碼存在問題的,有興趣的同學(xué)可以先嘗試找一下問題的所在~?
操作:

單擊【start】按鈕開始clock,同時(shí)web開發(fā)控制臺(tái)會(huì)打印實(shí)時(shí)信息

單擊【stop】按鈕停止clock,同時(shí)web開發(fā)控制臺(tái)會(huì)輸出停止信息




  
  
  
  setInterval


  
  

  

上述代碼存在兩個(gè)問題:

如果不斷的單擊【start】按鈕,會(huì)斷生成新的clock。

單擊【stop】按鈕不能停止clock。

輸出結(jié)果:

針對(duì)暴露出的問題,對(duì)代碼做如下修改:

    var counter = 0;
    var clock = {
      timer: null,
      start: function () {
        // 解決第一個(gè)問題
        if (this.timer) {
          clearInterval(this.timer);
        }
        this.timer = setInterval(this.step.bind(null, ++counter), 1000);
      },
      step: function (flag) {
        var date = new Date();
        var h = date.getHours();
        var m = date.getMinutes();
        var s = date.getSeconds();
        console.log("%d-----> %d:%d:%d", flag, h, m, s);
      },
      // 解決第二個(gè)問題
      destroy: function () {
        console.log("----> stop <----");
        clearInterval(this.timer);
        node = null;
        counter = void(0);
      }
    }
    document.querySelector(".start").addEventListener("click", clock.start.bind(clock), false);
    document.querySelector(".stop").addEventListener("click", clock.destroy.bind(clock), false);
EventListener

做移動(dòng)開發(fā)時(shí),需要對(duì)不同設(shè)備尺寸做適配。如在開發(fā)組件時(shí),有時(shí)需要考慮處理橫豎屏適配問題。一般做法,在橫豎屏發(fā)生變化時(shí),需要將組件銷毀后再重新生成。而在組件中會(huì)對(duì)其進(jìn)行相關(guān)事件綁定,如果在銷毀組件時(shí),沒有將組件的事件解綁,在橫豎屏發(fā)生變化時(shí),就會(huì)不斷地對(duì)組件進(jìn)行事件綁定。這樣會(huì)導(dǎo)致一些異常,甚至可能會(huì)導(dǎo)致頁面崩掉。

實(shí)例------>demos/callbacks.html



  
  
  
  callbacks


  

頁面是存在問題的,這里結(jié)合Devtools–>Performance分析一下問題所在,操作步驟如下:

??注:最好在隱藏窗口中進(jìn)行分析工作,避免瀏覽器插件影響分析結(jié)果

開啟Performance項(xiàng)的記錄

執(zhí)行一次CG,創(chuàng)建基準(zhǔn)參考線

對(duì)窗口大小進(jìn)行調(diào)整

執(zhí)行一次CG

停止記錄

如分析結(jié)果所示,在窗口大小變化時(shí),會(huì)不斷地對(duì)container添加代理事件。

同一個(gè)元素節(jié)點(diǎn)注冊(cè)了多個(gè)相同的EventListener,那么重復(fù)的實(shí)例會(huì)被拋棄。這么做不會(huì)讓得EventListener被重復(fù)調(diào)用,也不需要用removeEventListener手動(dòng)清除多余的EventListener,因?yàn)橹貜?fù)的都被自動(dòng)拋棄了。而這條規(guī)則只是針對(duì)于命名函數(shù)。對(duì)于匿名函數(shù),瀏覽器會(huì)將其看做不同的EventListener,所以只要將匿名的EventListener,命名一下就可以解決問題:

    var container = document.querySelector(".container");
    var counter = 0;
    var createHtml = function (n, counter) {
      var template = `${(new Array(n)).join(`
${counter}: this is a new data
`)}` container.innerHTML = template; } // var clickCallback = function (event) { var target = event.target; if (target.tagName === "INPUT") { container.removeChild(target.parentElement) } } var resizeCallback = function (init) { createHtml(10, ++counter); // 事件委托 container.addEventListener("click", clickCallback, false); } window.addEventListener("resize", resizeCallback, false); resizeCallback(true);

在Devtools–>Performance中再重復(fù)上述操作,分析結(jié)果如下:

在開發(fā)中,開發(fā)者很少關(guān)注事件解綁,因?yàn)闉g覽器已經(jīng)為我們處理得很好了。不過在使用第三方庫時(shí),需要特別注意,因?yàn)橐话愕谌綆於紝?shí)現(xiàn)了自己的事件綁定,如果在使用過程中,在需要銷毀事件綁定時(shí),沒有調(diào)用所解綁方法,就可能造成事件綁定數(shù)量的不斷增加。如下鏈接是我在項(xiàng)目中使用jquery,遇見到類似問題:jQuery中忘記解綁注冊(cè)的事件,造成內(nèi)存泄露?猛擊?

總結(jié)

本文主要介紹了幾種常見的內(nèi)存泄露。在開發(fā)過程,需要我們特別留意一下本文所涉及到的幾種內(nèi)存泄露問題。因?yàn)檫@些隨時(shí)可能發(fā)生在我們?nèi)粘i_發(fā)中,如果我們對(duì)它們不了解是很難發(fā)現(xiàn)它們的存在。可能在它們將問題影響程度放大時(shí),才會(huì)引起我們的關(guān)注。不過那時(shí)可能就晚了,因?yàn)楫a(chǎn)品可能已經(jīng)上線,接著就會(huì)嚴(yán)重影響產(chǎn)品的質(zhì)量和用戶體驗(yàn),甚至可能讓我們承受大量用戶流失的損失。作為開發(fā)的我們必須把好這個(gè)關(guān),讓我們開發(fā)的產(chǎn)品帶給用戶最好的體驗(yàn)。

參考文章:

An interesting kind of JavaScript memory leak

Memory Leaks in Microsoft Internet Explorer

Memory leak when logging complex objects

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90183.html

相關(guān)文章

  • 完整網(wǎng)站開發(fā)技術(shù)學(xué)習(xí)建議

    摘要:本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。 本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。一.網(wǎng)頁編輯1.網(wǎng)頁編輯技術(shù) (1)起步:包含html,css 推薦網(wǎng)站:html菜鳥教程 慕課網(wǎng) w3cschool 推薦書籍:headfirst系列 推薦編輯器:webstorm /Hbuilder/ esclipe/ At...

    iOS122 評(píng)論0 收藏0
  • 完整網(wǎng)站開發(fā)技術(shù)學(xué)習(xí)建議

    摘要:本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。 本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。一.網(wǎng)頁編輯1.網(wǎng)頁編輯技術(shù) (1)起步:包含html,css 推薦網(wǎng)站:html菜鳥教程 慕課網(wǎng) w3cschool 推薦書籍:headfirst系列 推薦編輯器:webstorm /Hbuilder/ esclipe/ At...

    RdouTyping 評(píng)論0 收藏0
  • 完整網(wǎng)站開發(fā)技術(shù)學(xué)習(xí)建議

    摘要:本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。 本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。一.網(wǎng)頁編輯1.網(wǎng)頁編輯技術(shù) (1)起步:包含html,css 推薦網(wǎng)站:html菜鳥教程 慕課網(wǎng) w3cschool 推薦書籍:headfirst系列 推薦編輯器:webstorm /Hbuilder/ esclipe/ At...

    ckllj 評(píng)論0 收藏0
  • 完整網(wǎng)站開發(fā)技術(shù)學(xué)習(xí)建議

    摘要:本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。 本文講解的是一份學(xué)習(xí)參考目錄,如果能堅(jiān)持學(xué)習(xí)下去,那你離全棧又近了一步。一.網(wǎng)頁編輯1.網(wǎng)頁編輯技術(shù) (1)起步:包含html,css 推薦網(wǎng)站:html菜鳥教程 慕課網(wǎng) w3cschool 推薦書籍:headfirst系列 推薦編輯器:webstorm /Hbuilder/ esclipe/ At...

    freewolf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<