Tencent выпускает Omi, интерфейсную структуру компонентов, полностью охватывающую веб-компоненты.

внешний интерфейс Тенсент React.js CSS Omi
Tencent выпускает Omi, интерфейсную структуру компонентов, полностью охватывающую веб-компоненты.

Оми - Единство

Веб-фреймворк следующего поколения

omi

характеристика

  • Размер кода 4 КБ, меньше, чем маленький
  • Плывите по течению, следите за разработкой браузеров и дизайном API
  • Веб-компоненты + JSX сливаются в один фреймворк Оми
  • Веб-компоненты также могут использовать представления, управляемые данными, UI = fn(data)
  • JSX — лучший опыт разработки (умные советы),Минимальный синтаксический шумвыражения пользовательского интерфейса для
  • Оригинальный механизм обновления пути, основанный на полностью автоматизированном и точном обновлении прокси, отличается низким энергопотреблением, высокой степенью свободы, отличной производительностью и простотой интеграции requestIdleCallback.
  • Использование системы хранения не требует вызова this.udpate, оно автоматически обновляет частичное представление по мере необходимости.
  • посмотриПреимущества Facebook React и веб-компонентов, Omi сочетает в себе преимущества каждого из них и дает разработчикам свободу выбора того, что им нравится.
  • Shadow DOM и Virtual DOM интегрированы, Omi использует как виртуальный DOM, так и реальный Shadow DOM, чтобы сделать обновления представлений более точными и быстрыми.
  • Подобно системе WeStore, 99,9% проектов не нуждаются в путешествиях во времени, и не только редуксы могут путешествовать во времени, пожалуйста, не придумывайте редуксы, система магазинов Omi может удовлетворить все проекты.
  • Частичное лучшее решение CSS (Shadow Dom), сообщество для локального CSS, выбрасывающее множество фреймов и библиотек (с использованием стилей записи JS или JSON, таких как: Radium, JSxStyle, React-Style; привязка с WebPack) Создание уникального имени класса文件名—类名—hash值, такие как: CSS Modules, Vue), являются хак-технологиями; Shadow DOM Style — самое идеальное решение

Сравнение структуры DOM, отображаемой Omi и React при разработке TodoApp:

Левая (верхняя) сторона — это Omi, правая (нижняя) сторона — это React, Omi использует Shadow DOM для изоляции стиля и семантической структуры.


HTML для начала

Следующая страница может быть выполнена без каких-либо инструментов сборки

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>Add Omi in One Minute</title>
</head>

<body>
  <script src="https://unpkg.com/omi"></script>
  <script>
    const { WeElement, h, render, define } = Omi

    class LikeButton extends WeElement {
      install() {
        this.data = { liked: false }
      }

      render() {
        if (this.data.liked) {
          return 'You liked this.'
        }

        return h(
          'button',
          {
            onClick: () => {
              this.data.liked = true
              this.update()
            }
          },
          'Like'
        )
      }
    }

    define('like-button', LikeButton)

    render(h('like-button'), 'body')
  </script>
</body>

</html>

Getting Started

Install

$ npm i omi-cli -g               # install cli
$ omi init your_project_name     # init project, you can also exec 'omi init' in an empty folder
$ cd your_project_name           # please ignore this command if you executed 'omi init' in an empty folder
$ npm start                      # develop
$ npm run build                  # release

Скаффолдинг проекта, автоматически созданный Cli, основан на преобразовании одностраничного приложения create-реагировать в многостраничное, можно просмотреть проблемы с конфигурацией.Руководство пользователя приложения create-реагировать.

Hello Element

Сначала создайте пользовательский элемент:

import { tag, WeElement, render } from 'omi'

@tag('hello-element')
class HelloElement extends WeElement {

    onClick = (evt) => {
        //trigger CustomEvent
        this.fire('abc', { name : 'dntzhang', age: 12 })
        evt.stopPropagation()
    }

    css() {
        return `
         div{
             color: red;
             cursor: pointer;
         }`
    }

    render(props) {
        return (
            <div onClick={this.onClick}>
                Hello {props.msg} {props.propFromParent}
                <div>Click Me!</div>
            </div>
        )
    }   
}

Используйте этот элемент:

import { tag, WeElement, render } from 'omi'
import './hello-element'

@tag('my-app')
class MyApp extends WeElement {
    static get data() {
        return { abc: '', passToChild: '' }
    }

    //bind CustomEvent 
    onAbc = (evt) => {
        // get evt data by evt.detail
        this.data.abc = ' by ' + evt.detail.name
        this.update()   
    }

    css() {
        return `
         div{
             color: green;
         }`
    }

    render(props, data) {
        return (
            <div>
                Hello {props.name} {data.abc}
                <hello-element onAbc={this.onAbc} prop-from-parent={data.passToChild} msg="WeElement"></hello-element>
            </div>
        )
    }
}

render(<my-app name='Omi v4.0'></my-app>, 'body')

Скажите Babel перевести JSX в вызов Omi.h() :

{
    "presets": ["env", "omi"]
}

Следующие два пакета npm должны быть установлены для поддержки вышеуказанной конфигурации:

"babel-preset-env": "^1.6.0",
"babel-preset-omi": "^0.1.1",

Если вы не хотите писать css в js, вы можете использоватьto-string-loader, Например, следующие конфигурации:

{
    test: /[\\|\/]_[\S]*\.css$/,
    use: [
        'to-string-loader',
        'css-loader'
    ]
}

Если ваш файл css начинается с_Вначале css будет использовать загрузчик to-string, например:

import { tag, WeElement render } from 'omi'
//typeof cssStr is string
import cssStr from './_index.css' 

@tag('my-app')
class MyApp extends WeElement {

  css() {
    return cssStr
  }
  ...
  ...
  ...

TodoApp

Вот пример относительно полного TodoApp:

import { tag, WeElement, render } from 'omi'

@tag('todo-list')
class TodoList extends WeElement {
    render(props) {
        return (
            <ul>
                {props.items.map(item => (
                    <li key={item.id}>{item.text}</li>
                ))}
            </ul>
        );
    }
}

@tag('todo-app')
class TodoApp extends WeElement {
    static get data() {
        return { items: [], text: '' }
    }

    render() {
        return (
            <div>
                <h3>TODO</h3>
                <todo-list items={this.data.items} />
                <form onSubmit={this.handleSubmit}>
                    <input
                        id="new-todo"
                        onChange={this.handleChange}
                        value={this.data.text}
                    />
                    <button>
                        Add #{this.data.items.length + 1}
                    </button>
                </form>
            </div>
        );
    }

    handleChange = (e) => {
        this.data.text = e.target.value
    }

    handleSubmit = (e) => {
        e.preventDefault();
        if (!this.data.text.trim().length) {
            return;
        }
        this.data.items.push({
            text: this.data.text,
            id: Date.now()
        })
        this.data.text = ''
    }
}

render(<todo-app></todo-app>, 'body')

Store

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

export default {
  data: {
    items: [],
    text: '',
    firstName: 'dnt',
    lastName: 'zhang',
    fullName: function () {
      return this.firstName + this.lastName
    },
    globalPropTest: 'abc', //更改我会刷新所有页面,不需要再组件和页面声明data依赖
    ccc: { ddd: 1 } //更改我会刷新所有页面,不需要再组件和页面声明data依赖
  },
  globalData: ['globalPropTest', 'ccc.ddd'],
  add: function () {
    if (!this.data.text.trim().length) {
        return;
    }
    this.data.items.push({
      text: this.data.text,
      id: Date.now()
    })
    this.data.text = ''
  }
  //默认 false,为 true 会无脑更新所有实例
  //updateAll: true
}

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

class TodoApp extends WeElement {
    static get data() {
        //如果你用了 store,这个只是用来声明依赖,按需 Path Updating
        return { items: [], text: '' }
    }
    ...
    ...
    ...
    handleChange = (e) => {
        this.store.data.text = e.target.value
    }

    handleSubmit = (e) => {
        e.preventDefault()
        this.store.add()
    }
}
  • Логика данных инкапсулирована в методах, определенных хранилищем (например, store.add).
  • Представление отвечает только за передачу данных в хранилище (например, вызов store.add выше или настройка store.data.text).

Хранилище необходимо внедрить из корневого узла во время рендеринга, чтобы использовать this.store во всех пользовательских элементах:

render(<todo-app></todo-app>, 'body', store)

→ Сохранить полный код

В заключение:

  • store.data используется для перечисления всех свойств и значений по умолчанию (за исключением компонентов представления, определяемых реквизитами)
  • Данные компонентов и страниц используются для перечисления атрибутов зависимого store.data (omi будет записывать путь) и обновлять его по мере необходимости.
  • Если на странице мало простых компонентов, для updateAll можно установить значение true, а компоненты и страницы не должны объявлять данные, поэтому они не будут обновляться по требованию.
  • Путь, объявленный в globalData, при изменении значения соответствующего пути все страницы и компоненты будут обновлены. globalData можно использовать для перечисления всех страниц или наиболее распространенных атрибутов.

Документация

My First Element

import { WeElement, tag, render } from 'omi'

@tag('my-first-element')
class MyFirstElement extends WeElement {
    render() {
        return (
            <h1>Hello, world!</h1>
        )
    }
}

render(<my-first-element></my-first-element>, 'body')

Взгляните на визуализированную структуру в Инструментах разработчика HTML:

Помимо рендеринга в тело, вы можете использовать его в любом другом пользовательском элементе.my-first-element.

Props

import { WeElement, tag, render } from 'omi'

@tag('my-first-element')
class MyFirstElement extends WeElement {
    render(props) {
        return (
            <h1>Hello, {props.name}!</h1>
        )
    }
}

render(<my-first-element name="world"></my-first-element>, 'body')

Вы также можете передавать любой тип данных в реквизит:

import { WeElement, tag, render } from 'omi'

@tag('my-first-element')
class MyFirstElement extends WeElement {
    render(props) {
        return (
            <h1>Hello, {props.myObj.name}!</h1>
        )
    }
}

render(<my-first-element my-obj={{ name: 'world' }}></my-first-element>, 'body')

my-objСопоставит с myObj, camelCase.

Event

class MyFirstElement extends WeElement {
    onClick = (evt) => {
        alert('Hello Omi!')
    }

    render() {
        return (
            <h1 onClick={this.onClick}>Hello, wrold!</h1>
        )
    }
}

Custom Event

@tag('my-first-element')
class MyFirstElement extends WeElement {
    onClick = (evt) => {
        this.fire('myevent', { name: 'abc' })
    }

    render(props) {
        return (
            <h1 onClick={this.onClick}>Hello, world!</h1>
        )
    }
}

render(<my-first-element onMyEvent={(evt) => { alert(evt.detail.name) }}></my-first-element>, 'body')

пройти черезthis.fireДля запуска пользовательского события первым параметром fire является имя события, а вторым параметром — передаваемые данные. пройти черезevt.detailПередаваемые данные могут быть получены.

Ref

@tag('my-first-element')
class MyFirstElement extends WeElement {
    onClick = (evt) => {
        console.log(this.h1)
    }

    render(props) {
        return (
            <div>
                <h1 ref={e => { this.h1 = e }} onClick={this.onClick}>Hello, world!</h1>
            </div>
        )
    }
}

render(<my-first-element></my-first-element>, 'body')

добавить элементref={e => { this.anyNameYouWant = e }}, то вы можете использовать в коде JSthis.anyNameYouWantполучить доступ к элементу.

Store System

import { WeElement, tag, render } from 'omi'

@tag('my-first-element')
class MyFirstElement extends WeElement {
    //You must declare data here for view updating
    static get data() {
        return { name: null }
    }
    
    onClick = () => {
        //auto update the view
        this.store.data.name = 'abc'
    }

    render(props, data) {
        //data === this.store.data when using store stystem
        return (
            <h1 onClick={this.onClick}>Hello, {data.name}!</h1>
        )
    }
}

const store = {
    data: { name: 'Omi' }
}
render(<my-first-element name="world"></my-first-element>, 'body', store)

При использовании системы магазиновstatic get dataиспользуется только для объявления зависимостей, например:

static get data() {
    return {
        a: null,
        b: null,
        c: { d: [] },
        e: []
    }
}

будет преобразовано в:

{
  a: true,
  b: true,
  'c.d':true,
  e: true
}

Пример, иллюстрирующий правило совпадения пути:

Proxy Path updatePath обновлять ли
abc abc возобновить
abc[1] abc возобновить
abc.a abc возобновить
abc abc.a не обновлять
abc abc[1] не обновлять
abc abc[1].c не обновлять
abc.b abc.b возобновить

Вышеупомянутое может быть обновлено до тех пор, пока выполняется одно условие!

Сводка должна обновляться до тех пор, пока она равна updatePath или ниже дочернего узла updatePath!

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

@tag('my-first-element', true)

Чистые элементы! не будет вводиться в магазин!

Жизненный цикл

Lifecycle method When it gets called
install before the component gets mounted to the DOM
installed after the component gets mounted to the DOM
uninstall prior to removal from the DOM
beforeUpdate before render()
afterUpdate after render()

экология

Найдите в нем нужный компонент, используйте его напрямую или преобразуйте в Omi Element за несколько минут (скопируйте шаблон в метод рендеринга и скопируйте стиль в метод css).

Совместимость с браузером

Omi 4.0+ works in the latest two versions of all major browsers: Safari 10+, IE 11+, and the evergreen Chrome, Firefox, and Edge.

Browsers Support

→ полифиллы

Откажитесь от IE из-за необходимости использования прокси!(Поскольку в некоторых проектах IE11 является практическим результатом, IE11 скоро будет поддерживаться) IE11 поддерживается→ https://github.com/Tencent/omi/tree/master/packages/omi-ie11

Звезда и вилка

License

MIT © Tencent