整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          JavaScript HTML DOM-改變 HTM

          JavaScript HTML DOM-改變 HTML

          HTML DOM 允許 JavaScript 改變 HTML 元素的內(nèi)容。

          改變 HTML 輸出流

          JavaScript 能夠創(chuàng)建動(dòng)態(tài)的 HTML 內(nèi)容:

          今天的日期是: Thu Aug 25 2016 09:23:24 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)

          在 JavaScript 中,document.write() 可用于直接向 HTML 輸出流寫(xiě)內(nèi)容。

          實(shí)例

          <!DOCTYPE html>

          <html>

          <body>

          <script>

          document.write(Date());

          </script>

          </body>

          </html>

          絕對(duì)不要在文檔加載完成之后使用 document.write()。這會(huì)覆蓋該文檔。

          改變 HTML 內(nèi)容

          修改 HTML 內(nèi)容的最簡(jiǎn)單的方法時(shí)使用 innerHTML 屬性。

          如需改變 HTML 元素的內(nèi)容,請(qǐng)使用這個(gè)語(yǔ)法:

          document.getElementById(id).innerHTML=新的 HTML

          本例改變了 <p>元素的內(nèi)容:

          實(shí)例

          <html>

          <body>

          <p id="p1">Hello World!</p>

          <script>

          document.getElementById("p1").innerHTML="新文本!";

          </script>

          </body>

          </html>

          本例改變了 <h1> 元素的內(nèi)容:

          實(shí)例

          <!DOCTYPE html>

          <html>

          <body>

          <h1 id="header">Old Header</h1>

          <script>

          var element=document.getElementById("header");

          element.innerHTML="新標(biāo)題";

          </script>

          </body>

          </html>

          實(shí)例講解:

          • 上面的 HTML 文檔含有 id="header" 的 <h1> 元素

          • 我們使用 HTML DOM 來(lái)獲得 id="header" 的元素

          • JavaScript 更改此元素的內(nèi)容 (innerHTML)

          改變 HTML 屬性

          如需改變 HTML 元素的屬性,請(qǐng)使用這個(gè)語(yǔ)法:

          document.getElementById(id).attribute=新屬性值

          本例改變了 <img> 元素的 src 屬性:

          實(shí)例

          <!DOCTYPE html>

          <html>

          <body>

          <img id="image" src="smiley.gif">

          <script>

          document.getElementById("image").src="landscape.jpg";

          </script>

          </body>

          </html>

          實(shí)例講解:

          • 上面的 HTML 文檔含有 id="image" 的 <img> 元素

          • 我們使用 HTML DOM 來(lái)獲得 id="image" 的元素

          • JavaScript 更改此元素的屬性(把 "smiley.gif" 改為 "landscape.jpg")

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!

          檔對(duì)象模型(DOM)

          JS 有很多地方讓咱們吐槽,但沒(méi)那么糟糕。作為一種在瀏覽器中運(yùn)行的腳本語(yǔ)言,它對(duì)于處理web頁(yè)面非常有用。在本中,我們將看到我們有哪些方法來(lái)交互和修改HTML文檔及其元素。但首先讓我們來(lái)揭開(kāi)文檔對(duì)象模型的神秘面紗。

          文檔對(duì)象模型是一個(gè)基本概念,它是咱們?cè)跒g覽器中所做的一切工作的基礎(chǔ)。但那到底是什么? 當(dāng)咱們?cè)L問(wèn)一個(gè) web 頁(yè)面時(shí),瀏覽器會(huì)指出如何解釋每個(gè) HTML 元素。這樣它就創(chuàng)建了 HTML 文檔的虛擬表示,并保存在內(nèi)存中。HTML 頁(yè)面被轉(zhuǎn)換成樹(shù)狀結(jié)構(gòu),每個(gè) HTML 元素都變成一個(gè)葉子,連接到父分支。考慮這個(gè)簡(jiǎn)單的HTML 頁(yè)面:

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <title>A super simple title!</title>
          </head>
          <body>
          <h1>A super simple web page!</h1>
          </body>
          </html
          

          當(dāng)瀏覽器掃描上面的 HTML 時(shí),它創(chuàng)建了一個(gè)文檔對(duì)象模型,它是HTML結(jié)構(gòu)的鏡像。在這個(gè)結(jié)構(gòu)的頂部有一個(gè) document 也稱為根元素,它包含另一個(gè)元素:html。html 元素包含一個(gè) head,head 又有一個(gè) title。然后是含有 h1的 body。每個(gè) HTML 元素由特定類型(也稱為接口)表示,并且可能包含文本或其他嵌套元素

          document (HTMLDocument)
           |
           | --> html (HTMLHtmlElement)
           | 
           | --> head (HtmlHeadElement)
           | |
           | | --> title (HtmlTitleElement)
           | | --> text: "A super simple title!"
           |
           | --> body (HtmlBodyElement)
           | |
           | | --> h1 (HTMLHeadingElement)
           | | --> text: "A super simple web page!"
          

          每個(gè) HTML 元素都是從 Element 派生而來(lái)的,但是它們中的很大一部分是進(jìn)一步專門化的。咱們可以檢查原型,以查明元素屬于什么“種類”。例如,h1 元素是 HTMLHeadingElement

          document.quertSelector('h1').__proto__
          // 輸出:HTMLHeadingElement
          

          HTMLHeadingElement 又是 HTMLElement 的“后代”

          document.querySelector('h1').__proto__.__proto__
          // Output: HTMLElement
          
          Element 是一個(gè)通用性非常強(qiáng)的基類,所有 Document 對(duì)象下的對(duì)象都繼承自它。這個(gè)接口描述了所有相同種類的元素所普遍具有的方法和屬性。一些接口繼承自 Element 并且增加了一些額外功能的接口描述了具體的行為。例如, HTMLElement 接口是所有 HTML 元素的基本接口,而 SVGElement 接口是所有 SVG 元素的基礎(chǔ)。大多數(shù)功能是在這個(gè)類的更深層級(jí)(hierarchy)的接口中被進(jìn)一步制定的。

          在這一點(diǎn)上(特別是對(duì)于初學(xué)者),document 和 window 之間可能有些混淆。window 指的是瀏覽器,而 document 指的是當(dāng)前的 HTML 頁(yè)面。window 是一個(gè)全局對(duì)象,可以從瀏覽器中運(yùn)行的任何 JS 代碼直接訪問(wèn)它。它不是 JS 的“原生”對(duì)象,而是由瀏覽器本身公開(kāi)的。window 有很多屬性和方法,如下所示:

          window.alert('Hello world'); // Shows an alert
          window.setTimeout(callback, 3000); // Delays execution
          window.fetch(someUrl); // makes XHR requests
          window.open(); // Opens a new tab
          window.location; // Browser location
          window.history; // Browser history
          window.navigator; // The actual device
          window.document; // The current page
          

          由于這些屬性是全局屬性,因此也可以省略 window:

          alert('Hello world'); // Shows an alert
          setTimeout(callback, 3000); // Delays execution
          fetch(someUrl); // makes XHR requests
          open(); // Opens a new tab
          location; // Browser location
          history; // Browser history
          navigator; // The actual device
          document; // The current page
          

          你應(yīng)該已經(jīng)熟悉其中的一些方法,例如 setTimeout()或 window.navigator,它可以獲取當(dāng)前瀏覽器使用的語(yǔ)言:

          if (window.navigator) {
           var lang=window.navigator.language;
           if (lang==="en-US") {
           // show something
           }
           if (lang==="it-IT") {
           // show something else
           }
          }
          

          要了解更多 window 上的方法,請(qǐng)查看MDN文檔。在下一節(jié)中,咱們深入地研究一下 DOM

          節(jié)點(diǎn)、元素 和DOM 操作

          document 接口有許多實(shí)用的方法,比如 querySelector(),它是用于選擇當(dāng)前 HTML 頁(yè)面內(nèi)的任何 HTML 元素:

          document.querySelector('h1');
          


          window 表示當(dāng)前窗口的瀏覽器,下面的指令與上面的相同:

          window.document.querySelector('h1');
          


          不管怎樣,下面的語(yǔ)法更常見(jiàn),在下一節(jié)中咱們將大量使用這種形式:

          document.methodName();
          


          除了 querySelector() 用于選擇 HTML 元素之外,還有很多更有用的方法

          // 返回單個(gè)元素
          document.getElementById('testimonials'); 
          // 返回一個(gè) HTMLCollection
          document.getElementsByTagName('p'); 
          // 返回一個(gè)節(jié)點(diǎn)列表
          document.querySelectorAll('p');
          

          咱們不僅可以選 擇HTML 元素,還可以交互和修改它們的內(nèi)部狀態(tài)。例如,希望讀取或更改給定元素的內(nèi)部?jī)?nèi)容:

          // Read or write
          document.querySelector('h1').innerHtml; // Read
          document.querySelector('h1').innerHtml=''; // Write! Ouch!
          

          DOM 中的每個(gè) HTML 元素也是一個(gè)“節(jié)點(diǎn)”,實(shí)際上咱們可以像這樣檢查節(jié)點(diǎn)類型:

          document.querySelector('h1').nodeType;
          


          上述結(jié)果返回 1,表示是 Element 類型的節(jié)點(diǎn)的標(biāo)識(shí)符。咱們還可以檢查節(jié)點(diǎn)名:

          document.querySelector('h1').nodeName;
          "H1"
          

          這里,節(jié)點(diǎn)名以大寫(xiě)形式返回。通常我們處理 DOM 中的四種類型的節(jié)點(diǎn)

          • document: 根節(jié)點(diǎn)(nodeType 9)
          • 類型為Element的節(jié)點(diǎn):實(shí)際的HTML標(biāo)簽(nodeType 1),例如 <p> 和 <div>
          • 類型屬性的節(jié)點(diǎn):每個(gè)HTML元素的屬性(屬性)
          • Text 類型的節(jié)點(diǎn):元素的實(shí)際文本內(nèi)容(nodeType 3)

          由于元素是節(jié)點(diǎn),節(jié)點(diǎn)可以有屬性(properties )(也稱為attributes),咱們可以檢查和操作這些屬性:

          // 返回 true 或者 false
          document.querySelector('a').hasAttribute('href');
          // 返回屬性文本內(nèi)容,或 null
          document.querySelector('a').getAttribute('href');
          // 設(shè)置給定的屬性
          document.querySelector('a').setAttribute('href', 'someLink');
          

          前面我們說(shuō)過(guò) DOM 是一個(gè)類似于樹(shù)的結(jié)構(gòu)。這種特性也反映在 HTML 元素上。每個(gè)元素都可能有父元素和子元素,我們可以通過(guò)檢查元素的某些屬性來(lái)查看它們:

          // 返回一個(gè) HTMLCollection
          document.children;
          // 返回一個(gè)節(jié)點(diǎn)列表
          document.childNodes;
          // 返回一個(gè)節(jié)點(diǎn)
          document.querySelector('a').parentNode;
          // 返回HTML元素
          document.querySelector('a').parentElement;
          

          了解了如何選擇和查詢 HTML 元素。那創(chuàng)建元素又是怎么樣?為了創(chuàng)建 Element 類型的新節(jié)點(diǎn),原生 DOM API 提供了 createElement 方法:

          var heading=document.createElement('h1');
          


          使用 createTextNode 創(chuàng)建文本節(jié)點(diǎn):

          var text=document.createTextNode('Hello world');
          


          通過(guò)將 text 附加到新的 HTML 元素中,可以將這兩個(gè)節(jié)點(diǎn)組合在一起。最后,還可以將heading元素附加到根文檔中:

          var heading=document.createElement('h1');
          var text=document.createTextNode('Hello world');
          heading.appendChild(text);
          document.body.appendChild(heading);
          

          還可以使用 remove() 方法從 DOM 中刪除節(jié)點(diǎn)。在元素上調(diào)用方法,該節(jié)點(diǎn)將從頁(yè)面中消失:

          document.querySelector('h1').remove();
          


          這些是咱們開(kāi)始在瀏覽器中使用 JS 操作 DOM 所需要知道的全部?jī)?nèi)容。在下一節(jié)中,咱們將靈活地使用 DOM,但首先要繞個(gè)彎,因?yàn)樵蹅冞€需要討論“DOM事件”

          DOM 和事件

          DOM 元素是很智能的。它們不僅可以包含文本和其他 HTML 元素,還可以“發(fā)出”和響應(yīng)“事件”。瀏覽任何網(wǎng)站并打開(kāi)瀏覽器控制臺(tái)。使用以下命令選擇一個(gè)元素:

          document.querySelector('p')
          


          看看這個(gè)屬性

          document.querySelector('p').onclick
          


          它是什么類型:

          typeof document.querySelector('p').onclick // "object"
          


          "object"! 為什么它被稱為“onclick”? 憑一點(diǎn)直覺(jué)我們可以想象它是元素上的某種神奇屬性,能夠?qū)c(diǎn)擊做出反應(yīng)? 完全正確。

          如果你感興趣,可以查看任何 HTML 元素的原型鏈。會(huì)發(fā)現(xiàn)每個(gè)元素也是一個(gè) Element,而元素又是一個(gè)節(jié)點(diǎn),而節(jié)點(diǎn)又是一個(gè)EventTarget。可以使用 instanceof 來(lái)驗(yàn)證這一點(diǎn)。

          document.querySelector('p') instanceof EventTarget // true
          


          我很樂(lè)意稱 EventTarget 為所有 HTML 元素之父,但在JS中沒(méi)有真正的繼承,它更像是任何 HTML 元素都可以看到另一個(gè)連接對(duì)象的屬性。因此,任何 HTML 元素都具有與 EventTarget相同的特性:發(fā)布事件的能力

          但事件到底是什么呢?以 HTML 按鈕為例。如果你點(diǎn)擊它,那就是一個(gè)事件。有了這個(gè).onclick對(duì)象,咱們可以注冊(cè)事件,只要元素被點(diǎn)擊,它就會(huì)運(yùn)行。傳遞給事件的函數(shù)稱為“事件監(jiān)聽(tīng)器”“事件句柄”

          事件和監(jiān)聽(tīng)

          在 DOM 中注冊(cè)事件監(jiān)聽(tīng)器有三種方法。第一種形式比較陳舊,應(yīng)該避免,因?yàn)樗詈狭诉壿嫴僮骱蜆?biāo)簽

          <!-- 不好的方式 -->
          <button onclick="console.log('clicked')">喜歡,就點(diǎn)點(diǎn)我</button>
          

          第二個(gè)選項(xiàng)依賴于以事件命名的對(duì)象。例如,咱們可以通過(guò)在對(duì)象.onclick上注冊(cè)一個(gè)函數(shù)來(lái)監(jiān)聽(tīng)click事件:

          document.querySelector("button").onclick=handleClick;
          function handleClick() {
           console.log("Clicked!");
          }
          

          此語(yǔ)法更加簡(jiǎn)潔,是內(nèi)聯(lián)處理程序的不錯(cuò)替代方案。還有另一種基于addEventListener的現(xiàn)代形式:

          document.querySelector("button").addEventListener("click", handleClick);
          function handleClick() {
           console.log("Clicked!");
          }
          

          就我個(gè)人而言,我更喜歡這種形式,但如果想爭(zhēng)取最大限度的瀏覽器兼容性,請(qǐng)使用 .on 方式。現(xiàn)在咱們已經(jīng)有了一 個(gè) HTML 元素和一個(gè)事件監(jiān)聽(tīng)器,接著進(jìn)一步研究一下 DOM 事件。

          事件對(duì)象、事件默認(rèn)值和事件冒泡

          作為事件處理程序傳遞的每個(gè)函數(shù)默認(rèn)接收一個(gè)名為“event”的對(duì)象

          var button=document.querySelector("button");
          button.addEventListener("click", handleClick);
          function handleClick() {
           console.log(event);
          }
          

          它可以直接在函數(shù)體中使用,但是在我的代碼中,我更喜歡將它顯式地聲明為參數(shù):

          function handleClick(event) {
           console.log(event);
          }
          

          事件對(duì)象是“必須要有的”,因?yàn)樵蹅兛梢酝ㄟ^(guò)調(diào)用事件上的一些方法來(lái)控制事件的行為。事件實(shí)際上有特定的特征,尤其是“默認(rèn)”“冒泡”。考慮一 個(gè)HTML 鏈接。使用以下標(biāo)簽創(chuàng)建一個(gè)名為click-event.html的新HTML文件:

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Click event</title>
          </head>
          <body>
          <div>
           <a href="/404.html">click me!</a>
          </div>
          </body>
          <script src="click-event.js"></script>
          </html>
          

          在瀏覽器中運(yùn)行該文件并嘗試單擊鏈接。它將跳轉(zhuǎn)到一個(gè)404的界面。鏈接上單擊事件的默認(rèn)行為是轉(zhuǎn)到href屬性中指定的實(shí)際頁(yè)面。但如果我告訴你有辦法阻止默認(rèn)值呢?輸入preventDefault(),該方法可用于事件對(duì)象。使用以下代碼創(chuàng)建一個(gè)名為click-event.js的新文件:

          var button=document.querySelector("a");
          button.addEventListener("click", handleClick);
          function handleClick(event) {
           event.preventDefault();
          }
          

          在瀏覽器中刷新頁(yè)面并嘗試現(xiàn)在單擊鏈接:它不會(huì)跳轉(zhuǎn)了。因?yàn)樵蹅冏柚沽藶g覽器的“事件默認(rèn)” 鏈接不是默認(rèn)操作的惟一HTML 元素,表單具有相同的特性。

          當(dāng) HTML 元素嵌套在另一個(gè)元素中時(shí),還會(huì)出現(xiàn)另一個(gè)有趣的特性。考慮以下 HTML

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Nested events</title>
          </head>
          <body>
          <div id="outer">
           I am the outer div
           <div id="inner">
           I am the inner div
           </div>
          </div>
          </body>
          <script src="nested-events.js"></script>
          </html>
          

          和下面的 JS 代碼:

          // nested-events.js
          var outer=document.getElementById('inner');
          var inner=document.getElementById('outer');
          function handleClick(event){
           console.log(event);
          }
          inner.addEventListener('click', handleClick);
          outer.addEventListener('click', handleClick);
          

          有兩個(gè)事件監(jiān)聽(tīng)器,一個(gè)用于外部 div,一個(gè)用于內(nèi)部 div。準(zhǔn)確地點(diǎn)擊內(nèi)部div,你會(huì)看到:

          兩個(gè)事件對(duì)象被打印。這就是事件冒泡在起作用。它看起來(lái)像是瀏覽器行為中的一個(gè) bug,使用 stopPropagation() 方法可以禁用,這也是在事件對(duì)象上調(diào)用的

          //
          function handleClick(event) {
           event.stopPropagation();
           console.log(event);
          }
          ///
          

          盡管看起來(lái)實(shí)現(xiàn)效果很差,但在注冊(cè)過(guò)多事件監(jiān)聽(tīng)器確實(shí)對(duì)性能不利的情況下,冒泡還是會(huì)讓人眼前一亮。考慮以下示例:

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Event bubbling</title>
          </head>
          <body>
          <ul>
           <li>one</li>
           <li>two</li>
           <li>three</li>
           <li>four</li>
           <li>five</li>
          </ul>
          </body>
          <script src="event-bubbling.js"></script>
          </html>
          

          如果要兼聽(tīng)列表的點(diǎn)擊事件,需要在列表中注冊(cè)多少事件監(jiān)聽(tīng)器?答案是:一個(gè)。只需要一個(gè)在ul上注冊(cè)的偵聽(tīng)器就可以截獲任何li上的所有單擊:

          // event-bubbling.js
          var ul=document.getElementsByTagName("ul")[0];
          function handleClick(event) {
           console.log(event);
          }
          ul.addEventListener("click", handleClick);
          

          可以看到,事件冒泡是提高性能的一種實(shí)用方法。實(shí)際上,對(duì)瀏覽器來(lái)說(shuō),注冊(cè)事件監(jiān)聽(tīng)器是一項(xiàng)昂貴的操作,而且在出現(xiàn)大量元素列表的情況下,可能會(huì)導(dǎo)致性能損失。

          用 JS 生成表格

          現(xiàn)在咱們開(kāi)始編碼。給定一個(gè)對(duì)象數(shù)組,希望動(dòng)態(tài)生成一個(gè)HTML 表格。HTML 表格由 <table> 元素表示。每個(gè)表也可以有一個(gè)頭部,由 <thead> 元素表示。頭部可以有一個(gè)或多個(gè)行 <tr>,每個(gè)行都有一個(gè)單元格,由一個(gè) <th>元 素表示。如下所示:

          <table>
           <thead>
           <tr>
           <th>name</th>
           <th>height</th>
           <th>place</th>
           </tr>
           </thead>
           <!-- more stuff here! -->
          </table>
          

          不止這樣,大多數(shù)情況下,每個(gè)表都有一個(gè)主體,由 <tbody> 定義,而 <tbody> 又包含一組行<tr>。每一行都可以有包含實(shí)際數(shù)據(jù)的單元格。表單元格由<td>定義。完整如下所示:

          <table>
           <thead>
           <tr>
           <th>name</th>
           <th>height</th>
           <th>place</th>
           </tr>
           </thead>
           <tbody>
           <tr>
           <td>Monte Falco</td>
           <td>1658</td>
           <td>Parco Foreste Casentinesi</td>
           </tr>
           <tr>
           <td>Monte Falterona</td>
           <td>1654</td>
           <td>Parco Foreste Casentinesi</td>
           </tr>
           </tbody>
          </table>
          

          現(xiàn)在的任務(wù)是從 JS 對(duì)象數(shù)組開(kāi)始生成表格。首先,創(chuàng)建一個(gè)名為build-table.html的新文件,內(nèi)容如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Build a table</title>
          </head>
          <body>
          <table>
          <!-- here goes our data! -->
          </table>
          </body>
          <script src="build-table.js"></script>
          </html>
          

          在相同的文件夾中創(chuàng)建另一個(gè)名為build-table.js的文件,并使用以下數(shù)組開(kāi)始:

          "use strict";
          var mountains=[
           { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
           { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
           { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
           { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
           { name: "Monte Amiata", height: 1738, place: "Siena" }
          ];
          

          考慮這個(gè)表格。首先,咱們需要一個(gè) <thead>:

          document.createElement('thead')
          


          這沒(méi)有錯(cuò),但是仔細(xì)查看MDN的表格文檔會(huì)發(fā)現(xiàn)一個(gè)有趣的細(xì)節(jié)。<table> 是一個(gè) HTMLTableElement,它還包含有趣方法。其中最有用的是HTMLTableElement.createTHead(),它可以幫助創(chuàng)建咱們需要的 <thead>。

          首先,編寫(xiě)一個(gè)生成 thead 標(biāo)簽的函數(shù) generateTableHead

          function generateTableHead(table) {
           var thead=table.createTHead();
          }
          

          該函數(shù)接受一個(gè)選擇器并在給定的表上創(chuàng)建一個(gè) <thead>:

          function generateTableHead(table) {
           var thead=table.createTHead();
          }
          var table=document.querySelector("table");
          generateTableHead(table);
          

          在瀏覽器中打開(kāi) build-table.html:什么都沒(méi)有.但是,如果打開(kāi)瀏覽器控制臺(tái),可以看到一個(gè)新的 <thead> 附加到表。

          接著填充 header 內(nèi)容。首先要在里面創(chuàng)建一行。還有另一個(gè)方法可以提供幫助:HTMLTableElement.insertRow()。有了這個(gè),咱們就可以擴(kuò)展方法了:

          function generateTableHead (table) {
           var thead=table,createThead();
           var row=thead.insertRow();
          }
          

          此時(shí),我們可以生成我們的行。通過(guò)查看源數(shù)組,可以看到其中的任何對(duì)象都有咱們需要信息:

          var mountains=[
           { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
           { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
           { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
           { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
           { name: "Monte Amiata", height: 1738, place: "Siena" }
          ];
          

          這意味著咱們可以將另一個(gè)參數(shù)傳遞給我們的函數(shù):一個(gè)遍歷以生成標(biāo)題單元格的數(shù)組:

          function generateTableHead(table, data) {
           var thead=table.createTHead();
           var row=thead.insertRow();
           for (var i=0; i < data.length; i++) {
           var th=document.createElement("th");
           var text=document.createTextNode(data[i]);
           th.appendChild(text);
           row.appendChild(th);
           }
          }
          

          不幸的是,沒(méi)有創(chuàng)建單元格的原生方法,因此求助于document.createElement("th")。同樣值得注意的是,document.createTextNode(data[i])用于創(chuàng)建文本節(jié)點(diǎn),appendChild()用于向每個(gè)標(biāo)記添加新元素。

          當(dāng)以這種方式創(chuàng)建和操作元素時(shí),我們稱之為“命令式” DOM 操作。現(xiàn)代前端庫(kù)通過(guò)支持“聲明式”方法來(lái)解決這個(gè)問(wèn)題。我們可以聲明需要哪些 HTML 元素,而不是一步一步地命令瀏覽器,其余的由庫(kù)處理。

          回到我們的代碼,可以像下面這樣使用第一個(gè)函數(shù)

          var table=document.querySelector("table");
          var data=Object.keys(mountains[0]);
          generateTableHead(table, data);
          

          現(xiàn)在我們可以進(jìn)一步生成實(shí)際表的數(shù)據(jù)。下一個(gè)函數(shù)將實(shí)現(xiàn)一個(gè)類似于generateTableHead的邏輯,但這一次咱們需要兩個(gè)嵌套的for循環(huán)。在最內(nèi)層的循環(huán)中,使用另一種原生方法來(lái)創(chuàng)建一系列td。方法是HTMLTableRowElement.insertCell()。在前面創(chuàng)建的文件中添加另一個(gè)名為generateTable的函數(shù)

          function generateTable(table, data) {
           for (var i=0; i < data.length; i++) {
           var row=table.insertRow();
           for (var key in data[i]) {
           var cell=row.insertCell();
           var text=document.createTextNode(data[i][key]);
           cell.appendChild(text);
           }
           }
          }
          

          調(diào)用上面的函數(shù),將 HTML表 和對(duì)象數(shù)組作為參數(shù)傳遞:

          generateTable(table, mountains);
          


          咱們深入研究一下 generateTable 的邏輯。參數(shù) data 是一個(gè)與 mountains 相對(duì)應(yīng)的數(shù)組。最外層的 for 循環(huán)遍歷數(shù)組并為每個(gè)元素創(chuàng)建一行:

          function generateTable(table, data) {
           for (var i=0; i < data.length; i++) {
           var row=table.insertRow();
           // omitted for brevity
           }
          }
          

          最內(nèi)層的循環(huán)遍歷任何給定對(duì)象的每個(gè)鍵,并為每個(gè)對(duì)象創(chuàng)建一個(gè)包含鍵值的文本節(jié)點(diǎn)

          function generateTable(table, data) {
           for (var i=0; i < data.length; i++) {
           var row=table.insertRow();
           for (var key in data[i]) {
           // inner loop
           var cell=row.insertCell();
           var text=document.createTextNode(data[i][key]);
           cell.appendChild(text);
           }
           }
          }
          

          最終代碼:

          var mountains=[
           { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
           { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
           { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
           { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
           { name: "Monte Amiata", height: 1738, place: "Siena" }
          ];
          function generateTableHead(table, data) {
           var thead=table.createTHead();
           var row=thead.insertRow();
           for (var i=0; i < data.length; i++) {
           var th=document.createElement("th");
           var text=document.createTextNode(data[i]);
           th.appendChild(text);
           row.appendChild(th);
           }
          }
          function generateTable(table, data) {
           for (var i=0; i < data.length; i++) {
           var row=table.insertRow();
           for (var key in data[i]) {
           var cell=row.insertCell();
           var text=document.createTextNode(data[i][key]);
           cell.appendChild(text);
           }
           }
          }
          

          其中調(diào)用:

          var table=document.querySelector("table");
          var data=Object.keys(mountains[0]);
          generateTable(table, mountains);
          generateTableHead(table, data);
          

          執(zhí)行結(jié)果:


          當(dāng)然,咱們的方法還可以該進(jìn),下個(gè)章節(jié)將介紹。

          總結(jié)

          DOM 是 web 瀏覽器保存在內(nèi)存中的 web 頁(yè)面的虛擬副本。DOM 操作是指從 DOM 中創(chuàng)建、修改和刪除 HTML 元素的操作。在過(guò)去,咱們常常依賴 jQuery 來(lái)完成更簡(jiǎn)單的任務(wù),但現(xiàn)在原生 API 已經(jīng)足夠成熟,可以讓 jQuery 過(guò)時(shí)了。另一方面,jQuery 不會(huì)很快消失,但是每個(gè) JS 開(kāi)發(fā)人員都必須知道如何使用原生 API 操作 DOM。

          這樣做的理由有很多,額外的庫(kù)增加了加載時(shí)間和 JS 應(yīng)用程序的大小。更不用說(shuō) DOM 操作在面試中經(jīng)常出現(xiàn)。

          DOM 中每個(gè)可用的 HTML 元素都有一個(gè)接口,該接口公開(kāi)一定數(shù)量的屬性和方法。當(dāng)你對(duì)使用何種方法有疑問(wèn)時(shí),參考MDN文檔。操作 DOM 最常用的方法是 document.createElement()用于創(chuàng)建新的 HTML 元素,document.createTextNode() 用于在 DOM 中創(chuàng)建文本節(jié)點(diǎn)。最后但同樣重要的是 .appendchild(),用于將新的 HTML 元素或文本節(jié)點(diǎn)附加到現(xiàn)有元素。

          HTML 元素還能夠發(fā)出事件,也稱為DOM事件。值得注意的事件為“click”、“submit”、“drag”、“drop”等等。DOM 事件有一些特殊的行為,比如“默認(rèn)”和冒泡。

          JS 開(kāi)發(fā)人員可以利用這些屬性,特別是對(duì)于事件冒泡,這些屬性對(duì)于加速 DOM 中的事件處理非常有用。雖然對(duì)原生 API 有很好的了解是件好事,但是現(xiàn)代前端庫(kù)提供了不容置疑的好處。用 Angular、React 和 Vue 來(lái)構(gòu)建一個(gè)大型的JS應(yīng)用程序確實(shí)是可行的。

          代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。

          原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter8.md

          引言

          如今,Javascript 模塊化規(guī)范非常方便、自然,但這個(gè)新規(guī)范僅執(zhí)行了2年,就在 4 年前,js 的模塊化還停留在運(yùn)行時(shí)支持,10 年前,通過(guò)后端模版定義、注釋定義模塊依賴。對(duì)經(jīng)歷過(guò)來(lái)的人來(lái)說(shuō),歷史的模塊化方式還停留在腦海中,反而新上手的同學(xué)會(huì)更快接受現(xiàn)代的模塊化規(guī)范。

          但為什么要了解 Javascript 模塊化發(fā)展的歷史呢?因?yàn)榉彩露加袃擅嫘裕私?Javascript 模塊化規(guī)范,有利于我們思考出更好的模塊化方案,縱觀歷史,從 1999 年開(kāi)始,模塊化方案最多維持兩年,就出現(xiàn)了新的替代方案,比原有的模塊化更清晰、強(qiáng)壯,我們不能被現(xiàn)代模塊化方式限制住思維,因?yàn)楝F(xiàn)在的 ES2015 模塊化方案距離發(fā)布也僅僅過(guò)了兩年。

          2 內(nèi)容概要

          直接定義依賴 (1999): 由于當(dāng)時(shí) js 文件非常簡(jiǎn)單,模塊化方式非常簡(jiǎn)單粗暴 —— 通過(guò)全局方法定義、引用模塊。這種定義方式與現(xiàn)在的 commonjs 非常神似,區(qū)別是 commonjs 以文件作為模塊,而這種方法可以在任何文件中定義模塊,模塊不與文件關(guān)聯(lián)。

          閉包模塊化模式 (2003): 用閉包方式解決了變量污染問(wèn)題,閉包內(nèi)返回模塊對(duì)象,只需對(duì)外暴露一個(gè)全局變量。

          模版依賴定義 (2006): 這時(shí)候開(kāi)始流行后端模版語(yǔ)法,通過(guò)后端語(yǔ)法聚合 js 文件,從而實(shí)現(xiàn)依賴加載,說(shuō)實(shí)話,現(xiàn)在 go 語(yǔ)言等模版語(yǔ)法也很流行這種方式,寫(xiě)后端代碼的時(shí)候不覺(jué)得,回頭看看,還是掛在可維護(hù)性上。

          注釋依賴定義 (2006): 幾乎和模版依賴定義同時(shí)出現(xiàn),與 1999 年方案不同的,不僅僅是模塊定義方式,而是終于以文件為單位定義模塊了,通過(guò) lazyjs 加載文件,同時(shí)讀取文件注釋,繼續(xù)遞歸加載剩下的文件。

          外部依賴定義 (2007): 這種定義方式在 cocos2d-js 開(kāi)發(fā)中普遍使用,其核心思想是將依賴抽出單獨(dú)文件定義,這種方式不利于項(xiàng)目管理,畢竟依賴抽到代碼之外,我是不是得兩頭找呢?所以才有通過(guò) webpack 打包為一個(gè)文件的方式暴力替換為 commonjs 的方式出現(xiàn)。

          Sandbox模式 (2009): 這種模塊化方式很簡(jiǎn)單,暴力,將所有模塊塞到一個(gè) sanbox 變量中,硬傷是無(wú)法解決明明沖突問(wèn)題,畢竟都塞到一個(gè) sandbox 對(duì)象里,而 Sandbox 對(duì)象也需要定義在全局,存在被覆蓋的風(fēng)險(xiǎn)。模塊化需要保證全局變量盡量干凈,目前為止的模塊化方案都沒(méi)有很好的做到這一點(diǎn)。

          依賴注入 (2009): 就是大家熟知的 angular1.0,依賴注入的思想現(xiàn)在已廣泛運(yùn)用在 react、vue 等流行框架中。但依賴注入和解決模塊化問(wèn)題還差得遠(yuǎn)。

          CommonJS (2009): 真正解決模塊化問(wèn)題,從 node 端逐漸發(fā)力到前端,前端需要使用構(gòu)建工具模擬。

          Amd (2009): 都是同一時(shí)期的產(chǎn)物,這個(gè)方案主要解決前端動(dòng)態(tài)加載依賴,相比 commonJs,體積更小,按需加載。

          Umd (2011): 兼容了 CommonJS 與 Amd,其核心思想是,如果在 commonjs 環(huán)境(存在 module.exports,不存在 define),將函數(shù)執(zhí)行結(jié)果交給 module.exports 實(shí)現(xiàn) Commonjs,否則用 Amd 環(huán)境的 define,實(shí)現(xiàn) Amd。

          Labeled Modules (2012): 和 Commonjs 很像了,沒(méi)什么硬傷,但生不逢時(shí),碰上 Commonjs 與 Amd,那只有被人遺忘的份了。

          YModules (2013): 既然都出了 Commonjs Amd,文章還列出了此方案,一定有其獨(dú)到之處。其核心思想在于使用 provide 取代 return,可以控制模塊結(jié)束時(shí)機(jī),處理異步結(jié)果;拿到第二個(gè)參數(shù) module,修改其他模塊的定義(雖然很有拓展性,但用在項(xiàng)目里是個(gè)攪屎棍)。

          ES2015 Modules (2015): 就是我們現(xiàn)在的模塊化方案,還沒(méi)有被瀏覽器實(shí)現(xiàn),大部分項(xiàng)目已通過(guò) babel 或 typescript 提前體驗(yàn)。

          3 精讀

          從語(yǔ)言層面到文件層面的模塊化

          從 1999 年開(kāi)始,模塊化探索都是基于語(yǔ)言層面的優(yōu)化,真正的革命從 2009 年 CommonJS 的引入開(kāi)始,前端開(kāi)始大量使用預(yù)編譯。

          這篇文章所提供的模塊化歷史的方案都是邏輯模塊化,從 CommonJS 方案開(kāi)始前端把服務(wù)端的解決方案搬過(guò)來(lái)之后,算是看到標(biāo)準(zhǔn)物理與邏輯統(tǒng)一的模塊化。但之后前端工程不得不引入模塊化構(gòu)建這一步。正是這一步給前端開(kāi)發(fā)無(wú)疑帶來(lái)了諸多的不便,尤其是現(xiàn)在我們開(kāi)發(fā)過(guò)程中經(jīng)常為了優(yōu)化這個(gè)工具帶了很多額外的成本。

          從 CommonJS 之前其實(shí)都只是封裝,并沒(méi)有一套模塊化規(guī)范,這個(gè)就有些像類與包的概念。我在10年左右用的最多的還是 YUI2,YUI2 是用 namespace 來(lái)做模塊化的,但有很多問(wèn)題沒(méi)有解決,比如多版本共存,因此后來(lái) YUI3 出來(lái)了。

          YUI().use('node', 'event', function (Y) {
           // The Node and Event modules are loaded and ready to use.
           // Your code goes here!
          });
          

          YUI3 的 sandbox 像極了差不多同時(shí)出現(xiàn)的 AMD 規(guī)范,但早期 yahoo 在前端圈的影響力還是很大的,而 requirejs 到 2011 年才誕生,因此圈子不是用著 YUI 要不就自己封裝一套 sandbox,內(nèi)部使用 jQuery。

          為什么模塊化方案這么晚才成型,可能早期應(yīng)用的復(fù)雜度都在后端,前端都是非常簡(jiǎn)單邏輯。后來(lái) Ajax 火了之后,web app 概念的開(kāi)始流行,前端的復(fù)雜度也呈指數(shù)級(jí)上漲,到今天幾乎和后端接近一個(gè)量級(jí)。工程發(fā)展到一定階段,要出現(xiàn)的必然會(huì)出現(xiàn)。

          前端三劍客的模塊化展望

          從 js 模塊化發(fā)展史,我們還看到了 css html 模塊化方面的嚴(yán)重落后,如今依賴編譯工具的模塊化增強(qiáng)在未來(lái)會(huì)被標(biāo)準(zhǔn)所替代。

          原生支持的模塊化,解決 html 與 css 模塊化問(wèn)題正是以后的方向。

          再回到 JS 模塊化這個(gè)主題,開(kāi)頭也說(shuō)到是為了構(gòu)建 scope,實(shí)則提供了業(yè)務(wù)規(guī)范標(biāo)準(zhǔn)的輸入輸出的方式。但文章中的 JS 的模塊化還不等于前端工程的模塊化,Web 界面是由 HTML、CSS 和 JS 三種語(yǔ)言實(shí)現(xiàn),不論是 CommonJS 還是 AMD 包括之后的方案都無(wú)法解決 CSS 與 HTML 模塊化的問(wèn)題。

          對(duì)于 CSS 本身它就是 global scope,因此開(kāi)發(fā)樣式可以說(shuō)是喜憂參半。近幾年也涌現(xiàn)把 HTML、CSS 和 JS 合并作模塊化的方案,其中 react/css-modules 和 vue 都為人熟知。當(dāng)然,這一點(diǎn)還是非常依賴于 webpack/rollup 等構(gòu)建工具,讓我們意識(shí)到在 browser 端還有很多本質(zhì)的問(wèn)題需要推進(jìn)。

          對(duì)于 css 模塊化,目前不依賴預(yù)編譯的方式是 styled-component,通過(guò) js 動(dòng)態(tài)創(chuàng)建 class。而目前 css 也引入了與 js 通信的機(jī)制 與 原生變量支持。未來(lái) css 模塊化也很可能是運(yùn)行時(shí)的,所以目前比較看好 styled-component 的方向。

          對(duì)于 html 模塊化,小尤最近爆出與 chrome 小組調(diào)研 html Modules,如果 html 得到了瀏覽器,編輯器的模塊化支持,未來(lái)可能會(huì)取代 jsx 成為最強(qiáng)大的模塊化、模板語(yǔ)言。

          對(duì)于 js 模塊化,最近出現(xiàn)的 <script type="module"> 方式,雖然還沒(méi)有得到瀏覽器原生支持,但也是我比較看好的未來(lái)趨勢(shì),這樣就連 webpack 的拆包都不需要了,直接把源代碼傳到服務(wù)器,配合 http2.0 完美拋開(kāi)預(yù)編譯的枷鎖。

          上述三中方案都不依賴預(yù)編譯,分別實(shí)現(xiàn)了 html、css、js 模塊化,相信這就是未來(lái)。

          模塊化標(biāo)準(zhǔn)推進(jìn)速度仍然緩慢

          2015 年提出的標(biāo)準(zhǔn),在 17 年依然沒(méi)有得到實(shí)現(xiàn),即便在 nodejs 端。

          這幾年 TC39 對(duì)語(yǔ)言終于重視起來(lái)了,慢慢有動(dòng)作了,但針對(duì)模塊標(biāo)準(zhǔn)制定的速度,與落實(shí)都非常緩慢,與 javascript 越來(lái)越流行的趨勢(shì)逐漸脫節(jié)。nodejs 至今也沒(méi)有實(shí)現(xiàn) ES2015 模塊化規(guī)范,所有 jser 都處在構(gòu)建工具的陰影下。

          Http 2.0 對(duì) js 模塊化的推動(dòng)

          js 模塊化定義的再美好,瀏覽器端的支持粒度永遠(yuǎn)是瓶頸,http 2.0 正是考慮到了這個(gè)因素,大力支持了 ES 2015 模塊化規(guī)范。

          幸運(yùn)的是,模塊化構(gòu)建將來(lái)可能不再需要。隨著 HTTP/2 流行起來(lái),請(qǐng)求和響應(yīng)可以并行,一次連接允許多個(gè)請(qǐng)求,對(duì)于前端來(lái)說(shuō)宣告不再需要在開(kāi)發(fā)和上線時(shí)再做編譯這個(gè)動(dòng)作。

          幾年前,模塊化幾乎是每個(gè)流行庫(kù)必造的輪子(YUI、Dojo、Angular),大牛們自己爽的同時(shí)其實(shí)造成了社區(qū)的分裂,很難積累。有了 ES2015 Modules 之后,JS 開(kāi)發(fā)者終于可以像 Java 開(kāi)始者十年前一樣使用一致的方式愉快的互相引用模塊。

          不過(guò) ES2015 Modules 也只是解決了開(kāi)發(fā)的問(wèn)題,由于瀏覽器的特殊性,還是要經(jīng)過(guò)繁瑣打包的過(guò)程,等 Import,Export 和 HTTP 2.0 被主流瀏覽器支持,那時(shí)候才是徹底的模塊化。

          Http 2.0 后就不需要構(gòu)建工具了嗎?

          看到大家基本都提到了 HTTP/2,對(duì)這項(xiàng)技術(shù)解決前端模塊化及資源打包等工程問(wèn)題抱有非常大的期待。很多人也認(rèn)為 HTTP/2 普及后,基本就沒(méi)有 Webpack 什么事情了。

          不過(guò) Webpack 作者 @sokra 在他的文章 webpack & HTTP/2 里提到了一個(gè)新的 Webpack 插件 AggressiveSplittingPlugin。簡(jiǎn)單的說(shuō),這款插件就是為了充分利用 HTTP/2 的文件緩存能力,將你的業(yè)務(wù)代碼自動(dòng)拆分成若干個(gè)數(shù)十 KB 的小文件。后續(xù)若其中任意一個(gè)文件發(fā)生變化,可以保證其他的小 chunck 不需要重新下載。

          可見(jiàn),即使不斷的有新技術(shù)出現(xiàn),也依然需要配套的工具來(lái)將前端工程問(wèn)題解決方案推向極致。

          模塊化是大型項(xiàng)目的銀彈嗎?

          只要遵循了最新模塊化規(guī)范,就可以使項(xiàng)目具有最好的可維護(hù)性嗎? Js 模塊化的目的是支持前端日益上升的復(fù)雜度,但絕不是唯一的解決方案。

          分析下 JavaScript 為什么沒(méi)有模塊化,為什么又需要模塊化:這個(gè) 95 年被設(shè)計(jì)出來(lái)的時(shí)候,語(yǔ)言的開(kāi)發(fā)者根本沒(méi)有想到它會(huì)如此的大放異彩,也沒(méi)有將它設(shè)計(jì)成一種模塊化語(yǔ)言。按照文中的說(shuō)法,99 年也就是 4 年后開(kāi)始出現(xiàn)了模塊化的需求。如果只有幾行代碼用模塊化是扯,初始的 web 開(kāi)發(fā)業(yè)務(wù)邏輯都寫(xiě)在 server 端,js 的作用小之又小。而現(xiàn)在 spa 都出現(xiàn)了,幾乎所有的渲染邏輯都在前端,如果還是沒(méi)有模塊化的組織,開(kāi)發(fā)過(guò)程會(huì)越來(lái)越難,維護(hù)也是更痛苦。

          本文中已經(jīng)詳細(xì)說(shuō)明了模塊化的發(fā)展和優(yōu)劣,這里不準(zhǔn)備做過(guò)多的贅述。但還有一個(gè)問(wèn)題需要我們?nèi)リP(guān)注,那就是在模塊化之后還有一個(gè)模塊間耦合的問(wèn)題,如果模塊間耦合度大也會(huì)降低代碼的可重用性或者說(shuō)復(fù)用性。所以也出現(xiàn)了降低耦合的觀察者模式或者發(fā)布/訂閱模式。這對(duì)于提升代碼重用,復(fù)用性和避免單點(diǎn)故障等都很重要。說(shuō)到這里,還想順便提一下最近流行起來(lái)的響應(yīng)式編程(RxJS),響應(yīng)式編程中有一個(gè)很核心的概念就是 observable,也就是 Rx 中的流(stream)。它可以被 subscribe,其實(shí)也就是觀察者設(shè)計(jì)模式。

          總結(jié)

          未來(lái)前端復(fù)雜度不斷增加已成定論,隨著后端成熟,自然會(huì)將焦點(diǎn)轉(zhuǎn)移到前端領(lǐng)域,而且服務(wù)化、用戶體驗(yàn)越來(lái)越重要,前端體驗(yàn)早不是當(dāng)初能看就行,任何網(wǎng)頁(yè)的異常、視覺(jué)的差異,或文案的模糊,都會(huì)導(dǎo)致用戶流失,支付中斷。前端對(duì)公司營(yíng)收的影響,漸漸與后端服務(wù)宕機(jī)同等嚴(yán)重,所以前端會(huì)越來(lái)越重,異常監(jiān)控,性能檢測(cè),工具鏈,可視化等等都是這幾年大家逐漸重視起來(lái)的。

          我們?cè)缫巡荒軐?javascript 早期玩具性質(zhì)的模塊化方案用于現(xiàn)代越來(lái)越重要的系統(tǒng)中,前端界必然出現(xiàn)同等重量級(jí)的模塊化管理方案,感謝 TC39 制定的 ES2015 模塊化規(guī)范,我們已經(jīng)離不開(kāi)它,哪怕所有人必須使用 babel。

          話說(shuō)回來(lái),標(biāo)準(zhǔn)推進(jìn)的太慢,我們還是把編譯工具當(dāng)作常態(tài),抱著哪怕支持了 ES2015 所有特性,babel 依然還有用的心態(tài),將預(yù)編譯進(jìn)行到底。一句話,模塊化仍在路上。js 模塊化的矛頭已經(jīng)對(duì)準(zhǔn)了 css 與 html,這兩位元老也該向前衛(wèi)的 js 學(xué)習(xí)學(xué)習(xí)了。

          未來(lái) css、html 的模塊化會(huì)自立門戶,還是賦予 js 更強(qiáng)的能力,讓兩者的模塊化依附于 js 的能力呢?目前 html 有自立門戶的苗頭(htmlModules),而 css 遲遲沒(méi)有改變,社區(qū)出現(xiàn)的 styled-component 已經(jīng)用 js 將 css 模塊化得很好了,最新 css 規(guī)范也支持了與 js 的變量通信,難道希望依附于 js 嗎?這里希望得到大家更廣泛的討論。

          我也認(rèn)同,畢竟壓縮、混淆、md5、或者利用 nonce 屬性對(duì) script 標(biāo)簽加密,都離不開(kāi)本地構(gòu)建工具。

          據(jù)說(shuō) http2 的優(yōu)化中,有個(gè)最佳文件大小與數(shù)量的比例,那么還是脫離不了構(gòu)建工具,前端未來(lái)會(huì)越來(lái)越復(fù)雜,同時(shí)也越來(lái)越美好。

          至此,對(duì)于 javascript 模塊化討論已接近尾聲,對(duì)其優(yōu)缺點(diǎn)也基本達(dá)成了一致。前端復(fù)雜度不斷提高,促使著模塊化的改進(jìn),代理(瀏覽器、node) 的支持程度,與前端特殊性(流量、緩存)可能前端永遠(yuǎn)也離不開(kāi)構(gòu)建工具,新的標(biāo)準(zhǔn)會(huì)讓這些工作做的更好,同時(shí)取代、增強(qiáng)部分特征,前端的未來(lái)是更加美好的,復(fù)雜度也更高。


          主站蜘蛛池模板: 亚洲av成人一区二区三区观看在线| 国产精品无码一区二区三区免费| 亚洲夜夜欢A∨一区二区三区| 秋霞午夜一区二区| 日韩精品乱码AV一区二区| 中文字幕乱码一区久久麻豆樱花| 人妻体体内射精一区二区| 亚洲狠狠久久综合一区77777 | 麻豆一区二区三区蜜桃免费| 一区二区三区四区视频在线| 无码日韩精品一区二区三区免费| 日韩一区二区三区四区不卡| 精品一区二区三区无码视频| 性色AV一区二区三区| 亚洲一区中文字幕在线电影网| 人妻久久久一区二区三区| 国产一区二区三区高清视频 | 一区二区三区免费看| 无码国产精品一区二区免费| 无码人妻品一区二区三区精99| 精品一区二区三区无码免费直播| 亚洲一区二区三区在线观看网站| 国产精华液一区二区区别大吗| 精品国产乱码一区二区三区| 四虎一区二区成人免费影院网址| 中文字幕日韩欧美一区二区三区 | 亚洲AV无码一区二区三区DV| 视频一区二区中文字幕| 国产精品无码一区二区三区免费 | 精品国产一区二区三区av片| 中文字幕一区二区三区在线观看| 亚洲一区AV无码少妇电影☆| 国产伦精品一区二区免费| 国产福利电影一区二区三区,亚洲国模精品一区 | 国产色综合一区二区三区| 亚洲国产成人久久一区久久| 国产成人精品久久一区二区三区av| 亚洲AV日韩综合一区| 国产精品亚洲一区二区三区| 日本中文一区二区三区亚洲| 精品成人av一区二区三区|