【JS】如何构建自定义React基础虚拟Dom框架(三)

前面两章详细描述了如何在自定义react框架" title="react框架">react框架中实现虚拟Dom渲染和对比更新,本章将继续添加ref属性和key属性。

ref属性

通过ref属性可以获取原生Dom对象或者组件实例。

修改入口文件,添加ref测试组件:

class DemoRef extends MyReact.Component {

handle() {

let value = this.input.value

console.log(value)

}

render() {

return (

<div>

<input type="text" ref={input => (this.input = input)} />

<button onClick={this.handle.bind(this)}>按钮</button>

</div>

)

}

}

MyReact.render(<DemoRef></DemoRef>, root)

原生Dom

在createDOMElemen的时候,判断props上是否存在ref属性,如果存在,则调用ref属性对应的函数,并将原生Dom元素传递进去。

【JS】如何构建自定义React基础虚拟Dom框架(三)

类组件实例

在mountComponent方法中,判断实例的props属性上是否存在ref属性,如果存在,调用ref属性对应的函数,并将类组件实例当作参数传递。

【JS】如何构建自定义React基础虚拟Dom框架(三)

key属性

ref属性主要是为了优化列表的对比更新,通过复用达到最小化操作Dom的目的。

修改入口文件,添加测试Demp:

import * as MyReact from './MyReact'

class DemoRef extends MyReact.Component {

handle() {

let value = this.input.value

console.log(value)

}

render() {

return (

<div>

<input type="text" ref={input => {

this.input = input

}

} />

<button onClick={this.handle.bind(this)}>按钮</button>

</div>

)

}

}

// MyReact.render(<DemoRef></DemoRef>, root)

class KeyDemo extends MyReact.Component {

constructor(props) {

super(props)

this.state = {

persons: [

{

id: 1,

name: "张三"

},

{

id: 2,

name: "李四"

},

{

id: 3,

name: "王五"

},

{

id: 4,

name: "赵六"

}

]

}

this.handleClick = this.handleClick.bind(this)

}

handleClick() {

const newState = JSON.parse(JSON.stringify(this.state))

// newState.persons.push(newState.persons.shift())

// newState.persons.splice(1, 0, { id: 100, name: "李逵" })

newState.persons.pop()

this.setState(newState)

}

render() {

return (

<div>

<ul>

{this.state.persons.map(person => (

<li key={person.id}>

{person.name}

<DemoRef />

</li>

))}

</ul>

<button onClick={this.handleClick}>按钮</button>

</div>

)

}

}

MyReact.render(<KeyDemo />, root)

在原有的diff算法中,会直接循环遍历虚拟Dom的所有子节点进行diff,当加入key属性后,需要修改此处代码。

首先需要循环遍历旧虚拟Dom的所有子节点,找出所有包含key属性的节点。

let keyedElements = {}

for (let i = 0, len = oldDom.childNodes.length; i < len; i++) {

let domElement = oldDom.childNodes[i]

if (domElement.nodeType === 1) {

let key = domElement.getAttribute("key")

if (key) {

keyedElements[key] = domElement

}

}

}

let hasNoKey = Object.keys(keyedElements).length === 0

然后在更新后的虚拟Dom中遍历查找所有和旧节点key相同的子节点,如果有,当前子节点不需要操作,否则是新增的子节点。

if (hasNoKey) {

// 对比子节点

virtualDom.children.forEach((child, i) => {

diff(child, oldDom, oldDom.childNodes[i])

})

} else {

// 2. 循环 virtualDom 的子元素 获取子元素的 key 属性

virtualDom.children.forEach((child, i) => {

let key = child.props.key

if (key) {

let domElement = keyedElements[key]

if (domElement) {

// 3. 看看当前位置的元素是不是我们期望的元素

if (oldDom.childNodes[i] && oldDom.childNodes[i] !== domElement) {

oldDom.insertBefore(domElement, oldDom.childNodes[i])

}

} else {

// 新增元素

mountElement(child, oldDom, oldDom.childNodes[i])

}

}

})

}

至此,自定义简单的react框架已经完成,剩余的如声明周期函数比较简单,就是在相应操作的时候去调用声明周期函数,不再赘述。

以上是 【JS】如何构建自定义React基础虚拟Dom框架(三) 的全部内容, 来源链接: utcz.com/a/104634.html

回到顶部