Vue.js设计与实现阅读-3

Vue设计与实现阅读-3

    • 1、声明式描述UI
    • 2、渲染器
    • 3、组件
    • 4、模板的工作原理
    • 5、Vue.js 是各个模块组成的有机整体

前言
前面一章我们了解了,开发体验是衡量一个框架的重要指标之一。提供友好的警告信息至关重要,但是越详细的警告信息,意味着框架体积越大。为了解决这一问题,可以利用 Tree-Shaking 机制,配合构建工具预定义常量,例如 __DEV __ ,从而实现只在开发环境中打印警告信息,生产环境中清楚这些代码,达到代码体积的可控性。
Tree-Shaking 是一种清除 dead code 的机制。可以利用/* #PURE */ 来辅助构建工具进行 Tree-Shaking。
框架的错误处理做的好坏决定了用户程序的健壮性,因此需要为用户提供统一的错误处理接口,这样用户可以通过注册自定义的错误处理函数来处理异常。

1、声明式描述UI

Vue.js 模板描述

<h1 @click="handleClick"><span></span>
</h1>

js 对象描述上面的UI

const title = {tag: 'h1',props: {onClick: handleClick},children: [{tag: 'span'}]
}

如果我们想表示一个标题,根据标题级别的不同,分别采用h1~ h6 这几个标签,如果用js实现

let level = 1;
const title = {tag: `h${level}`
}

如果 level 的值变化,相应的标签名称也会变化。但是如果用 模板来描述,就需要一个个列举出来。

<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
...
<h6 v-else-if="level === 6"></h6>

远远没有js对象灵活。使用js对象来描述 UI 的方式,其实就是虚拟DOM。Vue3中除了支持使用模板描述 UI外,还支持虚拟DOM描述 UI。例如我们手写的渲染函数就是使用虚拟 DOM 描述UI。

import {h} from 'vue';
export default {render() {return h('h1', {onClick: handleClick})}
}

2、渲染器

虚拟 DOM 就是用 JS 对象来描述真实的DOM结构。如何将虚拟DOM变成真实的DOM并且渲染到浏览器页面中---渲染器

渲染器的作用,把虚拟DOM渲染为真实DOM

虚拟DOM如下:

const vnode = {tag: 'div',props: {onClick: () => alert('hello')},children: 'click me'
}

编写一个 渲染器,将上面的虚拟 DOM 渲染成真实DOM

function renderer (vnode, container) {// 创建 dom 元素const el = document.createElement(vnode.tag);// 添加属性、事件到 dom 元素for (const key in vnode.props) {if (/^on/.test(key)) {// on 开头,事件,给元素绑定事件el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])}}// children 处理if (typeof vnode.children === 'string') {// string -- 文本el.appendChild(document.createTextNode(vnode.children));} else if (Array.isArray(vnode.children)) {// 数组,递归调用,渲染子节点vnode.children.forEach(child => renderer(child, el));}container.appendChild(el)
}

运行结果:
在这里插入图片描述
上述分析器实现思路

  • 创建DOM元素,vnode.tag 为标签名
  • 给元素添加属性和事件。遍历 props 对象,如果 key 是 on 开头,说明是事件,截取字符 on 使用 toLowerCase将事件名称小写,再调用 addEventListener 绑定事件
  • 处理元素的 children。如果是字符串,创建文本节点,如果是数组,递归调用 renderer 函数渲染。

发现渲染器实现起来也很简单,主要是使用一些熟悉的 DOM 操作 API 来完成渲染工作。上面只是创建节点,渲染器的精髓在更新节点的阶段,需要精确找啊到 vnode对象的变更点,并且只更新变更的内容。

3、组件

上面了解了渲染器会将虚拟 DOM 渲染成真实的 DOM 元素。那组件呢,组件和虚拟 DOM 之前有什么关系。

**组件是一组 DOM 元素的封装,这组 DOM 元素就是组件要渲染的内容。**定义一个函数来代表组件,函数的返回值代表组件要渲染的内容。

const MyComponent = function () {return {tag: 'div',props: {onClick: () => alert('click')},children: 'click me'}
}

可以看到组件的 返回值也是虚拟 DOM,代表了组件要渲染的内容,那么我们就可以使用下面的方式来描述组件。其中 tag 可以用来描述组件。

const vnode = {tag: MyComponent
}

修改之前的渲染函数

	// 渲染函数renderer(vnode, container) {if (typeof vnode.tag === 'string') {// string -- 普通标签元素this.mountElement(vnode, container);} else if (typeof vnode.tag === 'function') {// string -- 组件this.mountComponent(vnode, container);}},// 渲染标签元素mountElement(vnode, container) {// 创建 dom 元素const el = document.createElement(vnode.tag);// 添加属性、事件到 dom 元素for (const key in vnode.props) {if (/^on/.test(key)) {// on 开头,事件,给元素绑定事件el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])}}// children 处理if (typeof vnode.children === 'string') {// string -- 文本el.appendChild(document.createTextNode(vnode.children));} else if (Array.isArray(vnode.children)) {// 数组,递归调用,渲染子节点vnode.children.forEach(child => this.renderer(child, el));}container.appendChild(el)},// 渲染组件mountComponent(vnode, container) {// 调用组件函数,获取要渲染的内容const subTree = vnode.tag();this.renderer(subTree, container)},

4、模板的工作原理

上面我们了解了虚拟 DOM 是如何渲染成真实 DOM的,那么模板是如何工作的呢?其实是依赖于编译器。

编译器的作用是将模板编译成渲染函数。
模板如下

<div @click="handleClick">click me</div>

对编译器来说,模板相当于一个字符串,它会分析字符串并生成一个功能与之相同的渲染函数:

render() {return h('div', {onClick: handleClick}, 'click me')
}

在 Vue中一个.vue文件就是一个组件

<template><div @click="handleClick">click me</div>
</template><script>
export default {data() {return {}},methods: {handleClick() {// do nothing}}
}
</script>

其中 < template > 标签里的内容为模板内容,编译器会把模板内容编译成渲染函数并且添加到 script 标签上,最终在浏览器里面运行的代码就是

export default {data() {return {}},methods: {handleClick() {// do nothing}},render() {return h('div', {onClick: handleClick}, 'click me')}
}

对一个组件来说,他要渲染的内容最终都是通过渲染函数产生的,渲染器再将渲染函数返回的虚拟 DOM 渲染为真实 DOM,这就是模板的工作原理,也是 Vue.js 渲染页面的过程。

5、Vue.js 是各个模块组成的有机整体

组件的实现依赖于渲染器,模板的编译依赖于编译器。编译后生成的代码是根据渲染器和虚拟 DOM的设计决定的。 因此Vue的各个模块之间是相互关联,相互制约的。
模板

<div id="foo" :class="cls"></div>

渲染函数

render() {return {tag: 'div',props: {id: 'foo',class: 'cls'}}
}

cls 是一个动态属性,那编译器如何知道 cls 发生了变化呢。从上面的模板 我们可以看出 id 是不会发生变化的, :class 可能会发生变化。编译器能识别出哪些是静态属性,哪些是动态属性,因此可以在生成代码的时候,标志出哪些是动态的属性。

render() {return {tag: 'div',props: {id: 'foo',class: 'cls'},patchFlags: 1  // 1 代表 class是动态的}
}

在虚拟 DOM 中多了一个 patchFlags 属性标志 class 的类型是动态还是静态,这样渲染器可以根据这个标志,知道有什么属性会发生变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/338510.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++重新认知:inline关键字

一、为什么要使用inline关键字 在C中.&#xff0c;为了解决频繁调用小函数造成大量消耗栈空间的问题&#xff0c;引进了inline关键字&#xff0c;inline修饰的函数为内敛函数&#xff08;频繁大量的使用栈空间会造成栈溢出&#xff09;。 在这段代码中&#xff0c;每次执行for…

用matlab解决简单的数学问题

1 微分和积分 微分和积分是数学计算中常用的手段。微积分最重要的思想就是”微元“与”逐次逼近“&#xff0c;一个整体的事物往往不好研究&#xff0c;但是通过微元分割成一下块一小块的&#xff0c;当做常量处理&#xff0c;最终加起来就能实现运行效果。在matlab中实现函数微…

在visual studio中调试时无法查看std::wstring

1.问题 在调试的时候发现std::wstring类型的变量查看不了&#xff0c;会显示(error)|0&#xff0c;百思不得其解。 2.解决方法 参考的&#xff1a;vs2015调试时无法显示QString变量的值&#xff0c;只显示地址_vs调试qstring的时候如何查看字符串-CSDN博客 在工具/选项/调试…

蓝凌EIS pdf.aspx 任意文件读取漏洞

漏洞描述&#xff1a; 蓝凌EIS智慧协同平台是一个简单、高效的工作方式专为成长型企业打造的沟通、协同、社交的移动办公平台&#xff0c;覆盖OA、沟通、客户、人事、知识等管理需求&#xff0c;集合了非常丰富的模块&#xff0c;满足组织企业在知识、项目管理系统建设等需求的…

12.22 探探 数分 已HR面

岗位信息 1222 3.30PM 1面 40min 能感觉数据基建还不是很完善 因此 问了一些指标体系的问题还挺多 自我介绍能力考察1.说说你是怎么异常归因的以付费场景项目为例2.归因中如果遇到一个页面同时存在有3个实验在跑 无法归因出数据的异常是哪个改动造成的怎么办&#xff1f;3.讲…

uniapp日期加减切换,点击切换

先上完成后的页面&#xff1a;当前年年份不显示&#xff0c;不然完整显示。 可以切换和自定义选择。 html:样式和图片自定义。 <view class"image-text_30"><image click"delMonth" :src"require(/static/home/zuo.png)" class"…

解决docker run报错:Error response from daemon: No command specified.

将docker镜像export/import之后&#xff0c;对新的镜像执行docker run时报错&#xff1a; docker: Error response from daemon: No command specified. 解决方法&#xff1a; 方案1&#xff1a; 查看容器的command&#xff1a; docker ps --no-trunc 在docker run命令上增加…

GLSL着色器入门(持续更新中...)

目录 第一章&#xff1a;OpenGL works with triangles 第二章&#xff1a; Parallel Processing 第章 推荐来自b站的课程004 GLSL is not Javascript_哔哩哔哩_bilibili 第一章&#xff1a;OpenGL works with triangles 当我们谈论GLSL着色器时&#xff0c;其实就是在说怎么…

react项目运行卡在编译:您当前运行的TypeScript版本不受@TypeScript eslint/TypeScript estree的官方支持

1.问题 错误信息具体如下&#xff1a; 搜索了一下&#xff0c;是typescript版本的问题&#xff0c;提示我版本需要在3.3.0和4.5.0中间&#xff0c;我查看了package.json&#xff0c;显示版本为4.1.3&#xff0c;然后一直给我提示我的版本是4.9.5&#xff0c;全局搜索一下&…

vue实现-年、月、日、时、分、秒、星期?

一、文章引导 #mermaid-svg-nP4oT3Y4d6oaxUsg {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-nP4oT3Y4d6oaxUsg .error-icon{fill:#552222;}#mermaid-svg-nP4oT3Y4d6oaxUsg .error-text{fill:#552222;stroke:#55222…

Django报错处理

django.template.exceptions.TemplateDoesNotExist: django/forms/widgets/text.html django.template.exceptions.TemplateDoesNotExist: django/forms/widgets/number.html以上报错是pycharm中创建虚拟环境之后把原本自带的templates文件删除&#xff0c;重新在app01下面创建…

启英泰伦推出「离线自然说」,离线语音交互随意说,不需记忆词条

离线语音识别是指不需要依赖网络&#xff0c;在本地设备实现语音识别的过程&#xff0c;通常以端侧AI语音芯片作为载体来进行数据的采集、计算和决策。但是语音芯片的存储空间有限&#xff0c;通过传统的语音算法技术&#xff0c;最多也只能存储数百条词条&#xff0c;导致用户…