初探SolidJS 您所在的位置:网站首页 SolidJS 初探SolidJS

初探SolidJS

2023-11-23 21:08| 来源: 网络整理| 查看: 265

介绍 什么是Solid?

www.solidjs.com/tutorial/in…

Solid:用于构建用户界面的声明式、高效且灵活的 JavaScript 库

支持现代前端特性,例如:JSX, Fragments, Context, Portals, Suspense, Streaming SSR, Progressive Hydration, Error Boundaries和Concurrent Rendering

好家伙,看这个特性,不知道的还以为是React的亲儿子,这不能说和React类似,只能说完全一样吧?

Solid 使用了和 React 相似的语法和类似 Svelte 的预编译 Solid 使用上类似于 React,使用 JSX 语法,但不同于 React, 组件只会初始化一次,并不是 state 改变就重新运行渲染整个组件,这类似于 Vue3 的 setup

为什么选择Solid呢?

Solid 官网 给出了以下理由

高性能 - 始终在公认的 UI 速度和内存利用率基准测试中名列前茅

强大 - 可组合的反应式原语与 JSX 的灵活性相结合

务实 - 合理且量身定制的 API 使开发变得有趣而简单

生产力 - 人体工程学和熟悉程度使构建简单或复杂的东西变得轻而易举

基本使用 Hello World

我们借助solidjs/templates/js 来搭建整个项目

初始化后,整个代码结构就是这样

// index.jsx import { render } from "solid-js/web"; function App() { return Hello,World; } render(() => , document.getElementById("root"));

是不是看起来非常熟悉,就和 React 一样,非常舒服

solidjs的jsx与React的jsx有一点最明显的不同,solidjs默认而且以后也不会使用类组件,所以不存在class关键字的冲突,solidjs类选择器为class而不是react的className

Props // index.jsx import { render } from "solid-js/web"; import Component1 from "./Component1.jsx"; function App() { return ( Solid My App children ); } render(() => , document.querySelector("body")); // Component1.jsx export default function Component1(props) { /** 错误写法 const {text , children} = props; return ( {text} {children} ) */ return ( {props.text} {props.children} ) }

与其他框架不同,你不能在组件的 props 上使用对象解构。这是因为 props 对象在底层依赖对象的 getter 来惰性获取值。使用对象解构破坏了 props 的响应性

虽然 props 对象在使用时看起来像一个普通对象,但实际上它是响应式的--使用Proxy实现响应式,解构会使值失去Proxy特性,可以使用官方提供的API——splitProps来进行解构。如果对Proxy有兴趣的话,欢迎大家对我写的文章进行指导——ahooks源码学习之useReactive ,其中涉及了一些Proxy相关的内容(夹带私货(o^^o))

Signal

Signal是最核心的响应式基本要素, 要创建 Signal,你可以从 solid-js 导入 createSignal

传递给 createSignal 调用的参数是初始值,createSignal 返回一个带有 2 个函数的数组,一个是 getter,一个是 setter

第一个返回值是一个 getter 而不是值本身,需要通过函数调用访问值

/* @refresh reload */ import { createSignal } from "solid-js"; import { render } from "solid-js/web";

function App() { const [count, setCount] = createSignal(0); const a=()=>count()*2; a(); return (

Count: {count()} setCount((c) => c + 1)}>add setCount(count() - 1)}>minus ); }

render(() => , document.getElementById("root"));

这里的setter和React的useState的第二个参数一样都可以有两种形式,但是这两种写法的表现是一致的。

Effect

effect为观察者,通过createEffect创建,接收一个函数,并监视其执行情况

createEffect 会自动订阅在执行期间读取的所有 Signal,并在这些 Signal 值之一发生变化时重新运行该函数,一般用于副作用,在状态改变的时候运行副作用。

createEffect 创建的 Effect 会在渲染完成后运行,主要用于调度渲染后与 DOM 交互的更新 它类似于 React 中的 useEffect 但其自动收集依赖,无需显式声明依赖,这和 Vue 中的 watchEffect 作用相同

import { createSignal, createEffect } from 'solid-js' import { render } from 'solid-js/web'; export default function App() { const [count, setCount] = createSignal(0) // 依赖于 signal `count` 的 effect createEffect(() => { console.log('The count is now', count()) }) return ( {count()} setCount(c => c + 1)}>add ) } render(() => , document.getElementById('app'))

effect 函数可以拿到上次执行返回的值。可以在第二个可选参数设置该值的初始化值。这可以让我们不用创建额外闭包的情况下就可以进行差异对比。

React hooks获取上一轮的state和props——juejin.cn/post/697200…

createEffect((prev) => { const sum = a() + b(); if (sum !== prev) console.log(sum); return sum; }, 0); Memo

我们可以使用 Memo缓存值以减少重复工作

Memo(一种特殊的 primitive)来存储和访问最后一个缓存的值,而无需重新进行求值,直到它们的依赖关系发生变化才重新求值,和 effect 是一样的,会自动监听期间使用的 state

createMemo 用于生成只读的派生值,类似于 Vue 中的 computed,与上面的相同,也需要通过调用来获取值

举个例子

未使用memo的情况 import { render } from 'solid-js/web' import { createSignal } from 'solid-js' function fibonacci(num) { if (num { console.log('值发生了改变') return fibonacci(count()) } return ( setCount(count() + 1)}>Count: {count()} {fib()} {fib()} {fib()} {fib()} {fib()} ) } render(() => , document.getElementById('app'))

可以看到每一次调用fib,都会重新执行一次

使用了memo /* @refresh reload */ import { createSignal, createMemo } from "solid-js"; import { render } from "solid-js/web"; function fibonacci(num) { if (num { console.log("值发生了改变"); return fibonacci(count()); }); return ( setCount(count() + 1)}>Count: {count()} {fib()} {fib()} {fib()} {fib()} {fib()} ); } render(() => , document.getElementById("root"));

memo处理之后,每一次调用fib,只要值没有变化不会重新执行

Batch

Solid 的反应性是同步的,这意味着在任何更改后的下一行 DOM 都会更新。“渲染”两次无关的更改实际上并不意味着浪费工作。 如果更改是相关的怎么办?Solid 的batch助手允许将多个更改排队,然后在通知观察者之前同时应用它们。在批处理中更新的信号值直到完成才会提交。

/* @refresh reload */ import { createSignal } from "solid-js"; import { render } from "solid-js/web"; const App = () => { const [firstName, setFirstName] = createSignal("Lin"); const [lastName, setLastName] = createSignal("xiaohui"); const fullName = () => { console.log(`${firstName()} ${lastName()}`); return `${firstName()} ${lastName()}`; }; const updateNames = () => { console.log("Button Clicked"); setFirstName(firstName() + "n"); setLastName(lastName() + "!"); }; return My name is {fullName()}; }; render(() => , document.getElementById("root"));

在这个例子中,我们在按钮点击时更新了两个状态,它触发了两次更新,可以在控制台中看到日志

让我们加上batch试一下

/* @refresh reload */ import { createSignal, batch } from "solid-js"; import { render } from "solid-js/web"; const App = () => { const [firstName, setFirstName] = createSignal("Lin"); const [lastName, setLastName] = createSignal("xiaohui"); const fullName = () => { console.log(`${firstName()} ${lastName()}`); return `${firstName()} ${lastName()}`; }; const updateNames = () => { console.log("Button Clicked"); batch(() => { setFirstName(firstName() + "n"); setLastName(lastName() + "!"); }); }; return My name is {fullName()}; }; render(() => , document.getElementById("root"));

现在,对于同一个元素只会触发一次更新

生命周期

Solid 中只有少量的生命周期,因为一切的存活销毁都由响应系统控制。响应系统是同步创建和更新的,因此唯一的调度就是将逻辑写到更新结束的 Effect 中

onMount

onMount是createEffect的一个非跟踪(从不重新运行)状态的别名

只是一个 Effect 调用,一旦所有初始渲染完成,它只会在组件中运行一次

可以拿来做一些网络的请求等初始化

/* @refresh reload */ import { createSignal, onMount } from "solid-js"; import { render } from "solid-js/web"; const App = () => { const [photos, setPhotos] = createSignal([]); onMount(async () => { const res = await fetch( `https://jsonplaceholder.typicode.com/photos?_limit=20` ); setPhotos(await res.json()); }); return ( Photo album {(photo) => } ); }; render(() => , document.getElementById("root")) onCleanup

会在当前响应范围内执行销毁或重新计算时候被触发。可用于任何组件或 Effect

import { render } from "solid-js/web" import { createSignal, onCleanup } from "solid-js" function Counter() { const [count, setCount] = createSignal(0) const timer = setInterval(() => setCount(count() + 1), 1000) onCleanup(() => clearInterval(timer)) return Count: {count()} } render(() => , document.getElementById('app')) 控制流(Control Flow)

控制流大多可以用 JSX 实现相同功能,但是使用其则具有高于 JSX 的性能,Solid 可以对其进行更多优化 fallback 是在失败后的显示

For

简单的引用键控循环控制流程。

export default function Container() { return ( {(item) => {item}} ) } 复制代码

For的源码看起来很短,其中用到了mapArray,mapArray也是solid暴露出来的一个API,mapArray是一个响应式映射工具函数,通过引用缓存每个子项,以减少不必要的映射更新。它只为每个值运行一次映射函数,然后根据需要移动或删除它。mapArray的源码比较复杂,先贴在这里,尚未研究github.com/solidjs/sol…

export function For(props: { each: readonly T[] | undefined | null | false; fallback?: JSX.Element; children: (item: T, index: Accessor) => U; }) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo( mapArray(() => props.each, props.children, fallback ? fallback : undefined) ); } Show

Show 控制流用于有条件地渲染视图的一部分。它类似于三元运算符 (a ? b : c),但非常适合模板化 JSX。

import { createSignal } from "solid-js"; export default function Container() { const [count, setCount] = createSignal(10); return ( {/* 在 count 大于 5 的时候渲染*/} 5} fallback={Failed} > content ) } 复制代码 Switch

Switch 在有 2 个以上的互斥条件时很有用。可以用来做一些简单的路由之类的事情。

import { createSignal } from "solid-js"; export default function Container() { const [count, setCount] = createSignal(10); return ( 5}> count > 5count < 5 ) } 复制代码 Index

非索引迭代循环控制流程,如果要迭代的不是数组,而是类似对象这类,使用 Index

export default function Container() { return ( {(item) => {item}} ) } 复制代码 ErrorBoundary

错误边界

function ErrorComponent() { // 抛出错误throw new Error("component error"); return ( content ) } export default function Container() { return ( ) } 复制代码 Portal

和 React Portal 作用相同 用于将元素渲染到组件之外的地方,这对于模态窗,信息提示等是刚需 示例:将元素直接渲染到 body 下

export default function Container() { return ( content ) } Store store/嵌套反应

Solid 中细粒度反应性的原因之一是它可以独立处理嵌套更新。你可以有一个用户列表,当我们更新一个名字时,我们只更新 DOM 中的一个位置,而不会对列表本身进行差异化。很少(甚至是反应式)UI 框架可以做到这一点。

createStore

用于创建一个 store,store 可用于精确地嵌套反应 此函数将创建一个信号树作为代理,允许独立跟踪嵌套数据结构中的各个值。create 函数返回一个只读代理对象和一个 setter 函数

前面所说的 For 标签在这里会很有用,因为直接使用 JSX 则会直接刷新整个表达式,从而无法细粒度更新

先看一个没有使用 store 的例子 这里使用一些示例数据并使用 For 标签迭代渲染 点击复选框可以切换其选择状态,这里先简单的映射原始数据生成新数据在 set 过去 复制代码运行,并尝试点击复选框,查看控制台输出

import { render } from "solid-js/web"; import { For, createSignal } from "solid-js"; const App = () => { const [state, setState] = createSignal( { // 初始化一个具有 id, text, completed 属性的对象组成的数组 todos: [ {id: 1, text: 1, completed: false}, {id: 2, text: 2, completed: false}, {id: 3, text: 3, completed: false}, {id: 4, text: 4, completed: false} ] } ); // 修改点击的复选框的选择状态 const toggleTodo = (id) => { setState({ todos: state().todos.map((todo) => ( todo.id !== id ? todo : { ...todo, completed: !todo.completed } )) }); } return ( {(todo) => { const { id, text } = todo; console.log(`Creating ${text}`) return {text} }} ); }; render(App, document.getElementById("app"));

会发现控制台随点击每次输出,这是因为每次都销毁重建了元素,我们只是修改了一个属性,却要重建元素,这是一种浪费 如果我们使用 store 可以有更精确的反应,而不需要重建元素,只会在原有的位置更新 把上面的代码修改为以下代码:

import { render } from "solid-js/web"; import { For } from "solid-js"; import { createStore } from "solid-js/store"; const App = () => { const [store, setStore] = createStore( { // 初始化一个具有 id, text, completed 属性的对象组成的数组 todos: [ {id: 1, text: 1, completed: false}, {id: 2, text: 2, completed: false}, {id: 3, text: 3, completed: false}, {id: 4, text: 4, completed: false} ] } ); // 修改点击的复选框的选择状态 const toggleTodo = (id) => { setStore( "todos", (t) => t.id === id, 'completed', (completed) => !completed ); }; return ( {(todo) => { const { id, text } = todo; console.log(`Creating ${text}`) return {text} }} ); }; render(App, document.getElementById("app"));

再次运行并且点击,会发现,元素不再被销毁重建。

仔细对比上面两个例子,我们会发现主要修改与区别如下 createSignal 修改为了 createStore 由于 createStore 直接返回只读代理,而不是 Getter,所以无需调用,直接使用 signal 设置值只是简单的遍历原始数据,改变并产生新数据,在大多数应用中都是如此,但 Solid 对于这种情况有一定的优化策略

设置 store 的值可以像类似 react setState 一样,让对象浅合并

但是此处我们使用了 solid 所支持的另外一种方式,这可以让 solid 知晓我们详细变化了哪些东西,从而细粒度地更新

关于Store的还有很多没提到——贴个官网的链接在这里

www.solidjs.com/docs/latest…

无虚拟DOM与虚拟DOM 虚拟DOM Diff

Virtual DOM 是一个表示页面如何展示的对象,也是一个快照。

每次数据更新触发 Virtual DOM 更新的操作 -

创建一个新的 Virtual DOM

新 Virtual DOM 和旧 Virtual DOM 进行对比

找出所有更新内容

更新实体 DOM

Virtual DOM 所做的工作是 1,2,3 + 4, 其中4本身是对实体 DOM 的操作。说 Virtual DOM 快意思是相对于重新构建实体 DOM 要快。

onChildStateChange(() => { parentComponent.innerHTML = renderChildComponent(); });

Virtual DOM “不够快” - 它需要进行 1,2,3 步骤的 "overhead" 计算,对两个快照进行 diff 也有一定的计算量。

传统框架如 React 对 app 内声明的变量没有任何概念,将所有 app 变量视为一个黑盒

useCallback shouldComponentUpdate // React 内部提供的绕过 vdom 更新的函数 No DOM Diff

No Dom Diff 是说 SolidJS在更新粒度方面,摒弃了虚拟dom,采用节点级更新。

说到更新粒度,可以先总结下目前前端主流的几种方案:

应用级更新:状态更新会引起整个应用render,具体渲染哪些内容取决于协调的结果。代表作有 React

组件级更新:状态更新时只会引起绑定了该状态的组件渲染,具体渲染哪些内容同样取决于协调的结果。代表作有vue2.x

节点级更新:状态更新时直接触发绑定该状态的节点更新,也就是指向型更新。代表作有vue1.x、Svelte、SolidJS

SolidJS的更新流程像一个画家,画面中哪儿需要更新就往哪儿画几笔

React的更新流程像是一个人拿相机拍一张照片,再拿这张照片和上次拍的照片找不同,最后把不同的地方更新了。

与React的区别

贴一个可以看到实时编译产物的Playground

playground.solidjs.com/

render执行不同

SolidJS 仅支持 FunctionComponent 写法,无论内容是否拥有状态管理,也无论该组件是否接受来自父组件的 Props 透传,都仅触发一次渲染函数。

所以其状态更新机制与 React 存在根本的不同:

React 状态变化后,通过重新执行 Render 函数体响应状态的变化。

Solid 状态变化后,通过重新执行用到该状态代码块响应状态的变化。

与 React 整个渲染函数重新执行相对比,Solid 状态响应粒度非常细,甚至一段 JSX 内调用多个变量,都不会重新执行整段 JSX 逻辑,而是仅更新变量部分:

import { render } from "solid-js/web"; import { createSignal } from "solid-js"; function Counter() { const [var1, setVar1] = createSignal(0); const [var2, setVar2]=createSignal(0); return ( setVar1(var1()+1)}>setVar1 var1: {console.log("var1", var1())} var2: {console.log("var2", var2())} ); } render(() => , document.getElementById("app")!);

初始化代码的时候,打印结果如下:

我们不清空控制台,然后我们点击setVar1按钮,我点击了两次,控制台只打印了var1的值

上面这段代码在 var1 单独变化时,仅打印 var1,而不会打印 var2,在 React 里是不可能做到的。

React 的一个核心思想是数据驱动视图, 即UI = f(state),但是实际上没有办法做到那么细粒度的操作。Solid渲染函数仅执行一次,也顺便衍生出变量更新粒度如此之细的结果,同时也是其高性能的基础。

我们用两段代码对比一下:

// React import ReactDOM from "react-dom"; import { useState } from "react"; const App = () => { console.log("hello"); const [var1, setVar1] = useState(0); const [var2, setVar2] = useState(0); return ( setVar1(var1 + 1)}>setVar1 var1: {console.log("var1", var1)} var2: {console.log("var2", var2)} ); }; ReactDOM.render(, document.querySelector("#root")); import { render } from "solid-js/web"; import { createSignal } from "solid-js"; function Counter() { console.log("hello"); const [var1, setVar1] = createSignal(0); const [var2, setVar2]=createSignal(0); return ( setVar1(var1()+1)}>setVar1 var1: {console.log("var1", var1())} var2: {console.log("var2", var2())} ); } render(() => , document.getElementById("app")!);

在React中,我们每点击一次按钮,就会导致我们的状态改变,然后re-render,会打印多一次hello,但是在solid中,我们点击一次按钮,是没有打印hello的,这也验证了solid的render函数只执行一次的结论。

hook的区别

SolidJS 用 createSignal 实现类似 React useState 的能力,但是这两者之间可以说毫无关系,主要源于SolidJS 的核心理念:面向状态驱动而不是面向视图驱动

为什么这么说呢?举个例子

const [count, setCount] = createSignal(0); const App = () => { return setCount(count() + 1)}>{count()}; };

SolidJS 理解的数据驱动是纯粹的数据驱动视图,无论数据在哪定义,视图在哪,都可以建立绑定;

这个设计自然也不依赖渲染函数执行多次,同时因为使用了依赖收集,也不需要手动申明 deps 数组,也完全可以将 createSignal 写在条件分支之后,因为不存在执行顺序的概念。

React 将状态存储在一个绑定在我们的组件上的隐藏数据存储器中,最新的状态值被保留下来并在重新渲染时返回。如果该组件被卸载然后重新挂载,一切就会从头开始。React Hooks - 组件重新渲染原理

React Hooks 使用 deps 收集依赖,在下次执行渲染函数体时,因为没有任何办法标识 “deps 是为哪个 Hook 申明的”,只能依靠顺序作为标识依据,所以需要稳定的顺序,因此不能出现条件分支在前面。

而 SolidJS 本身渲染函数仅执行一次,所以不存在 React 重新执行函数体的场景,而 createSignal 本身又只是创建一个变量,createEffect 也只是创建一个监听,逻辑都在回调函数内部处理,而与视图的绑定通过依赖收集完成,所以也不受条件分支的影响。

状态监听

对标 React 的 useEffect,SolidJS 提供的是 createEffect,但相比之下,不用写 deps,是真的监听数据,而非组件生命周期的一环:

const App = () => { const [count, setCount] = createSignal(0); createEffect(() => { console.log(count()); // 在 count 变化时重新执行 }); };

多香,都不存在闭包陷阱这种东西

在 SolidJS,生命周期函数有 onMount、onCleanUp,状态监听函数有 createEffect;而 React 的所有生命周期和状态监听函数都是 useEffect,虽然看上去更简洁,但即便是精通 React Hooks 的老手也不容易判断哪些是监听,哪些是生命周期。

模版编译

我们知道React编译时,会把JSX 会编译为 React.createElement 调用形式——React编译原理

React JSX

Click Me

React编译后

React.createElement( MyButton,{color: 'blue', shadowSize: 2},'Click Me' )

Solid JSX

function Count() { const [count, setCount] = createSignal(0); const increment = () => setCount(count() + 1); return ( {count()} ); }

Solid编译后

const _tmpl$ = /*#__PURE__*/ template(``, 2); function Count() { const [count, setCount] = createSignal(0); const increment = () => setCount(count() + 1); return (() => { const _el$ = _tmpl$.cloneNode(true); _el$.$$click = increment; insert(_el$, count); return _el$; })(); } render(() => createComponent(Count, {}), document.getElementById("app")); //这行代码的目的是在document上注册click事件代理。 delegateEvents(["click"]);

如何更新****dom,具体的做法是:在编译阶段提前生成类似 insert、update、delete的dom操作方法,将来更新时直接调用。

首先把组件 JSX 部分提取到了全局模板。初始化逻辑:将变量插入模板;更新状态逻辑:由于 insert(_el$, count) 时已经将 count 与 _el$ 绑定了,下次调用 setCount() 时,只需要把绑定的 _el$ 更新一下就行了,而不用关心它在哪个位置。

这里的 jsx 被编译成了 createComponent,这个函数位于 component.ts 文件中,除去 ssr 的逻辑,有效代码是这么多:

export function createComponent(Comp: Component, props: T): JSX.Element { if ("_SOLID_DEV_") return devComponent(Comp, props || ({} as T)); return untrack(() => Comp(props || ({} as T))); }

无论是否为 dev 模式,我们传入的 Comp 都是作为一个函数来执行的,其中 props 会以参数形式传入。因此在这个过程中,我们只需要把组件逻辑理解为一个函数本身即可。

我们可以看到,在编译后的版本中,createSignal 还保持原状,这个是 solid 运行时的逻辑,用于实现 solid 的响应式更新能力。此外 dom 的创建,事件绑定都被转化为一些内部函数调用,这些函数都是比较基础的,下面直接对应了原生的 dom 操作,也就是说在这里调用的已经是对原生 dom 的操作代码了,没有虚拟 dom 层,一切都非常直接。

附上React的深入JSX和一个专研Solid源码的掘金博主

zh-hans.reactjs.org/docs/jsx-in…

juejin.cn/user/729731…

性能对比

看看 js-framework-benchmark 跑分结果,直接上图

除了原生 js,在所有的参与对比的 UI 库中,solid.js 的速度是最快的,性能方面如此强悍。

我们看看为啥那么快

举个例子

import { render } from "solid-js/web"; import { createSignal } from "solid-js"; function Counter() { const [count, setCount] = createSignal(0); const increment = () => setCount(count() + 1); return ( {count()} ); } render(() => , document.getElementById("app"));

我们知道,在React与Vue中存在一层虚拟****DOM(React中叫Fiber树)。

每当发生更新,虚拟****DOM会进行比较(Diff算法),比较的结果会执行不同的DOM操作(增、删、改)。

而SolidJS与Svelte在发生更新时,可以直接调用编译好的DOM操作方法,省去了虚拟DOM比较这一步所消耗的时间。

上文的计时器,当点击后,从触发更新到视图变化的调用栈如下:

触发事件,更新状态,更新视图,一路调用走到底,清晰明了。

同样的例子放到React中,调用栈如下:

左中右红、绿、蓝框调用栈分别对应:

处理事件

对比并生成Fiber树

根据对比结果执行DOM操作

总结 React假的响应式,Solid yyds

React 响应的是组件树的变化,通过组件树自上而下的渲染来响应式更新。而 SolidJS 响应的只有数据,甚至数据定义申明在渲染函数外部也可以。

所以 React 虽然说自己是响应式,但开发者真正响应的是 UI 树的一层层更新,在这个过程中会产生闭包问题,手动维护 deps,hooks 不能写在条件分支之后,以及有时候分不清当前更新是父组件 rerender 还是因为状态变化导致的。

这一切都在说明,React 并没有让开发者真正只关心数据的变化,如果只要关心数据变化,那为什么组件重渲染的原因可能因为 “父组件 rerender” 呢?

为什么 SolidJS 移除了虚拟 dom 依然很快?

虚拟 dom 虽然规避了 dom 整体刷新的性能损耗,但也带来了 diff 开销。对 SolidJS 来说,它问了一个问题:为什么要规避 dom 整体刷新,局部更新不行吗?

对啊,局部更新并不是做不到,通过模板渲染后,将 jsx 动态部分单独提取出来,配合依赖收集,就可以做到变量变化时点对点的更新,所以无需进行 dom diff。

缺点明显,运行一次暴露出来

playground.solidjs.com/?version=1.…

import { render } from "solid-js/web"; import { createSignal } from "solid-js"; const BasicComponent = props => { const value = props.value || "BasicComponent default value"; return {value}; }; export default function Form() { const [value, setValue] = createSignal(""); return ( setValue(e.currentTarget.value)} /> ); } render(() => , document.getElementById("app"));

这个例子,一开始运行时,传入的是空串,所以表现是很XX

从表现和编译后的产物看

const BasicComponent = props => { // 这里会一直是 "BasicComponent default value" const value = props.value || "BasicComponent default value"; return (() => { const _el$ = _tmpl$.cloneNode(true); insert(_el$, value); return _el$; })(); };

那我们要怎么做呢?

// 改成下面这样就可以正常表现了 const BasicComponent = props => { // 或者const value = () => props.value || "default"; // return { value() } return { props.value || "BasicComponent default value"}; };

看编译后的产物

const BasicComponent = props => { return (() => { const _el$ = _tmpl$.cloneNode(true); insert(_el$, () => props.value || "BasicComponent default value"); return _el$; })(); }; 相关生态

虽然 SolidJS 很棒,但相关组件生态还没有起来,巨大的迁移成本是它难以快速替换到生产环境的最大问题。

脚手架:degit,内部集成了 vite。

支持TS且类型友好

现代前端框架大部分特性:Fragments、Portals、Context、Suspense、事件委托、SSR等等

附上链接

www.zhihu.com/question/46…

参考文献

juejin.cn/post/699214…

www.zhihu.com/question/46…

juejin.cn/post/701882…

juejin.cn/post/708356…

juejin.cn/post/699729…

juejin.cn/post/704148…

juejin.cn/post/697995…

www.solidjs.com/tutorial/in…

typeofnan.dev/what-does-i…

zhuanlan.zhihu.com/p/558894211…

cloud.tencent.com/developer/a… Hook的顺序执行)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有