前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学不完的框架,🐔啄不完的米,SolidJS,你到底爱谁?😘

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

原创
作者头像
萌萌哒将军
发布2023-05-25 13:35:00
8730
发布2023-05-25 13:35:00
举报
文章被收录于专栏:前端框架前端框架

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

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

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

经过一番深入交流才知道,居然又是个前端框架。
“还有完没完了,一个接一个的框架啥时候是个头啊!”
不过本着给大家踩坑避雷的精神,我又秉烛夜读,通宵达旦研究了一番。
🚀模仿?超越?

💎写法

先上代码

代码语言:javascript
复制
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,对应的接口如下

代码语言:javascript
复制
// 定义变量
  const [count, setCount] = createSignal(0);

  // 缓存中间值
  const fib = createMemo(() => (count() * 2 + 10));
  
  // 执行副作用
  createEffect(() => { console.log("The count is now", count()); });

来看看createSignal的大致逻辑

代码语言:javascript
复制
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查看)

代码语言:javascript
复制
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
代码语言:javascript
复制
_$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方法如下

代码语言:javascript
复制
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别的用法以及响应式原理。

敬请期待!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 💎写法
  • 💎响应式原理
  • 💎模板编译原理
  • 💎运行时原理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档