1 webpack简介
webpack is a module bundler -- 一言以蔽之,webpack就是一个模块化打包工具
我们日常开发过程中编写的代码可视为源码,而webpack将从入口文件出发,依次识别出模块导入语句,将所需的文件打包输出成一个或多个独立文件。
2 webpack安装
2.1 环境
注意需要nodeJS 环境才能运行,没有安装node的需要去安装,nodejs.org/en/
2.2 安装
推荐在项目初识化后,进入到项目内安装,不建议全局安装~
# 项目中安装到开发依赖中
npm i -D webpack
# 安装指定版本
npm i -D webpack@<version>
# webpack4以后需要同时安装webpack和webpack-cli
npm i -D webpack-cli
2.3 检查安装版本
如下可以检查项目内的webpack
npx webpack -v
./node_modules/.bin/webpack -v
3 webpack启动
3.1 webpack默认配置
webpack4以后是可以支持零配置运行的,也就是说可以不配置webpack.config.js这样一个文件,就可以实现构建,但是很弱,只支持js和json文件的打包;
默认配置如下:
const path = require("path");
module.exports = {
// 入口文件地址
entry: "./src/index.js",
output: {
// 打包输出文件名
filename: "main.js",
// 打包输出文件绝对地址
path: path.resolve(__dirname, "./dist")
}
};
3.2 准备
可以创建src文件然后写一个index.js如下:
### index.js
const json = require("./index.json");//commonJS
import { add } from "./other.js";//es module
console.log(json, add(2, 3));
### index.json
{
"name": "JOSN"
}
### other.js
export function add(n1, n2) {
return n1 + n2;
}
3.3 执行构建
# npx 方式
npx webpack
# npm script
npm run dev
# package.json 添加如下的命令
"scripts": {
"dev": "webpack"
}
正常构建成功,可以在项目目录中看到一个dist目录,且其下有打包成的main.js文件;
4 webpack核心配置
通过上面3.1中所说的默认配置,我们可以看到配置文件webpack.config.js 是通过module.exports的形式暴露了一个配置对象,我们需要根据自己的业务需求,结合loader和plugin来配置即可;
关于module.exports, export和export default:
4.1 entry-入口
入口文件设置,有三种形式, 注意字符串和数组都是打包到一个文件,而对象形式的配置是用于打包多个js文件适用于mpa;
-
字符串:
entry:"./src/index.js"
-
数组
entry:["./src/index.js","./src/login.js"]
-
对象
entry:{ index:"./src/index.js", login:"./src/login.js" }
4.2 output-输出
如下的配置中name是占位, 对应入口中的文件名,chunkhash占位指的是本次打包产出hash值,文件有修改更新时,每次构建的hash值会不同;
// 对象形式定义输出文件路径和文件名称
output: {
filename: "[name][chunkhash:8].js",
path: path.resolve(__dirname, "dist")
},
扩展:
这里涉及到前端缓存的问题,当项目在服务器部署以后,客户端访问链接获取到对应的html文件,此时html文件中的main_[hash].js会被缓存到浏览器,服务端可以设置过期时间来实现前端性能优化,也就是再次访问时可以取缓存。此时当我们迭代前端代码并上线后,构建出的main_[hash].js文件会与之前不同,浏览器就会重新向服务器发请求来获取这个新的****main_[hash].js,那么我们就可以看到修改后的页面;
这就是为什么前端有缓存以提高性能,网站更新后还能让用户可获取最新应用;
4.3 mode-构建模式
mode的值有三种:
-
production :用于正式环境构建,触发自身的一些优化压缩函数;
-
development :用于开发环境的构建,更好的服务于开发时热更新;
-
none:没有任何默认的优化项目;
更详细的说明见如下:www.webpackjs.com/concepts/mo…
4.4 module-模块
webpack就是用来将各个模块文件打包到一起的,而上面提到过webpack打包时可以对引入的js和json进行打包,那么遇到其他文件时如何解析处理呢?这时候就需要配置module了,module以对象形式进行配置,可以看做是模块解析规则配置,描述的就是匹配到什么样的文件,指定用什么loader去解析;
配置的举例如下:
module: {
rules: [// 此处如果配置多个,执行有顺序,从右到左侧,从下到上;
{
test: /\.(png|jpe?g|gif)$/,// 对于png,jpg,jpeg,gif的文件使用file-loader
use: {
loader: "file-loader",
options: {
name: "[name]_[hash].[ext]",// 名字并添加hash,ext是与原来文件后缀名一致
outputPath: "images/"// 输出到dist/images/目录下
}
}
},
{// 处理css文件
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{// 处理less文件
test: /\.less$/,
// loader从右向左依次处理,postcss-loader+autoprefixer 实现自动补全css前缀,
// 注意需要配置postcss.config.js
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
// postcss.config.js
const autoPrefix = require("autoprefixer");
module.exports = {
plugins: [
autoPrefix({
overrideBrowserslist: ["last 2 versions", ">1%"]
})
]
}
4.5 loader
loader可以看作是模块转化器,它可以将源码中现有的文件转化为我们需要的形式;
比如:
file-loader 处理图片或字体文件,作用就是对静态资源复制放到打包输出文件中,并让js可以正确引用到对应路径;
css-loader和style-loader可以将样式文件中的内容提取出来放到style标签中;
4.6 plugins-扩展插件
在webpack执行的某个过程中,可以添加一些拓展逻辑来实现你的需求,比如生成html模版将新构建的js引入到其中,再或者是上传服务器直接部署上线;
HtmlWebpackPlugin
这里说一个比较常用的插件,就是这个插件可以实现我们刚才说的,创建html引入最新js;
配置方法:
安装
npm install --save-dev html-webpack-plugin
配置
// webpack.config.js
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
new htmlWebpackPlugin({
title: "My App",
filename: "app.html",
template: "./src/index.html"
})
]
};
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initialscale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
结果
在打包后的dist目录中出现一个app.html文件,并在其中插入了打包后的js文件;
clean-webpack-plugin
该插件作用于构建之前,在构建之前将上一次打包的结果清空后再进行重新打包;
配置方式:
安装
npm install --save-dev clean-webpack-plugin
配置
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
...
plugins: [
new CleanWebpackPlugin()
]
...
mini-css-extract-plugin
将css文件打包时抽离出来成为一个单独的文件再进入到html中,而不是使用style标签去插入;
配置方式:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
...
// module > rules
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"] //这里就不使用style-loader了
}
...
// plugins
new MiniCssExtractPlugin({
filename: "[name][chunkhash:8].css"
})
4.7 Devtool-开发工具
此选项控制是否及如何生成source map,配置不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
当然也可以使用 SourceMapDevToolPlugin 进行更细粒度的配置。
devtool各个值的说明:webpack.docschina.org/configurati…
开发环境推荐使用:
eval-cheap-module-source-map
eval--使用eval包裹代码
cheap--只映射行关系,不映射列,这样提高打包性能
module--第三方模块也需要映射关系
inline--不单独生成.map文件,将文件以base64形式写入到当前js文件里;
none--不使用映射
4.8 DevServer-开发服务器
webpack-dev-server 是在本地开发时启动一个服务器,可以让我们通过一个ip地址+端口号访问到打包的资源,可以直接调试开发效果;
安装
npm install webpack-dev-server -D
配置
// package.json
...
"scripts":{
"server":"webpack-dev-server"
}
// webpack.config.js
...
devServer:{
contentBase:"./dist",
open:true,
port:3000
}
启动服务
npm run server
启动服务后,会发现dist⽬录没有了,这是因为devServer把打包后的模块不会放在
dist⽬录下,⽽是放到内存中,从⽽提升速度;
兼容问题
如果你使用的是 webpack: "^5.9.0",webpack-cli: "^4.2.0",webpack-dev-server: "^3.11.0",可能会遇到用webpack-dev-server 无法启动;可以尝试如下:
"scripts": {
"server": "webpack serve --open Chrome.exe"
},
代理解决跨域
在开发阶段前端应用和后端API服务不在一个源,因此直接访问会跨域,那么可以使用webpack-dev-server的代理功能进行配置,如将/api开头的接口代理到目标服务,配置如下:
// webpack.config.js
...
devServer:{
contentBase:"./dist",
open:true,
port:3000,
proxy:{
"/api":{
target:"http://localhost:8080"
}
}
}
4.9 HMR热模块替换
可以在捕捉到文件更新时,自动重新构建并刷新浏览器展示最新的结果;
配置
// webpack.config.js
const webpack = require("webpack");
...
devServer: {
contentBase: "./dist",
open: true,
hot:true, // 开启热模块更新
hotOnly:true // hotOnly为true时,浏览器不⾃动刷新就可以替换最新结果;
}
...
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "src/index.html"
}),
new webpack.HotModuleReplacementPlugin()//webpack内置的插件
],
// index.js
// js需要使⽤module.hot.accept来观察模块更新,从⽽更新
if (module.hot) {
// 监听某个文件变化,然后执行某个操作
module.hot.accept("./b", function() {
document.body.removeChild(document.getElementById("number"));
number();
});
}
5 Babel
将es6,es7语法转化成es5语法输出,使我们在开发时不用考虑兼容性问题。
安装
npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D
配置
// webpack.config.js
// module>rules
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
}
到此为止,默认的Babel只⽀持let等⼀些基础的特性转换,Promise等⼀些还有转换过来,这时候需要借助@babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性。
@babel/polyfill
安装
由于是业务所需的代码了,因此应该安装到生产依赖
npm install --save @babel/polyfill
使用
//index.js 顶部
import "@babel/polyfill";
优化-按需加载
// webpack.config.js
// module>rules
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
corejs: 2,//新版本需要指定核⼼库版本
useBuiltIns: "usage"// 自动检查并按需注⼊,但是需要安装@babel/polyfill
}
]
]
}
}
}
React配置
安装babel与react转换的插件@babel/preset-react
npm install --save-dev @babel/preset-react
// webpack.config.js
// module>rules
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
corejs: 2,//新版本需要指定核⼼库版本
useBuiltIns: "entry"// 需要引入@babel/polyfill
}
],
"@babel/preset-react"// react
]
}
}
}
若有收获,就点个赞吧