webpack(二)

320 阅读3分钟

webpack 之 DefinePlugin 插件

DefinePlugin是webpack 自带插件。可以使用webpack.DefinePlugin()获取。并且它有一个参数,是一个对象。对象的键是代码的中的变量。

const webpack = require('webpack');
module.exports = {
  mode: 'none',
  entry: './src/main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.DefinePlugin({
      API_BASE_URL: JSON.stringify('https://api.examle.com')
    })
  ]
}
// 注释: 代码中所有使用API_BASE_URL的地方都会被替换。注意:输出的是代码片段。所有如果想输出字符串需要使用:JSON.stringify()

webpack 之 Tree-shaking

未引用的代码(dead-code)就是在打包之后将未引用的代码剔除掉保留有用的代码。也是去除冗余代码有效方法。会在生产功能自动启用。yarn webpack --mode production

optimization: {
	usedExports: true, // 负责标记【枯树叶】
    minimize: true // 代码压缩,负责【摇掉】它们
}

webpack 之代码合并 Scope Hoisting

// babel-loader: 配置参数:
use: {
	loader: 'babel-loader',
    options: {
      presets: [ // @babel/preset-env 传递参数需要数组里在创建一个数组。第一个元素为'@babel/preset-env', 第二个元素为一个对象来配置参数。
        ['@babel/preset-env', {modules: 'commonjs'}]
      ]
    }
}

webpack tree shaking 与 Babel 同时使用失效的传说

网上说babel 和 tree shaking 一起使用会导致去除不了冗余代码。 是因为当执行@babel/preset-env这个组合插件的时候里面有一个可以将esm代码转换为commonjs的语法。而tree shaking 只能转换esm 的规则。所以导致失效。

  • 但是最新的babel 已经默认禁用转换为commonjs的插件。默认使用esm的语法。所以不会出现以上问题。
  • 不管这个插件可以强制将babel使用commonjs转换的插件。
  • 这个时候tree shaking 去除冗余代码就没有效果了。

webpack 之 sideEffects

optimization: {
    /**
     * sideEffects: true 开启副作用检查。
     * 在package.json里面添加一个字段。sideEffects: false表示代码没有副作用。
     * usedExports: true 将没有引入的代码不进行导出
     * minimize: true 压缩代码
     * {
     *  "name": "your-project",
     * "sideEffects": [
     *   "./src/some-side-effectful-file.js" // 如果有副作用的话package.json里面sideEffects数组表示。
     * ]
     * }
    */
    sideEffects: true, 
    usedExports: true,
    minimize: true
  },

webpack 代码分割

  • 多入口打包
  • 动态导入
  1. 多入口打包 使用与多页面打包程序。
/**
 * 使用多入口打包。
 * 1. 需要 entry 为对象,
 * 健是html-webpack-plugin参数一个chunks
 * 属性的一个元素可以任意组合,
 * 我们在html-webpack-plugin配置多个页面。配置完多个页面
 * 但是webpack不知道我们这个几个html到底加载那几个js文件
 * 这个时候就需要我们去指定js了
 * 我们需要根据entry对象健名去配置html-webpack-plugin  chunks:Array<entry.key>
 * 
*/

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); 
module.exports = {
  mode: 'none',
  // entry: './src/index.js', //多入口是对象。
  entry: {
    index: './src/index.js', // 对象的健跟html-webpack-plugin参数配置的chunks名字保持一致。
    album: './src/album.js'
  },
  output: {
    filename: '[name].bundle.js' // 因为需要输出多个入口文件所以使用[name]占位符。生成多个文件。
  },
  module: {
    rules:[
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Multi Entry',
      template: './src/index.html',
      chunks: ['index'] // 决定使用哪个打包完的js文件
      // 如果有一些公共的js文件也可以配追在这个数组里面。
    }),
    new HtmlWebpackPlugin({
      title: 'Multi Entry',
      template: './src/album.html',
      filename: 'album.html',
      chunks: ['album'] 
    })
  ]
}

webpack 公共模块提取

optimization: { // 优化
    splitChunks: { // 将所有公共模块提取出来。
      chunks: 'all',
    }
  },

webpack 动态导入

  • 按需加载。
  • 需要到某个模块时再去加载某个模块。
  • 动态导入的模块被自动分包。

使用import()动态加载文件,import()返回的是promise()。then()可以接收该模块的内容。可以根据页面判断是否加载。 避免使用import * from * 必须先于模块执行。 因为import()是esm模块化语法。所以webpack可以直接处理这些。只加载用户访问的页面。 大大增加在加载效率。

webpack 魔法注释

if (hash === '#posts') {
    // /* webpackChunkName: 'components' */ 根据这个webpackChunkName来决定这个文件合并后的名字,如何同一个项目有类似的名字的话也会合并一起。
    import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {
      mainElement.appendChild(posts())
    })
  } else if (hash === '#album') {
    // mainElement.appendChild(album())
    import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => {
      mainElement.appendChild(album())
    })
  }

webpack 之 mini-css-extract-plugin

  • 将样式提取出来单独的css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 'style-loader', // 将样式通过style标签的方式注入
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
},
plugins: [
    new MiniCssExtractPlugin()
  ]

webpack 之 optimize-css-assets-webpack-plugin

  • webpack 只是在生产环境对js文件使用压缩。其他文件需要自行压缩。
  • 所以有这样一款插件来压缩css文件。
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
  mode: 'none',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 'style-loader', // 将样式通过style标签的方式注入
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  optimization: {
    minimize: true,
    minimizer: [// 配置这个数组就被认定需要自定义插件
      new CssMinimizerPlugin(), 
      new TerserWebpackPlugin()
    ], 
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Dynamic import',
      template: './src/index.html',
      filename: 'index.html'
    }),
    new MiniCssExtractPlugin(),
  ]
}

webpack 输出文件名 Hash

  • 指定hash值的长度 filename: '[name]-[contenthash: 8].bundle.js'
  • hash // 整体hash 输出文件的filename 和插件输出的filename都支持hash.并且是全局的哈希。
  • chunkhash // 分组哈希 输出文件的filename 和插件输出的filename都支持chunkhash.并且是分组的哈希
  • contenthash根据不同的文件生成的hash。文件级别的

splitChunks webpack 优化

splitChunks实现分包机制。

externals 指定变量名字 如vue。然后使用cdn.解决第三方包过大。