首页 > 其他分享 >Vue.js 的设计与实现(一)

Vue.js 的设计与实现(一)

时间:2023-09-08 16:02:39浏览次数:32  
标签:Vue obj 框架 Render 代码 js 编译 命令式 设计

一、框架设计思路:权衡的艺术

从范式角度看,框架设计成命令式好还是声明式好?

框架设计成运行时编译时运行时 + 编译时

1.1、命令式

代表框架:jQuery

特点:关注过程

1.2、声明式

代表框架:Vue.js

特点:关注结果

1.3、例子

有以下需求:

1、获取id为app的div标签

2、它的文本内容为hello world

3、为其绑定点击事件

4、当点击时弹出提示: ok


转换成命令式代码就是:

$('#app') //获取div
text('hello world') //设置文本内容
  . on( 'click', () => { alert('ok') }) //绑定点击事件

可以看出代码描述的是做事的过程


转换成声明式代码就是:

<div @click="() => alert('ok')">hello world</div>

可以看出,代码直接给我们展示一串结果,这段结果就是告诉Vue让它去做上面命令式代码做的东西。也就是说,Vue帮我们封装了过程(你肯定猜到Vue内部的实现方式是命令式的),而我们只需要使用特定的指令就可以让Vue为我们工作而不需要我们去弄清楚其中实现的细节。

1.4、二者优缺点(命令式、声明式)

性能上

命令式代码直接执行,而声明式相当于要先编译声明式代码,将其转成命令式,再去执行,所以声明式代码的性能是永远也优先不了命令式代码的

可维护性上

命令式代码可维护性更差,它需要我们手动去创建、更新、删除dom结构,而声明式代码没有这个操作,只需使用指定的指令即可。

权衡

这体现了我们在框架设计上要做出的关于可维护性与性能之间的权衡:在采用声明式提升可维护性的同时,性能就会有一定的损失,而框架设计者要做的就是:在保持可维护性的同时让性能损失最小化

1.5、虚拟DOM

声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗

如果我们能够最小化找出差异的性能消耗,就可以让声明式代码的性能无限接近命令式代码的性能。而所谓的虚拟DOM,就是为了最小化找出差异这一步的性能消耗而出现的。

一般来说采用虚拟DOM的更新技术的性能理论上不可能比原生JavaScript 操作DOM更高。这里我们强调了理论上三个字,因为这很关键,为什么呢?因为在大部分情况下,我们很难写出绝对优化的命令式代码,尤其是当应用程序的规模很大的时候,即使你写出了极致优化的代码,也一定耗费了巨大的精力,说白了就是投入产出比不高,无法在一定时间内达到性能、代码完成度两者的最大化,而这个就是虚拟DOM需要解决的问题。

Vue.js 的设计与实现(一)_开发者

Vue.js 的设计与实现(一)_Vue_02

Vue.js 的设计与实现(一)_开发者_03

Vue.js 的设计与实现(一)_Vue_04

1.6、运行时

假设我们设计了一个框架,它提供一个Render函数,开发者们可以为该函数提供一个树型结构的数据对象,然后Render函数会根据该对象递地将数据渲染成DOM元素。我们规定树型结构的数据对象如下:

const obj = { 
  tag: 'div', 
  children: [ 
    { tag: 'span', children: 'hello world' } 
  ] 
};

每个对象都有两个属性:tag 代表标签名称,children 既可以是一个数组(代表子节点),也可以直接是一段文本(代表文本子节点)。

Render函数的实现如下:

function Render(obj, root) { 
  const el = document.createElement(obj.tag) 
  if (typeof obj.children === 'string') { 
    const text = document.createTextNode(obj.children) 
    el.appendChild(text) 
  } else if (obj.children) { 
    // 数组,递归调用 Render,使用 el 作为 root 参数 
    obj.children.forEach((child) => Render(child, el)) 
  } 

  // 将元素添加到 root 
  root.appendChild(el) 
}

有了这个函数,我们就可以这样使用它:

const obj = { 
  tag: 'div', 
  children: [ 
    { tag: 'span', children: 'hello world' } 
  ] 
};
// 渲染到 body 下 
Render(obj, document.body)

如果开发者觉得手写树型结构的数据对象太麻烦,不直观,希望支持用类似于HTML标签的方式描述树型结构的数据对象,但是我们不支持,那么,我们刚刚编写的框架就是一个纯运行时的框架,即用指定结构的对象Render函数来进行工作的这么一种方式。

1.7、运行编译时

书接上文,我们考虑了开发者的诉求,决定引人编译的手段,把HTM标签编译成树型结构的数据对象,这样心智就会大大降低,编码的速度也会快很多。

Vue.js 的设计与实现(一)_数据_05

我们决定编写一个Compiler程序,它的作用j就是把HTML字符串编译成树型结构的数据对象。那么开发者该怎么用呢?其实这也是我们要思考的问题,最简单的方式就是让用户分别调用Compiler函数和Render函数:

const html = `<div> 
<span>hello world</span> 
</div> 
` 
// 调用 Compiler 编译得到树型结构的数据对象 
const obj = Compiler(html)
// 再调用 Render 进行渲染 
Render(obj, document.body)

此时我们的框架就是一个运行时 + 编译时框架,准确地说,上面的代码其实是运行时编译。意思是代码运行的时候才开始编译,而这会产生一定的性能开销,因此我们也可以在构建的时候就执行Compiler程序将开发者提供的内容编译好,等到运行时就无须编译了,这对性能是非常友好的。

1.8、编译时

书接上文,既然编译器可以把HTML字符串编译成数据对象,那么也可以直接编译成命令式代码吧?

Vue.js 的设计与实现(一)_Vue_06

这样我们只需要一-个Compiler函数就可以了,连Render都不需要了。其实这就变成了一个纯编译时的框架,因为我们不支持任何运行时内容,用户的代码通过编译器编译后才能运行。

1.9、三者优缺点(运行时、编译时、运行时+编译时)

运行时

由于它没有编译的过程,因此我们没办法分析用户提供的内容。

运行时+编译时

如果在运行时的基础上加入编译步骤,我们可以分析用户提供的内容,看看哪些内容未来可能会改变,哪些内容永远不会改变,这样我们就可以在编译的时候提取这些信息,然后将其传递给Render函数,Render 函数得到这些信息之后,就可以做进一步的优化了。

编译时

假如我们设计的框架是纯编译时的,那么它也可以分析用户提供的内容。由于不需要任何运行时,而是直接编译成可执行的JavaScript代码,因此性能可能会更好,但是这种做法有损灵活性,即用户提供的内容必须编译后才能用。

总结

实际上,在这三个方向上业内都有探索,其中svelte就是纯编译时的框架,但是它的真实性能可能达不到理论高度。Vue3仍然保持了运行时+编译时的架构,在保持灵活性的基础上能够尽可能地去优化。在Vue3在保留运行时的情况下,其性能甚至不输纯编译时的框架。

标签:Vue,obj,框架,Render,代码,js,编译,命令式,设计
From: https://blog.51cto.com/u_16253710/7411271

相关文章

  • java设计模式,简单工厂和抽象工厂有什么区别?
    java设计模式,简单工厂和抽象工厂有什么区别?简单工厂模式:这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。它由三种角色组成:工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工......
  • Web阶段:第十四章:JSTL标签库
    JSTL标签库JSTL标签库全称是指JSPStandardTagLibraryJSP标准标签库。是一个不断完善的开放源代码的JSP标签库。EL表达式主要是为了替换jsp中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp页面变得更佳简洁。JSTL由五个不同功能的标签库组成。功能范围URI......
  • vue项目Canvas给图片改色,返回base64的图片
     (适应于纯色图片)1.vue文件中创建canvas<!--落图图片改色--><canvasid="mycanvas"></canvas>2.创建changeImgColor.js文件letctx=null;letcanvas=null;letbase64Info=null;exportfunctionsetImgColor(url){returnnewPromise((resolve......
  • Vue开发流程
    #Vue简介  Vue(发音为/vjuː/,类似 view)是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue都可以胜任。声明式渲染:Vue基于标准H......
  • [RxJS] "Animation Allowed" problem
    consttasks=of([....]);/***{*...{...4......5......2}*...........{3...........2...5}*..................................{6....3}*..........................................{3..4....2}*}**/constanimationAllowed=t......
  • Vercel 与 Next.js:开源全明星团队背后的商业逻辑
    Vercel与Next.js:开源全明星团队背后的商业逻辑aryu2022-01-2610:183616 视频版本推荐同步观看,喜欢请一键三连~#Vercel与Next.js:开源全明星团队背后的商业逻辑|MonetizingOpenSource引子Vercel是由GuillermoRauch创立的云服务公司,以拥有数个知名开源项目......
  • 解决vue3+js unplugin-auto-import/vite 自动引入生效后 页面eslint报错
           ......
  • vue实现一个右侧悬浮工具栏
    最近在写商城时需要用到一个右侧悬浮的工具栏,并把这个写成了控件,刚开始是想要使用elementui自带的backtop组件的,无奈能力有限,不能改造成多列的结果,所以就只能自己写一个了这个控件实现的功能为,一开始只有3个内容,当页面下滑后会多出一个回到顶部的内容,并使用定时器连贯的回到顶部......
  • PCB设计丨电源设计的重要性
    电源是最容易被忽视的,电源是系统运行的重要组成部分,电源就像“人体的心脏”,为系统的硬件输送血液(电),要是心脏(电源)运行不正常或供血(电)不足,会导致系统不运行或运行不稳定,在设计之前应该对核心模块峰值电流表进行知悉,供PCBLayout时评估线宽作用,如下表值得注意的是,不能简单的全部加起......
  • 设计模式-单例模式
    保证在整个软件系统中,对某个类只能存在一个对象实例。饿汉式(类加载时创建,没用到也创建)1、构造器私有化(防止new对象)。2、类内部创建私有的静态对象。3、用一个公共的getInstance()静态方法返回该对象。如Runtime类懒汉式(使用才创建)1、仍然使构造器私有化。2、类内部定义静......