Taro学习笔记
0. 快速链接
1. 安装Taro
-
目前Taro仅提供一种开发方式:安装 Taro 命令行工具(Taro CLI)进行开发
-
Taro CLI 依赖于 Node.js 环境。
-
如果Node.js环境存在,输入
npm i -g @tarojs/cli安装, 安装好之后,输入taro验证:
(base) jiexu:~/ $ taro [12:01:03]
👽 Taro v4.1.6
2. 初始化项目
2.1 命令行初始化
taro init
Taro 即将创建 一个新项目!
Need help? Go and open issue: https://tls.jd.com/taro-issue-helper
? 请输入项目名称! 001-docs-larning-notes
? 请输入项目介绍 will add latter
? 请选择框架 (Use arrow keys)
❯ React
PReact
Vue3
Solid
? 是否需要使用 TypeScript ? (Y/n)
? 请选择 CSS 预处理器(Sass/Less/Stylus) 无
? 请选择包管理工具 npm
? 请选择编译工具 Webpack5
? 请选择模板源 Github(最新)
✔ 拉取远程模板仓库成功!
? 请选择模板
wxcloud(云开发模板)
wxplugin
youshu(腾讯有数统计模板(https://nervjs.github.io/taro/docs/youshu))
❯ 默认模板
mobx
pwa
react-NutUI(NutUI + React 模板(https://nutui.jd.com/react/))
(Move up and down to reveal more choices)
3. 认识项目
3.1 入口组件
- 每一个 Taro 项目都有一个入口组件和一个入口配置,我们可以在入口组件中设置全局状态/全局生命周期,一个最小化的入口组件会是这样:
// src/app.js
import React, { Component } from 'react'
import './app.css'
class App extends Component {
render() {
// this.props.children 是将要会渲染的页面
return this.props.children
}
}
// 每一个入口组件都必须导出一个 React 组件
export default App
// src/app.js
import Vue from 'vue'
import './app.css'
const App = {
render(h) {
// this.$slots.default 是将要会渲染的页面
return h('block', this.$slots.default)
},
}
export default App
每一个入口组件(例如 app.js)总是伴随一个全局配置文件(例如 app.config.js),我们可以在全局配置文件中设置页面组件的路径、全局窗口、路由等信息,一个最简单的全局配置如下:
export default {
pages: ['pages/index/index'],
}
export default {
pages: ['pages/index/index'],
}
你可能会注意到,不管是 React 还是 Vue,两者的全局配置是一样的。
这是因为在配置文件中,Taro 并不关心框架的区别,Taro CLI 会直接在编译时在 Node.js 环境直接执行全局配置的代码,并把 export default 导出的对象序列化为一个 JSON 文件。
因此,我们必须保证配置文件是在 Node.js 环境中是可以执行的,不能使用一些在 H5 环境或小程序环境才能运行的包或者代码,否则编译将会失败。
3.2 页面组件
页面组件是每一项路由将会渲染的页面,Taro 的页面默认放在 src/pages 中,每一个 Taro 项目至少有一个页面组件
一个简单的页面组件如下:
//src/pages/index/index.jsx
import { View } from '@tarojs/components'
class Index extends Component {
state = {
msg: 'Hello World!',
}
onReady() {
console.log('onReady')
}
render() {
return <View>{this.state.msg}</View>
}
}
export default Index
// src/pages/index/index.vue
<template>
<view> {{ msg }} </view>
</template>
<script>
export default {
data() {
return {
msg: 'Hello World!',
}
},
onReady() {
console.log('onReady')
},
}
</script>
与react和vue的细微差别
-
onReady生命周期函数。这是来源于微信小程序规范的生命周期,表示组件首次渲染完毕,准备好与视图交互。Taro 在运行时将大部分小程序规范页面生命周期注入到了页面组件中,同时 React 或 Vue 自带的生命周期也是完全可以正常使用的。 -
View组件。这是来源于@tarojs/components的跨平台组件。相对于我们熟悉的 div、span 元素而言,在 Taro 中我们要全部使用这样的跨平台组件进行开发。
和入口组件一样,每一个页面组件(例如 index.vue)也会有一个页面配置(例如 index.config.js),我们可以在页面配置文件中设置页面的导航栏、背景颜色等参数,一个最简单的页面配置如下:
// src/pages/index/index.config.js
export default {
navigationBarTitleText: '首页',
}
3.3 自定义组件
- 我们先把首页写好,首页的逻辑很简单:把论坛最新的帖子展示出来。
// src/pages/index/index.jsx
import Taro from '@tarojs/taro'
import React from 'react'
import { View } from '@tarojs/components'
import { ThreadList } from '../../components/thread_list'
import api from '../../utils/api'
import './index.css'
class Index extends React.Component {
config = {
navigationBarTitleText: '首页',
}
state = {
loading: true,
threads: [],
}
async componentDidMount() {
try {
const res = await Taro.request({
url: api.getLatestTopic(),
})
this.setState({
threads: res.data,
loading: false,
})
} catch (error) {
Taro.showToast({
title: '载入远程数据错误',
})
}
}
render() {
const { loading, threads } = this.state
return (
<View className="index">
<ThreadList threads={threads} loading={loading} />
</View>
)
}
}
export default Index
// src/pages/index/index.vue
<template>
<view class="index">
<thread-list :threads="threads" :loading="loading" />
</view>
</template>
<script>
import Vue from 'vue'
import Taro from '@tarojs/taro'
import api from '../../utils/api'
import ThreadList from '../../components/thread_list.vue'
export default {
components: {
'thread-list': ThreadList,
},
data() {
return {
loading: true,
threads: [],
}
},
async created() {
try {
const res = await Taro.request({
url: api.getLatestTopic(),
})
this.loading = false
this.threads = res.data
} catch (error) {
Taro.showToast({
title: '载入远程数据错误',
})
}
},
}
</script>
- 在我们的首页组件里,还引用了一个 ThreadList 组件,我们现在来实现它:
// src/components/thread_list.jsx
import React from 'react'
import { View, Text } from '@tarojs/components'
import { Thread } from './thread'
import { Loading } from './loading'
import './thread.css'
class ThreadList extends React.Component {
static defaultProps = {
threads: [],
loading: true,
}
render() {
const { loading, threads } = this.props
if (loading) {
return <Loading />
}
const element = threads.map((thread, index) => {
return (
<Thread
key={thread.id}
node={thread.node}
title={thread.title}
last_modified={thread.last_modified}
replies={thread.replies}
tid={thread.id}
member={thread.member}
/>
)
})
return <View className="thread-list">{element}</View>
}
}
export { ThreadList }
src/components/thread.jsx
import Taro, { eventCenter } from '@tarojs/taro'
import React from 'react'
import { View, Text, Navigator, Image } from '@tarojs/components'
import api from '../utils/api'
import { timeagoInst, Thread_DETAIL_NAVIGATE } from '../utils'
class Thread extends React.Component {
handleNavigate = () => {
const { tid, not_navi } = this.props
if (not_navi) {
return
}
eventCenter.trigger(Thread_DETAIL_NAVIGATE, this.props)
// 跳转到帖子详情
Taro.navigateTo({
url: '/pages/thread_detail/thread_detail',
})
}
render() {
const { title, member, last_modified, replies, node, not_navi } = this.props
const time = timeagoInst.format(last_modified * 1000, 'zh')
const usernameCls = `author ${not_navi ? 'bold' : ''}`
return (
<View className="thread" onClick={this.handleNavigate}>
<View className="info">
<View>
<Image src={member.avatar_large} className="avatar" />
</View>
<View className="middle">
<View className={usernameCls}>{member.username}</View>
<View className="replies">
<Text className="mr10">{time}</Text>
<Text>评论 {replies}</Text>
</View>
</View>
<View className="node">
<Text className="tag">{node.title}</Text>
</View>
</View>
<Text className="title">{title}</Text>
</View>
)
}
}
export { Thread }
// src/components/thread_list.vue
<template>
<view className="thread-list">
<loading v-if="loading" />
<thread
v-else
v-for="t in threads"
:key="t.id"
:node="t.node"
:title="t.title"
:last_modified="t.last_modified"
:replies="t.replies"
:tid="t.id"
:member="t.member"
/>
</view>
</template>
<script>
import Vue from 'vue'
import Loading from './loading.vue'
import Thread from './thread.vue'
export default {
components: {
loading: Loading,
thread: Thread,
},
props: {
threads: {
type: Array,
default: [],
},
loading: {
type: Boolean,
default: true,
},
},
}
</script>