↓
译者: 翔子丶
https://juejin.cn/post/7235547967112806437
create-vct
这是两个用作调用 vite + React 虚拟化工程项目的钢架辅助工具。
🔥 运转业务流程
create-vct-flow.gif🎁 加装 & 采用
npx create-vct
# ornpm i create-vct -g
create-vct
直接原因
他们子公司后端标准化用 vue3,但前段时间有位Chalancon工程项目”商品副经理:顾客想个素描,我后端能无法做?“,多于 react 的架构能做。党委说让我来弄那个工程项目,别忘了完了后搭两个 react 钢架辅助工具用作之后加速合作开发 react 应用领域。ok,拒绝接受考验 😜。
create-vct 钢架辅助工具是在 create-vite 的基础上修正,PCB了 react 中常见到的各式各样包:react-router、redux-toolkit、react-query、antd 之类,包涵 ts 和 js 版,与此同时也PCB了 eslint + prettier + husky + commitlint 用作项目组规范化。
下面让他们来看看整个过程,篇幅较长,请耐心观看。
针对 create-vite 的修正
create-vite-load.png删除不需要的模块
因为他们只需要 react 的模板,所以把其他不需要的一并删除掉,最后只留下这些文件。
create-vite-delete.png修正模板 vite.config.ts 代码
配置 alias 添加别名设置
配置 server 代理服务器
// vite.config.ts…
export defaultdefineConfig({
plugins: [react()],
resolve: {
alias: {
@: path.resolve(__dirname, src), // src 路径}
},
server: {
port: 5173, // 合作开发环境启动的端口proxy: {
/api: {
// 当遇到 /api 路径时,将其转换成 target 的值 target: http://xx.xx.xx.xx:8080/api,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ), // 将 /api 重写为空},
},
},
})
路径别名与此同时需要配置 tsconfig.json,不然直接采用 ts 会报错// tsconfig.json{
“compilerOptions”: {
“paths”: {
“@/*”: [“./src/*”]
}
}
}
采用 sass 作为 css 预处理器
子公司工程项目都是采用 sass,所以那个钢架自然采用 sass 来处理 css。
在 package.json 中依赖中添加”sass”: “^1.62.1″;
将.css 后缀文件修正为.scss 后缀文件;
创建 src/styles/variables.scss,设置全局 sass 变量;
在 vite.config.js 中配置全局 sass 变量:
export defaultdefineConfig({
…
css: {
preprocessorOptions: {
scss: {
additionalData: @import “@/styles/variables.scss”;,
},
},
}
…
})
注意:vite 对.sass 已经提供了内置支持,所以不再需要加装 loader 了,官方解释( https://cn.vitejs.dev/guide/features.html#css-pre-processors )
将 classnames 引入工程项目
个人感觉 CSS Modules 比 CSS In JS 用起来更舒服,所以该钢架采用 CSS Modules 的方式来处理 css,当然你也能用 CSS In JS 的方式处理,工程项目模板没有太多 css 代码,不会影响到你的选择。
在 package.json 中依赖中添加”classnames”: “^2.3.2″即可。
采用也很简单:
import classNames from classnames/bindimport styles from ./index.module.scssconstcx = classNames.bind(styles)
const App = () =>{
return <div className={cx(btn, btn-primary)}></div>}
标准化代码 & git 规范化
eslint-logo.png采用 EditorConfig 标准化 IDE 编码风格
.editorconfig 文件创建在工程项目根目录下:
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = falseinsert_final_newline = false添加 eslint & prettier 用作代码规范化
eslint 和 prettier 的加装参考的是vite-pretty-lint[1],将工程项目源码克隆到本地,只需要创建文件的那部分代码,其余的可删除。
添加 pre-commit 和 commit-msg 钩子
添加 githooks,在提交前对代码进行校验和格式化处理,并规范化提交信息
他们看看在两个工程项目中配置 git hooks 都需要哪些步骤。
添加 husky 和 lint-staged 依赖 yarn add lint-staged husky
调用 husky,生成.husky 文件 yarn husky install
在 package.json 的 scripts 中添加 prepare 个钩子“scripts”: {
“dev”: “vite”,
“build”: “tsc && vite build”,
“preview”: “vite preview”,
“lint”: “eslint –ext .ts –ext .tsx src –fix”,
“prepare”: “npx husky install”},
在 package.json 中配置 lint-staged“lint-staged”: {
“src/**/*.{js,jsx,ts,tsx,json,md}”: [
“eslint –fix –max-warnings=0”,
“prettier –write”],
“src/**/*.{scss,less,css}”: [
“prettier –write”],
}
添加 pre-commit 钩子,会在.husky 目录下生成 pre-commit 文件 npx husky add .husky/pre-commit“npx lint-staged”这一步完成后,在提交代码的时候就会有对暂存区的代码做 ESLint 代码校验和 Prettier 格式化处理。
接着是 commitlint 规范化提交信息,加装依赖 yarn add @commitlint/cli @commitlint/config-conventional -D
创建 commitlint.config.js 配置文件 module.exports = {
extends: [@commitlint/config-conventional],
}
生成 pre-commit hook npx husky add .husky/commit-msg npx commitlint –edit到这里,husky + lint-staged + commitlint 都配置完成了。
husky-error.png这一步完成后,他们与此同时配置了代码规范化和 git 规范化,添加了 husky,所以需要在工程项目创建完成后,首先执行一下 git init 调用 git 仓库,然后 husky 才能正常运转,于是就把提示信息多加了一项 🤔,如下:
create-vct-init.png基于以上,他们修正 create-vite 的代码,添加如下:
if(isEslint) {
const eslintTemplate = ../eslint-templates const eslintFile = path.join(targetPath, .eslintrc.json)
const prettierFile = path.join(targetPath, .prettierrc.json)
consteslintIgnoreFile = path.join(targetPath,.eslintignore)
const { packages, eslintOverrides } = await import(
`${eslintTemplate}/${template}.js`)
constpackageList = { …commonPackages, …packages }
consteslintConfigOverrides = […eslintConfig.overrides, …eslintOverrides]
const eslint = { …eslintConfig, overrides: eslintConfigOverrides }
const viteConfigFiles = [vite.config.js, vite.config.ts]
const[viteFile] = viteConfigFiles
.map((file) =>path.join(targetPath, file))
.filter((file) =>fs.existsSync(file))
constviteConfig = viteEslint(fs.readFileSync(viteFile,utf8))
fs.writeFileSync(eslintFile, JSON.stringify(eslint, null, 2))
fs.writeFileSync(prettierFile,JSON.stringify(prettierConfig, null, 2))
fs.writeFileSync(eslintIgnoreFile, eslintIgnore.join(\n))
fs.writeFileSync(viteFile, viteConfig)
constfiles = fs.readdirSync(eslintTemplate)
for (constfileof files.filter((f) => !f.includes(react))) {
write(file, eslintTemplate)
}
pkg.devDependencies = { …pkg.devDependencies, …packageList }
pkg.scripts = { …pkg.scripts, …packageScripts }
pkg[lint-staged] = packageMore
write(package.json, templateDir, JSON.stringify(pkg, null, 2) + \n)
}
这一步会将.husky、.editorconfig、commitlint.config.js、.eslintrc.json、.prettierrc.json和.eslintignore配置添加到所创建的工程项目中,并修正 package.json 文件,添加 ESLint 的依赖项。
集成 ant design 作为 UI 库
antd-logo.png他们先来梳理下,如果往工程项目中添加 Antd 需要做哪些事:
添加 Antd 依赖;
ant-design@v5 版支持 tree-shaking,就不用配置按需加载了。那么就很简单,他们只需要在 package.json 的dependencies字段中添加 Antd 的库。
全局引入 reset.css 文件;
设置 ConfigProvider 全局化配置;
import antd/dist/reset.css import zhCN from antd/locale/zh_CN importdayjsfrom dayjs import dayjs/locale/zh-cn import { ConfigProvider } from antd dayjs.locale(zh-cn)
ReactDOM.createRoot(document.getElementById(root)).render(
<React.StrictMode> <ConfigProvider locale={zhCN}> <App /> </ConfigProvider> </React.StrictMode>)
修正 App 组件,添加两个 antd 的组件,这样启动工程项目就能看到 Antd 的组件采用方法。基于以上的步骤,他们能仿照 ESLint 那块的代码来修正,最终实现如下:
// antd配置const fileSuffix = template.endsWith(-ts) ? .tsx : .jsxif(isAntd) {
constAppComponent = path.join(targetPath,`/src/App${fileSuffix}`)
constMainComponent = path.join(targetPath,`/src/main${fileSuffix}`)
// @ts-ignore const { packages, App, Main } = await import(../antd-templates/index.js)
fs.writeFileSync(AppComponent, App)
fs.writeFileSync(MainComponent, Main)
pkg.dependencies = { …pkg.dependencies, …packages }
write(package.json, templateDir, JSON.stringify(pkg, null, 2) + \n)
}
集成 react-router 作为路由
react-router-logo.png这里他们采用 react-router@v6 版,v6 版之前如果采用 typescript 时,需要与此同时加装@types/react-router、@types/react-router-dom、react-router和react-router-dom。
react-router 和 react-router-dom 的关系类似于 react 和 react-dom。dom 及浏览器环境,react-router-dom 通过添加用作 DOM 的组件,能让 react-router 运转在浏览器环境,与此同时还有 react-router-native,用作 native 环境。
采用 v6 后不需要再引入额外的类型,只需要加装react-router和react-router-dom即可。
react-router v6 说明
<Routes />: 新增组件,移除 v5 的<Switch />组件,用<Routes />组件代替,用法相同;<Router />: 基础路由组件,v5 的 componentreact-router v6 采用教程
同 Antd,首先他们先梳理一下将 react-router 添加到工程项目中的步骤。
添加 react-router 和 react-router-dom 依赖; yarn add react-router-dom react-router
添加 src/routes/routerConfig.ts 文件,配置路由表; import ErrorPage from @/pages/ErrorPage import AppLayout from @/layout/AppLayout import { lazy } from react import { MetaMenu, AuthRouteObject } from ./interface // 加速导入辅助工具函数 const lazyLoad = (moduleName: string) => lazy(() => import(`@/pages/${moduleName}/index.tsx`))
const Home = lazyLoad(Home)
const ReduxToolkitDemo = lazyLoad(ReduxToolkitDemo)
constReactQueryDemo = lazyLoad(ReactQueryDemo)
const routers: AuthRouteObject
{
path: /,
element: <AppLayout />,
errorElement: <ErrorPage />,
meta: {
title: ,
},
children: [
{
path: home,
element: <Home />,
meta: {
title: Home,
},
},
{
path: toolkit,
element: <ReduxToolkitDemo />,
meta: {
title: React Toolkit,
},
},
{
path: query,
element: <ReactQueryDemo />,
meta: {
title: React Query,
},
},
],
},
]
export defaultrouters
创建 src/layout 文件夹,添加 AppLayout 组件;
创建 src/pages 文件夹,添加 Home 和 ReactQueryDemo 组件;
通过 useRoutes[2] 钩子将上面的路由表一一映射为路由对象
useRoutes 也就是<Routes />组件的 js 实现,在路由跳转时需要增加 loading 转场,他们能采用<Suspense />组件传入两个 loading 组件来实现。
此处的 Loading 组件可根据工程项目需求来修正转场动画
import ./App.scss
import routers from ./routes/routerConfig
import { useRoutes } from react-router-dom
import { Suspense } from react
import { Spin } from antd
function App() {
const elements = useRoutes(routers)
return
}
export default App
在 main.tsx 中配置
ReactDOM.createRoot(document.getElementById(root) as HTMLElement).render(
)
基于以上步骤,他们实现的代码如下:
首先询问是否需要加装 react-router,并返回 isRouter 是否为 true,如果为 true 时,将 react-router 的模板文件添加到输出目录中,与此同时修正 App.tsx 和 main.tsx 的代码:
if(isRouter) {
copyTemplateFile(router)
// @ts-ignore let{ packages, App, Main, Antd_App, Antd_Main } =await import(
../router-templates/index.js)
if(isAntd) {
App = Antd_App
Main = Antd_Main
}
fs.writeFileSync(AppComponent, App)
fs.writeFileSync(MainComponent, Main)
pkg.dependencies = { …pkg.dependencies, …packages }
write(package.json, templateDir, JSON.stringify(pkg, null, 2) + \n)
}
集成 Redux toolkit 作为状态管理
redux-toolkit-logo.png为什么是 Redux toolkit
Redux Toolkit[3] 是 Redux 官方强烈推荐,开箱即用的两个高效的 Redux 合作开发工具集。它旨在成为标准的 Redux 逻辑合作开发模式,强烈建议你采用它。
优点如下:
优化 Redux 中间件、各式各样配置以及书写目录规范化等,简化操作,例如取代 Redux 中很恶心的 types、actions、reducers;React-Redux Hook API 取代麻烦的connect和mapState;Reducer 中默认采用 Immer 来更新 Immutable 数据,不用再返回 state,简单、省事;PCB好了 redux-devtools-extension,可直接采用;已经集成 redux-thunk,不需要再次加装;按 feature 组织 Redux 逻辑,更加清晰。总体来说,Redux Toolkit 的出现让之前想尝试 Redux,但又被 Redux 各式各样繁琐的配置劝退的人重新归位 😄。
Redux Toolkit 采用教程
参考:https://redux.js.org/usage/usage-with-typescript#define-typed-hooks
添加 Redux Toolkit 相关依赖; yarn add @reduxjs/toolkit react-redux
创建 store 文件夹;
store 中包涵 feature(包涵所有的 Slice)、hooks(PCB useSelector 和 useDispatch)、index(主入口文件)
store
│ ├── feature
│ │ └── appSlice.ts
│ ├── hooks.ts
│ └── index.ts
index.ts 入口; import{ configureStore, ThunkAction, Action }from @reduxjs/toolkit import appReducer from ./feature/appSlice // 创建store export conststore = configureStore({
// feature中创建多个子reducer 最终在这里进行合并reducer: {
app: appReducer,
},
// 中间件 middleware: (getDefaultMiddleware) =>getDefaultMiddleware({
serializableCheck: false,
}),
// redux-devtools-extension何时开启 devTools: process.env.NODE_ENV !== production,
})
export type AppDispatch = typeofstore.dispatch
export type RootState = ReturnType<typeofstore.getState>
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>
> serializableCheck: false 关闭 serializableCheck 检查,当数据较复杂时,开启时会报错; \@reduxjs/toolkit 已经PCB好了 redux-devtools-extension,通过 devTools: true 开启。
hooks.ts 钩子
官方推荐采用 useAppSelector 来操作 store 数据,采用 useAppDispatch 触发子 store 中的 action。
import{ TypedUseSelectorHook, useDispatch, useSelector }from react-redux import type { RootState, AppDispatch } from . // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = ()=> useDispatch
useAppSelector: TypedUseSelectorHook
将 Redux 连接到 React
在 main.tsx 中,采用 react-redux 的 Provider 组件将 store 注入到上下文中,和之前没有变化
import { Provider } from react-redux;
import { store } from ./store;
…
…
组件中怎样采用
ion。
import { useAppDispatch, useAppSelector } from ./store/hooks
import {
selectCount,
decremented,
incremented,
} from ./store/feature/appSlice
function App() {
const elements = useRoutes(routers)
const count = useAppSelector(selectCount)
const dispatch = useAppDispatch()
return (
<>
style={{ marginRight: 8px }}
type=”primary”
onClick={() => dispatch(incremented())}
>
+
…
)
}
export default App
Redux Toolkit 异步操作
在 Redux 中,如果需要异步操作则需要加装 Redux-Thunk,而 Redux Toolkit 已经内置了 Redux-Thunk,不需要另外加装和配置。
他们只需要采用 createAsyncThunk 就能完成异步 action 的创建。
创建 userSliceimport { createSlice, createAsyncThunk } from @reduxjs/toolkitimport { RootState } from ..type UserType = { [props: string]: string | number | boolean}
export interfaceUserState {
users: UserType[]
total: number}
constinitialState: UserState = {
users: [],
total: 0,
}
// createAsyncThunk创建两个异步action,return出去的值,会在extraReducers中接收,有三种状态:// pending(进行中)、fulfilled(成功)、rejected(失败)export const getUserData = createAsyncThunk(user/getList, async() => {
const res = await fetch(https://api.github.com/search/users?q=wang).then(
(res) =>res.json()
)
returnres
})
// 创建两个 Sliceexport constuserSlice = createSlice({
name: user,
initialState,
reducers: {
// Reducer 中默认采用 Immer 来更新 Immutable 数据,不用再返回 state deleteUser: (state, { payload }) =>{
state.users = state.users.filter((user) =>user.id !== payload)
state.total -= 1},
},
// extraReducers 拒绝接受 createAsyncThunk的状态extraReducers(builder) {
builder
.addCase(getUserData.pending,() => console.log(loading…))
// 通常只需要拒绝接受fulfilled即可 接收到返回值在同步到state状态中即可.addCase(getUserData.fulfilled,(state, { payload }) =>{
state.users = payload.items
state.total = payload.total_count
})
.addCase(getUserData.rejected, (_, err) => console.log(error, err))
},
})
export const{ deleteUser } = userSlice.actions
export const selectUser = (state: RootState) =>state.user.users
export defaultuserSlice.reducer
在 userSlice 中他们主要做了这几件事:
采用 createAsyncThunk 创建两个异步 action
通过 createAsyncThunk return 出去的值,会在 extraReducers 中接收,有三种状态:
pending(进行中)fulfilled(成功)ejected(失败)通过 extraReducers 来拒绝接受 createAsyncThunk 的结果
创建两个 deleteUser action,用来删除用户
这一步主要是为了演示 immer 的作用。
要是以前在 Redux 中,他们需要这样操作:
deleteUser: (state, { payload }) =>{
return{
…state,
users: […state.users].filter((user) =>user.id !== payload),
count: state.total – 1}
而采用 Redux Toolkit 后,只需要这样操作:
deleteUser: (state, { payload }) =>{
state.users = state.users.filter((user) =>user.id !== payload);
state.total -= 1;
},
在主入口合并到 reducer 中,和 appSlice 一样操作;
采用,同同步操作
基于以上步骤,他们实现的代码如下:
首先询问是否需要加装 Redux Toolkit,并返回 isRedux 是否为 true,如果为 true 时,将 Redux Toolkit 的模板文件添加到输出目录中,与此同时修正 main.tsx 的代码:
if(isRedux) {
copyTemplateFile(redux)
// @ts-ignore let{ packages, Main, Router_Main, Antd_Main, Antd_Router_Main } =
await import(../redux-templates/index.js)
if(isAntd) Main = Antd_Main
if(isRouter) Main = Router_Main
if(isAntd && isRouter) Main = Antd_Router_Main
fs.writeFileSync(MainComponent, Main)
pkg.dependencies = { …pkg.dependencies, …packages }
write(package.json, templateDir, JSON.stringify(pkg, null, 2) + \n)
}
集成 react-query 作为请求库
react-query-logo.png为什么是 react query
ta-fetching)库,但是从更广泛的角度来看,它。
接下来,跟着我的业务流程看一下,你就会发现 react query 太香了。
react query 采用教程
官方教程[4]
添加 react query 和 axios 依赖 yarn add react-query axios
创建 api 文件夹
api 中包涵 feature(包涵所有的请求接口)、interface(类型标准化管理)、query(react query 相关配置)、request(PCB axios)。
├── api
│ ├── feature
│ │ └── app.ts
│ ├── interface.ts
│ ├── query
│ │ ├── query.client.ts
│ │ └── query.constant.ts
│ └── request.ts
对于 axios,网上有大把过渡PCB的案例 🤮,我只是简单的添加了请求拦截和相应拦截,感觉就已经够用了。
import { message } from antd import Axios, { AxiosRequestConfig } from axios import { resData } from ./interface // 标准化配置 constbaseURL =
export constservice = Axios.create({
baseURL,
responseType: json,
timeout: 600000,
})
// 请求拦截service.interceptors.request.use((res: any) =>{
res.headers.token = token returnres
})
// 拦截响应service.interceptors.response.use(
(response: any) =>{
constres = response.data
// 这块需要修正 根据请求返回成功标志 if(res || res.success) {
if(res.message) message.success(res.message,3)
returnresponse
} else{
if(res.message) {
message.error(res.message, 3)
if (res.code === 401) {
window.location.href = /login}
}
return Promise.reject(res)
}
},
(err) =>{
message.error(err.message,5)
return Promise.reject(err)
}
)
// 设置返回值和请求值范型 export default function request<Res = any, Q = any>(req: AxiosRequestConfig & {
data?: Q
}
){
returnservice(req).then(
(res) =>{
return res.data asresData
},
(res) =>{
return Promise.reject((res.data || res) as resData
}
)
}
别忘了看下导出的 request 方法,传入了返回和请求值的类型,然后在 app.ts 中,采用 request 发送请求时,他们只要传入后端的返回类型,这样在接口请求完成后,采用数据的时候就会方便很多,这就是 TS 的好处 🐮。
import { GithubType } from ../interface import request from ../request /* 用户列表 */ export const getUserList = (searchName: string) => request
,
method: get,
})
axios-ts-resdata.gifquery.client.ts 用作调用 react query,导出 client 会在 QueryClientProvider 中进行透传。
import { QueryClient } from react-query const client = newQueryClient({})
// client.setQueryDefaults(QUERY_CHAT_TRANSLATE_INPUT, { // select: (res) => res.data, // enabled: false, // }); export defaultclient
query.constant.ts 用作标准化管理 react query 所有的 key,key 有什么用呢?这个只要你把 react query 用起来就能知道 key 的作用了。
export const QUERY_USER_LIST = user/list在 main.tsx 中配置 import { QueryClientProvider } from react-query
import client from ./api/query/query.client
…
ReactDOM.createRoot(document.getElementById(root) as HTMLElement).render(
{/* 添加devtools */}
{process.env.NODE_ENV === development ? (
initialIsOpen={false}
position=”bottom-right”
/>
) : (
)}
…
)
在组件中采用
通过下面那个例子能看出来,不采用 react query 的情况下,我要既要通过 useState 管理 loading 和数据状态,还得通过 useEffect 来发送请求;而采用 react-query 的情况下,各式各样数据状态直接能采用 useQuery 来代替,简化他们的代码。
import { getUserList } from @/api/feature/app
import { UserType } from @/api/interface
import { QUERY_USER_LIST } from @/api/query/query.constant
// import React, { useEffect, useState } from react;
import { useQuery } from react-query
const QueryDemo = () => {
// 不采用react-query时的请求
// const [loading, setLoading] = useState(false);
// const [users, setUsers] = useState
// const getList = () => {
// setLoading(true);
// getUserList(wang)
// .then((res) => setUsers(res.items))
// .finally(() => setLoading(false));
// };
// useEffect(() => getList(), []);
// 采用react-query
const { data: users, isLoading: loading } = useQuery(
QUERY_USER_LIST,
() => getUserList(wang),
{
select: (res) => res.items,
}
)
return (
<>
{loading &&
{users?.map((user: UserType) => (
))}
)
}
export default QueryDemo
基于以上步骤,他们实现的代码如下:
首先询问是否需要加装 react-query,并返回 isQuery 是否为 true,如果为 true 时,将 react-query 的模板文件添加到输出目录中,与此同时修正 main.tsx 的代码:
if(isQuery) {
copyTemplateFile(query)
制到输出目录中 const MainComponent = path.join(targetPath, ./src/Main.tsx)
// @ts-ignore const{ packages, Main } =await import(../query-templates/index.js)
fs.writeFileSync(MainComponent, Main)
pkg.dependencies = { …pkg.dependencies, …packages }
write(package.json, templateDir, JSON.stringify(pkg, null, 2) + \n)
}
🎉🎉🎉 到这里,他们需要做的事情都已经完成了!接下来就是发布 npm 包。
发布 npm
“bin”: {
“create-vct”: “index.js”,
“cvt”: “index.js”},
“files”: [
“index.js”,
“template-*”,
“*-templates”,
“dist”],
发布 npm 包时,他们需要将所有模板文件都发布,修正 package.json 的files如上所示,files 就是发布到 npm 仓库的文件。
bin 字段修正为create-vct,这样在执行create-vct命令时最终执行的文件就是 index.js。
package-bin.pngbin 里的命令对应的是两个可执行的文件,通过软链接或者符号链接到指定资源的映射,这些可执行文件必须以 #!/usr/bin/env node 开头,否则脚本将在没有 node 可执行文件的情况下启动。
修正完成后,执行
pnpm build
nrm use npm
npm publish
怎样采用
全局加装 npm i create-vct -g
create-vct
全局加装后,直接在控制台输入create-vct即可。
采用 npx
npm 从 5.25.2 版开始,增加了 npx 命令,能让他们在当前路径采用全局包。npx 运转时,会到当前 node_modules/.bin 路径和环境变量$PATH 里面,检查命令是否存在。如果找不到时会从 npm 上下载最新版采用。
npx create-vct
总结
整个业务流程走下来,真的是收获很多,学到了 node 操作文件的很多 api。对 react-router、redux-toolkit 和 react-query 又重新回顾了一遍。之前做的Chalancon要派上用场了,我用它来做每个模块的图(虽然略丑 😅),与此同时还学着做了一下 svg 的图标,可谓是收获满满。
以上就是本文的全部内容,希望这篇文章对你有所帮助,欢迎点赞和收藏 🙏,如果发现有什么错误或者更好的解决方案及建议,欢迎随时联系。
本文工程项目地址: https://github.com/wang1xiang/create-vct
参考资料
[1]vite-pretty-lint: https://github.com/tzsk/vite-pretty-lint
[2]useRoutes: https://reactrouter.com/en/main/hooks/use-routes
[3]Redux Toolkit: https://cn.redux.js.org/redux-toolkit/overview/
[4]https://cangsdarm.github.io/react-query-web-i18n/react/: https://link.juejin.cn?target=https%3A%2F%2Fcangsdarm.github.io%2Freact-query-web-i18n%2Freact%2F
– EOF –
加主页君微信,不仅后端技能+1
主页君日常还会在个人微信分享后端合作开发学习资源和技术文章精选,不定期分享一些有意思的活动、岗位内推以及怎样用技术做业余工程项目
加个微信,打开一扇窗
推荐阅读 点击标题可跳转3、Vite2 + Vue3 + TypeScript + Pinia 构筑一套虚拟化的合作开发钢架
觉得本文对你有帮助?请分享给更多人
技能
点赞和在看就是最大的支持❤️