不廢話看看官方怎麼談5,Webpack5的新特性javascript
安裝webpack(v5)威武版,不用用怎麼知道他有多好用?html
npm init -y npm i webpack@next --save-dev
能夠直接經過@next方式來安裝webpack5版本,目前版本是"^5.0.0-alpha.23"
先來看下基本結構java
├── bootstrap.js // 手動啓動webpack ├── pack.js // 本身實現的webpack ├── package-lock.json ├── package.json ├── README.md ├── src │ ├── a.js // 入口文件會引用 a.js │ └── index.js // 打包的入口文件 └── webpack.config.js // webpack配置文件
a.js只是導出個變量而已,很是的簡單webpack
module.exports = 'webyouxuan'
index.js負責引入a.js模塊web
let webyouxuan= require('./a'); console.log(webyouxuan)
webpack.config文件算法
const path = require('path'); module.exports = { mode:'development', entry:'./src/index.js', output:{ path:path.join(__dirname,'dist'), filename:'main.js' } }
你會發現和webpack4的配置基本沒有變化
開始打包,你會發現npx webpack 須要webpack-cli的支持,比較尷尬的是,到目前尚未與之匹配的webpack-cli,沒辦法啦,咱們只好手動啓動webpack了~~~npm
bootstrap.js 引入webpack進行打包項目json
const webpack = require('webpack'); const webpackOptions = require('./webpack.config'); // 須要將 配置文件傳入到webpack中,打包成功後咱們打印stats信息 webpack(webpackOptions,(err,stats)=>{ if(err){ console.log(err); }else{ console.log(stats.toJson()) } })
看下打包出來的信息:
bootstrap
咱們須要掌握一些關鍵詞:數組
發現比webpack4,打包出來的結果確實少了很多!更簡潔,更容易讀懂(這裏我已把註釋刪掉)。
// 2.總體函數是個自執行函數 ((modules) => { // 3.module傳入的爲全部打包後的結果 var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) { // 作緩存的能夠先不理 return installedModules[moduleId].exports; } var module = (installedModules[moduleId] = { // 5.建立模塊,每一個模塊都有一個exports對象 i: moduleId, l: false, exports: {} }); modules[moduleId](module, module.exports, __webpack_require__); // 6.調用對應的模塊函數,將模塊exports傳入 module.l = true; // 8.用戶會將結果放到module.exports對象上 return module.exports; } function startup() { // 經過入口開始加載 return __webpack_require__("./src/index.js"); // 默認返回的是 module.exports結果; } return startup(); // 4.啓動加載 })({ // 1.列出打包後的模塊 "./src/a.js": module => { eval( "module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?" ); }, "./src/index.js": (__unusedmodule, __unusedexports, __webpack_require__) => { // 7.加載其餘模塊,拿到其餘模塊的module.exports結果 eval( 'let webyouxuan = __webpack_require__(/*! ./a */ "./src/a.js");\nconsole.log(webyouxuan)\n\n//# sourceURL=webpack:///./src/index.js?' ); } });
總的來講不難理解,其實仍是內部實現了個___webpack_require__
方法,若是看不懂就多來幾遍,看懂了發現也沒什麼。。。
這樣咱們能夠直接把main.js在html中直接引入啦~,發現是否是已經能夠打印出webyouxuan啦,順便作個廣告,關注咱們!持續推送精品文章,給你點個贊👍
咱們在index入口文件中,採用 import語法動態導入文件
const button = document.createElement('button'); button.innerHTML = '關注 webyouxuan'; document.body.appendChild(button); document.addEventListener('click',()=>{ import('./a').then(data=>{ console.log(data.default); }) });
再回頭看編譯的結果,貌似好像打包出來的結果有些複雜啦,不要緊!其實核心很簡單就是個jsonp加載文件。
打包出來的結果多了個src_a_js.main.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([ ["src_a_js"], { "./src/a.js": module => { eval( "module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?" ); } } ]);
最終會經過script標籤加載這個文件,加載後默認會調用 window下的 webpackJsonp的push方法
咱們來看看 main.js 發生了哪些變化,話說有代碼些小複雜,分段來看
先來看index模塊作了那些事,爲了看着方便我來把代碼整理一下
"./src/index.js": ((__unusedmodule, __unusedexports, __webpack_require__) => { eval(` const button = document.createElement('button'); button.innerHTML = '關注 webyouxuan'; document.body.appendChild(button); document.addEventListener('click',()=>{ __webpack_require__.e("src_a_js").then( __webpack_require__.t.bind(__webpack_require__, "./src/a.js", 7)).then(data=>{ console.log(data.default); }) }) `); })
這裏出現了兩個方法 __webpack_require__.e
和__webpack_require__.t
__webpack_require__.e
方法看似是用來加載文件的,我們來找一找
__webpack_require__.f = {}; __webpack_require__.e = (chunkId) => { // chunkId => src_a_js動態加載的模塊 return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { __webpack_require__.f[key](chunkId, promises); // 調用j方法 將數組傳入 return promises; }, [])); }; var installedChunks = { "main": 0 // 默認main已經加載完成 }; // f方法上有個j屬性 __webpack_require__.f.j = (chunkId, promises) => { var installedChunkData = Object.prototype.hasOwnProperty.call(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; if(installedChunkData !== 0) { // 默認installedChunks確定沒有要加載的模塊 if(installedChunkData) { promises.push(installedChunkData[2]); } else { if(true) { // 建立一個promise 把當前的promise 成功失敗保存到 installedChunks var promise = new Promise((resolve, reject) => { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); // installedChunks[src_a_js] = [resolve,reject,promise] promises.push(installedChunkData[2] = promise); // 這句的意思是看是否配置publicPath,配置了就加個前綴 var url = __webpack_require__.p + __webpack_require__.u(chunkId); // 1)建立script標籤 var script = document.createElement('script'); var onScriptComplete; script.charset = 'utf-8'; script.timeout = 120; script.src = url; // 2)開始加載這個文件 var error = new Error(); onScriptComplete = function (event) { // 完成工做 script.onerror = script.onload = null; clearTimeout(timeout); var reportError = loadingEnded(); if(reportError) { var errorType = event && (event.type === 'load' ? 'missing' : event.type); var realSrc = event && event.target && event.target.src; error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; error.name = 'ChunkLoadError'; error.type = errorType; error.request = realSrc; reportError(error); } }; var timeout = setTimeout(function(){ // 超時工做 onScriptComplete({ type: 'timeout', target: script }); }, 120000); script.onerror = script.onload = onScriptComplete; document.head.appendChild(script); // 3)將標籤插入到頁面 } else installedChunks[chunkId] = 0; } } };
雖然代碼量比較多,其實核心就幹了一件事 : 建立script標籤,文件加載回來那確定就會調用push方法咯!
先跳過這段看下面的
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([ ["src_a_js"], { "./src/a.js": module => { eval( "module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?" ); } } ]);
function webpackJsonpCallback(data) { // 3) 文件加載後會調用此方法 var chunkIds = data[0]; // data是什麼來着,你看看src_a_js怎麼寫的你就知道了 看上面! ["src_a_js"] var moreModules = data[1]; // 獲取新增的模塊 var moduleId, chunkId, i = 0, resolves = []; for(;i < chunkIds.length; i++) { chunkId = chunkIds[i]; if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { // installedChunks[src_a_js] = [resolve,reject,promise] 這個是上面作的 // 很好理解 其實就是取到剛纔放入的promise的resolve方法 resolves.push(installedChunks[chunkId][0]); } installedChunks[chunkId] = 0; // 模塊加載完成 } for(moduleId in moreModules) { // 將新增模塊與默認的模塊進行合併 也是就是modules模塊,這樣modules中就多了動態加載的模塊 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { __webpack_require__.m[moduleId] = moreModules[moduleId]; } } if(runtime) runtime(__webpack_require__); if(parentJsonpFunction) parentJsonpFunction(data); while(resolves.length) { // 調用promise的resolve方法,這樣e方法就調用完成了 resolves.shift()(); } }; var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; // 1) window["webpackJsonp"]等於一個數組 var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); jsonpArray.push = webpackJsonpCallback; // 2) 重寫了數組的push方法 var parentJsonpFunction = oldJsonpFunction;
加載的文件會調用 webpackJsonpCallback方法,內部就是將新增的模塊合併到modules上,而且讓__webpack_require__.e
完成
__webpack_require__.t = function(value, mode) { // t方法其實很簡單就是 if(mode & 1) value = this(value); // 就是調用__webpack_require__加載最新的模塊 };
這樣用戶就能夠拿到新增的模塊結果啦~~~,源碼雖難,可是多看幾遍總會有收穫!
到此咱們就將webpack5的懶加載功能整個過了一遍,其實思路和webpack4的懶加載同樣呢~,不過不得不說webpack5打包出來的代碼更加簡潔啦! 期待webpack5正式發版!!!