当我们不使用任何构建工具,直接使用es6的import引入js文件时,会报错不支持importUncaught SyntaxError: Cannot use import statement outside a module
,此时我们就需要使用webpack来编译成为浏览器能识别的语法
npm init -y // 初始化一个package.json文件
npm i webpack webpack-cli -D // 安装webpack依赖
// 执行webpack的命令进行development环境打包
npx webpack ./src/main.js --mode=development
// 生产环境打包会对代码进行压缩
npx webpack ./src/main.js --mode=production
即可生成dist文件夹,此时为index.html文件引入dist文件夹中的main.js即可使用之前import引入的js文件
webpack本身的功能只能处理js资源,所以之后的重点是如何处理其他资源
1.entry:入口
2.output:输出
3.loader:加载器(借助loader加载非js、json资源)
4.plugins:插件
devServer:开发服务器 // 非核心概念
5.mode:模式(开发:development,生产:production)
在文件夹根目录创建webpack.config.js配置文件
const path = require('path') // nodejs核心模块,专门用来处理路径问题
module.exports = {
entry: './src/main.js',
output: {
// __dirname nodejs变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, 'dist'),
// filename入口文件打包输出的文件名
filenam: 'main.js',
},
module: {
rules: [
// loader的配置
],
},
plugins: [
// 插件的配置
],
mode: 'development',
}
上面的内容就相当于我们文章最开始执行的默认命令,因为已经配置完成,我们只需要执行npx webpack
即可实现打包
开发代码时使用的模式,该模式下我们主要做两件事
参考官网css-loader | webpack 中文文档 (docschina.org)
在入口文件index.js引入样式
使用文档对应loader配置并下载
其他css资源例如less、sass等同理
在webpack5中已经内置的处理图片资源的功能,因此不需要配置
但是如果我们想要添加一些额外的功能,就需要参照官网进行配置
例如,将小于10k的图片转化为base64格式
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
},
查看dist目录我们会发现,当前打包的图片以及js文件都在一个目录下,这样子会很乱。因此我们要分类进行打包。
output可以设置入口文件即index.js打包输出的文件名,因此我们配置此处就可以设置js文件的输出
output: {
// __dirname nodejs变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, 'dist'),
// filename入口文件打包输出的文件名
filename: 'static/js/main.js',
},
那图片该如何配置呢,我们查阅webpack官网,发现有一个generator
属性进行配置
{
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]",
},
},
这样我们就达到了效果
在output配置属性clean
为true
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js",
clean: true, // 自动将上次打包目录资源清空
},
实际上字体图标也不需要进行处理,引入css即可,但是我们想将字体资源打包到一个单独的文件夹下,这样就需要进行配置
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
这里补充一下
type: "asset/resource"
相当于file-loader
, 将文件转化成 Webpack 能识别的资源,其他不做处理(即原封不动输出资源)
type: "asset"
相当于url-loader
, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
同处理字体图标一样,原封不动输出即可,我们可以写在一起
test: /\.(ttf|woff2?|map4|map3|avi)$/,
配置文件由很多种写法:
.eslintrc.*
:新建文件,位于项目根目录
.eslintrc
.eslintrc.js
.eslintrc.json
package.json
中 eslintConfig
:不需要创建文件,在原有文件基础上写ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
webpack5中的Eslint是一个插件,因此我们要下载并引入
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
],
接下来创建并开始配置.eslintrc.js
文件
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}
"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 除了少数情况下不会有警告
],
}
更多规则详见:规则文档
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
eslint:recommended
plugin:vue/essential
react-app
VSCode Eslint 插件
打开 VSCode,下载 Eslint 插件,即可不用编译就能看到错误,可以提前解决
但是此时就会对项目所有文件默认进行 Eslint 检查了,我们 dist 目录下的打包后文件就会报错。但是我们只需要检查 src 下面的文件,不需要检查 dist 下面的文件。
所以可以使用 Eslint 忽略文件解决。在项目根目录新建下面文件:.eslintignore
# 忽略dist目录下所有文件
dist
配置文件由很多种写法:
babel.config.*
:新建文件,位于项目根目录
babel.config.js
babel.config.json
.babelrc.*
:新建文件,位于项目根目录
.babelrc
.babelrc.js
.babelrc.json
package.json
中 babel
:不需要创建文件,在原有文件基础上写
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
babel在webpack中使用的是loader
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
接下来创建并配置babel.config.js
module.exports = {
// 预设
presets: [],
};
简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env
: 一个智能预设,允许您使用最新的 JavaScript。@babel/preset-react
:一个用来编译 React jsx 语法的预设@babel/preset-typescript
:一个用来编译 TypeScript 语法的预设前面的内容中,我们所有打包好的js都需要在html中引入,非常不方便,webpack为我们提供了插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "public/index.html"),
}),
此时我们就不需要在html中专门引入js了,此处template
属性的作用是获取原html文件的内容
每次代码都要手动输入指令才能重新编译,太过繁琐
我们下载一个webpack的包
npm i webpack-dev-server -D
配置webpack的devServer
属性
// 开发服务器
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
运行指令此时变为
npx webpack serve
注意:开发服务器不会将资源打包到dist目录
事实上,开发模式本身就不需要输出;所以就不需要配置output字段,只有生产模式才需要。
我们创建一个config
文件用来存放开发和生产模式下webpack的配置文件
├── config (Webpack配置文件目录)
│ ├── webpack.dev.js(开发模式配置文件)
│ └── webpack.prod.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: undefined, // 开发模式没有输出,不需要指定输出目录
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
// clean: true, // 开发模式没有输出,不需要清空输出结果
},
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]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
],
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
],
// 其他省略
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
mode: "development",
};
运行指令
npx webpack serve --config ./config/webpack.dev.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,
},
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]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
],
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
],
// devServer: {
// host: "localhost", // 启动服务器域名
// port: "3000", // 启动服务器端口号
// open: true, // 是否自动打开浏览器
// },
mode: "production",
};
为了方便运行不同模式的指令,我们将指令定义在 package.json 中 scripts 里面
// package.json
{
// 其他省略
"scripts": {
"start": "npm run dev",
"dev": "npx webpack serve --config ./config/webpack.dev.js",
"build": "npx webpack --config ./config/webpack.prod.js"
}
}
npm start
或 npm run dev
npm run build
上面的配置会使css文件打包在js文件中,会出现先加载js再加载css的情况,用户体验不好,因此我们使用插件来单独打包css,参考官网教程即可
npm i mini-css-extract-plugin -D
wenpack配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
{
test: /\.s[ac]ss$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
{
test: /\.styl$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "stylus-loader"],
},
],
},
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
],
npm i postcss-loader postcss postcss-preset-env -D
wenpack配置
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
],
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
"less-loader",
],
},
{
test: /\.s[ac]ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
"sass-loader",
],
},
{
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
"stylus-loader",
],
},
],
},
控制兼容性
在 package.json
文件中添加 browserslist
来控制样式的兼容性做到什么程度。实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
{
// 其他省略
"browserslist": ["last 2 version", "> 1%", "not dead"]
}
上面代码中重复配置太多了,我们选择封装一个函数
// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};
// 下面就可以复用了
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
下载插件
npm i css-minimizer-webpack-plugin -D
调用
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
new CssMinimizerPlugin(),