Webpack-CodeSplit(静态文件篇)
何为CodeSplitting?
webpack从入口文件开始遍历,找到所有依赖文件,然后打包成最终的一个文件,即bundle.js文件,这是我们经常使用的方式,当一个项目慢慢变得复杂的时候会导致这个bundle.js文件越来越大,浏览器加载的速度也会越来越慢,这个过程还不排除我们需要引用的第三方文件,这样每次无论是构建,还是浏览器加载这个最终文件,都会存在效率问题,webpack提供了codesplitting功能来解决这个问题,这可以最大限度的减少浏览器加载必要代码时间(比如首屏渲染优化)。这个过程我们可以分为两种情况来讨论,第三方的分为静态文件处理,浏览器加载必要文件作为动态文件处理(按需加载,懒加载)
具体方案参考“webpack分离第三方解决方案“,这里我们仅仅对optimization.splitChunks分离进行阐述。
先看一个简单例子
demo.js
import $ from "jquery";
webpack.js
module.exports = { mode: "development", entry: { app: "./demo.js", }, output: { path: path.resolve(__dirname, "./build/"), filename: "[name]-[chunkhash].js", }, devtool: "source-map", }
构建结果
添加chunk,并指定加载第三方类库
entry: { app: "./demo.js", vendor: ["jquery"], },
构建结果
然后会发现,vendor跟app中均出现了第三方的代码,明显不能满足我们的需求,改进如下
optimization: { splitChunks: { chunks: "all", cacheGroups: { default: false, vendors: false, vendors: { test: /[\\/]node_modules[\\/]/, name: "vendor", priority: -10, chunks: "all", reuseExistingChunk: true, enforce: true, }, }, } },
这里通过正则从node_modules里面获取第三方类库,然后通过业务代码检测引用过哪些第三方,最后将引用的第三方文件打包到vendor.js中
构建结果
app文件结构
(function(modules) { // webpackBootstrap /******/ // install a JSONP callback for chunk loading /******/ function webpackJsonpCallback(data) { /******/ var chunkIds = data[0]; /******/ var moreModules = data[1]; /******/ var executeModules = data[2]; /******/ /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, resolves = []; /******/ for(;i < chunkIds.length; i++) { /******/ chunkId = chunkIds[i]; /******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { /******/ resolves.push(installedChunks[chunkId][0]); /******/ } /******/ installedChunks[chunkId] = 0; /******/ }
vendor文件结构
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["vendor"],{ /***/ "taue": /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; ( function( global, factory ) { "use strict"; if ( true && typeof module.exports === "object" ) { module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
可以看出将第三方跟运行时代码打到了一起,网上查看以前webpack版本会发现,如果修改业务逻辑,会导致运行代码改变,从而改变打包出来的vendor.js,但是在这里我试过,并没有发生改变,而且查看运行时的代码,在业务逻辑中,并不在vendor中。
原因有三种(查明原因更新此文章)
1.webpack本身对运行代码做了优化,降低了运行代码与加载类库的耦合性从而保证了运行代码的独立性。(可能性很大)
2.类库的挂载形式不同导致了运行时不一样(可能性不大)
3.demo写的有局限性
具体原因等查明再更新该文章。在这里留个疑问。
然后我们如何避免运行时代码的改变,我们要提取运行时的代码,改进如下
optimization: { splitChunks: { chunks: "all", cacheGroups: { default: false, vendors: false, vendors: { test: /[\\/]node_modules[\\/]/, name: "vendor", priority: -10, chunks: "all", reuseExistingChunk: true, enforce: true, }, }, }, runtimeChunk: { name: "manifest", }, }
构建结果
我们可以检查下manifest.js代码
(function(modules) { // webpackBootstrap /******/ // install a JSONP callback for chunk loading /******/ function webpackJsonpCallback(data) { /******/ var chunkIds = data[0]; /******/ var moreModules = data[1]; /******/ var executeModules = data[2]; /******/ /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, resolves = [];
保留了运行时的代码
查看业务代码
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["app"],{ /***/ "fhko": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "taue"); /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
已经没有了运行时的代码。
总结
到这里其实我们已经看到了webpack加载模块代的方式了。大致流程就是将模块代码放入了window["webpackJsonp"],window["webpackJsonp"]是数组类型,数组中的每一项为一个chunk文件
chunk又包含了chunk[0]依赖文件名(可多个),chunk文件内容。然后运行文件也就是manifest遍历这个数组,然后挨个执行目标函数,我们在回忆下怎么调用的。
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); /******/ jsonpArray.push = webpackJsonpCallback; /******/ jsonpArray = jsonpArray.slice(); /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); /******/ var parentJsonpFunction = oldJsonpFunction;
好了针对静态的code split的总结就这些,具体细节可以查看官方文档,别看中文的,已经不更新了。关于webpack如何运行的,以后整理。