原文:https://www.jianshu.com/p/dc4a1b17ff53
一.什么是taro?
Taro 是一套遵循 React 語法規(guī)范的 多端開發(fā) 解決方案。通過一套react的代碼,就可以分別編譯出微信小程序、H5、支付寶小程序。。。 我根據(jù)taro redux模板創(chuàng)建了一套自己寫的H5種子項(xiàng)目,大家可以一起學(xué)習(xí),項(xiàng)目地址:https://github.com/WangxinsHub/taro-seed
二.如何安裝taro?
1.首先全局安裝taro命令行:
$ npm install -g @tarojs/cli
$ yarn global add @tarojs/cli
2.創(chuàng)建taro種子項(xiàng)目
$ taro init myApp
3.編譯taro,在創(chuàng)建的種子項(xiàng)目中,package文件如下:
\”scripts\”: {\”build:weapp\”: \”taro build –type weapp\”,//打包小程序\”build:h5\”: \”taro build –type h5\”, //打包H5\”dev:weapp\”: \”npm run build:weapp — –watch\”,//編譯小程序\”dev:h5\”: \”npm run build:h5 — –watch\”,//編譯H5},
三、開發(fā)前注意
小程序工具:
需要設(shè)置關(guān)閉 ES6 轉(zhuǎn) ES5 功能,開啟可能報(bào)錯(cuò)
需要設(shè)置關(guān)閉上傳代碼時(shí)樣式自動(dòng)補(bǔ)全,開啟可能報(bào)錯(cuò)
需要設(shè)置關(guān)閉代碼壓縮上傳,開啟可能報(bào)錯(cuò)
四、項(xiàng)目說明
1.dist是編譯(dev/build)結(jié)果目錄
2.config配置目錄
index.js(默認(rèn)配置)
const path = require(\’path\’)const config = { projectName: \’Taro-time-bus\’, date: \’2019-1-8\’, designWidth: 750,//設(shè)計(jì)稿以 iPhone6 750px 作為設(shè)計(jì)尺寸標(biāo)準(zhǔn)。 //目前 Taro 支持 750、 640 、 828 三種尺寸設(shè)計(jì)稿,他們的換算規(guī)則如下: deviceRatio: { \’640\’: 2.34 / 2, \’750\’: 1, \’828\’: 1.81 / 2 }, // 項(xiàng)目源碼目錄 sourceRoot: \’src\’, // 項(xiàng)目產(chǎn)出目錄 outputRoot: \’dist\’, // 通用插件配置 plugins: { //plugins 用來設(shè)置一些各個(gè)端通用的編譯過程配置,例如 babel 配置,JS/CSS 壓縮配置等。 babel: { sourceMap: true, presets: [\’env\’], plugins: [\’transform-decorators-legacy\’, \’transform-class-properties\’, \’transform-object-rest-spread\’] } /* //設(shè)置打包過程中的 JS 代碼壓縮 uglify: { enable: true, config: { // 配置項(xiàng)同 https://github.com/mishoo/UglifyJS2#minify-options } }, //設(shè)置打包過程中的 CSS 代碼壓縮 csso: { enable: true, config: { // 配置項(xiàng)同 https://github.com/css/csso#minifysource-options } }*/ }, // 全局變量設(shè)置 defineConstants: { context:{ iconPath:\’xxx\’ } }, alias: { \’@components\’: path.resolve(__dirname,\’../src/components\’), \’@icons\’: path.resolve(__dirname,\’../src/icons\’), \’@src\’: path.resolve(__dirname,\’../src/\’), \’@utils\’: path.resolve(__dirname, \’..\’, \’src/utils\’) }, weapp: { //小程序編譯過程的相關(guān)配置。 compile: { compressTemplate: true,//決定小程序打包時(shí)是否需要壓縮 wxml }, module: { postcss: { autoprefixer: { enable: true, config: { browsers: [ \’last 3 versions\’, \’Android >= 4.1\’, \’ios >= 8\’ ] } }, pxtransform: { enable: true, config: { onePxTransform: true, //設(shè)置 1px 是否需要被轉(zhuǎn)換 unitPrecision: 5,//REM 單位允許的小數(shù)位。 selectorBlackList: [],//黑名單里的選擇器將會(huì)被忽略。 replace: true,//直接替換而不是追加一條進(jìn)行覆蓋。 mediaQuery: false,//允許媒體查詢里的 px 單位轉(zhuǎn)換 minPixelValue: 0//設(shè)置一個(gè)可被轉(zhuǎn)換的最小 px 值 } }, url: { enable: true, config: { limit: 10240 // 設(shè)定轉(zhuǎn)換尺寸上限 } }, cssModules: { enable: false, // 默認(rèn)為 false,如需使用 css modules 功能,則設(shè)為 true config: { namingPattern: \’module\’, // 轉(zhuǎn)換模式,取值為 global/module generateScopedName: \'[name]__[local]___[hash:base64:5]\’ } } } } }, h5: { devServer: { port: 10086 }, publicPath: \’/\’, //設(shè)置輸出解析文件的目錄。 staticDirectory: \’static\’,//h5 編譯后的靜態(tài)文件目錄。 esnextModules: [\’taro-ui\’],//配置需要額外的編譯的源碼模塊,比如taro-ui: miniCssExtractPluginOption: { filename: \’css/[name]/[hash].css\’, chunkFilename: \’css/[name]/[hash].css\’ }, module: { postcss: { autoprefixer: { enable: true, config: { browsers: [ \’last 3 versions\’, \’Android >= 4.1\’, \’ios >= 8\’ ] } }, cssModules: { enable: false, // 默認(rèn)為 false,如需使用 css modules 功能,則設(shè)為 true config: { namingPattern: \’module\’, // 轉(zhuǎn)換模式,取值為 global/module generateScopedName: \'[name]__[local]___[hash:base64:5]\’ } } } } }}module.exports = function (merge) { if (process.env.NODE_ENV === \’development\’) { return merge({}, config, require(\’./dev\’)) } return merge({}, config, require(\’./prod\’))}
dev.js
module.exports = { env: { NODE_ENV: \’\”development\”\’, API_HOSTNAME:JSON.stringify(\’https://appdev.ibuscloud.com\’),//test環(huán)境地址 }, defineConstants: { }, weapp: { }, h5: {}}
3.入口文件為 app.js
import \’@tarojs/async-await\’import Taro, { Component } from \’@tarojs/taro\’import { Provider } from \’@tarojs/redux\’import \’taro-ui/dist/style/index.scss\’ // 全局引入一次即可import Index from \’./pages/index\’import configStore from \’./store\’import \’./app.less\’// 如果需要在 h5 環(huán)境中開啟 React Devtools// 取消以下注釋:// if (process.env.NODE_ENV !== \’production\’ && process.env.TARO_ENV === \’h5\’) {// require(\’nerv-devtools\’)// }const store = configStore()class App extends Component { config = { pages: [ \’pages/index/index\’, \’pages/search/index\’, \’pages/lineDetail/index\’, \’pages/page2/index\’ ], \”permission\”: { \”scope.userLocation\”: { \”desc\”: \”你的位置信息將用于小程序位置接口的效果展示\” } }, window: { backgroundTextStyle: \’light\’, navigationBarBackgroundColor: \’#fff\’, navigationBarTitleText: \’WeChat\’, navigationBarTextStyle: \’black\’ } } componentDidMount () {} componentDidShow () { //獲取用戶位置,TODO:H5 Taro.getLocation().then(data=>{ console.log(data) const {latitude,longitude} = data ; Taro.setStorageSync(\’userLat\’, latitude); Taro.setStorageSync(\’userLng\’, longitude); }) } componentDidHide () {} componentCatchError () {} componentDidCatchError () {} // 在 App 類中的 render() 函數(shù)沒有實(shí)際作用 // 請(qǐng)勿修改此函數(shù) render () { return ( <Provider store={store}> <Index /> </Provider> ) }}Taro.render(<App />, document.getElementById(\’app\’))
1??其中config主要參考微信小程序的全局配置而來,在編譯成小程序時(shí),這一部分配置將會(huì)被抽離成
app.json
,而編譯成其他端,亦會(huì)有其他作用。 頁面路由在此配置,頁面背景色導(dǎo)航欄等也在此設(shè)置 2??app.js的生命周期、頁面與組件的生命周期: 而且由于入口文件繼承自 Component 組件基類,它同樣擁有組件生命周期,但因?yàn)槿肟谖募奶厥庑?,他的生命周期?不完整 ,如下
3??普通頁面生命周期與component一致
小程序?qū)S械姆椒ǎ海℉5中暫不支持)
4??組件Taro 的組件同樣是繼承自 Component 組件基類,與頁面類似,組件也必須包含一個(gè) render 函數(shù),返回 JSX 代碼。 比頁面多了一個(gè)componentWillReceiveProps。
注意 : 組件的 constructor 與 render 提前調(diào)用,所以componentWillMount這個(gè)生命周期有一個(gè)滯后性,不可以直接在render中用路由得來的數(shù)據(jù)
render () { // 在 willMount 之前無法拿到路由參數(shù) const abc = this.$router.params.abc return <Custom adc={abc} />}
?
// 正確寫法componentWillMount () { const abc = this.$router.params.abc this.setState({ abc })}render () { // 增加一個(gè)兼容判斷 return this.state.abc && <Custom adc={abc} />}
? 由于微信小程序里頁面在 onLoad 時(shí)才能拿到頁面的路由參數(shù),而頁面 onLoad 前組件都已經(jīng) attached 了。因此頁面的 componentWillMount 可能會(huì)與預(yù)期不太一致。
五、開發(fā)中的問題
1.靜態(tài)資源的引入
1??通過ES6的import引用圖片、js、等文件(暫不支持svg),而且不需要安裝任何 loader。 2??可以先上傳到服務(wù)器,然后引用服務(wù)器的地址(在less中background用的多一點(diǎn))
全局原始app.less 只會(huì)影響到頁面級(jí)別的文件,組件的獲取不到全局的樣式 可以同過@import 讓組件獲得app.less全局樣式
@import \”../../app\”;
2.jsx的支持程度:
- 不能在包含 JSX 元素的 map 循環(huán)中使用 if 表達(dá)式
- 不能使用 Array#map 之外的方法操作 JSX 數(shù)組
- [不能在 JSX 參數(shù)中使用匿名函數(shù)](自 v1.2.9 開始支持注意:在各小程序端,使用匿名函數(shù),尤其是在 循環(huán)中 使用匿名函數(shù),比使用 bind 進(jìn)行事件傳參用更大的內(nèi)存,速度也會(huì)更慢。)(https://github.com/NervJS/taro/blob/master/packages/eslint-plugin-taro/docs/no-anonymous-function-in-props.md)
- 暫不支持在 render() 之外的方法定義 JSX
- 不能在 JSX 參數(shù)中使用對(duì)象展開符
<View {…this.props} /><View {…props} /><Custom {…props} />
以上是錯(cuò)誤寫法
- 不支持無狀態(tài)組件(必須return)
3.組件化 & props &state
1??使用 PropTypes 檢查類型
Greeting.propTypes = { name: PropTypes.string};
2??給組件設(shè)置 defaultProps
3??組件傳遞函數(shù)屬性名以 on 開頭
4??當(dāng)組件傳入jsx的時(shí)候必須用render開頭,在小程序中其實(shí)是通過slot插槽來實(shí)現(xiàn)的,所以和this.props.children一樣, this.props.children && this.props.children、this.props.children[0] 在 Taro 中都是非法的。且組合只能傳入單個(gè) JSX 元素,不能傳入其它任何類型。當(dāng)你需要進(jìn)行一些條件判斷或復(fù)雜邏輯操作的時(shí)候,可以使用一個(gè) Block 元素包裹住,然后在 Block 元素的里面填充其它復(fù)雜的邏輯。
state:
5??state this.state 和 props 一定是異步更新的,所以你不能在 setState 馬上拿到 state 的值
6??不要在
state
與
props
上用同名的字段,因?yàn)檫@些被字段在微信小程序中都會(huì)掛在
data
上 7??盡量避免在 componentDidMount 中調(diào)用 this.setState 因?yàn)樵?componentDidMount 中調(diào)用 this.setState 會(huì)導(dǎo)致觸發(fā)更新 (盡量避免,可以componentWillMount 中處理) 不要在 componentWillUpdate/componentDidUpdate/render 中調(diào)用 this.setState
5.事件
事件類型參照微信小程序https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html
6.路由
1??傳參:
Taro.navigateTo({ url: \’/pages/page/path/name?id=2&type=test\’})
接收:this.$router.params
2??預(yù)加載傳參 在微信小程序中,從調(diào)用 Taro.navigateTo、Taro.redirectTo 或 Taro.switchTab 后,到頁面觸發(fā) componentWillMount 會(huì)有一定延時(shí)。因此一些網(wǎng)絡(luò)請(qǐng)求可以提前到發(fā)起跳轉(zhuǎn)前一刻去請(qǐng)求。
傳參:
this.$preload({ x: 1, y: 2})Taro.navigateTo({ url: \’/pages/B/B\’ })(也能夠繞過 componentWillMount 延時(shí))接收componentWillMount () { console.log(\’preload: \’, this.$router.preload)}
7異步編程 以及 接口請(qǐng)求
$ yarn add @tarojs/async-await import \’@tarojs/async-await\’ 異步dispatch,action.js:
import \’@tarojs/async-await\’import { ADD, MINUS, ASYNC, ASYNC_BEFORE} from \’./action-type\’;import Http from \’../../api/Server\’import Url from \’../../api/url\’;export const add = () => { return { type: ADD }}export const minus = () => { return { type: MINUS }}export const asyncAdd = (params) => { // 返回函數(shù),異步dispatch return async dispatch => { try{ dispatch({ type: ASYNC_BEFORE, }) let result = await Http.request(\’post\’,Url.lineRecommendQuery,params); // 如果不成功,則將不成功的信息打印出來 if(result){ if(!result.success) console.error(result.message); dispatch({ type: ASYNC, response: result, }) } }catch(err){ console.error(err); } }}
Http 請(qǐng)求工具類:api.js
import Taro from \’@tarojs/taro\’class Http { constructor(){ const HOSTNAME = process.env.API_HOSTNAME this.url={ } } request(method = \’post\’, url, params) { console.log(process.env.TARO_ENV) Taro.showNavigationBarLoading(); return new Promise((resolve, reject) => { Taro.request({ url, method, data: params, header: { \’Content-Type\’: \’application/x-www-form-urlencoded\’, \’Accept\’: \’*/*\’ } }).then(res => { Taro.hideNavigationBarLoading() resolve(typeof res.data === \’object\’ ? res.data : JSON.parse(res.data)) }, err => { Taro.hideNavigationBarLoading() reject(err) }) }) }}export default new Http();
版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請(qǐng)發(fā)送郵件至 舉報(bào),一經(jīng)查實(shí),本站將立刻刪除。