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 classNameFile name - Class name - Hash valueCSS 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