omi README

The project's README from github.com/Tencent/omi. As one markdown file: README.md.

English | 简体中文

omi

Omi - Web Components Framework

import { render, signal, tag, Component, h } from 'omi'

const count = signal(0)

function add() {
  count.value++
}

function sub() {
  count.value--
}

@tag('counter-demo')
export class CounterDemo extends Component {
  static css = 'span { color: red; }'

  render() {
    return (
      <>
        <button onClick={sub}>-</button>
        <span>{count.value}</span>
        <button onClick={add}>+</button>
      </>
    )
  }
}

Use this component:

import { h } from 'omi'
import './counter-demo'

render(<counter-demo />, document.body)

// or 
import { CounterDemo, Other } from './counter-demo'
// Prevent tree Shaking when importing other things
render(<CounterDemo />, document.body)

// or
document.body.appendChild(document.createElement('counter-demo'))

Install

npm i omi

To quickly create an Omi + Vite + TS/JS project:

$ npx omi-cli init my-app    # or create js project by: npx omi-cli init-js my-app
$ cd my-app           
$ npm start           # develop
$ npm run build       # release

To quickly create an Omi + Router + Signal + Suspense + Tailwindcss + Vite + TS project:

$ npx omi-cli init-spa my-app  
$ cd my-app           
$ npm start           # develop
$ npm run build       # release

Packages

If you want to help the project grow, start by simply sharing it with your peers!

Thank you!

Usage

TodoApp with reactivity functions

Data oriented programming

In data-oriented programming, the focus is on the data itself and the operations on the data, rather than the objects or data structures that hold the data. This programming paradigm emphasizes the change and flow of data, and how to respond to these changes. The TodoApp with reactivity functions is a good example of this, using the concepts of reactive programming, where the UI automatically updates to reflect changes in the data (i.e., the to-do list).

import { render, signal, computed, tag, Component, h } from 'omi'

const todos = signal([
  { text: 'Learn OMI', completed: true },
  { text: 'Learn Web Components', completed: false },
  { text: 'Learn JSX', completed: false },
  { text: 'Learn Signal', completed: false }
])

const completedCount = computed(() => {
  return todos.value.filter(todo => todo.completed).length
})

const newItem = signal('')

function addTodo() {
  // api a
  todos.value.push({ text: newItem.value, completed: false })
  todos.update() // Trigger UI auto update
  
  // api b, same as api a
  // todos.value = [...todos.value, { text: newItem.value, completed: false }]

  newItem.value = '' // Changing the value type can automatically update the UI
}

function removeTodo(index: number) {
  todos.value.splice(index, 1)
  todos.update() // Trigger UI auto update
}

@tag('todo-list')
class TodoList extends Component {
  onInput = (event: Event) => {
    const target = event.target as HTMLInputElement
    newItem.value = target.value
  }

  render() {
    return (
      <>
        <input type="text" value={newItem.value} onInput={this.onInput} />
        <button onClick={addTodo}>Add</button>
        <ul>
          {todos.value.map((todo, index) => {
            return (
              <li>
                <label>
                  <input
                    type="checkbox"
                    checked={todo.completed}
                    onInput={() => {
                      todo.completed = !todo.completed
                      todos.update() // Trigger UI auto update
                    }}
                  />
                  {todo.completed ? <s>{todo.text}</s> : todo.text}
                </label>
                {' '}
                <button onClick={() => removeTodo(index)}>❌</button>
              </li>
            )
          })}
        </ul>
        <p>Completed count: {completedCount.value}</p>
      </>
    )
  }
}

render(<todo-list />, document.body)

Auto Import h

vite.config.js:

import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    jsxInject: "import { h } from 'omi'",
    jsxFactory: "h",
    jsxFragment: "h.f"
  }
})

You can inject code during construction, so you don't have to manually export h.

Define Cross Framework Component

The case of using Omi component in Vue is as follows:

my-counter.tsx:

import { tag, Component, h, bind } from 'omi'

@tag('my-counter')
class MyCounter extends Component {
  static props = {
    count: {
      type: Number,
      default: 0,
      changed(newValue, oldValue) {
        this.state.count = newValue
        this.update()
      }
    }
  }

  state = {
    count: null
  }

  install() {
    this.state.count = this.props.count
  }

  @bind
  sub() {
    this.state.count--
    this.update()
    this.fire('change', this.state.count)
  }

  @bind
  add() {
    this.state.count++
    this.update()
    this.fire('change', this.state.count)
  }

  render() {
    return (
      <>
        <button onClick={this.sub}>-</button>
        <span>{this.state.count}</span>
        <button onClick={this.add}>+</button>
      </>
    )
  }
}

Using in Vue3

<script setup>
import { ref } from 'vue'
// import omi component
import './my-counter'

defineProps({
  msg: String,
})

const count = ref(0)

const change = (e) => {
  count.value = e.detail
}

</script>

<template>
  <h1>{{ msg }}</h1>

  <my-counter @change="change" :count="count" />
  <p>
    【Omi】 
  </p>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
     【Vue】 
    </p>
  </div>

</template>

If you fire the count-change in an Omi component:

this.fire('count-change', this.state.count)

To use the component and listen for events in Vue:

<my-counter @count-change="change" :count="count" />

Using in React

import { useState, useRef, useEffect } from 'react'
import useEventListener from '@use-it/event-listener'
import './my-counter'

function App() {
  const [count, setCount] = useState(100)
  const myCounterRef = useRef(null)

  useEffect(() => {
    const counter = myCounterRef.current
    if (counter) {
      const handleChange = (evt) => {
        setCount(evt.detail)
      }
      counter.addEventListener('change', handleChange)
      return () => {
        counter.removeEventListener('change', handleChange)
      }
    }
  }, [])

  return (
    <>
      <h1>Omi + React</h1>
      <my-counter count={count} ref={myCounterRef}></my-counter>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  )
}

export default App

Contributors

License

MIT © Tencent

omi (Tencent/omi)

Searching Database…

13.3K ·GitHub
Что это такое?Omi — это лёгкий фреймворк для Web Components от Tencent, который добавляет JSX, функциональные компоненты, маршрутизацию, Suspense, директивы и реактивность на сигналах поверх нативных пользовательских элементов и Shadow DOM.
Это фреймворк вроде React или просто тонкая обёртка над нативными пользовательскими элементами?Это полноценный фреймворк на основе нативных Web Components: он компилируется в настоящие пользовательские элементы с Shadow DOM, но добавляет JSX, маршрутизатор, Suspense, директивы и поддержку Tailwind, а компоненты можно переиспользовать в React, Vue и обычном HTML.
Как начать новый проект с Omi?Используйте Omi CLI, чтобы создать проект: либо базовое приложение Omi + Vite + TS/JS, либо более полную настройку Omi + Router + Signal + Suspense + Tailwindcss + Vite + TS.
Нужны ли Vite или TypeScript для работы с Omi?Стартовые наборы построены на Vite и TypeScript или JavaScript, с необязательной интеграцией Tailwind CSS, но сам базовый пакет 'omi' можно использовать в любой сборке.
Обязателен ли JSX для написания компонентов Omi?Нет, Omi поддерживает как объектно-ориентированные классовые компоненты, так и функциональные компоненты на основе данных, поэтому стиль JSX/ООП и стиль простых реактивных данных и функций одинаково допустимы.
Где работает компонент Omi?Компоненты Omi работают в любом современном браузере как нативные пользовательские элементы, поэтому их можно использовать отдельно или встраивать в страницы React, Vue или обычного HTML.
Можно ли использовать компоненты Omi внутри приложения на React или Vue?Да, ведь Omi компилируется в стандартные пользовательские элементы: в README показаны примеры определения компонента и его использования прямо внутри Vue 3 с подпиской на его пользовательские события, то же относится и к React.
Какие готовые UI-наборы и шаблоны поставляет Omi?Omi предоставляет TDesign Web Components, UI-набор Omi Elements на основе Tailwind, более 100 шаблонов Omi Templates, библиотеку форм Omi Form и привязки иконок Lucide для Omi.
Как Omi работает с общими стилями между компонентами?Он использует Constructable Stylesheets — API браузера, которое позволяет нескольким компонентам с Shadow DOM эффективно использовать и управлять одним и тем же объектом таблицы стилей.
Что такое reactive-signal в Omi?Это система реактивности на сигналах, которая лежит в основе стиля программирования Omi, ориентированного на данные: интерфейс автоматически обновляется в ответ на изменения сигналов, как показано в примере TodoApp.
Creator
Tencent
Size
156 MB
Stars
13.3K
Age
11y
Commits · 90 Days