uniApp小程序改造插件笔记-分包插件调研

概述:

分包速度快,第一版稳定性较强,后台修改较少,需要主包配合一些配置,但后期可能每次都需要主包进行一些配合。 (速度型,类似sdk)
插件修改内容较多,开发速度较慢,第一版相对不稳定,后台配合修改较多,也需要主包进行一点配置,暂不支持手机号获取等部分API,但后期操作可能更加独立,更适合其它小程序对接。(长远型)

  • 注意:2.12.2基础库后插件页就可以调小程序组件了,也就是可以实现手机号、用户信息获取功能而无需功能页,但是只能小程序组件只能在插件页使用,不能在插件的组件中使用。
插件不能使用及处理:
  • getApp() ===> getApp()替换成处理vue 的store
  • 获取用户信息、手机号 ===> 用户功能页/2.12.2基础库调小程序组件(仅插件页)
  • 其它Api ===> 可通过小程序提供方法 给插件,插件回调小程序方法 间接调取微信Api (js 中重写Api方法)
  • ext.json ===> 需通过将插件ext.json合并到小程序ext.json中通过插件回调小程序提供方法方式间接获取
  • 路由 ===> navigateTo url处理,底层磨平使用plugin-private://wxXXXXXXXX+url 跳转

tips: 外部直接跳会员中心小程序页面:(_plugin_/wxXXXXXXXX+url)

插件改造步骤

嵌套模式开发 : 一套代码,子目录为小程序目录,外层目录为插件壳目录。从而实现一套代码开发插件与小程序,小程序从小程序目录运行,插件通过外壳编译后使用微信开发者工具运行。

插件判断页面中可以通过方法(getCurrentPages..pop().route中是否包含plugin/Appid )判断是否是插件
非页面中不能判断是否是插件

插件开发策略:尽量通过全局处理方式进行处理,避免过多的差异开发

改造流程:
->首先:git新增分支插件+小程序dev作为日后开发分支。
->分支集成添加vue-cli
->安装uniapp2wxpack
->将小程序项目配置到src中
->修改插件测试主程序内容(pluginActions.js、functionalPages、插件引入、跳转插件链接)
(注意:主程序可通过pluginActions.js 提供接口方法、变量给插件,插件通过plugin.json可提供组件、页面、接口js给主程序。)
->插件跳转链接:plugin://插件名/插件页名或 plugin-private://wxXXXXXXXX/+url,一般用第一种,因为不对外暴露页面路径

npm run dev:mp-weixin-pack-plugin (开发模式) 方便调试
npm run build:mp-weixin-pack-plugin (生产模式)打出的包小

相关命令:
-> npx uniapp2wxpack --create (安装uniapp2wxpack)
-> npm run dev:mp-weixin-pack-plugin (打包)

开发优化:

切换小程序/插件开发需要在项目src目录下手动执行build.js 重新生成pages.json, 通过pages-gaps.json文件配置小程序与插件在pages.json中的差异点
-> 执行 “ node ./build.js app “或“ node ./build.js “生成 小程序 pages.json
-> 执行 node ./build.js plugin 生成 插件 pages.json

如果使用全局变量切换项目是否是插件可重写main.js添加MP_Plugin字段实现

build文件内容

/*
    处理pages.json内容插件/小程序差异
    -> cd 到build.js 所在目录
    -> 执行 “ node ./build.js app “或“ node ./build.js “生成 小程序 pages.json
    -> 执行 node ./build.js plugin 生成 插件 pages.json
*/

const fs = require('fs')
var arguments = process.argv.splice(2);

/* pages.json */
// 读
let pagesJson = fs.readFileSync(__dirname + '/pages.json','utf8')
let gapsJson = fs.readFileSync(__dirname + '/pages-gaps.json','utf8')

// 改
pagesJson = JSON.parse(pagesJson);
gapsJson = JSON.parse(gapsJson);
if(arguments.length>0){
    if ((typeof pagesJson) === "object") {
        findGaps(pagesJson,arguments[0])
    }
}

function findGaps(obj,type){
    for(let key in obj){
        let item = obj[key];
        if ((typeof item) === "object") {
            if (item.gaps_key && item.gaps_title) { // 找到差异点
                changeGaps(item,type)
            }
            findGaps(item,type)
        } 
    }
}

function changeGaps(obj,type){
    let change = "";    
    if (type == 'plugin') { // 插件
        if(gapsJson.plugin[obj.gaps_key]){
            change = gapsJson.plugin[obj.gaps_key]
        }
    } else { // 小程序
        if(gapsJson.app[obj.gaps_key]){
            change = gapsJson.app[obj.gaps_key]
        }
    }
    obj[obj.gaps_title] = change;
}

// 写
fs.writeFile(
 __dirname + '/pages.json',
 // 我这边是用两个空格来缩进 pages.json,如果喜欢制表符,第三个参数更换你为 \t 即可
 JSON.stringify(pagesJson, null, '\t'),
 e => e ? console.error(e) : console.log('pages.json 配置文件更新成功')
)


/* main.js */
let mainJs = fs.readFileSync(__dirname + '/main.js','utf8')
if(arguments.length>0){
    let strA = "import Vue from 'vue'\nVue.prototype.MP_Plugin = true;\n";
    let strB = "import Vue from 'vue'\nVue.prototype.MP_Plugin = false;\n";
    if (arguments[0] == 'plugin') { // 插件
        changeMP_Plugin(strA, strB);
    } else { // 小程序
        changeMP_Plugin(strB,strA);
    }
}

function changeMP_Plugin(newStr,oldStr){
    if(mainJs.indexOf(newStr) > -1){ // 不用修改
        return;
    }   
    if(mainJs.indexOf(oldStr) > -1){
        var reg= new RegExp(oldStr,"g"); 
        mainJs = mainJs.replace(reg,newStr)
    } else {
        if(mainJs.indexOf("import Vue from 'vue'") > -1){
            var reg= new RegExp("import Vue from 'vue'","g");
            mainJs = mainJs.replace(reg,newStr)
        }else{
            console.log("未找到import Vue from 'vue'")
        }
        
    }
}

fs.writeFile(
 __dirname + '/main.js',
 // 我这边是用两个空格来缩进 main.js,如果喜欢制表符,第三个参数更换你为 \t 即可
 mainJs,
 e => e ? console.error(e) : console.log('main.js 配置文件更新成功')
)

pages-gaps.json文件内容:
mini-program-plug-xxx-comp: 插件中使用小程序的组件

{
    "//"  : "差异json配置说明: 在pages.json 中有差异地方添加属性gaps_key和gaps_title( \"gaps_key\": \"gaps_xxx\",\"gaps_title\": \"yyy\" ,在此文件中写差异配置,key 为gaps_key定义的值)",
    "app" : {
        "gaps_home_style":{
            "enablePullDownRefresh": true,
            "mp-weixin": {
                "usingComponents": {
                    "cell":     "plugin://contactPlugin/cell"
                }
            }
        }
    },
    "plugin" : {
        "gaps_home_style":{
            "enablePullDownRefresh": true,
            "componentGenerics":{
                "mini-program-plug-xxx-comp":true
            }
        }
    }
}

总括

src添加文件:
build.js : 用来执行生成pages.json ,从插件开发切换到小程序开发或从小程序开发切换到插件开发时使用,命令node ./build.js plugin
pages-gaps.json: 配置pages.json中插件与小程序差异内容文件
plugin-fit.js: 底层磨平插件与小程序差异内容(api调用,方法等)

主程序添加文件:
pluginActions.js : 主程序供插件调用api方法
ext.json : 插件ext配置文件(合并主程序中)
plug-xxx-comp组件: 外部组件供插件使用(处理插件一些无法调用的组件)
app.json配置:

    "plugins" : {
            "xxx" : {
                "version" : “1.0.0”,
                "provider" : "wxXXXXXXXXXX",
                "export" : "pluginActions.js",
                "genericsImplementation": {
                    "xxx-home": {
                      "mini-program-plug-xxx-comp": "component/plug-xxx-comp/plug-xxx-comp"
                    }
                }
                     }
         },
           "functionalPages" : {
               "independent" : true
           },
           "permission": {
             "scope.userLocation": {
               "desc": "根据位置进行定位"
             }
         }

tips:主包使用不了分包的组件,分包可以使用主包的组件

底层磨平文件plugin-fit.js内容:


/* 
    插件处理 
*/
import Vue from 'vue'
// Api
uni.getExtConfigSync = function() {
    return actionApi("getExtConfigSync")
}

uni.authorize = function(params) {
    console.log('authorize');
    return actionApi("authorize",params)
}

wx.addCard = function(params){
    return actionApi("addCard",params);
}


uni.clearStorageSync = function(params) {
    return actionApi("clearStorageSync",params)
}

uni.hideKeyboard = function() {
    return actionApi("hideKeyboard")
}

uni.saveFile = function(params) {
    return actionApi("saveFile",params)
}

uni.getSavedFileList = function(params) {
    return actionApi("getSavedFileList",params)
}

uni.checkSession = function(params) {
    return actionApi("checkSession",params)
}

uni.login = function(params) {
    return actionApi("login",params)
}

uni.getUserInfo = function(params) {
    return actionApi("getUserInfo",params)
}

// 插件小程序跳转 如果是本地则直接返回
uni.navigateToMiniProgram = function(params) {
    if (params["appId"] == uni.getAccountInfoSync().miniProgram.appId) {
        return uni.navigateBack({
            delta:1000,
        });
    }
    return actionApi('navigateToMiniProgram', params);
}

uni.getAccountInfoSync = function(){
    return actionApi("getAccountInfoSync");
}




// route
const pluginUrlHead = "plugin-private://wxXXXXXXXXXX";

defineRoute ('navigateTo')
defineRoute ('switchTab')
defineRoute ('reLaunch')
defineRoute ('redirectTo')
 
var unit = require("@/common/unit/unit.js");
function defineRoute(name) {
    const originRoute = uni[name];
    Object.defineProperty(uni, name, {
        configurable: true,
        enumerable: true,
        writable: true,
        value: function (obj) {
            if (!Vue.prototype.MP_Plugin) {
                return originRoute(obj);
            }
            if(obj.url.indexOf('/xxxxx')>-1){
                let mall_appid = unit.getMallAppid();
                let mall_path = unit.getMallAppPath();
                console.log(mall_appid)
                uni.navigateToMiniProgram({
                  appId: mall_appid,
                  path: mall_path,
                  success(res) {
                  },
                  fail(err) {
                  }
                })
                return;
            }
            console.log("url",obj);
            var newurl = pluginUrlHead + obj.url;
            if(obj.miniProgram_page){
                return originRoute({
                    url:obj.url
                });
            }else{
                return originRoute({
                    url:newurl
                });
            }
        }
    })
}

// Storage 存储 
const getStorageSync = uni.getStorageSync;
uni.getStorageSync = function(key) {
    if(Vue.prototype.MP_Plugin && key == 'userInfo'){ 
        return requireMiniProgram().getMemberUserinfo();
    }
    return getStorageSync(key);
}

/** 
 *  api调用
 *  apiName api 名称
 *  params 参数
*/
function actionApi(apiName,params) {
    let originApi = null;
    // #ifdef MP-WEIXIN
        originApi = wx[apiName];
    // #endif
    // #ifndef MP-WEIXIN
        originApi = uni[apiName];
    // #endif
    if (!Vue.prototype.MP_Plugin) { // 小程序还执行原始api
        return originApi(params);
    }
    return requireMiniProgram().xxxPublic(apiName, params);
}

pluginActions.js文件内容

module.exports = { 
    //名称 需要 xxxPublic 
      xxxPublic(funName, param) {
          return wx[funName](param);
      },
      getMemberUserinfo(){
            return wx.getStorageSync('userInfo');   
      }
}

========================================================================================================================

小程序使用插件修改内容:

1.app.json

    "subPackages": [
        {
            "root": "pages/xxx/",
            "pages": [
                {
                    "//": "授权页",
                    "path": "plugin-auth",
                    "style": {}
                }
            ],
            "plugins": {
                "xxx": {
                    "version": "1.0.0",
                    "provider": "wxXXXXXXXXXX",
                    "export": "pluginActions.js",
                    "genericsImplementation": {
                        "xxx-home": {
                            "mini-program-plug-xxx-comp": "component/plug-xxx-comp/plug-xxx-comp"
                        }
                    }
                },
                "contactPlugin": {
                    "version": "1.3.0",
                    "provider": "wx104a1a20c3f81ec2"
                    }
            },
            "functionalPages": {
                "independent": true
            },
            "permission": {
                "scope.userLocation": {
                    "desc": "根据位置进行定位"
                }
            }
        }
    ]
  1. 单独文件夹添加 plugin-auth 授权页、pluginActions.js、plug-xxx-comp
    3.ext中添加内容
    "ext": {
        "version": "1.0",
        "api_url": "https://x x x/", // 插件网络请求地址
        "mall_appid": "",     // 其它小程序id,没有“”即可
        "mall_path": ""     // 其它小程序path,没有“”即可
      },

4.需要跳转插件的地方直接跳转授权页就行,授权页已授权会直接跳插件,未授权会展示授权页内容

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342