9

56 个 NPM 包解决 16 个 React 问题

 3 years ago
source link: https://segmentfault.com/a/1190000040569553
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于如何选择完美的 npm 包的深入指南。

React 是用于构建用户界面的 JavaScript 库,它不仅是一个前端 UI 开发框架,更是一套完整的前端开发生态体系。

虽然 React 没有包含所有的解决方案,但是我们可以从繁荣的生态系统中找到应对不同场景的 NPM 包,来解决开发中遇到的问题。

今天,我们就从以下 16 个纬度着手,寻找最佳解决方案。

1. 全局状态管理

在 99% 的应用程序中,组件之间共享状态是强制性的,并且有一些很好的本地和外部解决方案。

如果你问我一种解决方案,我会说 Redux,不是因为他是最好的,而是因为它是最实用的。许多公司已经在使用它,意味着您也不得不在某个时刻使用它。

以前我们使用 Redux,通常是指 Redux + React Redux 组合方案,但是现在有了更简化的方案:Redux Toolkit + React Redux,它帮助我们避免了 Redux 的三个常见问题:

  1. "配置一个 Redux 存储太复杂了"
  2. "我必须添加很多包才能让 Redux 做任何有用的事情"
  3. "Redux 需要太多样板代码"

Redux Toolkit 简化了编写 Redux 逻辑和设置 store 的过程,允许我们编写更容易阅读的更短的逻辑,同时仍然遵循相同的 Redux 行为和数据流。

# 安装 react-toolkit(方式一)
$ npm install @reduxjs/toolkit --save
# or 
$ yarn add @reduxjs/toolkit

# 还可以通过脚手架的 redux 模版安装使用(方式二)
# Redux + Plain JS template
$ npx create-react-app my-app --template redux

# Redux + TypeScript template
$ npx create-react-app my-app --template redux-typescript

示例代码:

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    incremented: state => {
      state.value += 1
    },
    decremented: state => {
      state.value -= 1
    }
  }
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
  reducer: counterSlice.reducer
})

store.subscribe(() => console.log(store.getState()))
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

如果您还是使用 Redux + React Redux 组合方案,Redux 社区也还提供了很多中间件来简化各种场景。

  1. redux-thunk - 用于处理异步动作(与 redux 使用);
  2. redux-persist - 用于本地存储数据(离线支持);
  3. reselect - 用于更快的查询存储;
$ npm install redux react-redux redux-thunk redux-persist reselect --save
  • context - 内置与 React,适合简单使用,不利于性能,特别是如果您有大量变化的数据。
  • recoil - 旨在解决特定问题,现在还是实验状态,精准更新、下一代状态管理方案。
  • jotai - 简约的 API、没有字符串键、面向 TypeScript,与 react-spring 同属于 Poimandres 。
  • mobx - 遵循观察者模式,适合响应式编程(reactive programming),推荐中小型项目使用。

2. 服务器状态管理

如果您的应用程序严重依赖某些外部数据源,那么管理该数据(缓存、更新等)对于性能至关重要。

React Query 将帮助你获取、同步、更新和缓存你的远程数据, 提供两个简单的 hooks,就能完成增删改查等操作。

它处理 caching 陈旧的数据,以及更多开箱即用的东西,它简单、强大且可配置。

# 安装
$ npm i react-query --save
# or
$ yarn add react-query

基本功能概览:

  1. 传输/协议/后端不可知的数据获取(REST、GraphQL、promise等等);
  2. 自动缓存+重新取回(过期时重新验证,窗口重新聚焦,轮询/实时);
  3. 并行 + 依赖查询;
  4. 突变 + 反应式查询重取;
  5. 多层缓存 + 自动垃圾收集;
  6. 分页 + 基于游标的查询;
  7. 加载更多 + 无限滚动查询/滚动恢复;
  8. 请求取消;
  9. React Suspense + Fetch-As-You-Render 查询预取;
  10. 专用的 Devtools。

简单示例(全局配置):

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

import { QueryClient, QueryClientProvider } from 'react-query'

const queryClient = new QueryClient()

ReactDOM.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
  document.getElementById('root')
)
// QueryExample.jsx
import React from 'react'
import { useQuery } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'

function QueryExample() {
  const { isLoading, error, data, isFetching } = useQuery('repoData', () =>
    fetch('https://api.github.com/repos/tannerlinsley/react-query').then((res) => res.json())
  )

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong> <strong>✨ {data.stargazers_count}</strong> <strong>🍴 {data.forks_count}</strong>
      <div>{isFetching ? 'Updating...' : ''}</div>
      <ReactQueryDevtools initialIsOpen /> 
    </div>
  )
}
export default QueryExample

还有一个类似于 React Query 的库。

“SWR” 这个名字来自于 stale-while-revalidate:一种由 HTTP RFC 5861 推广的 HTTP 缓存失效策略。这种策略首先从缓存中返回数据(过期的),同时发送 fetch 请求(重新验证),最后得到最新数据。

这个库的主要好处是它由 Vercel 构建的,他们是创建 Next.js 的人。因此,在使用 Next.js 时,您可以期待更好的性能。

# 安装
$ npm i swr --save
# or
$ yarn add swr

SWR 涵盖了性能正确性和稳定性的各个方面,以帮你建立更好的体验:

  • 快速页面导航
  • 聚焦时重新验证
  • 网络恢复时重新验证
  • 本地缓存更新 (Optimistic UI)
  • 智能错误重试
  • 分页和滚动位置恢复
  • React Suspense

3. 脚手架

从头开始创建 React 应用程序很复杂,设置 webpack、babel 等会让人望而生畏。

它将 webpack 和 babel 封装在一起,组成一个新的脚本工具 react-scripts 来管理整个应用。

# 创建 React 项目
$ npx create-react-app my-app
# or
$ npm init react-app my-app
# or 
$ yarn create react-app my-app

Easy to get started in seconds

  • vite - 使用原生 ESM 文件,无需打包,下一代前端开发与构建工具。

Vite 充分利用了「操作系统的原生能力」,缩短了链路,省去了繁杂的打包步骤,规避了很多在在构建上时的性能问题,Vite具有「跨时代」的意义。

目前来看,虽然 Vite 生态没有 Webpack 繁荣,但随着时间的推移, Vite 必将会替代 Webpack。

# 创建 react 项目
# npm 6.x
$ npm init vite@latest my-react-app --template react 

# npm 7+, 需要额外的双横线:
$ npm init vite@latest my-react-app -- --template react

# yarn
$ yarn create vite my-react-app --template react
  • next.js - 构建 React 应用的框架。

Next.js 开箱即用,提供服务器端渲染、静态站点生成、无服务器功能等等。

它是一个工具箱,最重要的功能是开箱即用的 SEO 支持,为您提供创建高性能 web 应用程序所需的一切。

# 创建 next.js 程序
$ npx create-next-app my-next-app
# or
$ yarn create next-app my-next-app

Next.js 是围绕着 页面(pages) 的概念构造的。一个页面(page)就是一个从 pages 目录下的 .js.jsx.ts.tsx 文件导出的 React 组件

页面(page) 根据其文件名与路由关联。例如,pages/about.js 被映射到 /about。甚至可以在文件名中添加动态路由参数。

如果您开始使用 React 构建一些基本项目,那么您还有其他选择。

  • gatsby - 构建面向内容的静态网站,不适用于其他项目。

4. UI 组件库

一套通用完善的 UI 库不仅能帮助我们解决重复的应用场景,也能节省开发成本,经社区锤炼后的库性能也有所保障。

  • antd - 文档全面,成熟的设计体系,生态丰富,国人首选。

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

# 安装
$ yarn add antd
# or
$ npm i antd --save

# 基于 Umi 搭建项目
$ yarn create @umijs/umi-app
# or 
$ npx @umijs/create-umi-app
  • blueprint - 一个基于 React 的 Web UI 工具包。

Blueprint 基于 TypeScript 和 Scss 开发,功能强大,并且有自己的色彩和排版规范,推荐使用。

image.png

它是一个模块化组件库,分为多个包(可以单独安装):

  1. core - 30+ 组件,300多个 icons。
  2. datetime - 关于日期和时间的 6 个组件。
  3. icons - 300+ 图标,支持 svg 和 fonts 两种格式。
  4. select - 下拉选择相关的 6 个组件。
  5. table - 高度交互的表格组件(个人感觉性能不咋地)。
  6. timezone - 处理时区相关的组件。
  7. popover- 强大的弹出框组件,基于 Popper.js
  8. tooltip - 由 popover 提供。
  9. contextMenu2 - 由 popover 提供。
# 安装
$ yarn add @blueprintjs/core 
# 使用 datetime
$ yarn add @blueprintjs/datetime

Blueprint 支持 Chrome、Firefox、Safari、IE 11 和 Microsoft Edge。

Blueprint 在其公共 API 中严格遵守 semver

  • blueprintroot/main 模块导出的 JS API;
  • 组件的 HTML 结构;
  • 渲染组件的 CSS 样式;
  • Material UI - Google‘s Material Design 风格,定制相对困难。
  • Semantic UI - 语义化、代码可读性与可理解性很强,与 bootstrap 风格接近。
  • React Bootstrap - 用 React 构建的 Bootstrap 组件。

5. 表单处理

90% 的 Web 应用程序都有这种情形 - 处理表单输入是一个很大的痛苦。但我们有一些好的方案。

React Hook Form 是用于处理表单最新最好的库(个人认为),它非常高效且灵活。

它对一些外部设计库有很好的支持,比如 material-uiant-design

# 安装
$ npm i react-hook-form --save
# or
$ yarn add react-hook-form
import React from 'react'
import { useForm } from 'react-hook-form'

export default function HookForm() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm()
  const onSubmit = (data) => console.log(data)

  // 通过 watch 可以监听 inupt 的变化
  console.log(watch('username')) 
  return (
    // handleSubmit 会在调用 onSubmit 之前验证用户输入
    <form onSubmit={handleSubmit(onSubmit)}>
      {/*通过 register 函数将输入注册到钩子中 */}
      <input defaultValue="test" {...register('username')} />
      
      {/* 通过 register 可以配置验证规则 */}
      <input {...register('password', { required: true })} />
      
      {/* 验证规则不通过时,将会显示如下内容  */}
      {errors.exampleRequired && <span>This field is required</span>}
      <input type="submit" />
    </form>
  )
}

这个领域有一些很好的选择。

  • formik - Formik 为输入验证、格式化、屏蔽、数组和错误处理提供了久经考验的解决方案。
  • redux-form - 不建议使用 ,它真的会损害性能。

6. HTTP 调用

在现代世界中,几乎所有网站都依赖于一些外部数据源,所以进行 HTTP 调用非常简单。

Axios 是进行 HTTP 调用的推荐方式。

  • axios - 一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端。

Axios 具有如下特征:

  1. 从浏览器中创建 XMLHttpRequest
  2. 从 node.js 发出 http 请求;
  3. 支持 Promise API;
  4. 拦截请求和响应;
  5. 转换请求和响应数据;
  6. 取消请求;
  7. 自动转换 JSON 数据;
  8. 客户端支持防止 CSRF/XSRF
# 安装
$ npm i axios --save
# or 
$ yarn add axios
// axios
axios.get(url)
  .then((response) => console.log(response))
  .catch((error) => console.log(error))
  • Fetch - 浏览器原生 API,有别与 xhr

小型项目的情况下,只需要几个简单的 API 调用,Fetch 是一个不错的解决方案。

// fetch
fetch(url)
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.log(error))

你将需要样式,这是毫无疑问的,有多种方法可以设置应用程序的样式。

许多人可能不同意我的看法,但我认为 Styled Components 是 React 应用程序中样式化的最佳选择方案。

Styled Components 是 CSS in JS 一种实现方式,其他实现方式还有:radiumreact-jss 等。

它有助于创建具有明确关注点分离的干净组件,此外,它可以通过 props 轻松管理和配置。

# 安装
$ npm i styled-components --save
# or
$ yarn add styled-components 
import React from "react";
import styled from "styled-components";

const Container = styled.div`
    padding: 12px;
    background: red;
    &:hover {
        background: blue;
}
`
const Homepage = () => {
  return (
      <Container>
        <h1>Welcome to React<h1>
    </Container>
  )
}

但是,正如我所说,还有其他很棒的选择!

  • Cascading Style Sheets (CSS) - W3C 标准,对于较小的项目应该没问题。
  • sass - CSS 预处理,它为管理 CSS 提供了很好的功能,使用于中型甚至更大的项目。
  • styled-jsx - 与 styled-compomnents 功能很相似的库。
  • radium - CSS in JS 的一种实现。
  • react-jss - CSS in JS 的一种实现。

好的文档可以在未来节省 100 多个小时,因此,在项目的早期就积极主动的采用文档库。

推荐创建文档的方式是 react-styleguidist

React Styleguidist 基于 react-docgen ,可以帮助 react 项目快速构建项目文档的库。

其原理是使用 react-docgen 来解析源文件(未转译)。react-docgen 查找导出的 React 组件并根据 PropTypes 或 Flow 注释生成文档。

React Styleguidist 使用 Markdown 文档:每个 JavaScript 代码块都被渲染为带有 react-simple-code-editor 的交互式演示(使用 Remark 提取所有这些代码块)。

Webpack loaders 生成包含所有用户组件、文档和示例的 JavaScript 模块,并将其传递给 React 应用程序,该应用程序渲染样式指南。

# 安装 (node >= 14.0.0)
npm install react-styleguidist --save-dev
yarn add react-styleguidist --dev

在使用 React Styleguidist 时,需要在项目根目录下建立 styleguide.config.js 文件,基本配置如下:

// styleguide.config.js
module.exports = {
  title: 'React Style Guide Example',
  defaultExample: true,
  webpackConfig: {
    module: {
      rules: [
        {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
        },
        ...
      ],
    },
  },
}

如果使用的是 vite2 构建的 React 项目,则需要手动安装 babel 相关依赖:

# babel 7
$ yarn add babel-loader @babel/core @babel/preset-env babel/preset-react --dev

然后添加 babel 配置 .babelrl

// .babelrc
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

默认情况下,React Styleguidist 搜索组件的位置使用的是 glob模式src/components/**/*.{js,jsx,ts,tsx},也就是说位于这个路径下的组件才会生成文档。

image.png

上图是 React Styleguidist 的文档界面(编写文档需要提供与组建同名的 .md 文件) 。

还有一些其他选择。

  • js-docs - JavaScript 的通用文档工具。
  • react-docz - 非常易于使用的文档指南,值得一试。

9. 多语言支持

如果您正在全球范围内构建产品,那么您可能希望在 React 应用程序中添加多语言支持。

支持 React 应用程序国际化,使用 i18next、i18n 生态系统。

还有其他一些不错的选择。

它也支持其他框架,如 VueJS 和 Angular。

10. 动画

动画使您的应用程序栩栩如生。在 React 中使用动画有一些不错的选择。

纯 CSS 是制作 React 应用程序动画的最佳方式,它简单快捷,性能也有保障。

const [name, setName] = useState('')
...
function App() {
  return (
    <div>
        <div className={['example', name].join(' ')}></div>
      <button onClick={() => setName('example-animation')}>点击</button>
    </div>
  )
}
.example {
  width: 100px;
  height: 100px;
  background-color: red;
  animation-duration: 4s;
}

.example-animation {
  animation-name: example;
}

@keyframes example {
  0%   {background-color: red;}
  25%  {background-color: yellow;}
  50%  {background-color: blue;}
  100% {background-color: green;}
}

5rhe3-upq2w.gif

如果您想要现成的东西。那么这里有一些建议给您。

  • react-motion - 官方推荐库之一,跨平台, 作者是 FB 大神 Cheng Lou。

该库融合了一些物理学的东西,还为 React 的 TransitionGroup (V2 之前的版本)提供了一些更强大的 API。

基于弹簧物理学的动画库,基本上能满足大多数与 UI 相关的动画需求。

一组组件,用于随时间管理组件状态(包括装载和卸载),专门针对动画设计。

11. 长列表渲染

渲染一个长列表会严重影响应用程序的性能,在这种情况下使用库可能是一个好主意。

如果你有某种无限滚动的应用程序, 那么你应该考虑 React Window

react-window 是是更轻量级的 react-virtualized, 同出一个作者(React 核心团队成员)。

# 安装
$ npm install react-window --save
# or
$ yarn add react-window

12. 代码质量工具

Linters 可以静态地发现代码中的任何错误,使用某种 linter 是个好主意。

首选解决方案是 Eslint

# 安装 ( Node.js (^10.12.0, or >=12.0.0) )
$ npm install eslint --save-dev
# or
$ yarn add eslint --dev

使用 Eslint 需要进行配置,通过 eslint --init 可以在项目根目录下自动生成配置文件 eslintrc.js

$ npx eslint --init
# or 
$ yarn run eslint --init

Eslint 配置文件支持多种格式,优先级顺序如下:

  1. .eslintrc.js
  2. .eslintrc.cjs
  3. .eslintrc.yaml
  4. .eslintrc.yml
  5. .eslintrc.json
  6. package.json
  • jshint - 比较旧的库。
  • tslint - TypeScript 的 linter,现在不推荐。

13. 格式化

具有一致的视觉样式对于任何应用程序都非常重要,代码格式化程序可以为您完成这项工作!

这对您来说是最好的解决方案,您不需要其他任何东西!

# 安装 prettier
$ npm install prettier --save-dev --save-exact 
# or 
$ yarn add prettier --dev --exact

使用 Prettier 时需要提供配置文件,prettier 遵循 cosmiconfig ,所以您可以通过(按优先顺序)进行配置配置。

  • package.json 中,提供 prettier 字段。
  • 可以是一个 .prettierrc 文件,使用 JSON 或 YAML 格式编写。
  • 也可以是 .prettierrc.json.prettierrc.yml.prettierrc.yaml,或.prettierrc.json5 文件。
  • 还可以使用 .prettierrc.js.prettierrc.cjsprettier.config.jsprettier.config.cjs 通过 module.exports 导出。
  • 一个 .prettierrc.toml 文件。

例如通过使用 .prettierrc 文件进行配置:

{
  "trailingComma": "es5",
  "tabWidth": 4,
  "semi": false,
  "singleQuote": true
}

之后可以通过命令行指令对所有文件进行格式化操作。

$ npx prettier --write .
# or 
$ yarn prettier --write .

Prettier 还可以与编辑器进行集成,详见

14. 分析

数据分析就是未来,今天的大多数企业都是数据驱动的,因此,为您的应用程序拥有一个好的分析工具非常重要!

最流行和最强大的工具是 React Ga

我认为您不需要其他任何东西。

# 安装
$ npm install react-ga --save

当使用 ReactGA.initialize 初始化参数(谷歌分析生成的 id)后,React 项目会自动在 header 中引入 Google Analytics 脚本。

15. 测试

我不需要重申测试对于任何应用程序的重要性。所以请往下看!

推荐的是 React Testing Library(也是官方推荐的测试库),是 Airbnb 的 Enzyme 测试库的替代方案。

React Testing Library 和 Jest 是截然不同的,它是其中一个可以测试 React 组件的库(还有 Enzyme 等)。

它非常易于使用,并且旨在遵循真实环境中的使用情况。

React Testing Library 不直接测试组件的实现细节,而是从一个 React 应用的角度去测试。 更加符合我们对于单元测试的原本诉求,以及最佳实践。

它让您的测试库从长远来看是可维护的,让重构工作变得轻而易举,组件的重构(更改实现但不是功能)不会破坏您的测试。

# 安装
$ npm install --save-dev @testing-library/react
# or
yarn add @testing-library/react --dev
// hidden-message.js
import * as React from 'react'

// NOTE: React Testing Library works well with React Hooks and classes.
// Your tests will be the same regardless of how you write your components.
function HiddenMessage({children}) {
  const [showMessage, setShowMessage] = React.useState(false)
  return (
    <div>
      <label htmlFor="toggle">Show Message</label>
      <input
        id="toggle"
        type="checkbox"
        onChange={e => setShowMessage(e.target.checked)}
        checked={showMessage}
      />
      {showMessage ? children : null}
    </div>
  )
}
export default HiddenMessage
// __tests__/hidden-message.js
import '@testing-library/jest-dom' // jest-dom 更方便的为测试添加断言,建议使用,但不是必需的
import * as React from 'react'
import {render, fireEvent, screen} from '@testing-library/react'
import HiddenMessage from '../hidden-message'

test('shows the children when the checkbox is checked', () => {
  const testMessage = 'Test Message'
  render(<HiddenMessage>{testMessage}</HiddenMessage>)
  //query* 函数将返回元素,如果找不到,则返回 null
  //get* 函数将返回元素,如果找不到,则抛出错误
         
  expect(screen.queryByText(testMessage)).toBeNull()
  //查询可以接受正则表达式,使选择器对内容调整和更改更具弹性
  fireEvent.click(screen.getByLabelText(/show/i))

  // .toBeInTheDocument() 是一个来着jest-dom 的断言
  // 还可以使用 .toBeDefined()
  expect(screen.getByText(testMessage)).toBeInTheDocument()
})
  • jest - 最受欢迎的 JS 测试框架。

Jest 是 Facebook 推出的老牌测试框架,也是 create-react-app 默认安装的测试库。

Jest 和 React Testing Library 的职责是不同的,React Testing Library 是跟 react 打交道,Jest 是跟测试用例打交道。

Jest 给予我们运行测试的能力,除此之外,Jest 还提供了一系列 API,例如 test suites、test cases、assertions,当然 Jest 提供的还不止这些,还有 spies、mocks、stubs 等等。

如果是使用 CRA 创建的 React 程序,则只需要安装 react-test-renderer 来呈现快照。 快照测试是 Jest 的一部分。 您可以使用测试渲染器快速呈现虚拟 DOM 的可序列化 HTML 输出,而不是呈现整个应用程序的UI。

$ yarn add react-test-renderer --dev

Cypress 通常被比作 Selenium;然而,Cypress 在根本上和架构上都不同。Cypress 不受与 Selenium 相同的限制。

它能够进行端到端测试、单元测试、集成测试,可以测试在浏览器中运行的任何东西。

# 安装
$ npm install cypress --save-dev
# or
$ yarn add cypress --dev

16. 构建可共享的组件

如果您在一个大型团队中,那么轻松共享组件可能会成为一个大问题。

如果您正在寻找最完整的解决方案,Storybook 是您的最佳选择。

Storybook welcome screen

Storybook 是 UI 组件的快速开发环境。它允许你浏览组件库,查看每个组件的不同状态,以及交互式开发和测试组件。StoryBook 可帮助你独立于应用程序开发组件,这也有助于提高组件的可重用性和可测试性。

# 安装 storybook
$ npx sb init

Storybook 需要安装到已经设置了框架的项目中,它不适用于空项目。有很多方法可以在给定的框架中引导应用程序,包括:

最后,我想现在您对何时选择哪个库有了很好的了解,如果您有任何不同的想法,请留言告诉我。

本文启发于 45-npm-packages-to-solve-16-react-problems


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK