1.2 组件与模板的本质
1.组件的本质
定义:组件就是一组DOM元素的封装
const MyComponet=function (){
return {
tag:'div',
props:{
onClick:()=>alert('hello world')
},
children:'click me'
}
}
然后我们用虚拟DOM来描述组件:
const vcode={
tag:MyComponet
}
为了能够渲染组件,需要渲染器的支持,修改前面的上节的render函数为:
function renderer(vnode,container){
if(typeof vnode.tag==='string'){
mountElement(vnode,container)
}else if(typeof vnode.tag==='function'){
mountComponent(vnode,container)
}
}
function mountElement(vnode,container){
const el=document.createElement(vnode.tag);
//遍历vnode.props,将属性事件添加到DOM元素
for(const key in vnode.props){
if(/^on/.test(key)){
//如果key以on开头,说明它是事件
el.addEventListener(
key.substr(2).toLowerCase(),//事件的click方法
vnode.props[key]//事件函数
)
}
}
if(typeof vnode.children==='string'){
const text=document.createTextNode(vnode.children);
el.appendChild(text)
}else if(Array.isArray(vnode.children)){
//递归地调用renderer函数
vnode.children.forEach((child)=>render(child,el))
}
container.appendChild(el)
}
function mountComponent(vnode,container){
//获取组件要渲染的虚拟DOM
const subtree=vnode.tag();
//递归地调用renderer函数
renderer(subtree,container)
}
当然这里的组件不止可以是函数,还可以是对象,只需要把条件判断中的'function'改为'object'即可。
2.模板的工作原理
对于编译器来说,模板就是普通的字符串,它会将该字符串生成一个功能与之相同的渲染函数:
模板:
<div @click="hanlder">
click me
</div>
渲染函数:
render(){
return h('div',{onclick:hanlder},'click me')
}
我们熟悉的.vue文件也是一样的道理:
<template>
<div @click="hanlder">
click me
</div>
</template>
<script>
export default {
data(){/**/},
methods:{
hanlder:()=>{/**/}
}
}
</script>
渲染器会将模板渲染到<script>标签中所以最终在浏览器运行的代码是:
export default {
data(){/**/},
methods:{
hanlder:()=>{/**/}
},
render(){
return h('div',{onclick:hanlder},'click me')
}
}
3.渲染器寻找变更点
形如:
render(){
return {
tag:'div',
props:{
id:'foo',
class:cls
}
}
}
cls是一个变量,当cls变化时,渲染器需要寻找变更点,完全可以在render函数中添加一个属性:
render(){
return {
tag:'div',
props:{
id:'foo',
class:cls
},
pathchFlags:1//1代表class是动态的
}
}
这里面添加了一个属性,代表class的属性会发生改变,这样就省去了渲染器再去寻找变更点,从而提升性能。

浙公网安备 33010602011771号