一、什么是webpack
WebPack是一个现代JS应用程序的静态模块打包器(module bundler)
模块(模块化开发,可以提高开发效率,避免重复造轮子)
打包(将各个模块,按照一定的规则组装起来)
官网:webpack.js.org/ 中文网 webpack.docschina.org/
特点:功能强大(打包,构建,发布web服务),学习成本高
什么是前端工程化
前端工程化是依据业务特点,将前端开发的规范、流程、技术、工具、经验等形成规范并建立成一种标准的体系
构建工具
构建工具的主要功能就是实现自动化处理,例如对代码进行检查、预编译、合并、压缩;生成雪碧图、sourceMap、版本管理;运行单元测试、监控等,当然有的工具还提供模块化、组件化的开发流程功能。
网上各类的构建工具非常多,有家喻户晓的 Webpack、Gulp、 Grunt,也有各大公司团队开源的构建工具,这里通过 Github 的 Star 数量来简单的对比下各个工具的流行度
二、安装webpack
1.初始化包描述文件
npm init
2.全局安装webpack
npm i webpack webpack-cli -g
3.将webpack添加到package.json的依赖中
npm i webpack webpack-cli -D
4.运行命令
npx webpack 或者 webpack
1).开发环境
默认会生成mian.js的文件
webpack ./src/index.js -o ./build --mode=development
2).生产环境
webpack ./src/index.js -o ./build --mode=production
npx会把当前项目下node_modules的bin当前运行环境
三、五大核心概念
- entry(入口)
指示 Webpack 从哪个文件开始打包
- output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
- loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
- plugins(插件)
扩展 Webpack 的功能
- mode(模式)
主要由两种模式:
- 开发模式:development
- 生产模式:production
1).配置文件
// Node.js的核心模块,专门用来处理文件路径
const path = require("path");
module.exports = {
// 入口
// 相对路径和绝对路径都行
entry: "./src/main.js",
// 输出
output: {
// path: 文件输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname 当前文件的文件夹绝对路径
path: path.resolve(__dirname, "dist"),
// filename: 输出文件名
filename: "main.js",
},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "development", // 开发模式
};
2).运行命令
npx webpack
四、loader
Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源
我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用
官方文档找不到的话,可以从社区 Github 中搜索查询
css 资源
1.下载包
npm i css-loader style-loader -D
注意:需要下载两个 loader
- 功能介绍
- css-loader:负责将 Css 文件编译成 Webpack 能识别的模块
- style-loader:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容
此时样式就会以 Style 标签的形式在页面上生效
- 配置
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
],
},
plugins: [],
mode: "development",
};
- 添加 Css 资源
- src/css/index.css
.box1 {
width: 100px;
height: 100px;
background-color: pink;
}
- src/main.js
import count from "./js/count";
import sum from "./js/sum";
// 引入 Css 资源,Webpack才会对其打包
import "./css/index.css";
console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));
- public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<!-- 准备一个使用样式的 DOM 容器 -->
<div class="box1"></div>
<!-- 引入打包后的js文件,才能看到效果 -->
<script src="../dist/main.js"></script>
</body>
</html>
Less 资源
1.下载包
npm i less-loader -D //建议是less-loader@6
- 功能介绍
- less-loader:负责将 Less 文件编译成 Css 文件
- 配置
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
],
},
plugins: [],
mode: "development",
};
4.添加Less资源
- src/less/index.less
.box2 {
width: 100px;
height: 100px;
background-color: deeppink;
}
- src/main.js
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/index.css";
import "./less/index.less";
console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));
- public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<div class="box1"></div>
<div class="box2"></div>
<script src="../dist/main.js"></script>
</body>
</html>
Sass 和 Scss 资源
1.下载包
npm i sass-loader sass -D
注意:需要下载两个
2.功能介绍
- sass-loader:负责将 Sass 文件编译成 css 文件
- sass:sass-loader 依赖 sass 进行编译
3.配置
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
{
test: /.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /.s[ac]ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [],
mode: "development",
};
4.添加 Sass 资源
- src/sass/index.sass
/* 可以省略大括号和分号 */
.box3
width: 100px
height: 100px
background-color: hotpink
- src/sass/index.scss
.box4 {
width: 100px;
height: 100px;
background-color: lightpink;
}
- src/main.js
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));
- public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
<div class="box4"></div>
<script src="../dist/main.js"></script>
</body>
</html>
图片资源
过去在 Webpack4 时,我们处理图片资源通过 file-loader 和 url-loader 进行处理
现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
{
test: /.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /.s[ac]ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /.styl$/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
],
},
plugins: [],
mode: "development",
};
使用图片资源
.box2 {
width: 100px;
height: 100px;
background-image: url("../images/1.jpeg");
background-size: cover;
}
自动清空上次打包资源
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js",
clean: true, // 自动将上次打包目录资源清空
},
处理字体图标资源
- 打开阿里巴巴矢量图标库
- 选择想要的图标添加到购物车,统一下载到本地
- src/main.js
import "./css/iconfont.css";
- public/index.html
<i class="iconfont icon-arrow-down"></i>
<i class="iconfont icon-ashbin"></i>
<i class="iconfont icon-browse"></i>
- 配置
{
test: /.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
type: "asset/resource"和type: "asset"的区别:
- type: "asset/resource" 相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理
- type: "asset" 相当于url-loader, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
视频音频也可以使用以上配置,只需要修改 test: /.(ttf|woff2?|map4|map3|avi)$/
TypeScript
1.安装模块
npm install ts-loader typescript -D
2.生成tsconfig.json
tsc --init
3.配置规则
rules: [
{
test: /.ts$/,
loader: "ts-loader",
},
],
alias别名设置
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, "src"),
'_c': path.resolve(__dirname, "src/components"),
},
// 配置省略文件路径的后缀名
extensions: [".ts", ".js",'.cjs','.json'],
// 告诉webpack 解析模块失去找哪个个目录
modules: [path.resolve(__dirname, '../../node_modules'), 'node_modules'], // 迅速找到node_modules所在
}
};
tree shaking
生产环境下默认有tree shaking
Tree Shaking 指的就是当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码,这就需要借助 webpack 里面自带的 Tree Shaking 这个功能来帮我们实现。
官方有标准的说法:Tree-shaking的本质是消除无用的js代码。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)
在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 Tree-Shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。
在production 模式下不用在webpack.config.js中配置
optimization: {
usedExports: true
}
Eslint
使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查,完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理
1.配置文件
配置文件由很多种写法:
- .eslintrc.*:新建文件,位于项目根目录
-
- .eslintrc
- .eslintrc.js
- .eslintrc.json
- 区别在于配置格式不一样
- package.json 中 eslintConfig:不需要创建文件,在原有文件基础上写
ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2.具体配置
我们以 .eslintrc.js 配置文件为例:
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
- parserOptions 解析选项
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}
- rules 具体规则
- "off" 或 0 - 关闭规则
- "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
- "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
rules: {
semi: "error", // 禁止使用分号
'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
'default-case': [
'warn', // 要求 switch 语句中有 default 分支,否则警告
{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
],
eqeqeq: [
'warn', // 强制使用 === 和 !==,否则警告
'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
],
}
更多规则详见:规则文档
- extends 继承
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
- Eslint 官方的规则:eslint:recommended
- Vue Cli 官方的规则:plugin:vue/essential
- React Cli 官方的规则:react-app
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};
3. 在 Webpack 中使用
- 下载包
npm i eslint-webpack-plugin eslint -D
- 定义 Eslint 配置文件
- .eslintrc.js
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};
- 修改 js 文件代码
- main.js
var result1 = count(2, 1);
console.log(result1);
var result2 = sum(1, 2, 3, 4);
console.log(result2);
4.配置
- webpack.config.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = {
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
],
mode: "development",
};
- . eslintignore
# 忽略dist目录下所有文件
dist
Babel
JavaScript 编译器。
主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
1.配置文件
配置文件由很多种写法:
- babel.config.*:新建文件,位于项目根目录
babel.config.js
babel.config.json - .babelrc.*:新建文件,位于项目根目录
.babelrc
.babelrc.js
.babelrc.json - package.json 中 babel:不需要创建文件,在原有文件基础上写
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2.具体配置
我们以 babel.config.js 配置文件为例:
module.exports = {
// 预设
presets: [],
};
- presets 预设
简单理解:就是一组 Babel 插件, 扩展 Babel 功能
- @babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。
- @babel/preset-react:一个用来编译 React jsx 语法的预设
- @babel/preset-typescript:一个用来编译 TypeScript 语法的预设
3. 在 Webpack 中使用
- 下载包
npm i babel-loader @babel/core @babel/preset-env -D
- 定义 Babel 配置文件
- babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
};
- 配置
- webpack.config.js
module.exports={
module:{
rules:[
{
test: /.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
]
}
}
Html 资源
1.下载包
npm i html-webpack-plugin -D
2.配置
- webpack.config.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true, // 自动将上次打包目录资源清空
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "public/index.html"),
}),
],
mode: "development",
};
3.修改 index.html
去掉引入的 js 文件,因为 HtmlWebpackPlugin 会自动引入
devServer
1).安装
npm i webpack-dev-server -D
2).配置
// 开发服务器
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器,
compress:true, //开启gzip
hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了
},
3).启动
"serve": "webpack-dev-server"
vue-loader配置
1).安装
"vue": "^2.7.14",
"vue-loader": "^15.7.2",
"vue-template-compiler": "^2.6.10"
2).配置 rules
{
test: /.vue$/,
use: ["vue-loader"],
},
{
test: /.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
}
3).配置plugins
const VueLoaderPlugin = require('vue-loader/lib/plugin')
plugins:[
new VueLoaderPlugin()
]
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
}).$mount("#app");
package.json
{
"name": "mywepack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"serve": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^4.2.2",
"less-loader": "^6.2.0",
"node-sass": "^8.0.0",
"sass-loader": "^13.2.0",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.6",
"vue": "^2.7.14",
"vue-style-loader": "^4.1.3"
},
"devDependencies": {
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1",
"vue-loader": "^15.7.2",
"vue-template-compiler": "^2.6.10"
}
}
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
// cpu核数
const os=require("os");
// 线程
const threads = os.cpus().length;
module.exports = {
mode: "development",
entry: path.resolve(__dirname, "/src/main.js"),
output: {
path: path.resolve(__dirname, "dist"),
filename: "build.js",
clean: true,
},
module: {
rules: [
// {
// test: /\.css$/i,
// use: ["style-loader", "css-loader"],
// },
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.s(a|c)ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(jpg|jpeg|png|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 30 * 1024, // 小于10kb的图片会被base64处理
},
},
generator: {
filename: "./images/[hash:8][ext][query]",
},
},
{
test: /\.vue$/,
use: ["vue-loader"],
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public/index.html"),
filename: "index.html",
inject: "body",
}),
new VueLoaderPlugin(),
],
optimization: {
minimizer: [
// css压缩也可以写到optimization.minimizer里面,效果一样的
new CssMinimizerPlugin(),
// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
new TerserPlugin({
parallel: threads, // 开启多进程
}),
]
},
// 开发服务器
devServer: {
compress:false,
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
};