Tencent / omi
- пятница, 19 октября 2018 г. в 00:16:05
JavaScript
Next generation web framework in 4KB javascript(Web Components + JSX + Proxy + Path Updating)
English | 简体中文
Next generation web framework in 4KB javascript(Merge JSX, Web Components, Proxy, Path Updating together).
this.update
method when using store system! It will update partial UI automatically when data changed.Compare TodoApp by Omi and React, Omi and React rendering DOM structure:
On the left is Omi, the right side is React, and Omi uses Shadow DOM isolation style and semantic structure.
This page demonstrates using Omi with no build tooling.
<!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>
$ 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's auto-created project scaffolding is based on a single-page create-react-app to be converted into a multi-page one, with configuration issues to see create-react-app user guide
Define a custom 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>
)
}
}
Using hello-element
:
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')
Tell Babel to transform JSX into Omi.h () call:
{
"presets": ["env", "omi"]
}
The following two NPM packages need to be installed to support the above configuration:
"babel-preset-env": "^1.6.0",
"babel-preset-omi": "^0.1.1",
If you don't want to write CSS in js, you can use to-string-loader, For example, the following configuration:
{
test: /[\\|\/]_[\S]*\.css$/,
use: [
'to-string-loader',
'css-loader'
]
}
If your CSS file starts with "_", CSS will use to-string-loader., such as:
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
}
...
...
...
Here is a relatively complete example of 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 = ''
this.update()
}
}
render(<todo-app></todo-app>, 'body')
Say goodbye to this.update
method when using store system! It will update partial UI automatically when data changed. The powerful Store architecture is high-performance because all data is mounted on the store, except for components that rely on props to determine the state of the component.
export default {
data: {
items: [],
text: '',
firstName: 'dnt',
lastName: 'zhang',
fullName: function () {
return this.firstName + this.lastName
},
globalPropTest: 'abc', //Change it will refresh all elements without changing the components and page declaring data dependency.
ccc: { ddd: 1 } //Change it will refresh all elements without changing the components and page declaring data dependency.
},
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 = ''
}
//Default value is false, set to true will update all instances when data changing.
//updateAll: true
}
Custom Element requires declaring dependent data so that Omi stores compute the dependency path based on the data declared on the custom component and update it locally as needed. Such as:
class TodoApp extends WeElement {
//If you use store, the data is only used to declare dependencies.
static get data() {
return { items: [], text: '' }
}
...
...
...
handleChange = (e) => {
this.store.data.text = e.target.value
}
handleSubmit = (e) => {
e.preventDefault()
this.store.add()
}
}
You need to inject store from the root node at render time to use this. store:
render(<todo-app></todo-app>, 'body', store)
Summary:
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() |
I believe you can easily convert web components elements to omi elements.
Omi 4.0+ works in the latest two versions of all major browsers: Safari 10+, IE 11+, and the evergreen Chrome, Firefox, and Edge.
If you want to be compatible with IE11, use the Omi file of → this project.This project uses JSON Diff instead of Proxy.
MIT © Tencent
Please contact me@dntzhang for any questions.