Omi - 前端跨框架跨平台框架

omi

omi

Omi - 前端跨框架跨平台框架

基于 Web Components 并支持 IE8+(omio),小程序(omip) 和 任意前端框架集成

Omi 生态

→ Omi 生态学习路线图

基础生态

项目描述
omi-docs 和 例子Omi 官方文档
omimOmi 打造的跨框架 Material Design UI 组件库, 任意框架可以使用,(DOCS & REPL && 加入我们!)
omio兼容老浏览器的 Omi 版本(支持到IE8+)
omis服务端同构渲染解决方案(目前只能用 omio)
omiu简单 Omi UI
omi-routerOmi 官方路由,超级小的尺寸,只有 1KB 的 js
omi-devtools谷歌浏览器开发工具扩展
omi-cli项目脚手架工具,各种模板任你选 → 基础模板 and → 其他模板
omilOmi components 的 Webpack loader
omi-snippetsVSCodse omi 文件扩展, 立即安装!

小程序生态

项目描述
omi-cloud小程序•云开发
omip直接使用 Omi 开发小程序或 H5 SPA
mps原生小程序增强框架(JSX + Less 输出 WXML + WXSS),也支持 QQ 轻应用
cax小程序 Canvas 和 SVG 渲染引擎
omix极小却精巧的小程序框架
omi-mp通过微信小程序开发和生成 Web 单页应用(H5 SPA)
comi小程序代码高亮和 markdown 渲染组件
wx-touch-event基于 AlloyFinger/omi-finger 改造的小程序手势解决方案

其他

项目描述
md2site用 markdown 生成静态网站文档.
omi-mvvmMVVM 王者归来, mappingjs 强力加持。
omi-chart一个 chart-x 标签搞定报表
mp-mvvm小程序插上 MVVM 的翅膀, mappingjs 强力加持。
omi-30-seconds30 秒理解一段有用的 Omi 代码片段.
omi-swiperOmi + Swiper
omi-vscodeVscode extension for omi, Install now!
omi-spriteWeb Components, JSX 和 Canvas 的完美融合
omi-canvasWeb Components, JSX 和 Canvas 的完美融合
omi-exOmi.js 扩展(TypeScript)
omi-transformOmi 和 css3transform 完美结合. 让 css3 transform 在你的 Omi项目中变得超级简单.
omi-fingerOmi 官方手势库
omi-touch丝般顺滑的触摸运动
omi-snap预渲染骨架屏
omi-i18nOmi 国际化解决方案
omi-page基于 page.js 的 Omi 路由

特性

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

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

OmiReact
OmiReact

TypeScript 智能提示

import { h, WeElement, tag, classNames } from 'omi';
import * as styles from './_index.less';

interface ButtonProps {
  href?: string,
  disabled?: boolean,
  type?: 'default' | 'primary' | 'danger',
  htmltype?: 'submit' | 'button' | 'reset',
  onClick?: (e: any) => void
}

const TAG = 'o-button'

declare global {
  namespace JSX {
    interface IntrinsicElements {
      [TAG]: Omi.Props & ButtonProps
    }
  }
}

@tag(TAG)
export default class oButton extends WeElement<ButtonProps, {}> {
...
...
...
 

omi

必须收藏的资源


目录

一个 HTML 完全上手

下面这个页面不需要任何构建工具就可以执行

<!DOCTYPE html>
<html>

<head>
  <title>Omi demo without transpiler</title>
</head>

<body>
  <script src="https://tencent.github.io/omi/packages/omi/dist/omi.js"></script>
  <script>
    const { define, WeElement, html, render } = Omi

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

      install() {
        this.data.count = 1
        this.sub = this.sub.bind(this)
        this.add = this.add.bind(this)
      }

      sub() {
        this.data.count--
        this.update()
      }

      add() {
        this.data.count++
        this.update()
      }

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

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

</html>

使用 store

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>Omi demo without transpiler</title>
</head>

<body>
  <script src="https://tencent.github.io/omi/packages/omi/dist/omi.js"></script>
  <script>
    const { define, WeElement, html, render } = Omi

    define('my-counter', class extends WeElement {
      initUse() {
        return ['count']
      }

      install() {
        this.sub = this.sub.bind(this)
        this.add = this.add.bind(this)
      }

      sub() {
        this.store.data.count--
      }

      add() {
        this.store.data.count++
      }

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

    render(html`<my-counter />`, 'body', {
      data: {
        count: 1
      }
    })
  </script>

</body>

</html>

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

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

再花 30 秒完全上手

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

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

@tag('my-counter')
class MyCounter extends WeElement {
  data = {
    count: 1
  }

  static css = `
    span{
        color: red;
    }`

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

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

  render() {
    return (
      <div>
        <button onClick={this.sub}>-</button>
        <span>{this.data.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     # 初始化项目,也可以在空目录里执行 'omi init'
$ cd my-app           # 如果在空目录里执行 'omi init' 忽略这条命令
$ npm start           # 开发
$ npm run build       # 编译发布

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 和 reomi

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

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

项目模板

Template TypeCommandDescribe
基础模板(v3.3.0+)omi init my-app基础模板,支持 omi 和 omio(IE8+)
小程序模板(v3.3.5+)omi init-p my-appOmi 开发小程序
基础模板(v3.3.9+)omi init-o my-app支持 IE8 的基础模板,只是 build 的时候支持 IE8,开发调试请用 IE9
支持预渲染快照骨架的模板omi init-snap my-app基础模板,支持 omi 和 omio(IE8+),内置预渲染
TypeScript Template(omi-cli v3.3.0+)omi init-ts my-app使用 TypeScript 的模板
Mobile Templateomi 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-appMVVM 模板

基础模板(omi init my-app)是基于单页的 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()
  }

  //如果需要在 html 里直接使用 <hello-element></hello-element>,必须声明 propTypes
  static propTypes = {
    msg: String
  }

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

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

使用该元素:

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

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

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

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

  render(props, data) {
    return (
      <div>
        Hello {data.abc}
        <hello-element
          onAbc={this.onAbc}
          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', class extends WeElement {
  static propTypes = {
    items: Array
  }

  render(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,而全局变量只有一个。

生命周期

钩子方法触发时机
install初始化安装
installed插入到文档之后且安装完成
uninstall从文档中卸载移除
beforeUpdateupdate 之前
updatedupdate 之后
beforeRenderrender() 之前
receiveProps父组件更新时候触发, 返回 false 可以阻止更新

调试工具

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

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

Omi DevTools

查看所有注册的元素

console.log(Omi.elements)

浏览器兼容

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/webcomponentsjs@2.0.0/webcomponents-bundle.js"></script>

贡献者们

维护者

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

感谢

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值