第242集:Vue 3 Taro多端开发深度指南

概述

Taro是一个开放式跨端跨框架解决方案,支持使用React/Vue/Nerv等框架来开发微信小程序、H5、React Native等应用。本集将深入探讨Taro框架的核心概念、项目结构、页面开发、组件开发、API调用以及跨平台适配等内容,帮助你掌握Taro多端开发的深度使用技巧。

一、Taro框架核心概念

1.1 Taro的设计理念

  • 开放式跨端跨框架:支持React、Vue、Nerv等多种前端框架
  • 编译时框架:通过编译将源代码转换为各平台可执行的代码
  • 组件化开发:采用组件化思想,提高代码复用性
  • API统一封装:提供统一的API调用方式,屏蔽平台差异
  • 小程序优先:基于小程序设计理念,同时支持多端开发

1.2 Taro支持的平台

  • 小程序:微信小程序、支付宝小程序、百度小程序、头条小程序、QQ小程序、京东小程序
  • H5:响应式网页
  • React Native:iOS、Android原生应用
  • HarmonyOS:华为HarmonyOS应用

1.3 Taro与其他跨平台框架对比

框架 特点 适用场景
Taro 跨框架支持、编译时方案、小程序优先 需要覆盖多个小程序平台的应用
Uni-app Vue语法、运行时方案、一套代码多端运行 熟悉Vue生态的开发者
Flutter 原生性能、Dart语言、UI一致性好 注重性能和UI一致性的应用
React Native JavaScript语言、原生组件、社区活跃 熟悉React生态的开发者

二、Taro环境搭建与项目创建

2.1 开发环境配置

  1. 安装Node.js:确保Node.js版本>=14.0.0
  2. 安装Taro CLI:Taro官方命令行工具
  3. 安装微信开发者工具:用于微信小程序预览和调试
  4. 安装React Native开发环境(可选):用于React Native应用开发
  5. 安装HarmonyOS开发环境(可选):用于HarmonyOS应用开发

2.2 安装Taro CLI

# 使用npm安装
npm install -g @tarojs/cli

# 使用yarn安装
yarn global add @tarojs/cli

# 验证安装
$ taro --version
3.6.0

2.3 创建Taro项目

# 创建Vue 3项目
taro init my-taro-project

# 选择框架:Vue 3
# 选择模板:默认模板
# 选择CSS预处理器:Sass/SCSS
# 选择状态管理:Pinia
# 选择编译工具:Webpack5

2.4 项目目录结构

├── config/               # 配置目录
│   ├── dev.js            # 开发环境配置
│   ├── index.js          # 默认配置
│   └── prod.js           # 生产环境配置
├── src/                  # 源代码目录
│   ├── app.config.ts     # 应用配置
│   ├── app.vue           # 应用入口组件
│   ├── components/       # 自定义组件目录
│   ├── pages/            # 页面目录
│   │   └── index/        # 首页
│   │       ├── index.config.ts  # 页面配置
│   │       └── index.vue        # 页面组件
│   ├── store/            # 状态管理目录
│   └── utils/            # 工具函数目录
├── package.json          # 项目依赖配置
└── tsconfig.json         # TypeScript配置

三、Taro页面开发

3.1 页面配置

// src/pages/index/index.config.ts
export default {
  navigationBarTitleText: '首页',
  navigationBarBackgroundColor: '#ffffff',
  navigationBarTextStyle: 'black',
  enablePullDownRefresh: true,
  onReachBottomDistance: 50
};

3.2 页面生命周期

<template>
  <view class="page-container">
    <text>页面内容</text>
  </view>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { useDidShow, useDidHide, usePullDownRefresh, useReachBottom } from '@tarojs/taro';

// Vue 3 生命周期
onMounted(() => {
  console.log('Vue onMounted');
});

onUnmounted(() => {
  console.log('Vue onUnmounted');
});

// Taro 页面生命周期
useDidShow(() => {
  console.log('页面显示');
});

useDidHide(() => {
  console.log('页面隐藏');
});

usePullDownRefresh(() => {
  console.log('下拉刷新');
  // 停止下拉刷新
  Taro.stopPullDownRefresh();
});

useReachBottom(() => {
  console.log('上拉触底');
});
</script>

3.3 页面路由与跳转

3.3.1 声明式导航

<!-- 普通跳转 -->
<navigator url="/pages/detail/detail">
  跳转到详情页
</navigator>

<!-- 带参数跳转 -->
<navigator url="/pages/detail/detail?id=1&name=test">
  跳转到详情页
</navigator>

<!-- 重定向 -->
<navigator url="/pages/login/login" open-type="redirect">
  重定向到登录页
</navigator>

<!-- 切换Tab -->
<navigator url="/pages/mine/mine" open-type="switchTab">
  切换到我的页面
</navigator>

3.3.2 编程式导航

import Taro from '@tarojs/taro';

// 普通跳转
Taro.navigateTo({
  url: '/pages/detail/detail?id=1&name=test',
  success: () => {
    console.log('跳转成功');
  }
});

// 重定向
Taro.redirectTo({
  url: '/pages/login/login'
});

// 切换Tab
Taro.switchTab({
  url: '/pages/mine/mine'
});

// 关闭所有页面,打开到应用内的某个页面
Taro.reLaunch({
  url: '/pages/index/index'
});

// 关闭当前页面,返回上一页面或多级页面
Taro.navigateBack({
  delta: 1
});

3.4 页面参数传递与接收

3.4.1 传递参数

// 方式1:URL参数
Taro.navigateTo({
  url: `/pages/detail/detail?id=${id}&name=${name}`
});

// 方式2:EventChannel
Taro.navigateTo({
  url: '/pages/detail/detail',
  success: (res) => {
    // 向被打开页面发送数据
    res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' });
  }
});

3.4.2 接收参数

// 方式1:通过路由参数接收
import { useRouter } from '@tarojs/taro-vue3';

const router = useRouter();
console.log('路由参数:', router.params);

// 方式2:通过EventChannel接收
import Taro from '@tarojs/taro';

const eventChannel = Taro.getCurrentInstance().eventChannel;
if (eventChannel) {
  eventChannel.on('acceptDataFromOpenerPage', (data) => {
    console.log('Received data:', data);
  });
}

四、Taro组件开发

4.1 自定义组件基础

4.1.1 创建自定义组件

<!-- src/components/CustomButton/CustomButton.vue -->
<template>
  <button 
    class="custom-button"
    :class="{ 'custom-button--primary': type === 'primary' }"
    @click="handleClick"
  >
    <slot></slot>
  </button>
</template>

<script setup lang="ts">
// 组件属性
interface Props {
  type?: 'default' | 'primary';
}

const props = withDefaults(defineProps<Props>(), {
  type: 'default'
});

// 组件事件
const emit = defineEmits(['click']);

const handleClick = () => {
  emit('click');
};
</script>

<style scoped lang="scss">
.custom-button {
  padding: 10px 20px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #fff;
  
  &--primary {
    background-color: #007aff;
    color: #fff;
    border-color: #007aff;
  }
}
</style>

4.1.2 使用自定义组件

<template>
  <view class="container">
    <CustomButton @click="handleButtonClick">
      默认按钮
    </CustomButton>
    
    <CustomButton type="primary" @click="handleButtonClick">
      主要按钮
    </CustomButton>
  </view>
</template>

<script setup lang="ts">
// 引入自定义组件
import CustomButton from '@/components/CustomButton/CustomButton.vue';

const handleButtonClick = () => {
  console.log('Button clicked');
};
</script>

4.2 全局组件注册

// src/app.vue
import { createApp } from 'vue';
import CustomButton from './components/CustomButton/CustomButton.vue';

const app = createApp(App);

// 注册全局组件
app.component('CustomButton', CustomButton);

app.mount('#app');

4.3 组件通信

4.3.1 Props与事件

<!-- 父组件 -->
<template>
  <view class="container">
    <ChildComponent 
      :message="parentMessage"
      @update:message="parentMessage = $event"
    ></ChildComponent>
  </view>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const parentMessage = ref('Hello from parent');
</script>

<!-- 子组件 -->
<template>
  <view class="child-container">
    <text>{{ message }}</text>
    <button @click="updateMessage">更新消息</button>
  </view>
</template>

<script setup lang="ts">
interface Props {
  message: string;
}

const props = defineProps<Props>();
const emit = defineEmits(['update:message']);

const updateMessage = () => {
  emit('update:message', 'Hello from child');
};
</script>

4.3.2 依赖注入(Provide/Inject)

<!-- 父组件 -->
<template>
  <view class="container">
    <ChildComponent></ChildComponent>
  </view>
</template>

<script setup lang="ts">
import { provide } from 'vue';
import ChildComponent from './ChildComponent.vue';

// 提供数据
provide('userInfo', {
  name: '张三',
  age: 25
});
</script>

<!-- 子组件 -->
<template>
  <view class="child-container">
    <text>{{ userInfo.name }}</text>
  </view>
</template>

<script setup lang="ts">
import { inject } from 'vue';

// 注入数据
interface UserInfo {
  name: string;
  age: number;
}

const userInfo = inject<UserInfo>('userInfo', { name: '', age: 0 });
</script>

五、Taro API调用

5.1 网络请求

import Taro from '@tarojs/taro';

// 发起GET请求
Taro.request({
  url: 'https://api.example.com/users',
  method: 'GET',
  data: {
    page: 1,
    pageSize: 10
  },
  success: (res) => {
    console.log('Request success:', res.data);
  },
  fail: (err) => {
    console.error('Request failed:', err);
  }
});

// 发起POST请求
Taro.request({
  url: 'https://api.example.com/users',
  method: 'POST',
  data: {
    name: '张三',
    age: 25
  },
  header: {
    'Content-Type': 'application/json'
  },
  success: (res) => {
    console.log('Request success:', res.data);
  }
});

// 上传文件
Taro.uploadFile({
  url: 'https://api.example.com/upload',
  filePath: tempFilePath,
  name: 'file',
  formData: {
    'user': 'test'
  },
  success: (res) => {
    console.log('Upload success:', res.data);
  }
});

// 下载文件
Taro.downloadFile({
  url: 'https://example.com/file.pdf',
  success: (res) => {
    if (res.statusCode === 200) {
      console.log('Download success:', res.tempFilePath);
    }
  }
});

5.2 本地存储

import Taro from '@tarojs/taro';

// 设置存储
Taro.setStorage({
  key: 'userInfo',
  data: {
    name: '张三',
    age: 25
  },
  success: () => {
    console.log('Storage set successfully');
  }
});

// 获取存储
Taro.getStorage({
  key: 'userInfo',
  success: (res) => {
    console.log('Storage get successfully:', res.data);
  }
});

// 移除存储
Taro.removeStorage({
  key: 'userInfo',
  success: () => {
    console.log('Storage removed successfully');
  }
});

// 清空存储
Taro.clearStorage({
  success: () => {
    console.log('Storage cleared successfully');
  }
});

5.3 界面交互

import Taro from '@tarojs/taro';

// 显示提示框
Taro.showToast({
  title: '操作成功',
  icon: 'success',
  duration: 2000
});

// 显示加载框
Taro.showLoading({
  title: '加载中...'
});

// 隐藏加载框
Taro.hideLoading();

// 显示模态框
Taro.showModal({
  title: '提示',
  content: '确定要执行此操作吗?',
  success: (res) => {
    if (res.confirm) {
      console.log('用户点击确定');
    } else if (res.cancel) {
      console.log('用户点击取消');
    }
  }
});

// 显示操作菜单
Taro.showActionSheet({
  itemList: ['选项1', '选项2', '选项3'],
  success: (res) => {
    console.log('用户选择了', res.tapIndex);
  }
});

5.4 设备API

import Taro from '@tarojs/taro';

// 获取系统信息
Taro.getSystemInfo({
  success: (res) => {
    console.log('System info:', res);
    // res.platform: 平台信息
    // res.screenWidth: 屏幕宽度
    // res.screenHeight: 屏幕高度
    // res.model: 设备型号
    // res.version: 操作系统版本
  }
});

// 获取网络状态
Taro.getNetworkType({
  success: (res) => {
    console.log('Network type:', res.networkType);
  }
});

// 监听网络状态变化
Taro.onNetworkStatusChange((res) => {
  console.log('Network status changed:', res.isConnected, res.networkType);
});

// 获取位置信息
Taro.getLocation({
  type: 'gcj02',
  success: (res) => {
    console.log('Location:', res.latitude, res.longitude);
  }
});

六、Taro状态管理

6.1 使用Pinia进行状态管理

6.1.1 安装Pinia

npm install pinia @tarojs/plugin-pinia

6.1.2 配置Pinia插件

// config/index.js
module.exports = {
  // ...
  plugins: [
    '@tarojs/plugin-pinia'
  ]
  // ...
};

6.1.3 创建Store

// src/store/user.ts
import { defineStore } from 'pinia';

interface UserInfo {
  name: string;
  age: number;
}

export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: null as UserInfo | null,
    token: ''
  }),
  
  getters: {
    isLoggedIn: (state) => !!state.token,
    userName: (state) => state.userInfo?.name || ''
  },
  
  actions: {
    login(userInfo: UserInfo, token: string) {
      this.userInfo = userInfo;
      this.token = token;
      // 保存到本地存储
      Taro.setStorageSync('userInfo', userInfo);
      Taro.setStorageSync('token', token);
    },
    
    logout() {
      this.userInfo = null;
      this.token = '';
      // 清除本地存储
      Taro.removeStorageSync('userInfo');
      Taro.removeStorageSync('token');
    },
    
    init() {
      // 从本地存储恢复状态
      this.userInfo = Taro.getStorageSync('userInfo');
      this.token = Taro.getStorageSync('token');
    }
  }
});

6.1.4 使用Store

<template>
  <view class="container">
    <text v-if="userStore.isLoggedIn">欢迎,{{ userStore.userName }}</text>
    <button v-else @click="handleLogin">登录</button>
    <button v-if="userStore.isLoggedIn" @click="handleLogout">退出</button>
  </view>
</template>

<script setup lang="ts">
import { useUserStore } from '@/store/user';
import Taro from '@tarojs/taro';

const userStore = useUserStore();

// 初始化状态
userStore.init();

const handleLogin = () => {
  userStore.login(
    { name: '张三', age: 25 },
    'token123456'
  );
};

const handleLogout = () => {
  userStore.logout();
};
</script>

七、Taro跨平台适配

7.1 条件编译

条件编译是Taro的核心特性,允许开发者根据不同平台编写不同的代码,实现平台特有的功能。

7.1.1 模板条件编译

<template>
  <view class="container">
    {/* #ifdef H5 */}
    <text>H5平台特有内容</text>
    {/* #endif */}
    
    {/* #ifdef MP-WEIXIN */}
    <text>微信小程序特有内容</text>
    {/* #endif */}
    
    {/* #ifdef APP */}
    <text>React Native平台特有内容</text>
    {/* #endif */}
    
    {/* #ifdef MP-WEIXIN || MP-ALIPAY */}
    <text>微信小程序和支付宝小程序共有内容</text>
    {/* #endif */}
  </view>
</template>

7.1.2 脚本条件编译

<script setup lang="ts">
// #ifdef H5
console.log('H5平台');
// #endif

// #ifdef MP-WEIXIN
console.log('微信小程序');
// #endif

// #ifdef APP
console.log('React Native平台');
// #endif
</script>

7.1.3 样式条件编译

/* #ifdef H5 */
.container {
  background-color: #f0f0f0;
}
/* #endif */

/* #ifdef MP-WEIXIN */
.container {
  background-color: #ffffff;
}
/* #endif */

7.2 平台特有API调用

import Taro from '@tarojs/taro';

// 调用微信小程序特有API
// #ifdef MP-WEIXIN
Taro.requestSubscribeMessage({
  tmplIds: ['template-id'],
  success: (res) => {
    console.log('Subscribe message success:', res);
  }
});
// #endif

// 调用React Native特有API
// #ifdef APP
// 使用React Native API
// #endif

7.3 适配不同屏幕尺寸

7.3.1 使用rpx单位

<template>
  <view class="container" style="width: 750rpx; height: 200rpx;">
    <text style="font-size: 32rpx;">文本内容</text>
  </view>
</template>

7.3.2 使用像素转换工具

// src/utils/pxTransform.ts
import Taro from '@tarojs/taro';

/**
 * 将px转换为rpx
 */
export const px2rpx = (px: number): number => {
  const systemInfo = Taro.getSystemInfoSync();
  return px * (750 / systemInfo.windowWidth);
};

/**
 * 将rpx转换为px
 */
export const rpx2px = (rpx: number): number => {
  const systemInfo = Taro.getSystemInfoSync();
  return rpx * (systemInfo.windowWidth / 750);
};

八、Taro打包与发布

8.1 打包命令

# 打包微信小程序
npm run build:weapp

# 打包支付宝小程序
npm run build:alipay

# 打包百度小程序
npm run build:swan

# 打包头条小程序
npm run build:tt

# 打包QQ小程序
npm run build:qq

# 打包H5
npm run build:h5

# 打包React Native
npm run build:rn

8.2 开发预览

# 预览微信小程序
npm run dev:weapp

# 预览H5
npm run dev:h5

# 预览React Native
npm run dev:rn

8.3 持续集成与自动化部署

# .github/workflows/taro-ci.yml
name: Taro CI/CD

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16.x'
    
    - name: Install dependencies
      run: npm install
    
    - name: Build WeChat Mini Program
      run: npm run build:weapp
    
    - name: Build H5
      run: npm run build:h5
    
    - name: Deploy H5 to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./dist
    
    - name: Upload WeChat Mini Program
      uses: easingthemes/ssh-deploy@v2.1.5
      with:
        SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
        REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
        REMOTE_USER: ${{ secrets.REMOTE_USER }}
        TARGET: ${{ secrets.REMOTE_TARGET }}
        SOURCE: ./dist/weapp

九、Taro性能优化

9.1 代码优化

  1. 减少不必要的渲染:使用v-if和v-show合理控制元素渲染
  2. 使用虚拟列表:对于长列表,使用虚拟列表减少DOM节点数量
  3. 优化图片加载:使用图片懒加载,压缩图片大小
  4. 减少网络请求:合并请求,使用缓存
  5. 优化JavaScript代码:减少不必要的计算,使用防抖和节流

9.2 启动性能优化

  1. 减少启动包大小:移除不必要的依赖,使用按需引入
  2. 预加载关键资源:使用预加载API加载关键资源
  3. 延迟加载非关键资源:非关键资源延迟加载
  4. 优化页面结构:减少页面嵌套层级

9.3 运行时性能优化

  1. 使用原生组件:对于性能要求高的场景,使用原生组件
  2. 优化动画效果:使用CSS动画代替JavaScript动画
  3. 及时清理资源:页面卸载时清理定时器、事件监听等
  4. 优化列表渲染:使用key属性,避免不必要的重渲染

9.4 小程序性能优化

  1. 控制页面数量:避免过多页面同时存在
  2. 优化setData调用:减少setData调用次数,合并数据
  3. 使用自定义组件:提高组件复用性,减少代码量
  4. 优化wx:if和hidden:合理使用条件渲染

十、Taro最佳实践

10.1 项目结构最佳实践

  1. 模块化开发:按功能模块组织代码,提高代码复用性
  2. 组件化设计:将通用UI组件抽象出来,便于维护和复用
  3. 状态管理:使用Pinia或Redux进行状态管理,便于组件间通信
  4. API封装:封装网络请求和本地存储等API,便于统一管理
  5. 工具函数:将通用工具函数抽象出来,提高代码复用性

10.2 开发流程最佳实践

  1. 使用Git进行版本控制:便于团队协作和代码管理
  2. 编写单元测试:提高代码质量,减少bug
  3. 使用ESLint进行代码检查:保持代码风格一致
  4. 使用Prettier进行代码格式化:保持代码格式一致
  5. 定期进行代码审查:提高代码质量,分享知识

10.3 性能优化最佳实践

  1. 使用rpx单位:适配不同屏幕尺寸
  2. 合理使用条件编译:针对不同平台进行优化
  3. 优化图片资源:压缩图片大小,使用合适的格式
  4. 减少DOM节点数量:使用虚拟列表,合理嵌套组件
  5. 优化网络请求:合并请求,使用缓存,减少请求次数

10.4 跨平台开发最佳实践

  1. 优先使用Taro统一API:减少平台特有代码
  2. 合理使用条件编译:针对平台差异进行适配
  3. 测试多平台兼容性:确保在所有目标平台上正常运行
  4. 关注平台特性:了解各平台的特性和限制
  5. 性能优化针对性:针对不同平台进行针对性的性能优化

十一、Taro 3.x新特性

11.1 Vue 3支持

Taro 3.x全面支持Vue 3,包括Composition API、Teleport、Suspense等特性。

11.2 编译优化

  • 并行编译:提高编译速度
  • 增量编译:只编译修改的文件
  • Tree Shaking:移除未使用的代码
  • 代码压缩:优化代码体积

11.3 运行时优化

  • 小程序运行时优化:提高小程序运行性能
  • H5运行时优化:优化H5渲染性能
  • React Native运行时优化:提高React Native应用性能

11.4 开发体验优化

  • 热更新:支持快速热更新
  • 调试工具:提供丰富的调试工具
  • 错误提示:优化错误提示信息
  • 文档完善:提供详细的文档和示例

十二、总结与展望

12.1 核心技术总结

  1. Taro框架:开放式跨端跨框架解决方案,支持React、Vue等多种框架
  2. 项目结构:清晰的目录结构,便于维护和扩展
  3. 页面开发:丰富的生命周期和路由管理
  4. 组件开发:支持自定义组件和全局组件注册
  5. API调用:统一的API调用方式,屏蔽平台差异
  6. 状态管理:支持Pinia和Redux进行状态管理
  7. 跨平台适配:条件编译和平台特有API调用
  8. 打包发布:支持多平台打包和发布
  9. 性能优化:多种性能优化策略

12.2 未来发展趋势

  1. 更好的Vue 3支持:持续优化Vue 3的支持
  2. 更多平台支持:支持更多的小程序平台和新的跨平台技术
  3. 更好的性能:持续优化各平台的性能
  4. 更丰富的生态:提供更多高质量的组件库和插件
  5. 更好的开发体验:优化开发工具和调试体验

12.3 学习建议

  1. 官方文档:深入学习Taro官方文档,掌握核心概念和API
  2. 实战项目:通过实战项目掌握Taro开发技巧
  3. 社区资源:关注Taro社区,学习他人的经验和技巧
  4. 平台特性:了解各平台的特性和限制,便于进行跨平台适配
  5. 性能优化:学习性能优化技巧,提高应用的用户体验

通过本集的学习,相信你已经掌握了Taro多端开发的核心概念和深度使用技巧,能够使用Taro开发高质量的跨平台应用。在实际开发中,应根据项目需求和目标平台,选择合适的技术方案和优化策略,不断提升应用的质量和用户体验。

« 上一篇 Vue 3 Uni-app框架深度使用指南:跨平台开发 下一篇 » Vue 3 Flutter与Vue集成深度指南:跨框架协作