虚拟DOM
在JS中,通过操作JS中的DOM对象来完成对页面的重排或重绘。但是DOM的操作成本是比较高的,所以引入虚拟DOM的概念。
是对真实DOM的映射,React通过新旧虚拟DOM对比,得到需要更新的部分,实现数据的增量更新。
diff算法
当我们修改数据时,只希望更新我们修改的那一小块dom,而不是整个dom,diff算法就帮我们实现了这点。
diff算法的本质:找出两个对象之间的差异,目的是尽可能做到节点复用
diff策略:用三大策略,将O(n3)f复杂度转化为O(n)复杂度 1.策略一:针对树结构(tree diff),对UI层的DOM节点跨层级的操作进行忽略。(数量少)
特点:
- React通过使用updateDepth对虚拟Dom树进行层次遍历
- 两棵树只对同一层级节点进行比较,只要该节点不存在了,那么该节点与其所有子节点会被完全删除,不再进行进一步比较
- 只要遍历一次,便完成对整个DOM树的比较 2.策略二:针对组件结构(component),拥有相同类的两个组件生成相似的树形结构,拥有不同类的两个组件会生成不同的属性结构。 3.策略三:针对元素结构(element-diff),对于同一层级的一组节点,提供了三种节点操作:插入,移动,删除,使用具有唯一性的id区分 特点:
- 插入:新的组件不在原来的集合中,而是全新的节点,则对集合进行插入操作
- 删除:组件已经在集合中,但集合已经更新,此时节点就需要删除
- 移动:组件已经存在于集合中,并且集合更新时,组件并没有发生更新,只是位置发生改变
组件化
每个组件都符合开放-封闭原则,封闭是针对渲染工作流来说的,指的是组件内部的状态都由自身维护,只处理内部的渲染逻辑。开放是针对组件通信来说的,指的是不同组件可以通过props(单项数据流)进行数据交互
JSX也是一个表达式,是对JavaScript的语法扩展,在编译之后,JSX表达式会被转为普通javascript函数调用,并且对其取值后得到JavaScript对象。
JSX 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。
语法规则
- 定义虚拟DOM时,不要写引号
- 标签中混入JS表达式时要用 {}
- 样式的类名指定不用 class,要用className
- 内联样式,要用style = {{key: value}} 的形式去写
- 只有一个根标签
- 标签必须闭合
- 标签首字母
(1)若小写字母开头,则将该标签转为 html同名元素,若html中无该标签,则报错
(2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
三、React生命周期
生命周期指的是组件实例从创建到销毁的过程,函数组件没有生命周期,只有类组件才有,因为只有class组件会创建组件实例
组件的生命周期分为挂载、更新、卸载阶段
挂载
由ReactDOM.render()触发 ---------------初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount() ------------ 常用
更新
由组件内部this.setState()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
卸载
由ReactDOM.unmountComponentAtNode()触发
- componentWilUnmount() ----------- 常用
一般在这做一些收尾的事,例如:关闭定时器、取消订阅消息
四、组件和props
组件类似于函数,它接受任意的入参,并返回用于描述页面展示内容的React元素
构造函数方式
- 组件如果需要接收外界的数据,需要在构造函数列表中使用
props来接收,props就相当于DOM元素的属性一般赋值。 - 在一个组件中,必须要一个返回值,这个返回值是一个合法的JSX虚拟DOM元素,如果
return null,则表示此组件是空的 - props是只读的,props需要用形参接收
- 组件的首字母必须大写
// 函数组件
function Welcome(props) {
return <h1>Helllo,{props.name}<h1>
}
class方式
- 使用class定义组件,必须让组件继承自React.Component
- 组件内部必须有render函数,render函数必须返回合法的JSX虚拟DOM结构
- 最基本的组件结构:
- props是只读的,在class中可以通过this.props使用
- 有自己的私有数据
state,class内部自身维护的可读写的数据,不通于props是通过外界传递而来
// class 组件
function SayHello extends React.Component{
constructor(props) {
super(props);
this.state = {message: 'Hello'};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert(this.state.message);
}
render() {
return(
<button onClick={this.handleClick}>
say hello
</button>
)
}
}
所有的组件都必须像纯函数一样保护它们的props不被更改 组件遵循单一功能原则,并且是单向数据传递
五、Redux工作原理
Redux是一个状态管理库,使用场景:
- 跨层级组件数据共享与通信
- 一些需要持久化的全局数据,比如用户登录信息
- state:驱动应用的真实数据源头
- view:基于当前状态的视图声明性描述
- actions:根据用户输入在应用程序中发生的事件,并触发状态更新
六、React-router
页面路由
window.location.href = '地址' // www.baidu.com
history.back() //回退
hash路由
import {HashRouter as Router,Route,Link} from 'react-router-dom'
class A extends React.Comopnent{
constructor(props) {
super(props)
}
render() {
return (
<div>A</div>
)
}
}
class B extends React.Comopnent{
constructor(props) {
super(props)
}
render() {
return (
<div>B</div>
)
}
}
class Wrapper extends React.Component{
constructor(props){
super(props)
}
render() {
return (
<div>
<Link to="/a">组件A</Link>
<br />
<Link to="/b">组件B</Link>
{this.props.children}
</div>
)
}
}
ReactDOM.render(
<Router>
<Wrapper>
<Route path="/a" component={A}></Route>
<Route path="/b" component={B}></Route>
</Wrapper>
</Router>
)
h5路由
history.pushState('name', 'title', '/path') // 推进一个状态
history.replaceState('name', 'title', '/path') // 更换一个状态
window.onpopstate = function () {
console.log(window.location.href)
console.log(window.location.pathname)
console.log(window.location.hash)
console.log(window.location.search)
}
react-router引入
import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
router传参,组件接受不同组件传参
引入switch
七、Hook
Hook是React16.8的新增特性,它可以在不编写class的情况下使用state以及其他React特性
stateHook
import React,{useState} from 'react'
function Example() {
// 声明一个叫做“count”的state变量
const [count,setCount] = useState(0);
return (
<div>
<p>You Click {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
)
}
在这里,useState就是一个Hook,通过在函数组件里调用它来给组件添加一些内部的state。React会在重复渲染时保留这个state
Hook是一个特殊的函数,可以“钩入”React的特性
useState是允许在React函数组件中添加state的Hook 我们声明了一个叫count的state变量,然后把它设为0。React会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数,我们可以通过调用setCount来更新当前的count
- 读取state this.state.count
- 更新state 调用this.setState()
Effect Hook
Effect Hook可以在函数组件中执行副作用操作。 数据获取,设置订阅以及手动更改React组件中的DOM都属于副作用
import React,{ useState,useEffect} from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You Click ${count} times`;
})
return(
<div>You Clicked {count}</div>
)
}
useEffect做了什么,通过使用这个Hook,你可以告诉React组件需要在渲染之后执行某些操作。React会保存你传递的函数,并且在执行DOM更新之后调用它。 默认情况下,useEffect在第一次渲染之后和每次更新之后都会执行
自定义Hook
自定义Hook是一个函数,其名称以“use”开头,函数内部可以使用其他的Hook