update tutorial
This commit is contained in:
parent
36b3c701e6
commit
aec35a1d51
|
@ -1,4 +1,4 @@
|
|||
# Omi 多端开发之 - omip 适配 h5
|
||||
# Omi 多端开发之 - omip 适配 h5 原理揭秘
|
||||
|
||||
Omip 是腾讯 Omi 团队开发的跨端开发工具集,支持小程序和 H5 SPA,最新的 omip 已经适配了 h5,如下方新增的两条命令,
|
||||
|
||||
|
@ -15,16 +15,24 @@ npm run build:h5 //发布 h5
|
|||
|
||||
> 也支持一条命令 `npx omi-cli init-p my-app` (npm v5.2.0+)
|
||||
|
||||
不仅可以一键生成小程序,还能一键生成 h5 SPA。怎么做到的?下面来一一列举难点,逐个击破。
|
||||
当然也支持 TypeScript:
|
||||
|
||||
```js
|
||||
omi init-p-ts my-app
|
||||
```
|
||||
|
||||
TypeScript 的其他命令和上面一样。
|
||||
|
||||
Omip 不仅可以一键生成小程序,还能一键生成 h5 SPA。怎么做到的?下面来一一列举难点,逐个击破。
|
||||
---
|
||||
|
||||
## 工作列表
|
||||
|
||||
* CSS rpx 转换问题
|
||||
* app.css 作用域问题
|
||||
* JSX 里的小程序标签映射
|
||||
* wx api 适配
|
||||
* 集成路由
|
||||
* app.css 作用域问题
|
||||
|
||||
## CSS rpx 转换问题
|
||||
|
||||
|
@ -40,9 +48,128 @@ function rpx(str) {
|
|||
}
|
||||
```
|
||||
|
||||
## app.css 作用域问题
|
||||
|
||||
Shadow tree 与 omi 不一样,omi 是从根开始 shadow root,而小程序是从自定义组件开始,omio 则没有 shadow root。
|
||||
|
||||
| | **omi** | **omio** | **小程序**|
|
||||
|-|-|-|
|
||||
| Shadow DOM| 从根节点开始 | 无 |从自定义组件开始|
|
||||
| Scoped CSS| 从根节点开始局部作用域,浏览器 scoped| 从根节点开始局部作用域(运行时 scoped)| 自定义组件局部作用域|
|
||||
|
||||
所以,app.css 需要污染到 page 里的 WXML/JSX,在 omi 中怎么做?
|
||||
|
||||
先看 app.js 源码:
|
||||
|
||||
```js
|
||||
import './app.css' //注意这行!!!
|
||||
import './pages/index/index'
|
||||
import { render, WeElement, define } from 'omi'
|
||||
|
||||
define('my-app', class extends WeElement {
|
||||
|
||||
config = {
|
||||
pages: [
|
||||
'pages/index/index',
|
||||
'pages/list/index',
|
||||
'pages/detail/index',
|
||||
'pages/logs/index'
|
||||
],
|
||||
window: {
|
||||
backgroundTextStyle: 'light',
|
||||
navigationBarBackgroundColor: '#fff',
|
||||
navigationBarTitleText: 'WeChat',
|
||||
navigationBarTextStyle: 'black'
|
||||
}
|
||||
```
|
||||
|
||||
上面是使用 omip 开发小程序的入口 js 文件,也是 webpack 编译的入口文件,在 cli 进行语法树分析的时候,可以拿到 import 的各个细节,然后做一些变换处理,比如下面 ImportDeclaration(即 import 语句) 的处理:
|
||||
|
||||
```js
|
||||
traverse(ast, {
|
||||
ImportDeclaration: {
|
||||
enter (astPath) {
|
||||
const node = astPath.node
|
||||
const source = node.source
|
||||
const specifiers = node.specifiers
|
||||
let value = source.value
|
||||
//当 app.js 里 import 的文件是以 .css 结尾的时候
|
||||
if(value.endsWith('.css')){
|
||||
//读取对应 js 目录的 css 文件,移除 css 当中的注释,保存到 appCSS 变量中
|
||||
appCSS = fs.readFileSync(filePath.replace('.js','.css'), 'utf-8').replace(/\/\*[^*]*\*+([^/][^*]*\*+)*\//g, '')
|
||||
//移除这里条 import 语句
|
||||
astPath.remove()
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
得到了 appCSS 之后,想办法注入到所有 page 当中:
|
||||
|
||||
```js
|
||||
traverse(ast, {
|
||||
ImportDeclaration: {
|
||||
enter (astPath) {
|
||||
const node = astPath.node
|
||||
const source = node.source
|
||||
let value = source.value
|
||||
const specifiers = node.specifiers
|
||||
//当 import 的文件是以 .css 结尾的时候
|
||||
if(value.endsWith('.css')){
|
||||
//读取对应 js 目录的 css 文件,移除 css 当中的注释,保存到 css 变量中
|
||||
let css = fs.readFileSync(filePath.replace('.js','.css'), 'utf-8').replace(/\/\*[^*]*\*+([^/][^*]*\*+)*\//g, '')
|
||||
//page 注入 appCSS
|
||||
if(filePath.indexOf('/src/pages/') !== -1||filePath.indexOf('\\src\\pages\\') !== -1){
|
||||
css = appCSS + css
|
||||
}
|
||||
//把 import 语句替换成 const ___css = Omi.rpx(.....) 的形式!
|
||||
astPath.replaceWith(t.variableDeclaration('const',[t.variableDeclarator(t.identifier(`___css`),t.callExpression(t.identifier('Omi.rpx'),[t.stringLiteral(css)]),)]))
|
||||
return
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
这就够了吗?不够!因为 ___css 并没有使用到,需要注入到 WeElement Class 的静态属性 css 上,继续 ast transformation:
|
||||
|
||||
```js
|
||||
const programExitVisitor = {
|
||||
ClassBody: {
|
||||
exit (astPath) {
|
||||
//注入静态属性 const css = ___css
|
||||
astPath.unshiftContainer('body', t.classProperty(
|
||||
t.identifier('static css'),
|
||||
t.identifier('___css')
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
编译出得 page 长这个样子:
|
||||
|
||||
```js
|
||||
import { WeElement, define } from "../../libs/omip-h5/omi.esm";
|
||||
|
||||
const ___css = Omi.rpx("\n.container {\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n padding: 200rpx 0;\n box-sizing: border-box;\n} \n\n.userinfo {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.userinfo-avatar {\n width: 128rpx;\n height: 128rpx;\n margin: 20rpx;\n border-radius: 50%;\n}\n\n.userinfo-nickname {\n color: #aaa;\n}\n\n.usermotto {\n margin-top: 200px;\n}");
|
||||
|
||||
const app = getApp();
|
||||
|
||||
define('page-index', class extends WeElement {
|
||||
static css = ___css;
|
||||
|
||||
data = {
|
||||
motto: 'Hello Omip',
|
||||
userInfo: {},
|
||||
hasUserInfo: false,
|
||||
canIUse: wx.canIUse('button.open-type.getUserInfo')
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
大功告成!
|
||||
|
||||
## 标签映射
|
||||
|
||||
由于小程序里的一些标签在浏览器中不能够识别,需要转换成浏览器识别的标签,所以这里列了一个映射表:
|
||||
由于小程序里的一些标签在浏览器中不能够识别,比如浏览器不识别 view、text 等标签,需要转换成浏览器识别的标签,所以这里列了一个映射表:
|
||||
|
||||
```js
|
||||
const mapTag = {
|
||||
|
@ -97,15 +224,15 @@ function h(nodeName, attributes) {
|
|||
| **wx** | **web** |
|
||||
|-|-|
|
||||
| wx.request| XMLHttpRequest |
|
||||
| 界面 api| 实现对应的omi组件 |
|
||||
| 界面 api(confirm、loaing、toast等)| 实现对应的omi组件 |
|
||||
| 数据存储 api| localStorage |
|
||||
|
||||
### 生命周期处理
|
||||
wx 特有的 api 还包括一些特有的生命周期函数,如:
|
||||
|
||||
* onShow
|
||||
* onHide
|
||||
|
||||
这是 wx 里 Page 里的生命周期,而 omi 是不包含的。这里需要在 router 的回调函数中进行主动调用。
|
||||
这是 wx 里 Page 里的生命周期,而 omi 是不包含的。这里需要在 router 的回调函数中进行主动调用。具体怎么出发且看路由管理。
|
||||
|
||||
未完待续..
|
||||
|
||||
|
|
Loading…
Reference in New Issue