(給前端大全加星標(biāo),提升前端技能) 作者:前端森林 公號(hào) / 前端森林
引言webpack 的打包優(yōu)化一直是個(gè)老生常談的話題,常規(guī)的無非就分塊、拆包、壓縮等。
本文以我自己的經(jīng)驗(yàn)向大家分享如何通過一些分析工具、插件以及webpack 新版本中的一些新特性來顯著提升webpack 的打包速度和改善包體積,學(xué)會(huì)分析打包的瓶頸以及問題所在。 本文演示代碼,倉(cāng)庫(kù)地址:https://github.com/Jack-cool/webpack4 速度分析 ??webpack 有時(shí)候打包很慢,而我們?cè)陧?xiàng)目中可能用了很多的 plugin 和 loader ,想知道到底是哪個(gè)環(huán)節(jié)慢,下面這個(gè)插件可以計(jì)算 plugin 和 loader 的耗時(shí)。 yarn add -D speed-measure-webpack-plugin
配置也很簡(jiǎn)單,把 webpack 配置對(duì)象包裹起來即可: const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({ plugins: [ new MyPlugin(), new MyOtherPlugin() ] });
來看下在項(xiàng)目中引入speed-measure-webpack-plugin 后的打包情況: 從上圖可以看出這個(gè)插件主要做了兩件事情: - 分析每個(gè)插件和 loader 的耗時(shí)情況 知道了具體
loader 和plugin 的耗時(shí)情況,我們就可以“對(duì)癥下藥”了
體積分析 ??打包后的體積優(yōu)化是一個(gè)可以著重優(yōu)化的點(diǎn),比如引入的一些第三方組件庫(kù)過大,這時(shí)就要考慮是否需要尋找替代品了。 這里采用的是webpack-bundle-analyzer ,也是我平時(shí)工作中用的最多的一款插件了。 它可以用交互式可縮放樹形圖顯示webpack 輸出文件的大小。用起來非常的方便。 首先安裝插件: yarn add -D webpack-bundle-analyzer
安裝完在webpack.config.js 中簡(jiǎn)單的配置一下: const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = { plugins: [ new BundleAnalyzerPlugin({ // 可以是`server`,`static`或`disabled`。 // 在`server`模式下,分析器將啟動(dòng)HTTP服務(wù)器來顯示軟件包報(bào)告。 // 在“靜態(tài)”模式下,會(huì)生成帶有報(bào)告的單個(gè)HTML文件。 // 在`disabled`模式下,你可以使用這個(gè)插件來將`generateStatsFile`設(shè)置為`true`來生成Webpack Stats JSON文件。 analyzerMode: 'server', // 將在“服務(wù)器”模式下使用的主機(jī)啟動(dòng)HTTP服務(wù)器。 analyzerHost: '127.0.0.1', // 將在“服務(wù)器”模式下使用的端口啟動(dòng)HTTP服務(wù)器。 analyzerPort: 8866, // 路徑捆綁,將在`static`模式下生成的報(bào)告文件。 // 相對(duì)于捆綁輸出目錄。 reportFilename: 'report.html', // 模塊大小默認(rèn)顯示在報(bào)告中。 // 應(yīng)該是`stat`,`parsed`或者`gzip`中的一個(gè)。 // 有關(guān)更多信息,請(qǐng)參見“定義”一節(jié)。 defaultSizes: 'parsed', // 在默認(rèn)瀏覽器中自動(dòng)打開報(bào)告 openAnalyzer: true, // 如果為true,則Webpack Stats JSON文件將在bundle輸出目錄中生成 generateStatsFile: false, // 如果`generateStatsFile`為`true`,將會(huì)生成Webpack Stats JSON文件的名字。 // 相對(duì)于捆綁輸出目錄。 statsFilename: 'stats.json', // stats.toJson()方法的選項(xiàng)。 // 例如,您可以使用`source:false`選項(xiàng)排除統(tǒng)計(jì)文件中模塊的來源。 // 在這里查看更多選項(xiàng):https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21 statsOptions: null, logLevel: 'info' ) ] }
然后在命令行工具中輸入npm run dev ,它默認(rèn)會(huì)起一個(gè)端口號(hào)為 8888 的本地服務(wù)器: 圖中的每一塊清晰的展示了組件、第三方庫(kù)的代碼體積。 有了它,我們就可以針對(duì)體積偏大的模塊進(jìn)行相關(guān)優(yōu)化了。 多進(jìn)程/多實(shí)例構(gòu)建 ??大家都知道 webpack 是運(yùn)行在 node 環(huán)境中,而 node 是單線程的。webpack 的打包過程是 io 密集和計(jì)算密集型的操作,如果能同時(shí) fork 多個(gè)進(jìn)程并行處理各個(gè)任務(wù),將會(huì)有效的縮短構(gòu)建時(shí)間。 平時(shí)用的比較多的兩個(gè)是thread-loader 和HappyPack 。 先來看下thread-loader 吧,這個(gè)也是webpack4 官方所推薦的。 thread-loader
安裝yarn add -D thread-loader
thread-loader 會(huì)將你的 loader 放置在一個(gè) worker 池里面運(yùn)行,以達(dá)到多線程構(gòu)建。
?把這個(gè) loader 放置在其他 loader 之前(如下面示例的位置), 放置在這個(gè) loader 之后的 loader 就會(huì)在一個(gè)單獨(dú)的 worker 池(worker pool )中運(yùn)行。 ? 示例module.exports = { module: { rules: [ { test: /\.js$/, include: path.resolve('src'), use: [ 'thread-loader', // your expensive loader (e.g babel-loader) ] } ] } }
HappyPack安裝yarn add -D happypack
HappyPack 可以讓 Webpack 同一時(shí)間處理多個(gè)任務(wù),發(fā)揮多核 CPU 的能力,將任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)的執(zhí)行,子進(jìn)程處理完后,再把結(jié)果發(fā)送給主進(jìn)程。通過多進(jìn)程模型,來加速代碼構(gòu)建。
示例// webpack.config.js const HappyPack = require('happypack');
exports.module = { rules: [ { test: /.js$/, // 1) replace your original list of loaders with 'happypack/loader': // loaders: [ 'babel-loader?presets[]=es2015' ], use: 'happypack/loader', include: [ /* ... */ ], exclude: [ /* ... */ ] } ] };
exports.plugins = [ // 2) create the plugin: new HappyPack({ // 3) re-add the loaders you replaced above in #1: loaders: [ 'babel-loader?presets[]=es2015' ] }) ];
這里有一點(diǎn)需要說明的是,HappyPack 的作者表示已不再維護(hù)此項(xiàng)目,這個(gè)可以在github 倉(cāng)庫(kù)看到: 作者也是推薦使用webpack 官方提供的thread-loader 。 ?thread-loader 和 happypack 對(duì)于小型項(xiàng)目來說打包速度幾乎沒有影響,甚至可能會(huì)增加開銷,所以建議盡量在大項(xiàng)目中采用。 ? 多進(jìn)程并行壓縮代碼 ??通常我們?cè)陂_發(fā)環(huán)境,代碼構(gòu)建時(shí)間比較快,而構(gòu)建用于發(fā)布到線上的代碼時(shí)會(huì)添加壓縮代碼這一流程,則會(huì)導(dǎo)致計(jì)算量大耗時(shí)多。 webpack 默認(rèn)提供了UglifyJS 插件來壓縮JS 代碼,但是它使用的是單線程壓縮代碼,也就是說多個(gè)js 文件需要被壓縮,它需要一個(gè)個(gè)文件進(jìn)行壓縮。所以說在正式環(huán)境打包壓縮代碼速度非常慢(因?yàn)閴嚎sJS 代碼需要先把代碼解析成用Object 抽象表示的AST 語法樹,再應(yīng)用各種規(guī)則分析和處理AST ,導(dǎo)致這個(gè)過程耗時(shí)非常大)。
所以我們要對(duì)壓縮代碼這一步驟進(jìn)行優(yōu)化,常用的做法就是多進(jìn)程并行壓縮。 目前有三種主流的壓縮方案: parallel-uglify-plugin
上面介紹的HappyPack 的思想是使用多個(gè)子進(jìn)程去解析和編譯JS ,CSS 等,這樣就可以并行處理多個(gè)子任務(wù),多個(gè)子任務(wù)完成后,再將結(jié)果發(fā)到主進(jìn)程中,有了這個(gè)思想后,ParallelUglifyPlugin 插件就產(chǎn)生了。 當(dāng)webpack 有多個(gè)JS 文件需要輸出和壓縮時(shí),原來會(huì)使用UglifyJS 去一個(gè)個(gè)壓縮并且輸出,而ParallelUglifyPlugin 插件則會(huì)開啟多個(gè)子進(jìn)程,把對(duì)多個(gè)文件壓縮的工作分給多個(gè)子進(jìn)程去完成,但是每個(gè)子進(jìn)程還是通過UglifyJS 去壓縮代碼。并行壓縮可以顯著的提升效率。 安裝yarn add -D webpack-parallel-uglify-plugin
示例import ParallelUglifyPlugin from 'webpack-parallel-uglify-plugin';
module.exports = { plugins: [ new ParallelUglifyPlugin({ // Optional regex, or array of regex to match file against. Only matching files get minified. // Defaults to /.js$/, any file ending in .js. test, include, // Optional regex, or array of regex to include in minification. Only matching files get minified. exclude, // Optional regex, or array of regex to exclude from minification. Matching files are not minified. cacheDir, // Optional absolute path to use as a cache. If not provided, caching will not be used. workerCount, // Optional int. Number of workers to run uglify. Defaults to num of cpus - 1 or asset count (whichever is smaller) sourceMap, // Optional Boolean. This slows down the compilation. Defaults to false. uglifyJS: { // These pass straight through to uglify-js@3. // Cannot be used with uglifyES. // Defaults to {} if not neither uglifyJS or uglifyES are provided. // You should use this option if you need to ensure es5 support. uglify-js will produce an error message // if it comes across any es6 code that it can't parse. }, uglifyES: { // These pass straight through to uglify-es. // Cannot be used with uglifyJS. // uglify-es is a version of uglify that understands newer es6 syntax. You should use this option if the // files that you're minifying do not need to run in older browsers/versions of node. } }), ], };
?webpack-parallel-uglify-plugin 已不再維護(hù),這里不推薦使用 ? uglifyjs-webpack-plugin
安裝yarn add -D uglifyjs-webpack-plugin
示例const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = { plugins: [ new UglifyJsPlugin({ uglifyOptions: { warnings: false, parse: {}, compress: {}, ie8: false }, parallel: true }) ] };
其實(shí)它和上面的parallel-uglify-plugin 類似,也可通過設(shè)置parallel: true 開啟多進(jìn)程壓縮。 terser-webpack-plugin
不知道你有沒有發(fā)現(xiàn):webpack4 已經(jīng)默認(rèn)支持 ES6 語法的壓縮。 而這離不開terser-webpack-plugin 。 安裝yarn add -D terser-webpack-plugin
示例const TerserPlugin = require('terser-webpack-plugin');
module.exports = { optimization: { minimize: true, minimizer: [ new TerserPlugin({ parallel: 4, }), ], }, };
預(yù)編譯資源模塊 ??什么是預(yù)編譯資源模塊?在使用webpack 進(jìn)行打包時(shí)候,對(duì)于依賴的第三方庫(kù),比如vue ,vuex 等這些不會(huì)修改的依賴,我們可以讓它和我們自己編寫的代碼分開打包,這樣做的好處是每次更改我本地代碼的文件的時(shí)候,webpack 只需要打包我項(xiàng)目本身的文件代碼,而不會(huì)再去編譯第三方庫(kù)。 那么第三方庫(kù)在第一次打包的時(shí)候只打包一次,以后只要我們不升級(jí)第三方包的時(shí)候,那么webpack 就不會(huì)對(duì)這些庫(kù)去打包,這樣的可以快速的提高打包的速度。其實(shí)也就是預(yù)編譯資源模塊 。 webpack 中,我們可以結(jié)合DllPlugin 和 DllReferencePlugin 插件來實(shí)現(xiàn)。
DllPlugin 是什么?
它能把第三方庫(kù)代碼分離開,并且每次文件更改的時(shí)候,它只會(huì)打包該項(xiàng)目自身的代碼。所以打包速度會(huì)更快。 DLLPlugin 插件是在一個(gè)額外獨(dú)立的webpack 設(shè)置中創(chuàng)建一個(gè)只有dll 的bundle ,也就是說我們?cè)陧?xiàng)目根目錄下除了有webpack.config.js ,還會(huì)新建一個(gè)webpack.dll.js 文件。
webpack.dll.js 的作用是把所有的第三方庫(kù)依賴打包到一個(gè)bundle 的dll 文件里面,還會(huì)生成一個(gè)名為 manifest.json 文件。該manifest.json 的作用是用來讓 DllReferencePlugin 映射到相關(guān)的依賴上去的。
DllReferencePlugin 又是什么?
這個(gè)插件是在webpack.config.js 中使用的,該插件的作用是把剛剛在webpack.dll.js 中打包生成的dll 文件引用到需要的預(yù)編譯的依賴上來。 什么意思呢?就是說在webpack.dll.js 中打包后比如會(huì)生成 vendor.dll.js 文件和vendor-manifest.json 文件,vendor.dll.js 文件包含了所有的第三方庫(kù)文件,vendor-manifest.json 文件會(huì)包含所有庫(kù)代碼的一個(gè)索引,當(dāng)在使用webpack.config.js 文件打包DllReferencePlugin 插件的時(shí)候,會(huì)使用該DllReferencePlugin 插件讀取vendor-manifest.json 文件,看看是否有該第三方庫(kù)。 vendor-manifest.json 文件就是一個(gè)第三方庫(kù)的映射而已。
怎么在項(xiàng)目中使用?上面說了這么多,主要是為了方便大家對(duì)于預(yù)編譯資源模塊 和DllPlugin 和、DllReferencePlugin 插件作用的理解(我第一次使用看了好久才明白~~) 先來看下完成的項(xiàng)目目錄結(jié)構(gòu): 主要在兩塊配置,分別是webpack.dll.js 和webpack.config.js (對(duì)應(yīng)這里我是webpack.base.js ) webpack.dll.js
const path = require('path'); const webpack = require('webpack');
module.exports = { mode: 'production', entry: { vendors: ['lodash', 'jquery'], react: ['react', 'react-dom'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, './dll'), library: '[name]' }, plugins: [ new webpack.DllPlugin({ name: '[name]', path: path.resolve(__dirname, './dll/[name].manifest.json') }) ] }
這里我拆了兩部分:vendors (存放了lodash 、jquery 等)和react (存放了 react 相關(guān)的庫(kù),react 、react-dom 等) webpack.config.js (對(duì)應(yīng)我這里就是webpack.base.js )
const path = require('path'); const fs = require('fs'); // ... const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); const webpack = require('webpack');
const plugins = [ // ... ];
const files = fs.readdirSync(path.resolve(__dirname, './dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, './dll', file) })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, './dll', file) })) } })
module.exports = { entry: { main: './src/index.js' }, module: { rules: [] }, plugins,
output: { // publicPath: './', path: path.resolve(__dirname, 'dist') } }
這里為了演示省略了很多代碼,項(xiàng)目完整代碼在這里 由于上面我把第三方庫(kù)做了一個(gè)拆分,所以對(duì)應(yīng)生成也就會(huì)是多個(gè)文件,這里讀取了一下文件,做了一層遍歷。 最后在package.json 里面再添加一條腳本就可以了: 'scripts': { 'build:dll': 'webpack --config ./webpack.dll.js', },
運(yùn)行yarn build:dll 就會(huì)生成本小節(jié)開頭貼的那張項(xiàng)目結(jié)構(gòu)圖了~ 利用緩存提升二次構(gòu)建速度 ??一般來說,對(duì)于靜態(tài)資源,我們都希望瀏覽器能夠進(jìn)行緩存,那樣以后進(jìn)入頁面就可以直接使用緩存資源,頁面打開速度會(huì)顯著加快,既提高了用戶的體驗(yàn)也節(jié)省了寬帶資源。 當(dāng)然瀏覽器緩存方法有很多種,這里只簡(jiǎn)單討論下在webpack 中如何利用緩存來提升二次構(gòu)建速度。 在webpack 中利用緩存一般有以下幾種思路: - 使用
hard-source-webpack-plugin
babel-loader
babel-loader 在執(zhí)行的時(shí)候,可能會(huì)產(chǎn)生一些運(yùn)行期間重復(fù)的公共文件,造成代碼體積冗余,同時(shí)也會(huì)減慢編譯效率。
可以加上cacheDirectory 參數(shù)開啟緩存: { test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader', options: { cacheDirectory: true } }], },
cache-loader
在一些性能開銷較大的 loader 之前添加此 loader ,以將結(jié)果緩存到磁盤里。 安裝yarn add -D cache-loader
使用cache-loader 的配置很簡(jiǎn)單,放在其他 loader 之前即可。修改Webpack 的配置如下:
// webpack.config.js module.exports = { module: { rules: [ { test: /\.ext$/, use: [ 'cache-loader', ...loaders ], include: path.resolve('src') } ] } }
?請(qǐng)注意,保存和讀取這些緩存文件會(huì)有一些時(shí)間開銷,所以請(qǐng)只對(duì)性能開銷較大的 loader 使用此 loader 。 ? hard-source-webpack-plugin
HardSourceWebpackPlugin 為模塊提供了中間緩存,緩存默認(rèn)的存放路徑是: node_modules/.cache/hard-source 。
配置 hard-source-webpack-plugin 后,首次構(gòu)建時(shí)間并不會(huì)有太大的變化,但是從第二次開始,構(gòu)建時(shí)間大約可以減少 80% 左右。 安裝yarn add -D hard-source-webpack-plugin
使用// webpack.config.js var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = { entry: // ... output: // ... plugins: [ new HardSourceWebpackPlugin() ] }
?webpack5 中會(huì)內(nèi)置hard-source-webpack-plugin 。 ? 縮小構(gòu)建目標(biāo)/減少文件搜索范圍 ??有時(shí)候我們的項(xiàng)目中會(huì)用到很多模塊,但有些模塊其實(shí)是不需要被解析的。這時(shí)我們就可以通過縮小構(gòu)建目標(biāo)或者減少文件搜索范圍的方式來對(duì)構(gòu)建做適當(dāng)?shù)膬?yōu)化。 縮小構(gòu)建目標(biāo)主要是exclude 與 include 的使用: // webpack.config.js const path = require('path'); module.exports = { ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, // include: path.resolve('src'), use: ['babel-loader'] } ] }
這里babel-loader 就會(huì)排除對(duì)node_modules 下對(duì)應(yīng) js 的解析,提升構(gòu)建速度。 減少文件搜索范圍這個(gè)主要是resolve 相關(guān)的配置,用來設(shè)置模塊如何被解析。通過resolve 的配置,可以幫助Webpack 快速查找依賴,也可以替換對(duì)應(yīng)的依賴。 resolve.modules :告訴 webpack 解析模塊時(shí)應(yīng)該搜索的目錄resolve.mainFields :當(dāng)從 npm 包中導(dǎo)入模塊時(shí)(例如,import * as React from 'react' ),此選項(xiàng)將決定在 package.json 中使用哪個(gè)字段導(dǎo)入模塊。根據(jù) webpack 配置中指定的 target 不同,默認(rèn)值也會(huì)有所不同resolve.mainFiles :解析目錄時(shí)要使用的文件名,默認(rèn)是index resolve.extensions :文件擴(kuò)展名
// webpack.config.js const path = require('path'); module.exports = { ... resolve: { alias: { react: path.resolve(__dirname, './node_modules/react/umd/react.production.min.js') }, //直接指定react搜索模塊,不設(shè)置默認(rèn)會(huì)一層層的搜尋 modules: [path.resolve(__dirname, 'node_modules')], //限定模塊路徑 extensions: ['.js'], //限定文件擴(kuò)展名 mainFields: ['main'] //限定模塊入口文件名
動(dòng)態(tài) Polyfill 服務(wù) ??介紹動(dòng)態(tài)Polyfill 前,我們先來看下什么是babel-polyfill 。 什么是 babel-polyfill?babel 只負(fù)責(zé)語法轉(zhuǎn)換,比如將ES6 的語法轉(zhuǎn)換成ES5 。但如果有些對(duì)象、方法,瀏覽器本身不支持,比如:
- 全局對(duì)象:
Promise 、WeakMap 等。 - 全局靜態(tài)函數(shù):
Array.from 、Object.assign 等。 - 實(shí)例方法:比如
Array.prototype.includes 等。
此時(shí),需要引入babel-polyfill 來模擬實(shí)現(xiàn)這些對(duì)象、方法。 這種一般也稱為墊片 。 怎么使用babel-polyfill ?使用也非常簡(jiǎn)單,在webpack.config.js 文件作如下配置就可以了: module.exports = { entry: ['@babel/polyfill', './app/js'], };
為什么還要用動(dòng)態(tài)Polyfill ?babel-polyfill 由于是一次性全部導(dǎo)入整個(gè)polyfill ,所以用起來很方便,但與此同時(shí)也帶來了一個(gè)大問題:文件很大,所以后續(xù)的方案都是針對(duì)這個(gè)問題做的優(yōu)化。
來看下打包后babel-polyfill 的占比: 占比 29.6%,有點(diǎn)太大了! 介于上述原因,動(dòng)態(tài)Polyfill 服務(wù)誕生了。通過一張圖來了解下Polyfill Service 的原理: 每次打開頁面,瀏覽器都會(huì)向Polyfill Service 發(fā)送請(qǐng)求,Polyfill Service 識(shí)別 User Agent ,下發(fā)不同的 Polyfill ,做到按需加載Polyfill 的效果。 怎么使用動(dòng)態(tài)Polyfill 服務(wù)?采用官方提供的服務(wù)地址即可: //訪問url,根據(jù)User Agent 直接返回瀏覽器所需的 polyfills https://polyfill.io/v3/polyfill.min.js
Scope Hoisting ??
什么是Scope Hoisting ?Scope hoisting 直譯過來就是「作用域提升」。熟悉 JavaScript 都應(yīng)該知道「函數(shù)提升」和「變量提升」,JavaScript 會(huì)把函數(shù)和變量聲明提升到當(dāng)前作用域的頂部?!缸饔糜蛱嵘挂差愃朴诖?,webpack 會(huì)把引入的 js 文件“提升到”它的引入者頂部。
Scope Hoisting 可以讓 Webpack 打包出來的代碼文件更小、運(yùn)行的更快。
啟用Scope Hoisting 要在 Webpack 中使用 Scope Hoisting 非常簡(jiǎn)單,因?yàn)檫@是 Webpack 內(nèi)置的功能,只需要配置一個(gè)插件,相關(guān)代碼如下: // webpack.config.js const webpack = require('webpack')
module.exports = mode => { if (mode === 'production') { return {} }
return { devtool: 'source-map', plugins: [new webpack.optimize.ModuleConcatenationPlugin()], } }
啟用Scope Hoisting 后的對(duì)比讓我們先來看看在沒有 Scope Hoisting 之前 Webpack 的打包方式。 假如現(xiàn)在有兩個(gè)文件分別是 export default 'Hello,Jack-cool';
import str from './constant.js'; console.log(str);
以上源碼用 Webpack 打包后的部分代碼如下: [ (function (module, __webpack_exports__, __webpack_require__) { var __WEBPACK_IMPORTED_MODULE_0__constant_js__ = __webpack_require__(1); console.log(__WEBPACK_IMPORTED_MODULE_0__constant_js__['a']); }), (function (module, __webpack_exports__, __webpack_require__) { __webpack_exports__['a'] = ('Hello,Jack-cool'); }) ]
在開啟 Scope Hoisting 后,同樣的源碼輸出的部分代碼如下: [ (function (module, __webpack_exports__, __webpack_require__) { var constant = ('Hello,Jack-cool'); console.log(constant); }) ]
從中可以看出開啟 Scope Hoisting 后,函數(shù)申明由兩個(gè)變成了一個(gè),constant.js 中定義的內(nèi)容被直接注入到了 main.js 對(duì)應(yīng)的模塊中。這樣做的好處是: - 代碼體積更小,因?yàn)楹瘮?shù)申明語句會(huì)產(chǎn)生大量代碼;
- 代碼在運(yùn)行時(shí)因?yàn)閯?chuàng)建的函數(shù)作用域更少了,內(nèi)存開銷也隨之變小。
Scope Hoisting 的實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單:分析出模塊之間的依賴關(guān)系,盡可能的把打散的模塊合并到一個(gè)函數(shù)中去,但前提是不能造成代碼冗余。因此只有那些被引用了一次的模塊才能被合并。
?由于 Scope Hoisting 需要分析出模塊之間的依賴關(guān)系,因此源碼必須采用 ES6 模塊化語句,不然它將無法生效。 ? 參考極客時(shí)間 【玩轉(zhuǎn) webpack】 - EOF -
|