應用場景
項目概述我的這條業務線叫歡樂送(項目名為enjoy_given),是轉轉旗下一個免費的以物換物平臺 因為我們這條業務線小程序是用mpvue構建的(整個項目也是通過mpvue的cli生成的),所以后面相關配置都是以mpvue為例,如果是wepy項目基本也大同小異。 下面就是我們的目錄結構
src目錄下的幾個js文件需要專門介紹下: src/App.vue 是小程序的入口文件,里面定義的是小程序的生命周期 src/main.js 里面初始化通用業務、定義小程序頁面路徑和全局變量 src/vars.js 存放整個項目的全局變量 src/baseInstall.js 基礎方法裝配邏輯(如:給vue對象掛載登錄、統計邏輯、識別渠道號等) 分包配置概述
分包接入需要注意的地方
一套代碼,通過不同打包命令生成對應的程序包(獨立包和分包)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,通過對webpack配置替換全局變量,直接修改項目的全局參數。 為了分開配置,我們拷貝了一份build.js更名為build-subpkg.js "scripts": { ..., "build_subPkg": "node build/build-subpkg.js && sh ./scripts/path-replace.sh" }
build_subPkg命令就是讀取的build-subpkg.js文件 var webpackConfig = require('./webpack.prod.conf') 變更為 var webpackConfig = require('./webpack.subpkg.prod.conf')
所以下一步就是創建webpack.subpkg.prod.conf文件 // 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 }), ... ] })
里面通過定義多個全局變量,實現打包時,通過不同的命令替換對應環境下的全局變量 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中的同名變量替換掉
這樣整個替換全局變量的流程就跑完了 ==作為分包,接入主程序中,自己的main.js和App.vue都不會執行==這個是大坑,因為很多通用業務的初始化如登錄、cookie、統計都是在這里完成的。 解決方案
把基礎功能的裝配業務(如在錄、統計、識別渠道號等邏輯)從main.js中抽離到另一個文件,我這里叫baseInstall.js。 那這樣的話,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 } 為什么要用VARS.baseInstallFlag標志位因為,在分包時候是不執行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) ... } }
因為主程序不會讀取main.js,所以,所有的分包頁面路徑,都要統一在主程序中注冊
頁面路徑在分包中,所有頁面路徑訪問要加入前綴
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的資源訪問路徑,不要用本地訪問路徑,要不然在分包路徑中是有問題的,同時也會增加程序包的體積 wxss路徑問題用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腳本對文件進行批量替換 #!/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 分享路徑問題主程序和獨立小程序分享出來的路徑也是一樣的,處理方式和跳轉類似。 解決方案建議通過通用方法統一處理,我們的做法是,在頁面的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 } } 這樣整個分包業務就配置完成了。是不是很麻煩~ 當初和主程序融合時候確實踩了很多坑,這里我把解決方案和大家分享下 如果有更好的解決方案,也希望一起交流:) |