Application scenarios

  • There are multiple applets under the same principal (company, department)
  • These applets consist of a main applets and later new lines of business (each line of business has its own applets)
  • Applets for each line of business need to be mounted below the main program because main program diversion is required
  • At the same time each line of business own small program also issued updates as usual
  • == a set of code to generate independent packages and subpackages by packaging commands == (after subpackages are generated, copy them to the subPages directory of the main program)

Description of project

My business line is called Enjoy_Given, a free barter platform owned by Zhuan

Because our business line applet is built with MPVue (the whole project is also generated through THE CLI of MPVue), the subsequent configuration is based on MPVue as an example, and the wepy project is basically the same.

Here is our directory structure

SRC directory js files need special introduction:

SRC/app. vue is the entry file of the applet, which defines the life cycle of the applet

SRC /main.js to initialize the generic business, define the applets page path and global variables

SRC /vars.js holds global variables for the entire project

SRC/baseinstall.js basic method assembly logic (e.g., mounting logins to vUE objects, statistical logic, identifying channel numbers, etc.)

Overview of subcontracting Configuration

  • Start by configuring the source and appID

    As a subpackage, both of these parameters should be used as the main package parameters (webPack configuration is recommended)

    Source: identifies each line of service for login, registration, and interface access. It is used to identify which line of service the user comes from

    Appid: small program appID assigned by wechat

    Why do YOU need to set these two parameters: Because you cannot log in without setting these parameters

  • Page path problem

    As a subcontract, the jump path of all pages should be prefixed with the jump prefix of the main package (it is suggested to implement the package jump method navigateTo, redirectTo, reLaunch and navigateBack, and it is suggested to deal with them in a unified manner with Webpack).

    When a new line is connected to the main program as a subcontract, a prefix must be added before the page jump path

    Such as: independent small program home page path to/pages/content/index/main

    As a subcontractor, the main program assigns packages as /subPages/enjoy_given

    So the subcontract business line path is: the home page/subPages enjoy_given/pages/content/index/main

  • WXSS reference path problem

    Do not use root import (webpack or shell scripts are recommended)

    Because in the subcontracting state, using root access directly accesses the root directory of the main program, the file does not exist

  • Image path problem

    All image paths adopt THE CDN resource access mode, and do not reference local images

  • Problem with subcontracted main.js and app.vue entry files not being executed

    Each entry from the main package to the subcontract page can be introduced separately by pulling away from the base business assembly method, as discussed later

  • Pull up the applet page for the H5 page within the applet

    When opening the WebView, add a flag bit, or prefix, to tell the H5 page that it is currently in subcontract and that the open path applet should be prefixed

  • Share path problem, add path prefix before path

    This can be done through a common sharing method, which will be discussed later

  • All pages of the applet need to be registered in the main package entry file (app.vue), and every new page needs to be registered

    This is a pit, especially when new pages, will be easy to ignore this problem, here to emphasize

Subcontracting access needs attention
  • Storage naming problem, in order to avoid conflicts with the main program or other business line small program (recommended to use zz_ business name _xxx, our business name is enjoy_given, eg for short, such as: zz_eg_address, zz refers to around)

  • For login problems, it is recommended to use the same cookie name as the main program, so that a common set of user information can be used to avoid the maintenance of a set by both parties, and duplicate authorization can be avoided.

  • Payment problem, ensure that the parameters in the cookie are consistent when placing an order and when paying

  • For debugging, you can ask for a test package of the main program from the main program side and copy the generated code (contents in the dist directory) to subPages/ business name/under the main program package

    For example, our directory is subPages/enjoy_given/

A set of code that generates corresponding packages (standalone and subcontracted) from different packaging commands

Package. The scripts in json

"scripts": {
    "dev": "node build/dev-server.js"."start": "node build/dev-server.js"."build": "rimraf dist && node build/build.js"."lint": "eslint --ext .js,.vue src"."build_subPkg": "node build/build-subpkg.js && sh ./scripts/path-replace.sh"
}
Copy the code
Independent applets (debug) NPM run dev Independent applets (build) NPM run Build Main program subpkg (build) NPM run build_subPkgCopy the code
Why is there no main program subcontracting (testing)

Since we need to copy the code under generated DIST to subPages/enjoy_given/ of the main program, the cost is basically the same no matter whether we build test subcontracting or formal subcontracting, so there is no command to write component subcontracting

Subcontract webPack configuration

Because of the need to be compatible with independent applets and subcontracting business, we recommend separate configuration of Webpack

We configured Webpack for the test environment and the formal environment respectively. By replacing global variables with webpack configuration, we directly modified the global parameters of the project. The replacement is performed dynamically through the NPM command.

To separate the configuration, we made a copy of build.js and renamed it build-subpkg.js

"scripts": {... ."build_subPkg": "node build/build-subpkg.js && sh ./scripts/path-replace.sh"
}
Copy the code

Build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg/build_subPkg

var webpackConfig = require('./webpack.prod.conf'Var webpackConfig = require('./webpack.subpkg.prod.conf')
Copy the code

So the next step is to create the webpack.subpkg.prod.conf file webpack.subpkg.prod.conf which is copied from webpack.prod.conf and still contains 99% of the same content

// webpack.prod.conf

. var config = require('.. /config')
var env = config.build.env
...
var webpackConfig = merge(baseWebpackConfig, {
    ...
    plugins: [
        new webpack.DefinePlugin({
            'process.env': env,
            'app.source': env.APP_SOURCE,
            'app.udeskDebug': env.UDESK_DEBUG,
            'app.id': env.APP_ID,
            'app.pathPrefix': env.APP_PATH_RREFIX,
            'app.isUseCrazyFormId': env.IS_USE_CRAZY_FORMD_ID
        }),
        ...
    ]
})
Copy the code

// webpack.subpkg.prod.conf

. var config = require('.. /config')
var env = config.build.env
...
var webpackConfig = merge(baseWebpackConfig, {
    ...
    plugins: [
        new webpack.DefinePlugin({
            'process.env': env,
            'app.source': env.APP_SUB_PKG_SOURCE,
            'app.udeskDebug': env.UDESK_DEBUG,
            'app.id': env.APP_SUB_PKG_ID,
            'app.pathPrefix': env.APP_SUB_PKG_PATH_RREFIX,
            'app.isUseCrazyFormId': env.IS_USE_CRAZY_FORMD_ID
        }),
        ...
    ]
})
Copy the code

DefinePlugin is used for global substitution such as ‘process.env’: ‘”hahaha”, which means global process.env is replaced with “hahaha”.

Inside by defining a number of global variables, to achieve packaging, through different commands to replace the corresponding environment of global variables let’s have a look.. File in /config/index.js

var path = require('path')

module.exports = {
  build: {
    env: require('./prod.env'),... }, dev: { env: require('./dev.env'),... }}Copy the code

Dev.env.js and prod.env.js are introduced

To prod. Env. Js, for example

Module. exports = {// environment NODE_ENV:'"production"', // Joy sends independent appletssource
  APP_SOURCE: '114', // Joy send subcontracting small programsource
  APP_SUB_PKG_SOURCE: '103'Appid APP_ID:'"wxaaaaaaaaaaaaaaa"'Appid APP_SUB_PKG_ID:'"wxbbbbbbbbbbbbbbbb"'// udesk test flag UDESK_DEBUG:falseAPP_PATH_RREFIX:'" "', // Happy send applets page path prefix APP_SUB_PKG_PATH_RREFIX:'"/subPages/enjoy_given"', // Whether to enable crazyFormId IS_USE_CRAZY_FORMD_ID:true
}

Copy the code

Then let’s take a look at the file SRC /vars.js that holds the global variables (shown in the project screenshot above)

// Applet constantsexportdefault { ... // Small program version number:'1.3.5, // appId appid: app.id, // appletssource(Replaced by Webpack for different environments)source: app.source, // pathPrefix: app.pathPrefix, // Whether to enable CrazyFormId isUseCrazyFormId: app.isUseCrazyFormId}Copy the code
var webpackConfig = merge(baseWebpackConfig, {
    ...
    plugins: [
        new webpack.DefinePlugin({
            'process.env': env,
            'app.source': env.APP_SUB_PKG_SOURCE,
            'app.udeskDebug': env.UDESK_DEBUG,
            'app.id': env.APP_SUB_PKG_ID,
            'app.pathPrefix': env.APP_SUB_PKG_PATH_RREFIX,
            'app.isUseCrazyFormId': env.IS_USE_CRAZY_FORMD_ID
        }),
        ...
    ]
})
Copy the code

After the packaging is complete, “app.xxx” in the global variables file is replaced with a variable of the same name in the Webpack

For example, app.id of appId: app.id in vars.js is replaced with “wxaaAAAAAAAAaaaaa” for standalone applets and “WXBBBBBBBBBBBBBBBBBBBB” for subcontractors.

This completes the whole process of replacing global variables

== as a subcontractor, connected to the main program, its own main.js and app. vue will not execute ==

This is a big hole, because a lot of initialization of common services such as login, cookies, statistics is done here.

The solution

Remove the basic functionality of assembly operations (such as logging, statistics, identifying channel numbers, etc.) from main.js to another file, which I call baseInstall.js. I also added query processing, such as channel number and wechat entrance scene.

In that case, SRC /main.js becomes very simple,

import Vue from 'vue'
import App from './App'
import baseInstall from './baseInstall'
App.mpType = 'app'Baseinstall.init () //!! The key is this line of code!! const app = new Vue(App) app.$mount(a)export default {
  config: {
    pages: [
      '^pages/content/index/main',            // 首页
      ...
    ],
    window: {
      ...
    }
  }
}

Copy the code

The key is the baseInstall.init() line of code

Let’s take a look at baseinstall.js

// General business assembly initialization... asyncfunction init (opts) {
  letoptions = opts ... No. / / for specified channels const channel = options. The channel | | options. C | |' '// Set the channel numberif (channel) {
    VARS.channel = channel.indexOf('waeg_') = = = 0? channel : ('waeg_' + channel)
  }
  ...
  if(! Vars.baseinstallflag) {// To avoid duplicate installations, vars.baseinstallFlag = is distinguished by flag bitstrue. Zzlogin. config({source: vars.source}) zzlogin. install() navigator.install () // count lestatic.config ({appid: VARS.source, pageTypePrefix (currentRoute) {return 'waeg_'} }).install() ... } // write cookie cookie.set({channelID: vars.channel, fromShareUid: vars.shareuid)return options
}

export default {
  init
}

Copy the code
Why use VARS. BaseInstallFlag flag bit

Because main.js is not executed during subcontracting, the actual scenario will jump directly from the main package to some of the subcontracting pages.

Since there is no fixed entry point, baseInstall.js is introduced in each of these pages, and this flag bit is set to avoid repeated assembly.

Why do you want to separate these businesses

Baseinstall. init covers all the services that need to be initialized when starting applets

As mentioned earlier, app.vue and main.js are not executed as subcontractors.

Add the baseInstall.init method to the onLoad lifecycle for all pages. , so we must be removed for more convenient reuse.

In home page, for example (pages/content/index/index. The vue)

import baseInstall from '@/baseInstall'

exportdefault { ... async onLoad (options) { options = await baseInstall.init(options) ... }}Copy the code

Async /await is used because part of the logic in baseInstall.init uses asynchronous requests

Since the main program does not read main.js, all subcontracted page paths must be registered in the main program

Note: Each new page must be registered in the main application. That is, to add a page, to notify the main application side, in their file unified registration

Page path

In subcontracting, all page path accesses are prefixed

Such as: the original access/pages/content/index/main

But the subcontract of the access path is: / subPages/enjoy_given/pages/content/index/main

Solution:

Take the packaged navigateTo, for example

async navigateTo (route) {
    route.url = VARS.pathPrefix + (route.url.indexOf('/') = = = 0?' ' : '/'+ route.url // do prefix processing console.log()'[Navigator] navigateTo:', route)
    ...
    wx.navigateTo(route)
}
Copy the code

The need for a prefix is determined by the pathPrefix in the global VARS variable

PathPrefix is dynamically replaced by Webpack according to the packaging command during the packaging process

The image access path is faulty

The image access path uniformly adopts the CDN resource access path, do not use the local access path, otherwise there is a problem in the subcontracting path, but also increase the volume of the program package

WXSS path problem

In the WXSS file generated by MPvue, the general vendor. WXSS will be introduced, but the introduction path is the root path. As a subcontract, the direct introduction of the root path will access the path of the main package, resulting in the file cannot be found.

@import "/static/css/vendor.wxss"; ._button,._input[type=button],._input[type=reset],._input[type=submit],._textarea{-webkit-appearance:none}._button:after{border:none}page{background-color:#fff}...
Copy the code
The solution

Scripts /path-replace.sh are used to replace files in batches using shell scripts

#! /bin/sh
sed -i "_bak" "s/\/static\/css\/vendor\.wxss/\/subPages\/enjoy_given\/static\/css\/vendor\.wxss/g" `grep "\/static\/css\/vendor\.wxss" -rl ./dist/static/css/pages/**/*.wxss ./dist/static/css/pages/*/*/*.wxss`
Copy the code

The purpose of this section of the shell script is to turn to. / dist/static/CSS/pages/all WXSS file under/static/CSS/vendor WXSS replace/subPages/huanlesong/static/CSS \ vendor WXSS

NPM run build_subPkg is ok when the path change OK generates the official package after the replacement

Sharing path Problems

The path shared by the main program and the independent applet is the same, and is treated like a jump.

The solution

It is suggested to use the common method to handle the problem uniformly. What we do is to add the common method Share. GetFinalShareInfo in the onShareAppMessage of the page

Take home page sharing as an example

import Share from '@/lib/share'
export default {
    ...
    onShareAppMessage() {...return Share.getFinalShareInfo({
            title: 'xxx',
            path: `/pages/content/index/main`,
            imageUrl: 'xxxx'}}})Copy the code

The share.getFinalShareInfo method is called when sharing

Let’s take a look at share.js

exportdefault class Share { static getFinalShareInfo (shareInfo) { ... / / path prefix treatment shareInfo path = VARS. PathPrefix + (shareInfo. Path. IndexOf ('/') = = = 0?' ' : '/') + shareInfo.path
        ...
        return shareInfo
    }
}
Copy the code

This completes the configuration of the entire subcontracting business. Is not very troublesome ~

At the beginning and the main program fusion time really stepped on a lot of pits, here I share the solution with you

If there is a better solution, we hope to communicate with each other 🙂