React(七)

React(七)

​ 前端框架,快速开发页面,函数式编程,与后端api快速搭建

ES-lint

react的代码规范库

1
yarn add eslint eslint-plugin-react

如果是typescript项目按照ts相关插件

1
yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser

使用yarn eslint –lint向导来完成配置,或者手动创建eslintrc。json填入如下配置

1
2
3
4
5
6
7
8
{
"extends": ["eslint:recommended","plugin:react/recommended"],
"parser": "@typescript-eslint/parser",
"plugins": ["react","@typescript-eslint"],
"rules": {
"react/self-closing-comp": ["error"] //组件无内容时自闭合
}
}

在vscode中配置

1
2
3
4
5
6
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]

Prettier

Prettier是

  • 一个代码格式工具,支持基本主流前端的语言(js, ts, Es6, Es7,markdown等等);
  • 会根据书写的代码,重新解析和构建显示格式(即,它清除原来的格式,按照自己认为美丽的方式重新显示代码格式)
  • Prettier 不会像EsLint,TsLint,StyleLint 那样告诉你,语法哪里错了,它只会告诉你代码这么写不美观

编写.prettierrc

1
2
3
4
5
6
7
8
9
10
11
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}

stylelint

stylelint是现代化的前端项目中一个强大的代码检查工具。可以帮忙检查样式文件并在样式中强制执行约定。

stylelint 默认地能解析如下的非标准语法,包括Sass、Less 和 SugarSS,非标准语法可以从以下文件扩展名 .sass.scss.less.sss 中自动推断出来。或者您也可以自己指定语法。

此外,在使用命令行界面或 Node.js 应用程序接口时,stylelint 可以接受任何PostCSS兼容语法。但请注意,stylelint 无法保证核心规则可以在上面列出的默认值以外的语法中正常工作

lint-staged

lint-staged 是一个在git暂存文件上运行linters的工具,当然如果你觉得每次修改一个文件就给所有文件执行一次lint检查不恶心的话,这个工具对你来说就没有什么意义了,请直接关闭即可

1
npx mrm lint-staged

配置

在package.json中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "My project",
"version": "0.1.0",
"scripts": {
"my-custom-script": "linter --arg1 --arg2"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js}": [
"eslint --cache --fix",
"prettier --write"
],
"*.css": [
"stylelint --cache --fix",
"prettier --write"
]
}
}

使用prettier自动修复javascripttypescriptmarkdownHTMLCSS的代码样式

1
2
3
{
"*.{js,jsx,ts,tsx,md,html,css}": "prettier --write"
}

Stylelint用于具有默认值的CSS和具有SCSS语法的SCSS

1
2
3
4
{
"*.css": "stylelint",
"*.scss": "stylelint --syntax=scss"
}

自动修复代码

1
2
3
{
"*.js": "eslint --fix"
}

过滤文件原则

Linter命令处理由glob模式定义的所有暂存文件的子集

如果全局模式不包含斜杠(/),matchBase则将启用micromatch的选项,因此无论目录如何,全局匹配文件的基本名称:

  1. "*.js"将匹配所有JS文件,例如/test.js/foo/bar/test.js
  2. "!(*test).js"。将匹配所有以结尾的JS文件test.js,因此foo.js但不匹配foo.test.js

如果全局模式确实包含斜杠(/),则它也将与路径匹配:

  1. "./*.js"将匹配git repo根目录中的所有JS文件,因此/test.js但不匹配`/foo/bar/test.js``
  2. ``”foo/*/\.js”将匹配/foo目录中的所有JS文件,所以/foo/bar/test.js但不匹配/test.js`

JSdoc

JSDoc 是一个针对 JavaScript 的 API 文档生成器,类似于 Java 中的 Javadoc 或者 PHP 中的 phpDocumentor;在源代码中添加指定格式的注释,JSDoc 工具便会自动扫描你的代码并生成一个 API 文档网站(在指定目录下生成相关的网页文件)

生成 API 文档只是一方面,其更主要的贡献在于对代码注释格式进行了规范化

安装

1
npm install -g jsdoc

在js文件中写入对应的函数和注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Returns the sum of a and b
* @param {number} a
* @param {number} b
* @returns {number}
*/
function sum(a, b) {
return a + b;
}
/**
* Return the diff fo a and b
* @param {number} a
* @param {number} b
* @returns {number}
*/
function diff(a, b) {
return a - b;
}

然后就是在当前目录执行以下命令

1
jsdoc doc.js

最后就会在当前目录下生成一个名为 out 的目录(也可以另外指定),里面有包含接口文档的html页面

常用写法:

@description:也可写作 @desc,描述当前注释对象的详细信息

@file:注释写在文件开头,用于描述当前文件的相关信息

@class 描述一个 class

@returns 或者写作 @return,描述函数的返回值的信息;

@param 与 @arg, @argument 含义相同,描述一个函数的参数信息;

@function 与 @func, @method 含义相同,描述一个函数;

@todo 描述接下来准备做的事情;

@copyright 描述当前文件的版权相关信息

@file 注释写在文件开头,用于描述当前文件的相关信息

react的Ts写法

react、react-dom类型声明文件

使用tsx之前要安装react的声明文件,否则会报错找不到模块react

安装

1
2
npm install @types/react -s
npm install @types/react-dom -s

有状态组件

有状态组件中的state和props使用ts去定义类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import * as React from 'react'

interface IProps {
color: string,
size?: string
}

interface IState {
count: number,
}

class App extends React.PureComponent<IProps, IState> {
public readonly state: Readonly<IState> = {
count: 1
}
public render () {
return (
<div>Hello world</div>
)
}
public componentDidMount () {
}
}

事件类型

常用Event事件对象类型

ClipboardEvent<T = Element> 剪贴板事件对象

DragEvent<T = element> 拖拽事件对象

ChangeEvent<T = element> Change事件对象

KeyboardEvent<T = element> 键盘事件对象

MouseEvent<T = element> 鼠标事件对象

TouchEvent<T = element> 触摸事件对象

WheelEvent<T = element> 滚轮事件对象

AnimationEvent<T = element> 动画事件对象

TransitionEvent<T = element> 过渡事件对象

1
2
3
4
5
import { MouseEvent } from 'react'

interface Iprops {
onClick (event: MouseEvent<HTMLDivElement>): void,
}

CSS属性类型

有时候会在props或者state中使用css属性,这个时候就使用react自带的css类型

1
2
3
4
5
6
import React from 'react';

export type EdgeTextProps = {
style?: React.CSSProperties;
color: React.CSSProperties['color'];
};

泛型组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//泛型ts组件
function Foo<T>(props: Props<T>){
return <div>{props.content}</div>
}

const App = () => {
return (
<div className="App">
<Foo content={42}></Foo>
<Foo<string> content={"hello"}></Foo>
</div>
)
}

//普通ts组件
interface Props {
content: string;
}

function Foo(props: Props) {
return <div>{props.content}</div>
}

const App = () => {
return (
<div className="App">
// Type number not assignable to type string
<Foo content={42}></Foo>
<Foo<string> content={"hello"}></Foo>
</div>
)
}

react库

recomponse

loadable-components

懒加载

安装

1
npm install @loadable/component

使用

1
2
3
4
5
6
7
8
9
10
11
import loadable from '@loadable/component'

const OtherComponent = loadable(() => import('./OtherComponent'))

function MyComponent() {
return (
<div>
<OtherComponent />
</div>
)
}

react-media-recorder

安装

1
npm i react-media-recorder

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { ReactMediaRecorder } from "react-media-recorder";

const RecordView = () => (
<div>
<ReactMediaRecorder
video
render={({ status, startRecording, stopRecording, mediaBlobUrl }) => (
<div>
<p>{status}</p>
<button onClick={startRecording}>Start Recording</button>
<button onClick={stopRecording}>Stop Recording</button>
<video src={mediaBlobUrl} controls autoPlay loop />
</div>
)}
/>
</div>
);

import { useReactMediaRecorder } from "react-media-recorder";

const RecordView = () => {
const {
status,
startRecording,
stopRecording,
mediaBlobUrl,
} = useReactMediaRecorder({ video: true });

return (
<div>
<p>{status}</p>
<button onClick={startRecording}>Start Recording</button>
<button onClick={stopRecording}>Stop Recording</button>
<video src={mediaBlobUrl} controls autoPlay loop />
</div>
);
};

react-three-fiber

在react中使用three.js的插件

安装

1
2
3
npm install three @react-three/fiber
## 如果使用ts还要安装ts包
npm install @types/three

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* eslint-disable */
import * as THREE from 'three'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'

function Box(props: JSX.IntrinsicElements['mesh']) {
// This reference will give us direct access to the THREE.Mesh object
const ref = useRef<THREE.Mesh>(null!)
// Hold state for hovered and clicked events
const [hovered, hover] = useState(false)
const [clicked, click] = useState(false)
// Rotate mesh every frame, this is outside of React without overhead
useFrame((state, delta) => (ref.current.rotation.x += 0.01))

return (
<mesh
{...props}
ref={ref}
scale={clicked ? 1.5 : 1}
onClick={(event) => click(!clicked)}
onPointerOver={(event) => hover(true)}
onPointerOut={(event) => hover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}

export default function App() {
return (
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
)
}

prop-types

使用第三方包 prop-types 可以对react的 props 进行类型校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React from 'react'
// 导入包
import PropTypes from 'prop-types'

function About (props) {
const { name, age } = props
console.log(name, age)
return (
<div>
<p>{ name }</p>
<p>{ age }</p>
</div>
)
}

About.defaultProps = {
name: 'ReoNa',
age: 22
}

// 这里通过函数组件的 propTypes 属性设置类型校验
// PropType.类型:规定传入类型
// PropType.类型.isRequired:规定必须传入
About.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}

export default About

react-helmet

React Helmet是一个HTML文档head管理工具,管理对文档头的所有修改。React Helmet采用纯HTML标记并输出纯HTML标记,非常简单,对react初学者友好

特点:

支持所有有效的head标签,title、base、meta、link、script、noscript和style

支持body、html和title的属性

支持服务端渲染

嵌套组件覆盖重复的head标签修改

同一组件中定义时将保留重复的head标签修改(比如“apple-touch-icon”)

支持跟踪DOM更改的回调

安装

1
npm i react-helmet

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import {Helmet} from "react-helmet"

class Application extends React.Component {
render(){
return(
<div className="application">
<Helmet>
<meta charSet="utf-8"/>
<title>My title</title>
<link rel="canonical" href="http://mysite.com/example" />
</Helmet>
<Child>
<Helmet>
<title>new Title</title>
</Helmet>
</Child>
</div>
)
}
}

上面代码中,后面的helmet会覆盖前面的helmet

服务端渲染时,需要在ReactDOMServer.renderToString或ReadDOMServer.renderToStaticMarkup后调用Helmet.renderStatic()来获得你预渲染的head数据

1
2
ReactDOMServer.renderToString(<Handler />);
const helmet = Helmet.renderStatic();

二维码

QR Code数据表示方法 : 深色模块表示二进制”1”,浅色模块表示二进制”0”。

纠错能力:

  • L级:约可纠错7%的数据码字;
  • M级:约可纠错15%的数据码字;
  • Q级:约可纠错25%的数据码字;
  • H级:约可纠错30%的数据码字;

使用qrcode.react npm包

安装

1
npm install qrcode.react

api

prop type default value
value string
renderAs string (‘canvas’ ‘svg’) ‘canvas’
size number 128
bgColor string (CSS color) “#FFFFFF”
fgColor string (CSS color) “#000000”
level string (‘L’ ‘M’ ‘Q’ ‘H’) ‘L’
includeMargin boolean false
imageSettings object (see below)

图片设置参数imageSettings

field type default value
src string
x number none, will center
y number none, will center
height number 10% of size
width number 10% of size
excavate boolean false

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<QRCode
id="qrCode"
value={"https://gongyi.m.jd.com/oneDetails.html?id=930"}
imageSettings={{
// 中间有图片logo
src: `http://img13.360buyimg.com/imagetools/jfs/t1/203384/29/6713/37826/6142ef39E5f79ed2b/47200134bf8d0571.jpg`,
height: 30,
width: 30,
excavate: true,
}}
size={99} // 二维码的大小
fgColor="#000000" // 二维码的颜色
/>
//转换为图片
changeCanvasToPic = () => {
const canvasImg = document.getElementById('qrCode'); // 获取canvas类型的二维码
const img = new Image();
img.src = canvasImg.toDataURL('image/png');
// canvas.toDataUrl() 可以将canvas格式的文件转换成基于base64的指定格式的图片
// 注意这个api ie9以下不支持
const downLink = document.getElementById('down_link');
downLink.href = img.src;
downLink.download = '二维码'; //下载图片name
};
//定时刷新
//定时刷新功能是使用 setInterval 定时更新 value 值来更新二维码,跳转地址后面拼上一个radomCode, radomCode定时更新,就实现二维码的刷新了,需要及时清理定时器。

antd

Ant-Design是蚂蚁金服开发的面向React和Vue的类似于bootstrap的框架,官网链接为:https://ant.design/index-cn

安装包

1
2
npm install antd --save
cnpm i antd -S

在App.css文件中导入样式

1
@import '~antd/dist/antd.css';

按需导入包

1
import {  } from 'antd';

组件

Upload

1
2


Table

antdtable 组件,tablecolumns 有一个属性叫做 align,它的使用是控制当前列是居左、居中、居右的。

它的类型为AlignType,在node_modules/rc-table/lib/interface.d.ts中可以找到

1
export declare type AlignType = 'left' | 'center' | 'right';

在使用时,如果对table进行二次封装,它的值

1
2
3
const columns = [{
align: 'right',
}]

此时会报错,类型推论会将align推论为string,而AlignType是字面量类型,没有string

使用as进行断言就不会报错

1
2
3
4
5
6
import { AlignType } from 'rc-table/lib/interface.d.ts';

columns: [{
align: 'right' as 'right'
// 或者 align: 'right' as AlignType
}]

tooltip

tooltip组件需要禁用时没有直接的disable属性,使用onchange事件进行回调

1
2
3
4
5
6
7
8
9
10
11
12
const checkTipVisible = (visible: boolean) => {
VisibleCrtl.toggle(!Boolean(enableCreatePlan) ? visible : false);
};

<Tooltip
title={formatMessage({
id: 'CREATE_OPERATING_PLAN_ERROR',
})}
visible={visible}
onVisibleChange={checkTipVisible}
>
</Tooltip>

Form

在form.item中可以使用shouldUpdate包一层,然后将Form传入item的回调函数中

这样做:

1.可以在item中取到form的其他值,从而进行表单联动

2.可以确保字段在更新时及时更新,相当于一次setState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<Form.Item shouldUpdate>
{(form) => {
const branches = form.getFieldValue('branches') || [];
return (
<>
<Form.List name="branches">
{(fields, operation) => {
return (
<BranchSortTable
readonly={readonly}
data={branches}
fields={fields}
operation={operation}
/>
);
}}
</Form.List>
</>
);
}}
</Form.Item>

验证

表单提交时对有rules的item要进行校验,比较繁琐的写法像这样

1
2
3
const handleSubmit = async () => {
await form.validateFields();
}

如果有单独的需要提前校验/接口校验的可以用validateFirst

校验时可以用validator写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<ProFormText
readonly={readOnly}
label={
<Text
style={{ maxWidth: 110 }}
ellipsis={{
tooltip: formatMessage({ id: 'FORM_LABEL_NODE_NAME' }),
}}
>
{formatMessage({ id: 'FORM_LABEL_NODE_NAME' })}
</Text>
}
required
validateFirst
name="name"
rules={[
{
required: true,
message: formatMessage({
id: 'FORM_LABEL_PLACEHOLDER_INPUT',
}),
},
{
validator: async (_rule, value) => {
if (!validateNameUnique(value, element.id)) {
throw formatMessage({
id: 'AUTO_FLOW_NAME_UNIQ_ERROR_MESSAGE',
});
}
},
},
]}
fieldProps={{
placeholder: formatMessage({
id: 'AUTO_FLOW_LIMIT_CHAR_LENGTH_MESSAGE',
}),
}}
/>

获取字段的校验状态可以使用getFieldError/getFieldsError获取字段或者全部字段的验证信息

namePath

在form.item的name中使用数组,能够把不同的表单放到同一个对象中,而不是普通的key-value

1
2
3
4
5
6
7
8
9
10
11
12
<Form.Item
name={['a', 'select']}
options={listData as treeItemType[]}
readonly={isView}
>
</Form.Item>
<Form.Item
name={['a', 'input']}
options={listData as treeItemType[]}
readonly={isView}
>
</Form.Item>

如果namePath后面跟的是index,可以自动合并成数组

namePath可以进行嵌套,输出对象数组的表单项

1
2
3
4
5
6
7
8
['1', '2','3'].map((index)=> {
<Form.Item
name={['a', 'select', index]}
options={listData as treeItemType[]}
readonly={isView}
>
</Form.Item>
})

自定义表单组件

表单组件不一定非要input、select,也可以自己通过form.item填充,取值的时候使用get和set就比较方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const handleChange = () => {
labelsForm.setFieldsValue({
tree: []
});
}

const FormTree: React.FC<{ value?: any }> = (value) => {
return (
<Tree
isDirectoryTree
searchAble
searchingMode="filter"
treeData={value.value || []}
onSelect={(selectKey, info) => {
setSelectId({ key: selectKey, title: info?.node.title });
}}
/>
);
};

<ProForm.Item name="tree" noStyle shouldUpdate>
<FormTree />
</ProForm.Item>

form之外的dom需要在form字段更新时重新render可以使用useWatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react';
import { Form, Input, InputNumber, Typography } from 'antd';

const Demo = () => {
const [form] = Form.useForm<{ name: string; age: number }>();
const nameValue = Form.useWatch('name', form);

return (
<>
<Form form={form} layout="vertical" autoComplete="off">
<Form.Item name="name" label="Name (Watch to trigger rerender)">
<Input />
</Form.Item>
<Form.Item name="age" label="Age (Not Watch)">
<InputNumber />
</Form.Item>
</Form>

<Typography>
<pre>Name Value: {nameValue}</pre>
</Typography>
</>
);
};

export default Demo;

给form item添加提示和自定义图标

1
2
3
4
5
6
<Form.Item
label="Field B"
tooltip={{ title: 'Tooltip with customize icon', icon: <InfoCircleOutlined /> }}
>
<Input placeholder="input placeholder" />
</Form.Item>

form的setFieldsValue 和 resetFields 不会触发 Form 的 onValuesChange,

Modal

如果在Modal的content中使用国际化,需要使用Modal的hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const [modal, contextHolder] = Modal.useModal();

modal.info({
title: null,
icon: null,
okText: formatMessage({ id: 'ACTION_CONFIRM' }),
className: styles.deleteCheckModal,
content: (
<div>
<div className={styles.deleteCheckModalTitle}>
{formatMessage({ id: 'TIPS' })}
</div>
<div className={styles.deleteCheckModalTips}>
<InfoCircleFilled className="mr-6" />
{formatMessage({ id: 'CANOT_DELETE_TIPS' })}
</div>
<div className={styles.deleteCheckModalTable}>
<ProTable
pagination={false}
scroll={{ y: 200 }}
size="small"
dataSource={checkResult}
>
<ProTable.Column title="ID" dataIndex="id" width={100} />
<ProTable.Column
title={formatMessage({ id: 'PLAN_NAME' })}
dataIndex="name"
/>
</ProTable>
</div>
</div>
),
});

return (
<>{contextHolder}</>
)

如果在modal中使用ref,在初次modal渲染时ref拿不到值,此时应该手动强制刷新一次modal子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const [Key, setKey] = useState(
`${+new Date()}`,
);
// ref相关的事件改变时也要reset key,防止拿不到
const handleChange = () => {
setKey(() => `${+new Date()}`);
};
<Modal
visible={visible}
form={modalForm}
title={formatMessage({
id: 'ATTACHMENT_ADD',
})}
modalProps={{
onCancel: () => {
setVisible(false);
},
}}
onVisibleChange={(visible) => {
if (visible) {
// 当modal打开后强制渲染一次 RichTextInput 否则获取不到ref
setTimeout(() => {
setImageAddressInputKey(() => `${+new Date()}`);
}, 0);
}
}}>
</ModalForm>

Select

select的下拉框展开时,如果滚动页面的话会下拉框会移动位置,需要添加一个属性防止下拉框滚动

1
2
3
4
5
6
<Select

getPopupContainer={(triggerNode) =>
triggerNode.parentNode
}
/>

TreeSelect

triggerNode.props非公开api

pro-components

ProComponents 是基于 Ant Design 而开发的模板组件,提供了更高级别的抽象支持,开箱即用。可以显著的提升制作 CRUD 页面的效率,更加专注于页面。

ProLayout 解决布局的问题,提供开箱即用的菜单和面包屑功能

ProTable表格模板组件,抽象网络请求和表格格式化

ProForm表单模板组件,预设常见布局和行为

ProCard提供卡片切分以及栅格布局能力

ProDescription定义列表模板组件,ProTable 的配套组件

ProSkeleton页面级别的骨架屏

Proform有很多ProFormFields 表单项组件。这些组件本质上是 Form.Item 和 组件的结合,我们可以帮他们当成一个 FormItem 来使用,并且支持各种 props。每个表单项都支持 fieldProps 属性来支持设置输入组件的props。 同时支持了 placeholder 的透传,你可以直接在组件上设置 placeholder

每个表单项同时也支持了 readonly ,不同的组件会有不同的只读样式,与 disable 相比 readonly 展示更加友好。生成的 dom 也更小,比如 ProFormDigit 会自动格式化小数位数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<ProFormText
width="md"
name="name"
label="签约客户名称"
tooltip="最长为 24 位"
placeholder="请输入名称"
/>
<ProFormDateRangePicker name="contractTime" label="合同生效时间" />
<ProFormSelect
width="xs"
options={[
{
value: 'time',
label: '履行完终止',
},
]}
name="unusedMode"
label="合同约定失效效方式"
/>

chakra-UI

Material-UI

推出很久 很好用

Elastic-UI

rsuitejs

Charts.rsuite.js

blueprint.js

NextUI

看起来很好看

HeadlessUI

tailwind的公司开源的UI库

geist-ui

swr

swr是用于数据请求的react hooks库。SWR 由 Next.js(React 框架)背后的同一团队创建

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

安装

1
npm install swr

对于返回 JSON 数据的普通 RESTful APIs,首先需要创建一个 fetcher 函数,这个函数只是原生 fetch 的包装

1
const fetcher = (...args) => fetch(...args).then((res) => res.json())

然后在组件中使用useSWR使用数据

1
2
3
4
5
6
7
8
9
10
11
import useSWR from "swr";

function Profile() {
const { data, error } = useSWR("/api/user/123", fetcher)

if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>

// 渲染数据
return <div>hello {data.name}!</div>
}

echarts-for-react

安装

1
npm install --save echarts-for-react

使用

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
import ReactECharts from 'echarts-for-react'; // or var ReactECharts = require('echarts-for-react');

<ReactECharts
option={this.getOption()}
notMerge={true}
lazyUpdate={true}
theme={"theme_name"}
onChartReady={this.onChartReadyCallback}
onEvents={EventsDict}
opts={}
/>

@monaco-editor/react

在页面内插入文本编辑器,可以提供代码高亮、错误提示等功能

1
npm install @monaco-editor/react

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import Editor, { DiffEditor, useMonaco, loader } from "@monaco-editor/react";

function App() {
const editorRef = useRef(null);

useEffect(() => {
if (monaco) {
console.log("here is the monaco isntance:", monaco);
}
}, [monaco]);

function handleEditorWillMount(monaco) {
// here is the monaco instance
// do something before editor is mounted
monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
}

function handleEditorDidMount(editor, monaco) {
editorRef.current = editor;
}

function handleEditorChange(value, event) {
console.log("here is the current model value:", value);
}

function showValue() {
alert(editorRef.current.getValue());
}

return (
<>
<button onClick={showValue}>Show value</button>
<Editor
height="90vh"
defaultLanguage="javascript"
defaultValue="// some comment"
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
onChange={handleEditorChange}
/>
</>
);
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

classnames

当react原生动态添加多个className时就会报错,这时我们就可以利用classnames库添加多个className,这也是react官方推荐使用

安装

1
npm install classnames --save

支持动态导入

1
2
3
4
5
6
7
import classnames from 'classnames'

<div className=classnames({
'class1': true,
'class2': true
)>
</div>

支持class动态传入变量,或者传入数组

1
2
3
4
5
6
7
8
9
10
11
import classNames from 'classnames';

render() {
const classStr = classNames({
'class1': true,
'class2': this.props.isCompleted,
'class3': !this.props.isCompleted
[a]: this.props.isCompleted
});
return (<div className={classStr}></div>);
}

GraphQL

Apollo是基于GraphQL的全栈解决方案集合,包括了apollo-client和apollo-server,从后端到前端提供了对应的lib使得开发GraphQL更加方便

1
2
3
4
apollo-boost 包含启动阿波罗客户端的所有依赖
react-apollo 视图层面的集合
graph-tag 解析查询语句
graphql 也是解析查询语句
1
2
3
4
5
6
7
8
import ApolloClient from 'apollo-boost' 

const client = new ApolloClient({
uri: 'http://localhost:5000/graphql'
})

import { ApolloProvider,Query } from 'react-apollo'
import { Mutation,MutationFunc } from 'react-apollo'
1
npm install @apollo/client graphql

使用hooks

react-hook-form

简单好看的react form表单

安装

1
npm install react-hook-form

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import { useForm } from 'react-hook-form';

function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = (data) => console.log(data);

return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('firstName')} /> {/* register an input */}
<input {...register('lastName', { required: true })} />
{errors.lastName && <p>Last name is required.</p>}
<input {...register('age', { pattern: /\d+/ })} />
{errors.age && <p>Please enter number for age.</p>}
<input type="submit" />
</form>
);
}

eventbus

安装

1
yarn add events
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//event.ts
import {EventEmitter} from 'events'
export default new EventEmitter()

//发布
import emitter from './event'

class Father extends React.Component {
constructor(props){
super(props)
}
handleClick = () =>{
emitter.emit('info','来自father的info')
}
}

export default Father
//订阅
//emitter.addListener()事件监听订阅
//emitter.removeListener()进行事件销毁,取消订阅
import emitter from './event'

class Son extends React.Component {
constructor(props){
super(props)
}
}

react-flow

安装

1
npm install --save react-flow-renderer

使用

1
2
3
4
5
6
7
8
9
10
import React from 'react';
import ReactFlow from 'react-flow-renderer';

const elements = [
{ id: '1', type: 'input', data: {lable: 'Node 1'},position: {x: 250, y: 50}},
{ id: '2', data: {lable: <div>Node 2</div>}, position: {x: 100, y: 100}},
{ id: 'el-2', source: '1', targetL '2', animated: true}
]

export default ()=> <ReactFlow elements={elements}></ReactFlow>

React Flow有两个背景变体:点和线。您可以通过将其作为子级传递给ReactFlow组件来使用它

1
2
3
4
5
6
7
8
9
10
11
import ReactFlow, { Background } from 'react-flow-renderer';

const FlowWithBackground = () => (
<ReactFlow elements={elements}>
<Background
variant="dots"
gap={12}
size={4}
/>
</ReactFlow>
);

可以通过将mini-map插件作为子级传递给ReactFlow组件来使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import ReactFlow, { MiniMap } from 'react-flow-renderer';

const FlowWithMiniMap = () => (
<ReactFlow elements={elements}>
<MiniMap
nodeColor={(node) => {
switch (node.type) {
case 'input': return 'red';
case 'default': return '#00ff00';
case 'output': return 'rgb(0,0,255)';
default: return '#eee';
}
}}
/>
</ReactFlow>
);

控制面板包含zoom-in、zoom-out、fit-view和一个锁定/解锁按钮。

1
2
3
4
5
6
7
import ReactFlow, { Controls } from 'react-flow-renderer';

const FlowWithControls = () => (
<ReactFlow elements={elements}>
<Controls />
</ReactFlow>
);

如果需要访问ReactFlow组件外部的React Flow的内部状态和操作,可以用ReactFlowProvider组件包装它

1
2
3
4
5
6
7
8
9
10
11
import ReactFlow, { ReactFlowProvider } from 'react-flow-renderer';

const FlowWithOwnProvider = () => (
<ReactFlowProvider>
<ReactFlow
elements={elements}
onElementClick={onElementClick}
onConnect={onConnect}
/>
</ReactFlowProvider>
);

https://www.5axxw.com/wiki/content/obkffc

react-children-utilities

返回组件中的文字

1
npm install --save react-children-utilities

使用

1
2
3
4
5
6
7
import React from 'react';
import Children from 'react-children-utilities';

const MyComponent = ({ children }) => {
const onlySpans = Children.filter(children, (child) => child.type === 'span');
return <div>{onlySpans}</div>;
};

其他api:

https://www.npmjs.com/package/react-children-utilities

react-beautiful-dnd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'

class DraggableTags extends Component {
render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, _snapshot)=> (
<Draggable>
{(provided, _snapshot)=> (
style={

}
)}
</Draggable>
)}
</Droppable>
</DragDropContext>
)
}
}

react-dnd

Https://juejin.cn/post/6933036276660731912 

react-intl-universal

不建议使用react-intl,而使用React-intl-universal实现

建立英文和中文语言包,可以是json或者js文件

1
2
3
4
5
6
7
8
const en_US = {
'hello':'nihao',
'name': 'zhangsan',
'age': '30',
'changelang': 'qiehuanyuyan',
}

export default en_US

中文包

1
2
3
4
5
6
7
8
const zh_CN = {
'hello':'nihao',
'name': 'zhangsan',
'age': '30',
'changelang': 'qiehuanyuyan',
}

export default zh_CN

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import intl from 'react-intl-universal'
import cn from '../../assets/locals/zh-CN'
import us from '../../assets'

class IntlExample extends React.Component{
constructor(){
super();
this.locals = {
'zh_CN': cn,
'en_US': us
}
this.state = {
intl: cn
}
}

componentDidMount() {
this.initLocale();
}
initLocale(locale="zh_CN"){

}
}

react-hot-loader

React-Hot-Loader 使用了 Webpack HMR API,针对 React 框架实现了对单个 component 的热替换,并且能够保持组件的 state。
React-Hot-Loader 在编译时会在每一个 React component 外封装一层,每一个这样的封装都会注册自己的 module.hot.accept 回调,它们会监听每一个 component 的更新,在当前 component 代码更新时只替换自己的模块,而不是整个替换 root component。
同时,React-Hot-Loader 对 component 的封装还会代理 component 的 state,所以当 component 替换之后依然能够保持之前的 state。

安装

1
npm install --save-dev react-hot-loader

hot-loader 是基于 webpack-dev-server,所以还得安装 webpack-dev-server

1
npm install --save-dev webpack-dev-server

首先还是要让 webpack-dev-server 打开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');

new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true
}).listen(3000, 'localhost', function (err, result) {
if (err) {
return console.log(err);
}

console.log('Listening at http://localhost:3000/')
});

然后在 webpack 的配置文件里添加 react-hot-loader。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var webpack = require('webpack');

module.exports = {
// 修改 entry
entry: [
// 写在入口文件之前
"webpack-dev-server/client?http://0.0.0.0:3000",
"webpack/hot/only-dev-server",
// 这里是你的入口文件
"./src/app.js",
],
output: {
path: __dirname,
filename: "build/js/bundle.js",
publicPath: "/build"
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
// 在这里添加 react-hot,注意这里使用的是loaders,所以不能用 query,应该把presets参数写在 babel 的后面
loaders: ['react-hot', 'babel?presets[]=react,presets[]=es2015']
}
]
},
// 添加插件
plugins: [
new webpack.HotModuleReplacementPlugin()
]

react-hot-toast

全屏的通知组件

安装

1
npm install react-hot-toast

使用

1
2
3
4
5
6
7
8
9
10
11
12
import toast, { Toaster } from 'react-hot-toast';

const notify = () => toast('Here is your toast.');

const App = () => {
return (
<div>
<button onClick={notify}>Make me a toast</button>
<Toaster />
</div>
);
};

remotion

用react写video

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useCurrentFrame } from "remotion";

export const MyVideo = () => {
const frame = useCurrentFrame();

return (
<div
style={{
flex: 1,
textAlign: "center",
fontSize: "7em",
}}
>
The current frame is {frame}.
</div>
);
};

配置帧数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useVideoConfig } from "remotion";

export const MyVideo = () => {
const { fps, durationInFrames, width, height } = useVideoConfig();

return (
<div
style={{
flex: 1,
textAlign: "center",
fontSize: "7em",
}}
>
This {width}px x {height}px video is {durationInFrames / fps} seconds long.
</div>
);
};

uuid

uuid是通用唯一识别码(Universally Unique Identifier)的缩写。是一种软件建构辨准,亦为开发软件基金会组织在分布式计算环境领域的一部分。其目的是让分布式系统中的所有元素具有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。

UUID由一组32位数的16进制数字构成。对于UUID,就算每纳秒产生一百万个UUID,要花100亿年才会将所有UUID用完。

格式

uuid32个16进制数字用连字号分成五组来显示,所以共有36个字符

UUID版本通过M表示,当前规范有5个版本,可选值为1、2、3、4、5,这5个版本使用不同的算法,利用不同的信息产生UUID,各版本有各版本的优势,具体来说:

uuid.v1():创建版本1(时间戳)UUID

uuid.v3():创建版本3(md5命名空间)UUID

uuid.v4():创建版本4(随机)UUID

uuid.v5():创建版本5(带SHA-1的命名空间)IIOD

安装

1
npm install uuid

使用

1
2
3
import { v4 as uuidv4} from 'uuid'

uuidv4()

可以使用uuid进行验证登陆,未登陆状态下生产uuid

1
2
3
4
5
6
7
8
9
10
11
12
13
let uuid = sessionStorage.getItem('uuid')
if(!uuid){
sessionStorage.setItem('uuid')
}

if(getToken()){
sessionStorage.removeItem('uuid');
}else {
let uuid = sessionStorage.getItem('uuid');
if(!uuid){
sessionStorage.setItem('uuid',uuidv4());
}
}

react-sortable-hoc

拖动排序

安装

1
npm install react-sortable-hoc --save

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, {Component} from 'react';
import {render} from 'react-dom';
import {SortableContainer, SortableElement} from 'react-sortable-hoc';
import arrayMove from 'array-move';

const SortableItem = SortableElement(({value}) => <li>{value}</li>);

const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem key={`item-${value}`} index={index} value={value} />
))}
</ul>
);
});

class SortableComponent extends Component {
state = {
items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
};
onSortEnd = ({oldIndex, newIndex}) => {
this.setState(({items}) => ({
items: arrayMove(items, oldIndex, newIndex),
}));
};
render() {
return <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />;
}
}

render(<SortableComponent />, document.getElementById('root'));

参数说明:

axis: 拖动时的轴。默认为y,当需要有多个方向时需要指定为xy

lockAxis:锁定子项目拖动的轴,可以为x或者y

useDragHandle:使用拖动图标。不是整个子项拖动

K-bar

k-bar可以给react部署的站点提供一个舒服的搜索框样式

安装

1
npm install kbar

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import {
KBarProvider,
KBarPortal,
KBarPositioner,
KBarAnimator,
KBarSearch,
useMatches,
NO_GROUP
} from "kbar";

function MyApp() {
const actions = [
{
id: "blog",
name: "Blog",
shortcut: ["b"],
keywords: "writing words",
perform: () => (window.location.pathname = "blog"),
},
{
id: "contact",
name: "Contact",
shortcut: ["c"],
keywords: "email",
perform: () => (window.location.pathname = "contact"),
},
]

return (
<KBarProvider actions={actions}>
<KBarPortal> // Renders the content outside the root node
<KBarPositioner> // Centers the content
<KBarAnimator> // Handles the show/hide and height animations
<KBarSearch /> // Search input
<RenderResults />;
</KBarAnimator>
</KBarPositioner>
</KBarPortal>
<MyApp />
</KBarProvider>;
);
}

自定义搜索结果样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import {
// ...
KBarResults,
useMatches,
NO_GROUP,
} from "kbar";

function RenderResults() {
const { results } = useMatches();

return (
<KBarResults
items={results}
onRender={({ item, active }) =>
typeof item === "string" ? (
<div>{item}</div>
) : (
<div
style={{
background: active ? "#eee" : "transparent",
}}
>
{item.name}
</div>
)
}
/>
);
}

nanoid

react-virtualized

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import {AutoSizer, List, CellMeasurerCache, CellMeasurer} from 'react-virtualized'

// 宽度固定
const measureCache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 60,
})

// 每一行内容
const rowRenderer = ({ index, key, parent, style }) => {
const item = records[index]

return (
<CellMeasurer cache={measureCache} key={key} parent={parent} rowIndex={index}>
<div style={style}>
content
</div>
</CellMeasurer>
)
}

<AutoSizer>
{({width, height}) => (
<List
width={width}
height={height}
deferredMeasurementCache={measureCache}
rowCount={records.length}
rowHeight={measureCache.rowHeight}
rowRenderer={rowRenderer}
className={styles.list}
/>
)}
</AutoSizer>

无限滚动 + 可编辑表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import {Table, Column} from 'react-virtualized'
import {Input, Empty} from 'antd'

// 防止Input组件不必要的渲染
const MemoInput = React.memo(function (props) {
const {rowIndex, field, handleFieldChange, ...restProps} = props
return <Input {... restProps} onChange={(e) => handleFieldChange(field, e, rowIndex)} />
})

function VirtualTable(props) {
// ...

// 列, 使用useCallback优化
const nameColumn = ({cellData, rowIndex, dataKey }) => {
// ...
return (
<div>
<MemoInput
placeholder="请输入姓名"
value={cellData}
rowIndex={rowIndex}
field="姓名"
handleFieldChange={handleFieldChange}
/>
</div>
)
}

// 表头
const columnHeaderRenderer = useCallback(({dataKey}) => dataKey, [])

const rowGetter = useCallback(({index}) => dataSource[index], [dataSource])

const noRowsRenderer = useCallback(() => <Empty className={styles.empty} />, [])

return <Table
ref={tableRef}
className={styles.virtualTable}
headerClassName={styles.header}
rowClassName={styles.row}
headerHeight={TableHeaderHeight}
width={TableWidth}
height={TableHeight}
noRowsRenderer={noRowsRenderer}
rowHeight={TableRowHeight}
rowGetter={rowGetter}
rowCount={dataSource.length}
overscanRowCount={OverscanRowCount}
>
<Column
width={120}
dataKey="姓名"
headerRenderer={columnHeaderRenderer}
cellRenderer={nameColumn}
/>
</Table>
}

react-window

react虚拟列表库
React Window是一个有效呈现大型列表和表格数据的组件,是React-virtualized的完全重写。

React Window专注于使软件包更小,更快,同时API(和文档)对初学者尽可能友好。

安装

1
npm i react-window

固定高度列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);

const Example = () => (
<List
height={150}
itemCount={1000}
itemSize={35}
width={300}
>
{Row}
</List>
);

VariableSizeList (可变尺寸列表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { VariableSizeList } from 'react-window';

const rowHeights = new Array(1000)
.fill(true)
.map(() => 25 + Math.round(Math.random() * 50));

const getItemSize = index => rowHeights[index]; // 此处采用随机数作为每个列表项的高度
/**
* 每个列表项的组件
* @param index:列表项的下标;style:列表项的样式(此参数必须传入列表项的组件中,否则会出现滚动到下方出现空白的情况)
**/
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);

const Example = () => (
<VariableSizeList
height={150} // 列表可视区域的高度
itemCount={1000} // 列表数据长度
itemSize={getItemSize} // 设置列表项的高度
layout= "vertical" // (vertical/horizontal) 默认为vertical,此为设置列表的方向
width={300}
>
{Row}
<VariableSizeList>
);

结合react-virtualized-auto-sizer使列表自适应当前页面的宽高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { FixedSizeList } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
const Example = () => (
<AutoSizer>
{({ height, width }) => (
<FixedSizeList
className="List"
height={height}
itemCount={1000}
itemSize={35}
width={width}
>
{Row}
</FixedSizeList>
)}
</AutoSizer>
);

react-sticky

让组件实现类似position-sticky的效果

安装

1
npm install react-sticky

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from 'react';
import { StickyContainer, Sticky } from 'react-sticky';

class App extends React.Component {
render() {
return (
<StickyContainer>
{/* Other elements can be in between `StickyContainer` and `Sticky`,
but certain styles can break the positioning logic used. */}
<Sticky>
{({
style,

// the following are also available but unused in this example
isSticky,
wasSticky,
distanceFromTop,
distanceFromBottom,
calculatedHeight
}) => (
<header style={style}>
{/* ... */}
</header>
)}
</Sticky>
{/* ... */}
</StickyContainer>
);
},
}

sticky上可以添加不同的属性

1
2
3
4
5
6
7
<StickyContainer>
...
<Sticky topOffset={80} bottomOffset={80} disableCompensation>
{ props => (...) }
</Sticky>
...
</StickyContainer>

crypto-browserify

加密

cookie插件

1
npm install js-cookie --save

引用

1
2
3
4
5
6
7
8
9
10
11
import Cookies from 'js-cookie'

//设置cookie
Cookies.set('name','value',{expire:7,path:''}); //7天过期
Cookies.set('name',{foo:'bar'}); //设置一个json
//获取cookie
Cookies.get('name'); //获取cookie
Cookies.get(); //读取所有cookie

//删除cookie
Cookies.remove('name'); //删除cookie

react-color

react-color是一个拾色器,通过它获取颜色值

安装

1
npm i react-color -S

使用

1
2
3
4
5
6
7
8
9
10
import { TwitterPicker } from 'react-dom'

function () {
render() {
<TwitterPicker
width="240px"

/>
}
}

react-lazyload

安装

1
npm install --save react-lazyload

懒加载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React from 'react';
import ReactDOM from 'react-dom';
import LazyLoad from 'react-lazyload';
import MyComponent from './MyComponent';

const App = () => {
return (
<div className="list">
<LazyLoad height={200}>
<img src="tiger.jpg" /> /*
Lazy loading images is supported out of box,
no extra config needed, set `height` for better
experience
*/
</LazyLoad>
<LazyLoad height={200} once >
/* Once this component is loaded, LazyLoad will
not care about it anymore, set this to `true`
if you're concerned about improving performance */
<MyComponent />
</LazyLoad>
<LazyLoad height={200} offset={100}>
/* This component will be loaded when it's top
edge is 100px from viewport. It's useful to
make user ignorant about lazy load effect. */
<MyComponent />
</LazyLoad>
<LazyLoad>
<MyComponent />
</LazyLoad>
</div>
);
};

ReactDOM.render(<App />, document.body);

默认懒加载组件

1
2
3
4
5
6
7
8
9
10
11
12
import { lazyload } from 'react-lazyload';

@lazyload({
height: 200,
once: true,
offset: 100
})
class MyComponent extends React.Component {
render() {
return <div>this component is lazyloaded by default!</div>;
}
}

评论

You forgot to set the app_id or app_key for Valine. Please set it in _config.yml.

 

本文章阅读量:

  0

IT学徒、技术民工、斜杠青年

机器人爱好者、摄影爱好者

PS、PR、LR、达芬奇潜在学习者

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×