React简单项目,熟悉项目大致完整流程

该文详细介绍了如何搭建一个基于React、MobX和react-router-dom的前端项目。从创建项目、目录结构规划、使用AntDesign组件库,到配置路由、状态管理和别名路径。文章还涉及了封装axios、登录状态管理、路由鉴权、项目打包分析以及CDN优化和路由懒加载等性能提升策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

准备:基本的前端环境,nodejs等 ,编辑器采用vscode、技术采用react、状态管理用mobx、路由react-router-dom

创建
生成项目: npx create-react-app project_demo
进入项目目录:cd project_demo
启动项目: yarn start
调整目录
    /public
    /src
        /asserts        //静态资源文件
        /components     //通用组件
        /hooks          //封装的钩子函数
        /pages          //页面
        /store          //mobx状态仓库
        /styles         //样式文件
        /utils          //工具类文件夹:token、anxios、Info等
        app.js          //根组件
        index.js        //项目入口
        index.css       //全局样式
核心代码保留

src/index.js文件

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
)

src/App.js

export default function App() {
  return <div>根组件</div>
}
使用scss预处理器
//安装sass
yarn add sass -D    //=>D表示只在dev环境下生效

创建全局样式文件index.scss

body{
    margin:0;
}
#root{
    height:100%
}
创建几个相关页面:例:登录、布局

pages/Login/index.js

const Login = () => {
  return <div>login</div>
}
export default Login

pages/Layout/index.js

const Layout = () => {
  return <div>layout</div>
}
export default Layout
创建基础路由
//安装路由
yarn add react-router-dom
//配置基础路由,app.js文件
// 导入路由
import { BrowserRouter, Route, Routes } from 'react-router-dom'

// 导入页面组件
import Login from './pages/Login'
import Layout from './pages/Layout'

// 配置路由规则
function App() {
  return (
    <BrowserRouter>    //浏览器路由最外层包裹
      <div className="App">
       <Routes>        //路由外层
            <Route path="/" element={<Layout/>}/>    //path:路由地址,element:路由地址对应的页面    
            <Route path="/login" element={<Login/>}/>
        </Routes>
      </div>
    </BrowserRouter>
  )
}

export default App
使用antd组件库
//安装
yarn add antd
//src/index.js中引入   *=>    新版本中,似乎可以不用在此处引入也同样可以使用,如需引入则是按照如下
import 'antd/dist/antd.mis.css
import './index.css'

//然后直接再页面中,再引入想要使用的组件即可。     例:
import { Button } from 'antd'
配置别名路径(通过第三方库Craco)

作用:可以通过配置@来简化路径

//安装
yarn add -D @craco/craco
//项目根目录创建:craco.config.js,并配置路径别名
const path = require('path')

module.exports = {
  // webpack 配置
  webpack: {
    // 配置别名
    alias: {
      // 约定:使用 @ 表示 src 文件所在路径
      '@': path.resolve(__dirname, 'src')
    }
  }
}

//修改package.json脚本命令
// 将 start/build/test 三个命令修改为 craco 方式
"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  "eject": "react-scripts eject"
}

//此时,vscode在输入@的时候还不会显示提示,所以此时需要船舰jsconfig.json,并添加如下配置
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]        //=>告诉编辑器@在路径中的意思
    }
  }
}

...//省略页面编写代码

封装anxios

utils/request.js

import axios from 'axios'

const request = axios.create({
  baseURL: ' ',    //服务地址
  timeout: 5000
})
// 添加请求拦截器
request.interceptors.request.use((config)=> {
    return config
  }, (error)=> {
    return Promise.reject(error)
})

// 添加响应拦截器
request.interceptors.response.use((response)=> {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response
  }, (error)=> {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error)
})

export { request }
通过utils中统一管理

utils/index.js

import {request} from './request';
export { request }
配置登录mobx(LoginStore)

/store/login.Store.js

import { removeToken, request, setToken } from "@/utils"
import { makeAutoObservable } from "mobx"


class LoginStore {
  token = this.getToken || ''
  constructor() {
    makeAutoObservable(this)
  }

  //getToken
  login = async ({ mobile, code }) => {
    //调用登录接口、存入token
    const res = await request.post('/api', {
      mobile, code
    })
    console.log(res)
    this.token = res.data
    setToken(this.token)
  }

  loginOut = () => {
    removeToken()
  }
}

export default LoginStore
统一管理

创建store/index.js文件

import React from "react"
import LoginStore from './login.Store'

class RootStore {
  // 组合模块
  constructor() {
    this.loginStore = new LoginStore()
  }
}
// 导入useStore方法供组件使用数据
const StoresContext = React.createContext(new RootStore())
export const useStore = () => React.useContext(StoresContext)
使token持久化

封装utils/token.js

const TOKEN_KEY = 'geek_pc'

const getToken = () => localStorage.getItem(TOKEN_KEY)
const setToken = token => localStorage.setItem(TOKEN_KEY, token)
const clearToken = () => localStorage.removeItem(TOKEN_KEY)

export { getToken, setToken, clearToken }

在调用登录接口的同时,将token设置为接口返回的token数据调用token.js里的setToken

请求拦截器注入token *

将token通过请求拦截器注入到token请求头中

utils/request.js    => axios(此处注入token) =>    .....

//通过config.headers.Authorization = Bearer ${token} 拼接上去

//utils/request.js
http.interceptors.request.use(config => {
  // if not login add token
  const token = getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})
路由鉴权

实现非登录用户过滤拦截并跳转到登录页面

封装组件components/AuthRoute/index.js

// 1. 判断token是否存在
// 2. 如果存在 直接正常渲染
// 3. 如果不存在 重定向到登录路由

// 高阶组件:把一个组件当成另外一个组件的参数传入 然后通过一定的判断 返回新的组件
import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'

function AuthRoute ({ children }) {
  const isToken = getToken()
  if (isToken) {
    return <>{children}</>
  } else {
    return <Navigate to="/login" replace />
  }
}

// <AuthComponent> <Layout/> </AuthComponent>
// 登录:<><Layout/></>
// 非登录:<Navigate to="/login" replace />

export { AuthRoute }

src/App.js

import { Router, Route } from 'react-router-dom'
import { AuthRoute } from '@/components/AuthRoute'
import Layout from '@/pages/Layout'
import Login from '@/pages/Login'

function App() {
  return (
    <Router>
      <Routes>
          {/* 需要鉴权的路由 */}
          <Route path="/*" element={
            <AuthRoute>
              <Layout />
            </AuthRoute>
          } />
          {/* 不需要鉴权的路由 */}
          <Route path='/login' element={<Login />} />
       </Routes>
    </Router>
  )
}
export default App

退出登录后,需要清除token

...

项目打包
1、yarn build     =>打包生产的内容会放在根下的build文件夹中
2、npm i -g serve    =>提供serve命令,启动打包后的本地服务
  serve -s ./build    =>在build目录中启动
3、http://localhost:3000
打包体积分析
1、yarn add source-map-explorer    =>安装分析打包体积的包
2、在package.json文件的scripts标签中添加
        "scripts": {
          "analyze": "source-map-explorer 'build/static/js/*.js'",
        }
3、项目打包:yarn build
4、运行分析命令:yarn analyze即可在浏览器中看到包大小
优化配置CDN

通过craco修改webpack配置,实现CDN优化

craco.config.js

// 添加自定义对于webpack的配置

const path = require('path')
const { whenProd, getPlugin, pluginByName } = require('@craco/craco')

module.exports = {
  // webpack 配置
  webpack: {
    // 配置别名
    alias: {
      // 约定:使用 @ 表示 src 文件所在路径
      '@': path.resolve(__dirname, 'src')
    },
    // 配置webpack
    // 配置CDN
    configure: (webpackConfig) => {
      // webpackConfig自动注入的webpack配置对象
      // 可以在这个函数中对它进行详细的自定义配置
      // 只要最后return出去就行
      let cdn = {
        js: [],
        css: []
      }
      // 只有生产环境才配置
      whenProd(() => {
        // key:需要不参与打包的具体的包
        // value: cdn文件中 挂载于全局的变量名称 为了替换之前在开发环境下
        // 通过import 导入的 react / react-dom
        webpackConfig.externals = {
          react: 'React',
          'react-dom': 'ReactDOM'
        }
        // 配置现成的cdn 资源数组 现在是公共为了测试
        // 实际开发的时候 用公司自己花钱买的cdn服务器
        cdn = {
          js: [
            'https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js',
            'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js',
          ],
          css: []
        }
      })

      // 都是为了将来配置 htmlWebpackPlugin插件 将来在public/index.html注入
      // cdn资源数组时 准备好的一些现成的资源
      const { isFound, match } = getPlugin(
        webpackConfig,
        pluginByName('HtmlWebpackPlugin')
      )

      if (isFound) {
        // 找到了HtmlWebpackPlugin的插件
        match.userOptions.cdn = cdn
      }

      return webpackConfig
    }
  }
}

同时需要在public/index.html中加入如下代码

<body>
  <div id="root"></div>
  <!-- 加载第三发包的 CDN 链接 -->
  <% htmlWebpackPlugin.userOptions.cdn.js.forEach(cdnURL => { %>
    <script src="<%= cdnURL %>"></script>
  <% }) %>
</body>
路由懒加载(按需加载)

App导入Suspense组件 => Router内部使用Suspense包裹Routes组件 => 为Suspense提供fallback属性,指定loading => 导入laze函数,改为懒加载方式导入路由

app.js

import { Routes, Route } from 'react-router-dom'
import { HistoryRouter, history } from './utils/history'
import { AuthRoute } from './components/AuthRoute'

// 导入必要组件
import { lazy, Suspense } from 'react'
// 按需导入路由组件
const Login = lazy(() => import('./pages/Login'))
const Layout = lazy(() => import('./pages/Layout'))
const Home = lazy(() => import('./pages/Home'))
const Article = lazy(() => import('./pages/Article'))
const Publish = lazy(() => import('./pages/Publish'))

function App () {
  return (
    <HistoryRouter history={history}>
      <Suspense
        fallback={
          <div
            style={{
              textAlign: 'center',
              marginTop: 200
            }}
          >
            loading...
          </div>
        }
      >
        <Routes>
          {/* 需要鉴权的路由 */}
          <Route path="/" element={
            <AuthRoute>
              <Layout />
            </AuthRoute>
          }>
            {/* 二级路由默认页面 */}
            <Route index element={<Home />} />
            <Route path="article" element={<Article />} />
            <Route path="publish" element={<Publish />} />
          </Route>
          {/* 不需要鉴权的路由 */}
          <Route path='/login' element={<Login />} />
        </Routes>
      </Suspense>
    </HistoryRouter>
  )
}

export default App
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值