官网 https://github.com/Tencent/omi
此文主要是对omi的一个初步了解,详细入门见手把手教程:Omi入门实战教程 - 仿淘票票
新概念
- 深入浅出 Shadow Dom
- HTM - JSX ?(why take react so close, but not vue)
- css3transform(我们反应快,我们经过了海量项目洗礼,虽然还是css3,但我们是鹅厂的,so)
- Native 方案(看,h5、小程序、原生app我全包了)
- css变量
- 我们用了requestIdleCallback
- ...
Let's Coding
$ npm i omi-cli -g # 全局安装脚手架
$ omi init my-app # 初始化项目
$ cd my-app
$ npm start # 不用npm install
项目目录结构
package.json:很多babel配置项、eslint配置项,webpack用的4.x,呃,等...
官网给出的目录说明
├─ config
├─ public
├─ scripts
├─ src
│ ├─ assets
│ ├─ elements //存放所有 custom elements
│ ├─ store //存放所有页面的 store
│ ├─ admin.js //入口文件,会 build 成 admin.html
│ └─ index.js //入口文件,会 build 成 index.html
嗯,没有路由,但有两个入口文件,看来它是一个多入口框架?
我讨厌过年我讨厌过年我讨厌过年,凭什么凭什么为什么
在index.js下面新添加一个usercenter.js文件,在elements中添加usercenter文件夹 -> index.js
## usercenter.js
import { render } from 'omi'
import './elements/usercenter'
import store from './store/admin-store'
render(<hello-element />, '#root', store)
## elements/usercenter/index.js
import { define, WeElement } from 'omi'
define('hello-element', class extends WeElement {
render(props, data) {
return (
<div class="hello">
<h1>个人中心 - {this.store.name}</h1>
<div> これは嫌な世界です.</div>
</div>
)
}
})
重启服务后(怎么还要手动重启),访问http://localhost:3000/usercenter.html渲染出了个人中心
组件间传值
Cli 自动创建的项目脚手架是基于单页的 create-react-app 改造成多页的(能不能改为成vue呢,还要熟悉react语法)
import { define, render, WeElement } from 'omi'
define('hello', function(props) {
onClick = evt => {
// trigger CustomEvent
this.fire('say', { name: 'dntzhang', age: 12 }) ## 触发父组件事件
evt.stopPropagation()
}
render(props) {
return (
<div onClick={this.onClick}>
Hello {props.msg} {props.propFromParent} ## 父组件传来的值
<div>Click Me!</div>
</div>
)
}
})
define('my-app', class extends WeElement {
data = { abc: 'abc', passToChild: 123 }
// define CustomEvent Handler
onSay = evt => {
// get evt data by evt.detail
this.data.abc = ' by ' + evt.detail.name + ', he is ' + evt.detail.age
this.data.passToChild = 1234
this.update()
}
handleChange = e => {
this.data.val = e.target.value
}
handleSubmit = e => {
this.data.items.push({id: this.data.items.length + 1, text: this.data.val})
this.update() ## 不加这句页面不刷新渲染
}
render(data) {
return (
<div>
Hello {data.abc}
<hello-demo
onSay={this.onSay} ## 由子组件触发的事件
propFromParent={data.passToChild} ## 传递给子组件
msg="WeElement " ## 传递给子组件
/>
<h3> -- TODO -- </h3>
<ul>
{data.items.map(item => ( ## map
<li key={item.id}>{item.text}</li>
))}
</ul>
<input
id="new-todo"
onChange={this.handleChange}
value={this.data.text}
/>
<button onClick={this.handleSubmit}>Add NO.{this.data.items.length + 1}</button>
</div>
)
}
})
再来看OMI的store
## store/admin-store.js
export default {
name: 'I am admin page',
rename(name) {
this.name = name
}
}
## index.js
render(<hello-element />, '#root', store) // 根部注入,其后的子组件可this.store调用
由于omi是多入口,所以store中的状态不可在页面间共用
Omi Observe
你可以为那些不需要 store 的自定义元素使用 observe 创建响应式视图:
install、installed、uninstall、beforeUpdate、updated、beforeRender、receiveProps
define("my-app", class extends WeElement {
static observe = true ## 启用 Omi Observe
install() {
this.data.name = "omi"
}
...
这是什么意思,不启用生命周期钩子就不能用?启用了就不能用store???
它也有路由
API,与vue路由大不相同,似乎和组件化结合的不够
import 'omi-router' // 引入
import './elements/list_movie'
import './elements/list_cinema'
import './elements/My'
define('my-app', class extends WeElement {
static observe = true
data = { tabbar: 'list-movie' }
install () {
route('/movie', () => {
this.data.tabbar = 'list-movie'
})
route('/cinema', (evt) => {
this.data.tabbar = 'list-cinema'
})
route('/my', (evt) => {
this.data.tabbar = 'my'
})
}
下面尝试懒加载(结束不如人意,感觉只能懒执行,加载一点都不懒)
import 'omi-router'
define('my-app', class extends WeElement {
static observe = true
data = { tabbar: 'list-movie' }
install () {
route('/movie', () => {
require('./elements/list_movie')
this.data.tabbar = 'list-movie'
})
route('/cinema', (evt) => {
require('./elements/list_cinema')
this.data.tabbar = 'list-cinema'
})
route('/my', (evt) => {
require('./elements/My')
this.data.tabbar = 'my'
})
}
changeTabber = e => {
route.to('/' + this.data.tabbar.replace('list-', ''))
}
...
尝试做根部注入,发现在只能注入store,看来这个框架是个偏向多页的框架,对于SPA支持的并不好,但多页都用在什么场景下呢?
// src/router/router.js
// 路由数据
export default [
{
path: '',
redirect: '/movie'
},
{
path: '/movie',
name: 'movie',
component: () => require('../elements/movie')
},
{
path: '/my',
name: 'my',
component: () => require('../elements/My')
}
]
// src/router/index.js
// 封装 router-view 及路由处理
import { define, WeElement } from 'omi'
import routerList from '../router/router'
import 'omi-router'
define('router-view', class extends WeElement {
static observe = true
data = { router_view: '' }
install () {
routerList.forEach(item => {
route(item.path, () => {
if (item.redirect) {
route.to('#' + item.redirect)
} else {
item.component()
this.data.router_view = item.name
}
})
})
}
render(props, data) {
return data.router_view ? ( <data.router_view /> ) : null
}
})
// src/store/admin-store.js
// 由于根部注入只能注入store,所有在store中添加route模块
$route: {
to: route.to,
view: <router-view />
}
// 使用
import { define, render, WeElement } from 'omi'
import store from './store/admin-store'
define('my-app', class extends WeElement {
changeTabber = e => {
this.store.$route.to('/' + e.target.dataset.tab) // 跳转
this.update()
}
render(props, data) {
return (
<div class="my-app">
<div class="main">
{this.store.$route.view} <!-- router-view -->
</div>
<ul class="bottombar">
<li data-tab='movie' onClick={this.changeTabber}>热映</li>
<li data-tab='cinema' onClick={this.changeTabber}>影院</li>
<li data-tab='my' onClick={this.changeTabber}>我的</li>
</ul>
</div>
)
}
})
render(<my-app />, '#root', store)
调试工具
下载地址:https://github.com/f/omi-devtools
下载下来是个.crx文件,装不上打开开发者模式多试几次~