个人这条业务线叫欢乐送(项目名为enjoy_given),是转转旗下一个免费的以物换物平台css
由于咱们这条业务线小程序是用mpvue构建的(整个项目也是经过mpvue的cli生成的),因此后面相关配置都是以mpvue为例,若是是wepy项目基本也大同小异。vue
下面就是咱们的目录结构node
src目录下的几个js文件须要专门介绍下:webpack
src/App.vue 是小程序的入口文件,里面定义的是小程序的生命周期web
src/main.js 里面初始化通用业务、定义小程序页面路径和全局变量shell
src/vars.js 存放整个项目的全局变量npm
src/baseInstall.js 基础方法装配逻辑(如:给vue对象挂载登陆、统计逻辑、识别渠道号等)json
首先要配置source和appid小程序
做为分包时,这两个参数都要统一采用主包参数(建议经过webpack配置来实现)bash
source:是每条业务线登陆、注册、和接口访问时用的标识,用来区分该用户来自于哪条业务线
appid:微信分配的小程序appid
为何要配置这两参数:由于不配置无法登陆
页面路径问题
做为分包时,全部页面的跳转路径都要加主包的跳转前缀(建议经过包装跳转方法navigateTo、redirectTo、reLaunch、navigateBack实现,建议配合webpack统一处理)
当新业务线做为分包接入主程序时,页面跳转路径前须要统一加一个前缀
如:独立小程序首页路径为 /pages/content/index/main
做为分包时,主程序分配的包为/subPages/enjoy_given
那么分包业务线首页路径为: /subPages/enjoy_given/pages/content/index/main
wxss引用路径问题
不要用使根目录引入方式(建议采用webpack或者shell脚原本完成)
由于在分包状态下,用根目录访问方式会直接访问主程序的根目录,文件是不存在的
图片路径问题
全部图片路径统一采用cdn资源访问方式,不要引用本地图片
对于分包的main.js和App.vue入口文件不执行的问题
能够经过抽离基础业务装配方法,对于每个从主包跳到分包页面的入口分别引入,后面会细说
对于小程序内的h5页面拉起小程序页面
在打开webview时候,要加入一个标志位,或者prefix,告诉h5页面,当前处于分包当中,打开的小程序path要加前缀
分享路径问题,在路径前面也要加入路径前缀
能够经过一个通用的分享方法,进行统一处理,后面会细说
小程序的全部页面都须要在主包入口文件(app.vue)注册,每新增页面都要注册
这个是坑,尤为新增页面时,会很容易忽略这个问题,这里要特别强调下
storage命名问题,为了不和主程序或者其余业务线小程序发生冲突(建议采用 zz_业务名_xxx, 咱们业务名是enjoy_given,简称eg,如: zz_eg_address, zz指的就是转转)
登陆问题,推荐和主程序使用一样的cookie名称,这样能够通用一套用户信息,省得双方各维护一套,还能避免重复受权。
支付问题,保证下单时和支付时,cookie中的参数保持一致
调试,能够找主程序那边要个主程序的测试包,把生成的代码(dist目录下的内容)拷贝到主程序包的 subPages/业务名/ 下面
例如咱们的目录是 subPages/enjoy_given/(目录结构同上)
package.json中scripts
"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"
}
复制代码
独立小程序(调试) npm run dev
独立小程序(构建) npm run build
主程序分包(构建) npm run build_subPkg
复制代码
由于咱们不管是构建测试分包仍是构建正式分包,都要把生成dist下的代码拷贝到主程序的subPages/enjoy_given/目录下,成本基本是同样的,因此,就没有写构件分包的命令
由于须要兼容独立小程序和分包业务,webpack咱们建议分开配置
咱们对测试环境和正式环境分别配置了webpack,经过对webpack配置替换全局变量,直接修改项目的全局参数。 经过npm命令动态执行替换。
为了分开配置,咱们拷贝了一份build.js改名为build-subpkg.js
"scripts": {
...,
"build_subPkg": "node build/build-subpkg.js && sh ./scripts/path-replace.sh"
}
复制代码
build_subPkg命令就是读取的build-subpkg.js文件 build.js和build-subpkg.js中99%的内容都同样,只有一行不同
var webpackConfig = require('./webpack.prod.conf')
变动为
var webpackConfig = require('./webpack.subpkg.prod.conf')
复制代码
因此下一步就是建立webpack.subpkg.prod.conf文件 webpack.subpkg.prod.conf由webpack.prod.conf拷贝而来,里面依旧99%的内容一致
// 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
}),
...
]
})
复制代码
// 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
}),
...
]
})
复制代码
DefinePlugin插件是用来进行全局替换的 如:'process.env': '"hahaha"', 指的就是全局process.env替换为"hahaha"
里面经过定义多个全局变量,实现打包时,经过不一样的命令替换对应环境下的全局变量 咱们看一下../config/index.js中的文件
var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
...
},
dev: {
env: require('./dev.env'),
...
}
}
复制代码
引入了dev.env.js和prod.env.js
以prod.env.js为例
module.exports = {
// 环境
NODE_ENV: '"production"',
// 欢乐送独立小程序source
APP_SOURCE: '114',
// 欢乐送分包小程序source
APP_SUB_PKG_SOURCE: '103',
// 欢乐送独立程序appid
APP_ID: '"wxaaaaaaaaaaaaaaa"',
// 欢乐送分包程序appid
APP_SUB_PKG_ID: '"wxbbbbbbbbbbbbbbbb"',
// udesk测试标志位
UDESK_DEBUG: false,
// 欢乐送独立小程序页面路径前缀
APP_PATH_RREFIX: '""',
// 欢乐送分包小程序页面路径前缀
APP_SUB_PKG_PATH_RREFIX: '"/subPages/enjoy_given"',
// 是否启用crazyFormId
IS_USE_CRAZY_FORMD_ID: true
}
复制代码
而后咱们再来看一下存放全局变量的文件src/vars.js(上面项目截图中有)
// 小程序常量
export default {
...
// 小程序版本号
version: '1.3.5',
// 小程序appid
appId: app.id,
// 小程序source(由webpack根据不一样环境统一替换)
source: app.source,
// 路径前缀
pathPrefix: app.pathPrefix,
// 是否启用CrazyFormId
isUseCrazyFormId: app.isUseCrazyFormId
}
复制代码
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
}),
...
]
})
复制代码
在打包完成后,全局变量文件中的"app.xxx"会被webpack中的同名变量替换掉
如vars.js中 appId: app.id的app.id会被替换,独立小程序时该值为"wxaaaaaaaaaaaaaaa",做为分包业务时,该值为"wxbbbbbbbbbbbbbb"
这样整个替换全局变量的流程就跑完了
这个是大坑,由于不少通用业务的初始化如登陆、cookie、统计都是在这里完成的。
把基础功能的装配业务(如在录、统计、识别渠道号等逻辑)从main.js中抽离到另外一个文件,我这里叫baseInstall.js。 里面我还加入了对query的处理,好比渠道号channel和微信入口scene。
那这样的话,src/main.js就会变得很是简单,
import Vue from 'vue'
import App from './App'
import baseInstall from './baseInstall'
App.mpType = 'app'
baseInstall.init() // !!!最关键就是这行代码!!!
const app = new Vue(App)
app.$mount()
export default {
config: {
pages: [
'^pages/content/index/main', // 首页
...
],
window: {
...
}
}
}
复制代码
里面最关键的是baseInstall.init()这行代码
下面咱们来看看baseInstall.js
// 通用业务装配初始化
...
async function init (opts) {
let options = opts
...
// 获取指定渠道号
const channel = options.channel || options.c || ''
// 设置渠道号
if (channel) {
VARS.channel = channel.indexOf('waeg_') === 0 ? channel : ('waeg_' + channel)
}
...
if (!VARS.baseInstallFlag) {
// 为了不重复装备,经过标志位进行区分
VARS.baseInstallFlag = true
...
// 登陆配置
ZZLogin.config({
source: VARS.source
})
ZZLogin.install()
Navigator.install()
// 统计
LeStatic.config({
appid: VARS.source,
pageTypePrefix (currentRoute) {
return 'waeg_'
}
}).install()
...
}
// 写入cookie
cookie.set({
channelid: VARS.channel,
fromShareUid: VARS.shareUid
})
return options
}
export default {
init
}
复制代码
由于,在分包时候是不执行main.js的,实际场景,会从主包的业务直接跳转到分包的一些页面。
因为没有固定入口,因此在这些页面中都要加入baseInstall.js的引入,为了不重复装配,才会设置这个标志位。
baseInstall.init里面涵盖了全部启动小程序时须要初始化的业务
前面也提到了在做为分包时,本身的App.vue和main.js是不会执行的。
那怎么办,这样,就在全部的页面中,在onLoad的生命周期中加入baseInstall.init方法。 ,因此咱们抽离确定是为了更方便的复用了。
以首页为例(pages/content/index/index.vue)
import baseInstall from '@/baseInstall'
export default {
...
async onLoad (options) {
options = await baseInstall.init(options)
...
}
}
复制代码
用async/await是由于baseInstall.init中部分逻辑用到了异步请求
注:每新增一个页面,都要在主程序中注册。也就是新增一个页面,就要通知主程序那边,在他们的文件里统一注册
在分包中,全部页面路径访问要加入前缀
如:原来访问/pages/content/index/main就能够了
可是分包的访问路径为:/subPages/enjoy_given/pages/content/index/main
以包装的navigateTo为例
async navigateTo (route) {
route.url = VARS.pathPrefix + (route.url.indexOf('/') === 0 ? '' : '/') + route.url // 这里作前缀处理
console.log('[Navigator] navigateTo:', route)
...
wx.navigateTo(route)
}
复制代码
这里面需不须要加前缀,都是由全局变量VARS中的pathPrefix来决定
而pathPrefix是在打包过程当中由webpack根据打包命令动态替换的
图片访问路径统一采用cdn的资源访问路径,不要用本地访问路径,要否则在分包路径中是有问题的,同时也会增长程序包的体积
用mpvue生成的wxss文件,里面会把通用的vendor.wxss引入,可是引入路径是根路径,做为分包,直接引入根路径,会去访问主包的路径,致使文件没法找到。
@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}...
复制代码
经过shell脚本对文件进行批量替换 scripts/path-replace.sh
#!/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`
复制代码
这段shell脚本的目的就是把./dist/static/css/pages/下全部的wxss文件中的/static/css/vendor.wxss替换成/subPages/huanlesong/static/css\vendor.wxss
替换完成后,路径变动ok 生成正式包的时候,用npm run build_subPkg就ok了
主程序和独立小程序分享出来的路径也是同样的,处理方式和跳转相似。
建议经过通用方法统一处理,咱们的作法是,在页面的onShareAppMessage中加入通用方法Share.getFinalShareInfo
以首页分享为例
import Share from '@/lib/share'
export default {
...
onShareAppMessage () {
...
return Share.getFinalShareInfo({
title: 'xxx',
path: `/pages/content/index/main`,
imageUrl: 'xxxx'
})
}
}
复制代码
分享时统一调用Share.getFinalShareInfo方法
咱们再来看下share.js
export default class Share {
static getFinalShareInfo (shareInfo) {
...
// 路径前缀处理
shareInfo.path = VARS.pathPrefix + (shareInfo.path.indexOf('/') === 0 ? '' : '/') + shareInfo.path
...
return shareInfo
}
}
复制代码
这样整个分包业务就配置完成了。是否是很麻烦~
当初和主程序融合时候确实踩了不少坑,这里我把解决方案和你们分享下
若是有更好的解决方案,也但愿一块儿交流:)