学不完的框架,🐔啄不完的米,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别的用法以及响应式原理。

敬请期待!

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

相关文章
|
8月前
|
前端开发
前端小项目:旋转太极
前端小项目:旋转太极
33 0
|
8月前
|
JavaScript 前端开发 索引
用Three.js搞个炫酷3D地球
地球人怎么可以不会画地球!从canvas画地球贴图开始,用Three.js手把手教你实现一个炫酷的3D地球!
用Three.js搞个炫酷3D地球
|
4月前
|
人工智能 JavaScript 前端开发
泰裤辣,可以利用AI测测Vue知识的掌握程度。
泰裤辣,可以利用AI测测Vue知识的掌握程度。
|
5月前
|
前端开发 JavaScript 数据可视化
元宇宙基础案例 | 大帅老猿threejs特训
元宇宙基础案例 | 大帅老猿threejs特训
|
5月前
|
前端开发
前端必学——实现电商图片放大镜效果(附代码)
放大镜可以说是前端人必须学会的程序之一,今天的案例为大家展示一下怎么实现放大镜的效果! 效果图展示   整个效果就是当鼠标放到展示图上的时候,会出现一个遮罩层以及弹出来一个框展示一个详情图,并且鼠标移动的时候详情图跟着移动,鼠标离开详情图消失。
|
IDE 定位技术 开发工具
如何用three.js实现我的太空遐想3D网页
如何用three.js实现我的太空遐想3D网页
如何用three.js实现我的太空遐想3D网页
|
前端开发
【我的前端】玻璃拟态效果实战开发:比毛玻璃更好看的CSS背景玻璃拟态效果
玻璃拟态是目前市面上的新风格,越来越受欢迎,新拟态 (Neumorphism) 模仿受到挤压的塑料材质,这种新的视觉风格更加注重垂直空间z轴的使用。它的典型特征是:
【我的前端】玻璃拟态效果实战开发:比毛玻璃更好看的CSS背景玻璃拟态效果
|
存储 图形学
Unity实战之王者荣耀段位计算
Unity实现王者荣耀段位计算
Unity实战之王者荣耀段位计算
|
JavaScript 容器
冇事来学系--Vue2.0中VueComponent(组件)
组件的本质 组件的本质就是一个 构造函数 f VueComponent (options) { this._init(options); } ,是Vue.extend( )生成的 当我们使用组件时,写了组件的标签,Vue解析时就会创建该组件的实例对象,即Vue帮我们执行了这一句代码 new VueComponent(options) (options就是我们写的data、methods、computed等数据) 每次调用Vue.extend( )时,返回的都是一个全新的VueComponent
269 0