工作中写React 学到了什么

10/8/2021 React

# 一、前言

工作过程中的技术栈主要是 React + React Router / React + Umijs ,会有 class 和 hooks 混用的情况。(hooks化不彻底)

这篇文章主要总结在开发中遇到的一些问题和解决方法

代码都是简化后的代码,不代表生产环境,但是其思想是相通的。

# 二、按需加载

一个中大型项目打包后会生成5M~20M的单个js文件

网络环境缓慢时,加载这种资源会显得非常卡,体验不好

React 提供了一个按需加载API,把部分路由(目录)抽离成多个小文件,再通过配置 webpack 打包,即可实现按需加载功能

  • React 版本 > 16.6

使用 react.lazy

import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import React, { Suspense, lazy } from "react";

const Program1 = lazy(() => import("./Program1"));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route path="/program1" component={Program1} />
      </Switch>
    </Suspense>
  </Router>
);

配置 webpack 4+版本

...
  output: {
    path: path.resolve(root, 'dist/dev'),
    publicPath: '/',
    filename: 'index.[hash:10].js',
    chunkFilename: '[name].[chunkhash:10].chunk.js',
  },
...

webpack and react-router按需加载 (opens new window)

# 三、终止请求和 时序控制CAS

# 3.1、终止请求

XHR、Fetch、Axios终止请求方式 (opens new window)

结合 React 封装一个 useFetch 的 hook:

export function useFetch = (config, deps) => {
  const abortController = new AbortController()
  const [loading, setLoading] = useState(false)
  const [result, setResult] = useState()

  useEffect(() => {
    setLoading(true)
    fetch({
      ...config,
      signal: abortController.signal
    })
      .then((res) => setResult(res))
      .finally(() => setLoading(false))
  }, deps)

  useEffect(() => {
    return () => abortController.abort()
  }, [])

  return { result, loading }
}

# 3.2、时序控制 CAS

页面中请求同一接口三次,顺序分别是 A B C
响应顺序是 A C B ,B 把 C 的结果给覆盖了,显然时有问题的

CAS(CompareAndSet),先比较在赋值,更偏向于后端的概念,但是前端也能用得上。

方法就是前端请求接口时,维护一个字段比如 { cas: 1633681618183 }
后端原样返回该字段,前端响应接口时比较一下,再存入 Store


并发扣款,如何保证数据的一致性? (opens new window)
并发扣款一致性优化,CAS下ABA问题 (opens new window)

# 四、以 URL 为数据仓库

在开发项目中,经常会涉及浏览器回退、分享等操作。那么保留「页面状态」就非常重要了。
如用户筛选某些状态,而后退回到该页面时,状态值需要保留的化,状态和 URL 同步尤为重要。

在传统状态管理思路中,常用 redux、recoil 等库去做一系列数据管理,如果把 URL 的 query 部分想象成数据仓库,是不是更合适呢?

尝试封装一下 react-router

export function useQuery() {
  const history = useHistory();
  const { search, pathname } = useLocation();
  // 保存query状态
  const queryState = useRef(qs.parse(search));
  // 设置query
  const setQuery = handler => {
    const nextQuery = handler(queryState.current);
    queryState.current = nextQuery;
    // replace会使组件重新渲染
    history.replace({
      pathname: pathname,
      search: qs.stringify(nextQuery),
    });
  };
  return [queryState.current, setQuery];
}

在组件中,这样使用

const [query, setQuery] = useQuery();

// 接口请求依赖 page 和 size
useEffect(() => {
  api.getUsers();
}, [query.page, query, size]);

// 分页改变 触发接口重新请求
const onPageChange = page => {
  setQuery(prevQuery => ({
    ...prevQuery,
    page,
  }));
};





我在工作中写React 学到了什么? (opens new window)

Last Updated: 10/8/2021, 5:03:31 PM