「记录篇」我是如何一步步为公司搭建react项目脚手架的

2023-09-06 0 417

译者: 翔子丶

https://juejin.cn/post/7235547967112806437

 create-vct

这是两个用作调用 vite + React 虚拟化工程项目的钢架辅助工具。

🔥 运转业务流程

「记录篇」我是如何一步步为公司搭建react项目脚手架的

create-vct-flow.gif

🎁 加装 & 采用

npx create-vct

# or

npm 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 的修正

「记录篇」我是如何一步步为公司搭建react项目脚手架的create-vite-load.png

删除不需要的模块

因为他们只需要 react 的模板,所以把其他不需要的一并删除掉,最后只留下这些文件。

「记录篇」我是如何一步步为公司搭建react项目脚手架的create-vite-delete.png

修正模板 vite.config.ts 代码

配置 alias 添加别名设置

配置 server 代理服务器

// vite.config.ts

export default

 defineConfig({

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 default

 defineConfig({

  …

  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.scssconst

cx = classNames.bind(styles)

const App = () =>

 {

  return <div className={cx(btnbtn-primary)}></div>

}

标准化代码 & git 规范化

「记录篇」我是如何一步步为公司搭建react项目脚手架的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 都配置完成了。

「记录篇」我是如何一步步为公司搭建react项目脚手架的husky-error.png

这一步完成后,他们与此同时配置了代码规范化和 git 规范化,添加了 husky,所以需要在工程项目创建完成后,首先执行一下 git init 调用 git 仓库,然后 husky 才能正常运转,于是就把提示信息多加了一项 🤔,如下:

「记录篇」我是如何一步步为公司搭建react项目脚手架的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`

  )

  const

 packageList = { …commonPackages, …packages }

  const

eslintConfigOverrides = […eslintConfig.overrides, …eslintOverrides]

  const eslint = { …eslintConfig, overrides

: eslintConfigOverrides }

  const viteConfigFiles = [vite.config.jsvite.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, null2

))

fs.writeFileSync(prettierFile,JSON.stringify(prettierConfig, null2

))

fs.writeFileSync(eslintIgnoreFile, eslintIgnore.join(\n

))

  fs.writeFileSync(viteFile, viteConfig)

  const

 files = 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, null2) + \n

)

}

这一步会将.husky、.editorconfig、commitlint.config.js、.eslintrc.json、.prettierrc.json和.eslintignore配置添加到所创建的工程项目中,并修正 package.json 文件,添加 ESLint 的依赖项。

集成 ant design 作为 UI 库

「记录篇」我是如何一步步为公司搭建react项目脚手架的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, null2) + \n

)

}

集成 react-router 作为路由

「记录篇」我是如何一步步为公司搭建react项目脚手架的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 的 component

react-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

: [

      {

        pathhome

,

        element<Home />

,

        meta

: {

          titleHome

,

        },

      },

      {

        pathtoolkit

,

        element<ReduxToolkitDemo />

,

        meta

: {

          titleReact Toolkit

,

        },

      },

      {

        pathquery

,

        element<ReactQueryDemo />

,

        meta

: {

          titleReact Query

,

        },

      },

    ],

  },

    ]

    export default

 routers

创建 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}>{elements}

}

export default App

在 main.tsx 中配置包裹 App 组件 import { BrowserRouter } from react-router-dom

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, null2) + \n

)

}

集成 Redux toolkit 作为状态管理

「记录篇」我是如何一步步为公司搭建react项目脚手架的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 const

 store = configureStore({

  // feature中创建多个子reducer 最终在这里进行合并

  reducer: {

    app: appReducer,

},

  // 中间件  middleware: (getDefaultMiddleware) =>

    getDefaultMiddleware({

      serializableCheck: false

,

    }),

  // redux-devtools-extension何时开启  devTools: process.env.NODE_ENV !== production

,

    })

    export type AppDispatch = typeof

store.dispatch

    export type RootState = ReturnType<typeof

 store.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()

    export const

 useAppSelector: TypedUseSelectorHook = useSelector

将 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 interface

 UserState {

  users: UserType[]

  total: number

}

const

 initialState: UserState = {

  users: [],

  total: 0

,

}

// createAsyncThunk创建两个异步action,return出去的值,会在extraReducers中接收,有三种状态:// pending(进行中)、fulfilled(成功)、rejected(失败)export const getUserData = createAsyncThunk(user/getListasync

 () => {

  const res = await fetch(https://api.github.com/search/users?q=wang

).then(

    (res) =>

 res.json()

  )

  return

 res

})

// 创建两个 Sliceexport const

 userSlice = 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 default

 userSlice.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, null2) + \n

)

}

集成 react-query 作为请求库

「记录篇」我是如何一步步为公司搭建react项目脚手架的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    // 标准化配置    const

 baseURL = 

    export const

 service = Axios.create({

  baseURL,

  responseType: json

,

  timeout: 600000

,

    })

    // 请求拦截service.interceptors.request.use((res: any) =>

 {

  res.headers.token = token  return

 res

    })

    // 拦截响应

service.interceptors.response.use(

  (response: any) =>

 {

    const

 res = response.data

    // 这块需要修正 根据请求返回成功标志    if

 (res || res.success) {

      if(res.message) message.success(res.message,3

)

      return

 response

    } 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 = anyQ = any>(

  req: AxiosRequestConfig & {

    data?: Q

  }

    

{

  return

 service(req).then(

    (res) =>

 {

      return res.data as

 resData

    },

    (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({

url:`/api/search/users?q=${searchName}`

,

    method: get

,

  })

「记录篇」我是如何一步步为公司搭建react项目脚手架的

axios-ts-resdata.gif

query.client.ts 用作调用 react query,导出 client 会在 QueryClientProvider 中进行透传。

    import { QueryClient } from react-query    const client = new

QueryClient({})

    // client.setQueryDefaults(QUERY_CHAT_TRANSLATE_INPUT, {    //   select: (res) => res.data,    //   enabled: false,    // });    export default

 client

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 &&

loading…
}

{users?.map((user: UserType) => (

  • {user.login}
  • ))}

    )

    }

    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, null2) + \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。

    「记录篇」我是如何一步步为公司搭建react项目脚手架的package-bin.png

    bin 里的命令对应的是两个可执行的文件,通过软链接或者符号链接到指定资源的映射,这些可执行文件必须以 #!/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 –

    「记录篇」我是如何一步步为公司搭建react项目脚手架的

    加主页君微信,不仅后端技能+1

    「记录篇」我是如何一步步为公司搭建react项目脚手架的「记录篇」我是如何一步步为公司搭建react项目脚手架的

    主页君日常还会在个人微信分享后端合作开发学习资源技术文章精选,不定期分享一些有意思的活动岗位内推以及怎样用技术做业余工程项目

    「记录篇」我是如何一步步为公司搭建react项目脚手架的

    加个微信,打开一扇窗

    推荐阅读  点击标题可跳转

    1、「后端工程化」从0-1构筑 React,TS 钢架

    2、React:他们的用法习惯可能是错误的

    3、Vite2 + Vue3 + TypeScript + Pinia 构筑一套虚拟化的合作开发钢架

    觉得本文对你有帮助?请分享给更多人

    技能

    点赞和在看就是最大的支持❤️

    相关文章

    发表评论
    暂无评论
    官方客服团队

    为您解决烦忧 - 24小时在线 专业服务