小卷子搬砖记之React开荒(一)

react白菜一颗,人老了脑子不好用,就多写一写,愿能坚持更新下去

背景和特性

  1. 传统ui操作关注细节太多
  2. 程序状态多,不好跟踪和维护

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生命周期

reactLifeCircle.png

三个阶段

  • 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) )

reactDiff.png

上图的一次更新中,涉及到几个主要的差异

  • 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来获取值

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享