Skip to content

项目搭建

项目规范

  1. 文件夹、文件名称统一小写、多个单词以连接符(-)连接
  2. JavaScript 变量名称采用小驼峰标识,常量全部使用大写字母,组件采用大驼峰
  3. CSS 采用普通 CSS 和 styled-component 结合来编写(全局采用普通 CSS、局部采用 styled-component)
  4. 整个项目不再使用 class 组件,统一使用函数式组件,并且全面拥抱 Hooks
  5. 所有的函数式组件,为了避免不必要的渲染,全部使用 memo 进行包裹
  6. 组件内部的状态,使用 useState、useReducer 业务数据全部放在 redux 中管理
  7. 函数组件内部基本按照如下顺序编写代码:
    • 组件内部 state 管理
    • redux 的 hooks 代码
    • 其他 hooks 相关代码(比如自定义 hooks)
    • 其他逻辑代码
    • 返回 JSX 代码
  8. redux 代码规范如下:
    • redux 目前我们学习了两种模式,在项目实战中尽量两个都用起来,都需要掌握
    • 每个模块有自己独立的 reducer 或者 slice,之后合并在一起;
    • redux 中会存在共享的状态、从服务器获取到的数据状态;
  9. 网络请求采用 axios
    • 对 axios 进行二次封装
    • 所有的模块请求会放到一个请求文件中单独管理
  10. 项目使用 AntDesign、MUI(Material UI)
    • 爱彼迎本身的设计风格更多偏向于 Material UI,但是课程中也会尽量讲到 AntDesign 的使用方法
    • 项目中某些 AntDesign、MUI 中的组件会被拿过来使用
    • 但是多部分组件还是自己进行编写、封装、实现

craco 配置别名

react 脚手架隐藏了 webpack 配置

  • 方法一:npm run eject
  • 方法二:craco -> create-react-app config
bash
$ npm install @craco/craco@alpha -D
$ npm install craco-less@2.1.0-alpha.0

craco.config.js

js
const path = require('path')
const CracoLessPlugin = require('craco-less')

const resolve = pathname => path.resolve(__dirname, pathname)

module.exports = {
  webpack: {
    alias: {
      '@': resolve('src'),
      components: resolve('src/components'),
      utils: resolve('src/utils')
    }
  },
  plugins: [
    {
      plugin: CracoLessPlugin
    }
  ]
}

CSS 样式的重置

css
body, button, dd, dl, dt, form, h1, h2, h3, h4, h5, h6, hr, input, li, ol, p, pre, td, textarea, th, ul {
  padding: 0;
  margin: 0;
}
a {
  color: #484848;
  text-decoration: none;
}
img {
  vertical-align: top;
}
ul, li {
  list-style: none;
}

Router 配置

bash
$ npm i react-router-dom

router/index.jsx

jsx
import React from 'react'
import { Navigate } from 'react-router-dom'

const Home = React.lazy(() => import('@/views/home'))
const Entire = React.lazy(() => import('@/views/entire'))
const Detail = React.lazy(() => import('@/views/detail'))

const routes = [
  {
    path: '/',
    element: <Navigate to='home' />
  },
  {
    path: '/home',
    element: <Home />
  },
  {
    path: '/entire',
    element: <Entire />
  },
  {
    path: '/detail',
    element: <Detail />
  }
]

App.jsx

jsx
import React, { memo } from 'react'
import { useRoutes } from 'react-router-dom'
import routes from './router'

const App = memo(() => {
  return (
    <div className='app'>
      <div className='header'>header</div>
      <div className='page'>{useRoutes(routes)}</div>
      <div className='footer'>footer</div>
    </div>
  )
})

index.js

jsx
import React, { Suspense } from 'react'
import ReactDOM from 'react-dom/client'
import App from '@/App'
import { HashRouter } from 'react-router-dom'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Suspense fallback='loading'>
    <HashRouter>
      <App />
    </HashRouter>
  </Suspense>
)

Redux 配置

bash
$ npm i @reduxjs/toolkit react-redux

index.js

jsx
import React, { Suspense } from 'react'
import ReactDOM from 'react-dom/client'
import App from '@/App'
import { HashRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Suspense fallback='loading'>
    <Provider store={store}>
      <HashRouter>
        <App />
      </HashRouter>
    </Provider>
  </Suspense>
)

store/index.js

js
import { configureStore } from '@reduxjs/toolkit'
import homeReducer from './modules/home'
import entireReducer from './modules/entire'

const store = configureStore({
  reducer: {
    home: homeReducer,
    entire: entireReducer
  }
})

store/modules/home.js

js
import { createSlice } from '@reduxjs/toolkit'

const homeSlice = createSlice({
  name: 'home',
  initialState: {
    productList: []
  },
  reducers: {}
})

export default homeSlice.reducer

store/modules/entire.js

js
const initialState = {
  currentPage: 0
}

function reducer(state = initialState, action) {
  switch (action.type) {
    default:
      return state
  }

export default reducer

Axios 配置

bash
$ npm i axios

Ui 配置

bash
$ npm install @mui/material @mui/styled-engine-sc styled-components @emotion/react @emotion/styled
bash
$ npm i normalize.css antd
$ npm i classnames

项目中的坑

useState 传入的初始值只有再组件第一次被渲染时才是有效的

image-20230104104139046

解决思路:

  1. 数据没值的时候不进行渲染
  2. 使用 useEffect 监听 name 变化,变化后进行 setName,但是这样页面会渲染 3 次,不是很好

常备不懈,才能有备无患