一,项目搭建
安装脚手架CRA
- 使用
create-react-app
生成项目npx create-react-app 自定义项目名
- 进入根目录
cd 自定义项目名
- 启动项目
npm run start
调整项目目录结构
/src /assets 项目资源文件,比如,图片 等 /components 通用组件 /pages 页面 /store mobx 状态仓库 /utils 工具,比如,token、axios 的封装等 App.js 根组件 index.css 全局样式 index.js 项目入口
保留核心代码
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>
}
二、使用gitee管理项目
- 在项目根目录打开终端,并初始化
git
仓库(如果已经有了git
仓库,无需重复该步),命令:git init
- 添加项目内容到暂存区:
git add .
- 提交项目内容到仓库区:
git commit -m '项目初始化'
- 添加 remote 仓库地址:
git remote add origin [gitee 仓库地址]
- 将项目内容推送到 gitee:
git push origin master -u
三、使用scss预处理器
SASS
是一种预编译的 CSS
,作用类似于 Less
。由于 React
中内置了处理 SASS
的配置,所以,在 CRA
创建的项目中,可以直接使用 SASS
来写样式。
- 安装解析 sass 的包:
npm i sass -D
src根目录
创建全局样式文件:index.scss
body { margin: 0; } #root { height: 100%; }
四、配置路由
基础路由
- 安装路由:
npm i react-router-dom
- 在
pages
目录中创建两个路由测试文件夹:Login
、Layout
分别在创建的两个目录中创建
index.js
文件,并创建一个简单的组件后导出: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
- 在
App
组件中,导入路由组件以及两个页面组件 - 配置
Login
和Layout
的路由规则
`App.js`
```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/>}>
{/* 配置嵌套路由*/}
{/* 二级路由默认页面 */}
{/*<Route index element={<组件1/>} /> */}
{/*<Route path="article" element={<Article />} /> */}
</Route>
<Route path="/login" element={<Login/>}/>
</Routes>
</div>
</BrowserRouter>
)
}
export default App
```
在非组件环境下拿到路由信息
- 安装:
npm i history
创建
utils/history.js
文件utils/history.js
// https://github.com/remix-run/react-router/issues/8264 import { createBrowserHistory } from 'history' import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom' const history = createBrowserHistory() export { HistoryRouter, history }
在app.js中使用我们新建的路由并配置history参数
app.js
import { HistoryRouter, history } from './utils/history' function App() { return ( //HistoryRouter替换BrowserRouter <HistoryRouter history={history}> ...省略无关代码 </HistoryRouter> ) } export default App
使用案例
utils/http.js
import { history } from './history' http.interceptors.response.use( response => { return response.data }, error => { if (error.response.status === 401) { // 跳转到登录页 history.push('/login') } return Promise.reject(error) } )
路由懒加载
- 在
App
组件中,导入Suspense
组件 - 在 路由
Router
内部,使用Suspense
组件包裹组件内容 - 为
Suspense
组件提供fallback
属性,指定loading
占位内容 - 导入
lazy
函数,并修改为懒加载方式导入路由组件
App.js
import { Routes, Route } from 'react-router-dom'
import { HistoryRouter, history } from './utils/history'
// 导入必要组件
import { lazy, Suspense } from 'react'
// 按需导入路由组件
const Login = lazy(() => import('./pages/Login'))
const Layout = lazy(() => import('./pages/Layout'))
function App () {
return (
<HistoryRouter history={history}>
<Suspense
fallback={
<div
style={{
textAlign: 'center',
marginTop: 200
}}
>
loading...
</div>
}
>
<Routes>
<Route path="/" element={<Layout/>}>
{/* 配置嵌套路由*/}
{/* 二级路由默认页面 */}
{/*<Route index element={<组件1/>} /> */}
{/*<Route path="article" element={<Article />} /> */}
</Route>
<Route path="/login" element={<Login/>}/>
</Routes>
</Suspense>
</HistoryRouter>
)
}
export default App
五、组件库antd使用
- 安装
antd
组件库:npm i antd
全局导入
antd
组件库的样式src/index.js
:// 先导入 antd 样式文件 // https://github.com/ant-design/ant-design/issues/33327 import 'antd/dist/antd.min.css' // 再导入全局样式文件,防止样式覆盖! import './index.css'
- 导入
Button
组件进行测试 - 在
Login
页面渲染Button
组件进行测试
pages/Login/index.js
import { Button } from 'antd'
const Login = () => (
<div>
<Button type="primary">Button</Button>
</div>
)
注意
- 在
src/index.js
文件中导入 antd 的样式文件 antd
的样式文件和我们自己的全局样式文件的导入顺序
六、配置别名路径
安装@craco/craco
CRA
将所有工程化配置,都隐藏在了react-scripts
包中,所以项目中看不到任何配置信息如果要修改
CRA
的默认配置,有以下几种方案:
- 通过第三方库来修改,比如,
@craco/craco
(推荐)- 通过执行
yarn eject
命令,释放react-scripts
中的所有配置到项目中
- 安装修改 CRA 配置的包:
npm i -D @craco/craco
在项目根目录中创建 craco 的配置文件:
craco.config.js
,并在配置文件中配置路径别名craco.config.js
const path = require('path') module.exports = { // webpack 配置 webpack: { // 配置别名 alias: { // 约定:使用 @ 表示 src 文件所在路径 '@': path.resolve(__dirname, 'src') } } }
修改
package.json
中的脚本命令package.json
// 将 start/build/test 三个命令修改为 craco 方式 "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "react-scripts eject" }
- 在代码中,就可以通过
@
来表示 src 目录的绝对路径 - 重启项目,让配置生效
@别名路径提示
- 在项目根目录创建
jsconfig.json
配置文件 在配置文件中添加以下配置
{ "compilerOptions": { "baseUrl": "./", "paths": { "@/*": ["src/*"] } } }
vscode会自动读取jsconfig.json
中的配置,让vscode知道@
就是src
目录
七、安装dev-tools调试工具
八、封装axios工具模块
- 安装
axios
:npm i axios
创建
utils/http.js
文件utils/http.js
import axios from 'axios' const http = axios.create({ baseURL: '请求统一地址', timeout: 5000 //请求超时时间 }) // 添加请求拦截器 http.interceptors.request.use((config)=> { return config }, (error)=> { return Promise.reject(error) }) // 添加响应拦截器 http.interceptors.response.use((response)=> { // 2xx 范围内的状态码都会触发该函数。 // 对响应数据做点什么 return response }, (error)=> { // 超出 2xx 范围的状态码都会触发该函数。 // 对响应错误做点什么 return Promise.reject(error) }) export { http }
在
utils/index.js
中,统一导出http
utils/index.js
import { http } from './http' export { http }
九、使用mobx
模块化配置
- 安装
mobx
:npm i mobx mobx-react-lite
store
文件夹下创建单一模块store
例:store/use.Store.js
//用户模块
import { computed, makeAutoObservable } from "mobx";
class UserStore {
//定义数据
userinfo= [];
constructor() {
//响应式处理
makeAutoObservable(this, {
// 标记computed
fillterList: computed,
});
//如果没有计算属性直接:
//makeAutoObservable(this);
}
//get计算属性:computed,计算属性需要在makeAutoObservable里做一下标记
get fillterList() {
return this.userinfo.filter((item) => item.name==='ailjx');
}
addUse = () => {
this.userinfo.push({//...});
};
}
//导出
export default UserStore;
store
文件下创建index.js
统一导出store
store/index.js
import React from "react"; import UserStore from "./use.Store"; class RootStore { // 组合store constructor() { //对子模块进行实例化操作并赋值给RootStore对应的属性 //这样将来实例化RootStore的时候就可以通过对应的属性获取导入的对应子模块的实例对象 this.userStore= new UserStore(); //多个模块按照上述语法补充... } } //实例化根store注入context const rootStore = new RootStore(); //使用React的useContext机制 导出useStore方法,供业务组件统一使用 //useContext查找机制:优先从Provider value找,如果找不到,就会找createContext方法传递过来的默认参数 //核心目的:让每个业务组件可以通过统一一样的方法获取store的数据 const context = React.createContext(rootStore); //通过useContext拿到rootStore实例对象,然后返回给useStore //导出useStore方法,供组件通过调用该方法使用根实例 //在业务组件中 调用useStore()->rootStore const useStore = () => React.useContext(context); export { useStore }; //以上是模板代码,在不同项目都通用
组件中使用mobx
例如:在Login
组件中使用
import { useStore } from '@/store'
const Login = () => {
//解构出useStore模块
const { useStore} = useStore()
//调用useStore模块的addUse方法
useStore.addUse ()
return (...)
}
十、项目本地预览
项目经过打包过后,往往需要本地预览服务端运行时的效果:
- 全局安装本地服务包
npm i -g serve
该包提供了serve
命令,用来启动本地服务 - 在项目根目录中执行命令
serve -s ./build
在build目录中开启服务器 - 在浏览器中访问:
http://localhost:3000/
预览项目
十一、 打包体积分析
- 安装分析打包体积的包:
npm i source-map-explorer
在
package.json
中的scripts
标签中,添加分析打包体积的命令package.json
中:"scripts": { "analyze": "source-map-explorer 'build/static/js/*.js'", }
- 对项目打包:
npm run build
(如果已经打过包,可省略这一步) - 运行分析命令:
npm run 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,配和public/index.html中配置使用
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.options.cdn.js.forEach(cdnURL=> { %>
<script src="<%= cdnURL %>"></script>
<% }) %>
</body>