Skip to content

前端代码写的比较仓促,很多代码是可以优化的。

这节我们统一来优化一下:

路由守卫

登录之后,我们会在 localStorage 保存 access_token、refresh_token、user_info 的信息:

然后进入会议室列表页。

这些页面是登录后才能访问的。

但是现在没登录访问会议室列表页不会自动跳转登录页:

只有点击其他页面的时候,才会跳转登录页:

因为这些页面的接口需要需要登录,没登录访问会返回 401,我们在响应的拦截器里做了处理,如果返回 401 就跳转登录页:

但是只在接口返回 401 的时候做处理不够,我们应该在路由级别也做下判断,也就是路由守卫。

分析下 frontend_user 的现有路由,需要登录的这几个页面都会渲染 Index 组件:

所以我们在 Index 组件里做下判断就好了:

之前有从 localStorage 里取 user_info 设置 headPic 的逻辑,我们加个 else,跳转登录页就好了。

测试下:

现在没有 user_info 的时候,也就是没有登录的时候就会跳转登录页了。

同理,frontend_admin 项目也是这样改:

javascript
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

javascript
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 来做:

javascript
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 和上次有没有变化,没变就不会重新渲染。

那如果我加一个函数作为参数呢?

javascript
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 就可以解决这个问题,它只有依赖数组变的时候才会返回新的函数,否则一直返回同一个函数:

javascript
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 可以去掉。

案例代码上传了小册仓库:

frontend_user

frontend_admin

总结

这节我们做了一些前端代码优化。

之前只是做了接口返回 401 的时候跳转登录页的逻辑,现在又补充了路由守卫,在 Index 组件里读取 localStorage 的值,如果没有 user_info 就跳转登录页面。

然后分析了 React 里 memo 的作用以及和 useCallback、useMemo 的配合使用,之前我们单独使用并没有什么意义,可以去掉。

这样,前端代码就没有啥明显的问题了,还有细节上的优化大家可以自己去做。