Skip to content

Latest commit

 

History

History
888 lines (699 loc) · 33.7 KB

README.CN.md

File metadata and controls

888 lines (699 loc) · 33.7 KB

English | 简体中文 | 한국어

v5.0.21

omi

Omi - 下一代 Web 框架,去万物糟粕,合精华为一点点 JS

基于 Web Components 并使用 omio 兼容老浏览器(IE8+)

特性

  • 小巧的尺寸
  • 拥有官方 UI 组件库 - omiu
  • 使用 omio 可以兼容到 IE8
  • 真正的 MVVM, 拥有 mappingjs 强力支持
  • 支持 TypeScript
  • 响应式数据绑定
  • 增强了 CSS, 支持 rpx 单位,基于 750 屏幕宽度
  • 基于 Shadow Dom 设计
  • 利用Chrome 开发工具扩展 轻松调试,从 Chrome 应用商店安装
  • 符合浏览器的发展趋势以及API设计理念
  • Web Components + JSX 相互融合为一个框架 Omi
  • Web Components 也可以数据驱动视图, UI = fn(data)
  • JSX 是开发体验最棒(智能提示)、语法噪音最少、图灵完备的 UI 表达式,模板引擎不完备,模板字符串完备但是语法噪音太大
  • 看看Facebook React 和 Web Components对比优势,Omi 融合了各自的优点,而且给开发者自由的选择喜爱的方式
  • Shadow DOMVirtual DOM 融合,Omi 既使用了虚拟 DOM,也是使用真实 Shadow DOM,让视图更新更准确更迅速
  • 局部 CSS 最佳解决方案(Shadow DOM),社区为局部 CSS 折腾了不少框架和库(使用js或json写样式,如:Radiumjsxstylereact-style;与webpack绑定使用生成独特的className文件名—类名—hash值,如:CSS ModulesVue),还有运行时注入scoped atrr 的方式,都是 hack 技术;Shadow DOM Style 是最完美的方案

对比同样开发 TodoApp, Omi 和 React 渲染完的 DOM 结构,Omi 使用 Shadow DOM 隔离样式和语义化结构:

Omi React
Omi React

Omi 生态

→ Omi 生态学习路线图

项目 描述
omi-docs Omi 官方文档
omio 兼容老浏览器的 Omi 版本(支持到IE8+和移动端浏览器)。
omiu Omi 官方 UI。
omi-mp-create 极小却精巧的小程序框架。
md2site 用 markdown 生成静态网站文档.
omi-mvvm MVVM 王者归来, mappingjs 强力加持。
omi-chart 一个 chart-x 标签搞定报表
mp-mvvm 小程序插上 MVVM 的翅膀, mappingjs 强力加持。
omi-html Using htm in omi.
omi-30-seconds 30 秒理解一段有用的 Omi 代码片段.
omi-sprite Web Components, JSX 和 Canvas 的完美融合
omi-canvas Web Components, JSX 和 Canvas 的完美融合
omi-mp 通过微信小程序开发和生成 Web 单页应用(H5 SPA)
omi-router Omi 官方路由,超级小的尺寸,只有 1KB 的 js。→ DEMO
omi-devtools 谷歌浏览器开发工具扩展
omi-cli 项目脚手架工具,各种模板任你选。
omi-ex Omi.js 扩展(TypeScript)
omi-transform Omi 和 css3transform 完美结合. 让 css3 transform 在你的 Omi项目中变得超级简单.
omi-tap Omi 原生支持 tap 事件(omi v4.0.24+)
omi-finger Omi 官方手势库
omi-touch 丝般顺滑的触摸运动
omi-use 跟 React hooks 类似的方式定义纯组件
omi-native 把 web components 渲染到 native,比如 IOS 、Android
omi-i18n Omi 国际化解决方案
omi-page 基于 page.js 的 Omi 路由

omi-mp

通过微信小程序开发和一键生成 Web 的 H5 SPA (基于 omi + omi-router)

看下官方模板的转换例子:

Index Logs

因为 Web 里拉取不到用户登录态,更换了用户头像和名称。

必须收藏的资源


目录

一个 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

    define('like-button',
      class 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'
          )
        }
      })

    render(h('like-button'), 'body')
  </script>
</body>

</html>

通过上面脚本的执行,你已经定义好了一个自定义标签,可以不使用 render 方法,直接使用 like-button 标签:

<body>
    <like-button></like-button>
</body>

再花 30 秒完全上手

你也可以使用现代化的 JS 语法,快速构建 Omi 项目:

import { render, WeElement, define } from 'omi'

define('my-counter', class extends WeElement {
    static observe = true
    
    css () {
      return `span{
        color: red;
      }`
    }

    data = {
      count: 1
    }

    sub = () => {
      this.data.count--
    }

    add = () => {
      this.data.count++
    }

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

render(<my-counter />, 'body')

也可以手动调用 this.update,这样你就可以选择最佳的时机进行更新,而且能够兼容 IE8。

import { render, WeElement, define } from 'omi'

define('my-counter', class extends WeElement {
  count = 1

  //也可以直接传递字符串
  css = `span{
        color: red;
      }`

  sub = () => {
    this.count--
    this.update()
  }

  add = () => {
    this.count++
    this.update()
  }

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

render(<my-counter />, 'body')

→ counter demo

快速入门

安装

$ npm i omi-cli -g               # install cli
$ omi init my-app     # init project, you can also exec 'omi init' in an empty folder
$ cd my-app           # please ignore this command if you executed 'omi init' in an empty folder
$ npm start                      # develop
$ npm run build                  # release

npx omi-cli init my-app 也支持(要求 npm v5.2.0+)

目录说明:

├─ config
├─ public
├─ scripts
├─ src
│  ├─ assets
│  ├─ elements    //存放所有 custom elements
│  ├─ store       //存放所有页面的 store
│  ├─ admin.js    //入口文件,会 build 成  admin.html
│  └─ index.js    //入口文件,会 build 成  index.html

Scripts

"scripts": {
    "start": "node scripts/start.js",
    "build": "PUBLIC_URL=. node scripts/build.js",
    "build-windows": "set PUBLIC_URL=.&& node scripts/build.js",
    "fix": "eslint src --fix"
}

你也可以设置 PUBLIC_URL, 比如:

...
"build": "PUBLIC_URL=https://fe.wxpay.oa.com/dv node scripts/build.js",
"build-windows": "set PUBLIC_URL=https://fe.wxpay.oa.com/dv&& node scripts/build.js",
...

切换 omi 和 omio

增加或删除 package.json 里的 alias config 可以切换 omi 和 omio 渲染:

 ...
 "alias": {
    "omi": "omio"
  }
  ...

项目模板

Template Type Command Describe
基础模板(v3.3.0+) omi init my-app 基础模板,支持 omi 和 omio(IE8+)
支持预渲染快照骨架的模板 omi init-snap my-app 基础模板,支持 omi 和 omio(IE8+),内置预渲染
TypeScript Template(omi-cli v3.3.0+) omi init-ts my-app 使用 TypeScript 的模板
Mobile Template omi init-weui my-app 使用了 weui 和 omi-router 的移动 web app 模板
omi-mp Template(omi-cli v3.0.13+) omi init-mp my-app 小程序开发 Web 的模板
MVVM Template(omi-cli v3.0.22+) omi init-mvvm my-app MVVM 模板

Cli 自动创建的项目脚手架是基于单页的 create-react-app 改造成多页的,有配置方面的问题可以查看 create-react-app 用户指南

Hello Element

先创建一个自定义元素:

import { define, WeElement } from 'omi'

define('hello-element', class extends WeElement {
  onClick = evt => {
    // trigger CustomEvent
    this.fire('abc', { name: 'dntzhang', age: 12 })
    evt.stopPropagation()
  }

  css = `
      div {
        color: red;
        cursor: pointer;
      }`
  }

  render(props) {
    return (
      <div onClick={this.onClick}>
        Hello {props.msg} {props.propFromParent}
        <div>Click Me!</div>
      </div>
    )
  }
})

使用该元素:

import { define, render, WeElement } from 'omi'
import './hello-element'

define('my-app', class extends WeElement {
  data = { abc: 'abc', passToChild: 123 }

  // define CustomEvent Handler
  onAbc = evt => {
    // get evt data by evt.detail
    this.data.abc = ' by ' + evt.detail.name
    this.data.passToChild = 1234
    this.update()
  }

  css = `
      div{
          color: green;
      }`
  }

  render(props, data) {
    return (
      <div>
        Hello {props.name} {data.abc}
        <hello-element
          onAbc={this.onAbc}
          propFromParent={data.passToChild}
          msg="WeElement"
        />
      </div>
    )
  }
})

render(<my-app name="Omi v4.0" />, 'body')

告诉 Babel 把 JSX 转化成 Omi.h() 的调用:

{
    "presets": ["env", "omi"]
}

需要安装下面两个 npm 包支持上面的配置:

"babel-preset-env": "^1.6.0",
"babel-preset-omi": "^0.1.1",

如果你使用 babel7,也可以使用如下包和配置:

npm install --save-dev @babel/preset-env
npm install --save-dev @babel/preset-react
{
  "presets": [
    "@babel/preset-env",
    [
      "@babel/preset-react",
      {
        "pragma": "Omi.h"
      }
    ]
  ]
}

如果不想把 css 写在 js 里,你可以使用 webpack to-string-loader, 比如下面配置:

{
    test: /[\\|\/]_[\S]*\.css$/,
    use: [
        'to-string-loader',
        'css-loader'
    ]
}

如果你的 css 文件以 _ 开头, css 会使用 to-string-loader. 如:

import { tag, WeElement render } from 'omi'

define('my-app', class extends WeElement {

  css = require('./_index.css')
  ...
  ...
  ...

你也可以忘掉这一对繁琐的配置直接使用 omi-cli,不需要你配置任何东西。

TodoApp

下面列举一个相对完整的 TodoApp 的例子:

import { define, render, WeElement } from 'omi'

define('todo-list', function(props) {
  return (
    <ul>
      {props.items.map(item => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  )
})

define('todo-app', class extends WeElement {
  static observe = true

  data = { 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 />, 'body')

Store

Omi 的 Store 体系: 从根组件注入,在所有子组件可以共享。使用起来非常简单:

import { define, render, WeElement } from 'omi'

define('my-hello', class extends WeElement {
  render() {
    //任意子组件的任意方法都可以使用 this.store 访问注入的 store
    return <div>{this.store.name}</div>
  }
})

define('my-app', class extends WeElement {
  handleClick = () => {
     //任意子组件的任意方法都可以使用 this.store 访问注入的 store
    this.store.reverse()
    this.update()
  }

  render() {
    return (
      <div>
        <my-hello />
        <button onclick={this.handleClick}>reverse</button>
      </div>
    )
  }
})

const store = {
  name: 'abc',
  reverse: function() {
    this.name = this.name.split("").reverse().join("")
  }
}
//通过第三个参数注入
render(<my-app />, document.body, store)

与全局变量不同的是, 当有多个根节点的时候就可以注入多个 store,而全局变量只有一个。

Mitt

如果不想使用 store 的 data 体系,也可以使用发布订阅模式。比如在 Omi 中使用 mitt 跨组件通讯:

Observe

Omi Observe

你可以为那些不需要 store 的自定义元素使用 observe 创建响应式视图,比如:

import { define, WeElement } from "omi"

define("my-app", class extends WeElement {
  static observe = true

  install() {
    this.data.name = "omi"
  }

  onClick = () => {
    this.data.name = "Omi V4.0"
  }

  render(props, data) {
    return (
      <div onClick={this.onClick}>
        <h1>Welcome to {data.name}</h1>
      </div>
    )
  }
})

生命周期

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 update
updated after update
beforeRender before render()
receiveProps parent element re-render will trigger it

调试工具

使用 Omi 开发工具 可以非常简单地调试和管理你的 UI。不需要任何配置,你只要安装然后就能调试。

既然 Omi 使用了 Web Components 和 Shadow-DOM, 所以不需要像 React 一样安装其他元素面板,只需要使用 Chrome 自带的 Elements' sidebar 便可,它和 React 开发者工具一样强大。

Omi DevTools

React 组件转成 Omi

举个例子,下面是吧 weui react 的 button 转成 weui omi 的 button 的例子 :

react to omi

浏览器兼容

Omio - 兼容老浏览器的 Omi 版本(支持到IE8+和移动端浏览器)

Omi 4.0+ works in the latest two versions of all major browsers: Safari 10+, IE 11+, and the evergreen Chrome, Firefox, and Edge.

→ Browsers Support

→ polyfills

<script src="https://unpkg.com/@webcomponents/[email protected]/webcomponents-bundle.js"></script>

贡献者们

维护者

任何 Omi 相关问题欢迎联系我们。也可以加入 Omi QQ 群进行讨论交流。

感谢

License

MIT © Tencent