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
-
把对应组件记为脏组件,推入到 dirty_components 数组中
-
调用
schedule_update()
方法把flush
方法推入到一帧中的微任务阶段执行。因为这样既可以做频繁更新 的截流,又避免了阻塞一帧中的 layout, repaint 阶段的渲染。
schedule_update 方法其实就是一个promise.then()
,
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
对象篇
模块化编程-自研模块加载器
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
?x-oss-process=image/format,png)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-MvnmWOJg-1712566117904)]