進擊webpack4 (優化篇)

進擊webpack 4 (基礎篇一)
進擊webpack4 (基礎篇二:配置 一)
進擊webpack4 (基礎篇三:配置 二)

不解析不依賴第三方模塊的模塊

noParse

有一些第三方模塊,它本身不依賴於其他模塊,比如jquery,lodash,不去編譯這些庫,會使得webpack打包更加快速
noParse是個正則或者包含正則的數組 RegExp | [RegExp]

module:{
        noParse:/jquery/, //不去解析jquery
        rules:[
            //...
        ]
    },
--------------------- 

忽略某些庫內的第三方模塊

ignorePlugin

以moment這個時間庫爲例, 導入moment的同時, moment會引入自身依賴的語言包,這些語言包其中有部分是我們不需要用到的,moment內部代碼

clipboard.png

  plugins: [
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
    ],

2個參數代表的意思是:

當匹配到moment這個庫的時候 引入moment並且忽略moment裏面匹配到locale的庫

這個時候我們如果想要自己需要的locale 需在main.js手動引入

import 'moment/locale/zh-cn'

動態鏈接庫

另起一個webpack.config.dll.js 專門用來生成動態鏈接庫

//webpack.config.dll.js

const path=require('path');
const webpack=require('webpack');
module.exports={
    mode:'development',
    entry: {
        react:['react','react-dom'],
        jquery:['jquery']
    },// 把 React 相關模塊的放到一個單獨的動態鏈接庫
    output: {
        path: path.resolve(__dirname,'dist'),// 輸出的文件都放到 dist 目錄下
        filename: '[name].dll.js',//輸出的動態鏈接庫的文件名稱,[name] 代表當前動態鏈接庫的名稱
        library: '_dll_[name]',//存放動態鏈接庫的全局變量名稱,例如對應 react 來說就是 _dll_react
    },
    plugins: [
        new webpack.DllPlugin({
            // 動態鏈接庫的全局變量名稱,需要和 output.library 中保持一致
            // 該字段的值也就是輸出的 mainfest.json 文件 中 name 字段的值
            // 例如 react.manifest.json 中就有 "name": "_dll_react"
            name: '_dll_[name]',
            // 描述動態鏈接庫的 manifest.json 文件輸出時的文件名稱
            path: path.join(__dirname, 'dist', '[name].mainfest.json')
        })
    ]
}
//打包
npx webpack --config webpack.config.dll.js 

這樣會在dist生成

clipboard.png

然後在webpack.config.js裏

const webpack= require('webpack')
plugins: [
  new webpack.DllReferencePlugin({
    manifest:require('./dist/react.mainfest.json')
  }),
  new webpack.DllReferencePlugin({
    manifest:require('./dist/jquery.mainfest.json')
  })
]

這裏它會從mainfest.json尋找name 然後根據它的標識找到相應內容, dll.js就是打包出來後的動態鏈接庫

clipboard.png

然後在html模板文件裏引入

<script src="./jquery.dll.js"></script>
<script src="./react.dll.js"></script>

如果你在main.jsimport React from 'react',他會首先找動態鏈接庫, 找不到纔會執行打包

clipboard.png
clipboard.png

注:使用react需要配置好rule


{
test:/\.js/,
    use:{
        loader:'babel-loader',
        options:{
            presets:[
                '@babel/preset-env',
                '@babel/preset-react'
            ]
        }
    }
},

開啓多進程打包

npm i happypack -D

如果一個項目代碼密集,讀寫操作頻繁,happypack 就能讓Webpack把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。

const HappyPack = require('happypack');
    rules: [
    {
        test: /\.js$/,
        // 把對 .js 文件的處理轉交給 id 爲 babel 的 HappyPack 實例
        use: ['happypack/loader?id=babel'],
        exclude: path.resolve(__dirname, 'node_modules'),
    },
    {
        test: /\.css$/,
        // 把對 .css 文件的處理轉交給 id 爲 css 的 HappyPack 實例
        use: ['happypack/loader?id=css']
    }
]
new Happypack({
            //ID是標識符的意思,ID用來代理當前的happypack是用來處理一類特定的文件的
            id: 'js',
            use: [{
                loader: 'babel-loader',
                //options=query都是向插件傳遞參數的
                options: {
                    presets: [["@babel/preset-env", { modules: false }], "@babel/preset-react"],
                    plugins: [
                        ["@babel/plugin-proposal-decorators", { "legacy": true }],
                        ["@babel/plugin-proposal-class-properties", { "loose": true }],
                    ]
                }
            }]
        }),
        new Happypack({
            //ID是標識符的意思,ID用來代理當前的happypack是用來處理一類特定的文件的
            id: 'css',
            use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
            threads: 4,//你要開啓多少個子進程去處理這一類型的文件
            verbose: true//是否要輸出詳細的日誌 verbose
        })

注意:開啓進程也需要時間, 如果一個項目並不是很複雜, 斟酌使用

不打包第三方庫 使用cdn引入

module.exports = {
  //...
  externals: {
    jquery: 'jQuery'
  }
};

main.js 引入jquery 將不會打包 import jquery from 'jQuery'

html模板內引入jquery的cdn地址即可

儘量使用es6的模塊導入

webpack的tree-shaking自己可以分析出哪些沒有使用到的代碼可以剔除,前提是es6模塊語法
scope-hosting可以提升作用域 比如 var a = 1; var b = a ; console.log(b) 會編譯成var b = 1; console.log(b)

提取公共代碼

做這種操作首先得是多頁面

 entry:{
    home:['./src/index.js'],
    login:['./src/login.js']
}, // 入口文件
//home.js
import React from 'react'
import {render }from 'react-dom'
render(<h1>動態鏈接庫</h1>,window.root)

//login.js
import React from 'react'
import {render }from 'react-dom'
render(<h1>動態鏈接庫</h1>,window.root)
//webpack.config.js

optimization:{  // 優化
    splitChunks:{ //分割代碼
        cacheGroups:{ // 緩存組
            common:{ // 公共的代碼  一般是自己寫的公共代碼
                chunks:'initial',
                minSize:0,   
                minChunks: 2, //最少被引用2次的模塊
                name: "common"
            },
            vendor:{  // 一般是第三方公共模塊
                priority:1, // 因爲執行是從上往下, 所以設置優先級比上面高  不然上面抽離的話第三方模塊也被抽離了   
                test:/node_modules/ , //匹配node_modules下的公共代碼,
                chunks:'initial',
                minSize:0,   
                minChunks: 2, //最少被引用2次的模塊
                name: "vendor"
            }    
        }
    }
}

clipboard.png

懶加載

這裏拿vue舉例

const Login = () => import(/* webpackChunkName: "login" */,"./login");
new VueRouter({
  routes: [{ path: "/login", component: Login }]
})

webpackChunkName雖然是註釋, 但是webpack能識別, 編譯後這個組件生產的名字就是login

可能會需要@babel/plugin-syntax-dynamic-import 才能識別

yarn add @babel/plugin-syntax-dynamic-import -D

具體配置看此文

熱更新

devServer:{
  // 告訴 DevServer 要開啓模塊熱替換模式
  hot: true,      
}  

在vue中只要這樣配置就可以了, vue自己幫我們做了配置

其他庫中:

import React from 'react'
import {render} from 'react-dom'




render(<App/>, document.getElementById('root'));

if (module.hot) {
  // accept 函數的第一個參數指出當前文件接受哪些子模塊的替換,這裏表示只接受 ./AppComponent 這個子模塊
  // 第2個參數用於在新的子模塊加載完畢後需要執行的邏輯
  module.hot.accept(['./App'], () => {
    // 新的 AppComponent 加載成功後重新執行下組建渲染邏輯
    let App=require('./App').default;  
    render(<App/>, document.getElementById('root'));
  });
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章