前端代码写的比较仓促,很多代码是可以优化的。
这节我们统一来优化一下:
路由守卫
登录之后,我们会在 localStorage 保存 access_token、refresh_token、user_info 的信息:
然后进入会议室列表页。
这些页面是登录后才能访问的。
但是现在没登录访问会议室列表页不会自动跳转登录页:
只有点击其他页面的时候,才会跳转登录页:
因为这些页面的接口需要需要登录,没登录访问会返回 401,我们在响应的拦截器里做了处理,如果返回 401 就跳转登录页:
但是只在接口返回 401 的时候做处理不够,我们应该在路由级别也做下判断,也就是路由守卫。
分析下 frontend_user 的现有路由,需要登录的这几个页面都会渲染 Index 组件:
所以我们在 Index 组件里做下判断就好了:
之前有从 localStorage 里取 user_info 设置 headPic 的逻辑,我们加个 else,跳转登录页就好了。
测试下:
现在没有 user_info 的时候,也就是没有登录的时候就会跳转登录页了。
同理,frontend_admin 项目也是这样改:
useEffect(()=> {
const userInfo = localStorage.getItem('user_info');
if(!userInfo) {
window.location.href = "/login";
}
}, []);
测试下:
没登录的时候,也同样跳转了登录页。
useCallback、useMemo
前面我们代码里加了很多 useMemo 和 useCallback,其实这是没必要的。
我们创建个测试项目:
npx create-vite test-memo
进入项目,安装依赖,然后把开发服务跑起来:
npm install
npm run dev
去掉 main.tsx 里的 index.css 和 StrictMode:
然后改下 App.tsx
import { useEffect, useState } from 'react'
function Aaa(props: { text: string }) {
console.log('Aaa render...');
return <div>{props.text}</div>
}
function App() {
const [num, setNum] = useState(0);
useEffect(() => {
setInterval(() => {
setNum(num => num + 1);
}, 1000);
}, []);
return <Aaa text="aaa"></Aaa>
}
export default App
App 组件里有一个定时器,不断的修改 setState 来触发重新渲染。
这时候 Aaa 会不会重新渲染呢?
答案是每次都会重新渲染。
很明显这里 Aaa 不需要重新渲染,因为参数没变。
这时候就可以用 memo 来做:
import { memo, useEffect, useState } from 'react'
function Aaa(props: { text: string }) {
console.log('Aaa render...');
return <div>{props.text}</div>
}
const MemoAaa = memo(Aaa);
function App() {
const [num, setNum] = useState(0);
useEffect(() => {
setInterval(() => {
setNum(num => num + 1);
}, 1000);
}, []);
return <MemoAaa text="aaa"></MemoAaa>
}
export default App
现在 Aaa 就只会渲染一次了:
memo 里会对比 props 和上次有没有变化,没变就不会重新渲染。
那如果我加一个函数作为参数呢?
import { memo, useEffect, useState } from 'react'
function Aaa(props: { text: string, callback: Function }) {
console.log('Aaa render...');
return <div>{props.text}</div>
}
const MemoAaa = memo(Aaa);
function App() {
const [num, setNum] = useState(0);
useEffect(() => {
setInterval(() => {
setNum(num => num + 1);
}, 1000);
}, []);
return <MemoAaa text="aaa" callback={() => { console.log('xxx');}}></MemoAaa>
}
export default App
这时候每次都会重新渲染:
因为函数每次都变,也就是参数每次都变,加了 memo 也没用。
这时候用 useCallback 就可以解决这个问题,它只有依赖数组变的时候才会返回新的函数,否则一直返回同一个函数:
import { memo, useCallback, useEffect, useState } from 'react'
function Aaa(props: { text: string, callback: Function }) {
console.log('Aaa render...');
return <div>{props.text}</div>
}
const MemoAaa = memo(Aaa);
function App() {
const [num, setNum] = useState(0);
useEffect(() => {
setInterval(() => {
setNum(num => num + 1);
}, 1000);
}, []);
const callback = useCallback(() => {
console.log('xxx');
}, []);
return <MemoAaa text="aaa" callback={callback}></MemoAaa>
}
export default App
现在又只渲染一次了:
所以说,memo 和 useCallback、useMemo 是配合使用的,我们之前单独用 useCallback 没什么意义。
代码里的 useCallback 可以去掉。
案例代码上传了小册仓库:
总结
这节我们做了一些前端代码优化。
之前只是做了接口返回 401 的时候跳转登录页的逻辑,现在又补充了路由守卫,在 Index 组件里读取 localStorage 的值,如果没有 user_info 就跳转登录页面。
然后分析了 React 里 memo 的作用以及和 useCallback、useMemo 的配合使用,之前我们单独使用并没有什么意义,可以去掉。
这样,前端代码就没有啥明显的问题了,还有细节上的优化大家可以自己去做。