组件渲染
组件类型的 Virtual DOM
Virtual DOM 分为普通 Virtual DOM(Native Element) 和 组件 Virtual DOM(Component)。
Native Element 和 Component 的主要区别就是它们的 type
不同:
- Native Element 的
type
是字符串 - Component 的
type
是函数- 函数组件:
type
存储的就是定义组件的函数 - 类组件:
type
存储的是定义组件的 class (JavaScript 中 class 其实就是函数)
- 函数组件:
function Heart () {
return <div>♥</div>
}
console.log(<Heart />)
// 组件的 Virtual DOM
{
type: f Heart(),
props: {},
children: []
}
在渲染组件时,要先将 Component 和 Native Element 区分开,如果是 Native Element 可以直接开始渲染,如果是 Component 需要特别处理。
// src/index.js
import TinyReact from './TinyReact'
// 容器
const root = document.querySelector('#root')
const virtualDOM = (
...
)
// TinyReact.render(virtualDOM, root)
function Heart () {
return <div>♥</div>
}
TinyReact.render(<Heart />, root)
// src/TinyReact/mountElement.js
import mountNativeElement from './mountNativeElement'
import isFunction from './isFunction'
import mountComponent from './mountComponent'
export default function mountElement(virtualDOM, container) {
// Component VS NativeElement
if (isFunction(virtualDOM)) {
// Component
mountComponent(virtualDOM, container)
} else {
// NativeElement
mountNativeElement(virtualDOM, container)
}
}
// src/TinyReact/isFunction.js
export default function isFunction(virtualDOM) {
return virtualDOM && typeof virtualDOM.type === 'function'
}
// src/TinyReact/mountComponent.js
export default function mountComponent(virtualDOM, container) {
// 判断组件是类组件还是函数组件
}
区分函数组件和类组件
在渲染组件的时候还要区分是函数型组件还是 class 组件,两者的主要区别是,类组件的 Virtual DOM 对象的 type
存储的函数的原型包含一个 render()
方法。
因为定义类组件必须定义render()
方法返回渲染的内容,而函数组件则直接返回:
class Foo {
render() {
return <div>Hello Foo</div>
}
}
function Bar() {
return <div>Hello Bar</div>
}
继续扩展:
// src/TinyReact/isFunctionComponent.js
import isFunction from './isFunction'
export default function isFunctionComponent(virtualDOM) {
const type = virtualDOM.type
return type && isFunction(virtualDOM) && !(type.prototype && type.prototype.render)
}
// src/TinyReact/mountComponent.js
import isFunctionComponent from "./isFunctionComponent";
export default function mountComponent(virtualDOM, container) {
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
console.log('函数组件');
}
}
函数组件
函数组件的内容就是它所生成的 Virtual DOM 对象的 type
属性存储的函数执行后返回的内容。
// src/TinyReact/mountComponent.js
import isFunctionComponent from './isFunctionComponent'
import mountNativeElement from './mountNativeElement'
export default function mountComponent(virtualDOM, container) {
let nextVirtualDOM = null
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
nextVirtualDOM = buildFunctionComponent(virtualDOM)
}
mountNativeElement(nextVirtualDOM, container)
}
function buildFunctionComponent(virtualDOM) {
return virtualDOM.type()
}
函数组件返回的也可能是另一个组件:
function Heart () {
return <App />
}
所以需要增加判断:
// src/TinyReact/mountComponent.js
import isFunction from './isFunction'
import isFunctionComponent from './isFunctionComponent'
import mountNativeElement from './mountNativeElement'
export default function mountComponent(virtualDOM, container) {
let nextVirtualDOM = null
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
nextVirtualDOM = buildFunctionComponent(virtualDOM)
}
// 判断渲染的组件是否直接返回了另一个组件
if (isFunction(nextVirtualDOM)) {
mountComponent(nextVirtualDOM, container)
} else {
mountNativeElement(nextVirtualDOM, container)
}
}
function buildFunctionComponent(virtualDOM) {
return virtualDOM.type()
}
而当函数组件返回的内容中包含其他组件时,会在递归创建 DOM 子节点的时候调用 mountElement()
,该方法又会重新判断组件的类型,不用额外处理。
function Heart () {
return <div>♥<App /></div>
}
函数组件 props 参数处理
函数组件会接受一个 props
作为参数,并在返回的内容中访问其中的属性。
在渲染的时候只需要将 Virtual DOM 对象的 props
属性传递给这个函数即可,注意要考虑 props
为 null
的情况。
// src/index.js
/* ... */
function Heart(props) {
return <div>{props.title}♥</div>
}
TinyReact.render(<Heart title="Hello React" />, root)
// src/TinyReact/mountComponent.js
import isFunction from './isFunction'
import isFunctionComponent from './isFunctionComponent'
import mountNativeElement from './mountNativeElement'
export default function mountComponent(virtualDOM, container) {
let nextVirtualDOM = null
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
nextVirtualDOM = buildFunctionComponent(virtualDOM)
}
// 判断渲染的组件是否直接返回了另一个组件
if (isFunction(nextVirtualDOM)) {
mountComponent(nextVirtualDOM, container)
} else {
mountNativeElement(nextVirtualDOM, container)
}
}
function buildFunctionComponent(virtualDOM) {
return virtualDOM.type(virtualDOM.props || {})
}
类组件
类组件的内容是调用这个类创建的实例对象的 render()
方法返回的内容。
在 React 中,类组件会继承 React.Component
,这里先定义一个 Component
类:
// src/TinyReact/Component.js
export default class Component {}
import createElement from './createElement'
import render from './render'
import Component from './Component'
export default {
createElement,
render,
Component
}
编写类组件示例:
// src/index.js
/* ... */
// TinyReact.render(<Heart title="Hello React" />, root)
class Alert extends TinyReact.Component {
render() {
return <div>Hello React</div>
}
}
TinyReact.render(<Alert />, root)
// src/TinyReact/mountComponent.js
import isFunction from './isFunction'
import isFunctionComponent from './isFunctionComponent'
import mountNativeElement from './mountNativeElement'
export default function mountComponent(virtualDOM, container) {
let nextVirtualDOM = null
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
// 函数组件
nextVirtualDOM = buildFunctionComponent(virtualDOM)
} else {
// 类组件
nextVirtualDOM = buildClassComponent(virtualDOM)
}
// 判断渲染的组件是否直接返回了另一个组件
if (isFunction(nextVirtualDOM)) {
mountComponent(nextVirtualDOM, container)
} else {
mountNativeElement(nextVirtualDOM, container)
}
}
function buildFunctionComponent(virtualDOM) {
return virtualDOM.type(virtualDOM.props || {})
}
function buildClassComponent(virtualDOM) {
const component = new virtualDOM.type()
const nextVirtualDOM = component.render()
return nextVirtualDOM
}
类组件 props 参数处理
React 通过在继承的 Component
组件中定义 props
属性,让继承它的子类可以通过 this.props
访问组件的参数。
// src/TinyReact/Component.js
export default class Component {
constructor(props) {
this.props = props
}
}
给示例添加参数:
// src/index.js
/* ... */
class Alert extends TinyReact.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.name}
{this.props.age}
</div>
)
}
}
TinyReact.render(<Alert name="张三" age={20} />, root)
JavaScript 的 class 默认会添加一个 constructor()
构造函数,如果是继承其他类的子类,则构造函数内部还会调用 super()
:
// 子类默认添加的构造函数
constructor(...args) {
super(...args)
}
所以上面的组件示例可以精简为:
class Alert extends TinyReact.Component {
// constructor(props) {
// super(props)
// }
render() {
return (
<div>
{this.props.name}
{this.props.age}
</div>
)
}
}
注意:如果要显示定义构造函数中的内容,必须要手动调用 super()
。
更新 DOM 元素
编写示例:
- 创建两个 Virtual DOM
- 在页面加载时渲染第一个 Virtual DOM
- 在延迟 2秒 后渲染第二个 Virtual DOM
- 两个 Virtual DOM 有一些修改:
<h2>
元素的data-test
- 元素被改变
<h3>
to<h6>
<span>
的文本内容<button>
点击事件的alert
内容
// src/index.js
import TinyReact from './TinyReact'
// 容器
const root = document.querySelector('#root')
const virtualDOM = (
<div className="container">
<h1>你好 Tiny React</h1>
<h2 data-test="test">(编码必杀技)</h2>
<div>
嵌套1 <div>嵌套 1.1</div>
</div>
<h3>(观察:这个将会被改变)</h3>
{2 == 1 && <div>如果2和1相等渲染当前内容</div>}
{2 == 2 && <div>2</div>}
<span>这是一段内容</span>
<button onClick={() => alert('你好')}>点击我</button>
<h3>这个将会被删除</h3>
2, 3
<input type="text" value="13" />
</div>
)
const modifyDOM = (
<div className="container">
<h1>你好 Tiny React</h1>
<h2 data-test="test123">(编码必杀技)</h2>
<div>
嵌套1 <div>嵌套 1.1</div>
</div>
<h6>(观察:这个将会被改变)</h6>
{2 == 1 && <div>如果2和1相等渲染当前内容</div>}
{2 == 2 && <div>2</div>}
<span>这是一段被修改过的内容</span>
<button onClick={() => alert('你好!!!')}>点击我</button>
<h3>这个将会被删除</h3>
2, 3
<input type="text" value="13" />
</div>
)
TinyReact.render(virtualDOM, root)
setTimeout(() => {
TinyReact.render(modifyDOM, root)
}, 2000)
在更新 DOM 的时候,要先对比两个 Virtual DOM 的差异,然后仅重新渲染差异部分,以达到最小更新。
VirtualDOM 对比
获取更新前的 Virtual DOM
在进行 Virtual DOM 对比时,需要用到更新后的 Virtual DOM 和 更新前的 Virtual DOM。
更新后的 Virtual DOM 目前可以通过 render 方法进行传递。
问题是如何获取更新前的 Virtual DOM ?
对于更新前的 Virtual DOM,对应的其实就是已经在页面中显示的真实 DOM 对象。
既然如此,那么我们在创建真实 DOM 对象时,可以将 Virtual DOM 添加到真实 DOM 对象的属性中。
在进行 Virtual DOM 对比之前,就可以通过真实 DOM 对象获取其对应的 Virtual DOM 对象了。
其实就是通过 render 方法的第三个参数获取的:container.firstChild
为什么是
container.firstChild
?因为 JSX 要求所有标签必须包含在一个父标签下,所以通过获取容器下的第一个元素,就可以拿到之前渲染到页面的DOM元素。
// src/TinyReact/render.js
import diff from './diff'
export default function render(virtualDOM, container, oldDOM = container.firstChild) {
diff(virtualDOM, container, oldDOM)
}
节点类型相同的情况
文本节点
- 节点类型相同的时候根据节点的类型选择更新方式:
- 文本节点:更新文本内容
- 元素节点:更新元素的属性
- 通过更新前的真实 DOM 元素去执行更新操作
- 更新后也要同步更新旧的 Virtual DOM 对象
- 该对象存储在真实 DOM 元素的属性上,将作为每次 DOM 更新对比时的**“更新前的Virtual DOM”**
- 如果包含子元素还要递归对比
- 当前暂时使用的**[序号]**进行对比,之后将扩展为使用
key
去对比。
- 当前暂时使用的**[序号]**进行对比,之后将扩展为使用
// src/TinyReact/diff.js
import mountElement from './mountElement'
import updateTextNode from './updateTextNode'
export default function diff(virtualDOM, container, oldDOM) {
const oldVirtualDOM = oldDOM && oldDOM._virtualDOM
// 判断 oldDOM 是否存在
if (!oldDOM) {
mountElement(virtualDOM, container)
} else if (virtualDOM.type === oldVirtualDOM.type) {
// 节点类型相同
if (virtualDOM.type === 'text') {
// 文本节点:更新内容
updateTextNode(virtualDOM, oldVirtualDOM, oldDOM)
} else {
// 元素节点:更新元素属性
}
// 对比子节点
virtualDOM.children.forEach((child, i) => {
diff(child, oldDOM, oldDOM.childNodes[i])
})
}
}
// src/TinyReact/updateTextNode.js
export default function updateTextNode(virtualDOM, oldVirtualDOM, oldDOM) {
if (virtualDOM.props.textContent !== oldVirtualDOM.props.textContent) {
// 更新文本内容
oldDOM.textContent = virtualDOM.props.textContent
// 同步更新 Virtual DOM 对象
oldDOM._virtualDOM = virtualDOM
}
}
元素节点
对比新旧元素节点的属性 props
并更新。
更新元素节点的属性使用的是之前定义过的 updateNodeElement()
方法。
更新元素节点有以下几种情况:
- 原有属性被修改或添加新的属性
- 如果是事件属性,则注册新的事件处理函数,并且删除旧的事件处理函数
- 如果是其他属性,则重新设置即可
- 属性被删除
- 如果是事件属性,则删除旧的事件处理函数
- 如果是
children
属性,则忽略 - 如果是其他属性,则进行删除
// src/TinyReact/diff.js
import mountElement from './mountElement'
import updateNodeElement from './updateNodeElement'
import updateTextNode from './updateTextNode'
export default function diff(virtualDOM, container, oldDOM) {
const oldVirtualDOM = oldDOM && oldDOM._virtualDOM
// 判断 oldDOM 是否存在
if (!oldDOM) {
mountElement(virtualDOM, container)
} else if (virtualDOM.type === oldVirtualDOM.type) {
// 节点类型相同
if (virtualDOM.type === 'text') {
// 文本节点:更新内容
updateTextNode(virtualDOM, oldVirtualDOM, oldDOM)
} else {
// 元素节点:更新元素属性
updateNodeElement(oldDOM, virtualDOM, oldVirtualDOM)
}
// 对比子节点
virtualDOM.children.forEach((child, i) => {
diff(child, oldDOM, oldDOM.childNodes[i])
})
}
}
// src/TinyReact/updateNodeElement.js
/**
* @param {*} newElement 要更新的 DOM 元素对象
* @param {*} virtualDOM 新的 Virtual DOM 对象
* @param {*} oldVirtualDOM 旧的 Virtual DOM 对象
*/
export default function updateNodeElement(newElement, virtualDOM = {}, oldVirtualDOM = {}) {
// 获取节点对应的属性对象
const newProps = virtualDOM.props
const oldProps = oldVirtualDOM.props || {}
// 属性被修改或添加属性的情况
Object.keys(newProps).forEach(propName => {
// 获取属性值
const newPropsValue = newProps[propName]
const oldPropsValue = oldProps[propName]
if (newPropsValue !== oldPropsValue) {
if (propName.startsWith('on')) {
// 判断属性是否是事件属性
// 事件名称 onClick -> click
const eventName = propName.toLowerCase().slice(2)
// 为元素添加事件
newElement.addEventListener(eventName, newPropsValue)
// 删除原有事件的事件处理函数
if (oldPropsValue) {
newElement.removeEventListener(eventName, oldPropsValue)
}
} else if (propName === 'value' || propName === 'checked') {
// 判断是否是不能用 setAttribute() 设置的属性
newElement[propName] = newPropsValue
} else if (propName !== 'children') {
// 过滤 children 属性
if (propName === 'className') {
newElement.setAttribute('class', newPropsValue)
} else {
newElement.setAttribute(propName, newPropsValue)
}
}
}
})
// 判断属性被删除的情况
Object.keys(oldProps).forEach(propName => {
const newPropsValue = newProps[propName]
const oldPropsValue = oldProps[propName]
if (!newPropsValue) {
// 属性被删除
if (propName.startsWith('on')) {
// 判断属性是否是事件属性
const eventName = propName.toLowerCase().slice(2)
// 为元素添加事件
newElement.removeEventListener(eventName, oldPropsValue)
} else if (propName !== 'children') {
newElement.removeAttribute(propName)
}
}
})
}
节点类型不同的情况
当两个节点类型不相同的时候,就没有对比的必要了。
只需要用新的 Virtual DOM 生成新的真实 DOM 对象,然后使用新的 DOM 对象替换旧的 DOM 对象即可。
组件需要特殊处理,这里仅处理了普通的 Virtual DOM。
// src/TinyReact/diff.js
import createDOMElement from './createDOMElement'
import mountElement from './mountElement'
import updateNodeElement from './updateNodeElement'
import updateTextNode from './updateTextNode'
import isFunction from './isFunction'
export default function diff(virtualDOM, container, oldDOM) {
const oldVirtualDOM = oldDOM && oldDOM._virtualDOM
// 判断 oldDOM 是否存在
if (!oldDOM) {
mountElement(virtualDOM, container)
} else if (
// 对比的两个节点类型不相同
virtualDOM.type !== oldVirtualDOM.type &&
// 并且节点的类型不是组件,因为组件要单独处理
!isFunction(virtualDOM)
) {
// 节点类型不相同
const newElement = createDOMElement(virtualDOM)
oldDOM.parentNode.replaceChild(newElement, oldDOM)
} else if (virtualDOM.type === oldVirtualDOM.type) {
// 节点类型相同
if (virtualDOM.type === 'text') {
// 文本节点:更新内容
updateTextNode(virtualDOM, oldVirtualDOM, oldDOM)
} else {
// 元素节点:更新元素属性
updateNodeElement(oldDOM, virtualDOM, oldVirtualDOM)
}
// 对比子节点
virtualDOM.children.forEach((child, i) => {
diff(child, oldDOM, oldDOM.childNodes[i])
})
}
}
Diff 对比
Virtual DOM 对比(Diff)的算法有两个原则:
- 同级节点对比
- 深度优先顺序
同级对比
Virtual DOM 在对比的时候是同级对比。即父元素和父元素对比,子元素和子元素对比,不会发生跨级对比的。
- 如果对比的节点类型相同
- 如果是文本节点,则对比文本内容,如果内容不同,则替换为新的内容
- 如果是元素节点,则对比元素属性
- 如果属性相同,则不作处理
- 如果属性值不同(包括新添加属性的情况),则替换为新节点属性值
- 如果属性被删除(新节点不包含该属性),则删除属性
- 如果对比的节点类型不同
- 则直接用新的 Virtual DOM 生成新的真实 DOM 对象,替换旧的 DOM 对象
深度优先
Virtual DOM 对比的顺序是深度优先。即子节点对比优先于同级节点对比。
例如图例:
- 首先对比最外层的节点
ul
- 接着对比
ul
节点下的第一个子节点li
- 第一个
li
节点对比完后,发现它包含子节点,所以继续对比该节点下的第一个子节点p
- 当第一个
li
节点下的p
节点对比完成后,接着对比li
的兄弟节点
对应到代码中,就是在对比当前节点的过程中,递归对比它的子节点,当全部对比完成后,继续对比下一个兄弟节点。
删除节点
- 时机:在节点更新完成之后才能分析哪些节点应该被删除
- 范围:发生在同一个父节点下的所有子节点身上
如何判断是否有节点需要被删除:在节点更新完成后,如果旧节点对象的数量多于新 Virtual DOM 节点的数量,就说明有节点需要被删除。
// src/TinyReact/diff.js
import createDOMElement from './createDOMElement'
import mountElement from './mountElement'
import updateNodeElement from './updateNodeElement'
import updateTextNode from './updateTextNode'
import isFunction from './isFunction'
import unmountNode from './unmountNode'
export default function diff(virtualDOM, container, oldDOM) {
const oldVirtualDOM = oldDOM && oldDOM._virtualDOM
// 判断 oldDOM 是否存在
if (!oldDOM) {
mountElement(virtualDOM, container)
} else if (
// 对比的两个节点类型不相同
virtualDOM.type !== oldVirtualDOM.type &&
// 并且节点的类型不是组件,因为组件要单独处理
!isFunction(virtualDOM)
) {
// 节点类型不相同
const newElement = createDOMElement(virtualDOM)
oldDOM.parentNode.replaceChild(newElement, oldDOM)
} else if (virtualDOM.type === oldVirtualDOM.type) {
// 节点类型相同
if (virtualDOM.type === 'text') {
// 文本节点:更新内容
updateTextNode(virtualDOM, oldVirtualDOM, oldDOM)
} else {
// 元素节点:更新元素属性
updateNodeElement(oldDOM, virtualDOM, oldVirtualDOM)
}
// 对比子节点
virtualDOM.children.forEach((child, i) => {
diff(child, oldDOM, oldDOM.childNodes[i])
})
// 删除节点
// 获取旧节点
const oldChildNodes = oldDOM.childNodes
// 判断旧节点的数量
if (oldChildNodes.length > virtualDOM.children.length) {
// 有节点需要被删除
for (let i = oldChildNodes.length - 1; i > virtualDOM.children.length; i--) {
unmountNode(oldChildNodes[i])
}
}
}
}
// src/TinyReact/unmountNode.js
export default function unmountNode(node) {
node.remove()
}
setState 方法实现类组件状态更新
更新 state
要更新类组件的状态,要用到setState()
方法。
setState
也是父类 (Component
)定义的方法。
setState
方法是组件实例调用的,所以内部的 this
指向实例对象。
该方法可以接受一个对象,调用结果会用传递的对象浅合并类组件的 state
属性。
示例:
class Alert extends TinyReact.Component {
constructor(props) {
super(props)
this.state = {
title: 'Default Title'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({
title: 'Changed Title'
})
}
render() {
console.log(this.state)
return (
<div>
{this.props.name}
{this.props.age}
<div>
{this.state.title}
<button onClick={this.handleClick}>改变Title</button>
</div>
</div>
)
}
}
TinyReact.render(<Alert name="张三" age={20} />, root)
// src/TinyReact/Component.js
export default class Component {
constructor(props) {
this.props = props
}
setState(state) {
this.state = Object.assign({}, this.state, state)
}
}
对比新旧 VirtualDOM
state
发生变化后要重新生成新的 Virtual DOM 对象与旧的进行比对,并将差异更新到 旧的 DOM 中。
- 获取新的 Virtual DOM 对象:调用
render()
方法 - 获取旧的 DOM 对象:
- 添加用于存储/获取 DOM 对象的方法,在挂载真实 DOM 时进行存储,之后可以通过 DOM 对象的
_virtualDOM
属性获取它的 virtual DOM。 - 在
mountNativeElement()
中调用存储 DOM 的方法,将 DOM 存储到组件实例对象上
- 添加用于存储/获取 DOM 对象的方法,在挂载真实 DOM 时进行存储,之后可以通过 DOM 对象的
mountNativeElement()
方法中如何访问组件实例对象:
- 在挂载类组件的时候调用了
buildClassComponent()
方法 - 内部实例化了组件实例对象
- 然后生成了 Virtual DOM 对象(
render()
),并返回 - 这个 Virtual DOM 又传递给
mountNativeElement()
所以可以在 buildClassComponent()
方法内部,将实例对象存储在 Virtual DOM 对象中进行传递。
// src/TinyReact/Component.js
export default class Component {
constructor(props) {
this.props = props
}
setState(state) {
this.state = Object.assign({}, this.state, state)
// 获取最新的要渲染的 VirtualDOM 对象
const virtualDOM = this.render()
// 获取旧的 VirtualDOM 对象进行比对
const oldDOM = this.getDOM()
const container = oldDOM.parentNode
diff(virtualDOM, oldDOM.parentNode, oldDOM)
}
setDOM(dom) {
this._dom = dom
}
getDOM() {
return this._dom
}
}
// src/TinyReact/mountComponent.js
import isFunction from './isFunction'
import isFunctionComponent from './isFunctionComponent'
import mountNativeElement from './mountNativeElement'
export default function mountComponent(virtualDOM, container) {
let nextVirtualDOM = null
// 判断组件是类组件还是函数组件
if (isFunctionComponent(virtualDOM)) {
// 函数组件
nextVirtualDOM = buildFunctionComponent(virtualDOM)
} else {
// 类组件
nextVirtualDOM = buildClassComponent(virtualDOM)
}
// 判断渲染的组件是否直接返回了另一个组件
if (isFunction(nextVirtualDOM)) {
mountComponent(nextVirtualDOM, container)
} else {
mountNativeElement(nextVirtualDOM, container)
}
}
function buildFunctionComponent(virtualDOM) {
return virtualDOM.type(virtualDOM.props || {})
}
function buildClassComponent(virtualDOM) {
const component = new virtualDOM.type(virtualDOM.props || {})
const nextVirtualDOM = component.render()
// 存储组件实例对象
nextVirtualDOM.component = component
return nextVirtualDOM
}
// src/TinyReact/mountElement.js
import createDOMElement from './createDOMElement'
export default function mountNativeElement(virtualDOM, container) {
const newElement = createDOMElement(virtualDOM)
// 将转换之后的 DOM 对象放置到页面中
container.appendChild(newElement)
const component = virtualDOM.component
// 判断是否是类组件返回的 VirtualDOM
if (component) {
component.setDOM(newElement)
}
}