前言
sevelt 能够在 vue 和 react 中占据一席之地,以更小的打包体积和跨框架组件作为技术核心,笔者出于了解的想法对 sevelt 的原理做了学习,以加深对前端和前端框架的了解。
是什么
Sevelte 作为一种区别于 vue 和 react 的框架,通过抛弃虚拟 DOM ,减少运行时代码的方式。将捕获状态变化更新视图这部分工作放到编译这一步实现,抛弃运行时代码,减小打包体积,提升运行效率。
简而言之,Sevelte通过抛弃 VNodes 相关的运行时代码,将这部分功能由,编译后直接生成的JS代码实现,从而减少运行时代码,减少了打包体积
前端框架的作用
前端开发中不可避免会存在一个问题,即如何高效编写更新页面的代码。
浏览器是通过读取 HTML 文件,解析为 DOM 树。 解析css生成 CSSOM 树, 结合 DOM 树和 CSSOM 树,形成 Render Tree,最后生成我们看到的整个网页的。
JS 的 DOM 操作只提供了获取 DOM, 新增删除 DOM 的API。那么在管理一个表单,管理一个页面树时,涉及到频繁更新页面元素的需求,就会带来两个问题:
1.针对每种情况写单独的js。
问题:会导致 control 层巨大,巨量的if else 导致页面维护成本剧增。
2.将页面分为几个单独的模块,每个模块通过一个 render 函数生成,这个模块单独监听几个关联的状态,状态更新则直接触发render函数,清空原有的 DOM, 将更新完状态的 DOM 元素插入。
问题: 在执行 render 函数的过程中,不可避免的会出现 JS 过量执行,如一个模块只需要更新模块中一个子节点,或是更新一个子节点的元素,但是整个模块的 DOM 元素都被删除后再插入,造成了性能上的浪费。 注:如 js 新增节点和删除节点函数的能耗,跟操作节点的大小成正相关,能操作小节点实现需求时,实际操作了更大的节点,那么,可称为 JS "执行过量"。
运行时代码存在的价值
那么,基于上述问题,vue 和 react 都通过自己的 diff 算法,在状态更新需要更新 DOM 节点时,通过比对每个节点的当前状态和更新后的目标状态,精准筛选出需要更新的节点,再执行一个小型的 render 函数,减缓更新 DOM 过程中,render 函数的 JS 执行过量的情况。 那么为了达到精准定位的效果,则添加了运行时代码,用于每次状态更新,需要执行一个 tick 来更新视图时,先捕获变更的状态,对比找到需要更新的节点,再执行 render 函数更新视图,用较小的代码达到目的,并且在程序员侧也减少了代码量和开发成本。
如何编译能省掉运行时代码
那么,脱离捕获每次状态更新,精准循环找到需要更新的节点这一方案。在编译时通过解析代码,将代码中存在的可能对状态有影响的函数,这段影响状态的函数导向的 render 函数。(也可能是一段修改属性,或是修改几个节点的js)在编译后直接生成。
如此,即为建立了一个,程序员编写影响状态的代码,编译器直接生成从影响状态到更新视图的代码。这一系统。 通过该系统,即可达到不影响开发效率又能保障运行时性能的目的。
sevelte 包小的代价
那么基于上述理解,可以得出结论。 就编译后的代码而言, 单独写 if else 是实现了一个又一个特解。 如sevelte.js, solid.js, vanpor(vue) 这种编译型框架 而 VNodes 这一方案是给了一个通解。 vue, react, angular 这种含 VNodes 框架。
可简要得出,以X轴为组件数量,Y轴为包体积:
Y = aX + B
B为运行时代码。
a表示包体积随着组件数量(代码数量)增长的增长趋势
编译型框架的 a 更大 B更小。
而含 VNodes 框架的 a 更小, B更大。
怎么做
基于上述讨论,那么程序员在开发编写前端项目时,技术调研这一步。 可以在碰到DOM太多时性能优化的场景,通过编译型框架生成的组件替换 VNodes 框架生成的组件,来作为一个可行的优化方案(实现后在使用过程中进行性能比对。)