webpack_code #项目根目录(所有指令必须在这个目录下运行
|____src #项目源码目录
|___ js #js文件目录
| |__ count.js
| |__ sum.js
|____main.js # 项目主文件
export default function count(x,y) {
return x-y;
}
export default function sum(...args) {
return args.reduce((p, c) => p + c, 0);
}
import count from "./js/count";
import sum from "./js/sum";
console.log(count(2,1));
console.log(sum(1, 2, 3, 4));
打开终端,来到项目根目录。运行以下指令:
npm init -y
此时会生成一个基础的package.json 文件。
需要注意的是 package.json 中 name 字段不能叫做 webpack ,否则下一步会报错
npm i webpack webpack-cli -D
npx webpack ./src/main.js --mode=development
npx webpack ./src/main.js --mode=production
npx webpack :是用来运行本地安装 Webpack 包的。
./src/main.js :指定 webpack 从 main.js 文件开始打包,不但会打包 main.js ,还会将其它依赖也一起打包进来。
--mode=xxx :指定模式(环境)
默认 Webpack 会将文件打包输出到 dist 目录下,我们查看 dist 目录下文件情况就好了
Webpack 本身功能比较少,只能处理 js 资源,一旦遇到 css 等其他资源就会报错。
所以我们学习 Webpack,就是主要学习如何处理其他资源。
在开始使用 Webpack 之前,我们需要对 Webpack 的配置有一定的认识。
1. entry(入口)
指示 Webpack 从哪个文件开始打包
2. output (输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
3. loader(加载器)
webpack 本身只能处理 js、json 等资源需要借助 loader,Webpack 才能解析
4. plugins (插件)
扩展 Webpack 的功能
5. mode(模式)
主要由两种模式:
在根目录下新建文件:webpack.config.js
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
module.exports = {
// 入口
entry: './src/main.js', // 相对路径
// 输出
output: {
// 文件的输出路径
// __dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, "dist"), // 绝对路径
// 文件名
filename: 'main.js',
},
// 加载器
module: {
rules: [
// loader的配置
]
},
// 插件
plugins: [
// plugin的配置
],
// 模式
mode: "development",
// mode:"production",
}
开发模式顾名思义就是我们开发时使用的模式。
这个模式下我们主要做两件事:
1.编译代码,使浏览器能识别运行
开发时我们有样式资源、字体图标、图片资源、html资源等,webpack默认不能处理这些资源,所以我们要加载配置来编译这些资源
2.代码质量检查,树立代码规范
提前检查代码的一些隐患,让代码运行时能更加健壮。
提前检查代码规范和格式,同意团队编码风格,让代码更优雅美观。
学习使用 Webpack 如何处理 Css、Less、Sass、Scss、Styl 以及 png、jpeg 、gif 等图片资源
Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源
我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用
官方文档找不到的话,可以从社区 Github 中搜索查询
Webpack官方Loader文档
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
module.exports = {
// 入口
entry: './src/main.js', // 相对路径
// 输出
output: {
// 所有文件的输出路径
// __dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, "dist"), // 绝对路径
// 入口文件打包输出文件名
filename: 'main.js',
},
// 加载器
module: {
rules: [
// loader的配置
{
test: /\.css$/, // 只检测 .css文件
use: [ // 执行顺序:从右到左(从下到上)
"style-loader", // 将js中css通过创建style标签添加html文件中生效
"css-loader", // 将css资源编译成 commonjs 的模块到js中
],
},
{
test: /\.less$/,
// loader:'xxx', // 只能使用1个loader
use: [ // 使用多个loader
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader', // 将less编译成css文件
],
},
{
test: /\.s[ac]ss$/,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
"style-loader",
"css-loader",
"stylus-loader", // 将 Stylus 文件编译为 CSS
],
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
}
/* {
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
// 小于10kb 的图片转base64
// 优点:减少请求数量 缺点:体积会更大
maxSize: 4 * 1024 // 4kb
}
}
} */
],
},
// 插件
plugins: [
// plugin的配置
],
// 模式
mode: "development",
// mode:"production",
}
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
module.exports = {
entry: './src/main.js', // 相对路径
output: {
// 所有文件的输出路径
// __dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, "dist"), // 绝对路径
// 入口文件打包输出文件名
filename: 'static/js/main.js',
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader","css-loader"],
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
generator: {
// [hash:10] 代表hash值只取前10位
filename: 'static/images/[hash:10][ext][query]'
}
}
],
},
plugins: [],
mode: "development",
// mode:"production",
}
const path = require("path");
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, "dist"),
filename: 'static/js/main.js',
// 自动清空上次打包的内容
// 原理:在打包前,将path整个目录内容清空,再进行打包
clean: true,
},
module: {
rules: [
{}
],
},
plugins: [],
mode: "development",
// mode:"production",
}
const path = require("path");
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, "dist"),
filename: 'static/js/main.js',
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
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|svg)$/,
type: 'asset',
generator: {
filename: 'static/images/[hash:10][ext][query]'
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
// 输出名称
filename: 'static/media/[hash:10][ext][query]'
}
},
],
},
plugins: [],
mode: "development",
// mode:"production",
}
开发中可能还存在一些其他资源,如音视频等,我们也一起处理了
const path = require("path");
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, "dist"),
filename: 'static/js/main.js',
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
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|svg)$/,
type: 'asset',
generator: {
filename: 'static/images/[hash:10][ext][query]'
}
},
{
// 有其他资源时继续往后添加,就会原封不动输出的
test: /\.(ttf|woff2?|map3|map4|avi)$/,
type: 'asset/resource',
generator: {
filename: 'static/media/[hash:10][ext][query]'
}
},
],
},
plugins: [],
mode: "development",
// mode:"production",
}
有人可能会问,js资源 Webpack 不是已经处理了吗,为什么我们还要处理呢?
原因是 Webpack 对 js 处理是有限的,只能编译 js 中 ES 模块化语法,不能编译其他语法,导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。
其中开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。
我们先完成 Eslint ,检测代码格式无误后,在由 Babel 做代码兼容性处理
可组装的 JavaScript 和 JSX 检查工具。
这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能
我们使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查
配置文件有很多种写法:
.eslintrc.* :新建文件,位于项目根目录
package.json 中 eslintConfig :不需要创建文件,在原文件基础上写 Eslint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
我们以 .eslintrc.js 配置文件为例:
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules:{},
// 继承其他规则
extends:[],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
1. parserOptions 解析选项
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures:{ // ES 其他特性
jsx: ture // 如果是 React 项目,就需要开启 jsx 语法
}
}
2. rules 具体规则
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 除了少数情况下不会有警告
],
}
更多规则详见:规则文档
3. extends 继承
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends:["react-app"],
rules:{
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq:["warn","smart"],
},
1. 下载包
npm i eslint-webpack-plugin eslint -D
2. 修改 webpack.config.js 文件
// 把插件添加到你的 webpack 配置
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
// 插件
plugins: [
// plugin的配置
new ESLintPlugin({
// 检测哪些文件
context: path.resolve(__dirname, "src")
})
],
// ...
};
3. 定义 Eslint 配置文件
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: ture, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};
4.修改 js 文件代码
import count from "./js/count";
import sum from "./js/sum";
// 要想 webpack 打包资源,必须引入该资源
import "./css/iconfont.css";
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
import "./stylus/index.styl";
const result = count(2, 1); // 不能用 var 会报错
console.log(result);
// console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));
5. 添加.eslintignore 文件
由于我在VS Code里添加了Eslint语法检查的插件,而我们不需要他检查输出的js文件,也就是dist文件夹,此时我们要新添加一个配置文件.eslintignore 文件,里面写dist,这样他就会忽略dist文件夹里的js文件,从而不报红色波浪线的错误
dist
JavaScript 编译器。
主要用于将 ES6 语法编写的代码转换为向后兼容的JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
1. 配置文件
配置文件有很多种写法:
babel.config.* :新建文件,位于项目根目录
.babelrc.* :新建文件,位于项目根目录
package.json 中 babel :不需要创建文件,在原文件基础上写 Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2. 具体配置
我们以 babel.config.js 配置文件为例:
module.exports = {
// 预设
presets: [],
};
1. presets 预设
简单理解:就是一组 Babel 插件,扩展 Babel 功能
3. 在 Webpack 中使用
1. 下载包
npm i babel-loader @babel/core @babel/preset-env -D
2. 定义 Babel 配置文件
module.export = {
// 智能预设:能够编译ES6语法
presets: ['@babel/preset-env'],
}
3. 配置 webpack.config.js 文件
在 webpack 配置对象中,需要将 babel-loader 添加到 module 列表中,就像下面这样:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/, // 排除node_modules中的js文件(这些文件不处理)
/* use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
} */
/* loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
} */
loader: 'babel-loader',
// options也可以写在外面,babel.config.js 里面
}
]
}
npm i html-webpack-plugin -D
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
// 模板:以public/index.html 文件创建新的html文件
// 新的html文件特点:1.结构和原来一致 2.自动引入打包输出的资源
template: path.resolve(__dirname, "public/index.html"),
})
],
};
注意:自动化开发服务器运行后不会自动生成dist文件夹
npm i webpack-dev-server -D
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ......
plugins: [],
// 开发服务器:不会输出资源,在内存中编译打包的
devServer: {
host:"localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
mode:"development",
}
npx webpack serve
生产模式是开发完成代码后,我们需要将代码进行部署上线。
这个模式下我们主要对代码进行优化,让其运行性能更好。
优化主要从两个角度出发:
我们分别准备两个配置文件来放不同的配置
|—— webpack-test(项目根目录)
|—— config (Webpack配置文件目录)
| |—— webpack.dev.js(开发模式配置文件)| |—— webpack.prod.js(开发模式配置文件)
|—— node_modules (下载包存放目录)
|—— src(项目源码目录,除了html其他都在src里面)
| |——略
|——public(项目html文件)
| |——index.html
|—— .eslintrc.js (Eslint配置文件)
|—— babel.config.js (Babel配置文件)
|—— package.json(包的依赖管理配置文件)
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
// 所有文件的输出路径
// 开发模式没有输出
path: undefined,
// ......
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader","css-loader"],
},
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader'],
},
// .......
],
},
plugins: [
new ESLintPlugin({
// 检测哪些文件
context: path.resolve(__dirname, "../src")
}),
new HtmlWebpackPlugin({
// 新的html文件特点:1.结构和原来一致 2.自动引入打包输出的资源
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"); // nodejs核心模块,专门用来处理路径问题
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
// 所有文件的输出路径
// __dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, "../dist"), // 绝对路径
// 入口文件打包输出文件名
filename: 'static/js/main.js',
// 自动清空上次打包的内容
// 原理:在打包前,将path整个目录内容清空,再进行打包
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader","css-loader"],
},
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader'],
},
// .......
],
},
plugins: [
new ESLintPlugin({
// 检测哪些文件
context: path.resolve(__dirname, "../src")
}),
new HtmlWebpackPlugin({
// 新的html文件特点:1.结构和原来一致 2.自动引入打包输出的资源
template: path.resolve(__dirname, "../public/index.html"),
})
],
mode: "production",
}
npx webpack --config ./config/webpack.prod.js
在package.json里面的script配置
"scripts": {
"start": "npm run dev",
"dev": "webpack serve --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
},
这样运行开发模式的代码就是:npm start
这样运行生产模式的代码就是:npm run build
Css文件目前被打包到js文件中,当js文件加载时,会创建一个style标签来生成样式
这样对于网站来说,会出现闪屏现象,用户体验不好
我们应该是单独的Css文件,通过link标签加载性能更好
1. 下载包
npm i mini-css-extract-plugin -D
2. 配置
webpack.prod.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [
new MiniCssExtractPlugin({
// 指定文件输出目录
filename: "static/css/main.css"
}),
],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
1.下载包
npm i postcss-loader postcss postcss-preset-env -D
2. 配置
webpack.prod.js
在css-loader后面,less-loader前面插入配置代码
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
},
},
],
},
],
},
};
或者使用 PostCSS 本身的 配置文件:
postcss.config.js
module.exports = {
// 你可以指定下面提到的所有选项 https://postcss.org/api/#processoptions
// parser: 'sugarss',
plugins: [
// PostCSS 插件
['postcss-short', { prefix: 'x' }],
'postcss-preset-env',
],
};
Loader 将会自动搜索配置文件。
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
],
},
};
最后,通过你喜欢的方式运行 webpack
3. 控制兼容性
我们可以在 package.json 文件中添加 browerslist 来控制样式的兼容性做到什么程度。
{
// 其他省略
"browserslist": ["ie >= 8"]
}
想要知道更多的 browserslist 配置,查看 browserslist 文档
以上为了测试兼容性所以设置兼容浏览器 ie8 以上。
实际开发中我们一般不考虑旧版本浏览器,所以我们可以这样设置:
{
// 其他省略
" browserslist": ["last 2 version", ">1%", "not dead"]
}
4. 合并配置
由于代码复用率较高,我们可以封装处理样式的函数
const path = require("path"); // nodejs核心模块,专门用来处理路径问题
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 用来获取处理样式的loader
function getStyleLoader(pre) {
return [
MiniCssExtractPlugin.loader, // 将js中css通过创建style标签添加html文件中生效
"css-loader", // 将css资源编译成 commonjs 的模块到js中
'postcss-loader',
pre,
].filter(Boolean);
}
module.exports = {
// 入口
entry: './src/main.js', // 相对路径
// 输出
output: {
// 所有文件的输出路径
// __dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, "../dist"), // 绝对路径
// 入口文件打包输出文件名
filename: 'static/js/main.js',
// 自动清空上次打包的内容
// 原理:在打包前,将path整个目录内容清空,再进行打包
clean: true,
},
// 加载器
module: {
rules: [
// loader的配置
{
test: /\.css$/, // 只检测 .css文件
use: getStyleLoader(),
},
{
test: /\.less$/,
// loader:'xxx', // 只能使用1个loader
use: getStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoader('sass-loader'),
},
{
test: /\.styl$/,
use: getStyleLoader('stylus-loader'),
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
generator: {
// [hash:10] 代表hash值只取前10位
filename: 'static/images/[hash:10][ext][query]'
}
},
/* {
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
// 小于10kb 的图片转base64
// 优点:减少请求数量 缺点:体积会更大
maxSize: 4 * 1024 // 4kb
}
}
} */
{
test: /\.(ttf|woff2?|map3|map4|avi)$/,
type: 'asset/resource',
generator: {
// 输出名称
filename: 'static/media/[hash:10][ext][query]'
}
},
{
test: /\.m?js$/,
exclude: /node_modules/, // 排除node_modules中的js文件(这些文件不处理)
/* use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
} */
/* loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
} */
loader: 'babel-loader',
// options也可以写在外面
},
],
},
// 插件
plugins: [
// plugin的配置
new ESLintPlugin({
// 检测哪些文件
context: path.resolve(__dirname, "../src")
}),
new HtmlWebpackPlugin({
// 模板:以public/index.html 文件创建新的html文件
// 新的html文件特点:1.结构和原来一致 2.自动引入打包输出的资源
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
// 指定文件输出目录
filename: "static/css/main.css"
}),
],
// 模式
// mode: "development",
mode: "production",
}
1. 下载包
npm i css-minimizer-webpack-plugin -D
2. 配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.s?css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
};
默认生产模式已经开启了:html 压缩和 js压缩
不需要额外进行配置