zoukankan      html  css  js  c++  java
  • Webpack4 高级概念 常用配置

    本文记录webpack的一些常用高级概念,需要对webapck基础有所了解,本文重点记录概念涉及到的配置,其他配置有所省略,更多内容参照官方文档。
    webpack中文网
    webpack英文网

    Tree Shaking

    作用是当引入一个模块时,不会引入模块全部代码,只引入需要的部分代码,Tree Shaking只支持ES Module,不支持CommonJS模块方式。

    例如只引入match.js的add方法

    // match.js
    export function add(a, b) {
      return a + b
    }
    export function minus(a, b) {
      return a - b
    }
    
    // index.js
    import { add } from './math'
    

    webpack配置

    // webpack.config.js 生产环境
    module.exports = {
      mode: 'production',
      devtool: 'cheap-module-source-map',
    }
    
    // webpack.config.js 开发环境
    module.exports = {
      mode: 'development',
      devtool: 'cheap-module-eval-source-map',
      optimization: {
        usedExports: true
      }
    }
    
    // package.json 如果某些文件不需要做Tree Shaking,需要增加如下配置
    "sideEffects": false, // 没有要处理的文件
    "sideEffects": ["*.css"], // 任何css文件都不做Tree Shaking
    

    Develoment 和 Production 模式的区分打包

    webpack.common.js

    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const CleanWebpackPlugin = require('clean-webpack-plugin')
    
    module.exports = {
      entry: {
        main: './src/index.js'
      },
      module: {
        // 相关loader配置(此处省略)
        rules: [ ... ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
          root: path.resolve(__dirname, '../')
        })
      ],
      output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '../dist')
      }
    }
    

    webpack.dev.js

    const webpack = require('webpack')
    const merge = require('webpack-merge')
    const commonConfig = require('./webpack.common.js')
    
    const devConfig = {
      mode: 'development',
      devtool: 'cheap-module-eval-source-map',
      devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin()
      ],
      optimization: {
        usedExports: true
      }
    }
    
    module.exports = merge(commonConfig, devConfig)
    

    webpack.prod.js

    const merge = require('webpack-merge')
    const commonConfig = require('./webpack.common.js')
    
    const prodConfig = {
      mode: 'production',
      devtool: 'cheap-module-source-map'
    }
    
    module.exports = merge(commonConfig, prodConfig)
    

    Webpack 和 Code Splitting

    • 代码分割,和webpack无关
    • webpack中实现代码分割,两种方式
      • 同步代码: 只需要在webpack.common.js中做optimization的配置即可
      • 异步代码(import): 异步代码,无需做任何配置,会自动进行代码分割,放置到新的文件中

    同步加载

    // index.js
    import _ from 'lodash'
    console.log(_.join(['a', 'b'], '-'))
    
    // webpack.common.js
    module.exports = {
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      }
    }
    

    异步加载

    注意需要借助babel插件来支持该语法

    npm install --save-dev @babel/plugin-syntax-dynamic-import
    
    // .babelrc
    {
      plugins: ["@babel/plugin-syntax-dynamic-import"]
    }
    
    // index.js
    function getComponent() {
      return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
        var element = document.createElement('div')
        element.innerHTML = _.join(['a', 'b'], '-')
        return element
      })
    }
    
    getComponent().then(element => {
      document.body.appendChild(element)
    })
    

    /* webpackChunkName:"lodash" */webpack魔法注释的作用是对异步加载的lodash打包生成的文件进行重命名,即为vendors~lodash.js,不设置的时候是0.js

    SplitChunksPlugin详细配置参数

    详细配置项详见webpack官方文档

    module.exports = {
      //...
      optimization: {
        splitChunks: {
          chunks: 'async', // 对异步模块进行优化,结合cacheGroups生效,有效值是all、async(异步)和initial(同步)
          minSize: 30000, // 引入的模块大于30kb才做代码分割
          maxSize: 50000, // 如果分割的lodash大于50kb,则尝试进行二次分割(了解即可)
          minChunks: 2, // 当一个模块至少用了2次的时候才进行代码分割
          maxAsyncRequests: 5, // 同时只能加载五个请求,超过五个就不会做代码分割
          maxInitialRequests: 3, // 入口文件引入的库如果做代码分割最多只能分割出3个文件
          automaticNameDelimiter: '~', // 默认打包生成文件名的连接符
          name: true, // cacheGroups中改变的名字生效
          cacheGroups: {
            vendors: {
              test: /[\/]node_modules[\/]/, // 是node_modules中引入的模块
              priority: -10, // 优先级值越大优先级越高
              filename: 'vendors.js' // 打包生成的文件名
            },
            default: {
              priority: -20, // 优先级值越大优先级越高
              reuseExistingChunk: true, // 如果模块已经被打包过,则忽略此模块,直接复用
              filename: 'commin.js' // 打包生成的文件名
            }
          }
        }
      }
    }
    

    Lazy Loading 懒加载

    懒加载其实就是上文 Code Splitting 中提到的异步加载模块,指的是需要的时候再去加载该模块

    // index.js
    async function getComponent() {
      const { default: _ } = await import(/* webpackChunkName:"lodash" */ 'lodash')
      const element = document.createElement('div')
      element.innerHTML = _.join(['a', 'b'], '-')
      return element
    }
    
    document.addEventListener('click', () =>{
      getComponent().then(element => {
        document.body.appendChild(element)
      })
    })
    

    打包分析

    暂不做详细介绍(后续补充),详细查看官方文档。

    webpack-analyse
    官方文档

    build命令需要增加 --profile 参数

    // package.js
    {
      "scripts": {
        "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
      }
    }
    

    Preloading,Prefetching

    • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
    • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。

    官方文档

    // click.js
    function handleClick() {
      const element = document.createElement('div')
      element.innerHTML = 'hello world'
      document.body.appendChild(element)
    }
    
    export default handleClick
    
    // index.js
    document.addEventListener('click', () =>{
      import(/* webpackPrefetch: true */ './click.js').then(({default: func}) => {
        func();
      })
    })
    

    /* webpackPrefetch: true */等有空闲带宽时加载此模块

    CSS 文件的代码分割 MiniCssExtractPlugin

    官方文档

    # 代码分割插件
    npm install --save-dev mini-css-extract-plugin
    # 代码压缩合并插件
    npm install --save-dev optimize-css-assets-webpack-plugin
    

    MiniCssExtractPlugin插件目前还不支持HMR support热更新,一般在线上环境做打包时使用

    webpack.common.js

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    module.exports = {
      entry: {
        main: './src/index.js',
      },
      module: {
        rules: [{ 
          test: /.js$/, 
          exclude: /node_modules/, 
          loader: 'babel-loader',
        }, {
          test: /.(jpg|png|gif)$/,
          use: {
            loader: 'url-loader',
            options: {
              name: '[name]_[hash].[ext]',
              outputPath: 'images/',
              limit: 10240
            }
          } 
        }, {
          test: /.(eot|ttf|svg)$/,
          use: {
            loader: 'file-loader'
          } 
        }]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
          root: path.resolve(__dirname, '../')
        })
      ],
      optimization: {
        usedExports: true, // 注意这里要对css文件不做Tree Shaking,具体在package.json中配置
        splitChunks: {
          chunks: 'all'
        }
      },
      output: {
        filename: '[name].js', // entry直接引入的(入口)文件走这里
        chunkFilename: '[name].chunk.js', // 间接引入的文件走这里
        path: path.resolve(__dirname, '../dist')
      }
    }
    

    webpack.dev.js

    const webpack = require('webpack');
    const merge = require('webpack-merge');
    const commonConfig = require('./webpack.common.js');
    
    const devConfig = {
      mode: 'development',
      devtool: 'cheap-module-eval-source-map',
      devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true
      },
      module: {
        rules: [{
          test: /.scss$/,
          use: [
            'style-loader', 
            {
              loader: 'css-loader',
              options: {
                importLoaders: 2
              }
            },
            'sass-loader',
            'postcss-loader'
          ]
        }, {
          test: /.css$/,
          use: [
            'style-loader',
            'css-loader',
            'postcss-loader'
          ]
        }]
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin()
      ],
    }
    
    module.exports = merge(commonConfig, devConfig);
    

    webpack.prod.js

    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
    const merge = require('webpack-merge');
    const commonConfig = require('./webpack.common.js');
    
    const prodConfig = {
      mode: 'production',
      devtool: 'cheap-module-source-map',
      module: {
        rules:[{
          test: /.scss$/,
          use: [
            MiniCssExtractPlugin.loader, 
            {
              loader: 'css-loader',
              options: {
                importLoaders: 2
              }
            },
            'sass-loader',
            'postcss-loader'
          ]
        }, {
          test: /.css$/,
          use: [
            MiniCssExtractPlugin.loader,
            'css-loader',
            'postcss-loader'
          ]
        }]
      },
      optimization: {
        // 代码压缩合并插件的配置
        minimizer: [new OptimizeCSSAssetsPlugin({})]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: '[name].css', // 直接引入的文件走这里
          chunkFilename: '[name].chunk.css' // 间接引入的文件走这里
        })
      ]
    }
    
    module.exports = merge(commonConfig, prodConfig);
    

    package.json

    // css文件不做Tree Shaking
    {
      "sideEffects": [
        "*.css"
      ]
    }
    

    Webpack 与浏览器缓存

    // webpack.common.js
    module.exports = {
      optimization: {
        // 老版本webpack4需要runtimeChunk配置(处理没改变业务代码也会改变打包后的contenthash情况)
        runtimeChunk: {
          name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
          chunks: 'all',
          cacheGroups: {
            vendors: {
              test: /[\/]node_modules[\/]/,
              priority: -10,
              name: 'vendors',
            }
          }
        }
      },
      performance: false, // 性能问题不做警告
      output: {
        path: path.resolve(__dirname, '../dist')
      }
    }
    
    // webpack.prod.js
    module.exports = {
      output: {
        // 文件名添加contenthash值
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].js'
      }
    }
    

    Shimming 的作用

    index.js

    import $ from 'jquery'
    import _ from 'lodash'
    import { ui } from './jquery.ui'
    
    ui()
    
    const dom = $('<div>')
    dom.html(_.join(['a', 'b'], '-'))
    $('body').append(dom)
    

    jquery.ui.js

    // 在另一个模块中直接使用jquery和lodash方法
    export function ui() {
      $('body').css('background', _join(['red'], ''))
    }
    

    webpack.common.js

    const webpack = require('webpack')
    
    module.exports = {
      plugins: [
        new webpack.ProvidePlugin({
          $: 'jquery', // 在一个模块引入可以在其他模块直接用$
          _join: ['lodash', 'join'] // _.join的另一种方式,可以直接用_join
        })
      ]
    }
    

    环境变量的使用方法

    在上文 Develoment 和 Production 模式的区分打包 中的区分打包可以改为使用环境变量的方式。即webpack.common.js中通过变量判断是合并开发环境还是生产环境的配置,webpack.dev.jswebpack.prod.js只写各自配置,不需要单独合并引入。

    webpack.common.js

    const merge = require('webpack-merge')
    const devConfig = require('./webpack.dev.js')
    const prodConfig = require('./webpack.prod.js')
    const commonConfig = {
      ...
    }
    
    module.exports = (env) => {
      if(env && env.production) {
        return merge(commonConfig, prodConfig);
      } else {
        return merge(commonConfig, devConfig);
      }
    }
    

    webpack.dev.js或webpack.prod.js直接导出,不用合并webpack.common.js

    const devConfig = {
      ...
    }
    
    module.exports = devConfig;
    

    package.json

    注意npm run build中需要传入--env.production变量,其他都是直接引入webpack.common.js文件。

    {
      "scripts": {
        "dev-build": "webpack --config ./build/webpack.common.js",
        "dev": "webpack-dev-server --config ./build/webpack.common.js",
        "build": "webpack --env.production --config ./build/webpack.common.js"
      }
    }
    
    完结~
  • 相关阅读:
    随笔
    我的舅舅
    代码规范
    SpringMVC_乱码问题
    SpringMVC_接受请求及数据回显
    Restful风格
    第六周总结
    SpringMVC_控制器
    SpringMVC_初次使用
    SpringMVC_简介
  • 原文地址:https://www.cnblogs.com/lwlblog/p/14744612.html
Copyright © 2011-2022 走看看