自从webpack4当前,官网帮咱们集成了很多个性,比方在生产模式下代码压缩主动开启等,这篇文章咱们一起来探讨一下webpack给咱们提供的高级个性助力开发。

摸索webpack的高级个性

个性:treeShaking

顾名思义treeShaking,就是摇树,那么体现在代码模块外面就是摇掉那些没有被内部成员援用的代码,指的留神的是在生产环境下treeShaking会主动开启。

treeShaking初体验

比方咱们在代码中引入lodash库,咱们只用到了once办法,那对于lodash其余的功能模块,在生产环境下打包,并不会输入到bundle.js文件外面,比方咱们在bundle.js外面去找lodash的一个办法debounce,他是齐全能够找失去的。

delelopment模式下打包的bundle.js

production模式下打包的bundle.js

在这里你可能会说了production模式下会开启n多插件,解决打包后果,怎么就能阐明是treeShaking做的呢,的确这种做法不能阐明是treeShaking做的,咱们能够把mode设置为none再试一下,不过这里须要咱们手动去开启treeShaking,开启的形式如下。

// webpack.config.jsmodule.exports = {    ...    optimization: {        usedExports: true, // 只导出内部成员援用模块        // 此属性用于模块导入合并,因为独自的模块导入要应用_webpack_require_函数。        // 此属性就是能够利用_webpack_require_一次性导入所有模块,也叫作用域晋升。        concatenateModules: true,         minimize: true, // 开启代码压缩    }    ...}
none模式下打包的bundle.js

所以none模式下,打包的后果仍然如此。

扩大

因为treeShaking是依赖于ESM的,如果我的项目中有配合应用babel-loader那么treeShaking是不是会生效呢?咱们能够在配置文件外面增加babel-loader来辅以测试。

// 装置npm i babel-loader @babel/core @babel/preset-env -D// webpack.config.jsmodule.exports = {    ...    module:[        {            test:/\.js$/,            use:{                loader:'babel-loader',                options:{                    presets:[                        ['@babel/preset-env']                    ]                }            }        }    ]}
文件成果

咱们能够看到没有应用的代码,仍然是被移除掉了。

起因剖析

因为babel-loader禁用了对ESM转化插件,所以通过babel-loader解决生成的仍旧是ESM代码,如果你想应用代码转换性能,那你就须要像上面这样配置,只不过这样treeShaking就会生效了。##### 参考webpack视频解说:进入学习

// 装置npm i babel-loader @babel/core @babel/preset-env -D// webpack.config.jsmodule.exports = {    ...    module:{        rules:[            {                test:/\.js$/,                use:{                    loader:'babel-loader',                    options:{                        presets:[                            // 强制应用commonjs转换                            ['@babel/preset-env', {modules: 'commonjs'}]                        ]                    }                }            }        ]    }}

那么treeShaking生效了,应该怎么办?不要怕,即便生效了还会有其余插件提供了相似treeShaking性能,比方代码压缩。

个性: sideEffect

sideEffect示意的意思就是副作用,了解起来并不难,比方内部成员援用了以后模块,那么以后模块必定是不会被treeShaking的,如果在以后模块外面写了冗余的代码,那么sideEffect就是去除这些冗余代码的,以达到更高的提效能力。

sideEffect的根底实际

这里咱们应该在webpack.config.js外面开启sideEffect,在package.json外面指定具备副作用的模块。

// webpack.config.jsmodule.exports = {    ...    optimization: {        sideEffect: true    }    ...}// package.json{    "scripts": {},    "sideEffect": [        // 告知webpack此文件具备副作用        "./src/app.js",        // *通配符css文件        "*.css"    ]}

个性: CodeSplitting分包策略

CodeSplitting分包策略旨在解决单入口打包导致bundle.js文件过大,从而导致浏览器http加载速度过慢造成页面短暂白屏状况,分包策略具备三种常见施行形式。

  • 依据我的项目背景,多入口打包。
  • 联合ESMDynamic import个性,按需加载模块。
  • 对第三方包应用拆包策略。

多入口打包的具体实际

多入口打包体现在多页利用,每一个页面依赖于一个打包文件,对于模块中的公共代码进行提取到公共后果中。

module.exports = {    entry: {            index: "./src/index.js",            add: "./src/add.js",    },    optimization: {        splitChunks: {            // 主动提取到一个公共的bundle.js中            chunks: "all"        }    }    plugins:[        ...        new HtmlWebpackPlugin({            filename: 'anout.html',            template: './aout.html',            chunks:['add']        }),        new HtmlWebpackPlugin({            titie: 'title',            template: './index.html',            meta:{                viewport: 'width=device-widt, initial-scale=2.0'            },            filename: 'index.html',            publicPath: './',            scriptLoading: 'module',            chunks:['index']        })        ...    ],    ...}

比方在index.js、add.js外面抖音用到了once办法,webpack就会提取公共的lodash到单的文件外面,在两个页面外面会通过script引入。

Dynamic import的按需加载实际

在选项卡切换场景下,在利用程序运行的过程中,只有当用户点击某个模块,才会对应去加载某个模块,大大的缩小了启动时须要加载模块的体积,升高了浏览器网路的带宽的占用,进步了利用的响应率。

const hash = window.location.hash;const container = document.getElementById('app');switch(hash){    case 'title_1':     import('./title_1.js').then({default:title_1}=>{        container.appednChild(title_1())    });    break;    case 'title_2':     import('./title_2.js').then({default:title_1}=>{        container.appednChild(title_2())    });    break;    case 'title_3':     import('./title_3.js').then({default:title_1}=>{        container.appednChild(title_3())    });    break;    default:}

按需加载的确不须要在首屏的时候一次性把文件全副加载结束,因为首屏并不须要所有模块,加载了也是节约

第三方包拆包策略

所谓三方包,在在多入口外面也提到过optimization.splitChunks只是一种提取三方包的形式,咱们当初要讲的是插件层面的DllPluginDllReferencePlugin,这个插件的意义更为广大一点,比方相似vuereact等三方包,配合着咱们的我的项目代码,只须要首次构建一次,再次构建webpack就会跳过这些依赖包,只有咱们不手动降级依赖包,那将会是永久性的缓存。

应用步骤
  1. 新建webpck.dll.config.js文件,写上如下内容。
const path = require('path');const DllPlugin = require('webpack/lib/DllPlugin');module.exports = {  // 入口文件  mode: "development",  entry: {    // 我的项目中用到该两个依赖库文件    lodash: ['lodash'],  },  // 输入文件  output: {    // 文件名称    filename: '[name].dll.js',     // 将输入的文件放到dist目录下    path: path.resolve(__dirname, 'vandor'),    /*     寄存相干的dll文件的全局变量名称,比方对于lodash来说的话就是 _dll_lodash, 在后面加 _dll     是为了避免全局变量抵触。    */    library: '_dll_[name]'  },  plugins: [    // 应用插件 DllPlugin    new DllPlugin({      /*       该插件的name属性值须要和 output.library保留统一,该字段值,也就是输入的 manifest.json文件中name字段的值。       比方在jquery.manifest文件中有 name: '_dll_jquery'      */      name: '_dll_[name]',      /* 生成manifest文件输入的地位和文件名称 */      path: path.join(__dirname, 'vandor', '[name].manifest.json')    })  ]};
  1. 在package.json外面新增命令 => "dll": "webpack --config webpack.dll.config.js"并执行,生成文件。

  1. 引入文件的依赖关系
const webpack = require('webpack');module.exports = {    plugins:[        ...        new webpack.DllReferencePlugin({           manifest: require('./vandor/lodash.manifest.json')        }),        ...    ]}

个性: 魔法正文

在分包或者定义其余模块的时候,咱们想给模块定义一个名称,那就能够应用如下形式。

/* webpackChunkName:'<chunkName>' */

摸索webpack带来的前端性能优化

在前几篇文章外面咱们就晓得了webpack通过mode来提供了nonedevelopmentproduction三种预设配置。每一种配置都会选择性的加载某些插件来优化我的项目的构建,然而作为一个开发者咱们该当去关注非主动的性能配置,上面咱们来一起摸索一下在开发中应用到的配置能带来肯定的性能优化

为什么要进行性能优化

性能优化是前端开发的永久性话题,高性能利用的开发这是咱们的指标,然而指标总就是指标,具体实施还是要一步一块板砖,webpack在实际如此多的新个性的同时,会给咱们的打包后果带来具备影响的内容,比方sourceMap,上有政策下有对策,那么咱们的种种可优化的点就是解决问题的对策。

具体对策

那么咱们应该怎么样来进步构建速度与打包后果呢?
理论的开发中你总会见到咱们会对不同的环境配置不同的文件,依据env的不同来启用不同的配置。

// webpack.development.config.jsmodule.exports = {    mode:"development",    detool:"source-map"    ...}// webpack.production.config.jsmodule.exports = {    mode:"production",    detool:"nosources-source-map"    ...}// webpack.config.jsmodule.exports = (env, args) => {    // 公共配置    const config = {        module:{},        plugins:[],        ...    }   env === "development" ? require('./webpack.development.config.js'):require('./webpack.production.config.js')   return config;}
  • DefinePlugin定义全局变量,可用作baseUrl

    ...plugin:[    new webpack.DefinePlugin({        API_BASEURL:'https://www.yixizhishi.com'    })]...
  • MiniCssExtractplugin用来从js代码中提取css代码。
const MiniCssExtractPlugin = require('mini-css-extract-plugin');...plugin:[    new MiniCssExtractPlugin()],module:{    rules:[        {            test:/\.css/,            use:[                // 通过link标签引入到页面中                MiniCssExtractPlugin.loader,                cssloader            ]        }    ]}
  • optimizeCssAssetsWebpackPlugin,用来压缩css代码。

    • webpack中所谓压缩就是压缩js文件的,而css文件,须要咱们独自解决。
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');...// 非plugin中应用optimization:[    minimizer:[        new OptimizeCssAssetsWebpackPlugin()        new terser-webpack-plugin()    ]]
  • terser-webpack-plugin用于压缩js代码。

    • 如果在optimization选项中开启了minimizer属性,则会笼罩掉webpack自身的压缩性能,所以咱们须要手动增加压缩插件。
const terserWebpackPlugin = require('terser-webpack-plugin');...optimization:[    minimizer:[        // 压缩css        new OptimizeCssAssetsWebpackPlugin()        // 压缩js        new terserWebpackPlugin()    ]]

当然还有一些其余的配置呀,比方。

  • splitChunks的一些配置呀,也就是按你的需要拆包呀。
splitChunks: {    cacheGroups: {        commons: {            chunks: "initial",//雷同的chunks提出来            minChunks: 2,//依赖了两个以上的关系            minSize: 0 //这个依赖最小体积为0        },        vendor: {            test: /node_modules/,            // 默认选项,示意只有有依赖的第三方包就要拆出去,跟all差不多            chunks: "initial",            name: "vendor",            enforce: true        }    },}
  • cdn的引入三方包呀。
module.exports = {    ...    // 通过内部引入第三方包    externals:['jQuery','lodash']    ...}
  • 多线程打包的开启呀,比方happyPack

    • happyPack的工作原理就是把loader加载调配多个线程去解决,最初在对立调度起来,解决实现之后告诉webpack进行chunks的组合,输入bundle.js留神:并不是说多过程打包就肯定好,因为创立多线程的时候也会有性能开销,所以还是斟酌而行。
  • 应用include防止webpack解决不须要解决的模块文件,进步编译效率。
  • webpack5提供了webpack资源模块,来代替个别的loader解决文件,益处是可能解决不同类型的文件并且不再须要针对性的配置loader

  • resolve模块个别被人们忘掉了,不过在vue/react的脚手架中还是看见过它的身影,个别用于通知webpack以什么样的模式去解决文件,比方。

    • 别名:alias
    • 文件类型:extensions
    • 解析的模块范畴:modules
module.exports = {    resolve: {        alias:{            '@':'root/src' // 指定别名@,通过@能够找到文件目录        },        extensions:{            ['.jsx', '.tsx', '.vue'] // 指定webpack须要解析哪些类型的文件        },        modules:{            ['node_modules', 'root/src'] // 指定webpack须要解析那些范畴的文件        }    }}

写在最初

因为下面的一些优化伎俩涵盖了webpack5以及webpack5以前的个性,那么在这里提及一下webapck5中开箱即用的个性以及不再保护的老版本的个性吧。

  • 长久化缓存,应用cache之后咱们便不须要应用dll拆包、cache-loader了,而且是webpack5中提供的性能。
module.exports = {    cache: {      type: 'filesystem', // 文件系统    },}
  • thread-loader开启多线程打包,上述代码中提到了happypack,不过在webpack5当中,曾经不再去保护happypack了,咱们就应该应用thread-loader来放慢构建过程。

总结

上述解说的内容均是在开发环境下的的配置的一步步实现,当然在mode:"production"webpack会主动帮咱们做,所以在不依赖他人的状况下,还是本人配比拟好玩。下一章咱们就一起来摸索一下各大成熟框架是怎么配置webpack