虚拟 DOM 的两种创建方式
1 | //1、jsx创建 |
虚拟 DOM
关于虚拟 DOM:
- 本质是 Object 类型的对象(一般对象)
- 虚拟 DOM 比较“轻”,真实 DOM 比较“重”,因为虚拟 DOM 是 React 内部在用,无需真实 DOM 上那么多的属性。
- 虚拟 DOM 最终会被 React 转化为真实 DOM,呈现在页面上。
JSX 语法规则
- 定义虚拟 DOM 时,不要写引号。
- 标签中混入 JS 表达式时要用
{}
。 - 样式的类名指定不要用 class,要用 className。
- 内联样式,要用
style={{key:value}}
的形式去写。 - 只有一个根标签
- 标签必须闭合
- 标签首字母
(1).若小写字母开头,则将该标签转为 html 中同名元素,若 html 中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react 就去渲染对应的组件,若组件没有定义,则报错。
一定注意区分:【js 语句(代码)】与【js 表达式】
(1). 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
1 | a; |
(2). 语句(代码):
下面这些都是语句(代码):
1 | if(){} |
模块与组件、模块化与组件化的理解
1、模块
- 理解:向外提供特定功能的 js 程序, 一般就是一个 js 文件
- 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
- 作用:复用 js, 简化 js 的编写, 提高 js 运行效率
2、组件
- 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image 等等)
- 为什么要用组件: 一个界面的功能更复杂
- 作用:复用编码, 简化项目编码, 提高运行效率
3、模块化
当应用的 js 都以模块来编写的, 这个应用就是一个模块化的应用
4、组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
ES6 类回顾
- 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
- 如果 A 类继承了 B 类,且 A 类中写了构造器,那么 A 类构造器中的 super 是必须要调用的。
- 类中所定义的方法,都放在了类的原型对象上,供实例去使用。
1 | //创建一个Person类 |
函数式和类式组件
1 | //1.创建函数式组件 |
执行了ReactDOM.render(<MyComponent/>.......)
之后,发生了什么?
- React 解析组件标签,找到了 MyComponent 组件。
- 发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到原型上的 render 方法。
- 将 render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中。
类方法中的 this 指向
1 | class Person { |
类中的方法默认是开启了局部的严格模式,所以 this 会指向
undefined
组件实例三大属性
1、state
- state 是组件对象最重要的属性, 值是对象(可以包含多个 key-value 的组合)
- 组件被称为”状态机”, 通过更新组件的 state 来更新对应的页面显示(重新渲染组件)
1 | class Weather extends React.Component { |
类组件简写形式
1 | class Weather extends React.Component { |
2、props
props 基本使用
1 | class Person extends React.Component { |
对 props 进行限制
1 | //引入prop-types 用于对组件标签属性进行限制 |
props 的简写
1 | class Person extends React.Component { |
扩展属性: 将对象的所有属性通过 props 传递
1 | /* |
3、refs
字符串形式的 ref
1 | class Demo extends React.Component { |
回调函数形式的 ref
1 | class Demo extends React.Component { |
回调 ref 回调指向次数问题
关于回调 refs 的说明
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
1 | class Demo extends React.Component { |
createRef 的使用React.createRef
调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点,该容器是“专人专用”的
1 | class Demo extends React.Component { |
事件处理
(1).通过 onXxx 属性指定事件处理函数(注意大小写)
a.React 使用的是自定义(合成)事件, 而不是使用的原生 DOM 事件 —————— 为了更好的兼容性
b.React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
(2).通过 event.target 得到发生事件的 DOM 元素对象 ——————————不要过度使用 ref(发生事件的元素正好是 ref 绑定的元素,可以使用 event)
收集表单数据
1、非受控组件
1 | class Login extends React.Component { |
2、受控组件
1 | //创建组件 |
生命周期
旧生命周期
- 初始化阶段: 由 ReactDOM.render()触发—初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 - 更新阶段: 由组件内部 this.setSate()或父组件 render 触发
- shouldComponentUpdate()
- componentWillUpdate()
- render() =====> 必须使用的一个
- componentDidUpdate()
强制更新:forceUpdate() -> 不更改任何状态中的数据,强制更新一下 -》 不受阀门控制 - 卸载组件: 由 ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
1 | //... |
新生命周期
- 初始化阶段: 由 ReactDOM.render()触发—初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 - 更新阶段: 由组件内部 this.setSate()或父组件重新 render 触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
- 卸载组件: 由 ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
1 | //... |
DOM 的 diffing 算法
1 | /* |