美团 前端面试,新兴前端框架 Svelte 从入门到原理,小码农也有大梦想

JSX 优缺点

jsx 具有 JavaScript 的完整表现力,非常具有表现力,可以构建非常复杂的组件。

但是灵活的语法,也意味着引擎难以理解,无法预判开发者的用户意图,从而难以优化性能。你很可能会写出下面的代码:

在使用 JavaScript 的时候,编译器不可能hold住所有可能发生的事情,因为 JavaScript 太过于动态化。也有人对这块做了很多尝试,但从本质上来说很难提供安全的优化。

Template优缺点

Template模板是一种非常有约束的语言,你只能以某种方式去编写模板。

例如,当你写出这样的代码的时候,编译器可以立刻明白:”哦!这些 p 标签的顺序是不会变的,这个 id 是不会变的,这些 class 也不会变的,唯一会变的就是这个“

在编译时,编译器对你的意图可以做更多的预判,从而给它更多的空间去做执行优化。

左侧 template 中,其他所有内容都是静态的,只有 name 可能会发生改变。

右侧 p 函数是编译生成的最终的产物,是原生的js可以直接运行在浏览器里,会在有脏数据时被调用。p 函数唯一做的事情就是,当 name 发生变更的时候,调用原生方法把 t1 这个原生DOM节点更新。这里的 set_data 可不是 React 的 setState 或者小程序的 setData ,这里的set_data 就是封装的原生的 javascript 操作DOM 节点的方法。

如果我们仔细观察上面的代码,发现问题的关键在于 if 语句的判断条件——changed.name, 表示有哪些变量被更新了,这些被更新的变量被称为脏数据。

任何一个现代前端框架,都需要记住哪些数据更新了,根据更新后的数据渲染出最新的DOM

Svelte 记录脏数据的方式:位掩码(bitMask)

Svelte使用位掩码(bitMask) 的技术来跟踪哪些值是脏的,即自组件最后一次更新以来,哪些数据发生了哪些更改。

位掩码是一种将多个布尔值存储在单个整数中的技术,一个比特位存放一个数据是否变化,一般1表示脏数据,0表示是干净数据。

用大白话来讲,你有A、B、C、D 四个值,那么二进制0000 0001表示第一个值A发生了改变,0000 0010表示第二个值B发生了改变,0000 0100表示第三个值C发生了改变,0000 1000表示第四个D发生了改变。

这种表示法,可以最大程度的利用空间。为啥这么说呢?

比如说,十进制数字3就可以表示 A、B是脏数据。先把十进制数字3, 转变为二进制0000 0011。从左边数第一位、第二位是1,意味着第一个值A 和第二个值B是脏数据;其余位都是0,意味着其余数据都是干净的。

JS 的限制

那么,是不是用二进制比特位就可以记录各种无穷无尽的变化了呢?

JS 的二进制有31位限制,number 类型最长是32位,减去1位用来存放符号。也就是说,如果 Svelte 采用二进制位存储的方法,那么只能存 31个数据。

但肯定不能这样,对吧?

Svelte 采用数组来存放,数组中一项是二进制31位的比特位。假如超出31个数据了,超出的部分放到数组中的下一项。

这个数组就是component.$.dirty数组,二进制的1位表示该对应的数据发生了变化,是脏数据,需要更新;二进制的0位表示该对应的数据没有发生变化,是干净的

一探究竟component.$.dirty

上文中,我们说到component.$.dirty是数组,具体这个数组长什么样呢?

我们模拟一个 Svelte 组件,这个 Svelte 组件会修改33个数据。

我们打印出每一次make_dirty之后的component.$.dirty, 为了方便演示,转化为二进制打印出来,如下面所示:

上面数组中的每一项中的每一个比特位,如果是1,则代表着该数据是否是脏数据。如果是脏数据,则意味着更新。

  • 第一行["0000000000000000000000000000001", "0000000000000000000000000000000"], 表示第一个数据脏了,需要更新第一个数据对应的dom节点

  • 第二行["0000000000000000000000000000011", "0000000000000000000000000000000"], 表示第一个、第二个数据都脏了,需要更新第一个,第二个数据对应的dom节点。

  • ……

当一个组件内,数据的个数,超出了31的数量限制,就数组新增一项来表示。

这样,我们就可以通过component.$.dirty这个数组,清楚的知道有哪些数据发生了变化。那么具体应该更新哪些DOM 节点呢?

数据和DOM节点之间的对应关系

我们都知道, React 和 Vue 是通过 Virtual Dom 进行 diff 来算出来更新哪些 DOM 节点效率最高。Svelte 是在编译时候,就记录了数据 和 DOM 节点之间的对应关系,并且保存在 p 函数中。

这里说的p 函数,就是 Svelte 的更新方法,本质上就是一大堆if判断,逻辑非常简单

if ( A 数据变了 ) {

更新A对应的DOM节点

}

if ( B 数据变了 ) {

更新B对应的DOM节点

}

为了更加直观的理解,我们模拟更新一下33个数据的组件,编译得到的p 函数打印出来,如:

我们会发现,里面就是一大堆if判断,但是if判断条件比较有意思,我们从上面摘取一行仔细观察一下:

首先要注意,&不是逻辑与,而是按位与,会把两边数值转为二进制后进行比较,只有相同的二进制位都为1 才会为真。

这里的if判断条件是:拿compoenent.$.dirty[0](00000000000000000000000000000100)和4(4 转变为二进制是0000 0100)做按位并操作。那么我们可以思考一下了,这个按位并操作什么时候会返回1呢?

4是一个常量,转变为二进制是0000 0100, 第三位是1。那么也就是,只有dirty[0]的二进制的第三位也是1时, 表达式才会返回真。换句话来说,只有第三个数据是脏数据,才会走入到这个if判断中,执行set_data(t5, ctx[2]), 更新t5这个 DOM 节点。

当我们分析到这里,已经看出了一些眉目,让我们站在更高的一个层次去看待这 30多行代码:它们其实是保存了这33个变量 和 真实DOM 节点之间的对应关系,哪些变量脏了,Svelte 会走入不同的if体内直接更新对应的DOM节点,而不需要复杂 Virtual DOM DIFF 算出更新哪些DOM节点

这 30多行代码,是Svelte 编译了我们写的Svelte 组件之后的产物,在Svelte 编译时,就已经分析好了,数据 和 DOM 节点之间的对应关系,在数据发生变化时,可以非常高效的来更新DOM节点。

Vue 曾经也是想采取这样的思路,但是 Vue 觉得保存每一个脏数据太消耗内存了,于是没有采用那么细颗粒度,而是以组件级别的中等颗粒度,只监听到组件的数据更新,组件内部再通过 DIFF 算法计算出更新哪些 DOM 节点。Svelte 采用了比特位的存储方式,解决了保存脏数据会消耗内存的问题。

整体流程

上面就是Svelte 最核心更新DOM机制,下面我们串起来整个的流程。

下面是非常简单的一个 Svelte 组件,点击<button>会触发onClick事件,从而改变name 变量。

上面代码背后的整体流程如下图所示,我们一步一步来看:

第一步,Svelte 会编译我们的代码,下图中左边是我们的源码,右边是 Svelte 编译生成的。Svelte 在编译过程中发现,『咦,这里有一行代码 name 被重新赋值了,我要插入一条make_dirty的调用』,于是当我们改写 name 变量的时候,就会调用make_dirty方法把 name 记为脏数据。

第二步,我们来看make_diry方法究竟做了什么事情:

  1. 把对应数据的二进制改为1

  2. 把对应组件记为脏组件,推入到 dirty_components 数组中

  3. 调用schedule_update()方法把flush方法推入到一帧中的微任务阶段执行。因为这样既可以做频繁更新 的截流,又避免了阻塞一帧中的 layout, repaint 阶段的渲染。

schedule_update 方法其实就是一个promise.then()

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

对象篇

模块化编程-自研模块加载器

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

?x-oss-process=image/format,png)

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-MvnmWOJg-1712566117904)]

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值