React 是一个JS UI库,用来构建用户界面。开发设计时,我们将UI界面分成独立的小块,每块就是一个组件,通过组件之间的组合嵌套来构建页面。
React 的3个特性:
(1)React 编程遵循:组件设计模式、声明式UI和函数式编程。
(2)使用虚拟dom去操作真实的dom。
(3)遵循从高阶组件流向低阶组件的单向数据流。
一、组件设计模式
1. 前端工程化、模块化、组件化
能够降低成本、提供项目质量和开发效率的事情都属于工程化,比如程序的性能、稳定性、可维护性等。前端工程化可以细分为模块化、组件化、规范化和自动化 4个方面。
(1)模块化:侧重于业务功能层面的拆分,比如购物、直播功能。一般来说,一个模块就是一个实现特定功能的文件,可以通过多个组件来构建。JS 模块化方案有AMD、CMD、module...,CSS 模块化方案有 less、sass..
(2)组件化:侧重于UI设计层面的拆分,比如提交按钮、确认按钮。组件独立可复用,组件之间自由组合。(每个UI组件都应该包含html、css、js文件)
(3)规范化:包括编码规范(eslint、文件命名规范..)、开发流程规范(code review..)等。
(4)自动化:包括自动化测试、构建、部署等,将简单重复的工作交给机器来做。(开发者提交本地代码后,merge master后就会跑自动化测试,包含单元测试、集成测试、e2e测试;测试通过后,代码会合入master,然后开始build,将源码转换为可运行的实际代码,比如安装依赖、配置各种资源等;然后当前代码就是一个可以直接部署的release版本)
2. 如何封装一个组件
React、Vue等框架都在引领着前端的组件化开发方向,组件封装含义是不会直接暴露内部结构,而是提供props去控制组件。
(1)为什么要封装组件?
- 有利于代码的复用,减少代码冗余。
- 有利于代码的维护。
- 有利于单元测试。
(2)封装组件需要考虑的点
- 单一职责原则:一个组件只负责一件事情,不要耦合一些没必要的逻辑。
- 可复用性:需要考虑适用的不同场景,考虑组件功能的通用性,可以被使用在多个UI场景。(公用组件更多要考虑通用性,项目组件则是需要处理当前业务中的特殊场景,业务部门一般都是项目组件)(UI组件只负责渲染,侧重复用性;业务组件侧重数据和业务的逻辑处理)
- 可组合:易于和其它组件一起使用,或者嵌套在另一个组件内部
- 可维护性:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护。像业务组件如果过度追求可复用性,兼容各种页面,这就会导致它本身变得难以维护。
- 组件的粒度:拆分并不是越细越好,根据具体的业务场景分析,尽量复合高内聚、低耦合的思路,使自己的组件易于维护。
二、声明式渲染、函数式编程
1. 声明式UI
(1)命令式UI:UI的更新是由开发人员使用代码主动刷新,UI与数据没有必然的映射关系。
(2)声明式UI:UI的更新不是主动刷新的,而是由渲染机制来负责更新,UI与数据有映射关系。
React 使用声明式UI,dom节点会随着状态的更新而更新,不需要手动操作dom。
补充:
- 命令式编程:告诉计算机每一步的具体操作,先做什么后做什么,重点关注怎么做,看重过程。
- 声明式编程:告诉计算机想要什么结果,重点关注要做什么,看重结果。
- 命令式编程:面向过程编程C,面向对象编程C++、Java。
- 声明式编程:函数式编程,领域专属语言DSL...。
2. 函数式编程
虽然 html、css也是声明式语言,但我们这里讲的声明式是可以处理逻辑的声明式,用到了很多函数式编程思想,是基于函数式编程实现的。
函数式编程是一种以使用函数为主的软件开发风格,初始数据通过多个函数依次处理,最后完成整体输出,能够避免可变数据和副作用的产生。函数式编程最重要的概念是纯函数,
2.1 纯函数满足的三个特征
- 无副作用:函数的副作用指的是在调用函数时,除了返回函数值还产生了其他的影响,例如:更改全局变量、处理用户输入、打印数据、调用Data.now()... 副作用影响代码可读性,会导致一些难以排查的错误。
- 引用透明:指的是一个函数对于相同输入始终会得到相同的输出,不依赖外部环境。
- 数据不可变(immutable):数据一旦被创建,就不能再更改。如果想要修改,那只能通过复制数据副本、在新对象上修改然后返回新数据的方式进行修改。
2.2 纯函数的好处和应用
- 纯函数的好处:(1)可以将结果缓存,对于那些需要多次调用&&执行耗时的函数,缓存结果可以缩短时间。(2)更容易测试,测试时可以保证输出稳定。
- 纯函数的应用:slice()、redux中的reducer是纯函数,splice不是纯函数。
2.3 函数式编程的常见应用
- 柯里化:把接受多个参数的函数变换成接受一个单一参数的函数,并返回接受剩余参数而且返回结果的新函数。
- 组合compose:把复杂的逻辑拆分成一个个简单的任务,最后组合起来完成任务。
- 高阶函数:以函数为参数或返回值是函数的函数,比如map、filter函数。
- 闭包。
- redux,react的函数式组件,react中的高阶组件(对应高阶函数)。
2.4 react 为什么推崇函数式编程:React Hooks
- 函数式编程强调数据的不可变性,这有利于在 shouldComponentUpdate 生命周期时,React 对新旧 state 只需通过浅比较即可判断是否修改,提高了效率。
- 开发速度快,因为无副作用稳定性更好,可以直接复用。
- 更容易测试。
三、单向数据流
数据流,指的是组件之间的数据流动,与数据绑定没有任何关系。
父组件可以向子组件传递props,但是子组件只能通过事件通知父组件进行数据更改,不能直接修改父组件传递来的prop。如果父组件的某个props改变了,那么React会重新渲染所有的子节点。
四、组件的生命周期(16.4开始)
每个组件都包含 “生命周期方法”,组件在进入和离开dom时要经历一系列的生命周期方法。
新旧版本对比:
- 删除了三个生命周期componentWillMount、componentWillUpdate、componentWillReceiveProps:因为这些生命周期经常被误用,像render前面的componentWillUpdate生命周期,如果在里面操作真实dom,调用时页面就会重绘,降低react性能。
- 新增两个生命周期getDerivedStateFromProps、getSnapshotBeforeUpdate,
五、与vue的比较
1. react 与 vue的异同
二者都是创建UI的库,都使用虚拟dom,路由、状态管理都是框架外的组件,区别在于:
- 语法:vue使用模板文件、易上手;react 基于JSX语法,把html和js混合起来、更灵活。
- 监听数据变化的原理:vue采用watch的方式,react 是通过比较引用的方式。
- react 一直是单向数据流;vue1.0是双向数据流,vue2.x不再支持父子组件的双向绑定。
2. react 相比vue的优势
- 支持适用于Android和iOS平台的移动端原生应用程序。
- 生态圈更大,会带来更多的支持和工具。
- 函数式编程会有更好的测试性。
3. react 相比原生js操作dom的优势
性能上有很大优势,主要由于 虚拟dom的batching策略和diff算法,
- batching把所有dom操作搜集起来,一次性提交给真实DOM。
- diff算法时间复杂度也降到了O(n)。