Cydon——轻量级高性能的前端响应式框架

1,058 阅读3分钟

简介

一个轻量级、无虚拟DOM、无编译、噪音少、高性能的前端响应式框架,提供接近原生的开发体验,比Vue更快,比petite-vue更小,brotli压缩后仅3KB

GitHub:cydon 欢迎 Star

文档:0-v-0.github.io/cydon/

原本应该称为库的,因为大家习惯了框架的称呼所以本文称框架

背景

2023年了,感觉原生JavaScript缺的最重要的特性就是响应式。

写这个框架的原因无非就是对现有框架不满意,一是没一个好用的接近原生的开发体验的框架(很多写惯了框架把原生js忘掉的);二是觉得虚拟DOM带来的好处与它所增加的复杂度相比不值一提。

Vue提供了很多原生已经有的功能,比如插槽和样式隔离,在原生Web Components中也有,不过和Vue的有一些不同。再加上虚拟DOM和历史包袱,Vue就有点重量级了。petite-vue虽然轻量,但性能太低。

Svelte需要编译,mustache语法混在HTML里也很奇怪。

React学习曲线陡峭,Solid、Stencil等JSX系的语法不习惯,HTML和JS混在一起感觉丑,尤其是嵌套深的情况。

剩下的吐槽点这篇文章总结的很到位。

与JSX类似,我也做了EMT这个语法糖,不过EMT是转换成HTML的,EMT和Cydon之间是相互独立的。

Cydon这个名字是乱起的,没有什么意义,只是为了防止重名。

核心理念

Cydon的核心理念就是在噪音少的前提下尽可能接近原生,所以无虚拟DOM,无需编译。Cydon没有自己的组件系统,而是沿用Web Components的组件系统,组件就是DOM元素。Cydon采用模板语法,所见即所得,保证了性能,牺牲了JSX那样的灵活性,过多的灵活性反而会增加复杂度。

实现

响应式部分使用Proxy实现,不会像Lit那样在需要响应式的地方插入注释作为锚点,也不会生成一堆像data-v-xxxxx的属性,在开发工具里基本看不到噪音。

因为HTML和JS是分离的,浏览器解析完DOM后再执行module js(type="module"引入的js),进而实现响应式,这个过程类似注水化操作,因为解析完DOM时已经能显示部分元素了,所以白屏时间通常较短。而其他框架往往等到module js执行时再解析DOM,导致白屏时间更长。

因为无编译,所以运行时生成代码不可避免(js-framework-benchmark 1139标记),在有CSP的页面可能无法在事件/属性绑定中使用内联js

c-for渲染的元素的事件绑定默认进行事件代理,算是在默认全部事件代理和不提供事件代理之间的折中方案

目前还未实现key机制,实现了就可以去js-framework-benchmark的keyed区卷了

路由暂未实现,因为Cydon最初是用于MPA的

示例

选项式API示例:

<div class="card">$msg</div>
<div class="$css_class">$msg</div>
<script type="module">
    import { Cydon } from 'cydon'

    const app = new Cydon({
        msg: 'Hello world',
        css_class: 'bold red'
    })
    app.mount(document.body)
    const { data } = app
    data.msg = 'foo'
    data.css_class = 'bar'
</script>

Class Component API示例:

<my-counter value="1">
    <template shadowroot="open">
        <style>
            button {
                padding: 0.3em;
            }
            .wrapper {
                display: flex;
                align-items: center;
            }
        </style>
        <div class="wrapper">
            $value
            <button @click="value++">+1</button>
            <button @click="value--">-1</button>
        </div>
    </template>
    <script type="module">
        import { CydonElement } from 'cydon'

        class MyCounter extends CydonElement {
            value = +this.getAttribute('value')
        }
        customElements.define('my-counter', MyCounter)
    </script>
</my-counter>

一个复杂点的例子:Todo MVC

跑分结果

krausest.github.io/js-framewor…

注意有两个vanillajs是原生实现,不算框架,只作参考,不参与排名

速度方面

nonkeyed-benchmark.png

Non-keyed框架共61个,Cydon排在第20位,Vue排第24位,综合性能比Vue略好

其中部分更新和交换行这块有点落后。因为更新DOM时会比较整个列表,并未做到真正意义上的部分更新。

其他方面

nonkeyed-benchmark-2.png Cydon在加载时间、总大小、内存占用方面表现非常好

按内存占用排名,Cydon能排第7位 nonkeyed-benchmark-3.png

指令概览

  1. 内置指令
    • 一般指令:c-ifc-showc-modelrefc-forc-tpc-cloak
    • 事件绑定:@event、@$event
    • 特殊指令::、:attr、$attr、.prop
  2. 自定义指令
    • 全局指令
    • 局部指令

兼容性

  • Chrome 86+(Declarative Shadow DOM需要90+)
  • Firefox 86+
  • Safari需要requestIdleCallback polyfill