Новый интерфейсный фреймворк Google, Lit

внешний интерфейс внешний фреймворк
Новый интерфейсный фреймворк Google, Lit

новый фреймворк GoogleLit, Фреймворк с благословения Google вызвал у меня интерес, поэтому я пошел вкратце разбираться, и просто поделился с вами результатами обучения. из-заLitФреймворк все еще находится в экспресс-итерации. Некоторые реализации кода, упомянутые в статье, скорее всего, подверглись рефакторингу. Заинтересованные студенты могут ознакомиться с ним.Litисходный код.

Что такое ЛИТ

Litосновывается наWeb-ComponentСозданный интерфейсный фреймворк, предшественник можно в основном понимать какPolymer,LitОбеспечивает следующие конкурентные функции

  • на основеWeb-ComponentИнкапсуляция более высокого уровня обеспечивает адаптивные данные и декларативные шаблоны, которые используются в современной интерфейсной разработке, сокращая часть стандартного кода веб-компонентов.

  • маленький. Только 5K во время выполнения

  • Мощная производительность. избегалиVDOMНекоторые недостатки: обработка только асинхронной части пользовательского интерфейса при обновлении (это можно понимать как обработку только адаптивной части).

  • Совместимость лучше. потому чтоweb-componentЭто родная способность HTML, что означаетweb-componentМожет использоваться везде, где используется HTML, независимо от фреймворка.

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

Что такое веб-компонент

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

Я лично считаю, что компонентизация является важной причиной быстрого развития и расширения интерфейса.Вспоминая время, когда был написан JQ, HTML-код в основном выглядел так:

<div>
    <div class="button1" >按钮</div>
    <div class="button2 active" >按钮</div>
    <div class="button3" >按钮</div>
</div>

Даже если синтаксис шаблона появится позже, ситуация не исправится (взаимодействуя с сервером для совместного использования шаблона -> JSP или для инкапсуляции метода в JS, инжект через синтаксис шаблона -> рули), простой рендеринг цикла может оказаться удобным Решить проблему повторного использования DOM, но повторное использование компонентов на разных уровнях все еще остается проблемой.

Таким образом, компонентизация является важным шагом для любой технологии фреймворка переднего плана, которая хочет развиваться дальше.Web-Componentэто возможность создавать повторно используемые элементы (пользовательские компоненты), изначально поддерживаемые браузером, иLitосновывается наWeb-Componentпостроен.

Тогда нам нужно сначала понять веб-компонент

Простой процесс разработки для веб-компонента

Create a class in which you specify your web component functionality, using the ECMAScript 2015 class syntax
Register your new custom element using the CustomElementRegistry.define() method, passing it the element name to be defined, the class or function in which its functionality is specified, and optionally, what element it inherits from.
If required, attach a shadow DOM to the custom element using Element.attachShadow() method. Add child elements, event listeners, etc., to the shadow DOM using regular DOM methods.
If required, define an HTML template using <template> and <slot> . Again use regular DOM methods to clone the template and attach it to your shadow DOM.
Use your custom element wherever you like on your page, just like you would any regular HTML element.

Давайте сначала разберемся в двух основных моментах веб-компонента.Поняв эти два свойства, вы можете создавать свои собственные компоненты.

Пользовательский элемент Пользовательский элемент

Во-первых, нам необходимо обеспечить**CustomElementRegistry******Экземпляр интерфейса регистрирует пользовательский элемент, и экземпляр монтируется вwindow.customElementsначальство

ПользовательскийЭлементРеестрdefineметод может использоваться для регистрации пользовательского элемента, есть два типа на выбор

  • пользовательский элемент: независимый элемент, поведение полностью определяется разработчиком.

  • Настройка встроенных элементов: эти элементы наследуют и расширяют встроенные элементы HTML.

API выглядит следующим образом

customElements.define(name, constructor, options);

nameимя вашего пользовательского элемента (в соответствии сDOMStringстандартный, должен иметь прочерк), взяв приведенный выше пример, вы можете передать<my-component></my-component>использовать в видеconstructorкомпонент, который мы определяемoptionsОбъявите, какой тип пользовательского элемента мы определяем, в настоящее время доступно только один продлен, указав какой элемент наследовать от. Когда вы определяете, какой элемент наследовать, метод использования не соответствует пользовательскому элементу, вам нужно использоватьisАтрибуты Предположим, вы определяете собственный встроенный элемент с именем my-paragraph, который наследуется от тега p, тогда при его использовании вам необходимо<p is="my-paragraph"></p>напиши вот так

В этом случае,Web-ComponentЯдро логики лежит в конструкторе.Как мы определяем наш собственный компонент? В отличие от React и Vue, вы можете написать JSX или синтаксис шаблона в методе рендеринга, чтобы создать дерево VDOM для определения структуры компонента.Web-ComponentОн не позволяет писать шаблоны (но это означает, что он может использовать любой синтаксис шаблона). Обычно вы используете обычные манипуляции с DOM для создания структуры компонентов в конструкторе. Возьмите каштан:

class MyComponent extends HTMLElement {
    constructor() {
      super()

      const wrapper = document.createElement('span');
      wrapper.setAttribute('class','wrapper');
      const info = document.createElement('span');
      info.setAttribute('class','info');
      info.textContent = this.getAttribute('text') || 'default';

      this.appendChild(wrapper);
      wrapper.appendChild(info);
    }
}

Приведенный выше код создает текстовый узел, вложенный в соответствии с двойным диапазоном.

<my-component></my-component>
<!-- 等价于 -->
<span class="wrapper"><span class="info">default</span></span>

<my-component text="test"></my-component>
<!-- 等价于 -->
<span class="wrapper"><span class="info">test</span></span>

Конечно, просто создавать UI смысла нет, давайте подумаем, какие еще возможности предоставляют современные фреймворки?

Отзывчивый! Привязка к событию! жизненный цикл!

Хорошо, организуй.

Давайте сначала поговорим о более простой привязке событий.Поскольку DOM создается внутри, привязывать события легко (обратите внимание на этот момент).

class extends HTMLElement {
    constructor() {
      super()
      this.handleClick = this.handleClick.bind(this)

      const wrapper = document.createElement('span');
      wrapper.setAttribute('class','wrapper');
      const info = document.createElement('span');
      info.setAttribute('class','info');
      info.textContent = this.getAttribute('text') || 'default';

      this.appendChild(wrapper);
      wrapper.appendChild(info);

      const button = this.querySelector('#button');
      button.addEventListener('click', this.handleClick)
    }

    handleClick () {
      this.parentNode.removeChild(this)
    }
}
<my-life-cycle-component>
  <button id='button'>Remove this</button>
</my-life-cycle-component>

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

Тогда давай поговоримlifecycle, Customeelements.define Принятый конструктор позволяет разработчикам определить следующие несколько жизненных циклов, его будет называться в соответствующее время

  • connectedCallback: вызывается, когда пользовательский элемент впервые вставляется в DOM документа.

  • disconnectedCallback: вызывается, когда пользовательский элемент удаляется из DOM документа.

  • adoptedCallback: вызывается, когда пользовательский элемент перемещается в новый документ.

  • attributeChangedCallback: вызывается, когда пользовательский элемент добавляет, удаляет или изменяет свои собственные атрибуты.

Проще говоря, это

  • componentDidMount

  • componentWillUnmount

  • Не существует (я не проверял какие-либо сценарии, которые появятся)

  • componentDidUpdate

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

Попутно будет выходить callback-ответ с изменениями атрибутов (разумеется, это только основа отзывчивости, изменения атрибутов напрямую не повлияют на внутреннюю логику рендеринга, умственную нагрузку+1) Если вам нужно инициировать функцию обратного вызова после изменения атрибута элемента, вы должны определитьobservedAttributes()получить функцию для прослушивания этого свойства

<my-life-cycle-component>
      <button id='button1'>Remove this</button>
      <button id='button2'>Toggle this</button>
      <input />
</my-life-cycle-component>

Глядя на код, здесь определяется компонент с полем ввода.После ввода поля ввода текст внутри компонента будет перерисован. Заодно прослушайте атрибут стиля, а после изменения распечатайте (бессмысленная демонстрация +1)

Тень DOM Тень DOM

Shadow DOMПреимущества многочисленны.Shadow DOMОсновная функция заключается в следующем: «Он может прикрепить к элементу отдельный DOM (тег стиля тоже принадлежит) (эта DOM скрыта) и не повлияет на внешний стиль, что дает очень мощные веб-компоненты. Возможность инкапсуляции, может полностью скрыть структуру, стиль и поведение компонента снаружи, и изолировать его снаружи»]

image-6-1624330780387.png

Есть понятие, которое требует внимания -Shadow boundary Shadow boundaryОтноситсяShadow DOMгде он заканчивается и где начинается обычный DOM. весьshadow domСтили и элементы внутри не повлияют на внешний мир больше, чемshadow boundaryспектр.

Shadow Dom не новая вещь - в течение длительного времени в прошлом браузеры использовали его для инкапсуляции внутренней структуры некоторых элементов. С помощью кнопки управления воспроизведением по умолчанию<video>элемент например. Все, что вы можете видеть, это<video>Фактически тег в своей Shadow DOM содержит ряд кнопок и других контроллеров. Стандарт Shadow DOM позволяет вам поддерживать набор Shadow DOM для ваших пользовательских элементов.Цитата из МДН

Итак, как его использовать? Есть основной API,Element.attachShadow({mode: }), теневой корень может быть присоединен к любому элементу, а значение режима открыто или закрыто. Разница в том, можно ли получить структуру shadowDOM извне. Для более глубокого понимания разницы между открытием и закрытием см.blog.Rev ill Web.com/open-vs-eat…Эта статья не будет описывать это здесь.

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

customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super()

      const shadow = this.attachShadow({mode: 'open'});
      const info = document.createElement('span');

      info.setAttribute('class','info');
      info.textContent = this.getAttribute('text') || 'default';

      // this.appendChild(info)
      shadow.appendChild(info);
    }
})

Это создает пользовательский элемент на основе shadowDOM. Он ничем не отличается. Давайте попробуем добавить некоторые пользовательские стили.

customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super()

      const shadow = this.attachShadow({mode: 'open'});
      const info = document.createElement('span');

      info.setAttribute('class','info');
      info.textContent = this.getAttribute('text') || 'default';

      const style = document.createElement('style')
      style.textContent = `
        span {
          color: red;
        }
      `
      // this.appendChild(info)
      shadow.appendChild(style);
      shadow.appendChild(info);

    }
})

Вы можете попробовать добавить несколько тегов span в другом месте на странице, но вы обнаружите, что только следующие теги span действуют с красным стилем.

Вышеупомянутые два свойства могут создать настраиваемый элемент (т.е. веб-компонент), на который не влияет внешнее влияние, который имеет внутреннюю логику выполнения JS и имеет независимый CSS. Я думаю, все начали жаловаться, такое JQ-подобное письмо просто неоднородно, и его трудно исправить после того, как DOM усложнится. Так как собрать более сложный дом, неужели нет решения? Нет, тогда есть еще одно свойство, о котором необходимо упомянуть здесь.

Template & Slot

Элемент шаблона — это функция, которую всегда поддерживали браузеры. Содержимое шаблона не будет отображаться при отображении HTML на экране. Прежде чем его можно будет создать и отобразить на странице, необходимо получить шаблон с помощью Javascript. Затем мы можем использовать шаблон как фрагмент контента, который можно сохранить в документе, а затем заполнить шаблонWeb-Componentизshadow domв. Или возьмите приведенный выше пример и измените его.

<template id="my-component">
    <style>
        span {
          color: red;
        }
    </style>
    <span class="info">
        12312312
    </span>
</template>
customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super()

      const template = document.getElementById('my-paragraph');
      const templateContent = template.content.cloneNode(true);
      const shadow = this
          .attachShadow({mode: 'open'})
          .appendChild(templateContent);      
    }
})

Таким образом, это означает что-то вроде Vue, но в чем недостаток?Свойство text может быть прочитано и заполнено в my-component раньше.span.infoВнутри, кажется, сейчас нет такой возможности.

В это время нам нужно попросить наш слот-слот сделать это

HTML<slot>элемент ,так какWeb ComponentsЧасть набора технологий, заполнитель в веб-компоненте. Позже этот заполнитель можно заполнить вашим собственным языком разметки, чтобы вы могли создать отдельное дерево DOM и объединить его с другими компонентами.

Слот идентифицируется атрибутом имени, а позиция размещения указывает на его позицию в шаблоне.Когда другой элемент определяет атрибут слота с тем же именем, этот элемент будет заменен в шаблоне. Давайте изменим пример выше

<!-- 使用 -->
<template id="my-component">
    <style>
        span {
          color: red;
        }
    </style>
    <span name="my-slot" class='info' >default</span>
</template>

<!-- 使用 -->
<my-component>
    <span slot="my-slot">
        12312312
    </span>
</my-component>
customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super()

      const template = document.getElementById('my-paragraph');
      const templateContent = template.content.cloneNode(true);
      const shadow = this
          .attachShadow({mode: 'open'})
          .appendChild(templateContent);      
    }
})

Конечно, это только самое простое использование, но пока в основномWeb-ComponentВы даже начинаете. Общая сложность написания не слишком высока, но есть еще много мест, на которые стоит пожаловаться.(подробнее о веб-компонентах). Итак, давайте посмотримLitЧто ты сделал, ты можешь позволитьWeb-Componentстать более удобным

что сделал Лит

Посмотрите, что мы только что сказалиWeb-ComponentНесколько недостатков в нем

  1. Отзывчивый имеет только обратные вызовы и не может быть автоматически сопоставлен с пользовательским интерфейсом.

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

  3. Нет шаблонного синтаксиса (можно использовать слот и шаблон)

Будьте ясны, учитесьLitВ процессе можно считать, что понятия состояния нет (собственно под ним понимаются частные реактивные свойства), есть только имяreactive propertiesсвойства члена. Его можно просто понимать как состояние и реквизит.

Итак, теперь проблема становится

  1. Как реагировать на изменения реактивных свойств и применять их к пользовательскому интерфейсу

  2. Как исправить синтаксис шаблона

Lit использует две основные библиотеки для решения этой проблемы, а именно:lit-elementа такжеlit-html

Lit-html

lit-htmlдаLitОсновная логика , может быть понята какLiteral Html, он создал еще один высокопроизводительный механизм HTML-шаблонов для потока символов, отличный от JSX. Lit решил напрямую унаследовать проект LitHTML от Polymer и переименовал общую структуру в Lit. мы знаемjsxЯвляется ли базовый слой, который необходимо скомпилировать в конечном итоге, илиcreateElement … а такжеlit-htmlне то же самое, оно основано наtagged template, заставляя его работать в браузере без компиляции, иHTML TemplateКомбинируя, как играть и как играть, способность расширения становится сильнее. Давайте расширим его.

lit-htmlПредоставляет два основных методаrenderа такжеhtml

lit-html.html

html`<p>${content}</p>`

Это собственный синтаксис es6 -Строка шаблона с тегами(шаблон с тегами), не волшебство, функция html примет следующие параметры

type taggedFunc = (strings: string[], ...values: any[]) => any;
// 上边的那个段代码接收到的参数就是
// ['<p>', '</p'>], content

проходить черезlit-htmlУкрашение приведенного выше кода в конечном итоге создастTemplate ResultОбъект в форме

declare class TemplateResult {
    readonly strings: TemplateStringsArray;
    readonly values: readonly unknown[];
    readonly type: string; // html or svg
    readonly processor: TemplateProcessor;
    constructor(strings: TemplateStringsArray, values: readonly unknown[], type: string, processor: TemplateProcessor);
    
    getHTML(): string;
    getTemplateElement(): HTMLTemplateElement;
}

const templateResult = {
    strings: ['<p>', '</p>'],
    value: [content]
    type: 'html'
}

Обратите внимание здесьgetHTMLа такжеgetTemplateElementметод, эти два метода могут преобразовывать строки в<template>Марк, который является шаблоном, упомянутым выше

const template = (title, content, className) => html`
  <h1 class="${className}">${title}</h1>
  <div>${content}</div>
`;
<template>
    <h1 class$lit$=\"{{lit-7227407027270176}}\"><!--{{lit-7227407027270176}}--></h1>
    <div><!--{{lit-7227407027270176}}--></div>
</template>

Чтобы кратко объяснить, этот процесс заключается в обработке данных в строках один за другим в соответствии с различными ситуациями.

  • Attribute

  • Node

  • Comment

Объединяется в полную строку, затем innerHTML вставляется в созданный тег шаблона. Q: Как отличить настоящие комментарии в коде?

lit-html.render

Теперь у нас есть то, что мы проходим через шаблон меткиTemplateResult(чистый объект-значение), рядом с вызовомrenderметод рендеринга шаблона на страницу, сначала посмотрите на APIrender(templateResult, container, options?) renderполучитьtemplateResult实例и рендеринг контейнера для завершения рендеринга, который делится на первый рендеринг и рендеринг обновления.

первый рендер

сначала создайтеNodePartObject (унаследован от Part, под которым можно понимать контроллер конструктора ноды, это реализация ядра, расширять пока не будем, увидим позже), а затем вызватьNodePartпримерappendIntoметод, добавьте два в контейнер рендерингаcomment, запись обоихcommentцитаты. следовать заNodePartположитDOMотдать этим двоимcommentсередина

<div id="container"><!---><!---></div>
<!-- 他是使用comment作为占位符的。 -->

тогда позвонюpart.commitметод, который отображает содержание в контейнер Commit делится на несколько ситуаций

  • directive

  • примитивный

  • templateResult

  • node

  • Iterable

  • пустой

По прежней логике, первый раз точно попадет сразу вtemplateResultВетвь , логика здесь может быть просто описана следующим образом, пройти черезFactory,использоватьTemplateResultРаздел шаблонов в строках создаетTemplateОбъект (промежуточный продукт)FactoryЗдесь выполняется слой кэширования, если вы используетеTemplateResultЕсли шаблон (строки) имеет существующий шаблон, используйте существующий шаблон напрямую, если нет, создайте его снова. При последующих вызовах метода рендеринга тот же самый шаблон (значение строки точно такое же, как и при первом вызове) повторно используется для первого шаблона, который можно понимать как постоянное значение, определяемое во время компиляции, и только значение изменения массива.

export declare class Template {
    readonly parts: TemplatePart[];
    readonly element: HTMLTemplateElement;
}
export type TemplatePart = {
  readonly type: 'node'; index: number;
} | {
  readonly type: 'attribute';
  index: number;
  readonly name: string;
  readonly strings: ReadonlyArray<string>;
};
  1. Сначала используйте шаблон TemplateResult (строка), чтобы узнать, есть ли готовый шаблон, если да, то повторно используйте его напрямую

  2. Если нет, проверьте, есть ли ссылка на template.join markerKey в шаблоне keyString (markKey означает lit-7227407027270176)

  3. Если нет, создайте экземпляр шаблона и кэшируйте шаблон, используя шаблон и keyString.

Процесс кеширования не поясняется, если интересно, посмотрите сами

TemplateОбъект разбивается на части и элемент, элемент конвертируется из TemplateResult<template>, часть частей, проходит<template>(dom walker) при создании. Упрощенное понимание процесса обработки

  • Если это узел узла

    • Определяем есть ли атрибут, а имя атрибута имеет специальную метку, если да, то убираем атрибут на шаблоне, и пропихиваем его в деталь{type: 'attribute', index, name, strings: statics}структура, индекс — это индекс текущего обходчика, имя — имя атрибута, строки — символы до и после интерполяции этого атрибута
  • Если это узел комментариев

    • Если содержание комментариев равен маркеру - (это можно отличить от реального комментария), затем нажимайте узел узла в деталь{type: 'node', index}

      • Если фронт - первый узел или узел уже является частью знака, сначала добавит пустой узел комментариев до текущего узла,
<template>
    <h1 class$lit$=\"{{lit-7227407027270176}}\"><!--{{lit-7227407027270176}}--></h1>
    <div><!--{{lit-7227407027270176}}--></div>
</template>

После обработки

{
    element: template
    parts: [
        {type: "attribute", index: 1, name: "class", strings: ["", ""]},
        {type: "node", index: 3},
        {type: "node", index: 7},
    ]
}
// templatee也会会简化成如下结构
<template>
  <h1><!----><!----></h1>
  <div><!----><!----></div>
</template>

понятныйTemplateявляется сформированнымDOMшаблон, у него есть полныйDOMи положение позиционирования, которое нужно интерполировать, но он еще не отрендерилсяDOMначальство

Далее проверьте текущийTemplateБыл ли он созданTemplateInstanceэкземпляр, если нет, создайте экземплярTemplateInstance

class TemplateInstance {
    private readonly __parts;
    readonly processor: TemplateProcessor;
    readonly options: RenderOptions;
    readonly template: Template;
    constructor(template: Template, processor: TemplateProcessor, options: RenderOptions);
    update(values: readonly unknown[]): void;
    _clone(): DocumentFragment;
}

TemplateInstance пройдет<template>Создайтеfragment; затем повторитеparts,согласно сTemplatePartЛитеральный тип, создайте экземпляры NodePart и AttributePart соответственно.

Последний вызовTemplateInstanceпримерupdateметод, который будет вызываться один за другимPartпримерsetValue(истинное значение) иcommit(метод рендеринга), в этот момент первый вызов метода рендеринга зацикливается, а остальные являются рекурсивными вызовами до тех пор, пока слой исходного типа значения не будет найден и отрендерен во фрагмент.

  • __commitText: изменить текст текстового узла напрямую

  • __commitNode: Пустой родительский узел от startNode до endNode (наиболее упоминаемый в начале двух заполнителей комментариев), а затем добавьте в узел.

При возврате на верхний уровеньcommitNodeсделать это полнымfragment, и поместите его в контейнер.

основной процесс

На данный момент завершен первый рендеринг, и общий процесс выглядит следующим образом

image-7-1624330788460.png

Это может показаться немного запутанным, мы можем пока игнорировать это.Template, что является промежуточным состоянием

TemplateResultпохоже наJSXБолее легкое буквальное описание шаблона является модельTemplateInstanceможно понимать как небольшойMVCвложение фреймов

  • DOM (фрагмент) — это внешняя структура приложения и постоянная часть представления.

  • TemplateResultЧленыvalueмодель

  • Вид и Модель связаны в Контроллере (Детали). Предоставляет метод обновления данных (setValue) и метод рендеринга в представлении (Commit).

обновление рендеринга

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

  1. Если попадется, используйте существующий шаблон, найдитеTemplateInstanceизPart,ПучокtemplateResultизvalueобновить доPart

  2. Если хита нет, переходим к первому процессу рендеринга

эффективность

  • 带标签的模版字符串выполнять по сравнению сJSXбудет более эффективным.JSXКаждый рендер требует полного построения виртуального DOM, иlit-html, он просто перестраивает очень легкий объект TemplateResult, и изменяется только набор значений.

  • Процесс от TemplateResult до

  • Фактический рендеринг DOM создается из template.importNode для завершения копии DOM. Вместо того, чтобы создавать один за другим узлы Dom, такие как React. Для больших DOM эффективность значительна.

  • В процессе инкрементного обновления Lit и React схожи тем, что повторно используются по одному и тому же уровню узлов.diff(VDOM, DOM)Для достижения инкрементного обновления LitHtml не использует алгоритм diff, а на основе рендеринга одного и того же шаблона необходимо обновлять только динамическую часть. Будет легче без алгоритма diff

Студенты, которые обратили внимание на статус Youda, возможно, видели, как что-то выходит, когда Vue 3 выпущен, vue-lit, vue-lit основан на механизме шаблонов lit-html и привязке данных @vue/reactivity Игрушка, созданная для будущего . Сам Lit также предоставляет пакет привязки данных, реагирующий на данные, для поддержки всей инфраструктуры.

Lit-element

Хорошо, синтаксис шаблона доступен, остальное — как применить изменение состояния в ответ на шаблон.

как использовать

Эта часть на самом деле несложная.Учащиеся, имеющие опыт разработки Vue, должны знать, как Vue связывает данные и представления.Lit-elementтоже так В Lit вам нужно объявить такой компонент

@customElement('simple-greeting')
export class SimpleGreeting extends LitElement { /* ..*/ }

customElement на самом деле является синтаксическим сахаром для customElement.defined , аLitElementдаLitПредоставляет базовый класс, в котором обрабатывается реактивная обработка данных (фактическиLitElementтакже унаследовалUpdateElement,Зависит отUpdateElementОтвечайте на обработку).

Reactivity Property

Давайте сначала посмотрим, как определить требования в документации Lit.reactivity property

class MyElement extends LitElement {
  @property()
  name: string;
}

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

  • геттер: взять напрямую

  • setter: запускать обновление после обновления

Каждый раз, когда он изменяется внутри компонентаreactive propertyКогда обновление свойства будет завершено, оно будет вызвано снова.lit-htmlМетод рендеринга отображает пользовательский интерфейс.

Это очень похоже на понятие государства, тогдаlit-elementИ как обрабатывать изменения внешних свойств транспорта (props)? Здесь нам нужно применить ранее упомянутоеWeb-Componentжизненный циклget observedAttributesа такжеattributeChangedCallback. Эти два цикла запускаются каждый раз, когда свойство, переданное компоненту, изменяется, и вам нужно только запросить, находится ли оно в_classProperties, и активно обновлятьreactive propertyВот и все. Кроме того, декоратор свойств также может принимать параметры для настройки некоторых свойств для адаптации.

  1. атрибут - определяет, привязана ли эта переменная-член к атрибуту элемента

  2. конвертер - определяет логику преобразования из атрибутов элемента (обе строки) в реальные атрибуты

  3. hasChanged — определяет, изменилось ли свойство

  4. type - используется, когда конвертер не определен, преобразует тип элемента

  5. Состояние — если состояние определено, эта переменная-член является внутренним состоянием, которое является частной, новой версией.LitДля переопределения этого свойства предоставляется отдельный декоратор @state.

LitОстальные, такие как декораторы, привязки событий и т. д., не будут прорабатываться.Заинтересованные студенты могут прочитать исходный код, который намного читабельнее, чем React (Doge). На этом этапе завершена полная и ориентированная на будущее интерфейсная структура.

резюме

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

Также есть проблема, которую нельзя применить в реальных сценариях: Lit все еще находится в стадии быстрой итерации, и каждый раз это большие переломные изменения. Например, только что упомянутый элемент UpdateElement был разделен на отдельный пакет. . . .