安装
现在学习的是 webpack4
的最新版,新建文件夹,npm init -y
初始化一下,然后执行下面命令进行安装,需要同时安装 webpack
和 webpack-cli
。
npm install webpack@4.43.0 webpack-cli@3.3.12 -D
不建议全局安装 webpack
, 因为全局安装会锁定版本,多个项目之间可能依赖的 webpack
版本不同, 所以还是建议项目内安装。
可以根目录下新建 .npmrc
文件,指定当前项目安装依赖的源地址
registry=https://registry.npm.taobao.org
webpack 的零配置
webpack
宣传的一个噱头就是零配置构建, 也就是什么配置都不写就可以进行打包,虽然确实是可以执行,但其实是满足不了实际开发需要的。
先来随便跑一下,新建 src/index.js
文件。我们在 index.js
中 只写一行
console.log("hello, webpack !")
然后执行
代码语言:javascript复制npx webpack -v
npx
在当前项目下查找并启动 webpack
。可以看到根目录下生成 dist/main.js
文件。
建议在 package.json
文件中,添加构建命令,便于添加构建参数,也更加易读。
"scripts": {
"dev": "webpack"
},
虽然我只写了一行代码,但是打包出来却有这么多字符,看下面的进度条就知道后面还有很多了。
是因为 webpack
默认给做了很多兼容,这也是相对于其他构建工具,出现了很多的冗余代码。
但其实问题不大, webpack5
进行了一些优化,但是当模块变的越来越多时候,就没有什么太大改变了。
构建信息
看一下构建时都输出了什么信息
打包出来的文件内容有很多,但是主结构很简单,其实就是一个自执行函数,如下
代码语言:javascript复制(function(modules) {
// todo
})({
"./src/login.js": (function(module, exports) {
eval("xxxxx")
})
})
在这个结构中,可以看到自执行函数的参数是一个对象,对象的每个 key
的 value
都包含一个 chunk
,也就是 代码片段。
也可以包含多个 chunk
, 被称为 chunks
, chunk组
,还可以被称为 chunk Names
。
这一整个对象中可以包含多个 key:value
,被称为依赖图谱。
构建后产生的资源文件, 称呼为 bundle
文件 。
几者遵循以下关系:
- 一个
bundle
对应至少一个chunks
- 一个
chunks
对应至少一个module
- 一个
module
对应至少一个chunk
一句话总结:
module
,chunk
和 bundle
其实就是同一份逻辑代码在不同转换场景下的取了三个名字:
我们直接写出来的是 module
,webpack
处理时是 chunk
,最后生成浏览器可以直接运行的 bundle
。
webpack的配置文件
如果没有添加配置文件,会走它的默认配置,也就是所谓的零配置,如果添加了配置文件 webpack
会按照配置文件里的配置进行打包。
通常情况下 webpack
的配置文件的文件名叫做 webpack.config.js
,其实也可以通过配置修改成别的,但是基本上没什么必要。
修改 package.json
文件,在 script
中 新建命令即可
--config
指定配置文件
"warbler": "webpack --config ./warbler.config.js"
由于配置文件的内容是非常多的,语法也很难一时全记下来,所以我们可以通过一点点小技巧,就可以让配置文件具有语法提示。
首先安装 @types/webpack
。
npm i -D @types/webpack
然后再配置文件中注明类型就可以了。
代码语言:javascript复制// webpack.config.js
/**
* @type {import('webpack').Configuration}
*/
module.exports = {
}
babel.config.js
也是支持同样方式的。
npm i -D @types/babel__core
/**
* @type {import('@babel/core').TransformOptions}
*/
vue.config.js
也是可以的,且不用安装其他包。
/**
* @type {import('@vue/cli-service').ProjectOptions}
*/
webpack的配置参数
webpack
是基于 nodejs
的, 所有的 nodejs
核心模块的 api
都可以直接引入使用。
entry
执行打包任务的入口,默认是 src/index
,支持相对路径, 也支持绝对路径。
entry: "./src/index.js"
支持 string
, arr
, object
如果传入的是字符串,也会在构建期间被转换成对象结构,上面的代码和下面的代码结果是一样的。
entry: {
main: "./src/index.js"
},
支持 spa
(单入口) 和 mpa
(多入口)。多入口会输出多个 bundle
文件。
entry: {
index: "./src/index.js",
login: "./src/login.js",
home: "./src/home.js"
},
output
输出资源文件的信息 , 包括 存储位置 , 文件名称
path
: 存储位置,打包出来的文件放在哪里 默认是dist
要求是绝对路径;filename
: 文件名称,打包出来的文件叫什么。
output: {
path: path.resolve(__dirname, './dist'),
filename: "main.js"
},
但是如果 entry
设置了多入口的话,就会产生多个 main.js
这样就会报错。
解决方法就是使用 占位符,语法如下
代码语言:javascript复制filename: "[name].js"
作用就是 entry
中的 key
叫什么,打包后的 filename
就叫什么,多个入口就会对应多个出口了。
mode
打包模式,默认是生产模式,生产模式下会做代码压缩,摇树等操作。
development
开发模式production
生产模式none
默认
plugins
插件,插件的本质就是一个类。后面会单独讲解各种常用插件的配置。
plugins
的执行顺序是从上到下的。
plugins:[]
module
模块,webpack
默认只支持 .js
, .json
文件,像我们平时常用的 .vue
, .png
, .scss
, .css
, .ts
等都是不支持的。
所以如果想要 webpack
支持其他类型的文件,就需要不同类型的 loader
进行解析。
下面这个配置的意思就是,当检测(test)到 .css
后缀文件的时候,使用(use) 什么 loader
来进行处理。
// webpack.config.js
module: {
rules: [
{
test: /.css$/,
use: ["style-loader", "css-loader"]
},
]
}
- css-loader :把
css
模块序列化,让webpack
认识css
语法并可以生成chunk
。 - style-loader :把
css
整合在html
文件的head
标签的style
中。
当多个 loader
作用于 一个模块的时候, 是按照自右向左的顺序执行的,也就是先执行 css-loader
在执行 style-loader
。
其实一个 loader
也可以搞定上面两件事,但是建议一个 loader
只做一件事情,也就是遵循 单一职责 原则。
也可以给 loader
书写配置,这个时候就要用对象的结构了。
// webpack.config.js
module: {
rules: [
{
test: /.less$/,
use: [
{
loader: 'style-loader',
},
{
loader: "css-loader",
options: {
modules: true
}
},
{
loader: 'postcss-loader',
},
{
loader: 'less-loader',
}
]
}
]
}
- less-loader :把
less
语法 转化为css
语法。 - postcss-loader :
postcss
是一个 工具集 ,这个就十分强大了,postcss
对css
的意义,就等同于babel
对于js
的意义,他自身作为插件,还可以携带插件。
resolveLoader
解析 loader
, 告知 webpack
如何匹配 loader
。当我们自定义一些 loader
的时候。可以通过绝对路径的方式导入,但是过于繁琐,当我们通过这个字段指定文件夹的时候,就可以像使用第三方 loader
一样直接写 loader
的名字了。
// webpack.config.js
resolveLoader: {
// 默认是 node_modules
modules: ["node_modules", "./myLoaders"]
},
常用插件的常用配置
html-webpack-plugin
版本:4.5.2
- template:使用指定模板生成
html
。 - filename:打包后的
html
文件名。 - chunks:打包后的
html
会包含哪些chunk
。
// webpack.config.js
plugins: [
// 自动生成 html 文件 , 引入 bundle 文件, 压缩 html
new htmlWebpackPlugin({
// 模板匹配
template: "./src/index.html",
filename: "index.html",
chunks: ["index", 'login'],
}),
]
如果要生成多个 html
,只需要创建多个实例即可。
// webpack.config.js
plugins: [
// 自动生成html 文件 ,引入bundle文件,压缩html
new htmlWebpackPlugin({
// 模板匹配
template: "./src/index.html",
filename: "index.html",
chunks: ["index", 'login'],
}),
// 自动生成html 文件 ,引入bundle文件,压缩html
new htmlWebpackPlugin({
// 模板匹配
template: "./src/index.html",
filename: "login.html",
chunks: ["login"],
}),
// 自动生成html 文件 ,引入bundle文件,压缩html
new htmlWebpackPlugin({
// 模板匹配
template: "./src/index.html",
filename: "home.html",
chunks: ["home"],
}),
]
但是作为能懒则懒得程序员,是万万不可能写这种代码的,因为 webpack
的配置文件本身就是一个对象。所以我们当然可以利用一些方法来自动生成配置项。
首先要规定好结构,本例中每个文件夹中存放一个 index.html
和 index.js
,结构如下。当然可以自己随意定义结构,然后自己编写对应的函数体就可以了。
├── src
├── list
│ ├── index.html
│ └── index.js
├── login
│ ├── index.html
│ └── index.js
├── detail
│ ├── index.html
│ └── index.js
├── index
│ ├── index.html
│ └── index.js
配置如下,利用 setMap
方法自动生成 entry
和 htmlWebpackPlugins
,这样每次添加文件都不用我们修改配置了。
// webpack.config.js
const path = require('path')
const htmlWebpackPlugin = require("html-webpack-plugin")
// 模糊匹配路径
const glob = require('glob')
// 自动生成 entry 和 htmlWebpackPlugins
const setMap = () => {
const entry = {};
const htmlWebpackPlugins = []
// 模糊匹配 src 目录下 任意目录下的 index.js 返回的是文件的绝对路径
const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"))
// 遍历匹配到的结果
entryFiles.forEach((entryFile) => {
// 获取到文件名
const pageName = entryFile.match(/src/(.*)/index.js$/)[1]
// 生成 entry
entry[pageName] = entryFile
// 生成 htmlWebpackPlugins
htmlWebpackPlugins.push(
new htmlWebpackPlugin({
template: `./src/${pageName}/index.html`,
filename: `${pageName}.html`,
chunks: [pageName]
}))
})
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins } = setMap()
// 配置文件
module.exports = {
entry,
// 输出资源文件的信息
output: {
// 存储位置
path: path.resolve(__dirname, './dist'),
// 文件名称
filename: " [name].js"
},
// 打包模式
mode: "development",
// 插件
plugins: [
...htmlWebpackPlugins,
],
}
postcss
postcss
也有他自己的配置文件,根目录下新建 postcss.config.js
,也是个模块,通过require("autoprefixer")
安装插件。
// postcss.config.js
module.exports = {
plugins: [
require("autoprefixer"),
require("cssnano")
]
}
postcss的插件autoprefixer
autoprefixer
自动给 css
属性加前缀,使用 can i use
的数据。
但是这样还不会生效,需要设置 browserslist
,目标浏览器集合 ,也就是 需要兼容的浏览器版本,用到浏览器集合的工具会根据 browserslist
的描述, 针对性的输出兼容性的代码。
browserslist
的设置有两种方式。
你可以直接在 package.json
文件中添加属性如下
"browserslist": [
">1%",
"last 2 versions"
]
也可以根目录下新建 .browserslistrc
文件,直接写,什么括号都不用。
// .browserslistrc
>1%
last 2 versions
- >1% : 全球市场占有率 大于1%的浏览器。
- last 2 versions : 兼容浏览器的最近两个大版本。
可以使用 npx browserslist
查询兼容的浏览器版本。
postcss的插件cssnano
压缩 css
代码。
mini-css-extract-plugin
把 css
代码抽离出来单独放到一个文件里,可以指定文件名,支持绝对路径,会自动生成文件夹。需要在写 loader
的位置, 使用 minicss.loader
替换 style-loader
。
// webpack.config.js
const minicss = require("mini-css-extract-plugin")
module.exports = {
plugins: [
new minicss({
filename: 'style/index.css'
})
],
module: {
rules: [
{
test: /.less$/,
use: [
minicss.loader,
{
loader: "css-loader",
options: {
modules: true
}
},
{
loader: 'postcss-loader',
},
{
loader: 'less-loader',
}
]
}
]
}
}
clean-webpack-plugin
每次打包之前清理打包目录。
代码语言:javascript复制// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin()
],
}
实现自定义loader
loader的引用
根目录下新建 myLoaders/a-loader.js
文件,然后在 module
中需要使用绝对路径的方式引入。
// webpack.config.js
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: path.resolve(__dirname, './myLoaders/a-loader.js'),
},
]
},
]
}
loader的结构
loader
就是一个函数。接收一个参数 source
,source
就是符合 test
中正则的模块内容,本例中就是 .js
后缀的文件内容。
- 这个函数不可以是 箭头函数,因为
webpack
提供了loader api
都是挂载到this
上的。 - 这个函数必须有 返回值,否则会报错。
// a-loader.js
module.exports = function(source) {
console.log('