Taro可以把你的原生开发的微信小程序转换成Taro代码,具体步骤如下:
安装:
npm i -g @tarojs/cli
如果你的原生小程序引入了regeneratorRuntime
也就是控制小程序异步解决方案的async
/await
,那么你的在转换之前可以全部把这些引入的文件删除掉,入口文件 app.js
中直接 import
@tarojs/async-await
这个插件,就可以开始使用 async functions
功能了。
$ npm install --save @tarojs/async-await
/* src/app.js */
import '@tarojs/async-await'
你的小程序根目录下运行
$ taro convert
即可完成转换。转换后的代码保存在根目录下的 taroConvert
文件夹下。
[外链图片转存失败(img-hTCpfTXX-1564630927403)(http://maint.deeptel.com.cn/upload/M00/00/84/CgIKO10cGbiAd2z1AABPRWJvfak945.png)]
转换完成后直接取出 taroConvert
目录里面的代码执行 npm install
命令之后就可以使用 npm run build
命令编译到对应平台的代码。
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch"
在taroConvert
文件中执行npm run build:weapp
得到下面的文件目录,每个项目不同的文件结构在转换后会有一些不一样,主要区别是在pages
文件的分包结构上。
├── dist 编译结果目录
├── config 配置目录
| ├── dev.js 开发时配置
| ├── index.js 默认配置
| └── prod.js 打包时配置
├── src 源码目录
| ├── pages 页面文件目录
| | ├── index index 页面目录
| | | ├── index.js index 页面逻辑
| | | └── index.css index 页面样式
| ├── app.css 项目总通用样式
| └── app.js 项目入口文件
| └── utils 放置一些公共方法
└── package.json
如果你是在Taro上进行一次开发小程序,那么你在安装完成cli
后就可以直接初始化一个Taro项目。
[外链图片转存失败(img-tvK3Plgk-1564630927403)(http://maint.deeptel.com.cn/upload/M00/00/B9/CgILPF0cGdCAERFZAApD0fPYFm0114.jpg)]
本地开发打包后dist文件的代码是经过webpack
压缩合并后的文件,这些都不需要我们配置,开箱即用非常方便,不过有一个问题就是我们的socket2.0
是引入外部插件wx-socket
无法被压缩,原因是@tarojs/plugin-uglifyjs
这个插件无法压缩使用es6语法开发的包。
前面工作弄好了之后我们正式进入使用Taro开发微信小程序的阶段,初入一个新的框架我们首先得熟悉他的语法特性,Taro 是一套遵循 React 语法规范的 多端开发 解决方案。说到React就不得不说说JSX。
在 Taro 中,使用 JSX 作为一种 DSL 进而编译成各端通用的语法,React官方对于JSX的简介,React 不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。
注:DSL指的是针对特定应用领域而设计使用的计算机语言,常见的DSL有:HTML,Shell,make语言,ant语言,maven语言,rpm语言,dpkg语言,awk语言,正则表达式,dc计算机语言等,有些DSL语言又被称为微型语言
const element = Hello, world!
这种既不是字符串也不是 HTML的标签语法就是JSX ,是一个 JavaScript 的语法扩展。我们知道元素是构成 React 应用的最小单位,JSX 可以生成 React “元素”,因此React官方强烈推荐我们使用JSX 来描述用户界面。
我们这样使用:
ReactDOM.render(element, document.getElementById('root'));
对于Taro,我们看下面的代码:
import { View } from '@tarojs/components'
import Taro from '@tarojs/taro'
import './MyPage.scss'
@withWeapp('Page')
class _C extends Taro.Component {
state = {}
componentWillMount = () => {}
componentDidMount = () => {}
componentDidShow = () => {}
componentDidHide = () => {}
componentWillUnmount = () => {}
onPullDownRefresh = () => {}
onReachBottom = () => {}
onShareAppMessage = () => {}
sayHellow = () =>{}
config = {}
render() {
return Hellow World!
}
}
export default _C
每一个小程序页面都是一个Taro组件,我们不需要指定我们的JSX模板到具体某一个标签或者页面当中,我们只需要关注的是每一个视图层的变化,其他的事Taro已经帮你处理好了。如果你需要处理一些数据(如转换时间格式等)我们可以写一个格式化数据的方法然后在JSX中直接使用,当然如果是全局能够使用的最好在公共方法文件那里定义好然后在头部引入直接调用。如:
import { getTimeFilter } from '../../../utils/getTimeFilter.js'
render () {
return (
{getTimeFilter('test')}View>
)
}
Taro官方提出了使用JSX的四个有点:
JSX也能在Vue上使用,这里也附上文档链接,不过Vue官方也还是推荐你使用template
模板来创建你的HTML
毕竟在模板上比较简洁直观,再添加一个render
函数渲染JSX不是不行只是如果Vue模板能处理的就没必要再使用JSX了,毕竟在Vue上使用JSX需要引入一些插件,这就对项目增加了无谓的压力。
恰当选择合适的UI组件库能够避免我们重复造轮子,不仅能节省一部分开发时间还可以学习和借鉴别人优秀的代码。有赞的UI组件库是我们星球组移动端的UI框架,移动端h5,微信小程序都是使用有赞的vantUI,除了一些需要根据自己业务需求调整的交互需要自己手动再造轮子,总体来说这里的组件基本能满足我们的业务需求了。
Taro在引入第三方组件或者插件首先需要将第三方组件库下载到项目的 src
目录下,随后在页面或者组件里通过配置 usingComponents
指定需要引用的第三方组件即可,组件调用的时候需要按照 JSX 的使用规范来进行传参和事件绑定。
[外链图片转存失败(img-Uj1hvwqQ-1564630927404)(http://maint.deeptel.com.cn/upload/M00/00/84/CgIKO10cGeqACAgYAAA8y1hh0Tg009.png)]
[外链图片转存失败(img-tpR9EaIw-1564630927405)(http://maint.deeptel.com.cn/upload/M00/00/B9/CgILPF0cGfaAIz-yAABeAv_y_Ds481.png)]
如果是我们自己自定义的组件,组件的编写规则也是基本上跟Taro组件的语法大同小异,除了一些父子组件的传参之外我们在写组件的时候尽量像编写一个纯函数那样编写一个组件。
[外链图片转存失败(img-VaLoBXNQ-1564630927405)(http://maint.deeptel.com.cn/upload/M00/00/B9/CgILPF0cGjCAY_IvAACaaZfX-V0385.png)]
纯函数的定义是:如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。比如这个加法函数:
function sum (a, b) {
return a + b
}
对于一个’纯函数式‘的组件,我们编写所有的 Taro 组件必须像纯函数那样使用它们的 props
,props
其实就相当于我们函数的参数。
如果你之前在列表循环的时候使用了index
作为key你会在编译打包的时候收到一大堆优化建议和警告,
[外链图片转存失败(img-eTJxyTfd-1564630927405)(http://maint.deeptel.com.cn/upload/M00/00/84/CgIKO10cGj-AMObVAACJ5uxqQLs769.png)]
不论是Vue还是React的列表渲染都不建议你使用index
作为循环项目的key
传入,我们在使用Taro开发的时候如果你使用index
作为key
在项目打包的时候Taro会有一些优化的提示,所以一般在跟后端商量接口的数据返回形式的时候都会建议后端在每个列表数据返回一个项目的Id作为唯一的属性,有些情况下后端无法返回区分的Id我们只能够在前端自己组装列表的id尽量避免使用index
作为key
.
具体这里有我写的两个案例vue案例,react案例,可以尝试把循环的key换成唯一的id,再次调换位置看看效果。
如果在使用了自定义组件使用了一些背景图片或者图标而且这个图片或者图标只在组件中使用过,此时不能在标签内直接引用图片地址。
如果你在组件中这样引用图片路径,Taro在打包项目的时候不会帮你把目标图片打包到项目上,也就是说当你在组件中引用目标图片的时候会提示图片不存在。解决这个问题的办法是通过es6的import
语法引用静态文件然后在JSX中直接使用。
// 引用文件
import demo from '../../images/demo.png'
// 使用
React也有一个大名鼎鼎的状态管理器redux,Taro也支持在项目中使用redux,不过对于简单的项目我们没必要使用他,个人认为全局数据放在globalData就可以了,也可以把一些缓存数据或者需要处理特定业务逻辑的数据放在本地,数据存储生命周期跟小程序本身一致,即除用户主动删除或超过一定时间被自动清理,否则数据都一直可用。单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB。
对于事件管理,如果需要把某个事件挂载到当前的运行环境,当发生了页面跳转或者在某个页面处理完逻辑之后需要再次执行该函数的情况可以使用event.js管理你的事件。
class Event {
/**
* on 方法把订阅者所想要订阅的事件及相应的回调函数记录在 Event 对象的 _cbs 属性中
*/
on(event, fn, ctx) {
if (typeof fn != 'function') {
console.error('fn must be a function')
return
}
this._stores = this._stores || {}
;(this._stores[event] = this._stores[event] || []).push({
cb: fn,
ctx: ctx
})
}
/**
* emit 方法接受一个事件名参数,在 Event 对象的 _cbs 属性中取出对应的数组,并逐个执行里面的回调函数。
*/
emit(event) {
this._stores = this._stores || {}
let store = this._stores[event]
let args
if (store) {
store = store.slice(0)
args = [].slice.call(arguments, 1)
for (let i = 0, len = store.length; i < len; i++) {
store[i].cb.apply(store[i].ctx, args)
}
}
}
/**
* off 方法接受事件名称和当初注册的回调函数作为参数,在 Event 对象的 _cbs 属性中删除对应的回调函数
*/
off(event, fn) {
this._stores = this._stores || {}
// remove all
if (!arguments.length) {
this._stores = {}
return
}
let store = this._stores[event]
if (!store) {
return
}
// remove all handlers
if (arguments.length === 1) {
delete this._stores[event]
return
}
// remove specific handler
let cb
for (let i = 0, len = store.length; i < len; i++) {
cb = store[i].cb
if (cb === fn) {
store.splice(i, 1)
break
}
}
return
}
}
export default Event
使用:
/**
* app.js上注册事件
*/
import Event from './utils/event.js'
event = new Event()
/**
* A页面上存放事件
*/
app.event.on('myEvent', this.myEvent, this)
app.event.emit('myEvent') //释放事件
待续…