Оми - Единство
Веб-фреймворк следующего поколения
характеристика
- Размер кода 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.
Откажитесь от IE из-за необходимости использования прокси!(Поскольку в некоторых проектах IE11 является практическим результатом, IE11 скоро будет поддерживаться)
IE11 поддерживается→ https://github.com/Tencent/omi/tree/master/packages/omi-ie11
Звезда и вилка
License
MIT © Tencent