前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【SolidJs】仅次于原生JS的超级性能!SolidJs框架教程【上】

【SolidJs】仅次于原生JS的超级性能!SolidJs框架教程【上】

作者头像
HoMeTown
发布2022-10-26 08:33:18
4.5K0
发布2022-10-26 08:33:18
举报
文章被收录于专栏:秃头开发头秃了

背景

今天被战友种草了一款前端框架,打开链接看文章,在各个指标的比较下,SolidJs脱颖而出,下面简单介绍一下这个框架,然后开始记录一下学习笔记。(Golang的事情暂时放一放,毕竟咱是专业前端「手动狗头」)。

SolidJs简介

SolidJs作为一个新星)可谓是各个厂牌的集大成者,它支持JSXFragmentsContextPortalsLazy等等,而且是继HyperApp和Svelte后,第三个比纯JS实现更小的库。

JSX + template

SolidJs在保持JSX语法的同时,做了一些template的规范,比如它的ForIndexSwitchMatch.....既保留了JSX语法的灵活性,又再某些程度提高了编译速度,perfect!

NO DOM DIFF

SolidJs并不像Vue&React采用了虚拟dom,解决了内存占用过多的问题

SolidJs教程

Hello World

代码语言:javascript
复制
import { render } from 'solid-js/web'
function HelloWorld() {
    return <p>Hello World</p>
}

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

组件的使用

SolidJs中,组件的含义与React基本一致,即组件即函数,举个🌰

nested.tsx

代码语言:javascript
复制
export default () => <p>from nested.tsx</p>

main.tsx

代码语言:javascript
复制
import {render} from 'solid-js/web'
import Nested from './nested'
function App() {
 return (
     <Nested />
 )
}
render(() => <App />, document.getElementById("app"))

Fragment

如果返回了2个元素,可以用Fragement包装起来,举个🌰

代码语言:javascript
复制
import {render} from 'solid-js/web'
import Nested from './nested'
function App() {
    return (
        <>
            <h1>组件:Nested</h1>
            <Nested />
        </>
    )
}
render(() => <App/>, document.getElementById('app'))

Signal

Signal是最核心的响应式基本要素,创建一个Signal的语法为:

代码语言:javascript
复制
const [count, setCount] = createSignal(0)

当然,createSignal需要从solid-js中导出:

代码语言:javascript
复制
import { createSignal } from 'solid-js'

传递给createSignal的值就是count的初始值,返回一个带有2个函数的数组,一个是getter,一个是setter,可以使用解构随意命名这些参数,例如上面那个🌰,count就是getter,setCount就是setter。 注意:第一个返回值是一个getter,而不是0值本身。 举一个计数器的🌰:

代码语言:javascript
复制
import { render } from 'solid-js/web'
import { createSignal } from 'solid-js'
function App() {
    const [count, setCount] = createSignal(0)
    setInterval(() => {
        setCount(count() + 1)
    }, 1000)
    return (
        <div>count的值为:{count()}</div>
    )
}

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

setCount也可以接受一个函数,举个🌰

代码语言:javascript
复制
setInterval(() => {
    setCount(c => c +1 )
}, 1000)

派生Signal

类似于Vue中的computed,举个🌰

代码语言:javascript
复制
import { render } from 'solid-js/web'
import { createSignal } from 'solid-js'
function App() {
    const [count, setCount] = createSignal(0)
    const doubleCount = () => count() * 2
    return (
        <>
            <div>doubleCount: {doubleCount()}</div>
            <button onClick={() => setCount(c => c+1)}>点击</button>
        </>
    )
}
render(() => <App/>, docu3ment.getElementById('app'))

Effect

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

代码语言:javascript
复制
import { render } from 'solid-js/web'
import { createSignal, createEffect } from 'solid-js'
function App() {
    const [count, setCount] = createSignal(0)
    createEffect(() => {
        console.log("当前 count:", count())
    })
    return (
        <button onClick={() => setCount(c => c + 1)}>点击</button>
    )
}

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

Show

Show组件用来控制DOM是否显示,举个🌰

代码语言:javascript
复制
import { render } from 'solid-js/web';
import { createSignal, Show } from 'solid-js';
function App() {
    const [loggedIn, setLoggedIn] = createSignal(false);
    const toggle = () => setLoggedIn(!loggedIn())
    return (
        <Show when={loggedIn()} fallback={() => <button onClick={toggle}>Log in</button>}>
            <button onClick={toggle}>Log out</button>
        </Show>
    )
}
render(() => <App />, document.getElementById('app'))

Vue不同的是,solidJs是在Show组件中通过when关键字后加条件来判断是否显示,通过fallback关键字来显示上一个条件为false时的组件。

For

For是SolidJs遍历的列表组件,举个🌰

代码语言:javascript
复制
import { render } from 'solid-js/web';
import { createSignal, For } from 'solid-js';
function App() {
  const [dogs, setDogs] = createSignal([
    { id: 0, name: "dog1" },
    { id: 1, name: "dog2" },
    { id: 2, name: "dog3" },
  ]);
  return (
    <For each={dogs()}>
      {(dog, i) => (
        <div>
          id:{i()}; name:{dog.name}
        </div>
      )}
    </For>
  );
}
render(() => <App />, document.getElementById('app'))

Index

Index组件也是用来遍历的,上面的🌰,用Index组件进行遍历:

代码语言:javascript
复制
<Index each={dogs()}>
    {(dog, i) => (
        {i}, {dog().name}
    )}
</Index>

可以发现,IndexFor具有相似的功能,不同点是,For中索引是SignalIndex中数据项是Signal

Switch

Switch组件是Show组件的扩展,如果在一个多层条件判断的情况下,使用Show组件会发生多级嵌套,代码臃肿,使用Switch/Match可以很好的解决这种情况,举个🌰

代码语言:javascript
复制
import { render } from 'solid-js/web';
import { createSignal, Switch, Match } from 'solid-js'
function App() {
    const [age, setAge] = createSignal(12);
      return (
        <Switch fallback={<div>未知</div>}>
          <Match when={age() < 18}>
            <div>未成年</div>
          </Match>
          <Match when={age() >= 18 && age() < 60}>
            <div>成年人</div>
          </Match>
          <Match when={age() >= 60}>
            <div>老年人</div>
          </Match>
        </Switch>
      );
}

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

Dynamic

Dynamic组件处可以让开发者编写<Switch>/<Match>组件变得更简练,举个栗子:

代码语言:javascript
复制
import { render, Dynamic } from "solid-js/web";
import { createSignal, Switch, Match, For } from "solid-js";

const RedThing = () => <strong style="color: red">Red Thing</strong>;
const GreenThing = () => <strong style="color: green">Green Thing</strong>;
const BlueThing = () => <strong style="color: blue">Blue Thing</strong>;

const options = {
  red: RedThing,
  green: GreenThing,
  blue: BlueThing,
};

function App() {
  const [selected, setSelected] = createSignal("red");

  return (
    <>
      <select
        value={selected()}
        onInput={(e) => setSelected(e.currentTarget.value)}
      >
        <For each={Object.keys(options)}>
          {(color) => <option value={color}>{color}</option>}
        </For>
      </select>
      <Dynamic component={options[selected()]} />
    </>
  );
}

render(() => <App />, document.getElementById("app"));

Portal

Portal组件可以在正常顺序之外插入元素,默认情况下,Portal的子内容将从正常的DOM顺序中拿出来,插入到document.body下的

中,举个🌰

main.tsx

代码语言:javascript
复制
import { render, Portal } from "solid-js/web";
import "./styles.css";

function App() {
  return (
    <div class="app-container">
      <p>Just some text inside a div that has a restricted size.</p>
      <Portal>
        <div class="popup">
          <h1>Popup</h1>
          <p>Some text you might need for something or other.</p>
        </div>
      </Portal>
    </div>
  );
}

render(() => <App />, document.getElementById("app"));

styles.css

代码语言:javascript
复制
.app-container {
  width: 200px;
  height: 100px;
  overflow: hidden;
}

.popup {
  position: relative;
  z-index: 2;
  background: #ddd;
  padding: 1rem;
  min-height: 200px;
  width: 200px;
}

.popup::after {
  content: " ";
  position: absolute;
  bottom: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent transparent #ddd transparent;
}

效果是这样的:

ErrorBoundary

ErrorBoundary是一个可以捕获子组件任何未知产生的JavaScript错误,并显示发生错误的报错信息,举个🌰

代码语言:javascript
复制
import { render } from "solid-js/web";
import { ErrorBoundary } from "solid-js";

const Broken = (props) => {
  throw new Error("Oh No");
  return <>Never Getting Here</>
}

function App() {
  return (
    <>
      <div>Before</div>
      <ErrorBoundary fallback={(err) => err}>
        <Broken />
      </ErrorBoundary>
      <div>After</div>
    </>
  );
}

render(() => <App />, document.getElementById("app"));

效果如下:

onMount 生命周期

引用一段官方的话:

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

举个🌰

代码语言:javascript
复制
import { render } from "solid-js/web";
import { createSignal, onMount, For } from "solid-js";
function App() {
  const [photos, setPhotos] = createSignal([]);
  onMount(async () => {
    const res = await fetch(
      `https://jsonplaceholder.typicode.com/photos?_limit=20`
    );
    setPhotos(await res.json());
  });
  return (
    <>
      <h1>photos</h1>
      <div class="photos">
        <For each={photos()} fallback={<p>Loading</p>}>
          {(photo, i) => (
            <figure>
              <img src={photo.thumbnailUrl} alt={photo.title} />
              <figcaption>{photo.title}</figcaption>
            </figure>
          )}
        </For>
      </div>
    </>
  );
}

render(() => <App />, document.getElementById("app"));

onCleanup

onCleanup函数可以执行一些销毁操作,举个栗子

代码语言:javascript
复制
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 <div>Count: {count()}</div>;
}

render(() => <Counter />, document.getElementById("app"));

事件

在SolidJs中,事件均以on为前缀,举个🌰

代码语言:javascript
复制
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

import "./style.css";

function App() {
  const [pos, setPos] = createSignal({ x: 0, y: 0 });

  function handleMouseMove(event) {
    setPos({
      x: event.clientX,
      y: event.clientY,
    });
  }

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

render(() => <App />, document.getElementById("app"));

动态样式 Style

SolidJs中的style属性接受样式字符串和对象,但需要注意一点,官话:

对象形式不同于 Element.prototype.style,Solid 通过调用 style.setProperty 的封装来进行样式设置。

意味着样式的键需要采用中划线的方式,例如background-color而不是backgroundColor,但是这意味着可以设置CSS变量:

代码语言:javascript
复制
<div style={{'--my-custom-color': themeColor()}}></div>

举个🌰

代码语言:javascript
复制
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

function App() {
  const [num, setNum] = createSignal(0);
  setInterval(() => setNum((num() + 1) % 255), 30)

  return <div style={{
      color: `rgb(${num()}, 180, ${num()})`,
      'font-size': `${num()}px`
  }}>Some Text</div>;
}

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

动态类名 ClassList

SolidJs支持使用 classclassName设置元素的classname属性,classList用来设置多个class名,key是类名,value是一个boolean,举个🌰

代码语言:javascript
复制
function App() {
    const [current, setCurrent] = createSignal('foo')
    return <>
        <button class={current() === 'foo' ? 'selected' : ''} onClick={setCurrent('foo')}>foo</button>
        <button class={current() === 'bar' ? 'selected' : ''} onClick={setCurrent('bar')}>bar</button>
        <button class={current() === 'baz' ? 'selected' : ''} onClick={setCurrent('bar')}>baz</button>
    </>
}

当然上面的代码也可以用classList,🌰:

代码语言:javascript
复制
<button classList={{'selected': current() === 'foo'}} onClick={setCurrent('foo')}>foo</button>

Ref

SolidJs中,可以通过ref属性获取元素的引用,官话:

赋值行为是在元素创建时,在 DOM 被追加前发生的。只需声明一个变量,元素引用就会赋值给该变量。

举个🌰

代码语言:javascript
复制
let myDiv;

<div ref={myDiv}>my Div</div>

就可以了...

属性绑定/扩展

利用...扩展运算符,可以直接把属性传进子组件,举个🌰

main.tsx

代码语言:javascript
复制
import { render } from "solid-js/web";
import Info from "./info";
const pkg = {
  name: "dog",
  age: 3,
  gender: 0,
};
function App() {
  return <Info name={pkg.name} age={pkg.age} gender={pkg.gender}></Info>;
}
render(() => <App />, document.getElementById("app"));

info.tsx

代码语言:javascript
复制
export default function Info(props) {
  console.log(props)
  return (
    <div>
      <p>name: {props.name}</p>
      <p>age: {props.age}</p>
      <p>gender: {props.gender === 0 ? "girl" : "boy"}</p>
    </div>
  );
}

上面的main.tsx可以这样改造一下:

代码语言:javascript
复制
function App() {
  return <Info {...pkg}></Info>;
}

是不是省了很多事儿!

指令

SolidJs可以通过use自定义指令,自定义指令仅仅只是一个形式为(element, valueAccesor)的函数,其中valueAccesor是一个获取绑定值的函数,只要函数是在作用域中导入的,就可以通过use:使用。

重要提示:use: 需要被编译器检测并进行转换,并且函数需要在作用域内,因此不能作为传值的一部分或在组件上使用。

举一个modal的🌰:

main.tsx

代码语言:javascript
复制
import { render } from "solid-js/web";
import { createSignal, Show } from "solid-js";
import clickOutside from './click-outside'
function App() {
    const [show, setShow] = createSignal(false)
    
    return (
        <Show when={show()} fallback={<button onClick={setShow(true)}>show Modal</button>}>
            <div class="modal" use:clickOutside={show(false)}>modal box</modal>
        </Show>
    )
}
render(() => <App />, document.getElementById("app"));

click-outside.tsx

代码语言:javascript
复制
import { onCleanup } from 'solid-js';
export default function clickOutside(el, accessor) {
    const onClick = (e) => !el.contains(e.target) && accessor()?.();
    document.body.addEventListener('click', onClick)
    
    onCleanup(() => document.removeEventListener('click', onClick))
}

props

SolidJs中的props传值上面已经有🌰了,很简单,再举个🌰

main.tsx

代码语言:javascript
复制
import WelcomeWord from './welcomeWord'
function App() {
    reutrn (
        <>
            <WelcomeWord gretting="Hi" name="Bob"></WelcomeWord>
        </>
    )
}

welcomeWord.tsx

代码语言:javascript
复制
export default function welcomeWord(props) {
    return <h1>{props.gretting || 'hello'} {props.name || 'Alice'}</h1>
}

props赋值默认值通过mergeProps操作,上面的🌰就可以改成这样

welcomeWord.tsx

代码语言:javascript
复制
import { mergeProps } from 'solid-js'
export default function welcomeWord(props) {
    const merged = mergeProps({ greeting: 'hello', name: 'Alice' }, props)
    return <h1>{merged.greeting} {merged.name} </h1>
}

就很完美,官话:

Solid 有一些工具函数可以帮助我们处理 props。第一个 mergeProps 函数听起来很像它名字描述得那样合并 props。mergeProps 将潜在的响应式对象合并而不会失去响应式性。最常见的情况就是是为组件设置默认 props。

props分离

在SolidJs中解构一个props会失去数据的响应性,可以通过splitProps来分离一个props,举个🌰

main.tsx

代码语言:javascript
复制
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

import Greeting from "./greeting";
function app() {
    const [name, setName] = createSignal("Jack")
    return <>
        <Greeting greeting="Yo" name={name()} style="color: teal;" />
        <button onClick={setName("Alice")}>set name</button>
    </>
}
render(() => <App />, document.getElementById('app'));

greeting.tsx

代码语言:javascript
复制
export default function Greeting(props) {
    let { greeting, name, ...others } = props
    return <h3 {...others}>{greeting} {name}</h3>
}

这种方式修改name的时候,发现子组件并没有修改,所以需要使用splitProps

代码语言:javascript
复制
import { splitProps } from "solid-js";

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

就很完美!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 秃头开发头秃了 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • SolidJs简介
    • JSX + template
      • NO DOM DIFF
      • SolidJs教程
        • Hello World
          • 组件的使用
            • Fragment
              • Signal
                • 派生Signal
                  • Effect
                    • Show
                      • For
                        • Index
                          • Switch
                            • Dynamic
                              • Portal
                                • ErrorBoundary
                                  • onMount 生命周期
                                    • onCleanup
                                      • 事件
                                        • 动态样式 Style
                                          • 动态类名 ClassList
                                            • Ref
                                              • 属性绑定/扩展
                                                • 指令
                                                  • props
                                                    • props分离
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档