background
As the main entry point for the business traffic of the JD Mini Program, the JD Shopping Mini Program carries many activities and pages. While many activities are carried out in the Mini Program, they will also be placed on the JD APP on the synchronous H5 page. At this time, the same activity, developing native applet pages and H5 pages at the again in front of front-end programmers.
Fortunately, we have Taro, an open cross-end and cross-framework solution. Can help us solve this kind of cross-terminal development problem well. Unfortunately, that runs the project as an independent subcontract in a small program. Therefore, this article will introduce how after the Taro project is an independent subcontracting through a set of suitable mixed development practices.
content
- background
- Overall process
Application process
- Prepare a suitable development environment
- Compile and package the Taro project as an independent sub-package
- Introduce the @tarojs/plugin-indie plug-in to ensure the priority execution of Taro's front-end logic
- Introduce the @tarojs/plugin-mv plug-in to automatically move the packaged files
Introduce public methods, public base classes and public components
- Introduce public methods
- Introduce common components
- Introduce the page public base class
- There is a problem
- Follow-up
Overall process
In general, to use Taro 3 to run the project as an independent subcontract in the JD shopping applet, we need to complete the following four steps:
- prepare the development environment , download the correct Taro version
- Install Taro hybrid compilation plug-in to solve the runtime logic problem of independent subcontracting
- Call the hybrid compilation command provided by Taro to package the Taro project
- Move the packaged Taro file to the main purchase applet directory
So next, we will explain each step in detail, telling everyone how to do it, and why you want to do it.
Application process
Prepare a suitable development environment
First, we need to install Taro 3 globally and ensure that the version of Taro under the 3.1.4
. Here we take the newly created Taro 3.2.6
project as an example:
yarn global add @tarojs/cli@3.2.6
taro init
After that, we used the React
syntax to write the simple hello word
code in the project, and set aside a Button
component in the code to prepare for calling the public jump method of the JD shopping applet in the future.
// src/pages/index/index.jsx
import { 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>
)
}
}
As the saying goes, there is a racer. Before we start coding, let's briefly set a few small goals:
- under the subcontract routing of the JD shopping applet, the Taro project Hello world
- introduces the public component nav-bar of the
- introduces the public method navigator.goto and can be used normally
- introduces the public base class JDPage and can be used normally
Compile and package the Taro project as an independent sub-package
When packaging the Taro project into the main purchase applet, we soon encountered the first problem: the file packaged by the default command under the Taro project is a whole applet, into a single sub-package?
Fortunately, 3.1.4
version provides the function of mixed development, which means that the native project and the files packaged by Taro can be mixed. You only need to add the --blended
command to
cross-env NODE_ENV=production taro build --type weapp --blended
blended
Chinese translation is a mixed meaning. After adding this command, Taro will export taroApp
app.js
file. We can introduce this variable to call the onShow, onHide and other life of the Taro project app on app.js
cycle.
// 必须引用 Taro 项目的入口文件
const taroApp = require('./taro/app.js').taroApp
App({
onShow () {
// 可选,调用 Taro 项目 app 的 onShow 生命周期
taroApp.onShow()
},
onHide () {
// 可选,调用 Taro 项目 app 的 onHide 生命周期
taroApp.onHide()
}
})
If you simply use blended
command, even though we do not need to call onShow, onHide these life cycle, we also need to in the native project app.js
introduction of import documents Taro project's , because in the implementation of our applet page, we need Initialize some runtime logic in advance, so ensure app.js
file under the Taro project can be executed first.
The ideal is very full, the reality is very skinny, because we need to package the Taro project as a separate sub-package into the main purchase project, so this method of directly introducing in the app.js of the native project only applies to the main package Page, not applicable to subcontracting.
Introduce the @tarojs/plugin-indie plug-in to ensure the priority execution of Taro's front-end logic
To solve the problem that mixed development is not applicable in subcontracting mode, we need to introduce another Taro plug-in @tarojs/plugin-indie
.
First, we first install the plugin in the Taro project
yarn add --dev @tarojs/plugin-indie
Then we introduce the plugin in Taro's configuration item file
// config/index.js
const config = {
// ...
plugins: [
'@tarojs/plugin-indie'
]
// ...
}
Looking at the source code of the plug-in, we can find that the logic of the plug-in processing is very simple, that is, when compiling the code, js chunk
require("../../app")
at the beginning of these js files, and add the corresponding module
sourceMap
mapping. After such processing, it can be guaranteed that enters the applet page under the Taro project, the runtime files packaged by Taro will be executed first.
Introduce the @tarojs/plugin-mv plug-in to automatically move the packaged files
So far, we have successfully packaged the Taro applet files that can be subcontracted independently. Next, we need to move the packaged dist
directory to the main purchase project.
Move manually? No, a good programmer should try his best to "slacken" in the development process.
Therefore, we will customize a Taro plug-in. When Taro is packaged, will automatically move the packaged files to the main purchase item.
// 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.blended
if (!blended) return
console.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('拷贝结束!')
})
}
Add this custom plug-in to the configuration file:
// config/index.js
const path = require('path')
const config = {
// ...
plugins: [
'@tarojs/plugin-indie',
path.join(process.cwd(), '/plugin-mv/index.js')
]
// ...
}
Re-execute the cross-env NODE_ENV=production taro build --type weapp --blended
packaging command to package and copy the Taro project to the routing folder corresponding to the JD shopping applet project.
At this point, we can open the main purchase applet project in the developer tools app.json
, and conditionally compile the route, you can successfully see the words Hello World
Introduce public methods, public base classes and public components
In the daily development of the main purchase project, we often need to use some public modules and methods encapsulated under the main purchase native project. Then, can the Taro project packaged through hybrid compilation be able to smoothly use these methods and modules in some way? Woolen cloth?
The answer is yes.
Introduce public methods
Let's briefly talk about the idea first, change the configuration items of , 1610bd2d14825b handles the introduction of public methods and public modules through externals configuration , retain these imported statements, and set the introduction method to the commonjs relative path method. The detailed code is as follows:
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()
},
],
})
}
// ...
}
// ...
}
By following this process, we can successfully in code by @common/*
, @api/*
and @libs/*
under the project to introduce native common/*
, api/*
and libs/*
up.
// src/pages/index/index.jsx
import { 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>
)
}
}
You can see that the introduced public methods can also run smoothly in the packaged applet page
Introduce common components
The introduction of public components is simpler. Taro provides the function of introducing public components by default, but if it is packaged in a mixed development mode, you will find that the reference path of the public component cannot be matched. After packaging, the json file of the page configuration is quoted as Taro pack out of the dist folder to the root directory of the applet, the path is also introduced to the root directory as the basis for reference, so we need to use the alias Taro configuration items to certain adjustments to the path:
// 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'),
}
// ...
}
Then we use the public components directly in the code without introducing:
// src/pages/index/index.jsx
import { 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-bar
navBarData={{
title: '测试公共组件导航栏',
capsuleType: 'miniReturn',
backgroundValue: 'rgba(0, 255, 0, 1)'
}}
/>
<Text>Hello world!</Text>
<Button onClick={this.handleButtonClick.bind(this)} >点击跳转到主购首页</Button>
</View>
)
}
}
In this way, the path in usingComponents
index.json
file can perfectly match the public component file under the native applet, and we can also see the normal use and operation of the nav-bar
Introduce the page public base class
In the JD shopping applet, every native page basically introduces a JDPage base class when it is initialized, and uses this base class to modify the original Page instance. will add some buried points to the original life cycle of the Page instance to report And parameter passing and other methods.
When we use Taro for mixed compilation and development, it is obviously a very stupid practice to implement these methods separately, so we need to find a way to perform similar operations in the Taro project and introduce the base class JDPage.
First, in the first step, we need to find the defined location of the Page instance in the compiled JS file. Here we will use regular matching to match the defined location of this Page instance in the code:
const pageRegx = /(Page)(\(Object.*createPageConfig.*?\{\}\)\))/
Find the Page instance, convert the Page instance into the JDPage base class we need, these steps we can write them in our previous self-made Taro plug-in plugin-mv
to complete:
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) return
const 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) return
child._value = child._value.replace(pageRegx, replaceFn)
})
} else {
assets[filename]._value = assets[filename]._value.replace(pageRegx, replaceFn)
}
})
})
}
After the plug-in is processed, the Page in the packaged page JS will be replaced with JDPage, and it will have some basic capabilities of the base class.
At this point, our Taro project has basically opened up the hybrid development process of the JD shopping applet. can use Taro to painlessly develop the native page of the JD shopping applet, and lay a solid foundation for the subsequent dual-end or even multi-end operation.
There is a problem
When using Taro for hybrid development of the JD shopping applet's native page, you will find that Taro has the following compatibility issues in the handling of some public styles and public methods:
Taro will extract the common styles of multiple pages and place them in the
common.wxss
file, but the packagedapp.wxss
file does not introduce these common styles, so the common styles of the page will be lost. The solution is also very simple. Justapp.wxss
file in the plug-in and add the introductioncommon.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") } } }) }
app.js
file packaged with Taro will contain some references to the public methods of the JD shopping applet. This part of the content uses the same relative path as the page JS, so there will be a problem of the wrong reference path. The solution is also It's very simple,app.js
adjust the reference path in 0610bd2d14868a: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 : child handleAppJsReplace(value) }) }) } else { handleAppJsReplace(assets[filename]._value) } } } }) }
Follow-up
This article mainly describes the application and development methods of the Taro project on the JD shopping applet, and there is no content related to the H5 part. Later, I plan to output a development guide for the Taro project on the H5 side, and describe the performance optimization methods of Taro in multi-terminal development.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。