webpack配置-优化篇

2,245 阅读2分钟

noParse

针对没有AMD/CommonJS的源代码,通过配置noParse忽略webpack对其进行递归解析和处理,提高构建性能。比如jq,庞大又没有采用模块化标准,让webpack去解析是没有意义的。

noParse: /jquery/,// 不解析jquery中的依赖库

IgnorePlugin

忽略第三方包指定模块,使其不被打包进去。比如moment.js这个日期处理库,引用时会将所有的locale文件引入,导致打包体积比较大。可以通过webpack的IgnorePlugin,忽略locale下的文件,使用时只加载需要的语言包,较少打包体积

// webapck.config.s
plugins: [ 
    new webpack.IgnorePlugin(/\.\/locale/, /moment/), 
]

// index.js
import moment from 'moment';
import 'moment/locale/zh-cn'; // 主动引入所需语言包
moment.locale('zh-cn'); // 设置语言 
let r = moment().endOf('day').fromNow();
console.log(r)

dllPlugin

DllPlugin: 即动态链接库插件,对于复用性较高的第三方模块,对其抽离打包到动态链接库中,再次构建时不需要重新打包此模块,只需重新打包业务代码,从而提升构建速度。 对于第三方模块,通过webpack进行打包后,需要定义一个变量来接收打包后的输出。可以通过library中指定这个变量

 // webpack.config.react.js 实现动态链接库功能的webpack文件
let path = require('path');
let webpack = require('webpack');

module.exports = {
    mode: 'development',
    entry: {
        react: ['react', 'react-dom'],
    },
    output: {
        filename: '_dll_[name].js', // 产生的文件名
        path: path.resolve(__dirname, 'dist'),
        library: '_dll_[name]', // 导出值 如果生成的输出文件,是在HTML页面中作为一个 script 标签引入,则变量 '_dll_[name]'应与入口文件的返回值绑定。
        libraryTarget: "window". // 配置如何暴露library,如果commonjs var this
    },
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]', // name和上面library的值对应
            path: path.resolve(__dirname, 'dist', 'manifest.json'), // 生成manifest.json任务清单 根据清单查找对应文件
        }),
    ],
};

// webpack.config.js 正式的webpack文件
plugins: [
    new webpack.DllReferencePlugin({ // 打包时 根据manifest.json引用动态链接库中文件
        manifest: path.resolve(__dirname, 'dist', 'manifest.json')
    }), 
],

// index.html引入打包好的文件
<body>
    <div id='root'></div>
    <script src="/_dll_react.js"></script>
</body>

在进行打包时,先根据manifest.json任务清单查找是否已经打包。如果在清单中没有找到,再去打包配置的第三方库。

Happypack

运行在Node.js上的webpack是单线程模型的,Happypack可以将文件解析任务分解成多个子进程并发执行,子进程处理完任务后再将结果发送给主进程,提升项目构件速度。

module: {
        rules: [{
        test: /\.js$/,
        use: 'Happypack/loader?id=js', // 将原来的babel-loder等替换为happypack-loader,在插件中使用happypackLoader
        exclude: /node_modules/,
    }],
},

plugins: [
    new Happypack({
        id: 'js',
        use: [{
            loader: 'babel-loader',
            options: {
                presets: [
                    '@babel/preset-env',
                    '@babel/preset-react',
                ],
            },
        }], 
    }),
],

如果文件很小,是用多线程打包时速度可能更慢,因为分配现成也会花费一些时间

webpack自动优化

tree-shaking

用于描述移除JavaScript上下文中的未引用代码, 生产环境打包时,会自动移除js中未被引用代码,仅适用于import语法,require不支持。 在开发环境可以通过配置tree-shaking主动移除,

import calc from './test.js';
// let calc = require('./test.js'); // es6模块会把结果放在default上
console.log(calc.default.sum(1, 2));

scope-hosting 作用域提升

webpack的引入是把各个模块分开,通过__webpack_require__导入导出模块。scope-hosting可以把需要导入的文件直接移入导入者顶部,代码量明显减少,并且,不用多次使用__webpack_require__调用模块,运行速度也会得到提升。

let a= 1, b = 2, c = 3, d = 4;
let e = a + b + c + d;
console.log(e);

splitChunks抽离公共代码块

适用于多页应用,替换原来的commonChunkPlugins

optimization: {
    splitChunks: {
        cacheGroups: {
            common: { // 对引用的公共模块抽离
                minSize: 0, // 超过0个字节即进行抽离
                minChunks: 2, // 公用次数超过2次即抽离
                chunks: 'initial', //从入口开始
            },
            vendor: { // 将引用的第三方模块进行单独抽离
                test: /node_modules/,
                minSize: 0,
                minChunks: 2,
                chunks: 'initial',
                priority: 1, // 指定权重, 先抽离第三方模块,在抽离common.否则会把第三方和其他代码抽离在同一个文件中
            }
        },  
    }
},

相关学习: zxc0328.github.io/2018/06/19/…

懒加载 @babel/plugin-syntax-dynamic-import

// webpack.config.js
module: {
    rules: [{
        test: /\.js$/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: [
                    '@babel/preset-env',
                    '@babel/preset-react',
                ],
                plugins: [
                    '@babel/plugin-syntax-dynamic-import' // 使用懒加载的插件
                ],
            },
        }, 
    }],
}

// index.js
let btn = document.createElement('button');
btn.innerHTML = 'hello';
btn.addEventListener('click', () => {
    // es6草案中语法 通过jsonp实现动态加载文件,加载完返回一个promise
    import('./source.js').then(data => {
        console.log(data.default); // 结果挂载在default属性中
    });
    // console.log('click');
});
document.body.appendChild(btn);

热更新HMR

HotModuleReplacementPlugin允许在运行时更新所有类型的模块,而不需要完全刷新。不要在生产环境(production)下启用HMR。

devServer: {
    hot: true, // 启动热更新
    port: 3000,
    open: true,
    contentBase: './dist',
},
plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html',
        filename: 'index.html',
    }), 
    new webpack.HotModuleReplacementPlugin(), // 热更新插件
    new webpack.NamedModulesPlugin(), // 打印更新的模块路径
],

// index.js
import str from './source.js';
if (module.hot) { // 添加接收模块更新的代码
    module.hot.accept('./source.js', () => {
        let str = require('./source.js').default;
        console.log(str, '文件更新了');
    })
}