Знакомство с библиотекой Solid JavaScript

Таким образом, компилятор может оптимизировать начальную визуализацию, а среда выполнения оптимизирует обновления. Такой упор на производительность делает Solid JavaScript одним из самых популярных фреймворков JavaScript.

Мне это стало любопытно и захотелось попробовать, поэтому я потратил некоторое время на создание небольшого приложения, чтобы изучить, как этот фреймворк обрабатывает компоненты рендеринга, обновляет состояние, настраивает хранилища и многое другое.

Вот финальная демонстрация, если вам не терпится увидеть окончательный код и результат:

JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

Как и для большинства фреймворков, мы можем начать с установки npm пакета. Чтобы использовать фреймворк с JSX, запустите:

JavaScript npm install solid-js babel-preset-solid

1 npm install solid-js babel-preset-solid

Затем нам нужно добавить babel-preset-solid в наш конфигурационный файл Babel, webpack или Rollup:

JavaScript “presets”: [“solid”]

1 “presets”: [“solid”]

Или, если вы хотите создать небольшое приложение, вы также можете использовать один из их шаблонов:

JavaScript # Create a small app from a Solid template npx degit solidjs/templates/js my-app # Change directory to the project created cd my-app # Install dependencies npm i # or yarn or pnpm # Start the dev server npm run dev

1234567891011 # Create a small app from a Solid templatenpx degit solidjs/templates/js my-app # Change directory to the project createdcd my-app # Install dependenciesnpm i # or yarn or pnpm # Start the dev servernpm run dev

Существует поддержка TypeScript, поэтому, если вы хотите запустить проект TypeScript, измените первую команду на .

Создание и рендеринг компонентов

Содержание статьи:

Для визуализации компонентов синтаксис похож на React.js, поэтому он может показаться знакомым:

JavaScript import { render } from “solid-js/web”; const HelloMessage = props => <div>Hello {props.name}</div>; render( () => <HelloMessage name=”Taylor” />, document.getElementById(“hello-example”) );

12345678 import { render } from “solid-js/web”; const HelloMessage = props => <div>Hello {props.name}</div>; render( () => <HelloMessage name=”Taylor” />, document.getElementById(“hello-example”));

Нам нужно начать с импорта функции render, затем мы создаем div с некоторым текстом и свойством, и вызываем render, передавая компонент и элемент контейнера.

Затем этот код компилируется в реальные выражения DOM. Например, приведенный выше пример кода, скомпилированный Solid, выглядит примерно так:

JavaScript import { render, template, insert, createComponent } from “solid-js/web”; const _tmpl$ = template(`<div>Hello </div>`); const HelloMessage = props => { const _el$ = _tmpl$.cloneNode(true); insert(_el$, () => props.name); return _el$; }; render( () => createComponent(HelloMessage, { name: “Taylor” }), document.getElementById(“hello-example”) );

1234567891011121314 import { render, template, insert, createComponent } from “solid-js/web”; const _tmpl$ = template(`<div>Hello </div>`); const HelloMessage = props => { const _el$ = _tmpl$.cloneNode(true); insert(_el$, () => props.name); return _el$;}; render( () => createComponent(HelloMessage, { name: “Taylor” }), document.getElementById(“hello-example”));

Solid Playground довольно крутая штука, она показывает, что Solid имеет разные способы рендеринга, включая клиентский, серверный и клиентский с гидратацией.

Отслеживание изменения значений с помощью Signals

Solid использует хук createSignal, который возвращает две функции: геттер и сеттер. Если вы привыкли использовать такой фреймворк, как React.js, это может показаться немного странным. Обычно вы ожидаете, что первым элементом будет само значение; однако в Solid нам нужно явно вызвать геттер, чтобы перехватить, где считывается значение, для отслеживания его изменений. Например, если мы напишем следующий код:

JavaScript const [todos, addTodos] = createSignal([]);

1 const [todos, addTodos] = createSignal([]);

Отслеживание todos вернет не значение, а функцию. Если мы хотим использовать значение, нам нужно вызвать функцию, как в todos(). Для небольшого списка todo это будет так:

JavaScript import { createSignal } from “solid-js”; const TodoList = () => { let input; const [todos, addTodos] = createSignal([]); const addTodo = value => { return addTodos([…todos(), value]); }; return ( <section> <h1>To do list:</h1> <label for=”todo-item”>Todo item</label> <input type=”text” ref={input} name=”todo-item” id=”todo-item” /> <button onClick={() => addTodo(input.value)}>Add item</button> <ul> {todos().map(item => ( <li>{item}</li> ))} </ul> </section> ); };

123456789101112131415161718192021222324 import { createSignal } from “solid-js”; const TodoList = () => { let input; const [todos, addTodos] = createSignal([]); const addTodo = value => {   return addTodos([…todos(), value]); }; return (   <section>     <h1>To do list:</h1>     <label for=”todo-item”>Todo item</label>     <input type=”text” ref={input} name=”todo-item” id=”todo-item” />     <button onClick={() => addTodo(input.value)}>Add item</button>     <ul>       {todos().map(item => (         <li>{item}</li>       ))}     </ul>   </section> );};

В приведенном выше примере кода будет отображаться текстовое поле, и после нажатия кнопки «Добавить элемент», todos будут обновлены новым элементом и отображены в списке.

Это может показаться очень похожим на использование useState, так чем отличается использование геттера? Рассмотрим следующий пример кода:

JavaScript console.log(“Create Signals”); const [firstName, setFirstName] = createSignal(“Whitney”); const [lastName, setLastName] = createSignal(“Houston”); const [displayFullName, setDisplayFullName] = createSignal(true); const displayName = createMemo(() => { if (!displayFullName()) return firstName(); return `${firstName()} ${lastName()}`; }); createEffect(() => console.log(“My name is”, displayName())); console.log(“Set showFullName: false “); setDisplayFullName(false); console.log(“Change lastName “); setLastName(“Boop”); console.log(“Set showFullName: true “); setDisplayFullName(true);

1234567891011121314151617181920 console.log(“Create Signals”);const [firstName, setFirstName] = createSignal(“Whitney”);const [lastName, setLastName] = createSignal(“Houston”);const [displayFullName, setDisplayFullName] = createSignal(true); const displayName = createMemo(() => { if (!displayFullName()) return firstName(); return `${firstName()} ${lastName()}`;}); createEffect(() => console.log(“My name is”, displayName())); console.log(“Set showFullName: false “);setDisplayFullName(false); console.log(“Change lastName “);setLastName(“Boop”); console.log(“Set showFullName: true “);setDisplayFullName(true);

Результатом выполнение приведенного выше будет:

JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

JavaScript Create Signals My name is Whitney Houston Set showFullName: false My name is Whitney Change lastName Set showFullName: true My name is Whitney Boop

12345678910111213 Create Signals My name is Whitney Houston Set showFullName: false My name is Whitney Change lastName Set showFullName: true My name is Whitney Boop

Заметьте, что My name is …не регистрируется после установки новой фамилии. Это потому, что на данный момент ничего не отслеживает изменения lastName(). Новое значение displayName() устанавливается только при изменении значения displayFullName(), поэтому мы можем видеть новую фамилию, отображаемую, когда значение setShowFullName установлено обратно на true.

Это дает нам более безопасный способ отслеживать обновления значений.

Примитивы

В последнем примере кода я представил createSignal, а также пару других примитивов: createEffect и createMemo.

createEffect

createEffect отслеживает зависимости и запускается после каждого рендеринга, в котором зависимость изменилась.

JavaScript // Don’t forget to import it first with ‘import { createEffect } from “solid-js”;’ const [count, setCount] = createSignal(0); createEffect(() => { console.log(“Count is at”, count()); });

123456 // Don’t forget to import it first with ‘import { createEffect } from “solid-js”;’const [count, setCount] = createSignal(0); createEffect(() => { console.log(“Count is at”, count());});

Count is at…регистрирует каждое изменение значения count().

createMemo

createMemo создает сигнал только для чтения, который пересчитывает значение при каждом обновлении зависимостей исполняемого кода. Вы можете использовать его, когда хотите кэшировать некоторые значения и получать к ним доступ без их повторной оценки до тех пор, пока не изменится зависимость.

Например, если мы хотим отображать счетчик 100 раз и обновлять значение при нажатии на кнопку, использование createMemo позволит выполнить пересчет только один раз за клик:

JavaScript function Counter() { const [count, setCount] = createSignal(0); // Calling `counter` without wrapping it in `createMemo` would result in calling it 100 times. // const counter = () => { // return count(); // } // Calling `counter` wrapped in `createMemo` results in calling it once per update. // Don’t forget to import it first with ‘import { createMemo } from “solid-js”;’ const counter = createMemo(() => { return count() }) return ( <> <button onClick={() => setCount(count() + 1)}>Count: {count()}</button> <div>1. {counter()}</div> <div>2. {counter()}</div> <div>3. {counter()}</div> <div>4. {counter()}</div> <!– 96 more times –> </> ); }

123456789101112131415161718192021222324 function Counter() {   const [count, setCount] = createSignal(0);   // Calling `counter` without wrapping it in `createMemo` would result in calling it 100 times.   // const counter = () => {   //    return count();   // }    // Calling `counter` wrapped in `createMemo` results in calling it once per update.// Don’t forget to import it first with ‘import { createMemo } from “solid-js”;’   const counter = createMemo(() => {       return count()   })    return (       <>       <button onClick={() => setCount(count() + 1)}>Count: {count()}</button>       <div>1. {counter()}</div>       <div>2. {counter()}</div>       <div>3. {counter()}</div>       <div>4. {counter()}</div>       <!– 96 more times –>       </>   );}

Методы жизненного цикла

Solid предоставляет несколько методов жизненного цикла, например onMount, onCleanup и onError. Если мы хотим, чтобы какой-то код запускался после первоначального рендеринга, нам нужно использовать onMount:

JavaScript // Don’t forget to import it first with ‘import { onMount } from “solid-js”;’ onMount(() => { console.log(“I mounted!”); });

12345 // Don’t forget to import it first with ‘import { onMount } from “solid-js”;’ onMount(() => { console.log(“I mounted!”);});

onCleanup аналогичен componentDidUnmount в React. onError выполняется, когда есть ошибка в ближайшей дочерней области. Например, мы могли бы использовать его при сбое выборки данных.

Хранилища

Для создания хранилищ данных Solid использует createStore которое возвращает прокси-объект и функцию сеттера. Например, если бы мы изменили наш пример задачи, чтобы использовать хранилище вместо state, это выглядело бы примерно так:

JavaScript const [todos, addTodos] = createStore({ list: [] }); createEffect(() => { console.log(todos.list); }); onMount(() => { addTodos(“list”, [ …todos.list, { item: “a new todo item”, completed: false } ]); });

123456789101112 const [todos, addTodos] = createStore({ list: [] }); createEffect(() => { console.log(todos.list);}); onMount(() => { addTodos(“list”, [   …todos.list,   { item: “a new todo item”, completed: false } ]);});

Приведенный выше пример кода начинается с регистрации прокси-объекта с пустым массивом, за которым следует прокси-объект с массивом, содержащим объект {item: «a new todo item», completed: false}.

Следует отметить, что объект состояния верхнего уровня не может быть отслежен без доступа к его свойству — поэтому мы используем todos.list а не todos.

Если бы мы отслеживали todo в createEffect, мы бы увидели начальное значение списка, а не то значение, которое было после обновления, сделанного в onMount.

Чтобы изменить значения в хранилищах, мы можем обновить их, используя функцию настройки, которую мы определяем при использовании createStore. Например, если мы хотим обновить элемент списка задач на «завершено», мы могли бы обновить хранилище следующим образом:

JavaScript const [todos, setTodos] = createStore({ list: [{ item: “new item”, completed: false }] }); const markAsComplete = text => { setTodos( “list”, i => i.item === text, “completed”, c => !c ); }; return ( <button onClick={() => markAsComplete(“new item”)}>Mark as complete</button> );

12345678910111213141516 const [todos, setTodos] = createStore({ list: [{ item: “new item”, completed: false }]}); const markAsComplete = text => { setTodos(   “list”,   i => i.item === text,   “completed”,   c => !c );}; return ( <button onClick={() => markAsComplete(“new item”)}>Mark as complete</button>);

Поток управления

Чтобы избежать расточительного воссоздания всех узлов DOM при каждом обновлении при использовании таких методов, как .map(), мы можем использовать шаблоны помошники.

Некоторые из них: For — перебрать элементы, Show -условно показать и скрыть элементы, Switch и Match — показать элементы, которые соответствуют определенному условию, и многое другое! Вот несколько примеров, показывающих, как их использовать:

JavaScript <For each={todos.list} fallback={<div>Loading…</div>}> {(item) => <div>{item}</div>} </For> <Show when={todos.list[0].completed} fallback={<div>Loading…</div>}> <div>1st item completed</div> </Show> <Switch fallback={<div>No items</div>}> <Match when={todos.list[0].completed}> <CompletedList /> </Match> <Match when={!todos.list[0].completed}> <TodosList /> </Match> </Switch>

12345678910111213141516 <For each={todos.list} fallback={<div>Loading…</div>}> {(item) => <div>{item}</div>}</For> <Show when={todos.list[0].completed} fallback={<div>Loading…</div>}> <div>1st item completed</div></Show> <Switch fallback={<div>No items</div>}> <Match when={todos.list[0].completed}>   <CompletedList /> </Match> <Match when={!todos.list[0].completed}>   <TodosList /> </Match></Switch>

Демонстрационный проект

Это было быстрое введение в основы Solid. Если вы хотите поэкспериментировать с ним, я создал стартовый проект, который вы можете автоматически развернуть на Netlify и клонировать в свой GitHub, перейдя по ссылке.

Этот проект включает в себя настройку по умолчанию для Solid проекта, а также образец приложения Todo с базовыми концепциями, которые я упомянул в этом посте.

Структура проекта гораздо больше, чем та, что я здесь рассмотрел, поэтому не стесняйтесь знакомиться с документацией для получения дополнительной информации!

Автор: Charlie Gerard

Источник: webformyself.com

Comments (0)
Add Comment