Vue.js设计与实现(1-3)
框架设计概览#Vue.js 3的设计思路
框架设计讲究全局视角的把控,围绕核心思路展开。
一、Vue.js 3的设计思路:声明式地描述UI
Vue.js 是一个声明式的 UI 框架。
编写前端页面 | 声明式 UI 描述 | 举例说明 |
---|---|---|
DOM 元素 | 使用与 HTML 标签一致的方式 | 是 div 标签还是 a 标签 |
属性 | 使用与 HTML 标签一致的方式使用 : 或 v-bind 来描述动态绑定的属性 | a 标签的 href 属性,及通用属性 id ,class |
事件 | 使用 @ 或 v-on 来描述事件 | click ,keydown 等 |
元素的层级结构 | 使用与 HTML 标签一致的方式 | DOM 树的层级结构,既有子节点,又有父节点 |
声明式地描述 UI
:用户不需要手写任何命令式代码。
- 使用模板描述
<h1 @click="handleClick"> <span></span> </h1>
- 使用
JavaScript
对象描述const title = { // 标签名称 tag: 'h1', // 标签属性 props: { onClick: handleClick, }, // 子节点 children: [ { tag: 'span', }, ], }
使用模板描述更直观,使用 JavaScript
对象描述更灵活。
例:
h1
~h6
不同级别标题的表示
/**
* 模板描述:穷举
*/
<h1 v-if="level === 1"></h1>
<h2 v-if="level === 2"></h2>
<h3 v-if="level === 3"></h3>
<h4 v-if="level === 4"></h4>
<h5 v-if="level === 5"></h5>
<h6 v-if="level === 6"></h6>
/**
* JavaScript对象描述:变量代表
*/
let level = 3 // h 标签的级别
const title = {
tag: `h${level}`, // h3标签
}
使用 JavaScript
对象来描述 UI
的方式,就是所谓的虚拟 DOM
。
渲染函数:render
函数,描述一个组件要渲染的内容。
Vue.js
根据组件的 render
函数的返回值拿到虚拟 DOM
,然后就可以把组件的内容渲染出来了。
二、Vue.js 3的设计思路:初识渲染器
渲染器的作用就是把虚拟 DOM
渲染为真实 DOM
。
- 虚拟
DOM
const vnode = { tag: 'div', props: { onClick: () => alert('hello') }, children: 'click me' }
tag
:标签名称。props
:一个对象,描述标签的属性、事件等内容。children
:可以是一个字符串,也可以是一个数组,描述标签的子节点信息。
- 渲染器
const renderer = (vnode, container) => { // 步骤一:创建元素 const el = document.createElement(vnode.tag) // 步骤二:为元素添加属性和事件 for (const key in vnode.props) { if (/^on/.test(key)) { el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key]) } else { // TODO: 属性解析-class, style, 其他动态属性... } } // 步骤三:处理 children if (typeof vnode.children === 'string') { // 字符串:文本节点 el.appendChild(document.createTextNode(vnode.children)) } else if (Array.isArray(vnode.children)) { // 数组:递归渲染 vnode.children.forEach(child => renderer(child, el)) } container.appendChild(el) }
vnode
:虚拟DOM
对象。container
:一个真实DOM
元素,作为挂载点,渲染器会把虚拟DOM
渲染到该挂载点下。
- 真实
DOM
// 初始将 body 作为挂载点 renderer(vnode, document.body)
渲染器的实现思路
- 创建元素
- 为元素添加属性和事件
- 处理
children
渲染器的工作原理
递归地遍历虚拟 DOM
对象,并调用原生 DOM API
来完成真实 DOM
的创建。
三、Vue.js 3的设计思路:组件的本质
虚拟 DOM
除了能够描述真实 DOM
之外,还能够描述组件。
组件就是一组 DOM
元素的封装,这组 DOM
元素就是组件要渲染的内容。
-
虚拟DOM
const vnode = { tag: 'div', props: { onClick: () => alert('hello') }, children: 'click me' } /** * 直接渲染 */ const mountComponent = (vnode, container) => { // typeof vnode.tag === 'string' renderer(subtree, container) }
-
组件函数
const MyComponent = function () { // 组件返回虚拟 DOM return { tag: 'div', props: { onClick: () => alert('hello') }, children: 'click me' } } /** * 渲染组件 */ const mountComponent = (vnode, container) => { // typeof vnode.tag === 'function' const subtree = vnode.tag() renderer(subtree, container) }
-
组件对象
const MyComponent = { // 组件的方法返回虚拟DOM render() { return { tag: 'div', props: { onClick: () => alert('hello') }, children: 'click me' } } } /** * 渲染组件 */ const mountComponent = (vnode, container) => { // typeof vnode.tag === 'object' const subtree = vnode.tag.render() renderer(subtree, container) }
不同形式的虚拟 DOM
,对应不同的渲染逻辑:渲染器渲染的内容始终是最纯粹的虚拟 DOM
。
四、Vue.js 3的设计思路:模板的工作原理
编译器:将模板编译为渲染函数。
- 模板内容
<div @click="handler">click me</div>
- 渲染函数
render() { return h('div', { onClick: handler }, 'click me') }
无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 DOM
渲染为真实 DOM
,这就是模板的工作原理,也是 Vue.js
渲染页面的流程。 —— 渲染器把渲染函数产生的虚拟 DOM
渲染为真实 DOM
。
五、Vue.js 3的设计思路:Vue.js是各个模块组成的有机整体
- 组件的实现依赖于渲染器
- 模板的编译依赖于编译器
- 编译后生成的代码是根据渲染器和虚拟
DOM
的设计决定的
编译器、渲染器都是 Vue.js
的核心组成部分,它们共同构成一个有机的整体,不同模块之间相互配合,进一步提升框架性能。
转载自:https://juejin.cn/post/7302404170413506586