共计 3058 个字符,预计需要花费 8 分钟才能阅读完成。
react 白菜一颗,人老了脑子不好用,就多写一写,愿能保持更新上来
背景和个性
- 传统 ui 操作关注细节太多
- 程序状态多,不好跟踪和保护
react:始终整体刷新页面,无需关怀细节
- 一个新概念
组件:react 用组件的形式去形容 ui
- 四个 API
- 单向数据流
- 欠缺谬误提醒
解决了 ui 问题,如何解决数据模型问题?
传统的 MVC 模式中 Model 和 View 的应用关系简单,出问题之后很难追踪问题,由此提出了 Flux 架构:单向数据流
当 view 上产生了用户操作会生成一个 action,action 通过 Dispatcher dispatch 进来交给 Store 解决,view 绑定在 store 上,整体建设在状态之上进行 ui 的更新。这里集体了解 MVC 框架的劣势自身其实并不是数据的双向绑定而是外部事件机制的暗箱操作不可控,事实上 VUE 框架自身也通过 v -model 实现双向数据绑定,而当咱们不关怀表单交互过多下的业务无关代码时双向绑定能够压缩很多人工 code,不好的就是黑盒下的谬误 debug 难度晋升,单向数据流的优缺就是镜像关系了,所以,no silver bullet
组件构建 UI
传统构建表单页面个别流程是,定义 HTML 模版,JS 拿数据并填充,提交表单时用 form 绑定事件实现。
react 示例:
class CommentBox extends Component {render() {
return {
<div className="comment-box">
<h1>Comments</h1>
<CommentList/>
<CommentForm/>
</div>
}
}
}
CommentBox、CommentList、CommentForm 就是组件,组件 = props + state —-> view,react 组件的特点:
- 不提供形式,相当于状态机
- 组件相似于纯函数
- 单向的数据绑定
相应的创立一个组件须要思考:
- 动态 UI:具体应用什么 HTML tag
- 组件状态组成:state 来自内部 or 外部?
- 组件交互方式:外部如何操作?如何裸露给内部使用者
- 一个组件只做一件事 (设计模式中的繁多职责准则),如果简单,应该做拆分
- 状态能计算就不存储
JSX
不是模版引擎,是一种语法糖,能够在 js 中间接写 HTML 标记,
const element = <h1>hello, {name}</h1>;
// 相当于
const element = React.creatElement('h1', null, 'Hello,', name);
// 属性中用表达式
<MyComponent foo={1+2+3+4} />;
// 延展属性 (ES6 中也有)
const props = {firtName: 'Ben', lastName: 'Hector'};
const greeting = <Greeting {...props} />;
// 表达式作为子元素
const element = <li>{props.message}</li>
/**
* 小写 tag 为原生,自定义大写结尾
**/
用申明形式形容动态创建组件过程,能够仍然应用 js 个性和相熟的语法
React 生命周期
三个阶段
- render 阶段:计算状态
- pre-commit 阶段:读取 DOM 内容
- commit 阶段:状态映射到 DOM,更新节点
三个类型
-
mounting
-
constructor: 构造函数
- 惟一能够间接批改 state 的阶段
-
getDerivedStateFromProps(version 16.3): 内部属性初始化外部状态
- state 从 props 初始化时应用,须要保护一致性,每次 render 都会调用(典型场景:表单默认输出)
- render:必定义,形容 UI DOM 构造
-
componentDIdMount
- UI 渲染实现时调用,只执行一次
-
-
updating(new props: 传进新属性,setState:外部批改状态,forceUpdate:强制刷新)
- getDerivedStateFromProps
- shouldComponentUpdate:是否真的须要 render(可优化局部,个别由 PureComponent 主动实现)
- render:diff,虚构 dom 计算
-
getSnapshotBeforeUpdate
- render 之前调用,state 曾经更新(场景:获取 render 之前的 DOM 状态)
-
componentDidUpdate
- 每次 UI 更新时调用(场景:从新获取变动后的 props)
- Unmounting(组件隐没时,销毁,做一些资源开释的操作)
这里比照 VUE 框架两者的流程是类似的,都是经验了初始化、创立、挂载、销毁、更新,稍有不同的一点,更新过程挂载阶段 react 应用 componentDidUpdate,vue 仍然会用 mounted
virtual DOM
不论是 vue 还是 react 都是通过了虚构 DOM 的创立和部分更新来达到更快地渲染体验(分层比拟,BFS,复杂度为 O(n))
上图的一次更新中,波及到几个次要的差别
- A B 程序替换
- D 的层级变换
- C 节点的隐没
- G 的节点的格局批改
react 的解决办法(这里的 diff 不会关怀删除的某个节点有没有在其余中央用到,即认为 DOM 构造绝对稳固)
- 第二层,获取 A B 惟一标识获知程序变动,替换地位
- 第三层,F 变为 G 类型变动,删除 F,append 一个新节点到 A 上;删除 D
- 第四层,从新创立一个 D(这里想当于放弃查看,因为真实情况跨层级的节点挪动并不多,可疏忽)
组件设计
高阶组件(HOC):接管组件作为参数,返回新组件
// 一个简略的计时器 HOC
import React from "react";
export default function withTimer(WrappedComponent) {
return class extends React.Component {state = { time: new Date() };
componentDidMount() {this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {clearInterval(this.timerID);
}
tick() {
this.setState({time: new Date()
});
}
render() {return <WrappedComponent time={this.state.time} {...this.props} />;
}
};
}
// 之后将 withTimer imoport 到想应用的 js 中,并 export withTimer({xxxApp}),即可在 xxxApp 中用 this.porps.time
函数作为子组件(组件如何 render 由应用组件的人来决定,升高 scope,减少灵活性):
//MyComponent 将一个函数作为 children,是一种通用的设计模式思维
class MyComponent extends React.Component {render() {
return (
<div>
{this.props.children('Nate Wang')}
</div>
)
}
}
<MyComponent>
{(name) => (<div>{name}</div> // 这里能够写任何 UI
)}
</MyComponent>
context API(version16.3 新个性)
罕用场景:组件树共享全局上下文数据,不须要一层层传递。const Context = React.creatContext('shareData');
通过 Provider value=”” 来提供值,Consumer state.name 来获取值