The joining together of Omi –
The next generation of Web framework, to all things dregs, the essence of one
- – > https://github.com/Tencent/omi
- → Github Chinese Readme
features
- 4KB code size, smaller than small
- Go with the flow and be compliant with browser development and API design
- Webcomponents + JSX are integrated into one framework Omi
- Webcomponents can also be data driven view, UI = fn(data)
- JSX is the UI expression with the best development experience (smart hints) and the least syntax noise
- Original Path Updating mechanism, based on Proxy fully automated precise update, low power consumption, high freedom, excellent performance, easy to integrate requestIdleCallback
- There is no need to call this.udpate to use the Store system, which automatically updates local views as needed
- Looking at Facebook React vs. Web Components, Omi combines the strengths of each and gives developers the freedom to choose which way they like
- The Shadow DOM is integrated with the Virtual DOM. Omi uses both the Virtual DOM and the real Shadow DOM to update views more accurately and quickly
- Similar to WeStore system, 99.9% of projects do not need any time travel, and only Redux can time travel, please do not redux, Omi Store system can meet all projects
- The best solution for local CSS (Shadow DOM), the community has been fidgeting with many frameworks and libraries for local CSS (using JS or JSON styles such as Radium, jsxstyle, React-style; Use with webPack bindings to generate a unique className
File name - Class name - Hash value
CSS Modules, Vue), are hacks; Shadow DOM Style is the perfect solution
Compare the DOM structure rendered by TodoApp, Omi, and React:
With Omi on the left (top) and React on the right (bottom), Omi uses Shadow DOM to isolate styles and semantic structures.
- An HTML completely mastered
- Getting Started
- Install
- Hello Element
- TodoApp
- Store
- The life cycle
- The document
- My First Element
- Props
- Event
- Custom Event
- Ref
- Store System
- ecological
- Browser compatibility
- Links
- License
An HTML completely mastered
The following page does not require any build tools to execute
- Click here to see the results.
- Omi.js CDN
<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: (a)= > {
this.data.liked = true
this.update()
}
},
'Like'
)
}
}
define('like-button', LikeButton)
render(h('like-button'), 'body')
</script>
</body>
</html>
Copy the code
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
Copy the code
The Cli automatically creates project scaffolding from a single page creative-React-app to a multi-page one. For configuration issues, check out the Creative-react-app user guide.
Hello Element
Create 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>)}}Copy the code
Using this 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 >)}} to render (< my - app name = 'Omi v4.0' > < / my - app >, 'body')Copy the code
Tell Babel to convert JSX to a call to omi.h () :
{
"presets": ["env"."omi"]}Copy the code
The following two NPM packages need to be installed to support the above configuration:
"babel-preset-env": "^ 1.6.0." "."babel-preset-omi": ^ "while".Copy the code
If you don’t want to write CSS in JS, you can use a to-string-loader, such as:
{
test: /[\\|\/]_[\S]*\.css$/.use: [
'to-string-loader'.'css-loader']}Copy the code
If your CSS file starts with _, CSS will use a 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() {
returncssStr } ... . .Copy the code
TodoApp
Here’s 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 = '' } } render(<todo-app></todo-app>, 'body')Copy the code
Store
Using Store system can bid farewell to update method, based on Proxy automatic attribute tracking and update mechanism. The high performance is due to the powerful Store architecture. All data is mounted on the Store except for the component that uses 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'.// I will refresh all pages, and no more components and pages need to declare data dependencies
ccc: { ddd: 1 } // I will refresh all pages, and no more components and pages need to declare data dependencies
},
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 = ' '
}
// The default is false. True will mindlessly update all instances
//updateAll: true
}
Copy the code
A custom Element needs to declare the dependent data, so the Omi Store calculates the dependency path based on the data declared on the custom component and updates it locally as needed. Such as:
class TodoApp extends WeElement {
static get data() {
// If you are using store, this is just for declaring dependencies, as needed Path Updating
return { items: [].text: ' '}}... . . handleChange =(e) = > {
this.store.data.text = e.target.value
}
handleSubmit = (e) = > {
e.preventDefault()
this.store.add()
}
}
Copy the code
- The data logic is encapsulated in store-defined methods (such as store.add)
- The view is only responsible for passing data to the store (such as calling store.add or setting store.data.text above).
To use this. Store in all custom elements, you need to inject the store from the root node at Render:
render(<todo-app></todo-app>, 'body', store)
Copy the code
→ Store complete code
To summarize:
- Store.data is used to list all properties and default values (excluding components of the view determined by props).
- The data for components and pages is used to list the attributes of the dependent store.data (OMI records the path), updated as needed
- If the page is simple and has few components, you can set updateAll to true, and the components and pages do not need to declare data, so they will not be updated on demand
- The path declared in globalData will refresh all pages and components as soon as the value of the path is changed. GlobalData can be used to list all pages or the most common attribute path
The document
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')
Copy the code
Take a look at the rendered structure in the HTML Developer tools:
In addition to rendering to the body, you can use my-first-Element in any custom 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')
Copy the code
You can also pass any type of data to props:
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')
Copy the code
My-obj will map to myObj, hump way.
Event
class MyFirstElement extends WeElement {
onClick = (evt) = > {
alert('Hello Omi! ')
}
render() {
return (
<h1 onClick={this.onClick}>Hello, wrold!</h1>)}}Copy the code
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')
Copy the code
Fires a custom event with this.fire. The first argument to fire is the event name and the second argument is the data passed. The data passed can be obtained through 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')
Copy the code
Add ref={e => {this.anynameYouwant = e}} to the element, and then you can access the element using this.anynameYouwant in your JS code.
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 = (a)= > {
//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)
Copy the code
When using the Store system, static get data is used only to declare dependencies. For example:
static get data() {
return {
a: null.b: null.c: { d: []},e: []}}Copy the code
Will be converted to:
{
a: true.b: true.'c.d':true.e: true
}
Copy the code
For example, the Path matching rule is as follows:
Proxy Path | updatePath | Whether or not to update |
---|---|---|
abc | abc | update |
abc[1] | abc | update |
abc.a | abc | update |
abc | abc.a | Don’t update |
abc | abc[1] | Don’t update |
abc | abc[1].c | Don’t update |
abc.b | abc.b | update |
The above can be updated as long as one condition is hit!
UpdatePath = updatePath = updatePath = updatePath = updatePath = updatePath = updatePath = updatePath = updatePath
You can see that the store system is a centralized system, right? So how do you decentralize some components? Use the second argument of tag:
@tag('my-first-element'.true)
Copy the code
Pure element! No store injection!
The life cycle
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() |
ecological
- www.webcomponents.org/
- www.webcomponents.org/elements
Find the component you want and use it directly, or convert it to an Omi Element in a few minutes (copy the template to render, copy style to CSS).
Browser compatibility
Omi 4.0+ works in the latest two versions of all major browsers: Safari 10+, IE 11+, and the evergreen Chrome, Firefox, and Edge.
– > polyfills
Because of the need to use Proxy reasons, give up IE! (since some IE11 is the bottom line of the project, so the support IE11) has support IE11 – > https://github.com/Tencent/omi/tree/master/packages/omi-ie11
Star & Fork
- Github
- omijs.org
License
MIT © Tencent