之前公司项目采用的是umi
脚手架一体化构建工具,得益于对webpack
与各框架的集成和封装,使得快速上手的能力大大加强,但是随着项目的不断迭代与功能增加,依赖的库也是越来越多,目前最明显的感受就是每次启动与打包构建的时长,往往是好几分钟~,热更新有时也要耗费数秒,对于开发效率与体验影响很大。。。
之前尤大发布vite1.0
时也了解了一点,最明显感受就是一个字“快”,不过一直没仔细研究过,只知道是基于`esbuild`和`rollup`,目前`vite2.0`已经发布,完全作为一个独立的构建工具,对`react`等其他非`vue`框架有着很好的支持。最近也算忙里偷闲,算是稍微研究了一下基本知识。本篇文章记录我以`vite`构建`react`的过程及细节,后续会继续深入研究输出`vite`相关系列文章,敬请期待
我对构建项目的要求如下:
Typescript
React
、JSX
语法ES6
语法Less module
Eslint
、Prettier
、Pre-commit hook
HMR
快速热更新Antd
按需引入与主题样式覆盖Proxy
代理、alias
别名chunk
分割浏览器原生支持 ES 模块。
ES
模块,即
,做到快速加载Esbuild
预构建依赖 (本地开发环境)Rollup
打包代码(线上生产环境)HMR
是在原生 ESM
上执行的HTTP
头信息的控制,优化缓存与重加载,高效率利用浏览器能力。Typescript
支持、JSX
支持、CommonJS
和UMD
兼容、css
预处理器与css modules
等JavaScript
。(如第三方依赖antd
、lodash
等)Esbuild
处理。(大量模块的组件库、CommonJS
格式的文件等)JavaScript
的文件,需要转换,时常会被编辑。(JSX
、CSS
、Vue
/React
组件等)Vite
以 原生 ESM
方式提供源码预构建:将有许多内部模块的 ESM
依赖关系转换为单个模块,以提高后续页面加载性能。简单来说就是尽量合并与减少请求。
例如:当我们引入的一个第三方模块依赖了大量其他模块时,在不合并请求的情况下,会请求上百次不等,造成网络拥塞影响性能,而通过预构建合并后只需要一个请求即可。(lodash-es
有超过个内置模块!当我们执行 import { debounce } from 'lodash-es'
时,浏览器同时发出 600 多个 HTTP
请求!通过预构建 lodash-es
成为一个模块,我们就只需要一个 HTTP
请求了!)
相较于传统的 webpack
构建工具,先打包构建所有的依赖和项目代码,然后再启动开发服务器。Vite
则利用浏览器对 ESM
的支持,先启动开发服务器,然后再根据代码执行按需加载剩下所需的对应模块。
因为缓存,在我们第二次启动时几乎可以做到秒开!非常的可怕~
官网图很清晰的描绘了区别:
官方支持React
模板预设有:react
、react-ts
,因为我需要Typescript
,所以直接用这个模板,省事了~
# npm 6.x
npm init @vitejs/app my-react-app --template react-ts
# npm 7+, 需要额外的双横线:
npm init @vitejs/app my-react-app -- --template react-ts
# yarn
yarn create @vitejs/app my-react-app --template react-ts
这里有兴趣的可以尝试下 pnpm
包管理工具,安装速度很快,不了解的可以查看pnpm官方文档,相较于传统的npm
、yarn
工具都有很好的性能提升与使用体验,这里不做过多介绍,放张图大家体会下~
注:就目前我的使用情况来看大部分场景几乎都没问题的,不过还是存在一小部分问题。如:安装precommit后Git hooks不生效等。
# pnpm
pnpm add react react-dom react-router-dom
# or npm
npm i react react-dom react-router-dom
在src
目录下创建pages
目录放置页面组件模块,然后我们简单写两个页面测试下:
// pages/Home/index.tsx
import React from 'react';
const Home: React.FC = () => Home ;
export default Home;
// pages/About/index.tsx
import React from 'react';
const About: React.FC = () => About ;
export default About;
// App.tsx
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Home from './pages/Home'
import About from './pages/About'
const App = () => {
return (
loading}>
);
};
export default App;
新建layouts
组件,主要用于区别渲染登录注册页面与布局界面:
layouts/BasicLayout.tsx
、layouts/UserLayout.tsx
这里就不一一做展示了,详细代码见仓库,地址贴在下面了。
新建路由配置文件router/index.ts
:
import React from 'react';
const Page404 = React.lazy(() => import('../pages/404'));
const Home = React.lazy(() => import('../pages/Home'));
const Login = React.lazy(() => import('../pages/User/Login'));
const Register = React.lazy(() => import('../pages/User/Register'));
const routes: IRoute[] = [
{
path: '/user',
component: React.lazy(() => import('../layouts/UserLayout')),
meta: {
title: '用户路由',
},
redirect: '/user/login',
children: [],
},
{
path: '/',
component: React.lazy(() => import('../layouts/BasicLayout')),
meta: {
title: '系统路由',
},
redirect: '/home',
children: [
{
path: '/home',
meta: {
title: '首页',
icon: 'home',
},
component: ,
},
{
path: '/about',
meta: {
title: '关于',
icon: 'about',
},
component: ,
},
],
},
]
export default routes;
自react hooks
诞生后,大部分场景使用hooks
与props
进行状态管理基本可以满足多数需求,少部分全局应用信息与用户信息等需要全局状态管理的,这里我觉得也不需要完整引入一个redux
、mobx
等这种库。当然还要结合具体场景和公司技术栈确定,较大型和复杂的项目等视情况而定~
我这里使用了zustand
做了简单配置,使用起来也是比较的简单,详情参见官方文档
// 创建store
import create from 'zustand'
const useStore = create(set => ({
bears: 0,
increasePopulation: () => set(state => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 })
}))
// 组件绑定
function BearCounter() {
const bears = useStore(state => state.bears)
return <h1>{bears} around here ...</h1>
}
function Controls() {
const increasePopulation = useStore(state => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
这里就不废话了,直接展示如何在vite
中配置antd
的按需加载,首先我们安装一个插件:
pnpm add vite-plugin-imp -D
然后在vite.config.ts
文件的plugins
中添加配置vitePluginImp
这里我们顺势再引入一个less-vars-to-js
包,less-vars-to-js
可以将less
文件转化为json
键值对的形式,当然你也可以直接在modifyVars
属性后写json
键值对。这样做的好处是可以把全局配置统一放到config
文件进行管理,方便维护。
自定义覆盖主题色
config/variables.less // @primary-color: ‘#ff7875’;
import reactRefresh from '@vitejs/plugin-react-refresh';
import lessToJS from 'less-vars-to-js';
import path from 'path';
import { defineConfig } from 'vite';
// vite-plugin-imp 该插件按需加载存在部分样式丢失的情况
// import vitePluginImp from 'vite-plugin-imp';
// 由于 vite 本身已按需导入了组件库,因此仅样式不是按需导入的,因此只需按需导入样式即可。
import styleImport from 'vite-plugin-style-import';
const themeVariables = lessToJS(
fs.readFileSync(path.resolve(__dirname, './config/variables.less'), 'utf8'),
);
export default defineConfig({
base,
plugins: [
reactRefresh(),
// 配置按需引入antd
// vitePluginImp({
// libList: [
// {
// libName: 'antd',
// style: (name) => `antd/es/${name}/style/index.less`,
// },
// ],
// }),
styleImport({
libs: [
{
libraryName: 'antd',
esModule: true,
resolveStyle: (name) => {
return `antd/es/${name}/style/index`;
},
},
],
}),
],
css: {
preprocessorOptions: {
less: {
// 支持内联 JavaScript,支持 less 内联 JS
javascriptEnabled: true,
// 重写 less 变量,定制样式
modifyVars: themeVariables,
},
},
}
})
通过 --mode
注入配置参数以匹配测试/开发环境等。
我们修改下package.json
文件:
scripts: {
"build:beta": "vite build --mode beta",
"build:release": "vite build --mode release",
"build:legacy ": "vite build --mode legacy ",
}
node
环境下直接process.argv
即可获取到,我们可以在vite.config.ts
中打印信息查看
// vite.config.ts
import {defineConfig} from 'vite'
const env = process.argv[process.argv.length - 1];
console.log('env:', env);
export default defineConfig({})
使用函数式写法配置动态获取环境变量等参数
首先在根目录下创建.env
文件
# port
VITE_PORT = 3100
# HTTP API
VITE_HTTP_API = http://127.0.0.1:8000
# title
VITE_APP_TITLE = Vite React App
然后我们调整下 vite.config.ts
文件
// 函数式配置
import { loadEnv } from 'vite';
import type { ConfigEnv, UserConfig } from 'vite';
export default ({ command, mode }: ConfigEnv): UserConfig => {
const root = process.cwd();
const env = loadEnv(mode, root);
console.log('env', env);
console.log('command', command);
console.log('mode', mode);
}
组件内可通过import.meta.env
获取,我们可以在Home/index.tsx
中打印信息查看
// Home/index.tsx
import React from 'react'
import { Button } from 'antd'
const Home: React.FC = () => {
console.log('import.meta.env', import.meta.env)
return
}
export default Home
export default defineConfig({
...
resolve: {
alias: [
{ find: /^~/, replacement: path.resolve(__dirname, './') },
{ find: '@', replacement: path.resolve(__dirname, 'src') },
],
// alias: {
// '~': path.resolve(__dirname, './'), // 根路径
// '@': path.resolve(__dirname, 'src') // src 路径
// }
}
...
})
export default defineConfig({
...
server: {
port: 8080, // 开发环境启动的端口
proxy: {
'/api': {
// 当遇到 /api 路径时,将其转换成 target 的值
target: 'http://127.0.0.1:8080/api/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), // 将 /api 重写为空
},
}
}
})
vite
默认只支持现代浏览器,即ES module
,对于IE
等老版本浏览器,可使用@vitejs/plugin-legacy
插件在build
时进行polyfill
。
yarn add @vitejs/plugin-legacy -D
// vite.config.js
import legacy from '@vitejs/plugin-legacy'
export default {
plugins: [
legacy({
targets: ['ie >= 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
})
]
}
使用eslint
这种代码检查工具是为了更好的规范代码和写法,列如:禁用var
,建议使用const
、let
,以及配合TS使用时对各种类型规范等,对于代码优化与后期维护都很方便。
而格式化对于多人协同开发时,统一代码风格很有用。
插件规则遵循就近原则,会优先启用本项目下的配置文件,当前配置会覆盖全局配置,如果没有就采用全局配置。
source.fixAll.eslint
开启后可使编辑器按照eslint
规则 auto fix
在 vscode
中安装 Eslint
、Prettier
插件,并开启功能,推荐在全局配置(或当前工作目录下)中添加:
// file: vscode setting.json
// onSave
"editor.formatOnSave": true, //每次保存的时候自动格式化
// eslint
"eslint.alwaysShowStatus": true, // 总是在 VSCode 显示 ESLint 的状态
"eslint.quiet": true, // 忽略 warning 的错误
"editor.codeActionsOnSave": { // 保存时使用 ESLint 修复可修复错误
"source.fixAll": true,
"source.fixAll.eslint": true
},
// prettier
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
为了方便配置,可以使用 VsCode
插件 setting sync(该插件通过GitHub Gist ID
绑定,实现云同步上传下载),通过GitHub Gist ID
云同步配置和插件。
git commit
原理:
git
在执行的过程会提供相关钩子函数,如commit
、push
时,在项目目录下的.git
目录下的hooks
文件夹下存在相关git
生命周期的钩子函数配置,我们可以手动自己配置,只需删除后缀.sample
即可,不过因为这是本地文件,对于团队协同开发同步配置不太友好,所以还是建议采用第三方库统一管理。
hooks
文件夹./.git/hooks/
我们先在项目根目录下创建.husky文件夹
然后添加文件pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged
package.json
中添加如下配置:
script 配置 postinstall是为了确保在安装依赖完成后,npm可以执行postinstall钩子,使husky安装配置文件到.husky文件下,npm在安装执行的过程提供了一些生命周期钩子,postinstall、prepare等。
{
"script": {
"postinstall": "husky install",
"lint:fix": "eslint --cache --ext .js,.jsx,.ts,.tsx --no-error-on-unmatched-pattern --quiet --fix ./src",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
},
"lint-staged": {
"**/*.{js,jsx,tsx,ts,json}": [
"npm run lint:fix",
"git add --force"
],
"**/*.{less}": [
"npm run lint:style",
"git add --force"
]
},
}
再安装 husky
、lint-staged
依赖库,
在提交代码时,lint git
暂存区的代码,若 lint
不通过则中断提交,保证问题代码不进入代码仓库。
├── dist // 默认的 build 输出目录
├── config // 全局配置文件
└── src // 源码目录
├── assets // 公共的文件(如image、css、font等)
├── components // 项目组件
├── constants // 常量/接口地址等
├── layout // 全局布局
├── routes // 路由
├── store // 状态管理器
├── utils // 工具库
├── pages // 页面模块
├── Home // Home模块,建议组件统一大写开头
├── ...
├── App.tsx // react顶层文件
├── main.ts // 项目入口文件
├── typing.d.ts // ts类型文件
├── .editorconfig // IDE格式规范
├── .env // 环境变量
├── .eslintignore // eslint忽略
├── .eslintrc // eslint配置文件
├── .gitignore // git忽略
├── .npmrc // npm配置文件
├── .prettierignore // prettierc忽略
├── .prettierrc // prettierc配置文件
├── .stylelintignore // stylelint忽略
├── .stylelintrc // stylelint配置文件
├── index.html // 项目入口文件
├── LICENSE.md // LICENSE
├── package.json // package
├── pnpm-lock.yaml // pnpm-lock
├── postcss.config.js // postcss
├── README.md // README
├── tsconfig.json // typescript配置文件
└── vite.config.ts // vite
项目Github地址
本文掘金地址
vitejs
Vite 2.0 + React + Ant Design 4.0 搭建开发环境
zustand
如何为你的 Vue 项目添加配置 Stylelint
从零配置 Eslint + Prettier + husky + lint-staged 构建前端代码工作流
ESLint 使用指南
深入浅出eslint——关于我学习eslint的心得
在 pre-commit 的钩子中运行 npm script
@umijs/fabric