• webpack优化配置


    HMR热模块替换

    HMR:hot moddule replacement 热模块替换
    作用:一个模块发生变化,只会重新打包这个模块,极大的提升了构建速度
    样式文件:可以使用HMR,style-loader内部实现
    js文件:默认没有HMR功能,需要修改js代码,添加支持该功能的代码
    HMR功能对js的处理,只能处理非入口文件的js文件
    html文件: 默认没有HMR功能,同时会导致js文件不能热更新了(不需要HMR)
    解决(热更新):修改entry,将HTML文件引入 entry: ['./src/index.js', './src/index.html']

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: ['./src/index.js', './src/index.html'],
      output: {
        filename: 'build.js',
        path: resolve(__dirname, 'build'),
      },
      module: {
        rules: [
          {
            test: /.css$/,
            use: ['style-loader', 'css-loader'],
          },
          {
            test: /.(jpg|png|gif)$/,
            loader: 'url-loader',
            options: {
              linit: 8 * 1024,
              name: '[hash:10].[ext]',
              exModule: false, // 关闭es6模块化
            },
            // outputPath:'img' 输出到build/img文件夹
          },
          {
            // 处理html中的img
            test: /.html$/,
            loader: 'html-loader',
          },
          {
            exclude: /.(html|css|js|jpg|png|gif|less)$/,
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
            },
          },
        ],
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
        }),
      ],
      mode: 'development',
      devServer: {
        contentBase: resolve(__dirname, 'build'),
        compress: true, // 优化
        port: 3000,
        open: true,
        hot: true //开启HMR功能
        // 当修改了webpack配置,新配置想要生效必须重启webpack服务
      },
    };
    

    js文件的修改:

    if (module.hot) {
      // 一旦module.hot为true 说明开启了HMR功能 -- 让HMR代码生效
      module.hot.accept('./print.js', () => {
        // 方法会监听print.js文件的变化,一旦发生变化,其他模块不会重新打包构建,
        // 会执行后面的回调函数
        print();
      });
    }
    

    懒加载

    懒加载lazy-loading:当文件需要使用时才加载
    预加载prefetch:会在使用前提前加载js文件(兼容性差,慎用)
    正常加载可以认为是并行加载(同一时间加载多个文件),预加载则是等其他资源加载完毕,浏览器空闲了,再加载资源

    console.log('index.js is loaded')
    
    document.getElementById('btn').onclick = () => {
    //webpackChunkName: 'test'设置模块名字
      import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test')
      .then(({mul}) => {
        console.log(mul(4, 5));
        
      })
    }
    

    source-map

    一种提供源代码构建后代码映射技术(如果构建后代码出错,通过映射关系可以追踪代码错误位置)

    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map (顺序不能弄错)
    

    source-map:外部,错误代码的准确信息和源代码的错误位置
    inline-source-map:内联source-map,只生成一个source-map,错误代码的准确信息和源代码的错误位置
    hidden-source-map:外部,错误代码错误原因,但是没有错误位置,只能提示到构建后代码的错误位置
    eval-source-map:内联,在每个文件后追加source-map,都在eval中,错误代码的准确信息和源代码的错误位置
    nosources-source-map:外部,错误代码是准确信息,但是没有任何源代码信息
    cheap-source-map:外部,错误代码的准确信息和源代码的错误位置,只能精确到行
    cheap-module-source-map:外部,错误代码的准确信息和源代码的错误位置,module会将loader的source map加入
    内联和外部的区别:1.外部生成了文件,内联没有 2.内联构建速度更快

    module.exports = {
      entry: ['./src/index.js', './src/index.html'],
      output: {
        filename: 'build.js',
        path: resolve(__dirname, 'build'),
      },
      module: {
        rules: [
          {
            test: /.css$/,
            use: ['style-loader', 'css-loader'],
          },
          {
            test: /.(jpg|png|gif)$/,
            loader: 'url-loader',
            options: {
              linit: 8 * 1024,
              name: '[hash:10].[ext]',
              exModule: false, // 关闭es6模块化
            },
            // outputPath:'img' 输出到build/img文件夹
          },
          {
            // 处理html中的img
            test: /.html$/,
            loader: 'html-loader',
          },
          {
            exclude: /.(html|css|js|jpg|png|gif|less)$/,
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
            },
          },
        ],
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
        }),
      ],
      mode: 'development',
      devServer: {
        contentBase: resolve(__dirname, 'build'),
        compress: true, // 优化
        port: 3000,
        open: true,
        hot: true //开启HMR功能
        // 当修改了webpack配置,新配置想要生效必须重启webpack服务
      },
      devtool: 'source-map',
    };
    
    

    开发环境:速度快,调试更友好
    速度快(eval>inline>cheap>...)
    参考:

    eval-cheap-source-map
    eval-source-map
    调试更友好:
    source-map
    cheap-moudle-source-map
    cheap-source-map

    -->eval-source-map / eval-cheap-module-source-map
    生产环境:源代码要不要隐藏,调试更友好
    内联会让代码体积变大,所以生产环境不用内联
    隐藏:
    nosource-source-map 全部隐藏
    hidden-source-map 只隐藏源代码,会提示构建后代码错误

    --> source-map / cheap-module-source-map

    oneof

    一般情况下,一个文件只能被一个loader处理,当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:先执行eslint再执行babel

    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/build.js',
        path: resolve(__dirname, 'build'),
      },
      module: {
        rules: [
          {
            // 在package.josn中eslintConfig --> airbnb
            test: /.js$/,
            exclude: /node_module/,
            // 优先执行
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              fix: true,
            },
          },
          {
            // 一下loader只会匹配一个,提升构建速度
            // 注意:不能有两项配置处理同一类型的文件
            oneOf: [
              {
                test: /.css$/,
                use: [
                  ...commonCssLoader,
                ],
              }, {
                test: /.less$/,
                use: [
                  ...commonCssLoader,
                  'less-loader',
                ],
              }, {
              // 在package.josn中eslintConfig --> airbnb
                test: /.js$/,
                exclude: /node_module/,
                loader: 'babel-loader',
                options: {
                  presets: [
                    [
                      '@babel/preset-env',
                      {
                        useBuiltIns: 'usage',
                        corejs: {
                          version: 3,
                        },
                        targets: {
                          chrome: '60',
                          firefox: '50',
                        },
                      },
                    ],
                  ],
                },
              }, {
                test: /.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                  limit: 8 * 1024,
                  name: '[hash:10].[ext]',
                  outputPath: 'imgs',
                  esModule: false,
                },
              }, {
                test: /.html$/,
                loader: 'html-loader',
              }, {
                exclude: /.(js|css|less|html|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                  outputPath: 'media',
                },
              },
            ],
          },
        ],
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/build.css',
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
      ],
      mode: 'production',
    };
    

    缓存

    babel缓存:CacheDirectory:true --让第二次打包构建速度更快
    文件资源缓存:让代码上线运行缓存更好使用
    hash:每次webpack构建时会生成一个唯一的hash值
    问题:因为js和css同时使用一个hash值
    如果重新打包,会导致所有缓存失败(可能只改动了一个文件)
    chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样
    问题:js和css的hash值还是一样
    因为css是在js中被引入,所以属于同一chunk(所有根据入口文件生成的东西都会成为一个chunk)
    contenthash:根据文件的内容生成hash值,不同的文件hash一定不一样

    const { resolve } = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    // 定义node环境变量,决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';
    
    // 复用loader
    const commonCssLoader = [
      MiniCssExtractPlugin.loader,
      'css-loader', {
        // 还需在package.json中定义browserslist
        loader: 'postcss-loader',
        options: {
          ident: 'postcss',
          plugins: () => {
            require('postcss-preset-env')();
          },
        },
      },
    ];
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'js/build.[contenthash:10].js',
        path: resolve(__dirname, 'build'),
      },
      module: {
        rules: [
          {
            // 在package.josn中eslintConfig --> airbnb
            test: /.js$/,
            exclude: /node_module/,
            // 优先执行
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              fix: true,
            },
          },
          {
            oneOf: [
              {
                test: /.css$/,
                use: [
                  ...commonCssLoader,
                ],
              }, {
                test: /.less$/,
                use: [
                  ...commonCssLoader,
                  'less-loader',
                ],
              }, {
              // 在package.josn中eslintConfig --> airbnb
                test: /.js$/,
                exclude: /node_module/,
                use: {
                  loader: 'babel-loader',
                  options: {                
                    presets: [
                      [
                        '@babel/preset-env',
                        {
                          useBuiltIns: 'usage',
                          corejs: {
                            version: 3,
                          },
                          targets: {
                            chrome: '60',
                            firefox: '50',
                          },
                        }
                      ]
                    ],
                    // 开启babel缓存
                    // 第二次构建时,会读取之前的缓存
                    cacheDirectory: true,
                  },
                },
              }, {
                test: /.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                  limit: 8 * 1024,
                  name: '[hash:10].[ext]',
                  outputPath: 'imgs',
                  esModule: false,
                },
              }, {
                test: /.html$/,
                loader: 'html-loader',
              }, {
                exclude: /.(js|css|less|html|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                  outputPath: 'media',
                },
              },
            ],
          },
        ],
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/build.[contenthash:10].css',
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
      ],
      mode: 'production',
      devtool: 'source-map',
    };
    

    服务器代码:
    安装express:

    cnpm install express--save
    cnpm install body-parser cookie-parser multer --save
    

    启动服务器指令:

    npm i nodemon -g
    nodemon server.js
    //-------or-------
    node server.js
    

    访问服务器地址:http://localhost:3000

    const express = require('express');
    const app = express();
    
    app.use(express.static('build', { maxAge: 1000 * 3600 }));//maxAge:有效期
    app.listen(3000);
    

    tree shaking

    去除无用代码:减少代码体积
    前提:

    • 必须使用ES6

    • 开启production环境

    满足以上两个条件就会开启tree shaking

    在package.json中配置:
    "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking),默认值时true,若不指定其他值,就默认所有文件都有副作用,无法进行tree shaking。
    设置为false的问题:可能会把css、@babel、polyfill(副作用)文件去掉
    "sideEffects": [".css", ".less"] 不会把css文件处理

    code-split

    方法一:通过多入口拆分代码

    const { resolve } = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      // 多入口:有几个入口,最终输出就有几个bundle
      entry: {
        main: './src/index.js',
        test: './src/test.js' 
      },
      output: {
        // [name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build'),
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
      ],
      mode: 'production'
    };
    

    方法二:

    1. 可将node_modules中代码单独打包一个chunk输出
    2. 自动分析多入口chunk中,有没有公共的文件,若有会打包成单独一个chunk
    module.exports = {
      entry: {
        main: './src/index.js',
        test: './src/test.js'
      },
      output: {
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build'),
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
      ],
      /* 
      1.可以将node_modules中代码单独打包一个chunk输出
      2.自动分析多入口chunk中,有没有公共的文件,若有会打包成单独一个chunk
      */
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      },
      mode: 'production'
    };
    

    方法三:
    通过js代码,让某个文件被单独打包成一个chunk
    import动态导入语法:能将某个文件单独打包

    import (/* webpackChunkName: 'test' */'./test')
      .then(({mul, count}) => {
        // 文件加载成功
        console.log(mul(2, 5));
      })
      .catch(() => {
        console.log('文件加载失败');
        
      })
    

    PWA

    渐进式网络开发应用程序(离线可访问)
    workbox --> workbox-webpack-plugin

    const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
    plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/build.[contenthash:10].css',
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
        new WorkboxWebpackPlugin.GenerateSW({
          /* 
          1.帮助serviceworker快速启动
          2.删除旧的serviceworker
    
          生成一个serviceworker配置文件
          */
          clientsClaim: true,
          skipWaiting: true
        }),
      ],
    

    js文件

    /*
    1.eslint 不认识window,navigator全局变量
     解决:需要修改package.json中的eslintConfig配置
     "env": {
       "browser": true
     }
    2.sw必须运行在服务器上
      --> node.js
      --> npm i serve -g
          serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
    */
    // 注册serviceworker
    // 处理兼容性问题
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js')
          .then(() => {
            console.log('sw registed');
          })
          .catch(() => {
            console.log('sw registe failed');
          });
      });
    }
    
    

    多进程打包

    开启多进程打包:thread-loader
    进程开启大概需要600ms,进程通信也有开销,只有工作消耗时间长,才需要多进程打包(慎用)

    const { resolve } = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    process.env.NODE_ENV = 'production';
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'js/build.[contenthash:10].js',
        path: resolve(__dirname, 'build'),
      },
      module: {
        rules: [
          {
            test: /.js$/,
            exclude: /node_module/,
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              fix: true,
            },
          },
          {
            oneOf: [
             {
              // 在package.josn中eslintConfig --> airbnb
                test: /.js$/,
                exclude: /node_module/,
                use: [
                  {
                    loader: 'thread-loader',
                    options: {
                      workers: 2 //2个进程
                    }
                  },
                  {
                    loader: 'babel-loader',
                    options: {
                      presets: [
                        [
                          '@babel/preset-env',
                          {
                            useBuiltIns: 'usage',
                            corejs: {
                              version: 3,
                            },
                            targets: {
                              chrome: '60',
                              firefox: '50',
                            },
                          },
                        ],
                      ],
                      // 开启babel缓存
                      // 第二次构建时,会读取之前的缓存
                      cacheDirectory: true,
                    },
                  },
                ],
              }
            ],
          },
        ],
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/build.[contenthash:10].css',
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true,
          },
        }),
        
      ],
      mode: 'production',
      devtool: 'source-map',
    };
    

    外部扩展externals

    让webpack不对一些不怎么需要更新的第三方 库打包,减少打包时间。可通过CMD,AMD,global等方式引入第三方库。

    const {resolve} = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry:'./src/index.js',
      output:{
        filename:'js/build.js',
        path:resolve(__dirname, 'build')
      },
      module:{
        rules:[
    
        ]
      },
      plugins:[
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }
          )
      ],
      mode: 'production',
      externals: {
        // 忽略库名--npm包
        // 拒绝jQuery被打包,通过cdn引入,提高加载速度
        jquery: 'jQuery'
      }
    }
    
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    

    dll

    使用dll技术,对一些第三方库(如:jquery,react等)进行单独打包(不重复打包)。
    当运行webpack时,默认查找webpack.config.js文件,使用dll技术需要运行webpack.dll.js文件,运行:

    webpack --config webpack.dll.js
    
    //webpack.dll.js
    const webpack = require('webpack')
    const {resolve} = require('path')
    module.exports = {
      entry: {
        // 最终打包生成[name] --> jquery
        // ['jquery'] --> 要打包的库是jquery
        jquery: ['jquery']
      },
      output: {
        filename: '[name].js',
        path:resolve(__dirname, 'dll'),
        library: '[name]_[hash]' //打包库里向外暴露出去的内容叫什么名字
      },
      plugins: [
        new webpack.DllPlugin({
          name: '[name]_[hash]', //映射库的暴露内容名称
          path: resolve(__dirname, 'dll/manifest.json') //输出文件路径
        })
      ]
    }
    
    //webpack.config.js
    const {resolve} = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const webpack = require('webpack') //使用dll的插件
    const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
    
    module.exports = {
      entry:'./src/index.js',
      output:{
        filename:'build.js',
        path:resolve(__dirname, 'build')
      },
      module:{
        rules:[
    
        ]
      },
      plugins:[
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }
          ),
          // 告诉webpack哪些库不参与打包,同时使用时名称也得变
          new webpack.DllReferencePlugin({
            manifest: resolve(__dirname, 'dll/manifest.json')
          }),
          // 将某个文件打包输出,并在 html中自动引入该资源(不用手动引入)
          new AddAssetHtmlWebpackPlugin({
            filepath: resolve(__dirname, 'dll/jquery.js')
          })
      ],
      mode: 'production'
    }
    

    与externals的区别:
    externals:彻底不需要打包,可用cdn引入
    dll:需要打包,只用打包一次

  • 相关阅读:
    ###STL学习--标准模板库
    打包C#程序
    ###学习《Effective C++》
    [ZJOI2007]棋盘制作
    [NOI2012]美食节
    [SCOI2012]奇怪的游戏
    5120: [2017国家集训队测试]无限之环
    序列取数
    1028: [JSOI2007]麻将
    1011: [HNOI2008]遥远的行星
  • 原文地址:https://www.cnblogs.com/xuewting/p/13330164.html
Copyright © 2020-2023  润新知