学不完的框架,🐔啄不完的米,SolidJS,你到底爱谁?😘

简介: 学不完的框架,🐔啄不完的米,SolidJS,你到底爱谁?😘

最近刚刚整明白点Svelte感觉整个世界都清净了,但是昨天,有人给我介绍了SolidJS

上篇:Svelte原理和进阶看这篇就够了

当时我心想:这又是啥玩意啊!

经过一番深入交流才知道,居然又是个前端框架。

“还有完没完了,一个接一个的框架啥时候是个头啊!”

不过本着给大家踩坑避雷的精神,我又秉烛夜读,通宵达旦研究了一番。

🚀模仿?超越?

💎写法

先上代码

import { render } from "solid-js/web";
import { createSignal, createMemo, createEffect } from "solid-js";
function Counter() {
  // 定义变量
  const [count, setCount] = createSignal(0);
  // 缓存中间值
  const fib = createMemo(() => {
    console.log('Calculating Fibonacci');
    return (count() * 2 + 10);
  });
  // 执行副作用
  createEffect(() => { console.log("The count is now", count()); });
  return (
      <div onClick={() => setCount(() => count() + 1)}>
          Count: {count()}
          fib Count: {fib()}
      </div>
  );
}
render(() => <Counter />, document.getElementById('app'));

是不是很熟悉,这不就是React吗?

难道这是React被抄袭的最惨的一次吗?

是的,官网明确告诉你,它会让你感觉既熟悉又现代。

React类似的hook写法,一样的Jsx模板语法,熟悉吧?

不过,当你扒掉它的衣服(神秘的面纱),你会发现里面居然是你曾经的神——Vue

💎响应式原理

因为它的响应式官方称为primitive,是基于Proxy的发布订阅模式的API

primitive的响应式主要包括SignalMemoEffect,对应的接口如下

// 定义变量
  const [count, setCount] = createSignal(0);
  // 缓存中间值
  const fib = createMemo(() => (count() * 2 + 10));
  // 执行副作用
  createEffect(() => { console.log("The count is now", count()); });

来看看createSignal的大致逻辑

function createSignal(value) {
  const subscribers = new Set();
  const read = () => {
    const listener = getCurrentListener();
    if (listener) subscribers.add(listener);
    return value;
  };
  const write = nextValue => {
    value = nextValue;
    for (const sub of subscribers) sub.run();
  };
  return [read, write];
}

在每次read()的地方收集listener,做为订阅者,每次write()的时候作为发布者,通知每个listener更新数据。

SolidJS的发布订阅模式也是基于Proxy的。下篇文章会做详细的对比。

React不同的是,reead是个方法,这也是前面模板使用count(),而不是count的原因。

createMemocreateEffect会自动收集依赖项,每次触发依赖项listener的更新时,都会重新执行。

到这,是不是觉得,这太简单了吧,这不就是ReactVue的结合体嘛!

欢愉之后,你又想和它谈心,可当你走近它的心,又发现了你最近心心念念的Svelte的影子!

💎模板编译原理

上述例子的编译结果如下: (编译结果可以在官网的演练场Output查看)

import { template as _$template } from "solid-js/web";
import { delegateEvents as _$delegateEvents } from "solid-js/web";
import { createComponent as _$createComponent } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
const _tmpl$ = /*#__PURE__*/_$template(`<div>Count: <!>fib Count: </div>`, 3);
import { render } from "solid-js/web";
import { createSignal, createMemo, createEffect } from "solid-js";
function Counter() {
  // 定义变量
  const [count, setCount] = createSignal(0); // 缓存中间值
  const fib = createMemo(() => {
    console.log('Calculating Fibonacci');
    return count() * 2 + 10;
  }); // 执行副作用
  createEffect(() => {
    console.log("The count is now", count());
  });
  return (() => {
    const _el$ = _tmpl$.cloneNode(true),
          _el$2 = _el$.firstChild,
          _el$4 = _el$2.nextSibling,
          _el$3 = _el$4.nextSibling;
    _el$.$$click = () => setCount(() => count() + 1);
    _$insert(_el$, count, _el$4);
    _$insert(_el$, fib, null);
    return _el$;
  })();
}
render(() => _$createComponent(Counter, {}), document.getElementById('app'));
_$delegateEvents(["click"]);

简单分析之后可以得出结论如下:

  • 🚗首先,使用_$template创建纯静态的jsx模板,
  • 🚗接着,通过cloneNode方法,以及firstChild等属性获取动态元素,
  • 🚗紧接着,为每个元素绑定对应的方法
  • 🚗再接着,将动态的片段使用_$insert方法插入模板中,注意到countfib都是未执行的函数
  • 🚗接着使用$createComponent包裹组件。
  • 🚗最后组装render方法,将组件包装成函数,和根节点一起作为render方法的参数。

这和Svelte的编译结果有两个十分类似的地方:

  • 💎将每动态片段的更新范围,精确到了原子级别。
  • 💎它们的返回值都没有虚拟DOM
_$insert(_el$, count, _el$4);
_$insert(_el$, fib, null);
// Svelte编译之后create_fragment返回的p方法,也就是update方法
p(ctx, [dirty]) {
  if (dirty & /*count*/ 1) set_data(t1, /*count*/ ctx[0]);
},

💎运行时原理

在运行时阶段,会执行render方法,render方法如下

function render(code, element, init, options = {}) {
  let disposer;
  createRoot(dispose => {
    disposer = dispose;
    element === document
        ? code()
        : insert(
            element,
            code(),
            element.firstChild ? null : undefined,
            init
         );
  }, options.owner);
  return () => {
    disposer();
    element.textContent = "";
  };
}

代码都会将编译的() => _$createComponent(Counter, {})执行,并挂载到document.getElementById('app')

由于在编译阶段还没有建立变量的响应式机制,执行render方法后,才会通过发布订阅模式创建响应式变量,每次调用write()、或者触发事件时,导致变量更新,以及对应的元素节点使用_$insert更新DOM

看着SolidJS朴素的运行时原理,

你才回过神来,发现你曾经邂逅过的一切,它早已拥有,

你爱慕着的,也为你准备完毕,

最后你不禁感叹,SolidJS才是你那个:

『众里寻他千百度,慕然回首,那人却在,灯火阑珊处』

的框架啊!

你刚想抓住它,它却早已隐入了那灯影里!!!

好了好了,不做梦了,今天的分享就这些了,

下篇文章会介绍下SolidJS别的用法以及响应式原理。

敬请期待!

如果这篇文章对你有帮助,可以帮我点个赞。十分感谢!

相关文章
星际争霸之小霸王之小蜜蜂(十三)--接着奏乐接着舞
星际争霸之小霸王之小蜜蜂(十三)--接着奏乐接着舞
|
4月前
|
前端开发 开发者
【前端大揭秘】CSS盒子模型的爱恨情仇:一场关于标准与IE模型的精彩对决!
【8月更文挑战第26天】本文深入探讨CSS中的两大盒子模型——标准盒模型与IE盒模型。通过理论解析与实例代码,清晰展示了两种模型下元素尺寸的构成方式及其应用场景。标准盒模型适合精确控制内容区尺寸,而IE盒模型在处理固定宽度元素时更为直观。掌握这些知识将帮助前端开发者在实际项目中做出更优的设计决策,提升网页布局的质量与美观性。
38 1
|
4月前
|
JavaScript 前端开发 开发者
决战前端之巅!Element UI与Vuetify谁才是Vue.js组件界的霸主?一场关于颜值与实力的较量!
【8月更文挑战第30天】本文对比了两款热门的Vue.js组件库——Element UI与Vuetify。Element UI由饿了么团队打造,提供多种高质量UI组件,设计简洁大方。Vuetify基于Material Design规范,支持Vue.js 2.0及3.0版本,具备前瞻性。两者均涵盖表单、导航、数据展示等组件,Element UI配置选项丰富,而Vuetify则提供了更深层的样式定制功能。开发者可根据项目需求及个人偏好选择合适的组件库。
347 0
|
7月前
|
前端开发 JavaScript 数据可视化
元宇宙基础案例 | 大帅老猿threejs特训
元宇宙基础案例 | 大帅老猿threejs特训
|
IDE 定位技术 开发工具
如何用three.js实现我的太空遐想3D网页
如何用three.js实现我的太空遐想3D网页
368 0
如何用three.js实现我的太空遐想3D网页
星际争霸之小霸王之小蜜蜂(三)--重构模块
星际争霸之小霸王之小蜜蜂(三)--重构模块
|
JavaScript 前端开发 Go
【SolidJs】仅次于原生JS的超级性能!SolidJs框架教程【上】(1)
背景 今天被战友种草了一款前端框架,打开链接看文章,在各个指标的比较下,SolidJs脱颖而出,下面简单介绍一下这个框架,然后开始记录一下学习笔记。(Golang的事情暂时放一放,毕竟咱是专业前端「手动狗头」)。
481 0
|
JavaScript API
【SolidJs】仅次于原生JS的超级性能!SolidJs框架教程【中】
Children 在SolidJs中,如果在组件中写一些children,并非仅仅作为展示,而需要在子组件中处理children时,需要用到children函数。
378 0
|
前端开发 JavaScript 编译器
【SolidJs】仅次于原生JS的超级性能!SolidJs框架教程【上】(2)
背景 今天被战友种草了一款前端框架,打开链接看文章,在各个指标的比较下,SolidJs脱颖而出,下面简单介绍一下这个框架,然后开始记录一下学习笔记。(Golang的事情暂时放一放,毕竟咱是专业前端「手动狗头」)。
488 0
|
小程序 JavaScript 前端开发
请收下这份源码,用Vue开发的一个“蚂蚁森林浇水偷菜”游戏
本文中的小程序开发于2019年8月,在一个月的时间里,我独自完成了策划、设计及前后端开发,本文中所涉及的源码、资源的版权均为本人所有
1672 0
请收下这份源码,用Vue开发的一个“蚂蚁森林浇水偷菜”游戏

热门文章

最新文章