3.1 webpack 下 IgnorePlugin、dllPlugin等插件应用和懒加载、热更新实现

1,621 阅读5分钟

一、noParse

作用:不去解析三方模块中的依赖项目。

module:{
    noParse: /jquery/, //不解析jquery中的依赖,节约打包时间
    ...
}

二、IgnorePlugin插件

作用:忽略三方包的指定目录,指定目录不会被打包进去。
很多教程都有介绍 moment
我们在项目上做个试验,index.js 如下

import moment from 'moment'
let t = moment().endOf('day').fromNow()
console.log(t)

看下打包输出的index.js文件,so big!!!

我们看下moment.js 的加载依赖

加载了local文件下的所有语言包,实际开发一般只需要几种就可以了,因此IgnorePlugin插件插件登场了。

let webpack = require('webpack')

 plugins: [
    new webpack.IgnorePlugin(/\.\/locale/,/monent/),
]

这个js是不是降了很多...
接下来我们在index.js中引入需要的语言包,代码如下。

import moment from 'moment'
// 手动引入语言
import 'moment/locale/zh-cn'
// 设置语言
moment.locale('zh-cn')
let t = moment().endOf('day').fromNow()
console.log(t)

三、dllPlugin 插件实现动态链接库

动态链接库是比较重要的优化,当项目引用的三方库增多,打包的时间会大大增多(特别是热更新的时候),因此我们需要把三方库打包成动态链接库,我们还是以moment为例。

  • 1.重新创建一个webpack.dll.config.js打包三方库。
let path = require('path')
let webpack = require('webpack')
module.exports = {
    mode:'development',
    entry: {
        moment:['moment'] // 打包的moment库
    },
    output: {
        filename: "_dll_[name].js", // _dll_moment.js
        path: path.resolve(__dirname,'dist/static'),
        library: '_dll_[name]', // _dll_moment
        libraryTarget: "var" // commonjs,var等输出类型
    },
    plugins: [
        new webpack.DllPlugin({
            name: "_dll_[name]",
            path: path.resolve(__dirname,'dist/static','manifest.json')
        }),
        new webpack.IgnorePlugin(/\.\/locale/,/moment/),//这里去除local引用,上面已经介绍了
    ]
}
  • 2.在package.json中创建npm打包脚本
"dll": "webpack --config webpack.dll.config.js"
  • 3.运行npm run dll 看下输出。

  • 4.在工程webpack.base.js创建对动态库的链接。
plugins: [
        // new webpack.IgnorePlugin(/\.\/locale/,/moment/),
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname,'dist/static','manifest.json')
        }),
        ...
    ]

  • 5.在需要引用的html中加入script引用。
<script src="./static/_dll_moment.js"></script> // 根据你的输出路径进行修改
  • 6.运行npm run build看看效果,打包只有了3k+的大小。

看到这里你是不是有想法了,这样不仅是加快了打包的速度,还有js是不是也可以分包分模块引用呢!!!

五、happypack 多线程打包。

这个模块也是增加打包速度的,当项目依赖比较多的时候效果比较明显,这边就介绍下他的用法。

  • 1.npm i happypack -D下载插件。
  • 2.webpack.base.js代码
let HappyPack = require('happypack') //定义

module:{
        {
            test:/\.js$/,
            use:'HappyPack/loader?id=js' //定义js使用happypack打包
        },
        {
            test: /\.css$/, //正则匹配css文件
            use: 'HappyPack/loader?id=css'//定义css使用happypack打包
        },
        ...
    }
plugins: [
        // new webpack.IgnorePlugin(/\.\/locale/,/moment/),
        // new webpack.DllReferencePlugin({
        //     manifest: path.resolve(__dirname,'dist/static','manifest.json')
        // }),
        new HappyPack({
           id:'js',
            use:[
                {
                    loader: "babel-loader",
                    options: {
                        presets:[
                            '@babel/preset-env' //包含es6 转 es5模块
                        ],
                        plugins:[
                            '@babel/plugin-proposal-class-properties'
                        ]
                    }
                }
            ]
        }),
        new HappyPack({
           id:'css',
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: "css-loader"
                },
                {
                    loader: "postcss-loader"
                }
            ]
        }),
        ...
    ]

执行npm run build看下结果吧,开启了三个线程处理。

四、抽离公共代码

为了加深理解,我创建两个文件a.js b.js 假设是工具js,在创建index.jstest.js都引用了上面两个文件。
a.js

console.log('a')

b.js

console.log('b')

index.js

import  './a'
import './b'
console.log('index.js')

test.js

import './a'
import './b'
console.log('test.js')

然后在webpack.base.js做如下配置

entry: {
        index: path.resolve(__dirname,'src/index.js'),
        test:path.resolve(__dirname,'src/test.js')
    }, //入口
    output: {
        filename: '[name].js', // 默认是main.js 这里修改成index.js
        path: path.resolve(__dirname,'dist')  // 路径必须是一个绝对路径
    },

执行npm run build看下打包出的index.js和test.js文件, 很容易看出,这两个文件都引用了a.js、b.js!在多页开发中还是很影响性能的。
因此优化是需要把a.js和b.js抽离打包的,怎么做呢?我们需要在webpack配置如下代码。

optimization: {
        splitChunks: {//分割代码块
            cacheGroups: { // 缓存组
                common:{ // 公共模块代码
                    minSize: 0, //文件大小大于0自己就抽离
                    minChunks: 2, // 引用两次抽离
                    chunks: "initial"// 从入口开始抽离
                }
            }
        }
    },

现在执行下npm run build,已经抽离出来了。

现在已经完成了自己代码的抽离,在实际开发中还需要抽离出三方库,因此上面的配置还需要修改下!

optimization: {
        splitChunks: {//分割代码块
            cacheGroups: { // 缓存组
                common:{ // 公共模块代码
                    minSize: 0, //文件大小大于0自己就抽离
                    minChunks: 2, // 引用两次抽离
                    chunks: "initial"// 从入口开始抽离
                },
                vendor:{
                    priority: 1, //优先抽离三方模块
                    test: /node_modules/, // 检测node_modules模块
                    chunks: "initial"// 从入口开始抽离
                }
            }
        }
    },

npm run build看下输出,已经成功抽出了三方库。

上面的打包的js还是index.html中,如果多页引用,请参考2.1 webpack 之 模块拆分打包配置
小结:三方库的抽离还是需要看具体优化需求的,有时候dllPlugin动态链接方式结构会更清晰。

六、懒加载

懒加载的理解就是有需求的时候在加载资源文件
下面就给个需求说明下,我们点击按钮,动态加载js并执行,这里需要一个@babel/plugin-syntax-dynamic-import,这里我已经安装了,只需要配置下。

use:[
                {
                    loader: "babel-loader",
                    options: {
                        presets:[
                            '@babel/preset-env' //包含es6 转 es5模块
                        ],
                        plugins:[
                            '@babel/plugin-proposal-class-properties',
                            '@babel/plugin-syntax-dynamic-import'
                        ]
                    }
                }
            ]

这个工程工程比较多,这边在重新创建一个source.js,代码就写

module.exports = 'js被加载了'

index.html 中加入一个按钮事件

<button class="btn-danger btn" onclick="getUserAction()">提交</button>

index.js 中实现按钮点击动态加载source.js

let getUserAction = () =>{
    import('./source').then(data => {
        console.log(data.default)
    })
}
window.getUserAction = getUserAction; //挂载到window上

现在直接npm run dev 看下结果

点击提交按钮,动态加载了js并输出了结果。

七、webpack 热更新插件

webpack已经提供了热更新插件,这里只需要在开发模式下配置下即可。

let webpack = require('webpack')
 devServer: {
    hot:true,
    ...
 }
 plugins: [
        new webpack.NamedModulesPlugin(),//打印热更新模块
        new webpack.HotModuleReplacementPlugin(),//人更新插件
        ...
    ]

这里还是用第五节的source.js进行修改热更新,index.js里面代码如下。

import str from './source'
console.log(str)
if (module.hot){
    module.hot.accept('./source',()=>{
        console.log('文件更新了')
    })
}

npm run dev ,对比发现,在没有配置热更新的时候,修改代码浏览器会刷新页面!配置了之后,只会修改相应的模块,并不会刷新页面。

源码下载

创建文件夹,cd至已创建文件夹,执行 git clone https://gitee.com/dolan_ge/webpack.git -b webpack_optimize

进入工程目录,执行 npm install -D