首页 » 软件优化 » Taro实战-快速开发「知乎」小程序多端应用(程序组件代码不支持开发)

Taro实战-快速开发「知乎」小程序多端应用(程序组件代码不支持开发)

少女玫瑰心 2024-11-01 12:14:21 0

扫一扫用手机浏览

文章目录 [+]

Taro 是由凹凸实验室打造的一套遵循 React 语法规范的多端统一开发框架。

使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信小程序、H5、App 端等)运行的代码。
目前 Taro 支持编译出支持微信小程序、H5 运行的代码,RN和支付宝小程序的支持还在开发中。
具体介绍请看这篇文章,github地址

2. 前言

Taro实战-快速开发「知乎」小程序多端应用(程序组件代码不支持开发) 软件优化
(图片来自网络侵删)

为了学习 Taro,本人在 github 找了知乎的小程序demo,本文通过修改该份代码,实现了 Taro 版的知乎H5、小程序 demo ,对 Taro 有兴趣的同学可以 star 或 fork 下来学习,github地址。

3. 安装

安装 Taro 开发工具 @tarojs/cli

使用 npm 或者 yarn 全局安装

npm install -g @tarojs/cli

// 或

yarn global add @tarojs/cli

下载代码

git clone https://github.com/jimczj/taro_zhihu

# 安装依赖

cd taro_zhihu

npm i

4. 运行代码

文件目录如下:

├── dist 编译结果目录

├── config 配置目录

| ├── dev.js 开发时配置

| ├── index.js 默认配置

| └── prod.js 打包时配置

├── src 源码目录

| ├── pages 页面文件目录

| | ├── index index页面目录

| | | ├── index.js index页面逻辑

| | | └── index.css index页面样式

| ├── app.css 项目总通用样式

| └── app.js 项目入口文件

└── package.json

进入项目目录开始开发,可以选择小程序预览模式,或者h5预览模式,若使用微信小程序预览模式,则需要自行下载并打开微信开发者工具,选择预览项目根目录。

微信小程序编译预览模式:

# npm script

npm run dev:weapp

# 或 仅限全局安装

taro build --type weapp --watch

H5编译预览模式:

# npm script

npm run dev:h5

# 或 仅限全局安装

taro build --type h5 --watch

5. 开发前注意

若使用 微信小程序预览模式 ,则需下载并使用微信开发者工具添加项目进行预览,此时需要注意微信开发者工具的项目设置 需要设置关闭ES6转ES5功能,开启可能报错

需要设置关闭上传代码时样式自动补全,开启可能报错

需要设置关闭代码压缩上传,开启可能报错

6. 功能实现详解

6.1 小程序全局配置

app.json文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
从原来的 app.json 转化为 Taro 项目的 app.js 几乎没有什么成本,配置基本一致。
只是有一点需要注意,静态图片资源文件夹要放在src目录下面,这样代码在编译打包的时候,才会把图片资源给复制打包过去。
我一开始将静态图片资源文件夹跟src同层级,然后各种找不到图片资源,浪费了许多时间。
好了,话不多说,看看下面代码对比,你就清楚这迁移无比轻松,转写成 React 写法,看起来也顺眼多了。

原微信小程序 app.json (有删减)代码如下:

{

'pages':[

'pages/index/index',

'pages/discovery/discovery',

'pages/more/more',

'pages/answer/answer',

'pages/question/question'

],

'window':{

'backgroundTextStyle':'light',

'navigationBarBackgroundColor': '#0068C4',

'navigationBarTitleText': '知乎',

'navigationBarTextStyle':'white',

'enablePullDownRefresh':true

},

'tabBar': {

'color': '#626567',

'selectedColor': '#2A8CE5',

'backgroundColor': '#FBFBFB',

'borderStyle': 'white',

'list': [{

'pagePath': 'pages/index/index',

'text': '首页',

'iconPath': 'images/index.png',

'selectedIconPath': 'images/index_focus.png'

}, {

'pagePath': 'pages/discovery/discovery',

'text': '发现',

'iconPath': 'images/discovery.png',

'selectedIconPath': 'images/discovery_focus.png'

}, {

'pagePath': 'pages/more/more',

'text': '我的',

'iconPath': 'images/burger.png',

'selectedIconPath': 'images/burger_focus.png'

}]

}

}

转写成Taro 代码如下:

import Taro, { Component } from '@tarojs/taro'

import Index from './pages/index'

import './app.scss'

class App extends Component {

config = {

pages: [

'pages/index/index',

'pages/discovery/discovery',

'pages/more/more',

'pages/answer/answer',

'pages/question/question'

],

window: {

backgroundTextStyle: 'light',

navigationBarBackgroundColor: '#0068C4',

navigationBarTitleText: 'Taro知乎',

navigationBarTextStyle: 'white',

enablePullDownRefresh: true

},

tabBar: {

color: '#626567',

selectedColor: '#2A8CE5',

backgroundColor: '#FBFBFB',

borderStyle: 'white',

list: [{

pagePath: 'pages/index/index',

text: '首页',

iconPath: './asset/images/index.png',

selectedIconPath: './asset/images/index_focus.png'

},{

pagePath: 'pages/discovery/discovery',

text: '发现',

iconPath: './asset/images/discovery.png',

selectedIconPath: './asset/images/discovery_focus.png'

},

{

pagePath: 'pages/more/more',

text: '我的',

iconPath: './asset/images/burger.png',

selectedIconPath: './asset/images/burger_focus.png'

}]

}

}

render () {

return (

<Index />

)

}

}

Taro.render(<App />, document.getElementById('app'))

6.2 首页

页面效果如下:

功能描述:上滑刷新,下拉加载更多,数据请求,点击跳转

刷新及继续加载的动作, 依靠的是ScrollView组件,并在组件上绑定 onScrolltoupper 和 onScrolltolower 来绑定滚动到顶部及底部所触发的事件, 同时 upperThreshold 和lowerThreshold 能够调整触发时距边界的距离。

数据请求 api 使用 Taro.request, 跟wx.request 使用方法基本一致,不同的是 Taro.request 天然支持 promise 化,mock 数据使用 easy-mock 来提供 。

点击跳转使用 Taro.navigateTo,跟 wx.navigateTo 也基本一致,在写跳转的时候,一开始想用匿名函数的形式写,发现 Taro 目前还不支持,据说就要支持了,Taro 的迭代速度还是很快的。

将首页进行页面重构的时候,遇到最费时间的问题,应该是wxss转化为scss,Taro 的组件转化为微信小程序和 web 后,标签是不一样的,比如Image组件会变成image或img标签,然而原来的wxss里面使用了标签名来命名 css,这样一来就造成了微信小程序跟 web 样式表现不一致。
所以不建议使用标签名命名 css,建议直接用 class。

export default class Index extends Component {

config = {

navigationBarTitleText: '首页'

}

constructor() {

super(...arguments)

this.state = {

loading:true,

list:[]

}

}

componentDidMount () {

// 获取远程数据

this.updateList()

}

updateList() {

Taro.showLoading({title: '加载中'})

Taro.request({

url: 'https://easy-mock.com/mock/5b21d97f6b88957fa8a502f2/example/feed'

}).then(res => {

Taro.hideLoading()

if (res.data.success) {

this.setState({

loading:false,

list:res.data.data

})

}

})

}

appendNextPageList() {

Taro.showLoading({title: '加载中'})

Taro.request({

url: 'https://easy-mock.com/mock/5b21d97f6b88957fa8a502f2/example/feed'

}).then(res => {

Taro.hideLoading()

if (res.data.success) {

this.setState({

list: this.state.list.concat(res.data.data)

})

}

})

}

render () {

return (<ScrollView className='container'

scrollY

scrollWithAnimation

scrollTop='0'

lowerThreshold='10'

upperThreshold='10'

onScrolltoupper={this.updateList}

onScrolltolower={this.appendNextPageList}

>

<View className='search flex-wrp'>

<View className='search-left flex-item'>

<View className='flex-wrp'>

<View className='flex1'><Image src={searchPng}></Image></View>

<View className='flex6'><Input type='text' placeholder='搜索话题, 问题或人' placeholderClass='search-placeholder'/></View>

</View>

</View>

<View className='search-right flex-item'>

<Image src={lightingPng}></Image>

</View>

</View>

{

this.state.loading

? <View className='txcenter'><Text>加载中</Text></View>

: this.state.list.map((item,index)=>{

return <Feed key={item} />})

}

</ScrollView>

)

}

}

6.3 Taro 组件

微信小程序的 Component 组件跟Page 页面的生命周期函函数不一致很让人头疼,页面的生命周期方法有 onLoad、onReady、onUnload 等,而到了组件中则是 created、attached 、ready 等,相比之下 Taro 就比较统一了,不管是页面还是组件,写法都跟 React 的生命周期一致,统一的api开发起来也顺畅了许多。

然而 Taro 组件目前还是有很多局限,比如,不支持直接渲染 children, 即不支持 this.props.children;props 不能传递jsx;

在抽象组件的时候,主要有以下注意点

在写法上,Taro 组件首字母要大写并采用驼峰命名法,比如在 wxml里面的标签是view、scroll-view、image,在 Taro 要写成View、ScrollView、Image。
Taro 的事件绑定事件绑定都以 on 开头并采用驼峰命名法

// 小程序代码

<scroll-view scroll-y='true' class='lanren49b1967d5a03a834 Class='search-placeholder' bindscrolltoupper='upper' upper-threshold='10' lower-threshold='5' bindscrolltolower='lower'>

</scroll-view>

// Taro 代码

<ScrollView className='container'

scrollY

scrollWithAnimation

scrollTop='0'

lowerThreshold='10'

upperThreshold='10'

onScrolltoupper={this.upper.bind(this)}

onScrolltolower={this.lower.bind(this)}

>

</ScrollView>

小程序引用本地静态资源直接在 src 写上相对路径,Taro 引用本地静态资源需要先 import进来再使用,为了让 h5 部署的时候图片路径不出错,最好把图片放在服务器上,然后直接写 http 路径

// 小程序 引用本地静态资源

<image src='../../images/search.png'></image>

// Taro 引用本地静态资源

import searchPng from '../../asset/images/search.png'

// ...此处省略无数代码

<Image src={searchPng}></Image>

// 最好把图片放在服务器上,然后写http 路径

<Image src='https://image.ibb.co/kUissy/search.png'></Image>

遍历列表的区别,小程序使用模版语言,而 Taro 使用 jsx

// 小程序

<block wx:for='{{feed}}' wx:for-index='idx' wx:for-item='item' data-idx='{{idx}}'>

<view class='lanren967d5a03a834c75c container'>

...

</view>

</block>

// Taro 代码

{

this.state.list.map((item,index)=>{

return <Feed {...item} key={index} />

})

}

答案组件在抽象过程中,想直接写成纯函数形式

// 暂不支持这种写法

const Text = ({ answer }) =>

<p>{answer}</p>

发现目前还不支持,于是老老实实写成class的形式了:

export default class Feed extends Component {

navigateTo(url) {

Taro.navigateTo({url:url})

}

render() {

return (

<View className='feed-item'>

<View className='feed-source'>

<View className='avatar flex1'>

<Image src={this.props.feed_source_img}></Image>

</View>

<View className='flex8'>

<Text className='feed-source-txt'>{this.props.feed_source_name}{this.props.feed_source_txt}</Text>

</View>

<View className='flex1'>

<Image className='item-more' mode='aspectFit' src={more}></Image>

</View>

</View>

<View className='feed-content'>

<View className='question' onClick={this.navigateTo.bind(this,'/pages/question/question')}>

<View className='question-link'>

<Text>{this.props.question}</Text>

</View>

</View>

<View className='answer-body'>

<View>

<Text className='answer-txt' onClick={this.navigateTo.bind(this,'/pages/answer/answer')} >{this.props.answer_ctnt}</Text>

</View>

<View className='answer-actions'>

<View className='like dot'>

<View>{this.props.good_num} 赞同 </View>

</View>

<View className='comments dot'>

<View>{this.props.comment_num} 评论 </View>

</View>

<View className='follow-it'>

<View>关注问题</View>

</View>

</View>

</View>

</View>

</View>

)

}

}

在使用的组件的时候,想使用{...item}的方式来解构赋值,发现也暂时不支持,内心有点奔溃。

不过所幸的是,作者的错误提醒很人性化,没有让我在这里浪费很多时间调试,于是我就老老实实一个一个赋值啦。

this.state.list.map((item,index) => {

return <Feed

feed_source_img={item.feed_source_img}

feed_source_txt={item.feed_source_txt}

question={item.question}

answer_ctnt={item.answer_ctnt}

good_num={item.good_num}

comment_num={item.comment_num}

key={index} />

})

6.4 “发现页面”的 tab 切换功能

tab切换原理: 在组件上绑定 onClick 事件 改变 this.state.currentNavtab 值,再通过判断来实现 tab 切换,函数参数传递方式为 this.switchTab.bind(this,index),具体代码如下:

export default class Discovery extends Component {

constructor() {

super(...arguments)

this.state = {

currentNavtab: 0,

navTab: ['推荐', '圆桌', '热门', '收藏'],

}

}

switchTab(index,e) {

this.setState({

currentNavtab: index

});

}

render () {

return (

<View>

<View className='top-tab flex-wrp flex-tab' >

{

this.state.navTab.map((item,index) => {

return (<View className={this.state.currentNavtab === index ? 'toptab flex-item active' : 'toptab flex-item' } key={index} onClick={this.switchTab.bind(this,index)}>

{item}

</View>)

})

}

</View>

<ScrollView scroll-y className='container discovery withtab'>

<View className='ctnt0' hidden={this.state.currentNavtab==0 ? false : true}>

...

</View>

<View className='txcenter' hidden={this.state.currentNavtab==1 ? false : true}>

<Text>圆桌</Text>

</View>

<View className='txcenter' hidden={this.state.currentNavtab==2 ? false : true}>

<Text>热门</Text>

</View>

<View className='txcenter' hidden={this.state.currentNavtab==3 ? false : true}>

<Text>收藏</Text>

</View>

</ScrollView>

</View>

)

}

}

6.5 轮播功能

轮播页使用了 Swiper 组件,参数跟小程序都是一一对应,具体可以查看详细文档,在重构过程也主要是把 wxml 换成 jsx 的形式。

<Swiper className='activity' indicatorDots='true'

autoplay='true' interval='5000' duration='500'>

{this.state.imgUrls.map((item,index) => {

return (<SwiperItem key={index}>

<Image src={item} className='slide-image' width='355' height='375' />

</SwiperItem>)

})}

</Swiper>

7. 使用中发现的问题总结

Taro 的组件转化为微信小程序跟web后,标签是不一样的,比如Image组件会变成image或img标签,所以不建议使用标签名命名css,建议直接用 class目前的事件绑定不支持匿名函数

// 不支持

<View onClick={()=> this.navigateTo('/pages/answer/answer')} > </View>

Taro 组件在 web 端有许多属性和事件暂不支持运行环境不同,某些 api 需要根据环境不同处理,比如 wx.getUserInfo 在 web 端是不存在的,此时我们需要判断环境来执行代码

if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) {

// 小程序环境

} else if (Taro.getEnv() === Taro.ENV_TYPE.WEB ) {

// WEB(H5)环境

}

Taro 目前不支持 SVG(毕竟小程序不支持)目前的第三方库还比较少(毕竟 Taro 刚出来不久,希望接下来社区能出来各种 ui 库)Taro 组件暂时不支持纯函数,不支持解构赋值 {...item}

8. 总结

将该项目迁移到 Taro 成本并不高,绝大多数工作是做语法的变换。
Taro采用 React 的写法写微信小程序,总体体验是非常不错的,有 React 开发经验的小伙伴相信能很快上手。
同一份代码就能同时支持web端跟小程序端,确实很让人惊艳,虽然目前 web 端的支持还比较薄弱,但方向是没有错的,接下来只是时间问题。
期待 Taro 在接下来的表现。
最后对该项目有兴趣的同学可以下载代码下来跑一跑 github地址。

标签:

相关文章