原生小程序和Rax混合开发最佳实践

150 阅读7分钟

前言

本文以“蚂蚁企业信用”小程序从最初的原生 DSL 开发模式到和 Rax 混合开发模式的转变,来讲解我们的最佳实践方案。该小程序最初只是在支付宝端内投放,所以当时采用了原生 DSL 的开发模式,随着业务的发展,新功能需要同时在支付宝端内和端外投放,所以端外投放我们采用了 Rax 开发模式,同时编译成小程序和 H5 页面,将 H5 投放在支付宝端外。因为既要保留现有原生 DSL 编写的功能,又要支持 Rax 开发的新功能,所以才有了原生小程序和 Rax 混合开发的方案。

名词解释

原生小程序:通过支付宝小程序或微信小程序等原生 DSL 开发的小程序,通常称之为原生小程序。
Rax:超轻量,高性能,易上手的前端解决方案。一次开发多端运行,解放重复工作,专注产品逻辑,提升开发效率。

最佳实践

本文介绍的大部分内容官方文档都没有介绍或总结,却是容易犯错的地方,其他更多实践或官方已有的介绍请参阅 Rax 官方文档 rax.js.org/。本文所有的示例代码都是以支付宝小程序为例,微信和其他小程序大同小异。

原生目录

原生小程序的静态资源、公共组件以及通用工具方法等内容如果不希望 Rax 进行编译,可以放到目录 miniapp-native 下面,Rax 构建的时候会直接原封不动地拷贝过去,其他非 miniapp-native 目录的文件都会被 Rax 打包构建。一个项目下面可以同时存在多个 miniapp-native 目录,具体根据项目构建需求来定。

分包加载

如果小程序本身没有使用分包加载,可以跳过该章节,感兴趣的同学可以继续阅读。

目录结构

假设原生小程序已经存在主包和子包(subpkg),目录结构如下:

src // 主目录
	pages // 主包页面
  subpkg // 子包目录
  	pages // 子包页面
  app.js
  app.json
  app.acss

为了保证小程序页面链接和之前一致(因为可能存在部分页面链接已经被运营投放出去),所以原生小程序页面和 Rax 页面混合后的目录结构如下:

src // 主目录
	pages // 主包页面
  subpkg // 原生子包目录
  	pages // 子包页面
    app.js
    app.json
  raxpkg // Rax 子包目录
  	pages // 子包页面
    app.js
    app.json
  app.js
  app.json
  global.less

tabBar配置

分包加载后,tabBar 仍可正常配置,但是配置内容和不使用分包的小程序有略微差异,就是配置页面路径的时候,必须用 pagePath,不能用 pageName,图片资源可放在 src/miniapp-native 目录下,配置如下所示:

// app.json
"tabBar": {
  "items": [
    {
      "pagePath": "pages/home/index",
      "name": "首页",
      "icon": "/miniapp-native/assets/img/tabs/home.png",
      "activeIcon": "/miniapp-native/assets/img/tabs/home1.png"
    },
    {
      "pagePath": "pages/other/index",
      "name": "其他",
      "icon": "/miniapp-native/assets/img/tabs/other.png",
      "activeIcon": "/miniapp-native/assets/img/tabs/other1.png"
    }
  ]
},

其他用法

分包加载其他配置及用法请参考文档:rax.js.org/docs/guide/…

公共组件

原生组件

原生组件包括小程序基础组件以及基于基础组件开发的自定义组件,自定义组件需要放到 miniapp-native 目录下面。
原生小程序页面可以继续引用原生组件,用法不变。
Rax 页面也可以引用原生组件,但是用法要改,比如支付宝小程序的 onTap 要改成 Rax 的 onClick等等。如果原生组件是项目本地的,直接按相对目录引用即可;如果组件是以 npm 包的形式提供的,除了在 package.json 中配置包名,还需要在 build.json 中配置 nativePackage,且组件名必须大写开头的驼峰格式。

// build.json
{
  "miniapp": {
    "buildType": "runtime",
    "subPackages": {
      "shareMemory": true
    },
    "nativePackage": {
      "autoInstall": true,
      "dependencies": {
        "mini-ali-ui-rpx": "^1.0.13"
      }
    }
  },
  "compileDependencies": []
}

注意:引用了原生小程序组件的页面仅限于编译成小程序,不能编译成 H5,如果要编译成 H5 ,请开发并使用 Rax 组件。

Rax 组件

Rax 页面中可以使用 Rax 基础组件,也可以使用基于 Rax 基础组件编写的自定义组件,可同时编译成小程序和 H5 页面,原生小程序页面不可使用 Rax 组件。

样式编译

原生页面

如果原生小程序项目采用了 Less 等,Rax 在编译原生小程序的时候,是不能将 Less 语法编译成原生小程序的语法,这时就需要对项目提前做一次预编译,可以采用其他预编译工具进行一次编译。如我们项目中原生小程序采用了 Less,我们把源码放在 code 目录下,把预编译后的代码放到 src 目录, Rax 构建的源文件目录就是 src 目录。

"scripts" {
  "compile": "run-s --npm-path tnpm compile:*",
  "compile:postcss": "postcss \"code/**/*.{acss,css}\" --base code --dir src --ext acss",
  "compile:sync": "sync-glob \"code/**/*\" \"!code/**/*.acss\" src --silent --delete=false",
}

Rax 页面

Rax 本身支持 Less 编译,所以 Rax 页面可以直接写 Less 语法。

通用工具

原生小程序用到的工具方法等,建议放到 miniapp-native 目录。因为如果将通用工具方法放在 src 目录下,会被 Rax 打包到 bundle.js 里面,那原生页面按相对路径引用就会失效,如果放到 miniapp-native 目录,原生小程序页面引用没有问题,但是有些 API 不局限于小程序使用,如果 Rax 页面引用到该方法,就会被打包到 bundle.js 里,虽然会增加一点点包的体积,但是能满足原生小程序页面和 Rax 页面共同引用的诉求。

原生app

app.js

如果原生小程序 app.js 里有逻辑代码,可以支持在 src/miniapp-native 目录下创建 app.js 文件并编写逻辑代码,不过写法要修改一下,原 app.js 写法如下:

App({
  data: {},
  onLaunch() {},
  onSHow() {},
  onHide() {},
  onError() {},
})

修改后 src/miniapp-native/app.js 写法如下:

module.exports = {
  data: {},
  onLaunch() {},
  onSHow() {},
  onHide() {},
  onError() {},
}

getApp() 可以继续在原生小程序页面和 Rax 页面内使用,用法不变。

app.acss

此处以支付宝小程序为例,支付宝全局样式为 app.acss。
开发时通常会将许多公共的样式写在 app.acss 里,在 Rax 里需要将全局样式写到 global.less 里。
如果小程序没有分包,Rax 会将 global.less 打包到 bundle.css.acss 里,并被全局 app.acss 引用;如果小程序采用了分包加载,每个包都会构建一份 bundle.css.acss,而全局 app.acss 里并没有引用 bundle.css.acss,这样就会导致全局样式加载失败。针对该问题,Rax 也提供了一种解决方案,就是共享内存的方案,可以通过配置 build.json 中 subPackages 里的 shareMemory为 true 来解决,这样就会把 global.less 打包成一份 vendors.css.acss 并被全局 app.acss 引用。如下配置:

{
  "miniapp": {
    "buildType": "runtime",
    "subPackages": {
      "shareMemory": true
    }
  }
}

静态资源

如果是原生小程序用到的静态资源,建议直接放到 miniapp-native 目录下;如果是 Rax 页面用的静态资源,参考文档: rax.js.org/docs/guide/…

插件使用

插件可以继续使用,用法和原生小程序页面用法完全相同,如支付宝小程序,需要在src 目录下的 app.json 中配置,如下:

"plugins": {
  "subscribeMsg": {
    "version": "*",
    "provider": "2021001155639035"
  }
}

构建 H5

在不分包的情况下,Rax 构建出来的 H5 是个 SPA 应用;如果是分包加载,每个包会被打包成一个 HTML 页面,即 MPA 应用,每个子包下的页面会被打包成多个组件,即每个子包是一个 SPA ,综合起来就是所谓的 MSPA 模式,所以构建出来的 H5 ,如果是分包的,页面路径切记加上子包的目录名。