Solidjs 简介

Solid-js的定义

一个声明式、高效且灵活用于构建用户界面的 JavaScript 库(官方文档

为什么选择Solidjs

  • 高性能 - 始终在公认的 UI 速度和内存利用率基准测试中名列前茅
  • 强大 - 可组合的反应式原语与 JSX 的灵活性相结合
  • 务实 - 合理且量身定制的 API 使开发变得有趣而简单
  • 生产力 - 人体工程学和熟悉程度使构建简单或复杂的东西变得轻而易举

优势

高性能 - 接近原生的性能,在 js-framework-benchmark 排名中名列前茅
极小的打包体积 - 编译为直接的DOM操作,无虚拟DOM,极小的运行时(类似于 Svelte),适合打为独立的 webComponent 在其它应用中嵌入
易于使用 - 近似 React 的使用体验,便于快速上手

快速开始

新建项目

> npx degit solidjs/templates/js my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm

示例

将HelloWorld渲染到app容器中

import { render } from 'solid-js/web';

function HelloWorld() {
  return <div>Hello World!</div>;
}

render(() => <HelloWorld />, document.getElementById('app'))

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

基本概念

jsx

jsx语法

Signal

Signal 是 Solid 中最基本的反应性单元,此函数类似于 React 的 useState,但返回函数用于获取调用它获取值,而不是像 React 一样直接取得值。使用createSignal创建

const [count, setCount] = createSignal(0);

 使用方式

setCount(count() + 1)
setCount((c) => c + 1); // c代表前一个值

读取Signal

<div>Count: {count()}</div>;

Effect 

Signal 是可追踪的值,但它们只是等式的一半。另一半是观察者,也就是计算。最基本的计算称为 Effect,它产生副作用 —— 我们系统的输出。

可以通过从 solid-js 导入 createEffect 来创建 Effect。createEffect 接收一个函数,并监视其执行情况。createEffect 会自动订阅在执行期间读取的所有 Signal,并在这些 Signal 值之一发生变化时重新运行该函数。

createEffect(() => {
  console.log("The count is now", count());
});

Memo

 Memo 既是是跟踪计算,类似 Effect,又是只读 Signal。由于知道对应依赖关系及其观察者,Memo 可以确保他们只对任何更改运行一次。

看一个例子

生命周期

onMount

它只是一个 Effect 调用,但你可以放心使用它,一旦所有初始渲染完成,它只会在组件中运行一次

onMount(async () => {
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/photos?_limit=20`
  );
  setPhotos(await res.json());
});

onCleanup 

一些框架的清理方法直接是框架的副作用或生命周期方法的返回值。由于 Solid 渲染树中的所有内容都存在于(可能是惰性的)Effect 中并且可以嵌套,因此我们将 onCleanup 设为一级方法。您可以在任何范围内调用它,它会在该范围被触发以重新求值以及最终销毁时运行。

onCleanup(() => clearInterval(timer));

 流程控制

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

Show

JSX 允许使用 JavaScript 来控制模板中的逻辑流。然而,如果没有虚拟 DOM,天真地使用诸如 Array.prototype.map 之类的东西会在每次更新时很浪费地重新创建所有 DOM 节点。相反,Reactive 库使用模板工具是很常见。在 Solid 中,我们将其封装在组件中

<Show
  when={loggedIn()}
  fallback={() => <button onClick={toggle}>Log in</button>}
>
  <button onClick={toggle}>Log out</button>
</Show>

fallback 属性充当 else,在传递给 when 的条件不为 true 时显示。

For

<For each={cats()}>
  {(cat, i) => (
    <li>
      <a target="_blank" href={`https://www.youtube.com/watch?v=${cat.id}`}>
        {i() + 1}: {cat.name}
      </a>
    </li>
  )}
</For>

index 是一个 Signal,因此它可以在移动行时独立更新

Index

<Index each={cats()}>
  {(cat, i) => (
    <li>
      <a target="_blank" href={`https://www.youtube.com/watch?v=${cat().id}`}>
        {i + 1}: {cat().name}
      </a>
    </li>
  )}
</Index>

<Index><For> 具有相似的签名,除了数据项是 Signal 并且索引是固定的。

Switch

<Switch><Match> 组件类比 javaScript的Switch/case

<Switch fallback={<p>{x()} is between 5 and 10</p>}>
  <Match when={x() > 10}>
    <p>{x()} is greater than 10</p>
  </Match>
  <Match when={5 > x()}>
    <p>{x()} is less than 5</p>
  </Match>
</Switch>

Dynamic

<Dynamic> 可以让你将元素的字符串或组件函数传递给它,并使用提供的其余 props 来渲染组件

<Dynamic component={options[selected()]} />

除此之外,还有Portal、ErrorBoundary等组件,详情见官方文档

绑定

事件绑定

常规事件绑定

<div onMouseMove={handleMouseMove}>
  The mouse position is {pos().x} x {pos().y}
</div>

Solid 支持数组语法调用事件处理程序

const handler = (data, event /*...*/) => (
  <button onClick={[handler, data]}>Click Me</button>
);

on: 命名空间来匹配冒号后面的事件处理程序 

<button on:WierdEventName={() => /* Do something */} >Click Me</button>

 样式

先写一段css

/* main.module.css */
.container {
  width: 100px;
  height: 100px;
  background-color: green;
}
.text {
  font-size: 20px;
  color: red;
}

样式使用也与 React 非常类似,只是使用 class 而不是 className 

import style from "./main.module.css";

export default function Container() {
  return (
    <div class={style.container}>
      <span class={style.text}>text</span>
    </div>
  )
}

Solid 中的 style 属性接受样式字符串或对象。然而,对象形式不同于 Element.prototype.style,Solid 通过调用 style.setProperty 的封装来进行样式设置。这意味着键需要采用破折号的形式,如 background-color 而不是 backgroundColor

classList 

用于设置给定的 class 是否存在, 也可以绑定响应式
下列是一个点击切换 class 的示例

import style from "./main.css";
import { createSignal } from "solid-js";

export default function Container() {
  const [hasTextClassName, setHasTextClassName] = createSignal(false);
  return (
    <div 
      classList={
        {
          [style.container]: true,
          [style.text]: hasTextClassName()
        }
      } 
      onClick={
        ()=> setHasTextClassName(!hasTextClassName())
      }
    >
    text
    </div>
  )
}

ref/ref转发

<div ref={myDiv}>My Element</div>;
<div ref={el => /* 处理 el... */}>My Element</div>
<canvas ref={props.ref} width="256" height="256" />

指令

Solid 通过 use: 命名空间支持自定义指令。但这只是 ref 一个有用的语法糖,类似于原生的绑定,并且可以在同一个元素上有多个绑定而不会发生冲突。这可以让我们更好地利用可重用 DOM 元素行为。 

<div class="modal" use:clickOutside={() => setShow(false)}>
  Some Modal
</div>

 click-outside.js

//click-outside.js
export default function clickOutside(el, accessor) {
  const onClick = (e) => !el.contains(e.target) && accessor()?.();
  document.body.addEventListener("click", onClick);

  onCleanup(() => document.body.removeEventListener("click", onClick));
}

 Props

设置props默认值(mergeProps)

一般情况

export default function Greeting(props) {
  return <h3>{props.greeting || "Hi"} {props.name || "John"}</h3>
}

使用mergeProps 

export default function Greeting(props) {
  const merged = mergeProps({ greeting: "Hi", name: "John" }, props);
  return <h3>{merged.greeting} {merged.name}</h3>
}

解构Props(splitProps

使用直接解构的方式,会失去响应性

export default function Greeting(props) {
  const { greeting, name, ...others } = props;
  return <h3 {...others}>{greeting} {name}</h3>
}

使用splitProps可以保持其相应行 

export default function Greeting(props) {
  const [local, others] = splitProps(props, ["greeting", "name"]);
  return <h3 {...others}>{local.greeting} {local.name}</h3>
}

注意:solid中的props读取时不可以使用延展操作符,否则会失去响应式

异步

lazy

在应用中,某些组件只在使用时加载,这些组件会被单独打包,在某个时间被按需加载,solid 也提供了方法
使用 lazy 替换普通的静态 import 语句
将 

import Component1 from "./Component1.jsx";

替换为

const Component1 = lazy(() => import("./Component1"));

 由于 lazy 接接收的参数只是返回 Solid 组件的 Promise,因此,还可以在加载的时候附加一些行为

createResource

创建一个可以管理异步请求的信号。fetcher 是一个异步函数,它接受sourceif 提供的返回值并返回一个 Promise,其解析值设置在资源中。fetcher 不是响应式的,因此如果您希望它运行多次,请使用可选的第一个参数。如果源解析为 false、null 或 undefined,则不会获取。

官网在线试玩

const [data, { mutate, refetch }] = createResource(getQuery, fetchData);
// 获取值
data();
// 检查其是否加载中
data.loading;
// 检查是否出错
data.error;
// 直接设置值
mutate(optimisticValue);
// 刷新,重新请求
refetch();

Suspense

Suspense 配合异步组件使用
在尚未加载完毕时显示 fallback 中给定的内容

const Component1 = lazy(() => import("./Component1"));

export default function App() {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Component1></Component1>
    </Suspense>
  )
}

 更多异步

总结

  • Solid 具有高性能,并且具有极小的打包体积,适合打包为独立的模块嵌入其它项目
  • Solid 上手简单,贴合 React 或是 Vue3 开发者的使用习惯
  • Solid 中 JSX 直接返回 DOM 元素,符合直觉,并且很纯净
  • Solid 某些地方需要使用其指定的东西才能达到高性能,高性能并不是毫无代价的
  • Solid 目前使用并不多,生态有待完善

友情链接 

docstar-history 、sizehomepage

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值