<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當(dāng)前位置: 首頁 - 科技 - 知識(shí)百科 - 正文

        淺談如何使用 webpack 優(yōu)化資源

        來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 21:50:31
        文檔

        淺談如何使用 webpack 優(yōu)化資源

        淺談如何使用 webpack 優(yōu)化資源:前言 在前端應(yīng)用的優(yōu)化中,對(duì)加載資源的大小控制極其的重要,大多數(shù)時(shí)候我們能做的是在打包編譯的過程對(duì)資源進(jìn)行大小控制、拆分與復(fù)用。 本片文章中主要是基于 webpack 打包,以 React、vue 等生態(tài)開發(fā)的單頁面應(yīng)用來舉例說明如何從 webpack 打包的層
        推薦度:
        導(dǎo)讀淺談如何使用 webpack 優(yōu)化資源:前言 在前端應(yīng)用的優(yōu)化中,對(duì)加載資源的大小控制極其的重要,大多數(shù)時(shí)候我們能做的是在打包編譯的過程對(duì)資源進(jìn)行大小控制、拆分與復(fù)用。 本片文章中主要是基于 webpack 打包,以 React、vue 等生態(tài)開發(fā)的單頁面應(yīng)用來舉例說明如何從 webpack 打包的層

        前言

        在前端應(yīng)用的優(yōu)化中,對(duì)加載資源的大小控制極其的重要,大多數(shù)時(shí)候我們能做的是在打包編譯的過程對(duì)資源進(jìn)行大小控制、拆分與復(fù)用。

        本片文章中主要是基于 webpack 打包,以 React、vue 等生態(tài)開發(fā)的單頁面應(yīng)用來舉例說明如何從 webpack 打包的層面去處理資源以及緩存,其中主要我們需要做的是對(duì) webpack 進(jìn)行配置的優(yōu)化,同時(shí)涉及少量的業(yè)務(wù)代碼的更改。

        同時(shí)對(duì)打包資源的分析可以使用 (webpack-contrib/webpack-bundle-analyzer) 插件,當(dāng)然可選的分析插件還是很多的,在本文中主要以該插件來舉例分析。

        TIP: webpack 版本 @3.6.0

        一、打包環(huán)境與代碼壓縮

        首先我們有一個(gè)最基本的 webpack 配置:

        // webpack.config.js
        const path = require('path');
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
        
        const PROJECT_ROOT = path.resolve(__dirname, './');
        
        module.exports = {
         entry: {
         index: './src/index.js'
         },
         output: {
         path: path.resolve(__dirname, 'dist'),
         filename: '[name].[chunkhash:4].js'
         },
         module: {
         rules: [
         {
         test: /\.js[x]?$/,
         use: 'babel-loader',
         include: PROJECT_ROOT,
         exclude: /node_modules/
         }
         ]
         },
         plugins: [
         new BundleAnalyzerPlugin()
         ],
         resolve: {
         extensions: ['.js', '.jsx']
         },
        };
        
        

        執(zhí)行打包可以看到一個(gè)項(xiàng)目的 js 有 1M 以上:

        Hash: e51afc2635f08322670b
        Version: webpack 3.6.0
        Time: 2769ms
         Asset Size Chunks Chunk Names
        index.caa7.js 1.3 MB 0 [emitted] [big] index

        這時(shí)候只需要增加插件 `DefinePlugin` 與 `UglifyJSPlugin` 即可減少不少的體積,在 plugins 中添加:

        // webpack.config.js
        ...
        {
         ...
         plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         })
         ]
         ...
        }
        

        可以看到這時(shí)候的打包輸出:

        Hash: 84338998472a6d3c5c25
        Version: webpack 3.6.0
        Time: 9940ms
         Asset Size Chunks Chunk Names
        index.89c2.js 346 kB 0 [emitted] [big] index
        

        代碼的大小從 1.3M 降到了 346K。

        (1)DefinePlugin

        DefinePlugin 允許創(chuàng)建一個(gè)在編譯時(shí)可以配置的全局常量。這可能會(huì)對(duì)開發(fā)模式和發(fā)布模式的構(gòu)建允許不同的行為非常有用。如果在開發(fā)構(gòu)建中,而不在發(fā)布構(gòu)建中執(zhí)行日志記錄,則可以使用全局常量來決定是否記錄日志。這就是 DefinePlugin 的用處,設(shè)置它,就可以忘記開發(fā)和發(fā)布構(gòu)建的規(guī)則。

        在我們的業(yè)務(wù)代碼和第三方包的代碼中很多時(shí)候需要判斷 `process.env.NODE_ENV` 來做不同處理,而在生產(chǎn)環(huán)境中我們顯然不需要非 `production` 的處理部分。

        在這里我們?cè)O(shè)置 `process.env.NODE_ENV` 為 `JSON.stringify('production')`,也就是表示講打包環(huán)境設(shè)置為生產(chǎn)環(huán)境。之后配合 `UglifyJSPlugin` 插件就可以在給生產(chǎn)環(huán)境打包的時(shí)候去除部分的冗余代碼。

        (2)UglifyJSPlugin

        UglifyJSPlugin 主要是用于解析、壓縮 js 代碼,它基于 `uglify-es` 來對(duì) js 代碼進(jìn)行處理,它有多種配置選項(xiàng):https://github.com/webpack-contrib/uglifyjs-webpack-plugin。

        通過對(duì)代碼的壓縮處理以及去除冗余,大大減小了打包資源的體積大小。

        二、代碼拆分/按需加載

        在如 React 或者 vue 構(gòu)建的單頁面應(yīng)用中,對(duì)頁面路由與視圖的控制是由前端來實(shí)現(xiàn)的,其對(duì)應(yīng)的業(yè)務(wù)邏輯都在 js 代碼中。

        當(dāng)一個(gè)應(yīng)用設(shè)計(jì)的頁面和邏輯很多的時(shí)候,最終生成的 js 文件資源也會(huì)相當(dāng)大。

        然而當(dāng)我們打開一個(gè) url 對(duì)應(yīng)的頁面時(shí),實(shí)際上需要的并非全部的 js 代碼,所需要的僅是一個(gè)主的運(yùn)行時(shí)代碼與該視圖對(duì)應(yīng)的業(yè)務(wù)邏輯的代碼,在加載下一個(gè)視圖的時(shí)候再去加載那部分的代碼。

        因此,對(duì)這方面可做的優(yōu)化就是對(duì) js 代碼進(jìn)行按需加載。

        懶加載或者按需加載,是一種很好的優(yōu)化網(wǎng)頁或應(yīng)用的方式。這種方式實(shí)際上是先把你的代碼在一些邏輯斷點(diǎn)處分離開,然后在一些代碼塊中完成某些操作后,立即引用或即將引用另外一些新的代碼塊。這樣加快了應(yīng)用的初始加載速度,減輕了它的總體體積,因?yàn)槟承┐a塊可能永遠(yuǎn)不會(huì)被加載。

        在 webpack 中提供了動(dòng)態(tài)導(dǎo)入的技術(shù)來實(shí)現(xiàn)代碼拆分,首先在 webpack 的配置中需要去配置拆分出來的每個(gè)子模塊的配置:

        // webpack.config.js
        const path = require('path');
        const webpack = require('webpack');
        const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
        
        const PROJECT_ROOT = path.resolve(__dirname, './');
        
        module.exports = {
         entry: {
         index: './src/index.js'
         },
         output: {
         path: path.resolve(__dirname, 'dist'),
         filename: '[name].[chunkhash:4].js',
         chunkFilename: '[name].[chunkhash:4].child.js',
         },
         module: {
         rules: [
         {
         test: /\.js[x]?$/,
         use: 'babel-loader',
         include: PROJECT_ROOT,
         exclude: /node_modules/
         }
         ]
         },
         plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         }),
         ],
         resolve: {
         extensions: ['.js', '.jsx']
         },
        };
        
        

        其中主要需要定義的則是 `output` 中的 `chunkFilename`,它是導(dǎo)出的拆分代碼的文件名,這里給它設(shè)置為 `[name].[chunkhash:4].child.js`,其中的 `name` 對(duì)應(yīng)模塊名稱或者 id,`chunkhash` 是模塊內(nèi)容的 hash。

        之后在業(yè)務(wù)代碼中 webpack 提供了兩種方式來動(dòng)態(tài)導(dǎo)入:

      1. `import('path/to/module') -> Promise`,
      2. `require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)`
      3. 對(duì)于最新版本的 webpack 主要推薦使用 `import()` 的方式(注意:import 使用到了 Promise,因此需要確保代碼中支持了 Promise 的 polyfill)。

        // src/index.js
        function getComponent() {
         return import(
         /* webpackChunkName: "lodash" */
         'lodash'
         ).then(_ => {
         var element = document.createElement('div');
        
         element.innerHTML = _.join(['Hello', 'webpack'], ' ');
        
         return element;
        
         }).catch(error => 'An error occurred while loading the component');
        }
        
        getComponent().then(component => {
         document.body.appendChild(component);
        })
        
        

        可以看到打包的信息:

        Hash: d6ba79fe5995bcf9fa4d
        Version: webpack 3.6.0
        Time: 7022ms
         Asset Size Chunks Chunk Names
        lodash.89f0.child.js 85.4 kB 0 [emitted] lodash
         index.316e.js 1.96 kB 1 [emitted] index
         [0] ./src/index.js 441 bytes {1} [built]
         [2] (webpack)/buildin/global.js 509 bytes {0} [built]
         [3] (webpack)/buildin/module.js 517 bytes {0} [built]
         + 1 hidden module

        可以看到打包出來的代碼生成了 `index.316e.js` 和 `lodash.89f0.child.js` 兩個(gè)文件,后者則是通過 `import` 來實(shí)現(xiàn)拆分的。

        `import` 它接收一個(gè) `path` 參數(shù),指的是該子模塊對(duì)于的路徑,同時(shí)還注意到其中可以添加一行注釋 `/* webpackChunkName: "lodash" */`,該注釋并非是無用的,它定義了該子模塊的 name,其對(duì)應(yīng)與 `output.chunkFilename` 中的 `[name]`。

        `import` 函數(shù)返回一個(gè) Promise,當(dāng)異步加載到子模塊代碼是會(huì)執(zhí)行后續(xù)操作,比如更新視圖等。

        (1)React 中的按需加載

        在 React 配合 React-Router 開發(fā)中,往往就需要代碼根據(jù)路由按需加載的能力,下面是一個(gè)基于 webpack 代碼動(dòng)態(tài)導(dǎo)入技術(shù)實(shí)現(xiàn)的 React 動(dòng)態(tài)載入的組件:

        import React, { Component } from 'react';
        
        export default function lazyLoader (importComponent) {
         class AsyncComponent extends Component {
         state = { Component: null }
        
         async componentDidMount () {
         const { default: Component } = await importComponent();
        
         this.setState({
         Component: Component
         });
         }
        
         render () {
         const Component = this.state.Component;
        
         return Component
         ? <Component {...this.props} />
         : null;
         }
         }
        
         return AsyncComponent;
        };
        
        

        在 `Route` 中:

        <Switch>
         <Route exact path="/"
         component={lazyLoader(() => import('./Home'))}
         />
         <Route path="/about"
         component={lazyLoader(() => import('./About'))}
         />
         <Route
         component={lazyLoader(() => import('./NotFound'))}
         />
        </Switch>
        

        在 `Route` 中渲染的是 `lazyLoader` 函數(shù)返回的組件,該組件在 mount 之后會(huì)去執(zhí)行 `importComponent` 函數(shù)(既:`() => import('./About')`)動(dòng)態(tài)加載其對(duì)于的組件模塊(被拆分出來的代碼),待加載成功之后渲染該組件。

        使用該方式打包出來的代碼:

        Hash: 02a053d135a5653de985
        Version: webpack 3.6.0
        Time: 9399ms
         Asset Size Chunks Chunk Names
        0.db22.child.js 5.82 kB 0 [emitted]
        1.fcf5.child.js 4.4 kB 1 [emitted]
        2.442d.child.js 3 kB 2 [emitted]
         index.1bbc.js 339 kB 3 [emitted] [big] index

        三、抽離 Common 資源

        (1)第三方庫的長緩存

        首先對(duì)于一些比較大的第三方庫,比如在 React 中用到的 react、react-dom、react-router 等等,我們不希望它們被重復(fù)打包,并且在每次版本更新的時(shí)候也不希望去改變這部分的資源導(dǎo)致在用戶端重新加載。

        在這里可以使用 webpack 的 CommonsChunkPlugin 來抽離這些公共資源;

        CommonsChunkPlugin 插件,是一個(gè)可選的用于建立一個(gè)獨(dú)立文件(又稱作 chunk)的功能,這個(gè)文件包括多個(gè)入口 chunk 的公共模塊。通過將公共模塊拆出來,最終合成的文件能夠在最開始的時(shí)候加載一次,便存起來到緩存中供后續(xù)使用。這個(gè)帶來速度上的提升,因?yàn)闉g覽器會(huì)迅速將公共的代碼從緩存中取出來,而不是每次訪問一個(gè)新頁面時(shí),再去加載一個(gè)更大的文件。
        首先需要在 entry 中新增一個(gè)入口用來打包需要抽離出來的庫,這里將 `'react', 'react-dom', 'react-router-dom', 'immutable'` 都給單獨(dú)打包進(jìn) `vendor` 中;

        之后在 plugins 中定義一個(gè) `CommonsChunkPlugin` 插件,同時(shí)將其 `name` 設(shè)置為 `vendor` 是它們相關(guān)聯(lián),再將 `minChunks` 設(shè)置為 `Infinity` 防止其他代碼被打包進(jìn)來。

        // webpack.config.js
        const path = require('path');
        const webpack = require('webpack');
        const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
        
        const PROJECT_ROOT = path.resolve(__dirname, './');
        
        module.exports = {
         entry: {
         index: './src0/index.js',
         vendor: ['react', 'react-dom', 'react-router-dom', 'immutable']
         },
         output: {
         path: path.resolve(__dirname, 'dist'),
         filename: '[name].[chunkhash:4].js',
         chunkFilename: '[name].[chunkhash:4].child.js',
         },
         module: {
         rules: [
         {
         test: /\.js[x]?$/,
         use: 'babel-loader',
         include: PROJECT_ROOT,
         exclude: /node_modules/
         }
         ]
         },
         plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         }),
         new webpack.optimize.CommonsChunkPlugin({
         name: 'vendor',
         minChunks: Infinity,
         }),
         ],
         resolve: {
         extensions: ['.js', '.jsx']
         },
        };
        
        

        運(yùn)行打包可以看到:

        Hash: 34a71fcfd9a24e810c21
        Version: webpack 3.6.0
        Time: 9618ms
         Asset Size Chunks Chunk Names
        0.2c65.child.js 5.82 kB 0 [emitted]
        1.6e26.child.js 4.4 kB 1 [emitted]
        2.e4bc.child.js 3 kB 2 [emitted]
         index.4e2f.js 64.2 kB 3 [emitted] index
         vendor.5fd1.js 276 kB 4 [emitted] [big] vendor
        

        可以看到 `vendor` 被單獨(dú)打包出來了。

        當(dāng)我們改變業(yè)務(wù)代碼時(shí)再次打包:

        Hash: cd3f1bc16b28ac97e20a
        Version: webpack 3.6.0
        Time: 9750ms
         Asset Size Chunks Chunk Names
        0.2c65.child.js 5.82 kB 0 [emitted]
        1.6e26.child.js 4.4 kB 1 [emitted]
        2.e4bc.child.js 3 kB 2 [emitted]
         index.4d45.js 64.2 kB 3 [emitted] index
         vendor.bc85.js 276 kB 4 [emitted] [big] vendor
        

        vendor 包同樣被打包出來的,然而它的文件 hash 卻發(fā)生了變化,這顯然不符合我們長緩存的需求。

        這是因?yàn)?webpack 在使用 CommoChunkPlugin 的時(shí)候會(huì)生成一段 runtime 代碼(它主要用來處理代碼模塊的映射關(guān)系),而哪怕沒有改變 vendor 里的代碼,這個(gè) runtime 仍然是會(huì)跟隨著打包變化的并且打入 verdor 中,所以 hash 就會(huì)開始變化了。解決方案則是把這部分的 runtime 代碼也單獨(dú)抽離出來,修改之前的 `CommonsChunkPlugin` 為:

        // webpack.config.js
        ...
        new webpack.optimize.CommonsChunkPlugin({
         name: ['vendor', 'runtime'],
         minChunks: Infinity,
        }),
        ...

        執(zhí)行打包可以看到生成的代碼中多了 `runtime` 文件,同時(shí)即使改變業(yè)務(wù)代碼,vendor 的 hash 值也保持不變了。

        當(dāng)然這段 `runtime` 實(shí)際上非常短,我們可以直接 inline 在 html 中,如果使用的是 `html-webpack-plugin` 插件處理 html,則可以結(jié)合 [`html-webpack-inline-source-plugin`](DustinJackson/html-webpack-inline-source-plugin) 插件自動(dòng)處理其 inline。

        (2)公共資源抽離

        在我們打包出來的 js 資源包括不同入口以及子模塊的 js 資源包,然而它們之間也會(huì)重復(fù)載入相同的依賴模塊或者代碼,因此可以通過 CommonsChunkPlugin 插件將它們共同依賴的一些資源打包成一個(gè)公共的 js 資源。

        // webpack.config.js
        plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         }),
         new webpack.optimize.CommonsChunkPlugin({
         name: ['vendor', 'runtime'],
         minChunks: Infinity,
         }),
         new webpack.optimize.CommonsChunkPlugin({
         // ( 公共chunk(commnons chunk) 的名稱)
         name: "commons",
         // ( 公共chunk 的文件名)
         filename: "commons.[chunkhash:4].js",
         // (模塊必須被 3個(gè) 入口chunk 共享)
         minChunks: 3
         })
        ],
        

        可以看到這里增加了 `commons` 的一個(gè)打包,當(dāng)一個(gè)資源被三個(gè)以及以上 chunk 依賴時(shí),這些資源會(huì)被單獨(dú)抽離打包到 `commons.[chunkhash:4].js` 文件。

        執(zhí)行打包,看到結(jié)果如下:

        Hash: 2577e42dc5d8b94114c8
        Version: webpack 3.6.0
        Time: 24009ms
         Asset Size Chunks Chunk Names
        0.2eee.child.js 90.8 kB 0 [emitted]
        1.cfbc.child.js 89.4 kB 1 [emitted]
        2.557a.child.js 88 kB 2 [emitted]
         vendor.66fd.js 275 kB 3 [emitted] [big] vendor
         index.688b.js 64.2 kB 4 [emitted] index
        commons.a61e.js 1.78 kB 5 [emitted] commons
        

        卻發(fā)現(xiàn)這里的 `commons.[chunkhash].js` 基本沒有實(shí)際內(nèi)容,然而明明在每個(gè)子模塊中也都依賴了一些相同的依賴。

        借助 webpack-bundle-analyzer 來分析一波:

        可以看到三個(gè)模塊都依賴了 `lodash`,然而它并沒有被抽離出來。

        這是因?yàn)?CommonsChunkPlugin 中的 chunk 指的是 entry 中的每個(gè)入口,因此對(duì)于一個(gè)入口拆分出來的子模塊(children chunk)是不生效的。

        可以通過在 CommonsChunkPlugin 插件中配置 `children` 參數(shù)將拆分出來的子模塊的公共依賴也打包進(jìn) `commons` 中:

        // webpack.config.js
        plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         }),
         new webpack.optimize.CommonsChunkPlugin({
         name: ['vendor', 'runtime'],
         minChunks: Infinity,
         }),
         new webpack.optimize.CommonsChunkPlugin({
         // ( 公共chunk(commnons chunk) 的名稱)
         name: "commons",
         // ( 公共chunk 的文件名)
         filename: "commons.[chunkhash:4].js",
         // (模塊必須被 3個(gè) 入口chunk 共享)
         minChunks: 3
         }),
         new webpack.optimize.CommonsChunkPlugin({
         // (選擇所有被選 chunks 的子 chunks)
         children: true,
         // (在提取之前需要至少三個(gè)子 chunk 共享這個(gè)模塊)
         minChunks: 3,
         })
        ],
        

        查看打包效果:

        其子模塊的公共資源都被打包到 `index` 之中了,并沒有理想地打包進(jìn) `commons` 之中,還是因?yàn)?`commons` 對(duì)于的是 entry 中的入口模塊,而這里并未有 3 個(gè) entry 模塊共用資源;

        在單入口的應(yīng)用中可以選擇去除 `commons`,而在子模塊的 `CommonsChunkPlugin` 的配置中配置 `async` 為 `true`:

        // webpack.config.js
        plugins: [
         new BundleAnalyzerPlugin(),
         new webpack.DefinePlugin({
         'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
         }),
         new UglifyJSPlugin({
         uglifyOptions: {
         ie8: false,
         output: {
         comments: false,
         beautify: false,
         },
         mangle: {
         keep_fnames: true
         },
         compress: {
         warnings: false,
         drop_console: true
         },
         }
         }),
         new webpack.optimize.CommonsChunkPlugin({
         name: ['vendor', 'runtime'],
         minChunks: Infinity,
         }),
         new webpack.optimize.CommonsChunkPlugin({
         // (選擇所有被選 chunks 的子 chunks)
         children: true,
         // (異步加載)
         async: true,
         // (在提取之前需要至少三個(gè)子 chunk 共享這個(gè)模塊)
         minChunks: 3,
         })
        ],
        

        查看效果:

        子模塊的公共資源都被打包到 `0.9c90.child.js` 中了,該模塊則是子模塊的 commons。

        四、tree shaking

        tree shaking 是一個(gè)術(shù)語,通常用于描述移除 JavaScript 上下文中的未引用代碼(dead-code)。它依賴于 ES2015 模塊系統(tǒng)中的靜態(tài)結(jié)構(gòu)特性,例如 import 和 export。這個(gè)術(shù)語和概念實(shí)際上是興起于 ES2015 模塊打包工具 rollup。
        在我們引入一個(gè)依賴的某個(gè)輸出的時(shí)候,我們可能需要的僅僅是該依賴的某一部分代碼,而另一部分代碼則是 `unused` 的,如果能夠去除這部分代碼,那么最終打包出來的資源體積也是可以有可觀的減小。

        首先,webpack 中實(shí)現(xiàn) tree shaking 是基于 webpack 內(nèi)部支持的 es2015 的模塊機(jī)制,在大部分時(shí)候我們使用 babel 來編譯 js 代碼,而 babel 會(huì)通過自己的模塊加載機(jī)制處理一遍,這導(dǎo)致 webpack 中的 tree shaking 處理將會(huì)失效。因此在 babel 的配置中需要關(guān)閉對(duì)模塊加載的處理:

        // .babelrc
        {
         "presets": [
         [
         "env", {
         "modules": false,
         }
         ],
         "stage-0"
         ],
         ...
        }

        然后我們來看下 webpack 是如何處理打包的代碼,舉例有一個(gè)入口文件 `index.js` 和一個(gè) `utils.js` 文件:

        // utils.js
        export function square(x) {
         return x * x;
        }
        
        export function cube(x) {
         return x * x * x;
        }
        ```
        
        ```js
        // index.js
        import { cube } from './utils.js';
        
        console.log(cube(10));
        ```
        
        打包出來的代碼:
        
        ```
        // index.bundle.js
        /* 1 */
        /***/ (function(module, __webpack_exports__, __webpack_require__) {
        
        "use strict";
        /* unused harmony export square */
        /* harmony export (immutable) */ __webpack_exports__["a"] = cube;
        function square(x) {
         return x * x;
        }
        
        function cube(x) {
         return x * x * x;
        }
        
        

        可以看到僅有 `cube` 函數(shù)被 `__webpack_exports__` 導(dǎo)出來,而 `square` 函數(shù)被標(biāo)記為 `unused harmony export square`,然而在打包代碼中既是 `square` 沒有被導(dǎo)出但是它仍然存在與代碼中,而如何去除其代碼則可以通過添加 `UglifyjsWebpackPlugin` 插件來處理。

        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        淺談如何使用 webpack 優(yōu)化資源

        淺談如何使用 webpack 優(yōu)化資源:前言 在前端應(yīng)用的優(yōu)化中,對(duì)加載資源的大小控制極其的重要,大多數(shù)時(shí)候我們能做的是在打包編譯的過程對(duì)資源進(jìn)行大小控制、拆分與復(fù)用。 本片文章中主要是基于 webpack 打包,以 React、vue 等生態(tài)開發(fā)的單頁面應(yīng)用來舉例說明如何從 webpack 打包的層
        推薦度:
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 暖暖免费中文在线日本| 亚洲第一se情网站| 久久精品无码精品免费专区| 国产偷窥女洗浴在线观看亚洲 | 欧洲黑大粗无码免费| 亚洲成a人片7777| 男女免费观看在线爽爽爽视频 | 狠狠色伊人亚洲综合成人| 岛国岛国免费V片在线观看 | 无码AV片在线观看免费| 亚洲第一页在线视频| 韩国免费一级成人毛片| 国产精品亚洲四区在线观看 | 亚洲综合国产精品| 在线看片免费人成视久网| 亚洲午夜在线一区| 在线视频免费观看www动漫| 美女被免费网站在线视频免费| 亚洲国产一区二区三区| 免费成人在线视频观看| 亚洲国产美女福利直播秀一区二区 | 最近的免费中文字幕视频 | 又黄又爽又成人免费视频| 亚洲精品一卡2卡3卡四卡乱码| 波多野结衣免费视频观看| 中国内地毛片免费高清| 亚洲黄色一级毛片| 午夜dj在线观看免费视频| 一个人看的免费观看日本视频www 一个人看的免费视频www在线高清动漫 | 亚洲精华液一二三产区| 国产又黄又爽又刺激的免费网址 | 久久亚洲私人国产精品| 成年女人色毛片免费看| 一级毛片免费在线观看网站| 亚洲精品福利网泷泽萝拉| 精品久久洲久久久久护士免费| 成人自慰女黄网站免费大全| 亚洲日韩乱码久久久久久| 吃奶摸下高潮60分钟免费视频| a级日本高清免费看| 亚洲av无码专区在线观看亚|