一、前言
我们从全局视角了解Vue.js 3 的设计思路、工作机制及其重要的组成部分。我们可以把这些组成部分当作独立的功能模块,看看它们之间是如何相互配合的。
- UI的两种形式:模板字符串和虚拟DOM
- Vue.js框架的两个重要组成部分:编译器和渲染器
二、本章内容
2.1 声明式地描述UI
Vue.js 3 是一个声明式的UI框架。设计一个这样的框架,我们需要了解编写前端页面都涉及哪些内容?具体如下:
- DOM元素:是
div标签还是a标签。 - 属性:如
a标签的href属性,再如id、class等通用属性 - 事件:如
click、keydown等。 - 元素的层级结构:DOM树的层级结构,既有子节点,又有父节点。
那么,如何声明式地描述上述内容呢? 在Vue.js 3 中的解决方案是:
- 使用与HTML标签一致的方式来描述DOM元素、属性与层级结构等,例如
<div id="app"><span>趋动科技</span></div>; - 使用:或v-bind来描述动态绑定的属性,使用@或v-on来描述事件例如
<div :name="name" @click="handleClick"></div>; 这样,用户不需要手写任何命令式代码,就可以实现声明式地描述UI。当然除了使用这种模板形式描述UI外,还可以用JS对象来描述:
const title = {tag: 'div',props: { onClick: handleClick},children: [{tag: 'span'}]
}
对应到Vue.js模板是:
<div @click="handleClick"><span></span></div>
使用JS对象来描述比模板来说的优势是更加灵活。假如我们根据变量level的取值分别渲染h1~h6标签:
//模板
<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
<h3 v-else-if="level === 3"></h3>
<h4 v-else-if="level === 4"></h4>
<h5 v-else-if="level === 5"></h5>
<h6 v-else-if="level === 6"></h6>
//JS对象描述,只需要一个变量来代表h标签即可
let level = 1; //h标签的级别
const title = {tag:`h${level}`//h1标签
}
我们在Vue.js组件中通过手写渲染函数就是使用虚拟DOM来描述UI, h函数的返回值就是一个对象,让我们编写虚拟DOM变动更加轻松:
import { h } from 'vue'
export default {render(){return h('h1',{ onClick:handler }) //虚拟DOM}
}
组件的渲染函数:一个组件要渲染的内容是通过渲染函数来描述的,也就是代码中的render函数,Vue.js会根据组件的render函数的返回值拿到虚拟DOM,然后将组件的内容渲染出来。
2.2 初识渲染器
我们可以使用JS对象来描述真实的DOM结构。那么虚拟DOM又是如何通过渲染函数转为真实DOM后,渲染到页面中的呢? 渲染器的作用就是把虚拟DOM渲染为真实DOM
假如我们有如下虚拟DOM:
const vNode = {tag:'div',props: {onClick: () => alert('hello')},children: 'click me'
}
接下来,我们需要编写一个渲染器,把上面这段虚拟DOM渲染为真实DOM,renderer函数接收两个参数:vnode虚拟DOM对象与container真实DOM挂载点,渲染器会把虚拟DOM渲染到该挂载点下。
function renderer(vnode,container){// 使用 vnode.tag 作为标签名选创建DOM元素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(), //事件名称 onClick --> clickvnode.props[key] //事件处理函数)}}// 处理 childrenif(typeof vnode.children === 'string'){// 如果children 是字符串,说明它是元素的文本子节点el.appendChild(document.createTextNode(vnode.children))}else if(Array.isArray(vnode.children)){//递归调用 renderer 函数渲染子节点,使用当前元素el作为挂载点vnode.children.forEach(child => renderer(child,el))}//将元素添加到挂载点下container.appendChild(el)
}
渲染器renderer的实现思路:
- 创建元素
- 为元素添加属性和事件
- 递归遍历children创建节点
2.3 组件的本质
组件就是一组DOM元素的封装,这组DOM元素就是组件要渲染的内容,因此可以定义函数来描述组件本身的内容。
const MyComponent = function(){return {tag:'div',props:{onClick:() => alert('hello')},children:'click me'}}
这时候的虚拟DOM就是这样的:
const vnode = {tag: MyComponent
}
对于处理函数和字符串的时候,render 函数也会有所不同:
// 字符串 渲染函数
function mountElement(vnode, container) {// 使用 vnode.tag 作为标签名创建 DOM 元素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(), // 事件名称 onClick --> clickvnode.props[key] // 事件处理函数)}}// 处理 childrenif(typeof vnode.children === 'string') {// 如果 children 是字符串,说明它是元素的文本子节点el.appendChild(document.createTextNode(vnode.children))} else if(Array.isArray(vnode.children)) {// 递归调用 renderer 渲染子节点,使用当前 el 作为挂载点vnode.children.forEach(child => renderer(child, el))}// 将元素添加到挂载点下container.appendChild(el)
}
//函数渲染函数
function mountComponent(vnode, container) {// 调用组件函数,获取组件要渲染的内容(虚拟DOM)const subtree = vnode.tag()// 递归调用 renderer 渲染 subtreerenderer(subtree, container)
}
//主 render 函数
function renderer(vnode, container) {if (typeof vnode.tag === 'string') {// 说明 vnode 描述的是标签元素mountElement(vnode, container)} else if (typeof vnode.tag === 'function') {// 说明 vnode 描述的是组件mountComponent(vnode, container)}
}
2.4 模板的工作原理
无论是手写虚拟 DOM还是使用模板,都是属于声明式 UI,上文中讲过,需要将虚拟 DOM 转换为真实 DOM,这一过程需要的就是编译器。 编译器和渲染器一样,就是一个程序,编译器的作用就是将模板编译成渲染函数,如下模板:
<div @click="handler">click me</div>
最终通过编译器编译,会将其转化为渲染函数:
export default {render() {return h('div', { onClick: handler }, 'click me')}
}
对于一个组件来说,最终都是通过渲染函数产生的,然后渲染器把渲染函数返回的虚拟 DOM,渲染为真实 DOM,这就是模板的工作原理,也是Vue.js 渲染页面的流程。
2.5 Vue.js是各个模块组成的有机整体
在Vue.js框架设计中,组件的实现依赖于渲染器和编译器,渲染器和编译器之间是互相关联、互相制约的,它们共同构成一个有机整体,不同模块之间互相配合,进一步提升框架性能。
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:




文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取
理解Vue.js 3:Web IDL在声明式UI中的应用
本文深入探讨Vue.js 3的设计,特别是其声明式UI的实现。通过Web IDL,我们理解组件、模板和渲染器如何协同工作,构建虚拟DOM并将其转换为实际DOM。Vue.js 3的组件是声明式UI的核心,编译器和渲染器共同作用,形成框架的有机整体。
230

被折叠的 条评论
为什么被折叠?



