zoukankan      html  css  js  c++  java
  • webpack 常见问题及优化

    Loaders

    1、文件Loader

    url-loaderfile loader 一样工作,可以返回 data URL

    {
         test: /.(jpg|png|gif|jpeg)$/,
         loader: 'url-loader',
         options:{
            name: 'img/[name]-[hash:8].[ext]',
            outputPath: 'img',
            esModule: false
         }
    }
    

    file-loader 将文件发送到输出文件夹,并返回相对URL

    {
          exclude: /.(html|js|jpg|css|less)$/,
          loader: 'file-loader',
          options: {
                name: '[hash:8].[ext]',
                outputPath: 'media'
          }
    }
    

    2、转换编译(Transpiling)

    Name Description
    script-loader 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析
    babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
    ts-loader awesome-typescript-loaderJavaScript 一样加载 TypeScript 2.0+
    coffee-loader JavaScript一样加载CoffeeScript`

    3、模板(Templating)

    Name Description
    html-loader 导出 HTML 为字符串,需要引用静态资源
    pug-loader 加载 Pug 模板并返回一个函数
    jade-loader 加载 Jade 模板并返回一个函数
    markdown-loader Markdown 转译为 HTML

    4、样式

    • style-loader 将模块的导出作为样式添加到 DOM
    • css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
    • less-loader 加载和转译 LESS 文件
    • sass-loader 加载和转译 SASS/SCSS 文件
    • postcss-loader 使用 PostCSS 加载和转译
     yarn add postcss-loader postcss-preset-env
    
    • stylus-loader 加载和转译 Stylus 文件

    5、清理和测试(Linting)

    eslint-loader 使用 ESLint语法检查 设置代码检查的规则:

    在package.json里面添加:

    eslintConfig:{
          extends: 'airbns'
    }
    

    添加依赖文件

    yarn add eslint-config-airbnb-base  eslint-plugin-import  eslint -D
    

    webpack.config.js 文件中

    {
          test: /.js$/,
          exclude: /node_modules/,
          loader: 'eslint-loader',
          options: {
                fix: true //自动格式化代码
          }
    }
    

    eslint-loader 使用 ESLint兼容性检查

    1、 preset-env 只能检查简单的语法。
    2、@babel/polyfull 能解决兼用性问题。但是全部引入体积太大了。
    3、 core-js第三方按需加载的包

    添加依赖文件

    npm install babel-loader @babel/core @bebel/preset-env -D 
    

    webpack.config.js文件中

    {
          test: /.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          options: {
                presets: [
                      @babel/preset-env,
                      {
                            useBuiltIns: 'usage',//按需加载
                            corejs: {
                                  versions: 3//指定版本
                            },
                            targets: {
                                  chrome: 60 //指定浏览器的版本
                            }
                      }
                ]
          }
    }
    

    Plugins

    1、HtmlWebpackPlugin 简单创建 HTML 文件

     plugins: [
          new HtmlWebpackPlugin({
                title: '',
                filename: '',
                template: './src/index.html',
                publicPath: '',
                favicon: '',
                neta: {}
          })
    ]
    

    压缩HTML文件

    plugins:[
          new HtmlWebpackPlugin({
                template: './src/index.html',
                minify: {
                      collapseWhitespace: true,
                      removeComments: true
                }
          })
    ]
    

    2、MinCssExtractPlugin 单独提取css文件

    plugins: [
          new MinCssExtractPlugin({
                filename: 'css/style.css',
                chunkFilename: ''
          })
    ]
    

    配合loader,postcss-loader 做浏览器的兼容性。添加了postcss-loader 后,要在package.json 里面配置browserslist:{development: {}, production: {}}对象。

    {
          test: /.css$/,
          use: [
                //style-loader
                MinCssExtractPlugin.laoder,
                css-loader,
                {
                      loader: 'postcss-loader',
                      options: {
                            ident: 'postcss',
                            plugins: () => require('postcss-preset-env')()
                      }
                }
          ]
    }
    

    3、OptimizeCssAssetsWebpackPlugin 压缩css

    plugins:[
          new OptimizeCssAssetsWebpackPlugin()
    ]
    

    一些小的优化点

    resolve.extensions:用来表明文件后缀列表,默认查找顺序是 ['.js', '.json'],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面。
    resolve.alias:可以通过别名的方式来映射一个路径,能让 Webpack 更快找到路径。

    module.exports ={
        // ...省略其他配置
        resolve: {
            extensions: [".js",".jsx",".json",".css"],
            alias:{
                "jquery":jquery
            }
        }
    };
    

    提高 Webpack 打包速度

    1、优化Loader搜索范围(include,exclude

    合理的使用exclude或者include的配置,来尽量减少loader被频繁执行的频率。当loader执行频率降低时,也会提升webpack的打包速度。比如:
    对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,我们是有办法优化的。
    首先我们可以优化 Loader 的文件搜索范围,在使用loader时,我们可以指定哪些文件不通过loader处理,或者指定哪些文件通过loader处理。

    module.exports = {
      module: {
        rules: [
          {
            // js 文件才使用 babel
            test: /.js$/,
            use: ['babel-loader'],
            // 只处理src文件夹下面的文件
            include: path.resolve('src'),
            // 不处理node_modules下面的文件
            exclude: /node_modules/
          }
        ]
      }
    }
    

    2、cache-loader缓存loader处理结果

    缓存分为 babel缓存和文件资源缓存

    Babel 缓存 cacheDirectory: true

    {
          cacheDirectory: true
    }
    

    文件资源的缓存

    1.hash 每次webpack构建时都会生成一个唯一的hash值 问题: 应为js和css同时使用一个hash值,如果重新打包就会导致缓存失效。
    2.chunkhash 根据chunk生产的hash值,如果打包来源于同一个chunk,那么同一个文件的js和css的hash值就时一样的。
    3.contenthash 根据文件的内容生成hash值,不同文件的hash值是不一样的。

    module.exports = {
      module: {
        rules: [
          {
            // js 文件才使用 babel
            test: /.js$/,
            use: [
              'cache-loader',
              ...loaders
            ],
          }
        ]
      }
    }
    

    3、使用多线程处理打包

    module: {
      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']
        }
      ]
    },
    plugins: [
      	new HappyPack({
            id: 'js', //ID是标识符的意思,ID用来代理当前的happypack是用来处理一类特定的文件的
            threads: 4, //你要开启多少个子进程去处理这一类型的文件
            loaders: [ 'babel-loader' ]
        }),
        new HappyPack({
            id: 'css',
            threads: 2,
            loaders: [ 'style-loader', 'css-loader' ]
        })
    ]
    
    

    4、DllPlugin&DllReferencePlugin 成动态链接库

    DllPlugin可以将特定的类库提前打包成动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据,把基础模块独立出来打包到单独的动态连接库里,当需要导入的模块在动态连接库里的时候,模块不用再次被打包,而是去动态连接库里获取。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
    这里我们可以先将react、react-dom单独打包成动态链接库,首先新建一个新的webpack配置文件:webpack.dll.js

    const path = require('path');
    const DllPlugin = require('webpack/lib/DllPlugin');
    module.exports = {
    	// 想统一打包的类库
        entry:['react','react-dom'],
        output:{
            filename: '[name].dll.js',  //输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
            path:path.resolve(__dirname,'dll'),  // 输出的文件都放到 dll 目录下
            library: '_dll_[name]',//存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
        },
        plugins:[
            new DllPlugin({
                // 动态链接库的全局变量名称,需要和 output.library 中保持一致
                // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
                // 例如 react.manifest.json 中就有 "name": "_dll_react"
                name: '_dll_[name]',
                // 描述动态链接库的 manifest.json 文件输出时的文件名称
                path: path.join(__dirname, 'dll', '[name].manifest.json')
            })
        ]
    }
    

    然后我们需要执行这个配置文件生成依赖文件:

    webpack --config webpack.dll.js --mode development
    

    接下来我们需要使用 DllReferencePlugin 将依赖文件引入项目中

    const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
    module.exports = {
      // ...省略其他配置
      plugins: [
        new DllReferencePlugin({
          // manifest 就是之前打包出来的 json 文件
          manifest:path.join(__dirname, 'dll', 'react.manifest.json')
        })
      ]
    }
    

    5、noParse 可以用于配置那些模块文件的内容不需要进行解析(即无依赖)

    module.noParse 属性,可以用于配置那些模块文件的内容不需要进行解析(即无依赖) 的第三方大型类库(例如jquery,lodash)等,使用该属性让 Webpack不扫描该文件,以提高整体的构建速度。

    module.exports = {
        module: {
          noParse: /jquery|lodash/, // 正则表达式
          // 或者使用函数
          noParse(content) {
            return /jquery|lodash/.test(content)
          }
        }
    }
    

    6、IgnorePlugin IgnorePlugin用于忽略某些特定的模块

    IgnorePlugin用于忽略某些特定的模块,让webpack 不把这些指定的模块打包进去。

    module.exports = {
      // ...省略其他配置
      plugins: [
        new webpack.IgnorePlugin(/^./locale/,/moment$/)
      ]
    }
    

    webpack.IgnorePlugin()参数中第一个参数是匹配引入模块路径的正则表达式,第二个参数是匹配模块的对应上下文,即所在目录名。

    7、打包文件分析工具

    webpack-bundle-analyzer插件的功能是可以生成代码分析报告,帮助提升代码质量和网站性能。

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
    module.exports={
          plugins: [
              new BundleAnalyzerPlugin({
                generateStatsFile: true, // 是否生成stats.json文件
              })  
            // 默认配置的具体配置项
            // new BundleAnalyzerPlugin({
            //   analyzerMode: 'server',
            //   analyzerHost: '127.0.0.1',
            //   analyzerPort: '8888',
            //   reportFilename: 'report.html',
            //   defaultSizes: 'parsed',
            //   openAnalyzer: true,
            //   generateStatsFile: false,
            //   statsFilename: 'stats.json', 
            //   statsOptions: null,
            //   excludeAssets: null,
            //   logLevel: info
            // })
      ]
    }
    

    使用方式:

    "generateAnalyzFile": "webpack --profile --json > stats.json", // 生成分析文件
    "analyz": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" // 启动展示打包报告的http服务器
    

    8、费时分析

    speed-measure-webpack-plugin,打包速度测量插件。这个插件可以测量webpack构建速度,可以测量打包过程中每一步所消耗的时间,然后让我们可以有针对的去优化代码。

    const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
    const smw = new SpeedMeasureWebpackPlugin();
    // 用smw.wrap()包裹webpack的所有配置项
    module.exports =smw.wrap({
        module: {},
        plugins: []
    });
    

    减少 Webpack 打包后的文件体积

    1、对图片进行压缩和优化

    image-webpack-loader这个loder可以帮助我们对打包后的图片进行压缩和优化,例如降低图片分辨率,压缩图片体积等。

    module.exports ={
        // ...省略其他配置
        module: {
            rules: [
                {
                    test: /.(png|svg|jpg|gif|jpeg|ico)$/,
                    use: [
                        'file-loader',
                        {
                            loader: 'image-webpack-loader',
                            options: {
                                mozjpeg: {
                                    progressive: true,
                                    quality: 65
                                },
                                optipng: {
                                    enabled: false,
                                },
                                pngquant: {
                                    quality: '65-90',
                                    speed: 4
                                },
                                gifsicle: {
                                    interlaced: false,
                                },
                                webp: {
                                    quality: 75
                                }
                            }
                        }
                    ]
                }
            ]
        }
    };
    

    2、删除无用的CSS样式

    有时候一些时间久远的项目,可能会存在一些CSS样式被迭代废弃,需要将其剔除掉,此时就可以使用purgecss-webpack-plugin插件,该插件可以去除未使用的CSS,一般与 glob、glob-all 配合使用。
    注意:此插件必须和CSS代码抽离插件mini-css-extract-plugin配合使用。
    例如我们有样式文件style.css:

    body{
        background: red
    }
    .class1{
        background: red
    }
    

    这里的.class1显然是无用的,我们可以搜索src目录下的文件,删除无用的样式。

    const glob = require('glob');
    const PurgecssPlugin = require('purgecss-webpack-plugin');
    
    module.exports ={
        // ...
        plugins: [
            // 需要配合mini-css-extract-plugin插件
            new PurgecssPlugin({
                paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, 
                      {nodir: true}), // 不匹配目录,只匹配文件
                })
            }),
        ]
    }
    

    3、以CDN方式加载资源

    我们知道,一般常用的类库都会发布在CDN上,因此,我们可以在项目中以CDN的方式加载资源,这样我们就不用对资源进行打包,可以大大减少打包后的文件体积。
    以CDN方式加载资源需要使用到add-asset-html-cdn-webpack-plugin插件。我们以CDN方式加载jquery为例:

    const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
    
    module.exports ={
        // ...
        plugins: [
            new AddAssetHtmlCdnPlugin(true,{
                'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
            })
        ],
        //在配置文件中标注jquery是外部的,这样打包时就不会将jquery进行打包了
        externals:{
          'jquery':'$'
        }
    }
    

    4、开启Tree Shaking (去除无用的代码,减少代码的体积。前提条件: 1.模块必须是ES6模块,2.必须是production环境)

    Tree-shaking,摇晃树。顾名思义就是当我们摇晃树的时候,树上干枯的没用的叶子就会掉下来。类比到我们的代码中就是将没用的代码摇晃下来,从而实现删除代码中未被引用的代码。
    这个功能在webpack4中,当我们将mode设置为production时,会自动进行tree-shaking

    在package.json 种配置 sideEffects: false 所有的代码都可以被tree-shaking 问题是: 可能吧css文件和 @babel/polyfill 等副作用全部干掉。要小心使用。
    解决方法 {sideEffects: ['*.css']}

    5、按需加载&动态加载

    必大家在开发单页面应用项目的时候,项目中都会存在十几甚至更多的路由页面。如果我们将这些页面全部打包进一个文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。那么为了首页能更快地呈现给用户,我们肯定是希望首页能加载的文件体积越小越好,这时候我们就可以使用按需加载,将每个路由页面单独打包为一个文件。在给单页应用做按需加载优化时,一般采用以下原则:

    • 对网站功能进行划分,每一类一个chunk
    • 对于首次打开页面需要的功能直接加载,尽快展示给用户,某些依赖大量代码的功能点可以按需加载
    • 被分割出去的代码需要一个按需加载的时机
      动态加载目前并没有原生支持,需要babel的插件:plugin-syntax-dynamic-import。安装此插件并且在.babelrc中配置:
    {
      // 添加
      "plugins": ["transform-vue-jsx", "transform-runtime"],
      
    }
    

    列如下面的实例:index.js

    let btn = document.createElement('button');
    btn.innerHTML = '点击加载视频';
    btn.addEventListener('click',()=>{
        import(/* webpackChunkName: "video" */'./video').then(res=>{
            console.log(res.default);
        });
    });
    document.body.appendChild(btn);
    

    webpack.config.js

    module.exports = {
        // ...
        output:{
          chunkFilename:'[name].min.js'
        }
    }
    

    6、这样打包后的结果最终的文件就是 video.min.js,并且刚启动项目时不会加载该文件,只有当用户点击了按钮时才会动态加载该文件。

    代码分割 code-slpit

    1、根据入口文件构建分割代码。

    1. 单入口打包,构建的就是一个文件,。
    module.exports = {
       enrty: './src/index.js',
       output: {
          filename: 'js/[name].js',
          path: path.resolve(__dirname, 'dist')
       }
    }
      
    

    2.多入口打包就会产生多个代码块

    module.exports = {
       enrty: {
          index: './src/index.js',
          home: './src/home.js'
       },
       output: {
          filename: 'js/[name]-[hash:8].js',
          path: path.resolve(__dirname, 'dist')
       }
    }
     
    

    2、用webapck自带的 optimization 1.来吧node_modules 单独打包到一个chunk输出 2.自动分析多入口文件种有没有公共的文件,如果有会打包成一个单独的chunk 。

    optimization: {
          splitChunks: {
                chunks: 'all'
          }
    }
    

    3、用js代码让文件单独打包成一个chunk 默认webapck会按文件的id打包生成文件名称,可以用 /webpackChunkName: 'abc'/ 修改生成的文件名。

    import(/*webpackChunkName: 'abc'*/'xxx.js').then(() => {
          console.log('success')
    }).catch(err){
          console.log('error')
    }
    
  • 相关阅读:
    12.使用正则表达式
    12/12
    thinkphp 5 及一下或php项目里实现模糊查询
    mysql中文乱码--存入mysql里的中文变成问号的解决办法
    ATOM使用的一点心得与技巧——在一个窗口打开多个项目
    php里的$this的 含义
    pycharm2017.3专业版激活注册码
    thinkphp3.2.3的使用心得之i方法(零)
    thinkphp3.2.3的使用心得(零)
    linux系统下phpstudy里的mysql使用方法
  • 原文地址:https://www.cnblogs.com/boyGdm/p/14086671.html
Copyright © 2011-2022 走看看