【Taro开发】四月芳菲,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框架。这个选择是在项目初始化的时候选择的,如下图:

扫描二维码关注公众号,回复: 13776615 查看本文章

项目迁移

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

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.js
config = {
  // ...
  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文档写的略微简单,我后面可能写一篇它的使用总结。

总结

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

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

猜你喜欢

转载自juejin.im/post/7084126264679727140