### 声明式开发
- 命令式开发:原生js和jq写代码的时候,大部分代码都是在操作dom,这种开发模式就是命令式开发。
- 声明式开发:react是面向数据编程,不需要直接去控制dom,你只要把数据操作好,react自己回去帮你操作dom,可以节省很多操作dom的代码。这就是声明式开发。
### 可以和其他框架并存
- react所控制的dom就是id为root的dom,页面上的其他dom元素你页可以使用jq等其他框架。所以react是可以和其他框架并存的。
### 组件化
- 在我们写todo-list的时候,我们已经使用react的组件了。通过继承react的Component去创建一个组件。
### 单向数据流
- react是单向数据流,父组件传递给子组件的数据,子组件能够使用,但是不能直接通过this.props修改。否则会报错。(cannot assign to read only property ‘xxx’ of object ‘#object’)
- 子组件要传值给父组件,或者要修改父组件的代码,都是要通过父组件传递过来的方法去实现。
- 这样的好处在于,让数据清晰代码容易维护,如果每个子组件都能直接修改父组件的数据,当子组件躲起来代码维护起来很麻烦。
### react是视图层框架
- 结合上面讲的单向数据流,如果是大型项目,非常多的子组件,要修改一个公共的参数,就需要很多层的传递才能完成一个数据变更。
- 单单react去做大型项目是不够的,他优势在于视图层的渲染,涉及到复杂的数据传递,还需要结合其他数据层的框架开发。如mox-box,redux。
### 函数式编程
- react项目中大部分都是函数,连html都是由render函数去实现的。
- 他的优势在于,方便代码维护,复杂的函数可以拆分成多个函数。
- 在前端自动化测试也很方便,只需要给函数一个参数,看他的输出就可以了。
### react开发调试工具
- react Developer Tools (需要翻墙,在谷歌浏览器应用商店)
### propTypes 与 DefaultProps
/** - value: PropTypes.string.isRequired --> value是父组件传递过来的参数,子组件规定value必须是字符串,并且必须传递(isRequired) - deletItem: PropTypes.arrayOf(PropTypes.string,PropTypes.func) --> deletItem必须是方法,或者字符串 - 更多类型参考官方文档 --> https://react.docschina.org/docs/typechecking-with-proptypes.html */ import React, {Component, Fragment} from 'react'; import PropTypes from 'prop-types'; //引入propTypes class Item extends Component { deletItem = () => { const { deletItem, index } = this.props; deletItem(index); } render(){ const { value } = this.props return( <Fragment> <div onClick = {this.deletItem} > {value} </div> </Fragment> ) } } Item.prototypes = { test: PropTypes.string.isRequired, value: PropTypes.string.isRequired,//定义传递过来的value参数必须是字符串 deletItem: PropTypes.arrayOf(PropTypes.string,PropTypes.number),// 必须是方法或者字符串的数组 deletTtem: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]),// 必须是这些类型的中的一个。 index: PropTypes.number // 必须是数字 } Item.defaultProps = { test: 'defaultProps' // 为test设置默认值,如果没有传入test,就使用这个默认值 } export default Item
### Props,State 与 Render 函数之间的关系
- 数据变化为什么视图会变化?
- react的视图是通过Render函数去渲染的,组件当中,如果Props或者State有变化,Render函数就会重新执行一次。
- 父组件的Render重新执行,对应的他的子组件中的Render也会重新执行。
### 什么是虚拟DOM
1. 先有数据 (state)
2. 模版(render中的jsx)
3. 数据 + 模版 = 生成真实Dom,来显示
4. state 发生改变
5. 数据 + 模版 = 生成真实Dom,替换原有的DOM
- 缺陷: 第一次生成真实dom,第二次又生成一个。最后替换。非常耗性能。
- 原因: 生成一个完整的dom,和替换一个完整的dom,非常耗性能。并不是每次数据变化dom的所有内容都要变。
1. 先有数据 (state)
2. 模版(render中的jsx)
3. 数据 + 模版 = 生成真实Dom,来显示
4. state 发生改变
5. 数据 + 模版 = 生成真实Dom,并不直接替换原始的DOM
6. 新旧DOM做对比,找差异。注意新的DOM其实是DoucumentFragment: 文档碎片并没有真实的挂载到页面上。
7. 找出input框发生变换
8. 只用新DOM中的input元素,替换掉老的DOM中的input元素。
- 缺陷: 节约了完整dom替换的性能,但是消耗了对比的性能。效果不明显。
1. 先有数据 (state)
2. 模版(render中的jsx)
3. 数据 + 模版 = 生成真实Dom,来显示
1. <div id='abc'><span>hello,world</span></div>
4. 生成虚拟DOM(生成虚拟DOM是一个js对象,用它来描述真是DOM)(损耗性能非常小)
1. ['div', {id: 'abc'}, ['span', {}, 'hello,world']]
5. state 发生改变
6. 数据 + 模版 = 生成新的虚拟DOM(极大提升了性能)
1. ['div', {id: 'abc'}, ['span', {}, 'bye,bye']]
7. 比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容
8. 直接操作DOM改变span中的DOM
- 优势:
1. 减少了对真实DOM的创建性能提高很大
2. js对象的比对非常不消耗性能的。
- 原因:
1. js去生产一个虚拟DOM其实就是一个js对象,而使用js去生成一个js对象是不怎么消耗性能的。
2. 对比上面两种方法,不仅减少了真实DOM的生产,同时也没有了真实DOM的对比,虚拟DOM对比也是非常节约新能的。