2021 年 11 月 左右,Strve 第一个版本发布,社区的反馈大部分是支持,也有少部分小伙伴提出了质疑,这都正常。你只要在社区发布一些作品,就必须接受其他人对你的作品的评价。
当初,开发 Strve 的初衷只是受到 JSX 语法的影响,觉得在 JavaScript 中编写 HTML 很酷,所以想能不能自己也开发一个前端框架。我最初给自己的目标就是能在 JavaScript 中写 HTML,然后通过编写 JavaScript 来改变页面状态。经过两个多周的调研,发现自己在原地打转。JSX 语法不能直接在浏览器运行,你必须通过类似 Babel 这种编译工具编译才能运行。所以,我就打算在模版字段串内编写 HTML。但是,又遇到了另一个问题,如何精确更新 DOM。当时,第一个想法就是利用虚拟 DOM,所以就抽时间来学习前端框架中都是怎么利用虚拟 DOM 来精确更新。不懂得就去网上查资料,然后就记下来。可能有些小伙伴就会问,是什么力量让你去做可能完成了也没什么意义的事情。怎么说呢!当时,我就是想要做出点东西来,觉得自己能做出来。至于做出来有什么意义,我没有多想。有时候我们做一件事情之前,考虑很多未必是好事,这往往会阻碍了我们做事情的脚步。
就这样,靠着自己的热情,第一个版本就这么问世了。
有很多小伙伴感觉 Strve 写法很像lit-html。说实话,开发 Strve 之前,我并不知道这个框架。后来看社区反馈,才到网上查看了有关的资料。虽然写法像,但并不是它的复制品。就比如 Strve 内部使用的是虚拟 DOM,这时的虚拟 DOM 还只是一个初版,只能说是简单的比对。
全新的认识
之前有看过我的文章的小伙伴,多多少少会知道 Strve。但是可能也只是停留在知道这个层面上,现在我通过几点来重新介绍下 Strve。
Strve 是一个易用、快速、灵活且轻量级的 JavaScript 库,用于构建用户界面。基于 tagged template 的 HTML 模板引擎,利用 ES6 的模板字符串来进行模板的书写,利用浏览器的原生的能力进行模板渲染。
- 更容易上手:只要你对 HTML、CSS 和 JavaScript 已经基本熟悉,就可以直接上手。
- 声明式渲染:我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系,开发者可以更加专注于业务逻辑的开发,不需要过多地关心 DOM 操作的细节。
- 顺滑的用户体验:模板字符串来进行模板的书写,在一些场景中代码智能提示、代码格式化方面不是特别友好。所以,我们提供了新的一种编码方式,我们可以使用 JSX 语法编写 Strve,提升用户开发体验。
- 性能出色:采用了虚拟 DOM 的模式,虚拟 DOM 使用 diff 算法的方法来计算出真正需要更新的节点,最大限度地减少了 DOM 操作以及 DOM 操作带来的排版与重绘损耗,从而显著提高了性能。另外,我们的 JavaScript 库在全球知名的测评榜单(js-framework-benchmark)上赢得了优秀的成绩。
- 组件化:一个函数就是一个组件,可以根据应用规模任意组合。并且组件特有的 “孤岛特性”,使得将虚拟 DOM 树计算的级别控制在组件级别。
- 灵活的应用场景:有无构建工具都可以使用,并且可以适配到其他前端框架开发的应用项目中去。
- 轻量级:压缩后的文件大小不足 10k。另外,可以根据不同应用场景,选择不同类型的文件。
想要了解更多关于 Strve 的特性,可以到官方中文文档中去探索。
https://strvejs.gitee.io/strve-doc-zh。
贴心的优化
2023 年我陆续发布了几个大的版本,大部分时间都是在优化底层代码。包括虚拟 DOM 的算法优化、减轻使用 API 时 心智负担等等优化措施。
下面我们来展开看下。
如果你不打算使用构建打包工具开发项目,而是直接开发。推荐使用全局构建版本,通过 CDN 使用 Strve 时,不涉及“构建步骤”,这使得设置更加简单。
<script src="https://cdn.jsdelivr.net/npm/strve-js@6.6.6/dist/strve.full.prod.js"></script>
<script>
const { html, defineComponent } = Strve;
defineComponent(
{
mount: '#app',
},
({ setData }) => {
let count = 0;
function add() {
setData(() => {
count++;
});
}
return () => html`<h1 onClick=${add}>${count}</h1>`;
}
);
</script>
模板字符串来进行模板的书写,在一些场景中代码智能提示、代码格式化方面不是特别友好。所以,我们提供了一种新的编码方式,我们可以使用 JSX 语法编写 Strve 应用,这样就大大提升了用户开发体验。
defineComponent(() => {
const state = {
msg: 'Hello',
};
return () => h1>{state.msg}</h1>;
});
另外,我们底层采用了虚拟 DOM 的模式,虚拟 DOM 使用 diff 算法的方法来计算出真正需要更新的节点,最大限度地减少了 DOM 操作以及 DOM 操作带来的排版与重绘损耗,从而显著提高了性能。
Strve 应用程序是由 组件 组成的。一个组件是 UI(用户界面)的一部分,它拥有自己的逻辑和外观。组件可以小到一个按钮,也可以大到整个页面。
在 Strve 中,组件就是一个函数。
const MyComponent = defineComponent(({ setData }) => {
let count = 0;
function add() {
setData(() => {
count++;
});
}
return () => (
<div class='MyComponent'>
<p>{count}</p>
<button onClick={add}>MyComponent</button>
</div>
);
});
// 复用组件
defineComponent(
{
mount: '#app',
},
({ setData }) => {
let count = 0;
const add = () => {
setData(() => {
count++;
});
};
return () => (
<div class='App'>
<p>{count}</p>
<button onClick={add}>App</button>
<component $is={MyComponent} />
</div>
);
}
);
Strve 内部的渲染系统是基于虚拟 DOM 构建的,虚拟 DOM (Virtual DOM,简称 VDOM) 是一种编程概念,意为将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后利用 Diff 算法来比对新老数据,将真实的 DOM 与之保持同步。
如何虚拟 DOM 树过于庞大,使得 Diff 计算时间大于 16.6ms,那么就可能造成性能的卡顿。组件有一个特性就是 ”孤岛“。何为“孤岛”,孤岛就是在 Strve 应用中我们可以理解成一个独立的模块。将一个庞大的虚拟 DOM 树分解成很多独立的模块,这样 Diff 计算时间就会控制在组件级别,大大缩减了计算的时间,提高了性能。
从 API 层面,我们尽可能的贴合易用的特性,将核心 API 缩减为 2 个。分别为defineComponent和setData。框架是需要容易使用的,太多繁琐的设置或者操作很容易增重心智负担。
丰富的生态
开发框架单单只有一个核心库肯定是不够的,你还需要其他生态工具来加以辅助。
你要有一套用于快速构建项目的命令行工具,也就是说用户可以通过输入命令快速搭建项目。CreateStrveApp 是一套用于快速构建 Strve 项目的命令行工具。CreateStrveApp 是使用 Vite 构建的,这是一个新的前端构建工具,可以显着提升前端开发体验。它有几个模版可供选择:strve、strve-apps、strve-jsx、strve-jsx-apps。
我们开发项目需要跳转多个页面,那么就需要路由管理器。StrveRouter 是 Strve 的官方路由管理器。它与 Strve 的核心深度集成,轻松构建单页应用程序。
另外,我们介绍两款编译时工具。BabelPluginStrve是一款 babel 插件,将 HTML 模板字符串转化为 Virtual Dom。从之前的运行时转移到编译时,大幅度提高渲染性能。如果你想使用 JSX 语法,BabelPluginJsxToStrve这款插件是必不可少的,它是一款 babel 插件,将 JSX 转换为与 Strve 一起使用的标记模板。CreateStrveApp 项目脚手架工具已默认安装,选择 strve-jsx 或者 strve-jsx-apps 模版即可。我们使用 CreateStrveApp 搭建完 Strve 项目你会发现,同时安装了 BabelPluginStrve、BabelPluginJsxToStrve,这是因为我们需要使用 BabelPluginJsxToStrve 将 JSX 转换为标签模版,之后再使用 BabelPluginStrve 将标签模版转换为 Virtual DOM。
我们最后压轴的一款生态工具可以说是近期更新力度最大的了。它被称为独立运行的可响应性 Strve,由 @vue/reactivity和 strve-js 提供支持的自定义元素 JavaScript 库。
主要特性有以下几个:
- Web Components
- Hooks
- Reactivity API
- Props
- Emit
- Slot
- Styles
- Automatic registration component
- Virtual DOM
Strve 结合 Vue 组合式 API 形成了一款新的 JavaScript 库。只要你熟悉组合式 API,就可快速上手。
// MyComponent.jsx
import { ref, defineComponent, reactive } from 'strve-reactivity';
const MyComponent = defineComponent(() => {
const items = reactive([
{
id: 1,
tit: 'A',
},
{
id: 2,
tit: 'B',
},
]);
const count = ref(4);
const increase = () => {
items.unshift({
id: count.value++,
tit: 'C',
});
};
return () => (
<fragment>
<button onclick={increase}>increase</button>
<ul>
{items.map((item) => (
<li key={item.id}>
<span>{item.id}</span>
<span>-</span>
<span>{item.tit}</span>
</li>
))}
</ul>
</fragment>
);
});
出色的跑分
我们只是口头说 Strve 性能是非常不错的,没有真凭实据那是说不过去的。跑分方面,Strve 在 js-framework-benchmark 中的表现比 React 要好得多。
https://krausest.github.io/js-framework-benchmark/current.html。
巨人的肩膀
前端框架最近几年来层出不穷,出现这种情况好吗?我觉得是个好现象。大家都在为前端社区贡献自己的力量,使得前端的生态生机勃勃。
大家可能非常讨厌跟知名框架比较,可能有些人会说蹭热度。其实不妨换个角度想想,为什么会比较,是因为要更好。怎么样才能更好,那只能不断地优化。那么在这优化的过程中你不光是做出一个 JavaScript 库或者前端框架,更多的是你可以从中获得你在平时工作中得不到的东西。比如,对设计一款框架需要考虑哪些方面。作者在设计 API 时为什么会这么设计等等一些非工作业务上的事情。
我开发这款 JavaScript 库,我是另辟蹊径吗?我感觉并不是,我只是学习其他优秀框架中可以借鉴的思想,并按照自己想要的方式去展现它。只有站在巨人的肩膀上才能望得更远!