一氓侧、小程序開發(fā)框架
Vue-like、React-like 代碼,如何在小程序中運行?
1.1 基本原理
- 編譯時處理(轉(zhuǎn)譯成小程序語法)
- 運行時適配(管理生命周期、數(shù)據(jù),處理事件等)
編譯原理
源代碼->詞法/語法/語義分析->抽象語法樹->轉(zhuǎn)換->目標代碼
抽象語法樹 AST
1.2 小程序框架多端支持情況
二突颊、Taro原理
Taro: 多端統(tǒng)一開發(fā)框架,支持用 React 的開發(fā)方式編寫一次代
碼潘悼,生成能運行在微信小程序律秃、H5、React Native 等的應用
多端能力:提供對應端的runtime治唤、組件棒动、redux、router等實現(xiàn)
- 提供相應端的
Runtime
實現(xiàn)肝劲,具備適配API
的能力 - 提供統(tǒng)一的基礎(chǔ)組件迁客,編譯時替換為相應端的組件實現(xiàn)
三、Taro項目中遇到的坑
3.1 一些坑
1辞槐、React-like掷漱,不完全等同 React
2、端能力差異不可避免:某些功能在相應端上沒有支持
3榄檬、樣式支持卜范、寫法上存在差異
4、還沒有真正支持多端的 UI 組件庫能在 Taro 上使用
5鹿榜、React Native 的 view 不支持 click 事件海雪,需要用 Touchable 組件
6、React Native 不支持 text-overflow舱殿,而是提供原生支持(Text 組件傳入 numberOfLines={num} 屬性)
Taro
目前沒有暴露原生RN
Text
組件的numberOfLines
屬性奥裸。因此目前通過Text
基礎(chǔ)組件沒法實現(xiàn)多端統(tǒng)一的文本截斷
H5情況較為樂觀
- 主要差異1: 小程序的頁面、組件樣式都是獨立的沪袭,H5會受同名樣式影響
- 主要差異2: 小程序的組件多了一層標簽
7湾宙、多端要求較高
- 對不同端的具體差異有所了解
- 樣式實現(xiàn)較為苛刻,需兼顧多端冈绊、有所取舍
- 端能力差異可能需要自己填坑
8侠鳄、多端填坑方式
多端實踐經(jīng)驗
- 對多端差異做好封裝
- 采用 BEM 命名方式管理樣式
- 采用全局樣式維護基礎(chǔ)組件
@taro/components
的樣式 - 基于
@taro/components
自行封裝組件庫
3.2 注意事項
- 函數(shù)需要
on+函數(shù)名
來規(guī)范命名 - 子組件中接收的
props
需要定義defaultProps
。否則小程序端報錯 - 在
Taro
中死宣,JS
代碼里必須書寫單引號伟恶,特別是 JSX 中,如果出現(xiàn)雙引號毅该,可能會導致編譯錯誤 -
css
樣式單位寫PX
大寫會轉(zhuǎn)成rem
單位 - 根據(jù)環(huán)境變量引入不同平臺組件博秫、編譯到對應平臺
- 小程序端不支持在
render()
之外定義jsx潦牛。比如在外面renderForm()
- 頁面布局:拆分組件方法--由上到下,由左到右拆分
- Taro 目前還沒有支持
React.Fragment
語法 - 運行時 報缺少包挡育,需要要在
.rn_temp
目錄里面安裝 - 文字要包在
Text
組件里面罢绽,否則不顯示 -
position:fixed
React Native
不支持 -
Animation
和transform
React Native
動畫不支持 - 狀態(tài)更新一定是異步的。React的狀態(tài)更新不一定是異步
四静盅、技巧
4.1 樣式文件條件編譯
假設(shè)目錄中同時存在以下文件:
- index.scss
- index.rn.scss
當在
JS
文件中引用樣式文件:import './index.scss'
時,RN
平臺會找到并引入index.rn.scss
寝殴,其他平臺會引入:index.scss
蒿叠,方便大家書寫跨端樣式,更好地兼容RN
4.2 內(nèi)置環(huán)境變量
process.env.TARO_ENV
用于判斷當前編譯類型蚣常,目前有
weapp / swan / alipay / h5 / rn / tt
六個取值市咽,可以通過這個變量來書寫對應一些不同環(huán)境下的代碼,在編譯時會將不屬于當前編譯類型的代碼去掉抵蚊,只保留當前編譯類型下的代碼施绎,例如想在微信小程序和 H5 端分別引用不同資源
if (process.env.TARO_ENV === 'weapp') {
require('path/to/weapp/name')
} else if (process.env.TARO_ENV === 'h5') {
require('path/to/h5/name')
}
同時也可以在
JSX
中使用,決定不同端要加載的組件
render () {
return (
<View>
{process.env.TARO_ENV === 'weapp' && <ScrollViewWeapp />}
{process.env.TARO_ENV === 'h5' && <ScrollViewH5 />}
</View>
)
}
4.3 統(tǒng)一接口的多端文件
多端組件
- 假如有一個 Test 組件存在微信小程序贞绳、百度小程序和 H5 三個不同版本谷醉,那么就可以像如下組織代碼
-
test.js
文件,這是Test
組件默認的形式冈闭,編譯到微信小程序俱尼、百度小程序和 H5 三端之外的端使用的版本 -
test.h5.js
文件,這是Test
組件的H5
版本 -
test.weapp.js
文件萎攒,這是Test
組件的 微信小程序 版本 -
test.swan.js
文件遇八,這是Test
組件的 百度小程序 版本 - 四個文件,對外暴露的是統(tǒng)一的接口耍休,它們接受一致的參數(shù)刃永,只是內(nèi)部有針對各自平臺的代碼實現(xiàn)
- 而我們使用
Test
組件的時候,引用的方式依然和之前保持一致羊精,import
的是不帶端類型的文件名斯够,在編譯的時候會自動識別并添加端類型后綴
import Test from '../../components/test'
<Test argA={1} argA={2} />
多端腳本邏輯
例如微信小程序上使用
Taro.setNavigationBarTitle
來設(shè)置頁面標題,H5 使用document.title
园匹,那么可以封裝一個setTitle
方法來抹平兩個平臺的差異
增加 set_title.h5.js
雳刺,代碼如下
export default function setTitle (title) {
document.title = title
}
增加
set_title.weapp.js
,代碼如下
import Taro from '@tarojs/taro'
export default function setTitle (title) {
Taro.setNavigationBarTitle({
title
})
}
調(diào)用的時候裸违,如下使用
import setTitle from '../utils/set_title'
setTitle('頁面標題')
五掖桦、React Native端的實踐
5.1 Taro運行RN步驟
1. 運行 yarn dev:rn
打開 http://127.0.0.1:8081/index.bundle?platform=ios&dev=true激活編譯
2. 下載react-native運行殼子
3. 打開gemotion安卓模擬器
- 參考文章
adb devices
查看是否有設(shè)備在運行
4. 啟動安卓
在殼子中啟動命令。
react-native-shell
放在哪里都可以供汛,建議放到項目根目錄即可
react-native run-android
5. 打包apk
把react-native-shell
中的android
文件加拷貝到.rn_temp
文件中枪汪,cd android
執(zhí)行打包命令即可
5.2 樣式
Note:如果要支持
React Native
端涌穆,必須采用 Flex 布局,并且樣式選擇器僅支持類選擇器雀久,且不 支持 組合器
以下選擇器的寫法都是不支持的宿稀,在樣式轉(zhuǎn)換時會自動忽略
.button.button_theme_islands{
font-style: bold;
}
img + p {
font-style: bold;
}
p ~ span {
color: red;
}
div > span {
background-color: DodgerBlue;
}
div span { background-color: DodgerBlue; }
樣式上 H5
最為靈活,小程序次之赖捌,RN
最弱祝沸,統(tǒng)一多端樣式即是對齊短板,也就是要以 RN
的約束來管理樣式越庇,同時兼顧小程序的限制罩锐,核心可以用三點來概括
- 使用
Flex
布局 - 基于
BEM
寫樣式 - 采用
style
屬性覆蓋組件樣式
RN
中View
標簽默認主軸方向是column
,如果不將其他端改成與 RN 一致卤唉,就需要在所有用到display: flex
的地方都顯式聲明主軸方向
5.3 問題
盡量避免在 componentDidMount 中調(diào)用 this.setState
因為在
componentDidMount
中調(diào)用this.setState
會導致觸發(fā)更新
import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'
class MyComponent extends Component {
state = {
myTime: 12
}
componentDidMount () {
this.setState({ // ? 盡量避免涩惑,可以在 componentWillMount 中處理
name: 1
})
}
render () {
const { isEnable } = this.props
const { myTime } = this.state
return (
<View className='test'>
{isEnable && <Text className='test_text'>{myTime}</Text>}
</View>
)
}
}
不要定義沒有用到的 state
import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'
class MyComponent extends Component {
state = {
myTime: 12,
noUsed: true // ? 沒有用到
}
render () {
const { myTime } = this.state
return (
<View className='test'>
<Text className='test_text'>{myTime}</Text>
</View>
)
}
}