美文网首页
Webpack性能优化

Webpack性能优化

作者: smartHui | 来源:发表于2019-11-08 10:51 被阅读0次

    源码地址:https://github.com/h2huanghui/WEBPACK-OPTIMIZATION

    1. 基本配置见webpack.config.js babel如下

    {
        test: /\.js$/,
        use: 'babel-loader',
    },
    

    =>

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

    2. CSS优化,尽量去删除无用的样式(CSS样式名尽量不要去html标签相同)

    npm i purgecss-webpack-plugin glob -D

    const glob = require('glob') //主要功能是查找匹配文件
    let paths = glob.sync('./src/**/*')//任意文件夹的任意文件
    console.log(paths)
    结果:
    [ './src/index.css',
      './src/index.js',
      './src/main.js',
      './src/template.html' ]
    

    注意:

    1. 不能在style中删除 只能配合MiniCssExtractPlugin使用

    2. 动态生成的样式名,可以单独放在一个文件里面,不要去匹配就可以了,语法glob参考文档

    3.图片压缩插件(降低分辨率和清晰度)

    npm install image-webpack-loader -D
    

    在file-loader之前使用压缩图片插件 (对源文件处理不太合理)

    npm run build之后,可以看到图片大小被压缩了

    4.CDN加载文件 (将jquery不打包)

    Bootcdn

    1. 在html中插入一个script标签

    问题:存在一个问题。js中引入jquery,还是会打包。所以加一个外部变量,这样就不会被打包了。

     externals: {
                'jquery':'$' //不去打包代码中的jquery
            },
    
    1. 如果是多个文件,需要多次引入。可以通过js来动态插入
    npm i add-asset-html-cdn-webpack-plugin -D
    
    new AddAssetHtmlCdnPlugin(true, {
        'jquery':'https://cdn.bootcss.com/jquery/3. jquery.min.js'
    }),
    

    5.Tree-shaking

    随着项目变大时,摇晃掉无用的代码(只支持es6语法,而且默认只在生产环境使用!!!)

    calc.js

    export const sum = (a, b) => {
        return a + b + 'sum'
    }
    
    export const minus = (a, b) => {
        return a - b + 'minus'
    }
    
    
    

    main.js

    import { minus } from './calc'
    console.log(minus(1,1))
    

    只使用到minus,没有使用到sum 。希望开发环境模拟线上打包时,能有提示

    optimization: {
        usedExports: true //把用到的模块和我说一下
    },
    
    

    会发现bundle.js中多了注释如下:

    /*! exports provided: sum, minus */(本来就有)
    /*! exports used: minus */
    

    5.1会存在一个问题。

    main.js中引入了test.js,但是main.js引入了但是并未使用。test.js代码如下:

    function test() {
        return 'hello'
    }
    console.log(test()) 
    export default test
    

    test.js内部自己执行了代码。(被称为副作用,有可能是外面文件引错了,但代码本身还是执行了)

    webpack不认为这是无用的代码,还是执行到代码。所以需要我们配置来手动删除

    package.json中

    "sideEffects":false, //默认是true(使用副作用),设置为false(不要副作用)
    

    5.2这种方式存在一个问题,即在main.js中引入css文件(css引入但也不会被去使用,就会也把这行代码删除失效)

    1. 通过require(只针对es6语法,但有些格格不入)
    2. package.json配置css文件不是副作用
    "sideEffects": ["**/*.css"],
    

    6.Scope Hoisting(wepack内置) 作用域提升(只在生产环境使用)

    每个模块都是函数,会导致内存过大。(在浏览器中跑的时候,都会产生一个作用域;一声明也会有作用域)

    7.DllPlugin (动态连接库,可以用作生产环境,可能有100多个,会有更好地方法)

    dll功能在开发之前 就先抽离好 打好包 以后就不用打包了

    场景:每次编译都会重新构建react 和 react-dom

    react react-dom先打包好放好。

    但是问题是,代码中还是引入的node modules下的啊...

    建立缓存列表 mainfest.json(先去找这个里面有没有)

    react:打包后的文件

    react-dom:打包后的文件

    如何打包第三方库!!!

    打包单个文件webpack.dll.js

    const path = require('path')
    module.exports = {
        mode: 'development',
        entry: './src/calc.js', //sum minus
        output: {
            library: 'calc', //打包后接收自执行函数的名字叫calc
            libraryTarget:'commonjs2', //默认用var模式,也可以用commonjs模式(结果是exports["calc"]),也可以是commonjs(module.exports) umd this 
            filename: 'calc.js',
            path: path.resolve(__dirname,'dll')
        }
    }
    

    package.json

     "dll":"webpack --config webpack.dll.js"
    
    

    打包多个文件,entry改为数组

    const path = require('path')
    module.exports = {
        mode: 'development',
        entry: ['react','react-dom'], 
        output: {
            library: 'react', //打包后接收自执行函数的名字叫calc
            // libraryTarget:'commonjs2', //默认用var模式,也可以用commonjs模式(结果是exports["calc"]),也可以是commonjs(module.exports) umd this 
            filename: 'react.dll.js',
            path: path.resolve(__dirname,'dll')
        }
    }
    

    需要产生一个缓存列表,这个列表需要指向真实的文件

       plugins: [
            new DllPlugin({
                name: 'react', //缓存列表指向真实的文件
                path: path.resolve(__dirname,'dll/mainfest.json')
            })
        ]
    

    本地使用了import React语法 ,需要去mainfest.json查找,找到后会加载对应的库的名字(也就是react.dll.js文件),可能会引用某个模块(列表中的某个模块),会去dll.js文件中查找(这里面存放的就是真实的文件)

    告诉webpack去mainfest.json里面去查找

    const DllReferencePlugin = require('webpack').DllReferencePlugin;
     new DllReferencePlugin({
        manifest: path.resolve(__dirname,'dll/mainfest.json')
    })
    

    没有放在dist文件夹的原因是,build的时候配置了 cleanWebpackPlugin。每次一大包删掉就没有意义。打包好就放在那里

    问题:dll下的react.dll.js并未在页面中被引用

    npm run dev报错

    external_"react":1 Uncaught ReferenceError: react is not defined
    
    1. 可以在html中插入script标签(比较麻烦)
     <script src="../dll/react.dll.js"></script>
    
    1. 插件(和cdn类似)
      npm i add-asset-html-webpack-plugin -D
    //把这个文件拷贝到dist文件夹下
    new AddAssetHtmlPlugin({ filepath: require.resolve('dll/react.dll.js') }),
    

    8.动态加载(原理就是jsonp)

    比如页面一个按钮,只有点击了才调用。但如果在页面初始化的时候,就去打包,性能会比较差。我们希望的是点击按钮的时候,才会动态加载这个文件

    import { sum } from './calc'
    let button = document.createElement('button')
    
    button.addEventListener('click', () => {
        console.log(sum(1, 2))
    })
    button.innerHTML = 'Click Me'
    document.body.appendChild(button)
    

    =>

    let button = document.createElement('button')
    
    button.addEventListener('click', () => {
        //草案语法 
        //动态加载 类似于路由懒加载 import()语法-结果是一个promise
        //webpack遇到这种语法,会使用jsonp动态加载这个calc文件
        import('./calc').then(data => {
            console.log(data.sum(1,7))
        })
    })
    button.innerHTML = 'Click Me'
    document.body.appendChild(button)
    

    点击按钮,可以看到浏览器请求0.bundle.js(代码分割被分割了)

    0可以修改(chunkFilename)

    output: { //出口
        filename: 'bundle.js', //同步打包的名字
        chunkFilename: '[name].min.js', //异步打包的名字,name默认是从0开始,1,2, ...也可以修改
        path: path.resolve(__dirname, "dist")
    },
    
    import(/* webpackChunkName:'video' */'./calc').then(data => {
        console.log(data.sum(1,7))
    })
    
    

    9.实现多入口打包

     entry: {
                'a': './src/a.js',
                'b': './src/b.js'
            },
    

    这个时候npm run build会报错,因为出口文件还是只有一个bundle.js

    Conflict: Multiple chunks emit assets to the same filename bundle.js (chunks a and b)
    

    需要修改output,这里的name就是对应的entry里面的a和b,最终打包到dist里面的就是a.js和b.js

      output: { //出口
                filename: '[name].js', //同步打包的名字
                chunkFilename: '[name].min.js', //异步打包的名字,name默认是从0开始,1,2,...也可以修改
                path: path.resolve(__dirname, "dist")
            },
    

    但这个时候a.js和b.js都在index.html中,而我们需要的是分别在两个html中引入

     new HtmlWebpackPlugin({
                    template: './src/template.html',
                    filename: 'index.html',
                    chunks:['a']
                }),
                new HtmlWebpackPlugin({
                    template: './src/template.html',
                    filename: 'login.html',
                    chunks:['b']
                }),
    

    如果一个login.html想先引入b,再引入a,需要手动配置

       new HtmlWebpackPlugin({
                    template: './src/template.html',
                    filename: 'login.html',
                    chunksSortMode:'manual',//手动排序
                    chunks:['b','a'] //(默认顺序以entry的顺序) 按照我自己配置的顺序引入,先引入b,再引入a。
                }),
    

    应用场景是,entry下面还有个common,需要引入common再引入具体的其他模块

    多入口好处,首页和登录页,引入的js不一样

    10.打包文件分析工具(依赖关系以及打包大小)

    npm install webpack-bundle-analyzer -D

    场景1:a.js和b.js都引入了jq,那么在访问login.html会加载一次jq,访问index.html还会再加载一次jq,不太合理

    解决方案-把jq抽取

    1)不和业务逻辑放在一起

    2)增加缓存(文件不变,可以从缓存中读取)

    11.在生产环境下, 将第三方模块进行抽离(开发环境也可以)

    参考webpack官方配置

     optimization: {
                splitChunks: {
                    chunks: 'async', //默认支持异步代码的代码分割 impot()
                    minSize: 30000, //文件超过30k 就会抽离
                    maxSize: 0,
                    minChunks: 1, //最少模块引用一次才抽离
                    maxAsyncRequests: 5, //最多5个请求
                    maxInitialRequests: 3, //最多首屏加载3个请求
                    automaticNameDelimiter: '~', //xxx~a~b 
                    automaticNameMaxLength: 30, //最长名字大小不超过30
                    name: true,
                    cacheGroups: { //缓存组
                        vendors: {
                            test: /[\\/]node_modules[\\/]/,
                            priority: -10
                        },
                        common: { // default~a~b 改成common
                            name: 'common',
                            minChunks: 2, //把上面的覆盖掉
                            priority: -20,
                            reuseExistingChunk: true
                        }
                    }
                }
            },
    

    12. 代码分割(主要用作上线前,分割第三方代码 也可以用于抽离自己写的js文件)

    chunks:

    值有initial 同步代码被抽离

    all 所有代码

    async(默认) 异步代码 import('')

    注意:dllPlugin不要和 splitChunks共同使用

    dllPlugin和代码分割区别:

    1. dllPlugin是在构建之前(提高开发的打包速度)

    2. 代码分割是在编译时(上线前把第三方代码分割出来)

    13. 费时分析

    npm install --save-dev speed-measure-webpack-plugin

    smp.wrap

     SMP  ⏱
    General output time took 8.21 secs
    
     SMP  ⏱  Plugins
    HtmlWebpackPlugin took 5.11 secs
    CleanWebpackPlugin took 0.02 secs
    
     SMP  ⏱  Loaders
    D:\frontEnd\WEBPACK-OPTIMIZATION\node_modules\babel-loader\lib\index.js took 7.84 secs
      module count = 50
    modules with no loaders took 4.069 secs
      module count = 3
    D:\frontEnd\WEBPACK-OPTIMIZATION\node_modules\html-webpack-plugin\lib\loader.js took 0.046 secs
    

    14.热更新

    15 懒加载

    相关文章

      网友评论

          本文标题:Webpack性能优化

          本文链接:https://www.haomeiwen.com/subject/rbjibctx.html