文章目录
- TL;DR
- 从创建一个 npm 项目开始
- Webpack
- PostCSS
- Tailwind CSS
- 中场休息:整理目录结构
- Babel
- TypeScript
- React.js
- ESLint
- Recap:站在巨人的肩膀上
- 值得一读
现代前端应用框架(如 Next.js、Nuxt.js 等)都直接集成了完整的工具链,按照官方文档做,一行命令就可以配置完毕。这整套工具在我们调试和构建项目时,在背后做了大量工作。虽然这有助于快速上手,但是非常不利于我们了解其中的原理。
然而,各种工具纷繁复杂,文档浩如烟海。由于工具之多,即使文档再友好、工具本身再易用,也很难快速入门。
本文将带你踏上一段旅程,从一个空文件夹开始,一步一步添加工具,最终配置一套完整的前端工具链。在其中,我们可以对各个工具的概念、用途和原理有一个比较系统的认识。每个部分都列出了相关文档的链接,方便查阅。
我们使用 React.js 前端框架,使用 Tailwind 编写 CSS,使用 TypeScript 编写脚本,并使用 ESLint 进行代码检查。最终,希望达到和使用 create-react-app 工具创建的项目类似的开发体验。
TL;DR
配置完毕后,整套工具链的示意图如下:
从创建一个 npm 项目开始
创建一个空目录(一般目录名就是项目名),进入其中执行 npm init
,这个命令会交互式地让你填写该项目的元信息。
我们将这个项目命名为 study-chain(意为 study frontend toolchain):
mkdir study-chain
cd study-chain
npm init
确认信息之后,目录下会生成 package.json
文件,记录了项目的元信息:
// package.json
{
"name": "study-chain",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "SkyWT",
"license": "ISC"
}
接下来,我们以 moment 模块为例,这是一个用于转换日期格式的模块(这个模块其实已经废弃,不推荐新项目使用。我们只是将其作为示例,参见文档)。
在项目根目录下,使用 npm i
安装模块:
npm i moment
我们只使用这个模块的一个功能为例:将模块引入为 moment
之后,moment().format()
返回当前日期时间字符串。
Webpack
让我们先忘掉 React.js,从编写纯 HTML 和 JavaScript 开始。如何在这个项目里使用之前安装的 moment 模块呢?
考虑编写一个简单的 HTML 文件 index.html
:
<html>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
这个 HTML 引用了 index.js
。这个 js 文件引入了 moment 模块,将 div 内的内容设置为当前时间:
// index.js
const moment = require("moment");
const app = document.getElementById("app");
app.innerText = moment().format();
当然,如果此时在浏览器中打开 HTML,这段 js 是无法运行的。因为 require 是 Node.js 的语法,浏览器并不支持。
但是我们知道这个模块就在本地,它的源文件就在 node_modules/moment
路径下。我们需要一个工具获取这个模块,整合进这段 js 里。这种工具就叫做 bundler。有了 bundler,即使在用于前端的 js 中,我们也能引入模块了。
Webpack 就是其中之一。
类似的工具有:Rollup、Parcel。
安装与使用
首先安装 webpack 和 webpack-cli。后者是配套的命令行工具。这两个工具都只是在开发阶段使用,所以使用 --save-dev
安装为开发环境依赖:
npm i webpack webpack-cli --save-dev
安装后,可以直接使用 npx webpack
命令:
npx webpack ./index.js --mode=development
这个命令处理 index.js
文件,解析其中引用的模块,将对应的 js 代码注入该文件。参数 --mode=development
指示生成开发环境下易于调试的文件版本。如果在生产环境,应使用 --mode=production
。
运行之后,会生成 dist/main.js
(这是默认的输出文件,可配置),这就相当于浏览器版的源文件。于是,修改 HTML 中引用的 script 路径:
<html>
<body>
<div id="app"></div>
<script src="./dist/main.js"></script>
</body>
</html>
用浏览器打开,可以发现成功地调用了该模块,div 中显示了当前的日期时间。
使用 --watch
参数可以使 webpack 保持运行,持续监听源文件的修改:
npx webpack ./index.js --mode=development --watch
运行时,每当编辑 index.js
并保存,都会自动重新生成 dist/main.js
文件。可以在终端看到对应的输出。
除了 require
语法,webpack 也支持更常用的 import
语法。刚才的引入模块语句可以改成:
// index.js
import moment from "moment";
// ...
配置文件 webpack.config.js
使用 webpack 的配置文件,可以替代运行命令时传递的参数,让命令行的使用更简洁和灵活。(相关文档)
在项目根目录创建名为 webpack.config.js
的文件,内容如下:
// webpack.config.js
const path = require("path");
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, "dist")
}
};
创建了配置文件之后,使用命令行时,只需要使用如下命令:
npx webpack
npx webpack --watch
设置 npm scripts
为了方便起见,可以将以上 webpack 命令设置为 npm scripts。
编辑 package.json
文件,添加 scripts:
// package.json
{
// ...
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
// ...
}
保存之后,只需使用如下命令,就等同于运行设置的 webpack 命令:
npm run build
npm run watch
为了表述方便,下文将运行 npm run build
命令的这一操作简称为 build。
生成 HTML
现在,构建完成后,访问 index.html
就能看到我们的网站。然而可以发现,这个 HTML 中 main.js
需要我们手动引用。能否让 webpack 帮我们完成这件事情呢?
这就需要让 webpack 为我们在 dist
目录中生成 HTML 文件。这可以通过 html-webpack-plugin 这个插件实现。没错,webpack 不仅是一个打包工具,其还拥有着丰富的插件生态。
运行以下命令安装 html-webpack-plugin(文档):
npm i html-webpack-plugin --save-dev
将 index.html
重命名为 template.html
,内容如下:
<html>
<body>
<div id="app"></div>
</body>
</html>
接下来修改 webpack.config.js
,添加 html-webpack-plugin 插件的配置:
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
// ...
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: "template.html"
})
]
};
再次使用 build 构建,会发现 dist
目录下生成了 index.html
,这个 HTML 引用了生成的 main.js
脚本。打开就能看见其实现了我们要的应用逻辑。
使代码可以 import CSS 文件
现在,有了 webpack 的加持,我们的 js 代码已经可以导入 module 了。但是如果需要引入其他静态资源,比如 CSS 文件,还是无法直接完成。为了使代码能直接 import 其他类型的文件,webpack 中可以安装配置一种称为 loader 的模块。
💡 Webpack 中的 loader 与 plugin:二者都是可以集成到 webpack 的模块,但是两个不同的概念。loader 一般用于处理特定类型的文件,而 plugin 可以提供更加广泛的功能。
比如,为了引入 CSS 文件,可以安装 style-loader 和 css-loader 两个模块(相关文档):
npm i style-loader css-loader --save-dev
接下来,修改 webpack 配置文件,添加一条规则:对于文件名以 .css
结尾的文件,使用这两个模块:
// webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
Webpack 会按照配置的顺序调用 loader。在该配置文件下,先调用 style-loader,再调用 css-loader。这两个 loader 分别的作用是:
- style-loader:将一个 CSS 文件注入 DOM,放在
<style>
元素中。(文档) - css-loader:解析 CSS 中的
@import
和url()
等语句,将对应引用的文件配置好。(文档)
现在,可以使用 import 语句导入 CSS 文件了。首先还是在根目录下编写 style.css
文件:
// style.css
.bg-gray {
background-color: #aaa;
}
保存后,修改 index.js
文件:
// index.js
import moment from "moment";
import "./style.css";
const app = document.getElementById("app");
app.innerText = moment().format();
app.classList.add("bg-gray");
重新 build 后,打开 HTML 即可发现样式的变化。
PostCSS
顾名思义。PostCSS 能够对 CSS 文件进行「后处理」(post-processing)。
和之前提到的 style-loader 和 css-loader 一样,PostCSS 也可以作为 loader 集成到 webpack。
集成到 webpack
首先还是安装 postcss-loader,同时安装 PostCSS 的一个插件 autoprefixer:
npm i postcss-loader autoprefixer --save-dev
在 webpack.config.js
中添加配置:
// webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/i,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1,
},
},
"postcss-loader",
],
},
],
},
// ...
}
⚠️ 注意:此处调用 css-loader 处添加了 options,将 importLoaders 设置为 1。这是考虑到 PostCSS 可能引入新的 @import
等语句,css-loader 要在其运行之后重新进行解析(相关文档)。如果确定 PostCSS 不会添加新的 @import
等语句,则此参数可不加。(可参考 GitHub 上的相关讨论)
配置文件 postcss.config.js
接下来创建 PostCSS 的配置文件,项目根目录下的 postcss.config.js
文件(文档):
// postcss.config.js
/** @type {import('postcss-load-config').Config} */
module.exports = {
plugins: [require("autoprefixer")],
};
在以上的配置文件中,我们加载了 PostCSS 的 autoprefixer 插件(文档)。由于浏览器支持的差异,部分浏览器中使用某些样式需要加上特定的前缀,比如 webkit
或者 moz
,这叫做 vendor prefix。这个插件会自动添加这种前缀,确保样式的兼容性。这里使用此插件只是为了演示 PostCSS 插件的使用,因为接下来我们将配置使用 Tailwind 插件。
PostCSS 是 webpack 的插件,autoprefixer 又是 PostCSS 的插件,也就是 webpack 的插件的插件。接下来我们还可以安装 Tailwind 的插件,即 webpack 的插件的插件的插件。前端工具链就是如此。
Tailwind CSS
使用过 Tailwind 之后,在开发任何前端项目时,我的心理状态:
没有它我不能活!😭😭😭
是的,之后在开发任何前端项目的时候,我没有一次离开过 Tailwind。即使是写纯 HTML 也要从 CDN 引入静态文件。因为它彻底改变了我们编写样式的方式。
作为现代前端项目,Tailwind 当然是必备的工具。
集成到 PostCSS
首先还是安装 Tailwind:
npm install tailwindcss --save-dev
接下来,在 PostCSS 中添加 Tailwind 插件(官方指南):
// postcss.config.js
/** @type {import('postcss-load-config').Config} */
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
};
配置文件 tailwind.config.js
下一步,使用以下命令创建 Tailwind 的配置文件:
npx tailwindcss init
Tailwind 会生成自己的配置文件 tailwind.config.js
(文档):
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
配置文件中的 content 的值,是一个字符串数组,其中存放着需要处理的文件路径。Tailwind 会尝试检测所有匹配的文件中出现的 class 值,并添加对应的 CSS 定义。
为了匹配我们根目录下的模板 HTML 和 js 文件,删除路径中 src
部分。(或者也可以将所有源文件放到 src
子目录里——大多数项目都是这样做的。下一步在整理文件环节,我们也会这样做)。
接下来,在我们引用的主样式表(即 style.css
)的开头,加上 @tailwind
指令:
// style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
.bg-gray {
background-color: #aaaaaa;
}
大功告成。接下来可以尝试修改 HTML 模板并重新 build,就可以发现能使用 Tailwind 了!
<html>
<body>
<div class="text-4xl" id="app"></div>
</body>
</html>
(当然,集成到 PostCSS 并不是使用 Tailwind 的唯一方式。官方的Get started 中提供了大量框架、工具的集成指南)
安装 Tailwind 插件
没错,Tailwind 也有插件生态,比如 tailwindcss-animated(文档)和 typography(文档),这两个插件我都比较常用。
Tailwind 插件配置起来并不难,这里不再展开了,可以查阅相关文档。
中场休息:整理目录结构
至此,CSS 相关的工具配置完了。在进行下一步之前,是时候整理一下我们项目的目录结构了。
如前文所述,为了让项目目录更简洁,我们将所有源文件移动到新建的 src
文件夹内。移动之后,项目目录结构如下:
node_modules/
...
dist/
...
src/
template.html
index.js
style.css
package-lock.json
package.json
postcss.config.js
tailwind.config.js
webpack.config.js
为了使所有工具只处理 src
目录下的文件,需要修改部分配置文件。
修改 tailwind.config.js
:
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{html,js}"],
// ...
};
修改 webpack.config.js
:
// webpack.config.js
module.exports = {
// ...
entry: "./src/index.js",
// ...
};
这下,我们的项目目录就干净了很多。是时候进行下一步了!
Babel
JavaScript 是浏览器原生支持的唯一语言,但:1)不同浏览器对该语言的新特性支持有所不同;2)许多人不喜欢 JavaScript 弱类型的特性,TypeScript 应运而生。但浏览器本身不支持 TypeScript。
所以,需要这样一种工具:1)将 JavaScript 的新特性相关代码转换为使用旧特性的实现;2)将 TypeScript 翻译为 JavaScript。这个过程和 C++ 这类语言「编译」的过程有些相似,只是目标是 JavaScript 而非二进制。
这种工具就叫做 transpiler(可以翻译成「转译器」)。它的作用是将一段代码「翻译」成另一段代码,但目标代码仍然是高级语言(一般是 JavaScript)。这个「翻译」和传统编程语言中的「Compile」概念不同,称为「Transpile」。
Babel 就是其中之一。
(吐槽:既然都要 transpile 才能运行代码,不如直接 compile 成更低级的字节码,执行效率还会更高。Web Assembly 就这样诞生了。不过这里不介绍了)
集成到 webpack
Babel 可以和 PostCSS 一样作为 loader 集成在 webpack 里。安装 Babel:
npm i babel-loader @babel/core --save-dev
安装后,修改 webpack 配置文件,对 .js 文件使用 babel-loader(排除 node_modules
目录中的文件):
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
};
(同样,集成到 webpack 也非安装 Babel 的唯一方式。官方指南提供了很多种配置方式)
目前,重新 build 时,虽然会调用 babel-loader,但是 Babel 还什么事情都没做。这是因为我们没有为其指定任何规则。一般可以通过 preset 指定规则。
presets
Babel 中的 preset 这一概念,官方的定义是「可分享的一组插件和配置的集合」(文档)。
官方提供了四种 preset:
- env:用于将较新的 ECMAScript 特性转译为兼容较旧环境的实现。
- react:用于转译 React.js 的 JSX 语法。
- typescript:用于转译 TypeScript。
- flow:用于 flow 工具,这是一个静态类型检查器。
配置文件 babel.config.json
在项目根目录下创建配置文件 babel.config.json
,其中可以添加 preset 指定规则。我们先添加一个 preser-env:
// babel.config.json
{
"presets": ["@babel/preset-env"]
}
别忘了安装这个 preset:
npm i @babel/preset-env --save-dev
安装和配置完毕后,重新 build,就会使用 preset-env 指定的 transpile 规则。这套规则有什么用呢?
preset-env
ECMAScript 标准每一两年都推出新的版本,引入新的特性。而不同浏览器对其的实现难免会有所滞后。为了:1)能及时使用 ECMAScript 的新特性;2)确保我们的代码在所有浏览器环境中的表现一致,Babel 提供的 preset-env 可以将使用新特性的代码 transpile 为使用旧特性的实现。(文档)(在 Babel 出现之前,许多应用引入一个静态的 js 脚本完成这一功能,这种脚本叫做「polyfill」)
比如,ES6 引入了箭头函数和 const 关键字:
const a = [1,2,3];
a.forEach((x) => console.log(x));
如果要兼容不支持 ES6 的环境(虽然所有现代浏览器都已经支持了 ES6),Babel 就要将箭头函数转换成普通函数,const 换成 var:
var a = [1, 2, 3];
a.forEach(function (x) {
return console.log(x);
});
可以在官网的 Try it out 中尝试。
TypeScript
TypeScript 也是开发现代 Web 应用的必备。如果 standalone 地安装,可以使用 tsc
命令将一个 .ts 文件 transpile 成一个 .js 文件。然而,为了使这一过程在 build 时自动完成,还是要将其集成到 Babel。
集成到 Babel
如前所述,Babel 已经提供了 TypeScript 的 preset(文档),其中包含了转译 TypeScript 的插件。只需直接安装:
npm i @babel/preset-typescript --save-dev
在 webpack.config.js
中,要修改两个地方:1)将 entry 改为 index.ts
;2)将 babel-loader 的 test 规则改为匹配 .ts 结尾的文件:
// webpack.config.js
module.exports = {
// ...
entry: "./src/index.ts",
// ...
module: {
rules: [
// ...
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
};
在 babel.config.json
里,加入 preset-typescript:
// babel.config.json
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript"
]
}
现在,可以将 src
中的 index.js
改写为 index.ts
了。由于这段代码很短,只需要改一个地方,即判断 app
是否为 null
:
import moment from "moment";
import "./style.css";
const app = document.getElementById("app");
if (app !== null) {
app.innerText = moment().format();
app.classList.add("bg-gray");
}
配置文件 tsconfig.json
TypeScript 也有配置文件。在项目根目录下创建 tsconfig.json
即可。
具体规则可参考官方文档。当我们配置好 React 后会再来修改 TypeScript 的规则配置。
React.js
使用 React 时我们会编写 JSX(或 TSX)语法的代码。JSX(或 TSX)全称 JavaScript(TypeScript)Extension,这是一种糅合了 HTML 和 JavaScript(TypeScript)语法的代码。当然,无论是浏览器还是 Node 都不支持这种代码,所以需要 Babel 为我们转译。其实,这样的代码中,类似 HTML 的那部分会被转译成 JavaScript 递归的函数调用的形式。
集成到 Babel
Babel 也提供了 React 的 preset(文档),包含了对应插件。只要安装:
npm i @babel/preset-react --save-dev
在 webpack.config.js
中设置匹配 .ts 或 .tsx 结尾的文件:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
};
修改 Babel 配置文件 babel.config.json
,添加 preset-react:
// babel.config.json
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
接下来,为了让 TypeScript 解释 TSX 语法,要在 tsconfig.json
中加入如下配置:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"jsx": "react",
"esModuleInterop": true,
"noEmit": true,
"allowImportingTsExtensions": true
}
}
这段配置文件中:
- strict 设为 true 表示开启严格类型检查,包括不允许隐式 any 类型等等。
- jsx 设为 react,表示启用 JSX 支持。
- esModuleInterop 设为 true 允许用 import 语法直接导入 CommonJS 模块(否则,必须使用 require 的语法)。
- noEmit 表示不输出编译后的结果文件。由于在该配置中 TypeScript 是作为 Babel 的一个插件,转译后结果文件由 Babel 输出。
- allowImportingTsExtensions 表示允许导入 .tsx 类型的文件。
以及,现在我们的脚本文件可以是 js、jsx、ts、tsx 格式了,要在 Tailwind 的配置文件中修改其检测的文件格式:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
最后,别忘了安装 React 本体,以及其对应的类型定义:
npm i react react-dom --save
npm i @types/react @types/react-dom --save-dev
编写 React 组件
现在,在 src
下创建 App.tsx
文件,我们可以在其中用 TSX 语法编写一个 React 组件了。
将之前写的显示时间的组件写进这里面:
// App.tsx
import React from "react";
import "./style.css";
import moment from "moment";
export default function App() {
return (
<div className="App">
<h1 className="text-4xl">Hello, World!</h1>
<p>{moment().format()}</p>
</div>
);
}
为了引用该组件,入口文件 index.ts
也需要用到 TSX 语法。因此,将其重命名为 index.tsx
,修改为如下内容:
// index.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
const container = document.getElementById("app");
if (container !== null) {
const root = createRoot(container);
root.render(<App />);
}
同时,要在 webpack 配置中修改 entry:
// webpack.config.js
module.exports = {
// ...
entry: "./src/index.tsx",
// ...
}
现在,build 之后,打开生成的 HTML,可以看到我们用 React 写的组件了。
ESLint
ESLint 是一个代码检查工具。对于团队项目,统一代码风格十分重要,而 ESLint 可以方便地做到这一点:如果没有满足指定的代码风格,则显示警告或错误(如果在 IDE 中集成的话),或者拒绝提交或部署(如果在提交部署流程中集成的话)。
为了使流程更加清晰,我们还是选择将 ESLint 作为一个插件集成到 webpack。
集成到 webpack
安装 eslint-webpack-plugin(相关文档):
npm i eslint-webpack-plugin --save-dev
修改 webpack 的配置,添加该插件:
// webpack.config.js
// ...
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [
// ...
new ESLintPlugin({
extensions: ["js", "jsx", "ts", "tsx"],
}),
],
// ...
};
配置文件中 new ESLintPlugin({})
可以传入一个 options 对象,用于指定 ESLint 插件选项(文档)。这里我们指定了要 lint 的文件拓展名。
配置文件 .eslintrc.js
可以使用 @eslint/config
创建配置文件(文档),这是一个友好的交互式命令:
npm init @eslint/config
在其中可以选择「项目使用了 React.js、TypeScript」,该命令会自动为我们安装配置对应的 ESLint 插件。
运行完成后,除了安装了一堆插件,项目根目录会产生配置文件 .eslintrc.js
(或者其他文件格式,取决于你的选择)。
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ["standard-with-typescript", "plugin:react/recommended"],
overrides: [
{
env: {
node: true,
},
files: [".eslintrc.{js,cjs}"],
parserOptions: {
sourceType: "script",
},
},
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["react"],
};
现在,再次 build,ESLint 会按照我们设定的规则进行代码检查。
可以在配置文件中添加一些自己习惯的规则,比如使用双引号、行末加分号。并且,需要设置对于 *.config.js
这类配置文件的特殊检测规则。我的 .eslintrc.js
文件设置如下:
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ["standard-with-typescript", "plugin:react/recommended"],
overrides: [
{
env: {
node: true
},
files: [".eslintrc.js", "*.config.js"],
parserOptions: {
sourceType: "script"
},
extends: ["plugin:@typescript-eslint/disable-type-checked"],
rules: {
"@typescript-eslint/no-var-requires": "off"
}
}
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module"
},
rules: {
"@typescript-eslint/semi": ["error", "always"],
"@typescript-eslint/quotes": ["error", "double"]
},
plugins: ["react"]
};
⚠️ 一个坑点:使用 TypeScript 时,由于使用 typescript-eslint 的解析器而非默认解析器(文档),添加规则要写 @typescript-eslint/quotes
而非 quotes
,否则不会生效。例如:
// eslintrc.js
module.exports = {
// ...
rules: {
"@typescript-eslint/quotes": ["error", "double"],
"@typescript-eslint/semi": ["error", "always"],
},
// ...
};
💡 或许你同时在 IDE 中使用 Prettier 一类的代码格式化工具。其文档中 Prettier vs. Linters 介绍了这两种工具的区别;Integrating with Linters 介绍了其与 linter 的集成指南。简而言之,在 ESLint 配置中应用 eslint-config-prettier 规则集即可自动关闭所有与 Prettier 冲突的规则。不过我更推荐的是在 IDE 中安装 ESLint 插件,对于 js 类文件直接使用 ESLint 作为代码格式化工具,这样能够确保遵循 eslintrc 中的规则。
(ESLint 这部分配置起来还是挺麻烦的,尤其要集成到 VSCode,同时兼容 TypeScript,并考虑到其和 Prettier 的冲突。改天配置好了一个比较 fancy 的方案再单独写一篇)
Recap:站在巨人的肩膀上
至此,一套比较完整的前端项目 starter 终于配置完毕了。完整的项目可以在这个仓库查看。
回顾一下,我们首先使用了 webpack 作为打包工具;其 PostCSS 插件能够对 CSS 进行处理;Tailwind 则可以作为 PostCSS 插件集成。我们使用 Babel 这个 transpiler 处理各种脚本文件,其中 env preset 将 ECMAScript 较先进的特性转译为旧特性的实现,确保兼容性;TypeScript 和 React JSX 两个 preset 则分别将它们各自的语法转译成 JavaScript。最后,我们使用 ESLint 作为代码质量检查工具,并配置其针对 TypeScript 和 JSX 的规则。
使用的工具链关系示意图如下:
相比从前,各种工具让开发的过程变得越来越优雅和美妙。然而每个工具背后,都有无数前人的辛勤付出,没有他们的这些努力,我们无法得到这样现代化的前端开发体验。
现代前端开发,就是站在巨人的肩膀上。
一点思考 🤔
最后,还有一个我的疑问:相比其他领域,为什么 Web 前端开发的工具链会呈现如此复杂的形态呢?我体验过 iOS 开发,也了解过基于 Qt 等框架的客户端开发,我个人的感觉是没有一个领域的客户端开发像 Web 前端这样有如此庞大复杂的工具链:某个工具可以配置插件,插件又有插件,插件的插件又有插件……那么归根结底,Web 前端工具链这种复杂的形式,是历史发展的必然,是某种设计缺陷的后果,还是某种设计思想的体现?🤔
欢迎分享你的思考。
值得一读
Web development used to be a great entry point for people new to programming precisely because it was so easy to get up and running; nowadays it can be quite daunting, especially because the various tools tend to change rapidly.
—— Modern JavaScript Explained For Dinosaurs
值得一读的相关文章;