webpack

88 阅读5分钟

开篇:Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

webpack的特点:

  1. 丰富的插件,方便进行开发工作
  2. 大量加载器,可以加载各种静态资源
  3. 代码分割,提供按需加载的能力
  4. 发布工具None

主要概念: entry, output,loader,plugin,mode,brower compatibility, env ​

基本配置

一般项目的webpack配置文件,webpack.config.js,它是一个commonjs模块

const path = require('path');
module.exports = {
	entry: './main.js', // 配置入口
  output: {
  	filename: 'app.js', // 配置输出
    path: path.resolve(__dirname, 'dist')
  }
}

1 入口文件entry

入口文件可以是单个或者多个

entry: './main.js'
entry:{
	bundle1: './main1.js',
	bundle2: './main2.js',
},
output: {
	filename: '[name].js' // 此时的输出[name]为文件名,即上面的bundle1,2
}

可以配合htmlWebpackPlugin,配置使用模板文件,即index.html

// webpack.config.js
const htmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
	new htmlWebpackPlugin({
  	title: 'ddd',
    name: 'qqq',
    template: 'index.html'
  })
]
<div><%= htmlWebpackPlugin.options.name%></div>

2 输出output

即最终结果的输出配置,输出的path必须是绝对路径

output: {
  // 单入口
  filename: 'main.js', // 输出文件名
  path: path.resolve(__dirname, "dist"), // 输出文件到磁盘目录,必须是绝对路径

  // 存在多入口时,可以使用占位符的写法
  filename: '[name].js', // hash chunkhash contenthash 
  path: path.resolve(__dirname, "dist")
}

如果在编译时,不知道最终输出文件的 publicPath 是什么地址,则可以将其留空,并且在运行时通过入口起点文件中的 webpack_public_path 动态设置。

__webpack_public_path__ = myRuntimePublicPath;

下面会列出几个输出文件名hash模式的配置,请前往webpack官网查看更多配置。

2.1 hash

即在输出的文件中添加哈希值,使每一次构建生成的哈希值都不一样

filename: '[name]-[hash:6].js' // 表示6位哈希后缀

2.2 chunkhash

为实现缓存效果, 需要另外一种hash计算方式,也就是chunkhash,它根据不通话的入口文件进行以来文件解析,构建对应的chunk,生成对应的hash值。生产环境中,公共库和程序入库文件区分开,单独打包更贱,接着采用chunkhash的方式,那么,只要不更改公共库,其hash值不会受影响,例如

entry:{
  main: path.join(__dirname, './index.js'),
  vendor: ['vue']
},
output:{
  path: path.join(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'bundle.[chunkhash].js'
}

2.3 contenthash

表示文件内容产生的hash值,内容不同产生的contenthash也不一样,例子就是css的打包,通常项目中的做法都是将css抽离出来作为css文件引用,内容相同,contenthash保持不变

const miniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports= {
	module:{
  	rules:[
      {
      	test: /\.css$/,
        use: [
          miniCssExtractPlugin.loader,
        	'css-loader'
        ]
      }
    ]
  },
  plugins: [
  	new miniCssExtractPlugin({
    	filename: 'main.[contenthash:8].css'
    })
  ]
}

如果对css使用了chunkhash之后,它与依赖它的chunk共用chunkhash,测试后会发现,css与js文件名的chunkhash值是一样的,如果修改了js文件,js的hash值会变化,css的文件名的hash还是和变化后的js文件的hash值一样,如果修改了css文件,也会导致重新构建,css的hash值和js的hash值还是一样的,即使js文件没有被修改。这样会导致缓存作用失效,所以css文件最好使用contenthash。

3 mode

告知wepack使用相对应模式的内置优化

mode = 'production': 'none' | 'production' | 'development'

设置方法,可以再config文件中直接设置, 或者在cli命令行中参数传递,如:

webpack --mode=production

下面是各种默认优化

4 module

即通过规则匹配loader进行编译的结果

5 chunk

webpack根据模块依赖,文件依赖引用关系,生成的文件

6 bundle

webpack通过loader或其他配置处理chunk文件,会输出bundle文件,最后经过加载和编译得到最终结果文件,放在浏览器或者其他环境中去加载使用

7 chunk和bundle,module区别,概念理解

image.png

  1. module,即我们写的代码,初始阶段
  2. 当我们写的 module 源文件传到 webpack 进行打包时,webpack 会根据文件引用关系生成chunk文件,webpack会对这个chunk文件进行一些操作
  3. webpack处理好chunk文件后,最终会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,放在浏览器中去加载使用,我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。​

8 resolve

配置模块如何解析。例如,当在 ES2015 中调用 import 'lodash',resolve 选项能够对 webpack 查找 'lodash' 的方式去做修改(查看模块)。

8.1 alias

创建 import 或 require 的别名,来确保模块引入变得更简单。例如,一些位于 src/ 文件夹下的常用模块,防止多层级的../../相对引入:

const path = require('path')
resolve:{
  alias:{
  	'@': path.resolve(__dirname, 'src'),
    'react-dom': '@hot-loader/react-dom',
  }
}
// ....index.jsx
import reactDom from 'react-dom';
import getPrice from '@/utils/getPrice'

8.2 extensions

官方文档上的解释有点过于官方,不是很好理解,但是大概的用途或者意思如下

resolve:{
  alias:{
  	'@': path.resolve(__dirname, 'src'),
    'react-dom': '@hot-loader/react-dom',
  },
  extensions: ['.vue', '.json', '.js', '.mjs']
}

import tik from '@/utils/tik'
// 此时webpack解析的时候,会去目录下面优先寻找tik.vue,找不到的话按照上述数组的顺序进行文件查找,所以上述目录也算是一个排序, 也可以省去我们引入的时候需要多些后缀名的问题

9 externals

排除bundle中的依赖引用,而是从运行环境中区获取外部的拓展依赖 如下,能对Amap进行一个esmodule方式的直接引用,而Amap实际上是通过运行时cdn的形式引入的

// webpack.config.js
externals: {
  AMap: 'AMap',
  AMapUI: 'AMapUI',
},
// ....index.jsx
 import Amap frpm 'Amap'

关于loader和plugin单独开篇来解读