這篇文章討論的是為什麼Web模組化是很有用的,並介紹了現在可以用來實現Web模組化的一些機制。這裡有另一篇文章介紹了RequireJS所使用的函數包裝格式的設計概念。
問題§1
網站逐漸轉化為Web apps
程式碼複雜度逐漸提高
組裝變的困難
開發者想要分離的JSHT/模組
組裝變的困難開發者想要分離的JSHT/模組組裝請求
解決方案§2
前端開發者需要這樣的解決方案:
一些這類的API #include/import/require
有能力加載的依賴
並且有最佳化工具在後面支持,有助於部署
腳本載入API § 3
首先梳理出腳本載入API。這裡有幾個選擇:
Dojo: dojo.require("some.module") LABjs: $LAB.script("some/module.js") CommonJS: require("some/module")
所有的都對應到載入 some/path/some/module.js。理想情況下,我們可以選擇CommonJS的語法,因為它很可能會變得更加常見,而且我們想要重用程式碼。
目前我們也希望一些語法能夠載入已存在的純文字JavaScript文件,因此開發者不用重寫所有的JavaScript來從腳本載入中獲益。
但是,我們需要一些能在瀏覽器中更好的工作的事物。 CommonJS 的require()是一個同步調用,它期望能夠立即返回那個模組。不過這在瀏覽器中運作的不是很好。
異步與同步§ 4
var Employee = require("types/Employee");function Manager () { this.reports = []; }//Error if require call is asyncManager.prototype = new Employee();
如上面註釋中所示,如果require()是異步的,這段程式碼不會運作。但是,在瀏覽器中同步載入腳步將會抹殺效能。那麼,怎麼辦?
腳本載入:XHR§ 5
使用XMLHttpRequest(XHR)載入腳本是很有吸引力的。如果使用XHR,我們就可以觸摸上面的文本,也就是可以透過正規表示式來查找require()調用,以確保我們載入了這些腳本,然後再用eval()或script元素將文本內容傳給使用XHR載入的腳本。
使用eval()來評估模組不太好:
開發者已經被告知eval()不好用。
有些環境不支援eval()。
難以調試。 Firebug和WebKit的檢查器有一個//@ sourceURL= 約定,用來為被評估的文字命名,不過這個特性不是所有的瀏覽器都支援。
不同的瀏覽器評估情境環境是不同的。 IE中的execScript或許可以做到,但同時也意味著更多的移動部件。
使用帶有文字內容的script標籤來設定為檔案文字也不太好:
調試的時候,你得到的錯誤行號和來源檔案對不上號。
XHR 在跨域請求的時候還有問題。有些瀏覽器現在有跨域XHR的支持,但不是全部。並且 IE 決定建立一個不同的API物件:XDomainRequest來實作跨域請求。出現了更多的需要改變的地方,更容易出錯。特別是,你需要確定不發送任何不標準的HTTPheader或還需要另外一個"預檢"的請求來保證這次跨域的請求是被允許的。
Dojo 透過eval()使用基於XHR的loader,但是,雖然它能用,但是一直是困擾開發者的源頭。 Dojo 有一個 xdomain loader但是它需要透過使用一個函數wrapper來修改require的模組,所以script src=""標籤可以用來載入模組了。還有很多邊界情況和變化的地方來給程式設計師增加困難。
如果我們建立一個新的腳本載入器,我們可以做的更好。
腳本載入:Web Workers § 6
web worker可能是另一個載入腳本的方法,但是:
它的跨平台性不好
它是一個訊息
它的跨平台性不好
🎜它是一個訊息傳遞API,並且該腳本可能要與MDOMDO交互,它只是使用worker獲取腳本的文本,然後將文本回傳給主窗口,然後用eval/script來執行腳本。這種方法帶有上面提到的XHR的全部問題。 🎜🎜 腳本載入:document.write()§ 7🎜🎜 document.write()可以用來載入腳本,它可以從其他的網域載入腳本並且映射了瀏覽器通常是如何使用腳本的,因此它可以用來進行簡單的調試。 🎜🎜 但是,在非同步VS同步的例子中,我們不能直接執行腳本。理想情況下,在執行腳本之前我們能夠透過require()知道相關依賴項,並且確保這些依賴項被先載入。但是我們不能在腳本執行之前訪問它。 🎜而且,document.write()在页面载入后就不工作了。对于你的网站,一个好的方法是在用户需要进行下一步操作时来载入脚本。
最后,通过document.write()载入脚本或阻塞页面的渲染。要让你的网站有最佳表现,这个方法是不可取的。
脚本载入:head.appendChild(script)§ 8
我们可以在需要时创建脚本并将它们添加到头部:
var head = document.getElementsByTagName('head')[0], script = document.createElement('script'); script.src = url; head.appendChild(script);
上面的脚本片段多了一点东西,不过那正是基本的思想。这种方法比document.write要好,因为它不会阻塞页面的渲染并且在页面载入后仍能工作。
但是,它仍然有同步VS异步例子的问题:理想情况下,在执行脚本前我们能够通过require()知道相关依赖项,并且确保这些依赖项被首先载入。
函数封装 § 9
在执行我们的脚本前,我们需要知道相关依赖项并确保已经将其载入。做这件事的最好方法是通过函数封装来构造我们的模块载入API。像这样:
define( //The name of this module "types/Manager", //The array of dependencies ["types/Employee"], //The function to execute when all dependencies have loaded. The //arguments to this function are the array of dependencies mentioned //above. function (Employee) { function Manager () { this.reports = []; } //This will now work Manager.prototype = new Employee(); //return the Manager constructor function so it can be used by //other modules. return Manager; } );
这是ReguireJS的句法。如果你想载入没有定义成模块的纯文本的JavaScript的话,有一种简单的句法:
require(["some/script.js"], function() { //This function is called after some/script.js has loaded. });
选择这种句法是因为,它足够简洁并且允许载入者使用head.appendChild(script)载入类型。
出于在浏览器中良好工作的需要,它有不同于普通的CommonJS句法。有建议说普通的CommonJS句法可以使用head.appendChild(script)的载入类型,如果服务器进程有封装的函数可以将模块转换成传输格式的话。
我相信不强制使用一个运行时服务器进程来转换代码是很重要的事:
一是调试变的很怪异,因为服务器在注入封装函数时会导致源文件的行号关闭。
二是需要做更多的工作。前端开发应该尽可能的使用静态文件。
关于设计的力量和功能封装格式的使用案例的更多细节,被叫做异步模块定义(Asynchronous Module Definition (AMD)),请前往为什么是AMD?
原文地址:http://www.php.cn/website-design-ask-340211.html
以上就是为什么要web网页模块化?的内容,更多相关内容请关注PHP中文网(www.php.cn)!