深入理解React(二) —— 数据流和事件原理

版权声明:本文由左明原创文章,转载请注明出处: 
文章原文链接:https://www.qcloud.com/community/article/158

来源:腾云阁 https://www.qcloud.com/community


这个,叫做竹笕,是中日传统禅文化中常见的庭院装饰品,它的构造可简单可复杂,但原理很简单,比如这个竹笕,水从竹笕顶部入口流入内部,并按照固定的顺序从上向下依次流入各个小竹筒,然后驱动水轮转动。对于强迫症患者来说,观赏竹笕的绝对是一种很享受的过程的最爱,你会发现这些小玩意竟然能这么流畅的协调起来,好神奇。

如果竹笕是一个组件的话,那么水就是组件的数据流。

在React中,数据流是自上而下单向的从父节点传递到子节点,所以组件是简单且容易把握的,他们只需要从父节点提供的props中获取数据并渲染即可。如果顶层组件的某个prop改变了,React会递归地向下遍历整棵组件数,重新渲染所有使用这个属性的组件。

这个是前面看到的 KM 热点问题组件,拥有一个叫做 articles 的属性。

在组件内部,可以通过this.props来访问props,props是组件唯一的数据来源,对于组件来说:

props永远是只读的。

不要尝试在组件内部调用setProps方法来修改props,如果你不小心这么做了,React会报错并给出非常详细的错误提示。

组件的属性类型如果不进行声明和验证,那么很可能使用者传给你的属性值或者类型是无效的,那会导致一些意料之外的故障。好在React已经为我们提供了一套非常简单好用的属性校验机制——

React有一个PropTypes属性校验工具,经过简单的配置即可。当使用者传入的参数不满足校验规则时,React会给出非常详细的警告,定位问题不要太容易。

PropTypes包含的校验类型包括基本类型、数组、对象、实例、枚举——


以及对象类型的深入验证等等。如果内置的验证类型不满足需求,还可以通过自定义规则来验证。
如果某个属性是必须的,在类型后面加上 isRequired 就可以了。

React的一大创新,就是把每一个组件都看成是一个状态机,组件内部通过state来维护组件状态的变化,这也是state唯一的作用。

state一般和事件一起使用,我们先看state,然后看看state和事件怎样结合。
这是一个简单的开关组件,开关状态会以文字的形式表现在按钮的文本上。
首先看render方法,返回了一个button元素,给button注册了一个事件用来处理点击事件,在点击事件中对state的on字段取反,并执行 this.setState() 方法设置on字段的新值。一个开关组件就完成了。


组件渲染完成后,必须有UI事件的支持才能正常工作。

React通过将事件处理器绑定到组件上来处理事件。
React事件本质上和原生JS一样,鼠标事件用来处理点击操作,表单事件用于表单元素变化等,Rreact事件的命名、行为和原生JS差不多,不一样的地方是React事件名区分大小写。
比如这段代码中,Article组件的section节点注册了一个onClick事件,点击后弹出alert。
有时候,事件的处理器需要由组件的使用者来提供,这时可以通过props将事件处理器传进来。

这个是刚才那个Article组件的使用者,它提供给Article组件的props中包含了一个onClick属性,这个onClick指向这个组件自身的一个事件处理器,这样就实现了在组件外部处理事件回调。

这是一个React组件实现组件可交互所需的流程,render()输出虚拟DOM,虚拟DOM转为DOM,再在DOM上注册事件,事件触发setState()修改数据,在每次调用setState方法时,React会自动执行render方法来更新虚拟DOM,如果组件已经被渲染,那么还会更新到DOM中去。

这些是React目前支持的事件列表。

React的组件拥有一套清晰完整而且非常容易理解的生命周期机制,大体可以分为三个过程:初始化、更新和销毁,在组件生命周期中,随着组件的props或者state发生改变,它的虚拟DOM和DOM表现也将有相应的变化。

首先是初始化过程,这里会着重讲,需要充分理解。
组件类在声明时,会先调用 getDefaultProps() 方法来获取默认props值,这个方法会且只会在声明组件类时调用一次,这一点需要注意,它返回的默认props由所有实例共享。
在组件被实例化之前,会先调用一次实例方法 getInitialState() 方法,用于获取这个组件的初始state。
实例化之后就是渲染,componentWillMount方法会在生成虚拟DOM之前被调用,你可以在这里对组件的渲染做一些准备工作,比如计算目标容器尺寸然后修改组件自身的尺寸以适应目标容器等等。
接下来就是渲染工作,在这里你会创建一个虚拟DOM用来表示组件的结构。对于一个组件来说,render 是唯一一个必须的方法。render方法需要满足这几点:
1.只能通过 this.props 或 this.state 访问数据
2.只能出现一个顶级组件
3.可以返回 null、false 或任何 React 组件
4.不能对 props、state 或 DOM 进行修改
需要注意的是,render 方法返回的是虚拟DOM。

渲染完成以后,我们可能需要对DOM做一些操作,比如截屏、上报日志、或者初始化iScroll等第三方非React插件,可以在 componentDidMount() 方法中做这些事情。当然,你也可以在这个方法里通过 this.getDOMNode() 方法取得最终生成DOM节点,然后对DOM节点做爱做的事情,但需要注意做好安全措施,不要缓存已经生成的DOM节点,因为这些DOM节点随时可能被替换掉,所以应该在每次用的时候去读取。

组件被初始化完成后,它的状态会随着用户的操作、时间的推移、数据更新而产生变化,变化的过程是组件声明周期的另一部分 ——

更新过程。

当组件已经被实例化后,使用者调用 setProps() 方法修改组件的数据时,组件的 componentWillReceiveProps() 方法会被调用,在这里,你可以对外部传入的数据进行一些预处理,比如从props中读取数据写入state。

默认情况下,使用者调用组件的 setProps() 方法后,React会遍历这个组件的所有子组件,进行“灌水”,将props从上到下一层一层传下去,并逐个执行更新操作,虽然React内部已经进行过很多的优化,这个过程并不会花费多少时间,但是程序员里永远不缺乏长期性能饥渴的同学,不用担心,React有一个能够解决你性能饥渴的办法——shouldComponentUpdate()。
有时候,props发生了变化,但组件和子组件并不会因为这个props的变化而发生变化,打个比方,你有一个表单组件,你想要修改表单的name,同时你能够确信这个name不会对组件的渲染产生任何影响,那么你可以直接在这个方法里return false来终止后续行为。这样就能够避免无效的虚拟DOM对比了,对性能会有明显提升。
如果这个时候有同学仍然饥渴难耐,那么你可以尝试 不可变数据结构(用过mongodb的同学应该懂)。
组件在更新前,React会执行componentWillUpdate() 方法,这个方法类似于前面看到的 componentWillMount()方法,唯一不同的地方只是这个方法在执行的时候组件是已经渲染过的。需要注意的是,不可以在这个方法中修改props或state,如果要修改,应当在 componentWillReceiveProps() 中修改。
然后是渲染,React会拿这次返回的虚拟DOM和缓存中的虚拟DOM进行对比,找出【最小修改点】,然后替换。
更新完成后,React会调用组件的componentDidUpdate 方法,这个方法类似于前面 componentDidMount 方法,你仍然可以在这里可以通过 this.getDOMNode() 方法取得最终的DOM节点。

香港电影结尾经常看到一个剧情,就是英雄打败了坏人,然后警察出来擦屁股——

警察偶尔还能立功,而 componentWillUnmount 最可怜,他除了擦屁股什么也做不了。
你可以在这个方法中销毁非React组件注册的事件、插入的节点,或者一些定时器之类。这个过程可能容易出错,比如绑定了事件却没销毁,这个React可帮不了你,你自己约的炮,含着泪也要打完。


两节内容讲了上手React所必备的知识。
后面讲价值。

直出有多快我就不多说了。
因为有虚拟DOM的存在,React可以很容易的将虚拟DOM转换为字符串,这便使我们可以只写一份UI代码,同时运行在node里和和浏览器里。

在node里将组件HTML渲染为一段HTML一句话即可。
不过围绕这个renderToString我们还要做一些准备工作。代码有点多,大家做好心理准备。

这是一个express的路由方法,在这里:
1.从后台server或数据库等来源拉取数据
2.引入要渲染的React组件
3.调用React.renderToString()方法来生成HTML
4.最后发送HTML和数据给浏览器

这里为了方便描述,没有写拉取数据的代码,大家自行脑补。

需要注意的是这里的JSON字符串中可能出现结尾标签或HTML注释,可能会导致语法错误,这里需要进行转义。

页面的示例代码本来打算用大家更熟悉的HTML,但发现代码量太多了PPT里一页放不下,所以换成了jade代码,没用过jade的同学也顺便了解一下,我也顺便给jade打个广告。
这个页面做了X个事:
1.将前面在action里生成的HTML写到#container元素里
2.引入必须的JS文件
3.获取action提供的数据
4.渲染组件

这就是React的服务端渲染,组件的代码前后端都可以复用。
<有没有没理解清楚的同学?>

是不是感觉React挺牛逼的?大家以为React就这么点能耐吗?

React能够用一套代码同时运行在浏览器和node里,而且能够以原生App的姿势运行在iOS和Android系统中,即拥有了web迭代迅速的特性,又拥有原生App的体验。
这个姿势叫做 React-Native。
这是React和React-Native在github上的数据,可以看出React-Native也是相当热门——因为React-Native能够使React的价值最大化,这个价值是什么呢——对业务来说,意味着不需要为了做终端版本就招聘和前端等量人力的终端开发,同时意味着我们成为全栈工程师有了一个捷径。

了解iOS开发的同学都知道,水果公司对应用上架的审核效率实在让人无力吐槽,很多团队上一个版本还没审核结束,下一个版本就已经做好了。而React-Native支持从网络拉取JS,这样iOS应用也能够像web一样实现快速迭代了。

这个是react-native的调试过程

作为一个没写过一句Object-C代码的web前端开发,我只用了一天时间就上手了react-native,然后用了半天时间做出了一个简单的demo页面,可以看到react-native的生产效率还是非常高的。

单元测试顾名思义,是对各个模块进行最小范围的测试,容易。
我们来演示一个checkbox的单元测试过程。

看代码

因为虚拟DOM的存在,使得react的代码很容易做好单元测试,这是上面那段代码的测试用例,通过karma执行后即可看到结果。

所以你可能需要这些东西


课后练习

(如果你已经看到这里了,为何不再花1分钟思考一下上面3个问题)

上一期React技术文章:

深入理解 React (一) - JSX和虚拟DOM

时间: 2024-10-14 11:12:27

深入理解React(二) —— 数据流和事件原理的相关文章

React Native 从入门到原理

抛砖引玉(帮你更好的去理解怎么产生的 能做什么) 砖一.动态配置 由于 AppStore 审核周期的限制,如何动态的更改 app 成为了永恒的话题.无论采用何种方式,我们的流程总是可以归结为以下三部曲:"从 Server 获取配置 –> 解析 –> 执行native代码". 很多时候,我们自觉或者不自觉的利用 JSON 文件实现动态配置的效果,它的核心流程是: 通过 HTTP 请求获取 JSON 格式的配置文件. 配置文件中标记了每一个元素的属性,比如位置,颜色,图片 UR

React-Native系列Android——Touch事件原理及状态效果

Native原生相比于Hybrid或H5最大优点是具有流畅和复杂的交互效果,触摸事件便是其中重要一项,包括点击(Click).长按(LongClick).手势(gesture)等. 以最简单常见的点击(Click)为例,Native组件可以自定义selector,使得被点击的组件具有动态效果,Android 5.0以上甚至可以有涟漪效果(Material Design).而这些在Hybrid或H5中很难实现,很多时候区分它们与原生最简单的方法就是检验点击交互效果. React-Native的强大

React Native 从入门到原理一

React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通俗的语言解释了相关的名词,重点介绍 React Native 出现的背景和试图解决的问题.适合新手对 React Native 形成初步了解. 下半部分则通过源码(0.27 版本)分析 React Native 的工作原理,适合深入学习理解 React Na

关于《手机扫描电脑二维码登录原理》的学习

技术学习:手机扫描电脑二维码登录原理 通用地实现方式(以登录电脑浏览器网页版微信为例): 1.每打开一次微信(Client)电脑浏览器网页时会随机生成一个含有唯一uid的二维码,每次刷新页面都会不一样(*这个可以保证一个uid只可以绑定一个帐号和密码,如果一个uid可以绑定多个帐号和密码,那么很可能你的电脑会登录别人的微信) ps: 返回uid的目的是识别用户身份,而且实际上打开这个页面时浏览器已经和Server创建了一个长连接等待确认信息.这个页面在加载完毕时,也已经把很多登录后才需要的相关资

深入理解OOP(二):多态和继承(继承)

本文是深入浅出OOP第二篇,主要说说继承的话题. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时多态) 深入理解OOP(四):多态和继承(C#中的抽象类) 深入理解OOP(五):C#中的访问修饰符(Public/Private/Protected/Internal/Sealed/Constants/Static and Readonly Fields) 深入理解OOP(六):枚举(实用方法)

图解ARP协议(二)ARP攻击原理与实践

一.ARP攻击概述 在上篇文章里,我给大家普及了ARP协议的基本原理,包括ARP请求应答.数据包结构以及协议分层标准,今天我们继续讨论大家最感兴趣的话题:ARP攻击原理是什么?通过ARP攻击可以做什么,账号是否可以被窃取?有哪些常见的ARP渗透(攻击)工具可以用来练手?ARP扫描和攻击有什么区别,底层数据包特征是怎样的? 接下来,我们通过图解的方式来深入了解ARP攻击是如何实现的. 二.ARP攻击原理 但凡局域网存在ARP攻击,都说明网络存在"中间人",我们可以用下图来解释. 在这个局

理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?

作者:Wang Namelos链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. . React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化.理解这个是理解React和Redux的前提.2. 一般构建的R

理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?(转)

作者:Wang Namelos 链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎 解答这个问题并不困难:唯一的要求是你熟悉React.不要光听别人描述名词,理解起来是很困难的.从需求出发,看看使用React需要什么:1. React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内

深入理解ButterKnife源码并掌握原理(一)

前言 话说在android这座大山里,有一座庙(方块公司-square),庙里住着一个神-jake(我是这么叫的嘻嘻). 不要小看这个小jake,这个神可是为android应用开发们提供了强有力的帮助.比如流行的开源库okhttp,eventbus系列 ,retrofit,butterknife 等等都是出于他之手.小弟佩服的不要不要的-,可以说是为android的应用开发效率和耦合性提高了一个台阶啊. 其它的大神我也是佩服的不要不要的-嘻嘻 声明 这一系列的文章是对ButterKnife的源码