webpack 是一个现代 JavaScript 应用程序的静态模块打包器,当 webpack 处理应用程序时,会递归构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle。
主要有四个核心概念:
-
入口:起点指示webpack应该使用那个模块,来作为构建其内部依赖图的开始,默认是./src/index.js,可以配置entry属性来指定入口。
-
输出:告诉webpack在哪里输出所创建的bundle,以及如何命名这些文件,输出默认值是./dist/main.js,可以通过output来指定出口。
-
loader:loader让webpack有能力去处理其他类型的文件,并将他们转化为有效模块,以供他们使用,以及被添加到依赖图中。
-
plugin:loader用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,包括:打包优化,资源管理,注入环境变量。使用插件可以通过require导入,再添加到plugins数组中。
项目初始化
新建文档webpack5.0,运行npm init -y初始化项目,安装webpack、webapck-cli:
npm install webpack webpack-cli -D
安装的版本为:
"webpack": "^5.64.2",
"webpack-cli": "^4.9.1"
新建一个src/idnex.js文件:
class Animal {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const dog = new Animal('dog');
在命令行运行npx webpack --mode=development可以看到在根目录下新增了一个dist/main.js文件。这里使用的都是webapck的默认配置,在 node_modules/webpack/lib/WebpackOptionsDefaulter.js里详细看一下,在node_modules/webpack/lib/config/defaults.js
F(output, "path", () => path.join(process.cwd(), "dist"));
我们可以自己设置入口与出口配置,自定义打包的入口文件与出口文件:
新建webpack.config.js文件:
const path = require('path');
module.exports = {
entry:'./src/index.js',
output: {
path: path.resolve(__dirname, 'dist'), //必须是绝对路径
filename: 'bundle.js',
}
}
一般我们在项目打包就是npm run build,现在需要在package.json的script中添加配置:
"build": "cross-env NODE_ENV=production webpack",
"devBuild": "cross-env NODE_ENV=development webpack"
完整的webpack.config.js如下:
const path = require('path');
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
mode:isDev ? 'development' : 'production',
devtool: isDev ? 'source-map' : false,
entry:'./src/index.js',
output: {
path: path.resolve(__dirname, 'dist'), //必须是绝对路径
filename: 'bundle.js',
}
}
现在运行npm run build或者npm run devBuild即可完成打包,即在dist/build.js下生成了打包文件。
项目基础已经搭建完成,接下来做一些有意思的小案例。
基本案例
将js转译为低版本
前面打包的代码还是ES6的代码,要将ES6代码转译为ES5代码,需要使用babel-loader和其依赖。
npm install babel-loader -D
npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
npm install @babel/runtime @babel/runtime-corejs3
loader有两个属性:
test属性,识别出哪些文件会被转换。use属性,定义出在进行转换时,应该使用哪个 loader。
loader使用有两种方式:
- 配置方式(推荐):在
webpack.config.js文件中指定loader - 内联方式:在每个
import语句中显示指定loader
在webpack.config.js添加module.rules:
rules: [
{
test: /\.jsx?$/,
use: ['babel-loader'],
exclude: /node_modules/ //排除 node_modules 目录
}
]
同时在根目录下配置一个.babelrc文件:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
在命令行运行npm run devBuild,就可以看到ES6代码已经被转译了。
在浏览器中查看页面
可以根据配置文件动态展示页面内容。
安装所需插件:
"webpack-dev-server": "^4.5.0"
"html-webpack-plugin": "^5.5.0",
先在根目录下新建一个public/index.html和public/config.js文件:
module.exports = {
dog: {
template: {
hair: 'red',
name:'jack'
}
},
cat: {
template: {
hair: 'black',
name:'Jerry'
}
}
}
<!DOCTYPE html>
<html>
<head>
<title><%= (htmlWebpackPlugin.options.config.name) %></title>
<meta charset="utf-8">
</head>
<body>
<div><%= (htmlWebpackPlugin.options.config.hair) %></div>
<div class="color">hello world</div>
</script>
</body>
</html>
在package.json中的script添加:
"dev": "cross-env NODE_ENV=development webpack-dev-server"
在webpack.config.js添加插件:
const HtmlWebpackPlugin = require('html-webpack-plugin');
...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html', //打包后的文件名
config: config.template
})
],
运行npm run dev,在http://localhost:8080/中即可看到dog模板中的内容。
处理样式文件
webpack处理css需要借助loader。首先安装依赖:
npm install style-loader less-loader css-loader postcss-loader autoprefixer less -D
在relus中添加:
{
test: /\.(le|c)ss$/,
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')()
]
}
}
}, 'less-loader'],
exclude: /node_modules/
}
新建src/index.css:
.color{
color:red;
}
这里改了半天,webpack5.0和高版本loader不兼容,降级之后打包成功。
"autoprefixer": "^9.0.0",
"postcss-loader": "^3.0.0",
//https://github.com/laravel-mix/laravel-mix/issues/2471
//https://stackoverflow.com/questions/64057023/error-postcss-plugin-autoprefixer-requires-postcss-8-update-postcss-or-downgra
已经可以看到页面的hello world变成了红色。
图片处理
处理本地资源可以使用url-loader。
在rules配置loader:
{
test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240, //10K
esModule: false
}
}
],
exclude: /node_modules/
}
在index.css中引入图片:
.img{
width:100vh;
height: 100px;
border:1px solid red;
background:url('../pka1.png');
}
既可以成功在页面上看到所引入的图片了。
静态资源拷贝
当需要使用已有的本地文件但不需要webpack编译,除了手动复制到构建目录(dist),还可以是使用copy-webpack-plugin插件。
该插件有两个参数:
- patterns, {Array<String|Object>} ,为插件指定文件相关模式。
- from 复制文件的路径
- to 输出路径
- content 决定如何解释from的路径
- toType 确定to的选项 dir-目录,file-文件,template-模板
- globOptions, 允许配置插件使用的 glob 模式匹配库
- options, {Object} , 指定插件选项
新建文件public/js/color.js``public/js/filter.js和:
console.log('这是待引入的js文件')
console.log('这是要过滤的js文件')
在webpack.config.js中添加配置:
const CopyWebpackPlugin = require('copy-webpack-plugin');
...
//plugins中添加
new CopyWebpackPlugin({
patterns:[
{
from: 'public/js/*.js',
to: path.resolve(__dirname, 'dist', 'js'),
toType:'dir',
globOptions:{
ignore: ["**/filter.*"],
}
},
})
运行npm run devBuild,即可看见在dist目录下有一个拷贝的js文件夹,里面只有color.js。
单独打包css文件
如果需要单独打包css文件,可以用到mini-css-extract-plugin插件。
这个插件将 CSS 提取到单独的文件中。它为每个包含 CSS 的 JS 文件创建一个 CSS 文件。它支持按需加载 CSS 和 SourceMap。
它建立在新的 webpack v5 功能之上,并且需要 webpack 5 才能工作。
常用参数:
- filename 确定每个输出css文件的名称
- chunkFilename 确定非条目块的名称
首先安装依赖:npm install mini-css-extract-plugin -D,
更改webapck.config.js:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
。。。
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
并且使用MiniCssExtractPlugin.loader替换style-loader。
运行nom run devBuild,既可以看见dist目录下新增了一个css文件。
按需加载
很多时候我们不需要一次性加载所有的JS文件,而应该在不同阶段去加载所需要的代码。
webpack内置了强大的分割代码的功能可以实现按需加载。
比如vue文档中提到可以使用路由懒加载提升性能:
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
热更新
配置devServer中的hot:true,随后在webpack.config.js中配置:
const webpack = require('webpack');
...//在plugins中添加插件
new webpack.HotModuleReplacementPlugin(), //热更新插件