背景

京东购物小程序作为京东小程序业务流量的主要入口,承载着许多的活动和页面,而很多的活动在小程序开展的同时,也会在京东 APP 端进行同步的 H5 端页面的投放。这时候,一个相同的活动,需要同时开发原生小程序页面和H5页面的难题又摆在了前端程序员的面前。 幸运的是,我们有 Taro,一个开放式跨端跨框架解决方案。可以帮助我们很好地解决这种跨端开发的问题。但不幸的是,Taro 并没有提供一套完整的将项目作为独立分包运行在小程序中的解决方案。因此,本篇文章将介绍如何通过一套合适的混合开发实践方案,解决 Taro 项目作为独立分包后出现的一些问题

目录

  • 背景
  • 整体流程
  • 应用过程
    • 准备合适的开发环境
    • 将 Taro 项目作为独立分包进行编译打包
    • 引入 @tarojs/plugin-indie 插件,保证 Taro 前置逻辑优先执行
    • 引入 @tarojs/plugin-mv 插件,自动化挪动打包后的文件
    • 引入公共方法、公共基类和公共组件
      • 引入公共方法
      • 引入公共组件
      • 引入页面公共基类
  • 存在问题
  • 后续

整体流程

总的来说,若要使用 Taro 3 将项目作为独立分包运行在京东购物小程序,我们需要完成以下四个步骤:

  1. 准备开发环境,下载正确的 Taro 版本
  2. 安装 Taro 混合编译插件,解决独立分包的运行时逻辑问题
  3. 调用 Taro 提供的混合编译命令,对 Taro 项目进行打包
  4. 挪动打包后 Taro 文件到主购小程序目录下

那么接下来,我们将对每个步骤进行详细的说明,告诉大家怎么做,以及为什么要这样做。

应用过程

准备合适的开发环境

首先我们需要全局安装 Taro 3,并保证全局和项目下的 Taro 的版本高于3.1.4,这里我们以新建的Taro 3.2.6项目为例:

yarn global add @tarojs/cli@3.2.6taro init

之后我们在项目中用React语法写入简单的 hello word 代码,并在代码中留出一个Button组件来为将来调用京东购物小程序的公共跳转方法做准备。

// src/pages/index/index.jsximport { Component } from 'react'
import { View, Text, Button } from '@tarojs/components'import './index.scss'export default class Index extends Component {handleButtonClick () {// 调用京东购物小程序的公共跳转方法console.log('trigger click')}render () {return (<View className='index'><Text>Hello world!</Text><Button onClick={this.handleButtonClick.bind(this)} >点击跳转到主购首页</Button></View>)}
}

俗话说得好,有竟者事竟成,在开始编码前,我们来简单地定几个小目标:

  • 成功地将 Taro 项目 Hello world 在京东购物小程序的分包路由下跑通
  • 引入京东购物小程序的公共组件 nav-bar 并能正常使用
  • 引入公共方法 navigator.goto 并能正常使用
  • 引入公共基类 JDPage 并能正常使用

将 Taro 项目作为独立分包进行编译打包

在将 Taro 项目打包进主购小程序时,我们很快就遇到了第一个难题:Taro 项目下默认的命令打包出来的文件是一整个小程序,如何打包成一个单独的分包?

幸运的是,在3.1.4版本后的 Taro,提供了混合开发的功能,意思为可以让原生项目和 Taro 打包出来的文件混合使用,只需要在打包时加入 --blended 命令即可。

cross-env NODE_ENV=production taro build --type weapp --blended

blended 中文翻译是混合的意思,在加入了这个命令后,Taro 会在构建出来的 app.js 文件中导出 taroApp,我们可以通过引入这个变量来在原生项目下的 app.js 调用 Taro 项目 app 的 onShow、onHide 等生命周期。

// 必须引用 Taro 项目的入口文件
const taroApp = require('./taro/app.js').taroAppApp({onShow () {// 可选,调用 Taro 项目 app 的 onShow 生命周期taroApp.onShow()},onHide () {// 可选,调用 Taro 项目 app 的 onHide 生命周期taroApp.onHide()}
})

如果单纯地使用 blended 命令,即使我们不需要调用 onShow、onHide 这些生命周期,我们也需要在原生项目下的 app.js 里引入Taro项目的入口文件,因为在执行我们的小程序页面时,我们需要提前初始化一些运行时的逻辑,因此要保证 Taro 项目下的 app.js 文件里的逻辑能优先执行。

理想很丰满,现实很骨感,由于我们需要将 Taro 项目作为单独的分包打包到主购项目中,因此这种直接在原生项目的 app.js 中引入的方式只适用于主包内的页面,而不适用于分包。

引入 @tarojs/plugin-indie 插件,保证 Taro 前置逻辑优先执行

要解决混合开发在分包模式下不适用的问题,我们需要引入另外一个 Taro 插件 @tarojs/plugin-indie

首先我们先在 Taro 项目中对该插件进行安装

yarn add --dev @tarojs/plugin-indie

之后我们在 Taro 的配置项文件中对该插件进行引入

// config/index.js
const config = {// ...plugins: ['@tarojs/plugin-indie'] // ...
}

查看该插件的源码,我们可以发现该插件处理的逻辑非常简单,就是在编译代码时,对每个页面下的 js chunk 文件内容进行调整,在这些 js 文件的开头加上 require("../../app"),并增加对应 modulesourceMap 映射。在进行了这样的处理后,便能保证每次进入 Taro 项目下的小程序页面时,都能优先执行 Taro 打包出来的运行时文件了。

引入 @tarojs/plugin-mv 插件,自动化挪动打包后的文件

到目前为止,我们已经可以成功打包出能独立分包的 Taro 小程序文件了,接下来,我们需要将打包出来的 dist 目录下的文件挪到主购项目中。

手动挪动?no,一个优秀的程序员应该想尽办法在开发过程中“偷懒”。 因此我们会自定义一个 Taro 插件,在 Taro 打包完成的时候,自动地将打包后的文件移动到主购项目中。

// plugin-mv/index.js
const fs = require('fs-extra')
const path = require('path')export default (ctx, options) => {ctx.onBuildFinish(() => {const blended = ctx.runOpts.blended || ctx.runOpts.options.blendedif (!blended) returnconsole.log('编译结束!')const rootPath = path.resolve(__dirname, '../..')const miniappPath = path.join(rootPath, 'wxapp')const outputPath = path.resolve(__dirname, '../dist')// testMini是你在京东购物小程序项目下的路由文件夹const destPath = path.join(miniappPath, `./pages/testMini`)if (fs.existsSync(destPath)) {fs.removeSync(destPath)}fs.copySync(outputPath, destPath)console.log('拷贝结束!')})
}

在配置文件中加入这个自定义插件:

// config/index.js
const path = require('path')const config = {// ...plugins: ['@tarojs/plugin-indie',path.join(process.cwd(), '/plugin-mv/index.js')] // ...
}

重新执行cross-env NODE_ENV=production taro build --type weapp --blended打包命令,即可将 Taro 项目打包并拷贝到京东购物小程序项目对应的路由文件夹中。

至此,我们便可在开发者工具打开主购小程序项目,在 app.json 上添加对应的页面路由,并条件编译该路由,即可顺利地在开发者工具上看到 Hello World 字样。

引入公共方法、公共基类和公共组件

在日常的主购项目开发中,我们经常需要用到主购原生项目下封装的一些公共模块和方法,那么,通过混合编译打包过来的 Taro 项目是否也能通过某种办法顺利引用这些方法和模块呢?

答案是可以的。

引入公共方法

先简单说一下思路,更改 webpack 的配置项,通过 externals 配置处理公共方法和公共模块的引入,保留这些引入的语句,并将引入方式设置成 commonjs 相对路径的方式,详细代码如下所示:

const config = {// ...mini: {// ...webpackChain (chain) {chain.merge({externals: [(context, request, callback) => {const externalDirs = ['@common', '@api', '@libs']const externalDir = externalDirs.find(dir => request.startsWith(dir))if (process.env.NODE_ENV === 'production' && externalDir) {const res = request.replace(externalDir, `../../../../${externalDir.substr(1)}`)return callback(null, `commonjs ${res}`)}callback()},],})}// ...}// ...
}

通过这样的处理之后,我们就可以顺利地在代码中通过 @common/*@api/*@libs/* 来引入原生项目下的 common/*api/*libs/* 了。

// src/pages/index/index.jsximport { Component } from 'react'
import { View, Text, Button } from '@tarojs/components'import * as navigator from '@common/navigator.js'import './index.scss'export default class Index extends Component {handleButtonClick () {// 调用京东购物小程序的公共跳转方法console.log('trigger click')// 利用公共方法跳转京东购物小程序首页navigator.goto('/pages/index/index')}render () {return (<View className='index'><Text>Hello world!</Text><Button onClick={this.handleButtonClick.bind(this)} >点击跳转到主购首页</Button></View>)}
}

能看到引入的公共方法在打包后的小程序页面中也能顺利跑通了

引入公共组件

公共组件的引入更加简单,Taro 默认有提供引入公共组件的功能,但是如果是在混合开发模式下打包后,会发现公共组件的引用路径无法对应上,打包后页面配置的 json 文件引用的是以 Taro 打包出来的 dist 文件夹为小程序根目录,所以引入的路径也是以这个根目录为基础进行引用的,因此我们需要利用 Taro 的 alias 配置项来对路径进行一定的调整:

// pages/index/index.config.js
export default {navigationBarTitleText: '首页',navigationStyle: 'custom',usingComponents: {'nav-bar': '@components/nav-bar/nav-bar',}
}
// config/index.js
const path = require('path')const config = {// ...alias: {'@components': path.resolve(__dirname, '../../../components'),}// ...
}

接着我们在代码中直接对公共组件进行使用,并且无需引入:

// src/pages/index/index.jsximport { Component } from 'react'
import { View, Text, Button } from '@tarojs/components'import * as navigator from '@common/navigator.js'import './index.scss'export default class Index extends Component {handleButtonClick () {// 调用京东购物小程序的公共跳转方法console.log('trigger click')// 利用公共方法跳转京东购物小程序首页navigator.goto('/pages/index/index')}render () {return (<View className='index'>{/* 公共组件直接引入,无需引用 */}<nav-barnavBarData={{title: '测试公共组件导航栏',capsuleType: 'miniReturn',backgroundValue: 'rgba(0, 255, 0, 1)'}}/><Text>Hello world!</Text><Button onClick={this.handleButtonClick.bind(this)} >点击跳转到主购首页</Button></View>)}
}

这样打包出来的 index.json 文件中 usingComponents 里的路径就能完美匹配原生小程序下的公共组件文件了,我们也由此能看到公共导航栏组件 nav-bar 在项目中的正常使用和运行了:

引入页面公共基类

在京东购物小程序,每一个原生页面在初始化的时候,基本都会引入一个 JDPage 基类,并用这个基类来修饰原本的 Page 实例,会给 Page 实例上原本的生命周期里添加一些埋点上报和参数传递等方法。

而我们在使用 Taro 进行混合编译开发时,再去单独地实现一遍这些方法显然是一种很愚蠢的做法,所以我们需要想办法在 Taro 项目里进行类似的操作,去引入 JDPage 这个基类。

首先第一步,我们需要在编译后的 JS 文件里,找到 Page 实例的定义位置,这里我们会使用正则匹配,去匹配这个 Page 实例在代码中定义的位置:

const pageRegx = /(Page)(\(Object.*createPageConfig.*?\{\}\)\))/

找到 Page 实例中,将 Page 实例转换成我们需要的 JDPage 基类,这些步骤我们都可以将他们写在我们之前自制 Taro 插件 plugin-mv 中去完成:

const isWeapp = process.env.TARO_ENV === 'weapp'
const jsReg = /pages\/(.*)\/index\.js$/
const pageRegx = /(Page)(\(Object.*createPageConfig.*?\{\}\)\))/export default (ctx, options) => {ctx.modifyBuildAssets(({ assets }) => {Object.keys(assets).forEach(filename => {const isPageJs = jsReg.test(filename)if (!isWeapp || !isPageJs) returnconst replaceFn = (match, p1, p2) => {return `new (require('../../../../../bases/page.js').JDPage)${p2}`}if (!assets[filename]._value &&assets[filename].children) {assets[filename].children.forEach(child => {const isContentValid = pageRegx.test(child._value)if (!isContentValid) returnchild._value = child._value.replace(pageRegx, replaceFn)})} else {assets[filename]._value = assets[filename]._value.replace(pageRegx, replaceFn)}})})
}

经过插件处理之后,打包出来的页面 JS 里的 Page 都会被替换成 JDPage,也就拥有了基类的一些基础能力了。

至此,我们的 Taro 项目就基本已经打通了京东购物小程序的混合开发流程了。在能使用 Taro 无痛地开发京东购物小程序原生页面之余,还为之后的双端甚至多端运行打下了结实的基础。

存在问题

在使用 Taro 进行京东购物小程序原生页面的混合开发时,会发现 Taro 在一些公共样式和公共方法的处理上面,存在着以下一些兼容问题:

  1. Taro 会将多个页面的公共样式进行提取,放置于 common.wxss 文件中,但打包后的 app.wxss 文件却没有对这些公共样式进行引入,因此会导致页面的公共样式丢失。解决办法也很简单,只要在插件对 app.wxss 文件进行调整,添加对 common.wxss 的引入即可:

    const wxssReg = /pages\/(.*)\/index\.wxss$/
    function insertContentIntoFile (assets, filename, content) {
    const { children, _value } = assets[filename]
    if (children) {children.unshift(content)
    } else {assets[filename]._value = `${content}${_value}`
    }
    }
    export default (ctx, options) => {
    ctx.modifyBuildAssets(({ assets }) => {Object.keys(assets).forEach(filename => {const isPageWxss = wxssReg.test(filename)// ...if (isPageWxss) {insertContentIntoFile(assets, filename, "@import '../../common.wxss';\n")}}
    })
    }
  2. 使用 Taro 打包后的 app.js 文件里会存在部分对京东购物小程序公共方法的引用,该部分内容使用的是和页面 JS 同一个相对路径进行引用的,因此会存在引用路径错误的问题,解决办法也很简单,对 app.js 里的引用路径进行调整即可:

    const appReg = /app\.js$/
    const replaceList = ['common', 'api', 'libs']
    export default (ctx, options) => {
    ctx.modifyBuildAssets(({ assets }) => {Object.keys(assets).forEach(filename => {const isAppJS = appReg.test(filename)const handleAppJsReplace = (item) => {replaceList.forEach(name => {item = item.replace(new RegExp(`../../../../../${name}`, 'g'), `'../../../${name}`)})}if (isAppJS) {if (!assets[filename]._value &&assets[filename].children) {assets[filename].children.forEach(child => {replaceList.forEach(name => {const value = child._value ? child._value : childhandleAppJsReplace(value)})})} else {handleAppJsReplace(assets[filename]._value)}}}
    })
    }

后续

本篇文章主要是讲述了 Taro 项目在京东购物小程序端的应用方式和开发方式,暂无涉及 H5 部分的内容。之后计划输出一份 Taro 项目在 H5 端的开发指南,并讲述 Taro 在多端开发中的性能优化方式。

欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章:

京东购物小程序 | Taro3 项目分包实践相关推荐

  1. 前端怎么获取cookie的值_京东购物小程序cookie方案实践(附Demo)

    一.前言 早期为了解决"会话保持"的需求,社区中出现了「cookie 方案」并最终成为 W3C 标准:当某个网站登录成功后,客户端(浏览器)收到一个 cookie 标识(文本)并保 ...

  2. 京东联盟-怎么跳转到京东购物小程序领券下单

    我们在自己的小程序里接入京东联盟,实际上模式是用户点击商品详情页下单,然后跳转到"京东购物"小程序领券下单(像下面那样),但是这个接口是需要申请的,且自有小程序日均访问人数> ...

  3. 京东快递小程序分包优化实践

    前言- 随着项目规模增大,小程序分包优化是必须要面对的问题.分包不合理,不仅影响项目迭代和上线计划,还关乎用户体验,甚至因此导致 C 端用户流失.本文主要介绍京东快递小程序分包过程中踩过的坑,以及小程 ...

  4. 如何比较两个速度的大小地程序_58安居客小程序平台化与多小程序开发探索与实践...

    导语 本文分享58安居客小程序团队在小程序向平台化转型.多小程序同步开发过程中遇到的问题.解决方案与实践. 背景 在提效.服务进阶的大背景下,为了让同一支团队,把一个业务做精做深,提高研发效率,HBG ...

  5. 微信小程序Taro + React开发实践

    微信小程序Taro + React开发实践 微信小程序原生开发有一套自己的规范和写法,开发体验十分类似Vue,但如果你想减少学习成本,那么Taro框架是一个在此基础上又封装了一层的轮子,从社区热度到开 ...

  6. 8个小程序开源项目助你快速搭建小程序

    今天和大家分享几个小程序开源项目, 帮助我们轻松搭建自己的小程序应用. 我会从大家最熟悉的小程序UI组件, 到可视化库, 再到完整项目, 一一和大家介绍. 小程序UI组件库 小程序多端开发框架 小程序 ...

  7. 微信小程序 自动解决分包大小问题_微信小程序-分包 -每包2M

    限制在2M以内.如果超过2M,可以通过分包加载实现,在app.json中配置即可, 小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载. 在构建小程序分包项目时,构建会输出一个 ...

  8. 蝶衣王——做小程序能月入1万?小程序赚钱项目有哪些?

    大家好,我是蝶衣王小编,小程序赚钱吗?小程序赚钱项目有哪些?普通人能做吗?今天小编就给大家分享小程序赚钱项目. 话不多说,今天给大家讲一下小程序赚钱的方法: 之前就有朋友问我,我的小程序已经做好了,但 ...

  9. 《十六》微信小程序中的分包

    某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载. 在构建小程序分包项目时,构建会输出一个或多个分包.每个使用分包小程序必定含有一个主包.所谓的主包, ...

最新文章

  1. Know about Oracle RAC Heartbeat
  2. 表 合并字段_多工作表动态合并,其实很简单
  3. Python:zip()函数
  4. sql语句之where子句
  5. Linux下python升级步骤
  6. Swift3 Scanner用法之判断是否数字、提取字符串里的数字
  7. python exchangelib 删除邮件_Python exchangelib在子文件夹中读取邮件 - python
  8. Java—接口(工厂模式代理模式)
  9. tensorflow 显存 训练_Tensorflow与Keras自适应使用显存方式
  10. Maven [ERROR] 不再支持源选项 5。请使用 6 或更高版本
  11. gitlab protected branch
  12. 《中国人工智能学会通讯》——5.4 结 论
  13. python的ctypes模块详解数组_如何使用Python的ctypes和readinto读取包含数组的结构?...
  14. LINUX上用Robot截屏得到全黑之分析
  15. 使用MVPArms框架时,访问网络没响应。
  16. [示例代码]植物大战僵尸网页版
  17. 弹球小游戏c语言编程,scratch丨(1) 弹球小游戏
  18. 怎么把计算机上打印任务解除,如何终止打印机任务_如何停止打印机任务-win7之家...
  19. 关于计算机的想象类作文,想象类作文范文
  20. windows 10 宽带拨号时无法开启热点,解决热点开启问题

热门文章

  1. 云计算行业现状及未来发展趋势
  2. 专家预言未来机器人性XX爱OO或成常态,并利于身心健康
  3. python习题#讲解6
  4. 工具技巧:Git 常用命令
  5. 前端学习之Bootstrap入门 网格系统
  6. arcmap十进制度和度分秒相互转换
  7. 一个月面试4家,3家Offer,来看看面霸真君如何面试的
  8. Chatgpt取代客服?取代客服的其实另有其人
  9. 程序员如何说一口流利英语?她只用一招……
  10. 【观察】联想发力企业混合云市场,云MSP新定位释放新价值