概述:
分包速度快,第一版稳定性较强,后台修改较少,需要主包配合一些配置,但后期可能每次都需要主包进行一些配合。 (速度型,类似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": "根据位置进行定位"
}
}
}
]
- 单独文件夹添加 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.需要跳转插件的地方直接跳转授权页就行,授权页已授权会直接跳插件,未授权会展示授权页内容