最近因爲一篇分享手淘過年項目中採用到的前端技術的影響,從新研究了一下項目中CSS的架構
.原本打算寫一篇文章,可是寫到一半忽然發現本身像在寫文檔介紹同樣,因此後來就放棄了。可是以爲過程當中研究的 Webpack
卻是能夠單獨拿出來說一講css
在這裏很是感謝 印記中文 團隊翻譯的 Webpack 文檔.
// Webpack 4.0 const htmlPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", entry: "./src/index.js", output: { filename: "[name].js", path: __dirname + "/dist" }, module: { rules: [ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", }, ] } ] }, plugins: [ new htmlPlugin({ title: "Test Webpack", filename: "index.html" }) ] };
一個基本的配置就搭建好了,詳細的配置內容我就不介紹了, 而後咱們在 src/index.js
上面寫咱們的測試代碼, 在 dist/main.js
看一下 webpack
實現的原理,那麼目前咱們的項目結構是這樣子的html
|-- project |-- dist |-- src |-- index.js |-- node_modules |-- webpack.config.js
在進入按需加載的講解以前,咱們須要看一個問題 require
和 import
在 webpack
的執行過程是怎樣的呢 ?如今咱們在 src
創建兩個文件 index.js
、module-es6.js
和 module-commonjs.js
。咱們經過這三個文件解析 require
和 import
的執行過程前端
首先咱們要區分的是 CommonJS
和 ES6
模塊導出之間的區別,在 CommonJS
中你導出模塊方式是改變 module.exports
,可是對於 ES6
來講並不存在 module
這個變量,他的導出方式是經過一個關鍵詞 export
來實現的。在咱們書寫 JS
文件的時候,咱們發現不管是以 CommomJS
仍是 ES6
的形式導出均可以實現,這是由於 Webpack
作了一個兼容處理node
咱們創建一個小 DEMO 來查看一下,咱們如今上面創建的三個文件的代碼以下webpack
// index.js // import moduleDefault, { moduleValue } from "./module-es6.js"; // import moduleDefault, { moduleValue1, moduleValue2 } from "./module-commanjs.js";
// module-es6.js export let moduleValue = "moduleValue" //ES6模塊導出 export default "ModuleDefaultValue"
// module-commonjs.js exports.moduleValue1 = "moduleValue1" exports.moduleValue2 = "moduleValue2"
如今咱們打開 index.js
中加載 module-commonjs.js
的代碼,首先會先給當前模塊打上 ES6
模塊的標識符,在 index
則會產生兩個變量 A
和 B
. A
保存 module-commonjs
的導出的結果,B
則是兼容 CommonJs
中沒有 ES6
經過 export default
導出的結果,其值跟 A
同樣. 用B
來兼容 export default
的結果es6
而後咱們從新註釋代碼,再打開 index.js
中加載 module-es6.js
的代碼web
此次和上面同樣會先給當前模塊打上 ES6
模塊的標識符,而後去加載 module-es6
,獲取他的導出值。可是瀏覽器是不識別 export
這個關鍵詞的因此 Webpack
會對的代碼進行解釋,首先給 module.exports
設定導出的值,若是是 export default
會直接賦值給 module.exports
,若是是其餘形式,則給module.exports
的導出的key
設定一個 getter
,該 getter
的返回值就是導出的結果npm
而對於require
來講整個執行過程其實過程和import
是同樣的。數組
對於 webpack
來講只要你使用了 import
或者 export
等關鍵字, 他就會給 module.exports
添加一個__esModule : true
來識別這是一個 ES6
的模塊,經過這個值來作一些特殊處理promise
若是以爲我上面講的不太明白 那能夠看看下面這些代碼
let commonjs = { "./src/index.js": function(module, __webpack_exports__, __webpack_require__) { "use strict"; //給當前模塊打上 `ES6`模塊的標識符 __webpack_require__.r(__webpack_exports__); //給當前模塊打上 `ES6`模塊的標識符 // 執行 ./src/module-commonjs.js 的代碼 獲取導出值 var A = __webpack_require__("./src/module-commonjs.js"); // 根據 ./src/module-commonjs.js 是否爲ES6模塊 給返回值增長不一樣的 getter函數 var B = __webpack_require__.n(A); }, "./src/module-commonjs.js": function(module, exports) { exports.moduleValue1 = "moduleValue1"; exports.moduleValue2 = "moduleValue2"; } }; let es6 = { "./src/index.js": function(module, __webpack_exports__, __webpack_require__) { "use strict"; //給當前模塊打上 `ES6`模塊的標識符 __webpack_require__.r(__webpack_exports__); // 執行 ./src/module-commonjs.js 的代碼 獲取導出值 var A = __webpack_require__("./src/module-es6.js"); }, "./src/module-es6.js": function(module, __webpack_exports__, __webpack_require__) { //給當前模塊打上 `ES6`模塊的標識符 __webpack_require__.r(__webpack_exports__); // 設置 __webpack_exports__.moduleValue 的 getter __webpack_require__.d(__webpack_exports__, "moduleValue", function() { return moduleValue;z }); __webpack_exports__["default"] = "ModuleDefaultValue"; let moduleValue = "moduleValue"; } };
看完上面的 require
和 import
,咱們回到 按需加載
這個執行過程. webpack
的按需加載是經過 import()
或者 require.ensure()
來實現的,有些讀者可能對於 require.ensure
比較熟悉,因此咱們先看看 require.ensure
的執行過程,
如今咱們修改創建一個 module-dynamic.js
文件,而後修改 index.js
文件
這裏吐槽一個問題,require.ensure 第一個參數是一個尷尬的存在,寫和不寫根本沒差,若是你填了的這個參數,webpack 會幫你把文件加載近來,可是不執行。一堆不執行的代碼是沒有意義的,你想讓他執行就必須 require() 一遍,可是執行力 require 也會幫你加載文件。因此根本沒差
// index.js setTimeout(function() { require.ensure([], function() { let d = require("./module2") }); }, 1000); // module2.js module.exports = { name : "Jason" }
執行 require.ensure(dependencies,callback,errorCallback,chunkName)
實際上會返回一個 promise
, 裏面的實現邏輯是 先判斷 dependencies
是否已經被加載過,若是加載過則取緩存值的 promise
, 若是沒有被加載過 則生成一個 promise
並將 promise
裏面的 resolve
,reject
和 promise
自己 存入一個數組,而後緩存起來.接着生成一個 script
標籤,填充完信息以後添加到HTML
文件上,其中的 script
的 src
屬性 就是咱們按需加載的文件(module2
),webpack
會對這個 script
標籤監聽 error
和 load
時間,從而作相應的處理。
webpack
打包過程當中會給 module2
添加一些代碼,主要就是主動觸發 window["webpackJsonp"].push
這個函數,這個函數會傳遞
兩個參數 文件ID
和 文件內容對象
,其中 文件標示
若是沒有配置的話,會按載入序號自動增加,文件內容對象
實際上就是上文說的 require.ensure
第一個參數dependencies
的文件內容,或者是 callback
,errorCallback
裏面須要加載的文件,以 key(文件路徑) --- value(文件內容)
的形式出現.裏面執行的事情其實就是執行上面建立的promise
的resolve
函數,讓require.ensure
裏面的callback
執行,以後的執行狀況就跟我上面將 requir
和 import
同樣了
固然其實講了那麼長的 require.ensure
並無什麼用,由於這個函數已經被 import()
取代了,可是考慮到以前的版本應該有不少人都是用 require.ensure
方法去加載的,因此仍是講一下,並且其實 import
的執行過程跟 require.ensure
是同樣的,只不過用了更友好的語法而已,因此關於 import
的執行流程我也沒啥好講的了,感興趣的人看一下二者的 API
介紹就行了。
到這裏就正式講完了,若是有大牛路過看到有不對的地方,但願能幫我指出來.很是謝謝!!!
而後再次感謝印記中文 團隊翻譯的 Webpack 文檔