# 第 3 章 Vue.js 3 的设计思路
# 1. 声明式地描述 UI
用模版描述 UI:
<h1 @click="handler">
<span></span>
</h1>
用 JS 对象(虚拟DOM)描述 UI:
const title = {
tag: 'h1',
props: {
onClick: handler,
},
children: [
{ tag: 'span' }
]
};
通过渲染函数描述:
import { h } from 'vue';
export default {
render() {
return h('h1', { onClick: handler })
}
}
# 2. 初识渲染器
虚拟 DOM:
- 用 JS 对象来描述真实的 DOM 结构
渲染器:
- 将 虚拟 DOM 渲染为 真实 DOM
自定义渲染器:
function renderer(vnode, container) {
const {
tag,
props,
children,
} = vnode;
const el = document.createElement(tag);
for (const prop in props) {
if (/^on/.test(prop)) {
const eventName = prop.substr(2).toLowerCase();
const handler = props[prop];
el.addEventListener(eventName, handler);
}
}
if (typeof children === 'string') {
const text = document.createTextNode(children);
el.appendChild(text);
} else if (Array.isArray(children)) {
children.forEach((child) => renderer(child, el));
}
container.appendChild(el);
}
// 使用
renderer({
tag: 'div',
props: {
onClick: () => alert('hello'),
},
children: [
{ tag: 'span', children: '点我' }
]
}, document.body);
# 3. 组件的本质
说明:
- 组件就是一组 DOM 元素的封装,也是虚拟 DOM
组件:
// 函数形式
const MyComponent = function() {
return {
tag: 'div',
props: {
onClick: () => alert('hello'),
},
children: [
{ tag: 'span', children: '点我' }
]
};
};
// 对象形式
const MyComponent = {
render: function() {
return {
tag: 'div',
props: {
onClick: () => alert('hello'),
},
children: [
{ tag: 'span', children: '点我' }
]
};
}
};
渲染器:
function renderer(vnode, container) {
const { tag } = vnode;
if (typeof tag === 'string') {
mountElement(vnode, container);
} else if (typeof tag === 'function') {
mountComponent(vnode, container);
}
}
function mountElement(vnode, container) {
const {
tag,
props,
children,
} = vnode;
const el = document.createElement(tag);
for (const prop in props) {
if (/^on/.test(prop)) {
const eventName = prop.substr(2).toLowerCase();
const handler = props[prop];
el.addEventListener(eventName, handler);
}
}
if (typeof children === 'string') {
const text = document.createTextNode(children);
el.appendChild(text);
} else if (Array.isArray(children)) {
children.forEach((child) => renderer(child, el));
}
container.appendChild(el);
}
function mountComponent(vnode, container) {
const subtree = vnode.tag(); // 获取 vnode
renderer(subtree, container);
}
使用:
const vnode = {
tag: MyComponent,
};
renderer(vnode, document.body)
# 4. 模板的工作原理
编译器:
- 将模板编译为渲染函数
示例:
模板:
<div @click="handler"> click me </div>
渲染函数:
render() { return h('div', { onClick: handler }, 'click me'); }
对于 .vue
文件,编译器会把 <template>
里的内容编译为 render()
函数,然后添加到组件对象上去
# 5. Vue.js 是各个模块组成的有机整体
Vue.js 各个模块之间相互关联、制约
编译器与渲染器:
- 编译器在编译阶段标记动态内容
上一篇: 下一篇:
本章目录