写点什么

「趣学前端」Taro 实践 + 踩坑记录第一期

作者:叶一一
  • 2022 年 9 月 18 日
    北京
  • 本文字数:5408 字

    阅读完需:约 18 分钟

「趣学前端」Taro实践+踩坑记录第一期

背景

最近接到多端开发,因为老项目使用的 React,考虑到迁移成本,选择了 Taro,迁移成本相对较低,且上手较快。

Taro 和 uni-app

我做了一下调研,目前市面上优秀且成熟的开源框架有很多。其中,Taro 和 uni-app 作为两大“豪门”框架,优秀之处各有千秋,为我提供了更多的选择项。


关于它们的对比可以参看下面这篇好文:

Taro

综合考量,尤其是前面提到的,迁移成本,我最后选择了 Taro。下面主要介绍 Taro 的使用以及迁移中的功能总结。


Taro 的官方文档内容很全面,基本的操作跟着官方文档即可完成,官方文档地址


我的项目目前只有两个端的业务场景,分别是微信小程序和 H5,所以技术探索也主要针对这两个端,文章也主要是这两个端使用总结。

新建项目

CLI 工具安装

# 使用 npm 安装 CLI$ npm install -g @tarojs/cli
# OR 使用 yarn 安装 CLI$ yarn global add @tarojs/cli
# OR 安装了 cnpm,使用 cnpm 安装 CLI$ cnpm install -g @tarojs/cli
复制代码

项目初始化

使用命令创建模板项目


$ taro init myApp
复制代码


npm 5.2+ 也可在不全局安装的情况下使用 npx 创建模板项目


$ npx @tarojs/cli init myApp
复制代码

安装依赖

# 进入项目根目录$ cd myApp
# 使用 yarn 安装依赖$ yarn
# OR 使用 cnpm 安装依赖$ cnpm install
# OR 使用 npm 安装依赖$ npm install
复制代码


安装完成之后,文件结构已经生成如下图:


编译命令

// 微信小程序# yarn$ yarn dev:weapp$ yarn build:weapp
# npm script$ npm run dev:weapp$ npm run build:weapp
// H5# yarn$ yarn dev:h5$ yarn build:h5
# npm script$ npm run dev:h5$ npm run build:h5
复制代码

项目迁移之 React 框架版

选择框架

因为我平时使用 React 框架进行开发,所以迁移的时候也直接选择了 React 框架。这个选择是在项目初始化的时候选择的,如下图:


项目迁移

直接把文件拷贝过来,然后进行调整,主要调整的内容有以下几个部分

UI 框架的调整

原来的项目使用的是antd-mobile,迁移之后改成了@antmjs/vantui,部分组件名以及组件的用法略有不同。


比如下面的页


  • antd-mobile 中的 List 组件在 @antmjs/vantui 是没有的,所以需要重写这部分代码;

  • Button 组件两个 UI 都有,但是里面的属性存在差异,针对这部分差异进行修改即可;


// import { List, Button } from 'antd-mobile';import { Button } from '@antmjs/vantui';
// antd-mobile的List组件使用 const listContent = content => { return ( <div className='content-list'> <List> {content.list.map(item => { return ( <List.Item className={classnames({ 'list-item-baseline': item.btnName })} prefix={materiaIcon[item.type]} key={item.type} description={item.btnName ? btnConten(item) : null}> <div className={classnames({ required: item.required })}>{item.name}</div> </List.Item> ); })} </List> </div> ); };
// @antmjs/vantui对List组件的改造const listContent = content => { return ( <View className='content-list'> {content.list.map(item => { return ( <View key={item.type} className='content-item'> <View className='flex'> <Icon color='#007af5' name={materiaIcon[item.type]} size='20px' /> <View className={classnames('ml5', { required: item.required })}>{item.name}</View> </View> {item.btnName ? btnContent(item) : null} </View> ); })} </View> ); };
// antd-mobile的Button组件使用<Button color='primary' block shape='rectangular'> 咨询客服</Button>
// antd-mobile的Button组件使用<Button type="primary" block={true}> 咨询客服</Button>
复制代码

页面跳转方法

原生 H5 的跳转使用的是 History 对象提供的 push 或者 replace 方法,在 Taro 里使用 Taro 提供的路由 API,因为小程序中 tabBar 中的页面和其他页面的跳转方法不一样,这个区别 Taro 也做了区分,为此我写了一个公共方法做跳转的统一处理。


/**   * 跳转处理   * @param {string} path app.config.js中的完整地址 例如/pages/home/index   */  commonNavigateTo(path) {    /** @name 底导航路由列表 */    const switchList = ['/pages/home/index'];    if (switchList.includes(path)) {      Taro.switchTab({        url: path,      });    } else {      Taro.navigateTo({        url: path,      });    }  }
复制代码

HTML 标签

Taro v3.3 以前是不支持使用 HTML 标签的,使用的是 Taro 提供的 View、Text 等标签,这些在Taro的组件库中有详细介绍。

import React, { Component } from 'react'import { View, Text } from '@tarojs/components'
export default class C extends Component { render () { return ( <View className='c'> <Text>c component</Text> </View> ) }}
复制代码


Taro v3.3+ 开始支持使用 HTML 标签,需要进行插件配置。


配置插件

1.首先下载安装插件 @tarojs/plugin-html

yarn add @tarojs/plugin-html
复制代码


2.然后在项目配置中添加使用插件

// config/index.jsconfig = {  // ...  plugins: ['@tarojs/plugin-html']}
复制代码


注:


  • 如果遇到不支持的标签可以使用 Taro 提供的组件,详见Taro组件库

项目迁移之原生小程序

后面有规划把原生小程序项目也使用 Taro 开发成多端,但是目前还没有实际应用,待我实际应用之后,再进行更新,我预测会遇到一些有趣的问题。

开发“指南针”

开发过程中难免会遇到各种问题,不过它也侧面成为了我的“试金石”,我把遇到的问题、解决方案,详细的列出来,供 jym 参考,有些解决方案可能不是最优,欢迎大佬提供更优的方案。

路由跳转路径问题

Taro 提供的路由跳转方法基本和小程序一致,可以参看文档


这里着重强调一下跳转的 url 的完整性,即 url 要以右斜线开始,否则或被当做相对路由处理。


错误示例

Taro.navigateTo({  url: 'pages/order/index',});
复制代码


报错如下

errMsg: "navigateTo:fail page "pages/order/orderStatement/pages/order/index" is not found"
复制代码


正确示例

Taro.navigateTo({  url: '/pages/order/index',});
复制代码

小程序文件过大的处理方法

1.根据提示开启压缩


Tips: 预览模式生成的文件较大,设置 NODE_ENV 为 production 可以开启压缩。Example:$ NODE_ENV=production taro build --type weapp --watch
复制代码


2.分包


分包限制如下,官方文档

  • 整个小程序所有分包大小不超过 20M

  • 单个分包/主包大小不能超过 2M


因为我们的项目不是很大,所以并没有实际应用这种方案,但是我找了几个不错的参考文章:


小程序分包(Taro分包案例)


京东购物小程序 | Taro3 项目分包实践


3.vendors.js 过大的原因


vendors.js 文件中包含了 node_modules 除 Taro 外的公共依赖,可能会因为某些原因导致它体积过大。


下面主要列一下我的项目中导致 vendors.js 文件过大的原因


3.1 引入了 crypto-js


这个第三方加密库,会导致一些意外的内容被打包进去(具体是什么官方也没有说的特别明白,可能是 node 的一些依赖之类的),解决方案就是降低 crypto-js 的版本或者直接把 crypto-js-min 放进本地(本地 83KB)。

Taro.request 在 H5 端不能自定义 header 的解决方案

因为我的项目某些特殊业务逻辑,所以必须添加自定义 header,但是 H5 端 Taro.request 不支持自定义 header(小程序端支持),所以根据不同端区分 request 的引入。


/** * @description 异步请求分发 H5和小程序使用的不一样 */import $ from './request-weapp';import $2 from './request-h5';export function requestFunc() {  if (process.env.TARO_ENV === 'weapp') {    return $;  } else {    return $2;  }}
复制代码

老项目迁移时 H5 端自定义路由

因为老项目有一些已经对外的入口,比如外推链接、第三方入口等,所以迁移的时候要进行兼容处理。

1.处理前的页面路由

处理前的页面路由如下,Taro 框架自动生成的,显然和老项目的不一致


https://{{domain}}/pages/index/index(browser 模式)
复制代码

2.自定义页面路由方案 1

方案 1 是使用 Taro 提供的方式,配置h5.router.customRoutes


config/index.js

module.exports = {  // ...  h5: {    // ...    router: {      customRoutes: {        // "页面路径": "自定义路由"        '/pages/index/index': '/index',        '/pages/detail/index': ['/detail'] // 可以通过数组为页面配置多个自定义路由      }    }  }}
复制代码


优点:不用做过多的逻辑。

缺点:1)当需要配置的页面多的时候容易漏;2)新增页面如果忘记补充,可能会导致跳转找不到页面。

3.自定义页面路由方案 2

自己编写自定义页面路由的代码逻辑

app.js

import resetRouter from '@utils/resetRouter';
// 挂载生命周期时,h5端引入路由重定向的处理方法componentDidMount() { // =>true:只有H5才处理路由 if (process.env.TARO_ENV === 'h5') { resetRouter.resetRouter(); } }
复制代码


utils/resetRouter.js

/** * @description 路由拦截处理 页面404重定向、页面非Taro最终的H5路由重定向等 */import Taro, { Current } from '@tarojs/taro';/** * 获取小程序tabBar的pagePath数组对象 * @param {Object} taroAppConfigInit 项目配置项 * @return {Array} 最终得到的pagePath数组 */const getTabBarPageList = taroAppConfigInit => {  const tabBarInit = taroAppConfigInit.tabBar ? taroAppConfigInit.tabBar : {};  const tabBarList = tabBarInit.list ? tabBarInit.list : [];  const tabBarPageList = [];  if (tabBarList.length !== 0) {    tabBarList.forEach(item => {      tabBarPageList.push('/' + item.pagePath);    });  }  return tabBarPageList;};/** * 获取全部路由的数组对象 * @param {Object} taroAppConfigInit 项目配置项 * @return {Array} 最终得到的数组数组 */const getAllPagesList = taroAppConfigInit => {  const allPagesListInit = taroAppConfigInit.pages ? taroAppConfigInit.pages : [];  let allPagesList = [];  allPagesListInit.forEach(item => {    allPagesList.push('/' + item);  });  return allPagesList;};/** * 获取页面是否是404的布尔值 * @param {Array} allPagesList 全部路由数组 * @param {string} pathname 当前页面路由 * @return {boolean} 最终得到的布尔值 */const getNoFoundFlag = (allPagesList, pathname) => {  let flag = true;  if (allPagesList.includes(pathname)) {    flag = false;  }  return flag;};/** * 页面路由处理 如果不包含pages的重组成/pages/pathname/index的格式 * @param {string} pathname 当前页面路由 * @return {boolean} 重组后的路由 */const resetPathname = pathname => {  let pathnameInit = '';  if (pathname.indexOf('pages') === -1) {    pathnameInit = '/pages' + pathname + '/index';  } else {    pathnameInit = pathname;  }  return pathnameInit;};/** * 跳转处理 跳转到tabBar页面使用switchTab,其他使用navigateTo * @param {string} pathname 当前页面路由 * @param {string} search 当前页面路由参数 */const switchTabOrnavigateTo = (pathname, search) => {  // 隐藏配置window.__taroAppConfig(包含app.config.js中所有内容)  const taroAppConfigInit = Current.app.config ? Current.app.config : {};  const allPagesList = getAllPagesList(taroAppConfigInit);  const tabBarPageList = getTabBarPageList(taroAppConfigInit);  const pathnameInit = resetPathname(pathname);  console.log(pathnameInit, 'pathnameInit');  //=>true: 如果路由类型是tabBar  if (tabBarPageList.includes(pathnameInit)) {    return Taro.switchTab({      url: pathnameInit,    });  } else {    const noFoundFlag = getNoFoundFlag(allPagesList, pathnameInit);    const url = noFoundFlag ? '/pages/dispatch/nofound/index' : pathnameInit + search;    return Taro.navigateTo({      url: url,    });  }};/** * 路由跳转拦截处理 */const resetRouter = () => {  // H5先从location获取路由getCurrentInstance().page值为nullTaro还没有修复  let { pathname, search } = window.location;  switchTabOrnavigateTo(pathname, search);};export default { resetRouter };
复制代码


缺点:需要自己写逻辑,增加了额外的工作量。

优点:一次性处理了所有的页面路由,不需要再次添加,且增加了 404 的重定向。

UI 框架

我们配合 Taro 的 UI 框架最终选择了有赞的 @antmjs/vantui。这个 UI 框架提供的组件很丰富,常见的功能都覆盖到了,不过它的 api 文档写的略微简单,我后面可能写一篇它的使用总结。

总结

万事开头难,但是世上无难事只怕有心人。

开源框架是推动技术发展的宝贵资源,有维护成本在的,所以遇到坑,可以转换思维,自己做兼容处理,比如某些问题可以通过环境区分做处理,虽然繁琐了一点,但是帮助解决了问题。

发布于: 刚刚阅读数: 4
用户头像

叶一一

关注

苍生涂涂,天下缭燎,诸子百家,唯我纵横。 2022.09.01 加入

非职业传道受业解惑前端程序媛,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。

评论

发布
暂无评论
「趣学前端」Taro实践+踩坑记录第一期_taro_叶一一_InfoQ写作社区