1. Taro
1.1. 介紹
Taro 是一套遵循 React 語法規範的 多端開發 解決方案。由京東的凹凸實驗室團隊於 2018-09-18 歷時 3 個月正式發佈。taro 的目標是使用一套代碼達到多端統一。
多端:
-
微信小程序
-
H5
-
React Native
-
支付寶小程序
-
百度智能小程序
-
快應用 適配中
-
QQ 瀏覽器輕應用
1.2. 注意
雖然 Taro 擁有多端編譯的能力,但是爲了讓我們的學習有一條比較完整的路線,本章課程是優先以微信小程序的開發爲主線來介紹 Taro。
2. 運行項目
2.1. 安裝腳手架工具@tarojs/cli
npm install -g @tarojs/cli
2.2. 創建項目
taro init myApp
2.3. 打包編譯
2.3.1. 發佈
-
微信小程序
npm run build:weapp
-
H5
npm run build:h5
-
百度智能小程序
npm run build:swan
-
支付寶小程序
npm run build:alipay
-
React Native
npm run build:rn
2.3.2. 監控
-
微信小程序
npm run dev:weapp
-
H5
npm run dev:h5
-
百度智能小程序
npm run dev:swan
-
支付寶小程序
npm run dev:alipay
-
React Native
npm run dev:rn
3. 項目結構
3.1. 基本目錄
所有項目源代碼請放在項目根目錄 src
目錄下,項目所需最基本的文件包括 入口文件 以及 頁面文件
- 入口文件爲
app.js
- 頁面文件建議放置在
src/pages
目錄下
一個可靠的 Taro 項目可以按照如下方式進行組織
├── config 配置目錄
| ├── dev.js 開發時配置
| ├── index.js 默認配置
| └── prod.js 打包時配置
├── src 源碼目錄
| ├── components 公共組件目錄
| ├── pages 頁面文件目錄
| | ├── index index 頁面目錄
| | | ├── banner 頁面 index 私有組件
| | | ├── index.js index 頁面邏輯
| | | └── index.css index 頁面樣式
| ├── utils 公共方法庫
| ├── app.css 項目總通用樣式
| └── app.js 項目入口文件
└── package.json
3.2. 文件命名
Taro 中普通 JS/TS 文件以小寫字母命名,多個單詞以下劃線連接,例如 util.js
、util_helper.js
Taro 組件文件命名遵循 Pascal 命名法,例如 ReservationCard.jsx
3.3. 其他規範
taro 做了更加詳細的編碼規範描述。參考
4. 項目配置
通過 Taro 模板創建的項目都會默認擁有 project.config.json
這一項目配置文件,這個文件 只能用於微信小程序,若要兼容到其他小程序平臺,請按如下對應規則來增加相應平臺的配置文件,其配置與各自小程序平臺要求的一致
各類小程序平臺均有自己的項目配置文件,例如
- 微信小程序,project.config.json
- 百度智能小程序,project.swan.json
- 頭條小程序, project.tt.json,文檔暫無,大部分字段與微信小程序一致
- 支付寶小程序,暫無發現
4.1. 微信小程序全局配置
小程序的全局配置文件在app.js
文件中的 config
字段中
class App extends Component {
// 小程序的全局配置
config = {
pages: ["pages/index/index"],
window: {
backgroundTextStyle: "light",
navigationBarBackgroundColor: "#fff",
navigationBarTitleText: "WeChat",
navigationBarTextStyle: "black"
}
};
render() {
return <Index />;
}
}
4.1.1. 生命週期對應關係
而且由於入口文件繼承自 Component
組件基類,它同樣擁有組件生命週期,但因爲入口文件的特殊性,他的生命週期並不完整,如下
生命週期方法 | 作用 | 說明 |
---|---|---|
componentWillMount | 程序被載入 | 在微信小程序中這一生命週期方法對應 app 的 onLaunch |
componentDidMount | 程序被載入 | 在微信小程序中這一生命週期方法對應 app 的 onLaunch ,在 componentWillMount 後執行 |
componentDidShow | 程序展示出來 | 在微信小程序中這一生命週期方法對應 onShow ,在 H5 中同樣實現 |
componentDidHide | 程序被隱藏 | 在微信小程序中這一生命週期方法對應 onHide ,在 H5 中同樣實現 |
componentDidCatchError | 錯誤監聽函數 | 在微信小程序中這一生命週期方法對應 onError |
componentDidNotFound | 頁面不存在監聽函數 | 在微信小程序中這一生命週期方法對應 onPageNotFound |
微信小程序中
onLaunch
通常帶有一個參數options
,在 Taro 中你可以在所有生命週期和普通事件方法中通過this.$router.params
訪問到,在其他端也適用
入口文件需要包含一個 render
方法,一般返回程序的第一個頁面,但值得注意的是不要在入口文件中的 render
方法裏寫邏輯及引用其他頁面、組件,因爲編譯時 render
方法的內容會被直接替換掉,你的邏輯代碼不會起作用。
4.2. 微信小程序頁面配置
小程序的頁面配置文件存在於 pages文件夾內的 index.js
config
字段中
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import "./index.scss";
export default class Index extends Component {
// 頁面配置
config = {
navigationBarTitleText: "首頁"
};
render() {
return (
<View className="index">
<Text>Hello world!</Text>
</View>
);
}
}
4.2.1. 生命週期對應關係
由於頁面 JS 也繼承自 Component
組件基類,所以頁面同樣擁有生命週期,頁面的生命週期方法如下:
生命週期方法 | 作用 | 說明 |
---|---|---|
componentWillMount | 頁面被載入 | 在微信小程序中這一生命週期方法對應 onLoad |
componentDidMount | 頁面渲染完成 | 在微信小程序中這一生命週期方法對應 onReady |
shouldComponentUpdate | 頁面是否需要更新 | |
componentWillUpdate | 頁面即將更新 | |
componentDidUpdate | 頁面更新完畢 | |
componentWillUnmount | 頁面退出 | 在微信小程序中這一生命週期方法對應 onUnload |
componentDidShow | 頁面展示出來 | 在微信小程序中這一生命週期方法對應 onShow ,在 H5 中同樣實現 |
componentDidHide | 頁面被隱藏 | 在微信小程序中這一生命週期方法對應 onHide ,在 H5 中同樣實現 |
微信小程序中
onLoad
通常帶有一個參數options
,在 Taro 中你可以在所有生命週期和普通事件方法中通過this.$router.params
訪問到,在其他端也適用
在小程序中,頁面還有在一些專屬的方法成員,如下
方法 | 作用 |
---|---|
onPullDownRefresh | 頁面相關事件處理函數–監聽用戶下拉動作 |
onReachBottom | 頁面上拉觸底事件的處理函數 |
onShareAppMessage | 用戶點擊右上角轉發 |
onPageScroll | 頁面滾動觸發事件的處理函數 |
onTabItemTap | 當前是 tab 頁時,點擊 tab 時觸發 |
componentWillPreload | 預加載,只在微信小程序中可用 |
4.3. 路由功能
4.3.1. 路由 API 說明
在 Taro 中,路由功能是默認自帶的,不需要開發者進行額外的路由配置。
我們只需要在入口文件的 config
配置中指定好 pages
,然後就可以在代碼中通過 Taro 提供的 API 來跳轉到目的頁面,例如:
// 跳轉到目的頁面,打開新頁面
Taro.navigateTo({
url: "/pages/page/path/name"
});
具體 API 說明,請查看導航部分說明。
4.3.2. 路由傳參
我們可以通過在所有跳轉的 url
後面添加查詢字符串參數進行跳轉傳參,例如
// 傳入參數 id=2&type=test
Taro.navigateTo({
url: "/pages/page/path/name?id=2&type=test"
});
這樣的話,在跳轉成功的目標頁的生命週期方法裏就能通過 this.$router.params
獲取到傳入的參數,例如上述跳轉,在目標頁的 componentWillMount
生命週期裏獲取入參數
class C extends Taro.Component {
componentWillMount() {
console.log(this.$router.params); // 輸出 { id: 2, type: 'test' }
}
}
4.4. 設計稿及尺寸單位
在 Taro 中尺寸單位建議使用 px
、 百分比 %
,Taro 默認會對所有單位進行轉換。
Taro 默認以 750px
作爲換算尺寸標準,如果設計稿不是以 750px
爲標準,則需要在項目配置 config/index.js
中進行設置。
const config = {
projectName: 'myApp',
date: '2018-12-6',
designWidth: 750,
deviceRatio: {
'640': 2.34 / 2,
'750': 1,
'828': 1.81 / 2
....
}
}
4.4.1. 注意
4.4.1.1. 行內樣式無法自動轉換
但是如果是在 JS 中書寫了行內樣式,那麼編譯時就無法做替換了,針對這種情況,Taro 提供了 API Taro.pxTransform
來做運行時的尺寸轉換。
let fontsize = Taro.pxTransform(10); // 小程序:rpx,H5:rem
4.4.1.2. 忽略轉換
默認配置會對所有的 px
單位進行轉換,有大寫字母的 Px
或 PX
則會被忽略
5. taro 中的 JSX
在 Taro 中, JSX
是一種看起來很像 XML 的 JavaScript 語法擴展,比起模板語言
它具有以下優點:
- 各大編輯器和 IDE 都能提供非常良好的支持;
- 可以做到類型安全,在編譯期就能發現錯誤;
- 提供語義化並且可以移動的標籤;
- 背後的社區支持非常強力;
5.1. 小規範
爲了更好的使用 jsx 實現功能,我們先簡單的總結一下 Taro 中的 jsx 的規範。
請觀察以下代碼:
import Taro, { Component } from "@tarojs/taro";
import { View } from "@tarojs/components";
class Home extends Component {
render() {
return <View>Hello World!</View>;
}
}
5.1.1. 必須聲明 Taro
和組件
- 變量
Taro
也是一個必須引入聲明的變量,因爲我們在編譯期和運行時會依賴這個變量做一些特殊處理。 - 變量
View
看起來並沒有被調用,但也必須從@tarojs/components
中引入聲明 - 組件
5.1.2. 首字母大寫與駝峯式命名
在 Taro 中,所有組件都應當首字母大寫並且使用大駝峯式命名法(Camel-Case)。
如:
import Taro, { Component } from "@tarojs/taro";
// 引入一個自定義組件組件
import HomePage from "./HomePage";
class App extends Component {
render() {
return <HomePage message="Hello World!" />;
}
}
5.2. 組件初體驗
5.2.1. 新建組件 HelloWorld
在src目錄下,新建組件文件夾components
和 組件 HelloWorld.jsx
或者 HelloWorld.js
。 輸入內容:
import Taro, { Component } from "@tarojs/taro";
import { View } from "@tarojs/components";
class HelloWorld extends Component {
render() {
return <View>組件 hello world</View>;
}
}
5.2.2. 使用組件 HelloWorld
在 src/pages/index.js
中 ,引入組件並渲染
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import HelloWorld from "../../components/HelloWorld.jsx";
import "./index.scss";
export default class Index extends Component {
config = {
navigationBarTitleText: "taro-index"
};
// 要聲明構造函數,否則 發佈容易失敗!
constructor(props) {
// 顯式調用,否則 this.props將會是未定義
super(props);
}
render() {
return (
<View>
<HelloWorld />
</View>
);
}
}
5.3. 屬性
在 JSX 中有幾種不同的方式來指定屬性。
5.3.1. 使用 JavaScript 表達式
你可以任意地在 JSX 當中使用 JavaScript 表達式,在 JSX 當中的表達式要包含在大括號裏。例如,在這個 JSX 中:
<Text data-title={ 1 + 2 + 3 + 4}> </Text>
if 語句和 for 循環在 JavaScript 中不是表達式,因此它們不能直接在 JSX 中使用。
錯誤的演示:
<Text data-title="{" if(true) { 1 } }> 錯誤演示 </Text>
5.3.2. 字符串常量
你可以將字符串常量作爲屬性值傳遞。下面這兩個 JSX 表達式是等價的:
<MyComponent message='hello world' />
<MyComponent message={'hello world'} />
5.3.3. 默認爲 True
如果你沒有給屬性傳值,它默認爲 true。因此下面兩個 JSX 是等價的:
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
5.3.4. 布爾值、Null 和 Undefined 被忽略
false、null、undefined 和 true 都是有效的 children,但它們不會直接被渲染。下面的表達式是等價的:
在小程序中,true和false是會直接編譯出來的
<View />
<View></View>
<View>{false}</View>
<View>{null}</View>
<View>{undefined}</View>
<View>{true}</View>
這在根據條件來確定是否渲染 元素時非常有用。以下的 JSX 只會在 showHeader 爲 true 時渲染
組件。
<View>
{showHeader && <Header />}
<Content />
</View>
5.3.5. 注意
React 可以使用 ...
拓展操作符來傳遞屬性,但在 Taro 中你不能這麼做。例如:
錯誤的寫法
const props = { firstName: "Plus", lastName: "Second" };
return <Greeting {...props} />;
這樣的操作會報錯。你只能手動地把所有需要引用的 props 寫上去:
正確的寫法
<Greeting firstName="Plus" lastName="Second" />
6. props & state
6.1. props
props 含義爲屬性,屬性不可變。
當我們需要在父組件上傳遞數據給子組件時,可以使用props
技術。
6.1.1. 新建 Person.jsx
person
組件中的 name
、height
、isMale
都是從外部接收的。
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
class Person extends Component {
constructor(props) {
// 顯式調用,否則 this.props容易出現未定義
super(props);
}
render() {
return (
<View>
<View>{this.props.name}</View>
<View>{this.props.height}</View>
<View>{this.props.isMale}</View>
</View>
);
}
}
6.1.2. 頁面 index.js
在頁面index.js
中,通過屬性的方式動態給 Person 傳入不同的 name
、height
、gender
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import Person from "../../components/Person.jsx";
import "./index.scss";
export default class Index extends Component {
config = {
navigationBarTitleText: "taro-index"
};
constructor(props) {
super(props);
}
render() {
return (
<View>
<Person name="小紅" height="150" isMale={true} />
<Person name="小藍" height="250" isMale={false} />
</View>
);
}
}
6.1.3. Props 的只讀性
爲了規範而定,在組件中,是不能修改傳入的props
屬性的。
class Person extends Component {
constructor(props) {
super(props);
// 不要修改
this.props.name='大白';
}
...
}
6.1.4. 使用 PropTypes 檢查類型
隨着應用日漸龐大,你可以通過類型檢查捕獲大量錯誤。要檢查組件的屬性,你需要配置特殊的 propTypes
屬性:
目前在小程序端還有些問題,但在 H5 端可以使用,用法和在 React 裏一樣。 更多可參照React 的相關文檔。
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
// 引入 propTypes
import PropTypes from "prop-types";
class Person extends Component {
constructor(props) {
super(props);
this.props.name = "大白";
}
render() {
return (
<View className="fz">
<View>{this.props.name}</View>
<View>{this.props.height}</View>
<View>{this.props.isMale}</View>
</View>
);
}
}
// 約束類型
Person.PropTypes = {
name: PropTypes.string,
height: PropTypes.number,
isMale: PropTypes.bool
};
6.1.5. props 不能使用 ...
拓展操作符
React 可以使用 ...
拓展操作符來傳遞屬性,但在 Taro 中你不能這麼做。例如:
const props = { firstName: "Plus", lastName: "Second" };
return <Greeting {...props} />;
這樣的操作會報錯。你只能手動地把所有需要引用的 props 寫上去:
<Greeting firstName="Plus" lastName="Second" />
6.2. State
state 含義爲狀態,狀態可變。
當組件需要實現動態修改某些數據時,可以通過 state
來實現。
6.2.1. 使用
一般是在組件的構造函數中進行初始化,然後就可以在組件的標籤中進行使用。
- 初始化
this.state={msg:'hello'}
- 修改值
this.setState({msg:'world'})
- 標籤中使用
<View>{this.state.msg}</View>
// 聲明
class Clock extends Component {
constructor(props) {
super(props);
// 初始化
this.state = {
msg: "hello"
};
// 修改
this.setState({
msg: "world"
});
}
render() {
return (
<View>
<View>{this.state.msg}</View>
</View>
);
}
}
6.2.2. 狀態更新一定是異步的
React 的 setState
不一定總是異步的。而對於 Taro 而言,setState
一定是異步的。如:
constructor(props) {
super(props);
// 初始化 count爲 0
this.state = {
count:0
};
// 修改 count 爲1
this.setState({
count:1
})
// 打印count的值 爲 0
console.log(this.state.count);
}
如想要順利的拿到修改後的值,正確的做法是 在 setState
的第二個參數傳入一個 callback:
this.setState(
{
count: 1
},
() => {
// 在這個函數內你可以拿到 setState 之後的值
console.log(this.state.count);
}
);
區別於 react,下面這種寫法也不行。
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
console.log(this.state.count);
6.2.3. 組件 state
與 props
裏字段重名
不要在 state
與 props
上用同名的字段,因爲這些被字段在微信小程序中都會掛在 data
上。
7. 事件處理
Taro 中關於事件的觸發有區別於微信小程序。
7.1. 事件的綁定
-
Taro 事件綁定屬性的命名採用駝峯式寫法 並且是
on
開頭如:
<button onClick={this.clickHandle}>點我點我</button>
而微信小程序中 事件的綁定是以
bind
或者catch
開頭,value
值 則是一個字符串<button onclick="clickHandle">點我點我</button>
-
事件的處理函數不是字符串,而是一個 jsx 中的函數
-
可以在事件執行函數中直接使用 this,而不像 React 中需要調用下列代碼來傳入
this
constructor(props) { super(props); // Taro 中不需要這樣 this.clickHandle = this.clickHandle.bind(this); }
-
使用
stopPropagation
來阻止事件冒泡,而不是微信小程序中的catchEvent
7.2. 事件觸發
Taro 中,事件處理函數要寫在和構造函數同層級,如
class Index extends React.Component {
constructor (props) {
super(props)
}
clickHandle = (e) => {
// 事件的邏輯
}))
}
}
7.3. 阻止事件冒泡
在 Taro 中不能像微信小程序一樣 使用 catchEvent
阻止事件冒泡,必須在執行事件中調用e.stopPropagation
clickHandle = e => {
// 阻止事件冒泡
e.stopPropagation();
};
7.4. 事件傳遞參數
當我們向事件的處理函數傳遞參數時,可以使用 bind
的方式來傳遞,同時,事件對象 e
要排在所傳遞參數的後面。如
class Popper extends Component {
constructor() {
super(props);
this.state = { name: "Hello world!" };
}
// 你可以通過 bind 傳入多個參數
preventPop(name, test, e) {
//事件對象 e 要放在最後
e.preventDefault();
}
render() {
return (
<Button onClick={this.preventPop.bind(this, this.state.name, "test")} />
);
}
}
7.5. 事件的傳遞
事件的傳遞,其實就是 子組件觸發父組件中的事件,並且可以傳遞對應的參數的過程。
需要注意的是 任何組件的事件傳遞都要以
on
開頭
組件 IndexComA
import Taro, { Component } from "@tarojs/taro";
import { View } from "@tarojs/components";
class IndexComA extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View onClick={this.props.onClick.bind(this, "IndexComA")}>
IndexComA
</View>
);
}
}
組件 IndexComB
import Taro, { Component } from "@tarojs/taro";
import { View } from "@tarojs/components";
class IndexComB extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View onClick={this.props.onClick.bind(this, "IndexComB")}>
IndexComB
</View>
);
}
}
父組件
import Taro, { Component } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import IndexComA from "../../components/IndexComA";
import IndexComB from "../../components/IndexComB";
import "./index.scss";
export default class Index extends Component {
config = {
navigationBarTitleText: "index"
};
constructor(props) {
super(props);
}
clickHd(arg) {
console.log("組件 " + arg);
}
render() {
return (
<View>
<IndexComA onClick={this.clickHd} />
<IndexComB onClick={this.clickHd} />
</View>
);
}
}
8. 條件渲染
在 Taro 中,我們可以使用 元素變量, if, 三元運算符 ,邏輯運算符 &&
來增加標籤的渲染能力。
8.1. 元素變量 和 if
// LoginStatus.js
class LoginStatus extends Component {
render() {
const isLoggedIn = this.props.isLoggedIn;
// 這裏最好初始化聲明爲 `null`,初始化又不賦值的話
// 小程序可能會報警爲變量爲 undefined
let status = null;
if (isLoggedIn) {
status = <Text>已登錄</Text>;
} else {
status = <Text>未登錄</Text>;
}
return <View>{status}</View>;
}
}
// app.js
import LoginStatus from "./LoginStatus";
// 這樣會渲染 `已登錄`
class App extends Component {
render() {
return (
<View>
<LoginStatus isLoggedIn={true} />
</View>
);
}
}
8.2. 邏輯運算符 &&
更加方便的渲染方式。
class LoginStatus extends Component {
render() {
const isLoggedIn = this.props.isLoggedIn;
return (
<View>
{isLoggedIn && <Text>已登錄</Text>}
{!isLoggedIn && <Text>未登錄</Text>}
</View>
);
}
}
8.3. 三元運算符
條件渲染的另一種方法是使用 JavaScript 的條件運算符 condition ? true : false
。
class LoginStatus extends Component {
render() {
const isLoggedIn = this.props.isLoggedIn;
return (
<View>{isLoggedIn ? <Text>已登錄</Text> : <Text>未登錄</Text>}</View>
);
}
}
9. 列表渲染
在 Taro 中,需要渲染列表時,是通過 javascript 中的map
方法進行遍歷的。同時也需要指定 key
屬性
如:
const numbers = [...Array(50).keys()]; // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map(number => {
return (
<View key={number} className="li">
我是第 {number + 1} 個數字
</View>
);
});
9.1. 注意
-
key
不會作爲參數傳遞到子組件 -
不同於
React
在Taro
中,能把 map 函數生成的模板當做一個數組來處理// 錯誤 const list = this.state.list .map(l => { if (l.selected) { return <li>{l.text}</li>; } }) .filter(React.isValidElement);
10. Children 與組合
10.1. Children
在我們設計組件時,有些組件通常不知道自己的子組件會有什麼內容,例如 Sidebar
和 Dialog
這樣的容器組件。
我們建議在這樣的情況使用 this.props.children
來傳遞子元素:
class Dialog extends Component {
render() {
return (
<View className="dialog">
<View className="header">Welcome!</View>
<View className="body">{this.props.children}</View>
<View className="footer">-- divider --</View>
</View>
);
}
}
這樣就能允許其它組件在 JSX 中嵌套任意子組件傳遞給 Dialog
:
class App extends Component {
render() {
return (
<View className="container">
<Dialog>
<View className="dialog-message">Thank you for using Taro.</View>
</Dialog>
</View>
);
}
}
在 <Dialog />
JSX 標籤內的任何內容都會作爲它的子元素(Children)都會傳遞到它的組件。
10.2. 組合
有些情況你不僅僅需要只傳遞一個子組件,可能會需要很多個「佔位符」。
dialog
class Dialog extends Component {
render() {
return (
<View className="dialog">
<View className="header">{this.props.renderHeader}</View>
<View className="body">{this.props.children}</View>
<View className="footer">{this.props.renderFooter}</View>
</View>
);
}
}
APP
class App extends Component {
render() {
return (
<View className="container">
<Dialog
renderHeader={<View className="welcome-message">Welcome!</View>}
renderFooter={<Button className="close">Close</Button>}
>
<View className="dialog-message">Thank you for using Taro.</View>
</Dialog>
</View>
);
}
}
10.3. 注意事項
請不要對 this.props.children 進行任何操作。Taro 在小程序中實現這個功能使用的是小程序的 slot
功能,也就是說你可以把 this.props.children
理解爲 slot
的語法糖,this.props.children
在 Taro 中並不是 React 的 ReactElement
對象,因此形如 this.props.children && this.props.children
、this.props.children[0]
在 Taro 中都是非法的。
this.props.children 無法用 defaultProps 設置默認內容。由於小程序的限制,Taro 也無法知道組件的消費者是否傳入內容,所以無法應用默認內容。
不能把 this.props.children 分解爲變量再使用。由於普通的 props
有一個確切的值,所以當你把它們分解爲變量運行時可以處理,this.props.children
則不能這樣操作,你必須顯性地把 this.props.children
全部都寫完整才能實現它的功能。
組件的組合需要遵守 this.props.children 的所有規則。組合這個功能和 this.props.children
一樣是通過 slot
實現的,也就是說 this.props.children
的限制對於組件組合也都同樣適用。
所有組合都必須用 render 開頭,且遵守駝峯式命名法。和我們的事件規範以 on
開頭一樣,組件組合使用 render
開頭。
組合只能傳入單個 JSX 元素,不能傳入其它任何類型。當你需要進行一些條件判斷或複雜邏輯操作的時候,可以使用一個 Block
元素包裹住,然後在 Block
元素的裏面填充其它複雜的邏輯。
11. 組件的外部樣式和全局樣式
自定義組件對應的樣式文件,默認只對該組件內的節點生效。編寫組件樣式時需要注意
- 組件和引用組件的頁面不能使用 id 選擇器(
#a
)、屬性選擇器([a]
)和標籤名選擇器,請改用 class 選擇器。 - 繼承樣式,如 font 、 color ,會從組件外(父組件)繼承到組件內。但是引用組件時在組件節點上書寫的 className 無效。
11.1. 外部樣式類
如果想傳遞樣式給引用的自定義組件,直接傳遞 className
不可行
需要利用 externalClasses
定義段定義若干個外部樣式類
CustomComp.js
export default CustomComp extends Component {
static externalClasses = ['my-class']
render () {
return <View className="my-class">這段文本的顏色由組件外的 class 決定</View>
}
}
MyPage.js
export default MyPage extends Component {
render () {
return <CustomComp my-class="red-text" />
}
}
11.2. 全局樣式類
使用外部樣式類可以讓組件使用指定的組件外樣式類,如果希望組件外樣式類能夠完全影響組件內部,可以將組件構造器中的 options.addGlobalClass
字段置爲 true
。
CustomComp.js
export default CustomComp extends Component {
static options = {
addGlobalClass: true
}
render () {
return <View className="red-text">這段文本的顏色由組件外的 class 決定</View>
}
}
12. Refs 引用
該知識點主要應用在 h5 端,如可以直接操作到對應的 dom 元素,動態設置焦點等。在微信小程序中並沒有太多的使用場景。因此省略。
13 其他
13.1. 最佳實踐
13.1.1關於 JSX 支持程度補充說明
由於 JSX 中的寫法千變萬化,我們不能支持到所有的 JSX 寫法,同時由於微信小程序端的限制,也有部分 JSX 的優秀用法暫時不能得到很好地支持,特在此補充說明一下對於 JSX 的支持程度