Appearance
环境搭建
# 安装pnpm
npm i -g pnpm
# 换成国内的镜像源
pnpm config set registry https://registry.npmmirror.com/
项目初始化
# 初始化 vite 项目
pnpm create vite
# 进入项目目录
cd vite-project
# 安装依赖
pnpm install
# 启动项目
pnpm run dev
# 删除自动生成的 tsconfig.node.json,并编辑 tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src",
"paths": {
"@/*": ["./*"]
}
},
"include": ["./src"]
}
项目入口加载
浏览器不能识别 tsx 语法,也无法直接 import css 文件
Vite 会将项目的源代码编 译成浏览器可以识别的代码,与此同时,一个 import 语句即代表了一个 HTTP 请求
,如 下面两个 import 语句:
import "/src/index.css";
import App from "/src/App.tsx";
上述两个语句则分别代表了两个不同的请求,Vite Dev Server
会读取本地文件,返回浏 览器可以解析的代码。当浏览器解析到新的 import 语句,又会发出新的请求,以此类推, 直到所有的资源都加载完成。
Vite 所倡导的 no-bundle
理念的真正含义: 利用浏览器原生 ES 模块的支持,实现开发阶段的 Dev Server,进行模块的按需加载,而不是先整体打包再进行加载
。 相比 Webpack 这种必须打包再加载的传统构建模式,Vite 在开发阶段省略了繁琐且耗时的 打包过程,这也是它为什么快的一个重要原因。
样式处理
css 解决方案
CSS 预处理器
:主流的包括Sass/Scss、Less和Stylus
。这些方案各自定义了一套语 法,让 CSS 也能使用嵌套规则,甚至能像编程语言一样定义变量、写条件判断和循环语 句,大大增强了样式语言的灵活性,解决原生 CSS 的开发体验问题。CSS Modules
:能将 CSS 类名处理成哈希值,这样就可以避免同名的情况下样式污染的 问题。- CSS 后处理器
PostCSS
,用来解析和处理CSS
代码,可以实现的功能非常丰富,比 如将px 转换为 rem
、根据目标浏览器情况自动加上类似于--moz--、-o-
的属性前缀 等等。 CSS in JS
方案,主流的包括emotion、styled-components
等等,顾名思义,这类方 案可以实现直接在 JS 中写样式代码,基本包含CSS 预处理器
和CSS Modules
的各 项优点,非常灵活,解决了开发体验和全局样式污染的问题。- CSS 原子化框架,如
Tailwind CSS、Windi CSS
,通过类名来指定样式,大大简化了样 式写法,提高了样式开发的效率,主要解决了原生 CSS 开发体验的问题。
CSS 预处理器
# 安装sass
pnpm i sass -D
# 全局导入变量文件,这样不需要手动引入
# 安装 @types/node,这样才能使用 path
pnpm i @types/node -D
# 在 vite.config.ts 写入
import { normalizePath } from 'vite';
import path from 'path';
// 全局 scss 文件的路径
// 用 normalizePath 解决 window 下的路径问题
const variablePath = normalizePath(path.resolve('./src/style/variable.scss'));
export default defineConfig({
// css 相关的配置
css: {
preprocessorOptions: {
scss: {
// additionalData 的内容会在每个 scss 文件的开头自动注入
additionalData: `@import "${variablePath}";`
}
}
}
})
CSS Modules
CSS Modules 在 Vite 也是一个开箱即用的能力,Vite 会对后缀带有 .module
的样式文 件自动应用 CSS Modules
。接下来我们通过一个简单的例子来使用这个功能。
index.scss 更名为 index.module.scss
// index.tsx
import styles from './index.module.scss';
export function Header() {
return <p className={styles.header}>This is Header</p>
};
在配置文件中的 css.modules 选项来配置 CSS Modules 的功能
// vite.config.ts
export default {
css: {
modules: {
// 一般我们可以通过 generateScopedName 属性来对生成的类名进行自定义
// 其中,name 表示当前文件名,local 表示类名
generateScopedName: "[name]__[local]___[hash:base64:5]"
},
}
}
PostCSS
通过 postcss.config.js 来配置 postcss
# 安装一个常用的 PostCSS 插件——autoprefixer
pnpm i autoprefixer -D
这个插件主要用来自动为不同的目标浏览器添加样式前缀,解决的 是浏览器兼容性的问题
。接下来让我们在 Vite 中接入这个插件:
// vite.config.ts 增加如下的配置
import autoprefixer from 'autoprefixer';
export default {
css: {
// 进行 PostCSS 配置
postcss: {
plugins: [
autoprefixer({
// 指定目标浏览器
overrideBrowserslist: ['Chrome > 40', 'ff > 31', 'ie 11']
})
]
}
}
}
pnpm run build 命令进行打包,可以看到产物中自动补上了浏览器前缀,如:
._header_kcvt0_1 {
<!-- 前面的样式省略 -->
-webkit-text-decoration: dashed;
-moz-text-decoration: dashed;
text-decoration: dashed;
}
由于有 CSS 代码的 AST (抽象语法树)解析能力 ,PostCSS 可以做的事情非常多,甚至能实现 CSS 预处 理器语法和 CSS Modules,社区当中也有不少的 PostCSS 插件,除了刚刚提到的 autoprefixer 插件,常见的插件还包括:
- postcss-pxtorem: 用来将 px 转换为 rem 单位,在适配移动端的场景下很常用。
- postcss-preset-env: 通过它, 你可以编写最新的 CSS 语法,不用担心兼容性问题。
- cssnano: 主要用来压缩 CSS 代码,跟常规的 代码压缩工具不一样,它能做得更加智能,比如提取一些公共样式进行复用、缩短一些常 见的属性值等等。
CSS In JS
社区中有两款主流的 CSS In JS 方案: styled-components
和 emotion
。
对于 CSS In JS
方案,在构建侧我们需要考 虑选择器命名问题
、DCE(Dead Code Elimination 即无用代码删除)
、代码压缩
、生成 SourceMap
、服务端渲染(SSR)
等 问题,而 styled-components
和 emotion
已经提供了对应的 babel 插件来解决这些 问题,我们在 Vite 中要做的就是集成这些 babel 插件。
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
// 加入 babel 插件
// 以下插件包都需要提前安装
// 当然,通过这个配置你也可以添加其它的 Babel 插件
plugins: [
// 适配 styled-component
"babel-plugin-styled-components"
// 适配 emotion
"@emotion/babel-plugin"
]
},
// 注意: 对于 emotion,需要单独加上这个配置
// 通过 `@emotion/react` 包编译 emotion 中的特殊 jsx 语法
jsxImportSource: "@emotion/react"
})
]
})
lint 规范
# 安装 ESLint
pnpm i eslint -D
# 初始化 eslint
npx eslint --init
# 安装 react、ts 的 eslint 依赖
pnpm i eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest -D
核心配置解读
ESLint 底层默认使用 Espree 来进行 AST 解析, 这个解析器目前已经基于 Acron
来实现,虽然说 Acron
目前能够解析绝大多数的 ECMAScript 规范的语法,但 还是不支持 TypeScript ,因此需要引入其他的解析器完成 TS 的解析。
社区提供了 @typescript-eslint/parser
这个解决方案,专门为了 TypeScript
的解 析而诞生,将 TS
代码转换为 Espree
能够识别的格式(即 Estree 格式),然后在 Eslint 下通过 Espree
进行格式检查, 以此兼容了 TypeScript 语法。
parserOptions - 解析器选项
这个配置可以对上述的解析器进行能力定制,默认情况下 ESLint 支持 ES5 语法,你可以 配置这个选项,具体内容如下:
- ecmaVersion: 这个配置和
Acron
的 ecmaVersion 是兼容的,可 以配置ES + 数字
(如 ES6)或者ES + 年份
(如 ES2015),也可以直接配置为latest
,启用最新的 ES 语法。 - sourceType: 默认为
script
,如果使用ES Module
则应设置为module
。 - ecmaFeatures: 为一个对象,表示想使用的额外语言特性,如开启
jsx
。
rules - 具体代码规则
rules
配置即代表在 ESLint 中手动调整哪些代码规则,比 如禁止在 if 语句中使用赋值语句
这条规则可以像如下的方式配置:
// .eslintrc.js
module.exports = {
// 其它配置省略
rules: {
// key 为规则名,value 配置内容
"no-cond-assign": ["error", "always"]
}
}
在 rules 对象中,key
一般为规则名
,value
为具体的配置内容
,在上述的例子 中我们设置为一个数组,数组第一项为规则的 ID
,第二项为规则的配置
。也能直接将 rules
对象的 value
配置成 ID
,如: "no-cond-assign": "error"
。
规则的 ID:它的语法对所有规则都适用,你可以设置以下的值:
off
或0
: 表示关闭规则。warn
或1
: 表示开启规则,不过违背规则后只抛出 warning,而不会导致程序退出 。error
或2
: 表示开启规则,不过违背规则后抛出 error,程序会退出。
具体的规则配置可能会不一样,有的是一个字符串,有的可以配置一个对象,参考 ESLint 官方文档。
extends - 继承配置
extends 相当于继承
另外一份 ESLint 配置,可以配置为一个字符串,也可以配置成一个 字符串数组。主要分如下 3 种情况:
- 从 ESLint 本身继承;
- 从类似
eslint-config-xxx
的 npm 包继承; - 从 ESLint 插件继承。
// .eslintrc.js
module.exports = {
"extends": [
// 第1种情况
"eslint:recommended",
// 第2种情况,一般配置的时候可以省略 `eslint-config`
"standard"
// 第3种情况,可以省略包名中的 `eslint-plugin`
// 格式一般为: `plugin:${pluginName}/${configName}`
"plugin:react/recommended"
"plugin:@typescript-eslint/recommended",
]
}
有了 extends 的配置,对于之前所说的 ESLint 插件中的繁多配置,我们就不需要手动一 一开启了,通过 extends 字段即可自动开启插件中的推荐规则:
extends: ["plugin:@typescript-eslint/recommended"]
env 和 globals
这两个配置分别表示运行环境
和全局变量
,在指定的运行环境中会预设一些全局变量, 比如:
// .eslint.js
module.export = {
"env": {
"browser": "true",
"node": "true"
}
}
指定上述的 env
配置后便会启用浏览器和 Node.js 环境,这两个环境中的一些全局变量 (如 window、global
等)会同时启用。
有些全局变量是业务代码引入的第三方库所声明,这里就需要在 globals 配置中声明全局 变量了。每个全局变量的配置值有 3 种情况:
"writable"
或者true
,表示变量可重写;"readonly"
或者false
,表示变量不可重写;"off"
,表示禁用该全局变量。
拿 jquery 举例,我们可以在配置文件中声明如下:
// .eslintrc.js
module.exports = {
"globals": {
// 不可重写
"$": false,
"jQuery": false
}
}
Prettier
虽然 ESLint 本身具备自动格式化代码的功能(eslint --fix
),但术业有专攻,ESLint 的主要优势在于代码的风格检查并给出提示
,而在代码格式化这一块 Prettier 做的更加 专业,因此我们经常将 ESLint 结合 Prettier 一起使用。
安装 Prettier
pnpm i prettier -D
在项目根目录新建 .prettierrc.js
配置文件,填写如下的配置内容:
// .prettierrc.js
module.exports = {
printWidth: 80, //一行的字符数,如果超过会进行换行,默认为80
tabWidth: 2, // 一个 tab 代表几个空格数,默认为 2 个
useTabs: false, //是否使用 tab 进行缩进,默认为false,表示用空格进行缩减
singleQuote: true, // 字符串是否使用单引号,默认为 false,使用双引号
semi: true, // 行尾是否使用分号,默认为true
trailingComma: "none", // 是否使用尾逗号
bracketSpacing: true // 对象大括号直接是否有空格,默认为 true,效果:{ a: 1 }
};
将 Prettier 集成到现有的 ESLint 工具中,安装两个工具包:
- eslint-config-prettier:用来覆盖 ESLint 本身的规则配置
- eslint-plugin-prettier:让 Prettier 来接管 eslint --fix 即修复代码的能力
pnpm i eslint-config-prettier eslint-plugin-prettier -D
在 .eslintrc.js 配置文件中接入 prettier 的相关工具链:
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
// 1. 接入 prettier 的规则
'prettier',
'plugin:prettier/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 'latest',
sourceType: 'module'
},
// 2. 加入 prettier 的 eslint 插件
plugins: ['react', '@typescript-eslint', 'prettier'],
rules: {
// 3. 注意要加上这一句,开启 prettier 自动修复的功能
'prettier/prettier': 'error',
quotes: ['error', 'single'],
semi: ['error', 'always'],
'react/react-in-jsx-scope': 'off'
},
// 指定 react 版本
settings: {
react: {
version: 'detect'
}
}
};
在 package.json 添加脚本
{
"scripts": {
"lint:script": "eslint --ext .js,.jsx,.ts,.tsx --fix --quiet ./",
}
}
新建并编辑 .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode" //支持 tsx 这些文件的 prettier 自动格式化
}
在 Vite 中接入 ESLint
安装 Vite 中的 ESLint 插件:
pnpm i vite-plugin-eslint -D
vite.config.ts 中接入:
// vite.config.ts
import viteEslint from 'vite-plugin-eslint';
// 具体配置
{
plugins: [
// 省略其它插件
viteEslint(),
]
}
配置好后在项目运行时即可捕捉到 eslint 的错误并提示,并且及时修复它
styleLint
安装 styleLint
pnpm i stylelint stylelint-prettier stylelint-config-prettier stylelint-config-recess-order stylelint-config-standard stylelint-config-standard-scss -D
新建并编辑 .stylelintrc.js
module.exports = {
// 注册 stylelint 的 prettier 插件
plugins: ['stylelint-prettier'],
// 继承一系列规则集合
extends: [
// standard 规则集合
'stylelint-config-standard',
// standard 规则集合的 scss 版本
'stylelint-config-standard-scss',
// 样式属性顺序规则
'stylelint-config-recess-order',
// 接入 Prettier 规则
'stylelint-config-prettier',
'stylelint-prettier/recommended'
],
// 配置 rules
rules: {
// 开启 Prettier 自动格式化功能
'prettier/prettier': true
}
};
在 package.json 添加脚本
{
"scripts": {
// 整合 lint 命令
"lint": "npm run lint:script && npm run lint:style",
// stylelint 命令
"lint:style": "stylelint --fix \"src/**/*.{css,scss}\""
}
}
在 Vite 中接入 styleLint
安装 stylelint
pnpm i @amatlash/vite-plugin-stylelint -D
在 Vite 配置文件中添加如下的内容:
import viteStylelint from '@amatlash/vite-plugin-stylelint';
{
plugins: [
// 省略其它插件
viteStylelint({
// 对某些文件排除检查
exclude: /windicss|node_modules/
}),
]
}
编辑 .vscode/settings.json,添加 styleLint 配置
{
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
},
"stylelint.validate": ["css", "less", "scss"]
}
配置好后,在运行阶段就能捕获 styleLint 错误,并及时修正。
stylelint 默认规则不能首字母大写,可自行修改配置或者忽略文件的验证
husky
# 安装依赖
pnpm i husky -D
初始化 Husky: npx husky install,并将 husky install 作为项目启动前脚本,如:
{
"scripts": {
// 会在安装 npm 依赖后自动执行
"postinstall": "husky install"
}
}
要在终端执行 npx husky install 或者 pnpm run postinstall
添加 Husky 钩子,在终端执行如下命令:
npx husky add .husky/pre-commit "npm run lint"
# windows 10 需要执行才生效:
npx husky add .husky/pre-commit "npm-run-test"
配置 lint-staged,只检查暂存区
# 安装依赖
pnpm i -D lint-staged
# 在 package.json 写入
"lint-staged": {
"**/*.{js,jsx,tsx,ts,json}": [
"npm run lint:script",
"git add --force"
],
"**/*.{scss,css,less}": [
"npm run lint:style",
"git add --force"
]
}
# 把 pre-commit 里的脚本改成
npx --no -- lint-staged
提交时的 commit 信息规范
# 安装依赖
pnpm i commitlint @commitlint/cli @commitlint/config-conventional -D
# 新建.commitlintrc.js:
// .commitlintrc.js
module.exports = {
extends: ["@commitlint/config-conventional"]
};
# 将commitlint的功能集成到 Husky 的钩子当中,windows 10 就改一下写入的内容为xxx,后面自己手动去改吧
npx husky add .husky/commit-msg "npx --no-install commitlint -e $HUSKY_GIT_PARAMS"
常用规范如下:
- feat: 添加新功能。
- fix: 修复 Bug。
- chore: 一些不影响功能的更改。
- docs: 专指文档的修改。
- perf: 性能方面的优化。
- refactor: 代码重构。
- test: 添加一些测试代码等等。