组件就是一组DOM元素的封装,这组DOM元素就是组件要渲染的内容
无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟DOM渲染成真实DOM
渲染器的功能 vnode –> 真实DOM
const vnode = {
tag: 'div',
props: {
onClick() {
alert('click')
}
},
children: 'click me'
}
const renderer = (vnode, root) => {
const { tag, props, children } = vnode
let d = document.createElement(tag)
for (let key in props) {
let event = key.slice(2).toLowerCase()
if (key.startsWith('on')) {
d.addEventListener(event, props[key])
}
}
if (typeof children === 'string') {
d.appendChild(document.createTextNode(children))
} else if (Array.isArray(children)) {
for (let child of children) {
renderer(child, d)
}
}
root.appendChild(d)
}
renderer(vnode, document.querySelector('#app2'))
自定义组件渲染
使用tag用来区分 原生HTML标签,function组件和类组件
<div id="app3"></div>
// 函数式组件
const MyComponent = () => {
return {
tag: 'div',
props: {
onClick() {
alert('click my component')
}
},
children: 'click my component'
}
}
// 类组件
const MyStateComponent = {
render() {
return {
tag: 'div',
props: {
onClick() {
alert('click my MyStateComponent')
}
},
children: 'click my MyStateComponent'
}
}
}
// renderer渲染器
const renderer2 = (vnode, container) => {
if (typeof vnode.tag === 'object') {
mountStateComponent(vnode, container)
} else if (typeof vnode.tag === 'function') {
mountComponent(vnode, container)
} else if (typeof vnode.tag === 'string') {
mountElement(vnode, container)
}
}
// 挂载节点
const mountElement = (vnode, container) => {
const { tag, props, children } = vnode
let dom = document.createElement(tag)
for (let key in props) {
if (key.startsWith('on')) {
const event = key.slice(2).toLowerCase()
dom.addEventListener(event, props[key])
}
}
if (typeof children == 'string') {
dom.appendChild(document.createTextNode(children))
} else if (Array.isArray(children)) {
for (let child of children) {
renderer2(child, dom)
}
}
container.appendChild(dom)
}
// 挂载函数式组件
const mountComponent = (vnode, dom) => {
const subtree = vnode.tag()
renderer2(subtree, dom)
}
// 挂载类组件
const mountStateComponent = (vnode, dom) => {
const subtree = vnode.tag.render()
renderer2(subtree, dom)
}
const vnode2 = {
tag: 'div',
children: [
{ tag: MyComponent },
{ tag: MyStateComponent },
]
}
renderer2(vnode2, document.querySelector('#app3'))