第二十一期:基于Taro的多端(小程序+H5)开发实践

2022-07-15 10:05:36 浏览数 (1)

背景

通过一套代码,一套业务代码,构建成不同的端(H5/小程序),满足代理人平台多端的业务场景,并为接下来的要开发的项目积累经验。

多端开发方式

  • 混合开发 cordova/uniapp
  • 原生体验开发 flutter/react-native
  • 优缺点 优点:可以快速开发跨ios和andriod两端的应用。降低开发成本 缺点:无法适配小程序端。

Taro相比之下的优势

  • 跨框架

可以同时支持 vue/react/nerv 等框架

  • 跨端

可以编译成不同的端形式。H5/小程序/RN

需要注意的H5和小程序的区别

H5和小程序的运行环境有本质上的差别,导致两者在很多方面有不同的地方。

H5主要与运行在浏览器及web-view的浏览器环境。简单来说,浏览器环境主要有window对象,Bom, Dom 等API构成,同时可操作的系统API不多,这里的系统API比如: 拍照,图片上传,位置服务等等。

小程序主要运行在以微信为代表的平台环境中。平台环境提供了一套能够操作系统权限的API,同时由于小程序架构的特点,小程序的js运行在平台封装的jsCore中,并没有一个完整浏览器对象,因而缺少浏览器中相应的Dom 及Bom操作。

另外,H5的渲染机制和小程序也有所不同。H5的js和dom是在一个线程中同时进行,小程序则分了渲染层和逻辑层两个线程,两个线程通过原生的消息机制进行通信。渲染层在IOS中使用【WKWebView】进行渲染,在Android中使用【chromium定制内核】进行渲染。

因此,在开发过程中对这些差异有所了解,能够预判这些差异给我们带来的影响。这些差异主要有以下几点:

  • 客户端环境
  • 运行机制
  • 系统权限
  • 更新机制
  • 其他

客户端环境

H5的宿主环境是浏览器以及web-view中的浏览器环境。

小程序的宿主环境是特定的应用平台(微信/支付宝...)。

导致小程序脚本中无法使用浏览器常用的window对象及dom,bom相关的api,以及两者的缓存机制略有不同,H5可以使用cookie进行设置,小程序虽然有Storage但是没有cookie。所以,在使用Taro进行开发时,需要注意以下问题:

  • 客户端运行宿主环境判断
  • 登录流程的控制
客户端运行环境判断

以往基于H5的移动端开发时,通常时用navigator.userAgent这个API来获取浏览器信息,判断是ios/android/浏览器。

代码语言:javascript复制
let browser={
    versions:function(){
        var u = navigator.userAgent, app = navigator.appVersion;
        return {
            trident: u.indexOf('Trident') > -1, //IE内核
            presto: u.indexOf('Presto') > -1, //opera内核
            webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
            gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
            mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
            ios: !!u.match(/(i[^;] ;( U;)? CPU. Mac OS X/), //ios终端
            android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
            iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
            iPad: u.indexOf('iPad') > -1, //是否iPad
            webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
            weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
            qq: u.match(/sQQ/i) == " qq" //是否QQ
        };
    }(),
    language:(navigator.browserLanguage || navigator.language).toLowerCase()
}

但是在Taro中,Taro提供了getEnv()方法,可以直接获取所在端信息。比如:

代码语言:javascript复制
const isWeb = Taro.getEnv() === 'WEB'
const isWeapp = Taro.getEnv() === 'WEAPP'
登录流程的控制

小程序的登录机制是,wx.login获取code传给服务端, 服务端接收code同时调用微信的凭证校验接口,微信服务返回seccion_key/openid给服务端,服务端根据返回的信息生成一个token返给前端,前端缓存token,以后的每次请求都写到请求头中。

H5的登录情况有两种,一种是调用login接口后,服务端根据接受到的用户信息生成token/cookie返回给前端,前端缓存后,每次请求都写到请求头中。另外一种是服务端直接设置cookie。

注意:本次开发时,H5的登录是由服务端直接设置cookie, 在测试环境调试的时候需要手动将cookie设置到对应的域名下。同时小程序的登录用了中台的登录插件,登录成功后可以直接使用回调函数

代码语言:javascript复制
plugin.loginSuccess((data)=>{
 // 登录成功后的操作
  ...
})

假设小程序登录不用插件,H5登录也不是服务端直接设置cookie,那么可以做如下封装:

代码语言:javascript复制
const loginMethods = {
 WEB:()=>{
   // h5登录流程
    ...
  },
  WEAPP:()=>{
   // 小程序登录流程
    ...
  }
}
  const currentEnv = Taro.getEnv()
  
  // 执行登录方法
  loginMethods[currentEnv]()
运行机制

H5的运行机制/运行过程通常是加载Dom树,加载渲染树Css,绘制页面这样一个过程。

小小程序的运行机制分为热启动和冷启动。【热启动】:如果用户已经打开过某小程序,在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台。【冷启动】: 如果用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动。

这里对我们造成的影响主要是:【数据请求时机】。

数据请求时机

H5的请求通常在document.ready即可调用。vue框架在created()函数中,react在componentDidMount()中。

小程的热启动在调用数据请求时可能会遇到以下问题:小程序的onShow函数,页面每次展示时都会请求数据,如果请求的数据是列表。手机息屏,重新点亮屏幕会出现重复数据,需要对这个现象进行处理。

系统权限

小程序可以直接调用系统的【保存图片】【拍照】【音频】【位置】【转发】等权限。H5则无法直接获取这写权限。这里的差异主要表现在:

  • 交互效果
分享转发

小程序可以直接使用wx.shareAppMessage方法及<button type="share">调起分享。H5则需要借助jsBridage进行实现。

小程序的实现

代码语言:javascript复制
// 小程序的转发
page({
 data:{},
  // 分享
  onShareAppMessage(){
   return {
      title: "转发的标题",        // 默认是小程序的名称(可以写slogan等)
    path: '/pages/share/share',        // 默认是当前页面,必须是以‘/’开头的完整路径
    imageUrl: '',     //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
    success: function(res){
      // 转发成功之后的回调
    },
    fail: function(){
      
    },
    complete: fucntion(){
      // 转发结束之后的回调(转发成不成功都会执行)
    }
    }
  }
})
代码语言:javascript复制
// H5 的实现需要  导入JsBridge中的Api
  import { InviteFriendDesc, PromotionShareDesc, shareInJrApp } from '@src/utils/jsbridge';
  
  // 调用Api
   shareInJrApp(
      `url`,
      cardText || PromotionShareDesc,
      cardUrl,
      shortUrl,
      shareImg,
    );
上传图片
  • 小程序上传图片及图片预览可以直接调用相应的Api从相册中获取图片进行上传。
  • H5则需要特定的上传组件,或借用jsBridge使用原生应用的能力。
更新机制

小程序每次 【冷启动】时,都会检查有无更新版本,如果发现有新版本,会异步下载新版本代码包,并同时用客户端本地包进行启动,所以新版本的小程序需要等下一次 冷启动 才会应用上,当然微信也有wx.getUpdateManager 可以做检查更新。这个对我们的影响主要在发布项目的过程。

发布项目

H5打包后可以直接发布实时更新。小程序需要打包后上传到小程序后台进行审核后,才能正常发布。

其他开发过程中需要注意的问题

除了以上列举的差异外,还有几个问题需要注意。

  • 组件化
  • 多端组件
  • 接入中台服务
  • 开发调试
  • 代码优化
组件化开发

前端项目基本上都采用组件化开发的形式。组件化开发的优势明显,将复杂业务逻辑拆分,解耦。组件内逻辑清晰。可以复用性强。同时便于后期维护。

多端组件

组件的封装过程中可能遇到不同端展示的效果不一致。这时候可以用环境判断进行相应的布局。

代码语言:javascript复制
// 多端组件
renderName(){
 const {name,role} = props
  return (
    <View className="nickName">
     <View>{name}<View>
    {Taro.getEnv()==='WEAPP'?null:<View className="role">{role}<View>}
    </View>
}

虽然Taro提供了根据文件后缀名编译不同端页面的能力,但是基于组件进行开发,就没必要创建那么多不同后缀名的文件了。

接入中台服务

接入流程一般是: 申请业务ID-->服务提供方开发排期-->双方联调测试-->服务提供方发布生产。

沟通成本略高,不可控因素较多【开发环境】【测试环境】...

开发调试

H5使用【vconsole】,小程序使用【微信开发者工具】

代码优化
  • 设计模式【策略】【迭代器】...
代码语言:javascript复制
// 上传文件
// 例如上传文件

const uploadH5 = () => {...}

const uploadWx = () => {...}

const uploadNative () => {...}

export const uploadFile = () => {
  let uploadlist = [uploadH5,uploadWx,uploadNative]
  for(let i in uploadList){
    let success = uploadList[i]()
    if(success){
      return 'upload success'
    }
  }
}
复用代码

某些代码需要多处用到,则可以将之封装起来,甚至直接添加到全局对象上或者Taro的实例上。比如实名状态判断

其他扩展

  • 跨框架组件
  • 开发真正的跨端组件

虽然Taro支持将代码打包成不同的端,同时也有相应的trao-ui组件库,但是该UI库目前只支持react框架,假如我们使用vue进行迭代,则需要引入基于vue的另一个组件库。同时目前的Taro-UI不支持ReactNative。

有没有什么方法,可以同时兼容两种框架?有没有什么方法可以同时支持ReactNative? 简单介绍下 web-componentstyled-component

web-component

web-component是一种自定义可重用元素的技术。其主要概念有以下三个:

  • 自定义元素
  • temlate / slot 模板和插槽
  • shadow Dom
自定义元素

我们可以使用customElements.define()方法,自定义一个元素。该方法接受两个参数:

  • 自定义的元素名称 自定义元素名称需要用连字符 - 进行连接
  • 一个用于定义元素行为的类
  • 一个可选参数,个包含 extends 属性的配置对象,是可选参数。它指定了所创建的元素继承自哪个内置元素,可以继承任何内置元素。

自定义元素示例:

代码语言:javascript复制
class MyButton extend HTMLElement {
 // ....
}
// 注册自定义元素
customElements.define('my-button', HTMLElement);

// 使用自定义元素 和 平时写html一样

<div>
 <my-button></my-button>
</div>
Shadow Dom

Shadow Dom的主要作用是将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。

Shadow DOM 不是一个新事物——在过去的很长一段时间里,浏览器用它来封装一些元素的内部结构。以一个有着默认播放控制按钮的 <video> 元素为例。你所能看到的只是一个 <video> 标签,实际上,在它的 Shadow DOM 中,包含来一系列的按钮和其他控制器。Shadow DOM 标准允许你为你自己的元素(custom element)维护一组 Shadow DOM。

Shadow Dom有以下几个重要的概念:

  • shadow host : 一个常规 DOM节点,Shadow DOM 会被附加到这个节点上。
  • Shadow tree:Shadow DOM内部的DOM树。
  • Shadow root: Shadow tree的根节点。

我们可以使用 Element.attachShadow() 方法来将一个 shadow root 附加到任何一个元素上。它接受一个配置对象作为参数,该对象有一个 mode 属性,值可以是 open 或者 closed:open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM。

同时,可以使用模板字符串给shadow dom 添加样式。这里有几个伪类选择器

  • :host 表示当前shadow dom的宿主节点
  • : slotted 表示插槽内容中的dom节点
代码语言:javascript复制
// web-components写法  换成图片
// footer 页脚
// 布局容器
const layout = `
<style>
    :host{
        display:flex;
        flex-direction: column;
        height:100%;
        background: #f0f2f5;
    }
    ::slotted(app-layout.inner){
        flex-direction: row;
    }
    ::slotted(app-content){
        height:100%;
    }
    ::slotted(app-sider){
        width:200px;
        flex-direction:column;
    }
    ::slotted(app-footer){
        width:100%;
        height:64px;
        background:#fff;
    }
</style>

class AppFooter extends HTMLElement {
    constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
        ${layoutStyle.footer}
        <slot></slot>
        `
    }

    connectedCallback() {
       
    }

    attributeChangedCallback (name, oldValue, newValue) {
       
    }
}

styled-component

简单介绍下如何使用styled-component

组件时代的视觉原语。

Use the best bits of ES6 and CSS to style your apps without stress

0 人点赞