八十、Taro使用簡介(Taro 是一套遵循 React 語法規範的 多端開發 解決方案---微信/百度/支付寶/字節跳動小程序、H5、React-Native 等)

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.jsutil_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 單位進行轉換,有大寫字母的 PxPX 則會被忽略

5. taro 中的 JSX

在 Taro 中, JSX 是一種看起來很像 XML 的 JavaScript 語法擴展,比起模板語言

它具有以下優點:

  1. 各大編輯器和 IDE 都能提供非常良好的支持;
  2. 可以做到類型安全,在編譯期就能發現錯誤;
  3. 提供語義化並且可以移動的標籤;
  4. 背後的社區支持非常強力;

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組件中的 nameheightisMale都是從外部接收的。

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 傳入不同的 nameheightgender

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. 組件 stateprops 裏字段重名

不要在 stateprops 上用同名的字段,因爲這些被字段在微信小程序中都會掛在 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. 注意

  1. key不會作爲參數傳遞到子組件

  2. 不同於ReactTaro中,能把 map 函數生成的模板當做一個數組來處理

    // 錯誤
    const list = this.state.list
      .map(l => {
        if (l.selected) {
          return <li>{l.text}</li>;
        }
      })
      .filter(React.isValidElement);
    

10. Children 與組合

10.1. Children

在我們設計組件時,有些組件通常不知道自己的子組件會有什麼內容,例如 SidebarDialog 這樣的容器組件。

我們建議在這樣的情況使用 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.childrenthis.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 的支持程度

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章