虚拟DOM
本质:用js对象模拟DOM和嵌套关系
目的:实现DOM的高效更新(数据更新时,浏览器DOM会整个重新渲染,而虚拟DOM只渲染更新的部分、按需渲染)
|
|
将新的DOM树和旧的DOM树进行对比,得到改变的部分对其按需渲染
然而,浏览器没有提供api可以用来获取整棵DOM树,因此,需要程序员创建虚拟DOM
Diff算法【逐层比对】
- tree diff:新旧两颗DOM树逐层对比(对比是否有增加或删除组件),如果有区别则重新渲染;如果没有区别,则进入下一步-> 组件对比
- component diff:每一层中组件的对比;如果对比前后,组件的类型不同,则移除旧组件,创建新组件,然后追加到页面上;如果对比前后,组件的类型相同,再进入下一步 -> element diff
- element diff:组件中,元素的对比(比如旧组件中的p标签变成了div标签)
开始一个React项目
|
|
配置package.json
在package.json中加上这些内容:
在入口文件引入react
|
|
React.createElement(参数1,参数2,参数3)
React.createElement接收3个参数:
- 参数1:标签名(String)
- 参数2:拥有的属性或者null(Object)
- 参数3:子节点
|
|
|
|
这种方法太过麻烦,React提供了JSX语法
JSX语法
|
|
|
|
渲染成innerHTML
jsx默认渲染文本,如果你想渲染HTML标签,你必须使用dangerouslySetInnerHTML,并通过 __html 键传递一个对象
更新已渲染的元素
React 元素是 不可突变(immutable) 的. 一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 冻结UI 。
就我们所知, 更新 UI 的唯一方法是创建一个新的元素, 并将其传入 ReactDOM.render() 方法
|
|
添加注释
jsx的注释,不能直接注释,要放在花括号内
className和htmlFor
在jsx中,class要写成className;label标签的for属性要写成htmlFor
列表循环
|
|
如果列表没有key,也可以用index代替(原则上不推荐用index作为key)
条件渲染
|
|
组件
组件:参考文档
组件名必须大写开头!<div />
表示div标签,<Div />
表示组件
组件中的props是只读的,无法进行修改
function组件/无状态组件
function组件通过props.xxx
接收数据
你可以用ES5的构造函数来写一个组件,直接return一个jsx:
class组件/有状态组件
class组件通过this.props.xxx
接收数据,通过this.state.xxx
获取自身state里的数据
你也可以用ES6的class来写一个组件,通过render函数return一个jsx:
|
|
使用展开运算符
|
|
|
|
【点击查看代码】
展开运算符可以将 数组 或者 对象 的项进行展开
function组件和class组件的区别
- class组件有props,状态state,生命周期函数,因此称为“有状态组件”
- function组件只有props,没有状态state和生命周期函数,因此称为“无状态组件”,运行效率比有状态组件高
|
|
合成组件
组件中,引入其他组件
提取组件
在src/components下新建一个Hello.jsx的组件
或者
在其他地方引入这个Hello组件:
状态state
|
|
|
|
props和state的区别
- state里的数据是可读可写的,props里的数据只读
- state里的数据是私有的(比如ajax请求),props里的数据是外部传入过来的
父子组件
父传子
|
|
子组件:
父组件:
然后在index.js中使用
子传父 - 状态提升
React的状态提升就是用户对子组件操作,子组件不改变自己的状态,通过自己的props把这个操作改变的数据传递给父组件,改变父组件的状态,从而改变受父组件控制的所有子组件的状态,这也是React单项数据流的特性决定的。
状态提升
|
|
Context
context 用于共享全局数据,类似于Vue中的eventBus。
context 一般用于传递 用户是否登录,主题色或首选语言等。
不要仅仅为了避免几个层级的组件传递数据而使用context!!
下面是一个爷孙组件的例子,如果数据通过 爷爷 -> 爸爸 -> 儿子,这样太繁琐。我们使用context,将爷爷的数据作为 全局数据 ,从而让孙子获得
新建一个xxx-context.js文件
然后在需要用到该context的组件中引入:
通过将根组件包裹在XxxContext..Provider
标签内,然后通过value
共享全局数据。
这样,包裹在该标签下的所有组件,都能访问到value
共享的数据
子孙组件通过<XxxContext.Consumer>
来获取数据,注意,该标签返回一个回调函数!!!需要通过value => {}
来展示数据
在jsx中写style样式
|
|
|
|
把样式对象抽离出来:
继续精简,把所有的样式放在一个样式表对象中:
最后,将styles抽离成单独的【样式模块】
新建styles.js文件
别处引入该样式模块:
css模块化
在vue中,使用scoped来作为组件私有样式,不加scoped的样式则为全局样式
在react,如果直接导入import 'css/styles.css'
则为全局样式;如果想要组件私有样式,则:import cssObj from 'css/styles.css'
注意!!!需要在webpack中配置css-loader:css-loader?modules&localIdentName=[path][name]-[local]-[hash:8]
,否则无法使用css模块
父组件的className为xxx,如果直接import 'css/styles.css'
则为全局样式,会导致子组件<Son />
中的字体颜色也为红色,因此需要css模块化
这里的类选择器和id选择器会替换成[path][name]-[local]-[hash:8]
,如果想让样式表中的某个样式时全局样式的话,可以使用:global(类选择器orid选择器)
|
|
注意!!css模块化只对类选择器和id选择器生效,对于标签选择器无效。
如果有多个样式,可以写成数组的形式,然后用join将逗号变成空格:
绑定事件
html中,绑定事件:
在jsx中绑定事件:
在组件中绑定事件
|
|
this的指向问题
在JSX回调中你必须注意 this 的指向。 在 JavaScript 中,类方法默认没有 绑定 的。如果你忘记绑定 this.xxx 并将其传递给onClick,那么在直接调用该函数时,this 会是 undefined 。
你可以通bind(this)
来绑定this:
这种写法,不用传入ev;回调函数xxx的最后一个参数默认是ev
或者可以通过箭头函数解决this的指向问题:
这种写法,ev需要显式地传入回调函数xxx中,箭头函数中ev放在第几个,那么在回调函数中,ev就是第几个参数【即,ev的位置手动决定】
setState()的注意点
setState是异步的,如果有多次setState,会合并成一次setState,从而提升性能
由于setState是异步的,因此:
|
|
|
|
最新React建议使用以下setState
|
|
input的数据双向绑定
必须给input提供value和onChange
受控组件:value由state里的值决定
ref获取DOM元素
在Vue中,通过this.$refs
获取DOM元素
而在React中,通过this.refs
获取DOM元素
非受控组件:value值由DOM元素决定
ref的另一种写法
在新版React中,推荐通过ref={xxx=>this.xxx=xxx}
的形式引用DOM
|
|
生命周期函数
生命周期函数指的是,在某一时刻组件会自动去调用执行的函数
一个React组件的生命周期分为三个部分:挂载期(Mounting)、存在更新期(Updating,外界传入props或自身state改变时触发)和销毁时(Unmounting)
|
|
数组的操作
数组的添加:
在vue中,给数组添加一项用this.lists.push('xxx')
即可
而在react中:
数组的删除:
虽然下面这种做法也行,但不推荐使用↓
在react中,不建议直接修改state中的数据;最好先进行拷贝,然后修改拷贝的数据,最后再把修改完成的拷贝数据赋值给原始数据!
一个todoList实例
React.Fragment作为根节点
React要求只能有一个根节点,如果用<div></div>
包裹,最终生成的html会有多余的div标签;通过<React.Fragment>
或者<></>
,生成的最终html不包含该标签
使用 PropTypes 进行类型检查
http://react.css88.com/docs/typechecking-with-proptypes.html
|
|
defaultProps设定默认值
|
|
React过渡动画
通过transition
|
|
通过@keyframes
|
|
通过react-transition-group模块实现动画
https://reactcommunity.org/react-transition-group/
通过yarn add react-transition-group
安装:
|
|
给多个元素添加动画样式
|
|
UI组件 vs 容器组件
- UI组件负责渲染,即function组件,自身没有state
- 容器组件负责逻辑
在容器组件中,通过属性的方式将数据传给UI组件
在UI组件中,通过props.xxx
接收数据
补充
- props由外界传入
- state是自身的
优化性能
- 将this的绑定放在constructor里
this.xxx = this.xxx.bind(this)
- setState是异步的,会自动把多次setState合并成1次,降低虚拟DOM的比对频率
- 通过设置key值,提高比对速度
shouldComponentUpdate
阻止不必要的render
新的setState
|
|