前言
作为一个前端工程师,前端工程化这个词不绝于耳,而Webpack在前端工程化中扮演着至关重要的角色。 要明白webpack为什么重要,就要知道它为什么出现,解决了什么问题。
前端模块化从文件划分模块、命名空间划分模块、IIFE通过约定来实现模块化,到CommonJS、ES Module通过行业规范实现模块化,如今ES Module已经是最正统最主流的模块化规范,但是它依然还存在兼容问题,所以开发者还需要解决兼容问题。而且模块化开发,会划分出很多文件,每个文件就是一个模块,文件过大,浏览器请求、加载文件时间过长,影响页面渲染速度,文件过多浏览器请求频繁,也影响性能,所以需要对这些文件进行合并拆分。而项目复杂后,html、css、图片、字体文件等也需要模块化来管理。
于是webpack顺势而出,它是一个现代化的模块打包工具
,支持js、css等不同种类资源的模块化(项目中使用的每个文件都是一个模块),同时对这些资源做兼容性处理,最后对这些资源文件根据需要做拆分合并压缩后打包为静态资源(html、css、js)
。
目前,webpack已经到了 webpack 5.89.0
了,应该静下心来好好看一看官网,这篇文章整理一下webpack5的基础配置,并用webpack5从零搭建一个vue3+ts项目,下篇文章整理webpack5的优化配置、分析相关原理!
一、基础配置
首先当然是先初始化一个项目,前提已经装好node
bash
npm init 或者 npm init -y
然后安装webpack,如果需要在命令行中调用 webpack
,就需要安装webpack-cli
bash
npm i webpack webpack-cli -D
然后建一个src目录和src/index.js、src/main.js,
js
// index.js
import getName from "./main";
console.log(getName());
// main.js
export default function getName() {
return "webpack5";
}
从 v4.0.0 开始,webpack 可以不用再引入一个配置文件 来打包项目,webpack打包入口默认值是 ./src/index.js
,主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
所以暂时先在命令行直接执行webpack来打包
bash
webpack
如果环境变量没有修改就会报错 webpack: command not found
,因为局部安装webpack的时候,安装到了node的bin下,并不是系统环境变量,可以用以下几种方法解决:
- 1、在package.json的scripts中添加脚本,
"build":"webpack"
,命令行执行npm run build
- 2、全局安装webpack再执行webpack (不推荐)
- 3、命令行执行
node_modules/.bin/webpack
- 4、命令行执行
npx webpack
npx
会在当前目录下的./node_modules/.bin
里去查找是否有可执行的命令,没有找到的话再从全局里查找是否有安装对应的模块,全局也没有的话就会自动下载对应的模块,用完就删,不会占用本地资源。
打包结果,代码做了压缩:
js
// /dist/main.js
(()=>{"use strict";console.log("webpack5")})();
1、引入配置文件
项目根目录下创建一个 webpack.config.js
文件,webpack 会自动使用它。
js
// webpack.config.js
const path = require("path");
module.exports = {
// 入口
entry: "./src/main.js",
output: {
// path 要用绝对路径
path: path.resolve(__dirname, "dist"),
// 文件名
filename: "my-first-webpack.bundle.js",
// 在生成文件之前清空 output 目录
clean: true
},
};
如果你不喜欢webpack.config.js
这个名字,假如你想改成like.js
,在命令行执行npx webpack --config like.js
,或者在package.json的scripts中修改 "build":"webpack --config like.js"
,再执行npm run build
。
上面的打包结果中发现webpack5打包默认输出就是一个箭头函数的IIFE,为了兼容低版本,要去掉这个箭头函数, 需要在出口处配置
js
module.exports = {
output:{
environment: {
arrowFunction: false,
}
}
}
2、加载 css
要知道,webpack只能理解 JavaScript
和 JSON
文件,对于其他文件类型,就需要使用 loader,loader 用于对模块的源代码进行转换,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
准备一个css文件
css
/* /src/style/index.css */
body h1{
background-color: #e0f314;
color: red;
}
webpack 的其中一个强大的特性就是能通过
import
导入任何类型的模块(例如.css
文件)
js
// /src/index.js
import "./style/index.css";
const h1 = document.createElement("h1");
h1.innerHTML = "hello, Webpack5";
document.body.appendChild(h1);
在根目录下建一个index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script src="./dist/main.js"></script>
</body>
</html>
此时打包不出意外将报错,
bash
ERROR in ./src/style/index.css 2:18
Module parse failed: Unexpected token (2:18)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| body: {
> background-color: #e0f314;
| color: red;
| }
@ ./src/index.js 4:0-27
需要一个合适的loader才能解析css文件,这个合适的loader就是 css-loader
bash
npm i css-loader -D
js
// webpack.config.js
module.exports = {
module: {
// rules是数组,因为可能会处理很多其他资源模块,需要其他loader
// test 匹配哪些文件需要转换
// use 需要使用什么loader,如果要处理匹配到的这个资源需要多个loader,就写成数组形式
rules: [{ test: /\.css$/, use: "css-loader" }],
// 只用一个loader,还可以写成
rules: [{ test: /\.css$/, loader: "css-loader" }],
},
};
再次打包不会报错,但是页面样式没生效,因为css-loader只做了一件事,在webpack打包前解析css文件,并没有将css加入html中,所以还需要一个 style-loader
bash
npm i style-loader -D
loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)
js
// webpack.config.js
module.exports = {
module: {
// 从右到左,先用css-loader解析,再用style-loader插入style标签到html中
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"]],
// use里面直接写字符串是简写,如果要颗粒度更细的配置,就写成对象,如
rules: [
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
// css-loader的配置项
},
},
],
},
],
},
};
style-loader是通过创建style标签将样式插入到文档中
3、加载 sa(c)ss
平时项目中一般都是用sass(scss)或者less,它们肯定是没法直接跑在浏览器上的,所以也需要 sass-loader
bash
npm install sass-loader sass -D
sass-loader依赖sass包,都需要安装
scss
/* /src/style/index.scss */
$fontSize: 20px;
$color: rgb(86, 221, 145) 6;
@mixin baseStyle {
background-color: aqua;
border: 1px solid rgb(238, 245, 255);
}
h2 {
@include baseStyle();
font-size: $fontSize;
color: $color;
}
js
// /src/index.js
import "./style/index.scss";
const h2 = document.createElement("h2");
h2.innerHTML = "hello, scss";
document.body.appendChild(h2);
js
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/,
// 从右到左,现将sass编译成css,再处理css,再加入style
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
};
如果要用到less,也是需要 less-loader,一样的道理
4、处理 css 新特性
部分css3的新属性在一些浏览器上还处于试验阶段,所以为了有效的显示css3的样式,对应不同的浏览器内核需要不同的前缀声明。
这就需要用到 PostCSS, PostCSS是一个用 JavaScript 工具和插件转换 CSS 代码的工具,利用从 Can I Use 网站获取的数据为 CSS 规则添加特定厂商的前缀。 Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。
postcss-loader 使用 PostCSS
处理 CSS 的 loader,autoprefixer
是 postcss 的一个插件
bash
npm i -D postcss-loader postcss autoprefixer
css
/* /src/style/index.css */
::placeholder {
color: red;
font-size: 1.5em;
}
::selection {
background-color: cyan;
}
body {
user-select: none;
}
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
// 先加前缀,再解析css文件,再插入style
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
//postcss插件
plugins: ["autoprefixer"],
},
},
},
],
}
],
},
};
也可以将postcss的配置提取到一个单独的文件 postcss.config.js
中,在根目录下建一个postcss.config.js,webpack调用postcss-loader的时候会读取到postcss.config.js里面的配置
js
// postcss.config.js
module.exports = {
// postcss插件
plugins: ["autoprefixer"],
};
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
}
],
},
};
也可以使用postcss-preset-env,它包含 autoprefixer
,不仅可以添加前缀,还能处理其他的css新特性
bash
npm i -D postcss-preset-env
css
/* /src/style/index.css */
:root {
--mainColor: #12345678;
}
body {
color: var(--mainColor);
font-family: system-ui;
/* 这个属性原本属于微软扩展的一个非标准、无前缀的属性,叫做 word-wrap,*/
/* 后来在大多数浏览器中以相同的名称实现。*/
/* 目前它已被更名为 overflow-wrap,word-wrap 相当于其别称。 */
overflow-wrap: break-word;
user-select: none;
}
a {
color: rgb(0 0 100% / 90%);
&:hover {
color: rebeccapurple;
}
}
js
// postcss.config.js
module.exports = {
// postcss插件
plugins: ["postcss-preset-env"]
};
5、处理图片资源
webpack5 可以使用内置的 资源模块 轻松处理图像、字体资源,不需要像webpack5之前一样使用loader,资源模块类型有4 种:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。
js
// src/index.js
import "./style/index.css";
import imgUrl from "./assets/10kb.jpeg"; //大小10kb
const imgEL = new Image();
imgEL.src = imgUrl;
document.body.appendChild(imgEL);
css
/* /src/style/index.css */
h1 {
background: url("../assets/242kb.jpg"); /*大小242kb*/
}
资源类型设置为 type: "asset/resource",打包成单独文件,并把路径注入到包文件dist/main.js中
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(png|svg|jpe?g|gif)$/,
// type 属性设置资源模块类型
// "asset/resource"无论图像大小,都会打包成单独的文件
type: "asset/resource",
generator: {
// 生成的图片在assets目录下
// 名字为原来的名字name截取10位hash值,保留原来的扩展名
filename: "assets/[name]_[contenthash:10][ext]",
},
},
],
},
};
资源类型设置为 type: "asset/inline",把所有图片文件都作为 data URI 注入到dist/main.js中。
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(png|svg|jpe?g|gif)$/,
// "asset/inline" 无论图像大小,图像文件默认编码为base64注入js文件中
type: "asset/inline",
},
],
},
};
资源类型设置为 type: "asset" ,默认小于 8kb 的文件,将会视为 inline
模块类型默认编码成base64注入包文件,否则会被视为 resource
模块类型单独打成文件。可以设置 Rule.parser.dataUrlCondition.maxSize
选项来修改此条件:
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(png|svg|jpe?g|gif)$/,
type: "asset",
parser: {
// 如果一个模块源码大小小于 `maxSize`,那么模块会被作为一个 Base64 编码的字符串注入到包中,
// 否则模块文件会被生成到输出的目标目录中。
// 大图片打包成base64注入js文件,使js文件变大,影响下载速度,所以最好单独打包下载
// 小图片单独打包下载,浏览器请求频繁,也影响性能,可以直接打成base64
dataUrlCondition: {
maxSize: 20 * 1024,
},
},
generator: {
filename: "assets/[name]_[contenthash:10][ext]",
},
},
],
},
};
自定义输出文件名方式,
generator.filename
与output.assetModuleFilename
相同,但仅适用于type:asset
和type:asset/resource
模块类型。
6、babel 转译 js
babel-loader
使用 Babel (一个强大的js编译器)会加载 ES2015+ 代码并将其转换为 ES5。
bash
npm install -D babel-loader @babel/core @babel/preset-env
npm install core-js@3
core-js
:它是JavaScript标准库的 polyfill(垫片/补丁)包集合。babel-loader
:在Webpack里使用babel转译js文件,@babel/core
:是babel的大脑,负责调度babel的各个功能模块,@babel/preset-env
:babel预设,一组babel插件的集合,就不需要一个个去设置babel的插件,但是preset-env只能进行语法转换,不能弥补浏览器缺失的一些新功能,比如一些内置对象和方法,所以需要corejs来弥补老版浏览器缺失的新功能, 参数选项:- targets :不配置,会查询package.json中的browserslist,或者项目根目录下的.browserslistrc,都不设,
@babel/preset-env
默认会使用 browserslist config sources,实际上是 "targets": "> 0.25%, not dead"; - useBuiltIns :
'usage'
,只对使用到的功能打补丁,core-js自动被引入按需打包;'entry'
,在index.ts入口import 'core-js'
,把所有的polyfill打包; - corejs :此选项仅在与
useBuiltIns: usage
或useBuiltIns: entry
一起使用时有效,一般使用版本 3,会 polyfill 实例方法,而 corejs2 不会
- targets :不配置,会查询package.json中的browserslist,或者项目根目录下的.browserslistrc,都不设,
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
// 排除node_modules,不用转译
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
// 设置兼容目标浏览器
targets: {
chrome: "58",
ie: "11",
},
// 自动引入core-js根据目标浏览器按需使用打补丁
useBuiltIns: "usage",
// 一般是指定 3,这个会 polyfill 实例方法,而 corejs2 不会
corejs: 3,
},
]
],
},
},
},
],
},
};
@babel/preset-env 转换用到的一些辅助代码是直接注入到模块里的,没有做抽离,多个模块可能会重复注入,并且用到的 polyfill 代码 core-js以及它提供的 Promise
、Set
和 Map
等内置函数也是全局导入 ,会污染全局作用域,如果代码是一个打算发布以供其他人使用的库,那么它就会成为一个问题。
解决这个问题就要使用 @babel/plugin-transform-runtime 插件,可以重用 Babel 的注入帮助代码以节省代码大小,依赖模块@babel/runtime
以避免编译输出中的重复。同时也通过设置选项corejs
进行polyfill,依赖@babel/runtime-corejs3
,corejs: 2
仅支持全局变量(例如 Promise
)和静态属性(例如 Array.from
),而 corejs: 3
还支持实例属性(例如 [].includes
)
bash
pnpm add -D @babel/plugin-transform-runtime
pnpm add @babel/runtime
pnpm add @babel/runtime-corejs3
启用
@babel/plugin-transform-runtime
插件后,@babel/preset-env
中的useBuiltIns
选项不得设置。 否则,此插件可能无法完全沙盒化环境。
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
// 排除node_modules,不用转译
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
// 设置兼容目标浏览器
targets: {
chrome: "58",
ie: "11",
},
// 自动引入core-js根据目标浏览器按需使用打补丁
// useBuiltIns: "usage",
// 一般是指定 3,这个会 polyfill 实例方法,而 corejs2 不会
// corejs: 3,
},
]
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
// 从@babel/runtime-corejs3 引入polyfill
corejs: 3,
},
],
],
},
},
},
],
},
};
注意:
babel插件在预设前运行,@babel/plugin-transform-runtime 在 @babel/preset-env 之前调用的,将不兼容的语法和api转换了,但是他没有target配置,不能按需polyfill,所以最后打包文件会变大。
和postcss一样也可以将babel的配置提取到一个单独的文件 babel.config.js
中,在根目录下建一个babel.config.js,webpack调用babel-loader的时候,babel会读取到babel.config.js里面的配置
js
// babel.config.js
module.exports = {
presets: [
// 预设从后往前先处理ts,再处理js
[
"@babel/preset-env",
{
// 设置兼容目标浏览器
targets: {
chrome: "58",
ie: "11",
},
// 自动引入core-js根据目标浏览器按需使用打补丁
// useBuiltIns: "usage",
// 一般是指定 3,这个会 polyfill 实例方法,而 corejs2 不会
// corejs: 3,
},
],
[
"@babel/preset-typescript",
{
//表示每个文件都应该被解析为 TS、TSX
allExtensions: true,
},
],
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
// 从@babel/runtime-corejs3 引入polyfill
corejs: 3,
},
],
],
};
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: "babel-loader",
},
],
},
};
babel的配置文件在根目录下还可以写成其他形式:
- babel.config.json
- .babelrc
- .babelrc.js
- .babelrc.json
- 直接在package.json中写配置
7、转译 ts 文件
方式一:
如果已经使用babel-loader
转译代码,可以使用 @babel/preset-typescript
以让 Babel 处理 JavaScript 和 TypeScript 文件,而不需要额外使用 loader。与 ts-loader
相反,底层的 @babel/plugin-transform-typescript
插件不执行任何类型检查
bash
npm i -D @babel/preset-typescript
js
// babel.config.js
module.exports = {
// 预设是从后往前执行的,ts先编译成js,再编译js
presets: ["@babel/preset-env", "@babel/preset-typescript"],
};
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
};
方式二:
使用ts-loader,在根目录创建 tsconfig.json
,
bash
npm install -D typescript ts-loader
json
// tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"allowJs": true,
"moduleResolution": "node"
}
}
ts-loader
使用 TypeScript 编译器tsc
,并依赖于tsconfig.json
配置,不要将module
设置为"CommonJS"
,否则 webpack 将无法对代码进行 tree shaking
js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: ["ts-loader"],
},
],
},
};
区别:
- ts-loader编译是会进行语法检查的,只能在入口文件全量引入polyfill
- babel-loader只会负责编译,并不会进行语法检查,可以按需引入polyfill
8、省略扩展名extensions
配置 resolve.extensions,默认数组['.js', '.json', '.wasm']能够使用户在引入模块时不带扩展,把用的最多的文件类型放前面,可以加快webpack解析速度。
webpack尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件,并跳过其余的后缀。使用 resolve.extensions
会 覆盖默认数组 ,这就意味着 webpack 将不再尝试使用默认扩展来解析模块。然而你可以使用 '...'
访问默认拓展名。
js
// webpack.config.js
module.exports = {
resolve: {
extensions: [".vue", ".ts", ".scss", "..."],
},
};
9、配置路径别名
resolve.alias配置import
或者require
引入路径的别名,假如在项目中,引入一个模块:
js
import moduleName from "../../../../util/axios-util.js"
使用相对路径的方式很繁琐,那就可以如下配置:
js
const path = require('path')
const resolve = dir => path.resolve(__dirname, dir)
module.exports = {
resolve: {
alias: {
"@": resolve("./src"),
"@/util": resolve("./src/moduleA/moduleB/moduleC/util")
}
},
}
然后就可以这样引入了:
js
import moduleName from "@/util/axios-util.js"
10、模块解析
webpack的 模块解析 就像node中require查找路径一样。一个模块可以作为另一个模块的依赖模块,然后被后者引用,所依赖的模块可以是来自应用程序的代码或第三方库,模块解析就是要从每个 require
/import
语句中,找到需要引入到 bundle 中的模块代码。
webpack 能解析三种文件路径:
1、绝对路径: 不需要解析了,一步到位。
2、相对路径: 使用 import
或 require
的资源文件所处的目录,被认为是上下文目录。在 import/require
中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径。
3、模块路径:
js
import 'module';
import 'module/lib/file';
-
(1)、按照在
resolve.modules
(数组) 中指定的所有目录中检索模块,数组指定的目录优先级从左到右,权重依次降低 ,默认是从node_modules
中搜索模块,如果想要添加一个优先于node_modules搜索:jsmodule.exports = { //... resolve: { modules: [path.resolve(__dirname, 'src'), 'node_modules'], }, }
如果搜索到的模块里面有package.json文件,在 resolve.exportsFields(数组)配置选项中指定的字段会被依次去package.json里面查找对应字段,默认是
exports
字段。如果查找exports字段,那就根据 package.json中exports的导出规则 查找模块。根据以上这一番操作,webpack解析出来的路径要么是一个确定文件,要么是文件夹
-
(2)、路径是文件
- 有扩展名,直接找到打包
- 没有扩展名,那就按
resolve.extensions
来分析
-
(3)、路径是文件夹
-
① 如果文件夹中包含
package.json
文件,则会根据resolve.mainFields
配置中的字段顺序查找,如果目标环境 target 是webworker
,web
或者没有指定,那resolve.mainFields
默认值是['browser', 'module', 'main']
,如果target
是其他,默认值是['module', 'main'],
然后在package.json
中根据resolve.mainFields
字段顺序确定文件路径。 -
② 如果不存在
package.json
文件或根据resolve.mainFields
没有找到有效路径,则会根据resolve.mainFiles
配置选项中指定的文件名顺序查找,默认为['index']
,看是否能匹配到一个存在的文件名。 -
③ 然后就根据
resolve.extensions
来分析
-
注意:
可以通过配置别名
resolve.alias
来替换初始模块路径,resolve.alias
优先级高于以上三种模块解析方式。
二、 搭建一个 vue3 + ts 工程
完整基本配置github地址:github.com/Xluo766/ln-...
技术栈: vue3 + typescript + scss + webpack5 + pnpm
bash
mkdir ln-webpack5-vue3-ts
cd ln-webpack5-vue3-ts
pnpm init
pnpm add webpack webpack-cli -D
1、使用ts编写webpack配置文件
使用Typescript来编写 webpack.config.ts的配置,需要先安装必要的依赖,比如 Typescript 以及其相应的类型声明
js
pnpm add -D typescript ts-node @types/node @types/webpack
同时创建 tsconfig.json
文件,在 compilerOptions
配置中,设置:
- "module": "commonjs",因为
ts-node
不支持commonjs
以外的其他模块规范 - "target": "es5",
- "esModuleInterop": true,用于兼容处理导入模块时的不同规范
- "allowSyntheticDefaultImports": true,在静态类型检查时,把
import
没有exports.default
的报错忽略掉
比如:由于 lodash
没有默认导出,因此现在需要修改 lodash
在 ts
文件中的引入
ts
// import _ from 'lodash';
import * as _ from 'lodash';
function component() {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
// 如果想在 TypeScript 中继续使用像 import _ from 'lodash'; 的语法,
// 让它被作为一种默认的导入方式,需要在 tsconfig.json 中设置
// "allowSyntheticDefaultImports" : true 和 "esModuleInterop" : true
json
// tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"allowJs": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
ts
// webpack.config.ts
import * as webpack from "webpack";
// 配置config的时候有类型提示
const config: webpack.Configuration = {
entry: "./src/index.ts",
};
export default config;
2、转译 .ts 文件
bash
pnpm add ts-loader -D
ts
// webpack.config.ts
import * as webpack from "webpack";
const config: webpack.Configuration = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.ts$/,
//排除 node_modules 目录
exclude: /node_modules/,
use: ["ts-loader"],
},
],
},
};
export default config;
ts-loader
使用 TypeScript 编译器 tsc
,并依赖于 tsconfig.json
配置,如果将 module
设置为 "CommonJS"
,webpack 将无法对代码进行 tree shaking,但是 ts-node
又不支持 commonjs
以外的其他模块规范,webpack5官网给出了解决办法
如果要为 tsc
保持 "module": "ESNext"
配置,那就在tsconfig.json中添加 ts-node 设置
json
// tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "ESNext",
"target": "es5",
"allowJs": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
示例:
ts
// src/main.ts
export function log() {
console.log("tree-shaking");
}
export function sum(a: number, b = 6) {
return a + b;
}
// src/index.ts
import { sum } from "./main.ts"; //下面会通过配置省略扩展名.ts
const count = sum(5, 5);
console.log(count);
//打包后,删除了log函数
// dist/main.js
(()=>{"use strict";var o,s=(void 0===(o=5)&&(o=6),5+o);console.log(s)})();
3、转译 .vue 文件
首先安装 vue-loader,
bash
pnpm add vue-loader -D
然后给ts-loader加上配置参数 appendTsSuffixTo: [/\.vue$/]
,从vue文件分离出来的ts内容会加上ts后缀给ts-loader处理;同时加入一个vue-loader的插件VueLoaderPlugin
,它的职责是将定义过的其它规则复制并应用到 .vue
文件里相应语言的块。例如,如果你有一条匹配 /.ts$/
的规则,那么它会应用到 .vue
文件里的 <script lang='ts'>
块
在webpack中,loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。想要使用一个插件,你只需要 require()
或者import
它,然后使用 new
操作符来创建一个插件实例,把它添加到 plugins
数组中
ts
// webpack.config.ts
import * as webpack from "webpack";
import { VueLoaderPlugin } from "vue-loader";
const config: webpack.Configuration = {
//...
plugins: [new VueLoaderPlugin()],
module: {
rules: [
{
test: /\.ts$/,
//排除 node_modules 目录
exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
// 从vue文件分离出来的ts内容会加上ts后缀给ts-loader处理
appendTsSuffixTo: [/\.vue$/],
},
},
},
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
};
export default config;
由于.vue不是一个常规文件,为了引入vue文件不报错,需要告诉ts编译器.vue是个什么类型,在src下建一个shims-vue.d.ts
文件
ts
// src/shims-vue.d.ts
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
测试一下打包:
ts
// src/index.ts
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");
html
// src/App.vue
<script setup lang="ts">
import { ref } from "vue";
const count = ref(1);
</script>
<template>
<h1 @click="count++">webpack5 + vue3 +ts</h1>
<h3>数字:{{ count }}</h3>
</template>
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script defer src="./dist/main.js"></script>
</body>
</html>
4、用babel转译.vue和.ts文件
bash
pnpm add -D babel-loader @babel/core @babel/preset-env @babel/preset-typescript
npm install core-js@3
ts
// webpack.config.ts
import * as webpack from "webpack";
import { VueLoaderPlugin } from "vue-loader";
const config: webpack.Configuration = {
// ...
plugins: [new VueLoaderPlugin()],
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
// 预设从后往前先处理ts,再处理js
presets: [
[
"@babel/preset-env",
{
targets: { chrome: "58", ie: "11" },
useBuiltIns: "usage",
corejs: 3
},
],
[
"@babel/preset-typescript",
{
//表示每个文件都应该被解析为 TS、TSX
allExtensions: true,
},
],
],
},
},
},
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
};
export default config;
5、自动生成html入口
目前测试打包后的代码是在根目录下建了一个index.html,把打包后的默认的/dist/main.js手动引入,用live server打开index.html看页面效果,如果更改入口起点的名称,或者添加一个新的入口起点,那么会在构建时重新命名生成的 bundle,而 index.html
仍然在引用旧的名称,每次都需要手动改。
HtmlWebpackPlugin插件可以解决这个问题,该插件将自动生成一个 HTML5 文件, 在 body 中使用 script
标签引入所有 webpack 生成的 bundle。
bash
pnpm add -D html-webpack-plugin
在根目录下建一个 public/index.html作为模板,模板中htmlWebpackPlugin.options.title
会在webpack编译时被替换
html
<!-- /public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="./favicon.ico" />
<!-- 文档标题会被自动替换 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
<%= EJS %>是EJS语法,利用普通的 JavaScript 代码生成 HTML ,html中的js代码编译时会被替换。
将图标favicon.ico放到public文件夹下,开发环境使用webpack-dev-server,devServer.static 配置默认将public作为静态资源目录,打包后默认放在根目录下,跟index.html同级,启动服务后,可以通过http://{host}/favicon.ico
进行访问。
如果发现图标不显示,而且浏览器根本没请求这个图标,这是缓存问题,stackoverflow有答案
ts
// webpack.config.ts
import * as webpack from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
const config: webpack.Configuration = {
plugins: [
//...
new HtmlWebpackPlugin({
// 会替换掉 html 中 htmlWebpackPlugin.options.title
title: "webpack5+Vue3+ts",
// 以public里面的indexx.html为模板,生成html
template: "./public/index.html",
}),
]
// ...
};
export default config;
6、处理css、scss,单独提取
包括vue文件style里面写的css、scss和单独的css、scss文件,同时将处理后的css单独打包,而不再像上面基础配置那样用style标签插入html。
需要用到一个插件MiniCssExtractPlugin,会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,当然也是和css-loader,sass-loader这些一起使用。同时也需要HtmlWebpackPlugin
,如果在 webpack 的输出中有任何 CSS 资源(例如,使用MiniCssExtractPlugin 提取的 CSS),HtmlWebpackPlugin
会把这些css资源在 HTML 文件 <head>
元素中的 <link>
标签内引入
bash
pnpm add -D mini-css-extract-plugin css-loader sass-loader sass postcss-loader postcss postcss-preset-env
ts
// webpack.config.ts
import * as webpack from "webpack";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
const config: webpack.Configuration = {
//...
plugins: [
//...
new MiniCssExtractPlugin({
// 只影响最初加载的输出css文件,name:入口名称
filename: "style/[name]_[contenthash:8].css",
// 按需加载的 chunk 文件命名,id:内部chunk id
chunkFilename: "style/[id].css",
}),
],
module: {
rules: [
//...
{
test: /\.s?css$/,
// 从下往上,解析scss文件,处理兼容性,处理css文件,提出文件
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: ["postcss-preset-env"],
},
},
},
"sass-loader",
],
},
],
},
};
export default config;
示例:
ts
// 入口index.ts
import "./style/index.scss";
less
// src/style/index.scss
$baseColor1: rgb(128, 255, 0);
$baseColor2: #fe3333;
@mixin baseStyle {
background-color: aqua;
border: 1px solid rgb(238, 245, 255);
}
body {
background-color: #d8c7c7;
}
// /src/App.vue
<style lang="scss" scoped>
@import "./style/index.scss";
h3 {
color: $baseColor1;
@include baseStyle();
}
h1 {
background-color: $baseColor2;
user-select: none;
}
</style>
打包后:
7、打包图片资源
ts
// webpack.config.ts
import * as webpack from "webpack";
const config: webpack.Configuration = {
//...
module: {
rules: [
//...
{
test: /\.(png|svg|jpe?g|gif)$/,
type: "asset",
parser: {
// 图片小于20kb,会被解析为一个 Base64 编码的字符串注入到包中,
dataUrlCondition: {
maxSize: 20 * 1024,
},
},
generator: {
// 此选项被称为文件名,但还是可以使用像 'js/[name]/bundle.js' 这样的文件夹结构
filename: "image/[name]_[hash:10][ext]",
},
},
],
},
};
export default config;
此时import引入图片,会提示 找不到模块"./assets/242kb.jpg"或其相应的类型声明。ts(2307)
,在项目根目录下建一个类型声明文件d.ts
,类型声明文件里面只有类型代码,没有具体的代码实现
ts
// env.d.ts
declare module "*.jpg" {
const src: string;
export default src;
}
declare module "*.jpeg" {
const src: string;
export default src;
}
declare module "*.png" {
const src: string;
export default src;
}
declare module "*.gif" {
const src: string;
export default src;
}
declare module "*.svg" {
const src: string;
export default src;
}
8、设置路径别名、路径提示
resolve.alias,创建 import
或 require
的别名,来确保模块引入变得更简单。
首先是配置webpack.config.ts让webpack在构建时能正确解析路径:
ts
// webpack.config.ts
import * as webpack from "webpack";
import path from "path";
const resolve = (dir: string) => path.resolve(__dirname, dir);
const config: webpack.Configuration = {
resolve: {
alias: {
"@": resolve("./src"),
"@/assets": resolve("./src/assets"),
"@/style": resolve("./src/style"),
},
}
}
export default config
然后是在tsconfig.json中配置让我们在项目中书写时有路径提示:
json
// tsconfig.json
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
],
"@/assets/*": [
"src/assets/*"
],
"@/style/*": [
"src/style/*"
]
}
},
9、搭建开发环境
在此之前,看页面效果都是把代码打包后,手动将output目录下的入口文件引入html然后用live server开启一个本地服务,每次修改代码就要重新打包,然后刷新浏览器,很麻烦。所以需要用webpack搭建 开发环境:开启一个本地服务器,每次修改代码,webpack自动重新编译打包,同时页面自动刷新。
webpack-dev-server 插件可以在本地搭建一个实时重新加载的web服务器,它不会写入任何输出文件,而是将打包后的文件保留在内存中,然后将它们作为可访问资源部署在 server 中,就像是挂载在服务器根路径上的真实文件一样。
webpack-dev-server
会将在 output.path
中定义的目录中的 bundle 文件作为可访问资源部署在服务中,output.path
没有配置,默认就是打包后的dist
目录
bash
pnpm add -D webpack-dev-server
package.json:
bash
"scripts": {
"dev": "webpack serve --open"
},
既然是开发环境,那就提供 模式(mode) 配置选项为development
,告知 webpack 使用开发模式的内置优化,process.env.NODE_ENV
的值被自动设置为 'development'
。
遇到的问题:
1、 vue警告
vue3的github仓库中有解决办法,stackoverflow有讨论,需要通过DefinePlugin
注入两个全局变量
js
// webpack.config.ts
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),
DefinePlugin 是webpack内置插件,定义全局变量,在 编译时 将代码中的变量替换为其他值或表达式。这在需要根据开发模式与生产模式进行不同的操作时,非常有用。
如果使用ts开发,在代码中使用DefinePlugin里面定义的变量,在d.ts文件里面定义一下全局变量,不然ts报未定义错误
注意看文档,如果值是字符串,会被当成代码执行;如果不是字符串,则将被转换成字符串(包括函数方法),再执行
比如:
- 要传递字符串'webpack5',就要写成
"'webpack5'"
,或者JSON.stringify('webpack5')
; - 要传递布尔值,写成
true
或者JSON.stringify(true)
、'true'
都一样,都会转成字符串运行,true运行还是true - 如果传递数字5,就写
5
,或者JSON.stringify(5)
,'5'
但是'1+1'运行后就是2了
js
new webpack.DefinePlugin({
WEBPACK_STRING1: "webapck5", //使用会报错,会运行webpack5,但是没有定义
WEBPACK_STRING2: "'webapck5'",
WEBPACK_BOOLEAN1: "true",
WEBPACK_BOOLEAN2: true,
WEBPACK_NUMBER1: 5,
WEBPACK_NUMBER2: "5",
WEBPACK_NUMBER3: "1+1",
}),
// index.ts
console.log(
WEBPACK_STRING2,
WEBPACK_BOOLEAN1,
WEBPACK_BOOLEAN2,
WEBPACK_NUMBER1,
WEBPACK_NUMBER2,
WEBPACK_NUMBER3
);
// webapck5 true true 5 5 2
2、 使用 MiniCssExtractPlugin 插件在HRM时报错,github有好几条相关 issues
因为在上面第6步处理css/scss的时候,是使用MiniCssExtractPlugin单独提取的文件,并且在配置filename
的时候使用了hash
,在热更新的时候,重新的生成css文件被浏览器重新请求,issue上面回答说热更新不支持filename配置hash、fullhash、contenthash、chunkhash、modulehash
,并且,官网上说hash、fullhash、modulehash都已经弃用了。
解决办法两个:
- 1、issues的回答是开发环境就不要配置filename不要使用hash,因为关于css的打包和热更新相关代码比较复杂,会有破坏性更改,他们将会在下一个主要版本修复;
- 2、开发阶段可以使用
style-loder
,不用MiniCssExtractPlugin打包成单独文件,也就不会有这个问题。
3、 webpack-dev-server的类型需要单独引入,github有 issues,否则报错 Object literal may only specify known properties, and 'devServer' does not exist in type 'Configuration'.
,webpack核心成员说了,将来可能把webpack-dev-server放到插件里面,所以webpack的配置类型里面没有放入devServer的类型。
ts
// webpack.config.ts
import type { Configuration as WebpackConfiguration } from "webpack";
import type { Configuration as DevServerConfiguration } from "webpack-dev-server";
type Configuration = WebpackConfiguration & { devServer: DevServerConfiguration };
const config: Configuration = {
//...
}
export default coonfig
- port:端口,默认8080,
- hot:热更新,默认已开启,从
webpack-dev-server
v4.0.0 开始,模块热替换 就已经是默认开启的 - host:主机,默认值'127.0.0.1',如果想让本地服务器可以被外部访问,可以设置
host: '0.0.0.0'
,查看localhost、'127.0.0.1'、'0.0.0.0'的区别, - proxy:配置代理,可以解决浏览器跨域问题
- historyApiFallback:默认false,解决使用路由使用html5的history API刷新页面404问题
- open:设置true服务启动后就打开浏览器,或者通过命令
webpack serve --open
- static:配置从目录提供静态文件的选项(默认是
'public'
文件夹),
选择eval-source-map
,它会生成用于开发环境的最佳品质的 source map。每个模块使用 eval()
执行,并且 source map 转换为 DataUrl 后添加到 eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。
ts
// webpack.config.ts
import * as webpack from "webpack";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import type { Configuration as WebpackConfiguration } from "webpack";
import type { Configuration as DevServerConfiguration } from "webpack-dev-server";
type Configuration = WebpackConfiguration & { devServer?: DevServerConfiguration };
const config: Configuration = {
// 开发模式
mode: "development",
devtool: "eval-source-map",
plugins: [
new MiniCssExtractPlugin({
// 最初加载的输出css文件名,使用id
filename: "style/[name]_[id].css",
}),
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),
],
devServer: {
// port: 999, // 服务端口号,默认8080
// hot: true, // 默认已开启
// host: "0.0.0.0", // 允许外部访问
historyApiFallback: true, // 解决history路由404问题
},
};
export default config;
10、配置文件区分环境
development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差异。
开发环境目标: 强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(模块热替换) 能力的 localhost server。
生产环境目标: 关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间
使用 webpack-merge
做合并工作,有基本合并使用和更灵活的合并配置
bash
pnpm add -D webpack-merge
(none)
(省略devtool
选项) - 不生成 source map。这是一个不错的选择。source-map
- 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。
生产环境复制静态资源:CopyWebpackPlugin
bash
pnpm add -D copy-webpack-plugin
放在public文件中的favicon.icon,需要被放到最后的打包产物中,根目录下public文件夹在开发环境没有设置devServer.static
的情况下,默认作为静态资源目录,但是在生产环境public不会自动打进包里面,所以需要用插件CopyWebpackPlugin
;
ts
plugins: [
new CopyPlugin({
patterns: [
{
// form:复制的文件或者目录
from: "./public",
// globOptions.ignore:不复制html,已经使用了插件HtmlWebpackPlugin,否则报重复错误
globOptions: { ignore: ["**/index.html"] },
// 默认就是配置的output输出目录,output.path没有配置,默认就是dist
// to: "",
},
],
}),
],
配置文件使用 导出函数的方式,会接收两个参数,第一个参数是环境(environment),第二个参数 是传递给 webpack 的配置项,修改一下脚本:
json
// package.json
"scripts": {
"build": "webpack --env production",
"dev": "webpack serve --env development --open"
},
第一个参数env
中将包含{production: true}
或者{development: true}
ts
// webpack.config.ts
import path from "path";
import { DefinePlugin } from "webpack";
import { merge } from "webpack-merge";
import { VueLoaderPlugin } from "vue-loader";
import CopyPlugin from "copy-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import type { Configuration as WebpackConfiguration } from "webpack";
import type { Configuration as DevServerConfiguration } from "webpack-dev-server";
type Configuration = WebpackConfiguration & {
devServe?: DevServerConfiguration;
};
type WebpackConfigEnv = {
[key: string]: boolean | number | string;
production: boolean;
development: boolean;
};
const resolve = (dir: string) => path.resolve(__dirname, dir);
// 基本配置
const baseConfig = (env: WebpackConfigEnv): Configuration => {
return {
entry: "./src/index.ts",
output: {
clean: true,
environment: {
//打包输出普通函数IIFE
arrowFunction: false,
},
},
resolve: {
//可以加快webpack解析速度
extensions: [".vue", ".ts", ".scss", "..."],
alias: {
"@": resolve("./src"),
"@/assets": resolve("./src/assets"),
"@/style": resolve("./src/style"),
},
},
plugins: [
// 解析.vue文件必需插件
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: "webpack5+Vue3+ts",
template: "./public/index.html",
}),
new DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),
],
module: {
rules: [
// ts-loader编译会进行语法检查,只能在入口文件全量引入polyfill
{
test: /\.ts$/,
//排除 node_modules 目录
exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
// 从vue文件分离出来的ts内容会加上ts后缀给ts-loader处理
appendTsSuffixTo: [/\.vue$/],
},
},
},
// babel-loader编译不会进行语法检查,可以按需引入polyfill
// {
// test: /\.ts$/,
// exclude: /node_modules/,
// use: "babel-loader",
// },
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.s?css$/,
use: [
// 生产环境单独打包,开发环境用style-loader
env.production ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader",
"sass-loader",
],
},
{
test: /\.(png|svg|jpe?g|gif)$/,
type: "asset",
parser: {
// 图片小于20kb,会被解析为一个 Base64 编码的字符串注入到包中,
dataUrlCondition: {
maxSize: 20 * 1024,
},
},
generator: {
// 此选项被称为文件名,但还是可以使用像 'js/[name]/bundle.js' 这样的文件夹结构
filename: "image/[name]_[contenthash:10][ext]",
},
},
],
},
};
};
// 生产环境
const prodConfig: Configuration = {
mode: "production",
plugins: [
new MiniCssExtractPlugin({
// 最初加载的输出css文件名,开发环境不要用hash/fullhash/contenthash/chunkhash/modulehash
// filename: "style/[name]_[contenthash:8].css",
filename: "style/[name]_[id].css",
// 按需加载的 chunk 文件名
chunkFilename: "style/[id].css",
}),
new CopyPlugin({
patterns: [
{
// form:复制的文件或者目录
from: "./public",
// globOptions.ignore:不复制html,已经使用了插件HtmlWebpackPlugin,否则报重复错误
globOptions: { ignore: ["**/index.html"] },
// 默认就是配置的output输出目录,output.path没有配置,默认就是dist
// to: "",
},
],
}),
],
};
// 开发环境
const devConfig: Configuration = {
mode: "development",
devtool: "eval-source-map",
devServer: {
// port: 999, // 服务端口号,默认8080
// hot: true, // 默认已开启
// host: "0.0.0.0", // 允许外部访问
historyApiFallback: true, // 解决history路由404问题
},
};
export default (env: WebpackConfigEnv) => {
return merge<Configuration>(
baseConfig(env),
env.production ? prodConfig : devConfig
);
};
11、集成 eslint、prettier
eslint
保证项目代码符合规范,prettier
保证项目的代码统一格式。
先安装
bash
pnpm add -D eslint
初始化eslint,使用npx找到node_modules下安装的eslint
js
npx eslint --init
命令行会出现选择提示:
js
? How would you like to use ESLint? ...
To check syntax only
> To check syntax and find problems //默认选这个,只做代码校验,代码格式交给prettier去做
To check syntax, find problems, and enforce code style
? What type of modules does your project use? ...
> JavaScript modules (import/export) // 选择默认es6
CommonJS (require/exports)
None of these
? Which framework does your project use? ...
React
> Vue.js // 选择在vue项目里面用
None of these
? Does your project use TypeScript? >> No / Yes //选择结合ts
? Where does your code run? ... (Press <space> to select, <a> to toggle all, <i> to invert selection)
√ Browser
√ Node // 浏览器和node都选上
? What format do you want your config file to be in? ...
> JavaScript //选择生成 .eslintrc.js配置文件
YAML
JSON
@typescript-eslint/eslint-plugin@latest eslint-plugin-vue@latest @typescript-eslint/parser@latest
? Would you like to install them now? >> No / Yes //选no,它会用npm安装,我们自己用pnpm安装
手动安装依赖
bash
pnpm add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-vue
对生成的.eslintrc.js
文件里面的默认配置做一些修改
js
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-essential"
],
overrides: [
{
env: {
node: true
},
files: [".eslintrc.{js,cjs}"],
parserOptions: {
sourceType: "script"
}
}
],
parserOptions: {
ecmaVersion: "latest",
parser: "@typescript-eslint/parser",
sourceType: "module"
},
plugins: ["@typescript-eslint", "vue"],
rules: {},
// 声明全局变量,同时在d.ts文件声明
globals: {
__VUE_OPTIONS_API__: "readonly"
}
};
加上.eslintignore
文件忽略校验
ignore
node_modules
dist
*.d.ts
*.css
*.scss
*.ipg
*.png
*.jpeg
*.svg
*.gif
通过命令校验代码,修复.vue .js .ts结尾文件
json
"scripts": {
"lint": "eslint . --ext .vue,.js,.ts --fix",
},
安装prettier,通过代码格式化需要使用
bash
pnpm add -D prettier
创建.prettierrc.js
文件做代码格式化
js
// .prettierrc.js
module.exports = {
//使用tab进行缩进,默认为false,表示用空格进行缩减
useTabs: false,
// tab是空格的情况下,默认选择2个
tabWidth: 2,
// 单行字符数,默认80
printWidth: 80,
// 字符串是否使用单引号,默认为false,使用双引号
singleQuote: false,
// 行尾是否使用分号,默认为true
semi: true,
// 是否使用尾逗号,如对象最后一个属性不用加逗号
trailingComma: "none"
};
创建.prettierignore
文件忽略格式化
ignore
node_modules
dist
同时VSCode需要安装prettier的插件,并且配置
- 设置里面勾选
format on save
- 设置里面 editor default format 选择 prettier
如果格式化没生效,重启vscode,重新打开项目,同时也可以通过脚本格式化:
json
"scripts": {
"prettier": "prettier --write .",
},
12、集成 .editorconfig
保证在不同的IDE编辑器保持统一的代码风格。
下载vscode插件 :EditorConfig for VS Code
, 创建.editorconfig
config
root = true
# 表示所有⽂件适⽤
[*]
# utf-8编码
charset = utf-8
# 缩进⻛格(tab | space),space空格缩进
indent_style = space
# 缩进2个空格
indent_size = 2
# 控制换⾏类型(lf | cr | crlf)
end_of_line = lf
# 去除⾏尾的任意空⽩字符
trim_trailing_whitespace = true
# 始终在⽂件末尾插⼊⼀个新⾏
insert_final_newline = true
# 表示仅 md ⽂件适⽤以下规则
[*.md]
max_line_length = off
trim_trailing_whitespace = false
13、配置 Git 提交规范
有篇文章写的非常详细: 【vue3-element-admin】Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范
总结
Webpack在前端工程化中提供的能力:
- 模块打包和依赖管理:Webpack可以将前端应用程序拆分为多个模块,并通过各种加载器(Loaders)和插件(Plugins)来处理和转换这些模块。它可以解析模块之间的依赖关系,并生成一个或多个打包后的文件,以供浏览器加载和执行。
- 资源管理和优化:Webpack不仅可以打包JavaScript模块,还可以处理其他类型的静态资源,如样式表(CSS、Sass、Less)、图片、字体等。通过加载器和插件,它可以对这些资源进行压缩、合并、优化和缓存等处理,以提高应用程序的加载性能和用户体验。
- 代码分割和懒加载:Webpack支持代码分割和懒加载,可以将应用程序代码拆分为多个块(chunks),并按需加载这些块。这种方式可以减小初始加载的文件大小,提高页面的加载速度,并实现按需加载,降低了用户首次访问时的等待时间。
- 开发环境和生产环境的配置:Webpack提供了强大的配置能力,可以根据开发环境和生产环境的需求来进行不同的配置。它支持开发服务器、热模块替换(Hot Module Replacement)、代码调试等功能,使开发人员能够更高效地进行开发和调试。
- 构建流程的自动化:Webpack可以与其他构建工具(如Grunt、Gulp)集成,并通过配置文件定义整个构建流程。它可以自动化处理资源依赖关系、编译、压缩、合并和输出最终的生产代码,简化了前端开发的构建过程,提高了开发效率。