webpack掠影-5

362 阅读6分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

前面我们练习 webpack 中暴露全局变量和 webpack 内置插件的使用;今天我们练习多页面应用的配置和 sourceMap

一、 sourceMap

为了提升代码的性能,我们开启了 webpack 压缩混淆代码的功能,但是如果代码运行时报错,压缩混淆后的代码十分不利于调试,为了解决这个问题,我们需要一个叫做 sourceMap 的机制。sourceMap 是浏览器的一种功能,可以根据 sourceMap 配置将压缩混淆后的代码虚拟还原成源码的样子。webpack 内置了相关配置,我们在打包时通过配置 devtool 选项来开启 sourceMapsourceMap 有如下可选值:

  • source-map ,生成 source-map 并且是全码映射,单独输出 .map 文件
  • eval-source-map 生成带有行和列策映射,但是不会产生单独的 source-map 文件
  • cheap-module-source-map 生成的 source-map 不带有行和列,产出单独 .map 文件
  • cheap-module-eval-source-map 生成 source-map,不带有行和列,不输出 .map 文件

值得一提的是,上面的顺序是按照详细程度降序排列的,越靠后表示详细程度越低。靠前的虽然详细程度很高,但是会拖慢打包的速度,一般用 eval-source-map;但是具体情况还要视情况而定,如果你的项目中需要 source-map 文件,如使用 sentry 等错误监控系统,那么就需要配置产出文件的方式;

1.1 修改配置文件开启 sourcemap

const path = require('path');

module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
+  devtool: "eval-source-map", // source-map
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
  },
  module: {
    rules: [
     
    ]
  },
  plugins: [
   
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
      
    ]
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}

1.2 效果如图

wbp5-source-map.png

二、 webpack 配置多页面应用

之前我们的示例中都是以时下很流行的单页面应用配置的,但是有很多情况下我们会遇到多页面应用,webpack 同样支持多页面应用的处理;多页面涉及到几个问题:

  • 多入口,js 文件的入口不再是一个 js 文件;
  • 多出口,多个页面依赖不同的 js 文件,所以对应需要输出多个 js 文件
  • 公用模块分离问题,项目中多个页面可能依赖相同的模块,这些模块并不需要打包多次

2.1. 多入口问题和多出口的问题

解决多入口,我们需要在 webpackentry 配置一个对象; 解决多出口问题,需要修改 output 配置项,之前 output 的输出的 filename 是一个固定的 bundle.js ;但是多页面应用,的名字应该由的它的入口文件决定,所以不能写死成一个固定字符串,配合多页面应用 [name] 表示文件名, [hash:5] 生成文件名后跟随 5 位 hash 戳记;

最后,每个页面还需要引入特定的 js 文件,这个同样也不能混乱,例如打包区 index.html 引入的是 index.jsother.html 引入的是 other.js,那么打包后还要维持这种引入的关系,所以此时我们需要修改 HTMLWebpackPlugin 的配置了,此时 HTMLWebpackPlugin 的实例就不再是一个了,而是每个页面一个,并且在每个实例的配置中加入 chunks 配置字段,标识当前 HTML 文件需要的代码块是哪些。配置这些内容后,在 webpack 打包后,会自动将原来属于这个页面的 js 代码块插入到页面中,这样我们就初步实现了一个多页面应用程序的配置;

  • 修改 webpack 配置文件

module.exports = {
  // entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  devtool: 'eval-source-map',
  entry: {
    home: './src/index.js',
    other: './src/other.js',
    x: './src/x.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
+  filename: '[name].[hash].js'
  },
  devServer: {
    // 开发服务器的配置
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [
+    // 为每个 HTML 文件设置一个 html-webpack-plugin 实例,chunks 属性是当前页面需要引入的 js 文件,如果有多个,就在数组中设置多项
+    new HTMLWebpackPlugin({
+      template: './src/index.html',
+      filename: "index.html",
+      chunks: ['home']
+    }),
+    new HTMLWebpackPlugin({
+      template: './src/other.html',
+      filename: "other.html",
+      chunks: ['other']
+    }),
    // 抽离 css 文件
    

    // 注入模块: key 是模块中可以访问的变量,value 是对应的模块
    

    // 定义环境变量数据
   

    // 清空打包的内容
    new CleanWebpackPlugin(),

    // 复制文件内容: 接收一个数组,数组项是对象,对象中的 from 表示被复制的目录或者文件,to 表示输出的目录
   

    // 在打包后的代码中输出必要信息
    new webpack.BannerPlugin('发上等愿,结中等缘,享下等福;择高处立,寻平处住,想宽处行')
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
     
    ]
  },

  // 配置 externals
}

2.2 抽离公共模块

经过上面配置后,即可实现一个多页面应用的打包;此时,如果 index.jsother.js 同时都依赖于 x.js 这个模块,打包后我们会发现 x 这个模块既被 index.js 打包了进去,又被 other.js 打包了进去;如果这个模块被更多的模块依赖,那么将会被更多次打包,这样一来就会增大代码的体积;为了解决这个问题,我们需要配置提取公共模块;

提取公共模块需要在 webpack.config.jsoptimization.splitChunks 选项,改项用于拆分代码, webpack4.x 以后用此功能代替之前的 CommonChunksPlugin

  • 配置如下:

  • cacheGroups: Object 缓存组,这个对象的每个 key 都对应一个 chunk 输出到文件输出目录,简言之一个 key 就是一个公共模块;

    • common: Object,定义一个叫做 common 的公共模块
      • chunks: 'initial', 标识哪些模块会被优化
      • minSize: 0, 打包后的代码超过该限制后的代码块会被提取成为公共模块,如果不超过该限制还是会打包到代码中,不会成为单独的模块
      • minChunks: 2, 代码块被会被提取成公共模块需要被引用的最小次数
    • vendors: Object,定义第二个 叫做 vendors 的模块,用于提取第三方代码
      • priority: 1, 优先级,优先提取第三方代码
      • test: /node_modules/,
      • chunks: 'initial',
      • minSize: 0,
      • minChunks: 2
  • 修改配置文件


module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',
  entry: {
    home: './src/index.js',
    other: './src/other.js',
    x: './src/x.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js' // 配合多页面应用 [name] 表示文件名,  [hash:5] 生成文件名后跟随 5 位 hash 戳记
  },
  devServer: {
    // 开发服务器的配置
  },
  module: {
    rules: [

    ]
  },
  plugins: [
   
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
     
    ],
+    splitChunks: { 
+      cacheGroups: { 
+        common: {
+          chunks: 'initial', 
+          minSize: 0, 
+          minChunks: 2,
+        },
+        vendors: { 
+          priority: 1,
+          test: /node_modules/,
+          chunks: 'initial',
+          minSize: 0,
+          minChunks: 2
+        }
+      }
+    }
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}

经过上面的配置后,我们再次执行打包的命令,再次检查打包输出的文件,我们发现会多出一个文件,文件名字中有 ~ 字符,这个文件就是提取出来的公共模块,包括这个 ~ 都是可以配置的。