正當移動互聯網進入白熱化階段時,以微信小程序爲表明的一類「輕應用」異軍突起。它們無需下載,使用方便,「用完即走」,同時功能也較爲完備,一經推出即獲得了各大平臺和及用戶的熱烈追捧。可是問題也隨之而來——開發者們要同時維護 Web 端、移動端、微信小程序、支付寶小程序等等多套用戶界面,其維護成本能夠想象。做爲一個優秀的多端統一開發解決方案,Taro 的出現則改變了這一狀況。正值 Taro 2.x 進入 beta 階段,讓咱們沏上一杯茶,開始咱們的 Taro 多端小程序開發之旅吧。javascript
若是您以爲咱們寫得還不錯,記得 點贊 + 關注 + 評論 三連,鼓勵咱們寫出更好的教程💪
對於國內 React 開發者來講,Taro 的出現無疑是福音——它可以讓咱們用熟悉的 React 代碼去搭建各種小程序,而且一份代碼能夠編譯成多個平臺的應用(目前包括微信小程序、支付寶小程序、React Native、H5 等等)。隨着 Taro 的不斷進化,它對 React 代碼的支持程度愈來愈好,所支持的目標平臺也愈來愈多,學習的價值天然沒必要多言。正值 Taro 進入 2.0.0 版本的 beta 階段,咱們在這一篇教程將手把手帶你實現一個可以部署到多端的小程序,讓你感覺 Taro 的強大與魅力!css
在這一系列教程中,咱們將構建一個多端小程序應用——奧特曼俱樂部(Ultraman Club,簡稱 UltraClub),一個支持多端登陸(微信和支付寶)的相似貼吧的小程序。咱們還提供了項目倉庫的 GitHub 地址。項目目前還在開發階段,您能夠跳轉到任意一次 commit 查看當前步驟的全部代碼哦。html
在完成這篇教程後,項目的 GIF 動圖展現以下:前端
在閱讀這篇教程以前,咱們但願你已經具有如下知識:java
除此以外,你還須要下載並安裝微信開發者工具,這裏是下載地址。react
本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦~
首先安裝 Taro CLI:git
npm install -g @tarojs/cli
而後建立咱們的項目:github
taro init ultra-club
以後會出現一系列選項,按照下圖所示進行選擇便可(CSS 預處理器選擇 Sass,模板選擇「默認模板」,老司機可自行選擇使用 TS):npm
提示本項目使用 Sass 主要是爲了兼容 taro-ui 的樣式,並無使用到 Sass 的高級特性,若是你不熟悉的話也不用擔憂哦,就當成是常規的 CSS 代碼。json
進入到咱們的項目目錄 ultra-club 以後,能夠看到項目模板包括如下文件:
. ├── config # 項目配置 │ ├── dev.js # 開發環境配置文件 │ ├── index.js # 主配置文件 │ └── prod.js # 生產環境配置文件 ├── package.json ├── project.config.json # 微信小程序項目配置 └── src # 項目源碼目錄 ├── app.scss # 根組件樣式 ├── app.jsx # 根組件 app ├── index.html # 等待被嵌入代碼的 HTML 文檔 └── pages # 頁面目錄 └── index # index 頁面模塊 ├── index.scss # index 頁面樣式 └── index.jsx # index 頁面組件
咱們主要看一下兩個代碼文件:src/app.jsx
以及 src/pages/index/index.jsx
。
src/app.jsx
定義了項目的根組件 App
,它的代碼以下:
import Taro, { Component } from '@tarojs/taro' import Index from './pages/index' import './app.scss' // 若是須要在 h5 環境中開啓 React Devtools // 取消如下注釋: // if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5') { // require('nerv-devtools') // } class App extends Component { config = { pages: ['pages/index/index'], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: 'WeChat', navigationBarTextStyle: 'black', }, } // 在 App 類中的 render() 函數沒有實際做用 // 請勿修改此函數 render() { return <Index /> } } Taro.render(<App />, document.getElementById('app'))
若是你熟悉 React 的話,那麼上面這段代碼必定不難理解,只不過是把相應的地方(導包、渲染)從以前的 React
以及 ReactDOM
改爲 Taro
。
注意能夠看到這個組件還多了一個
config
屬性,這個屬性是小程序應用專屬的。其中要重點關注的是pages
數組,列出了全部的頁面模塊,例如這裏的pages/index/index
就對應 src/pages/index/index.jsx。後面在實現路由時還會用到pages
屬性。
咱們再看看 src/pages/index/index.jsx
。按照最佳實踐,Taro 項目中通常把頁面組件放到 src/pages
目錄中,src/pages/index
就是 index
頁面組件模塊,其中 index.jsx 的代碼以下:
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> ) } }
依舊是熟悉的 React 組件風格,只不過與普通的 React 相比,在 render
函數中咱們用的再也不是 div
和 p
標籤,而是 Taro 爲咱們準備好的 View
和 Text
組件。爲何 Taro 要本身搞一套組件庫呢?由於 Taro 的目標是星辰大海……sorry,是可以編譯到各個平臺。只有經過制訂 Taro 本身的組件庫,才能在各個平臺的原生組件庫上蓋了一層抽象層,進而實現跨平臺的目標。
提示若是你有過 React Native 的開發經驗,那麼必定對 Taro 組件庫不陌生。
Taro 提供的模板代碼直接能夠運行。打開終端,運行如下命令:
npm run dev:weapp
會出現如下提示信息:
當看到「監聽文件修改中...」的提示後,咱們就能夠打開微信開發者工具,用微信掃碼登陸後界面以下:
點擊那個碩大的➕號,開始導入咱們剛纔建立的 ultra-club 項目:
如上圖所示,首先切換到」導入項目「一欄,而後點擊」目錄「輸入欄右側的按鈕選擇剛纔建立的 ultra-club 文件夾,最後點擊右下角的」導入「按鈕便可。
導入成功後,微信開發者工具的界面以下圖所示:
在模擬器頁面中,看到了咱們 index
頁面渲染的 Hello world;編輯器可以查看全部代碼,不過一般咱們用本身習慣的代碼編輯器來開發(VSCode 真香!);調試器則是相似 Chrome 的開發者工具。
一切就緒,讓咱們開始動工吧!
提示從這一步開始,咱們的主要開發目標將是微信小程序,可是不要擔憂,咱們會在下一篇教程中演示怎麼編譯到其餘平臺哦。
從這一步開始,咱們就來實現」奧特曼俱樂部「小程序。按照 React 中」萬物皆組件「的思想,咱們抽象出兩個組件:
PostCard
:用於展現一篇帖子,包括標題 title
和內容 content
PostForm
:用於發佈新帖子的表單首先建立 src/components
目錄,咱們的通用組件都會放在這裏面。而後建立 src/components/PostCard
組件目錄,在其中分別建立 index.jsx
和 index.scss
。index.jsx
代碼以下:
import Taro from '@tarojs/taro' import { View } from '@tarojs/components' import './index.scss' export default function PostCard(props) { return ( <View className="postcard"> <View className="post-title">{props.title}</View> <View className="post-content">{props.content}</View> </View> ) }
正如以前所說,PostCard
組件包含兩個 props:標題 title
和內容 content
。
PostCard 組件的樣式 index.scss
代碼以下:
.postcard { margin: 30px; padding: 20px; border: 1px solid #ddd; } .post-title { font-weight: bolder; margin-bottom: 10px; } .post-content { font-size: medium; color: #666; }
接着咱們實現用於建立新帖子的 PostForm 組件。在 src/components
中建立 PostForm
目錄,並在其中添加 index.jsx
和 index.scss
文件。index.jsx
代碼以下:
import Taro from '@tarojs/taro' import { View, Form, Input, Textarea, Button } from '@tarojs/components' import './index.scss' export default function PostForm(props) { return ( <View className="post-form"> <View>添加新的帖子</View> <Form onSubmit={props.handleSubmit}> <View> <View className="form-hint">標題</View> <Input className="input-title" type="text" placeholder="點擊輸入標題" value={props.formTitle} onInput={props.handleTitleInput} /> <View className="form-hint">正文</View> <Textarea placeholder="點擊輸入正文" className="input-content" value={props.formContent} onInput={props.handleContentInput} /> <Button className="form-button" formType="submit" type="primary"> 提交 </Button> </View> </Form> </View> ) }
PostForm
組件一共定義了五個 props,分別以下:
formTitle
:當前編輯中帖子的標題formContent
:當前編輯中帖子的內容handleSubmit
:處理提交表單的回調函數handleTitleInput
:處理標題接收到用戶輸入時的回調函數handleContentInput
:處理內容接收到用戶輸入時的回調函數提示若是你不熟悉 React,可能會對上面編寫表單的方式有點困惑。實際上,React 推薦用」受控組件「的方式編寫表單,可參考這篇文檔。
PostForm 的樣式文件 index.scss
的代碼以下:
.post-form { border: 1px solid #ddd; margin: 30px; padding: 30px; } .input-title { border: 1px solid #eee; padding: 10px; font-size: medium; } .input-content { border: 1px solid #eee; padding: 10px; height: 200px; font-size: medium; } .form-hint { font-size: small; color: gray; margin-top: 20px; margin-bottom: 10px; } .form-button { margin-top: 40px; }
爲了方便在頁面組件中使用 PostCard
和 PostForm
組件,咱們把 src/components
變成一個模塊。具體地,建立 src/components/index.jsx
,代碼以下:
import PostCard from './PostCard' import PostForm from './PostForm' export { PostCard, PostForm }
最後在 src/pages/index/index.jsx
文件中加入以前寫好的 PostCard 和 PostForm 組件,代碼以下:
import Taro, { Component } from '@tarojs/taro' import { View } from '@tarojs/components' import { PostCard, PostForm } from '../../components' import './index.scss' export default class Index extends Component { state = { posts: [ { title: '泰羅奧特曼', content: '泰羅是奧特之父和奧特之母惟一的親生兒子。', }, ], formTitle: '', formContent: '', } config = { navigationBarTitleText: '首頁', } handleSubmit(e) { e.preventDefault() const { formTitle: title, formContent: content } = this.state const newPosts = this.state.posts.concat({ title, content }) this.setState({ posts: newPosts, formTitle: '', formContent: '', }) } handleTitleInput(e) { this.setState({ formTitle: e.target.value, }) } handleContentInput(e) { this.setState({ formContent: e.target.value, }) } render() { return ( <View className="index"> {this.state.posts.map((post, index) => ( <PostCard key={index} title={post.title} content={post.content} /> ))} <PostForm formTitle={this.state.formTitle} formContent={this.state.formContent} handleSubmit={e => this.handleSubmit(e)} handleTitleInput={e => this.handleTitleInput(e)} handleContentInput={e => this.handleContentInput(e)} /> </View> ) } }
能夠看到,除了接入以前定義的兩個組件外,咱們還加入了一些狀態:
posts
:當前全部的帖子,每一個帖子是一個包含 title
和 content
的對象formTitle
:當前正在編輯的帖子的標題formContent
:當前正在編輯的帖子的內容以及定義了 PostForm
組件中所須要的三個回調函數。
若是以前的開發服務器還打開着,那麼微信開發者工具應該就能直接看到效果了(若是剛纔關了,能夠運行 npm run dev:weapp
再次打開哦):
注意有時候 Taro 可能會出現樣式加載失敗的問題。若是你遇到了,能夠關閉開發服務器,從新運行
npm run dev:weapp
。
自從 React 團隊在 2018 年的 React Conf 引入了 Hooks 以後,前端圈無疑是經歷了一場地震。僅僅只需幾個 API,就輕鬆地用純函數的方式搞定了組件的狀態管理和數據流,這是何等的神仙操做?
幸運的是,Taro 團隊也在 v1.3.0 版本中添加了對 Hooks 的支持。所以,咱們也將在本項目中用 Hooks 解決狀態管理和數據流的問題。
本文在這裏簡單地過一遍 useState
Hook,若是你已經很熟悉了,請直接移步下面的動手環節。
好比咱們以前有這麼一個類組件 ClickMe
,它會抱怨你點了它多少次:
class ClickMe extends Component { state = { count: 0 } render() { return ( <div> <button onClick={() => this.setState({ count: this.state.count + 1 })}> 你點了我 {this.state.count} 次! </button> </div> ) } }
用 Hooks 改寫以後,就變成了一個函數式組件:
// 記得導入 useState 函數 import Taro, { useState } from '@tarojs/taro' function ClickMe() { const [count, setCount] = useState(0) return ( <div> <button onClick={() => setCount(count + 1)}>你點了我 {count} 次!</button> </div> ) }
能夠看到,useState
函數返回了兩個值:
count
):能夠在渲染時直接使用setCount
):用於在處理相應事件時,經過傳入新的狀態來更新狀態還注意到 useState
接受一個參數,即狀態的初始值。這裏咱們取了一個 Number
類型,事實上還能夠是字符串、數組、對象等等。
到了動手環節,咱們用 useState
來重構咱們的 index 頁面。具體地,咱們將整個 Index
組件轉換成函數式組件,而後以前的三個狀態都用 useState
來建立,代碼以下:
import Taro, { useState } from '@tarojs/taro' import { View } from '@tarojs/components' import { PostCard, PostForm } from '../../components' import './index.scss' export default function Index() { const [posts, setPosts] = useState([ { title: '泰羅奧特曼', content: '泰羅是奧特之父和奧特之母惟一的親生兒子。', }, ]) const [formTitle, setFormTitle] = useState('') const [formContent, setFormContent] = useState('') function handleSubmit(e) { e.preventDefault() const newPosts = posts.concat({ title: formTitle, content: formContent }) setPosts(newPosts) setFormTitle('') setFormContent('') } return ( <View className="index"> {posts.map((post, index) => ( <PostCard key={index} title={post.title} content={post.content} /> ))} <PostForm formTitle={formTitle} formContent={formContent} handleSubmit={e => handleSubmit(e)} handleTitleInput={e => setFormTitle(e.target.value)} handleContentInput={e => setFormContent(e.target.value)} /> </View> ) } Index.config = { navigationBarTitleText: '首頁', }
注意因爲咱們把
Index
從類組件改形成了函數組件,因此掛載config
要在Index
組件定義以後直接掛載在Index
上面。
你盡能夠打開模擬器試一下重構以後效果,看看功能是否與上一步徹底一致哦!在接下來的第二篇中,咱們將進一步實現多頁面跳轉,並用 Taro UI 組件庫升級咱們的界面。
想要學習更多精彩的實戰技術教程?來 圖雀社區逛逛吧。本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦~