React

概念

特点

  1. 组件化
  2. Virtual DOM

流程

React,用户不会直接操作DOM,而是用户直接更改状态,让React帮助操作DOM。具体如下:

graph TB
A(用户更改状态) --> B(Render)
B --> C(新的完整的Virtual DOM)

C1(旧Virtual DOM) --> D(DIFF比较差异)
C --> D

D--> E(一次性更新真实DOM)

style A fill:greenyellow
style E fill:greenyellow

注意点:

  1. 当父组件state变动,会引发所有子组件Render(即便子组件本身的state毫无改变)。如果不想引发所有子组件render,见步骤5。
  2. Render渲染出来的是完整的Virtual DOM。
  3. 利用DIFF算法,比较新旧两个Virtual DOM结构的差异(树对象的比较)
  4. 将差异变动,一次性渲染到真实DOM树。
  5. 如果不想引发所有子组件render,目前有两种方案:

    • shouldComponentUpdate() {return false}

    • immutablejs + PureComponent

期待

React增加了VDOM的比较时间、减少了浏览器操作。

在重复渲染的时才可能提高性能。

因此,React完成了框架的意义:降低开发者的开发成本,但真正的性能比不上经过认真优化过的原生JS。

为什么DOM操作浪费资源?

  • 操作js对象

  • 触发浏览器行为(罪魁祸首)

    • 重绘paint——调用浏览器UI引擎渲染展示页面(耗CPU和内存)
    • 重排layout——布局变动,造成重新计算(耗CPU,有时也很耗内存)

其中,重绘重排 最浪费性能。

graph TB
A(DOM操作 -- 触发 --浏览器行为) --> B>浏览器解析DOM生成DOM树]
A --> C>浏览器解析CSS生成样式树]

C--> D>进行layout和paint]
B--> D

D --> E(展现到设备)

为了减少重绘重排,可做如下努力:

  • 用Canvas / CSS3 , 替代DOM动画
  • JQuery 减少DOM操作——

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 避免,频繁DOM操作
    for (var i=0; i < items.length; i++){
    var item = document.createElement("li");
    item.appendChild(document.createTextNode("Option " + i));
    list.appendChild(item);
    }

    // 推荐,使用容器存放临时变更, 最后再一次性更新DOM
    var fragment = document.createDocumentFragment();
    for (var i=0; i < items.length; i++){
    var item = document.createElement("li");
    item.appendChild(document.createTextNode("Option " + i));
    fragment.appendChild(item);
    }
    list.appendChild(fragment);
  • 上述所讲Virtual DOM,也是减少DOM操作的一个手段: 通过预先比较,得到差异,一次性更新DOM。

react-router

传参

react-router 4.x 传参不再支持query string。query的值不会反映到url上,而是放在内存里,状态非持久,刷页面,query值丢失。

如果你想打开全新的浏览器窗口,必须将参数反馈到url地址,可采取如下办法:

办法1: 传递多个参数。

使用【 search + 字符串解析工具 】

1
2
3
4
5
6
7
8
9
10
11
12
13
// 路由route定义
path: '/app-detail'


// 路由使用(NavLink 或 push 配置)
{
pathname: '/app-detail',
search: `id=${row.id}&isEdit=${isEdit}` // 这里不需要写问号?
}

// 获取参数
import querystring from 'querystring'
const {id, isEdit} = querystring.parse(this.props.location.search)
办法2: 传递少量参数

将变量放到路径

1
2
3
4
5
6
7
8
9
10
// 路由route定义
path: '/app-detail/:id?/:isEdit?'

// 路由使用(NavLink 或 push 配置)
{
pathname: `/app-detail/${id}/${isEdit}`,
}

// 获取参数
const {id, isEdit} = this.props.match.params;

高阶组件

graph TD
A(共用组件) --引用--> B(组件1)
A --引用--> C(组件2)
A --引用--> D(组件3)

高阶组件定义–用作公共组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import './box.less';

const Box = InnerComponent => class extends React.Component {
render() {
return (
<div className={`box-wrapper ${this.props.className}`}>
<InnerComponent {...this.props} />
<div className="box-mask" />
</div>
);
}
};
export default Box;

高阶组件调用–用共用组件包裹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 组件1
import React from 'react';
import Box from '../Card/box';

class LineTrend extends React.Component {
}

export default Box(LineTrend);

// 组件2
import React from 'react';
import Box from '../Card/box';

class Pie extends React.Component {
}

export default Box(Pie);

// ...其他组件

hooks

react的特点是组件化,而定义组建的方式如下:

  1. 函数组件: function。数据驱动,只负责渲染。方便复用,可独立测试。
  2. 类组件: class。包含状态,生命周期。提供数据维护及渲染。不方便复用。
  3. hook: 让函数组件具备类组件的特性,比如状态和生命周期。

版本: React v16.8及以上

作用: use state and other React features without writing a class。利用function定义组件时,可使用state等React特性。(不一定要class才有状态和生命周期啦, function也可以!)

  1. 组件间状态逻辑的复用

旧有方式中,如果想复用状态,有如下方式:

  • react:
    • render props :维护状态的组件+负责渲染的子组件
    • higher-order components :高阶组件(一个函数接受一个组件作为参数)
    • 将state置于父组件,使其子组件可复用(陷入wrapper hell
  • redux: store + props 状态属于全局而非哪个组件,所以可以任意复用(状态不随组件而卸载,所以当使用该数据的组件都已被卸载,但数据仍然像幽灵一样存在)

hooks:允许复用状态逻辑(reuse stateful logic),而不更改组件层级。即,一个独立组件,既方便渲染又自己包含状态。

  1. 生命周期钩子函数
    通常在componentDidMount中做很多不同业务类型的事情,比如ajax请求、事件监听等。有时还需在componentDidUpdate做一遍同样的事情。

hooks:希望把不同业务类型独立书写,逻辑更直观。

  1. class组件
    类组件,存在this指向问题、不方便复用、最初敲定使用function后因业务变动需大动干戈改为class组件等问题。

hook:本质是带着状态的function,不存在上述问题。

注意点:React是通过Hook调用的次序来记录各个内部状态的,因此react规定我们必须把hooks写在函数的最外层,不能写在if-else等条件语句当中,来确保hooks的执行顺序一致。

useState

状态维护。替代state 和 onStateChange。

利用数组解构,在function中实现如下功能:

  1. 定义状态变量
  2. 定义更新状态方法(直接替换老状态、返回全新状态)
  3. 给出初始状态值

useEffect

状态更改后,做一些操作。替代生命周期函数(如,componentDidMount,componentDidUpdate和componentWillUnmount)。

  1. 获取数据
  2. 操作DOM
  3. 清理计时器

useEffect执行时机:

  1. react首次渲染和之后的每次渲染都会调用传给useEffect的函数。
  2. useEffect中函数的执行不会阻碍浏览器更新视图,是异步的。
  3. useEffect中执行清理操作,需要在其中返回一个清理函数。带return(从 effect 中返回一个 function,是 effect 可选的清理机制,每个 effect 都可以返回一个在它之后清理的 function, 当组件卸载时,React 会自己执行清理函数,进行清理工作。)

可以控制useEffect执行时机:

  1. 给出第二个参数,只有当这个参数改变时,才执行。
  2. 给出第二个参数为空数组[]时,只在首次渲染的时候执行(<=> componentDidMount,少用)。

useReducer

复杂状态维护。

参考:

官网

react衍生

styled-components

用途

使样式组件化

案例

百度地图”自动搜索地址”功能。

该所得到结果下拉框的样式是”内部全局样式”。只能通过”覆盖原生样式类”实现自定义样式。无法将其挂在到某个DOM,或直接设置类名 / style等。

1
2
3
4
5
// 引用百度地图api
const suggest = new BMap.Autocomplete({
input: inputId,
location: map,
});
1
2
3
4
5
6
7
8
9
10
11
/* 原生样式类 */
.tangram-suggestion-main {
.tangram-suggestion {
}

.tangram-suggestion table tr td {
}

.tangram-suggestion-grey {
}
}

当该全局地图样式需要实现多套样式主题切换(换肤),该怎么实现呢?

以下方案无效:

  1. 切换主题时,动态切换类名。(因为无法给自动搜索地址注入类名)
  2. 切换主题时,动态切换style对象值。(因为无法给自动搜索地址注入style)
  3. 将不同样式写入不同css文件,切换主题时,动态引入css文件。
    1. 因为需要判断主题类型,因此不能采用import一开始就引入,只能采用require引入。
    2. require引入后,将不能卸载,多个主题文件同时存在,后者覆盖前者。

实现手段

将主题样式组件化,动态引入/卸载组件。

1
2
3
{
theme === 'day' ? <DayStyle /> : <NightStyle />
}
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
58
59
60
61
import { createGlobalStyle } from 'styled-components';

const NightStyle = createGlobalStyle`
.tangram-suggestion-main {
z-index: 150;
margin-top: 10px;

.tangram-suggestion {
background: rgba(19, 39, 56, 0.82);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.73);
color: #fff;
border: 1px solid #2C9CFA;
height: 8rem;
overflow-y: auto;
}

.tangram-suggestion table tr td {
height: 24px;
line-height: 24px;
color: #fff;

&:hover, &:focus, &.tangram-suggestion-current {
background: rgba(44, 156, 250, 0.8);
}
}

.tangram-suggestion-grey {
color: #c0c0c0;
}
}
`;
const DayStyle = createGlobalStyle`
.tangram-suggestion-main {
z-index: 150;
margin-top: 10px;

.tangram-suggestion {
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 1px 3px 0 rgba(255, 255, 255, 0.73);
color: #fff;
border: 1px solid #e9e9e9;
height: 8rem;
overflow-y: auto;
}

.tangram-suggestion table tr td {
height: 24px;
line-height: 24px;
color: #3c3e4a;

&:hover, &:focus, &.tangram-suggestion-current {
background: #e6f7ff;
}
}

.tangram-suggestion-grey {
color: #999;
}
}
`;
export { NightStyle, DayStyle };
-------------Keep It Simple Stupid-------------
0%