chiwent / blog

个人博客,只在issue内更新
https://chiwent.github.io/blog
8 stars 0 forks source link

小程序开发记录 #19

Open chiwent opened 4 years ago

chiwent commented 4 years ago

小程序开发记录

持续更新中...

微信体系的一些基本概念

关于unionid机制下如何获取用户基本信息,可以参考官方文档:获取用户基本信息(UnionID机制)UnionID 机制说明

微信小程序的登录流程:
image

微信授权和开发功能

关于如何获取openid和unionid

openid的获取方式有两种:

unionid的获取机制可以参考上一节提到的官方文档,下面做一些简要补充。
一般来说,获取unionid可以有两种方式来实现:

用户在首次加载小程序后完成授权,后续再次进入小程序就是静默授权(保有缓存),不会有弹窗授权

开发者如何正确调用授权

目前,在用户首次授权时,微信强制开发者使用调出按钮的方式来引导用户授权。所以,正确的授权方式应该是在页面中定义一个授权弹窗,弹窗上的授权按钮是带有授权功能的,点击后即可完成授权:

授权窗口

<view class="authorize" hidden="{{isShowAuth}}">
    <view class="box">
      <view class="title">授权登录</view>
      <view class="content">是否授权获取用户信息</view>
      <view class="footer">
        <button bindtap="cancelAuthroize">取消</button>
        <button open-type="getUserInfo" bindgetuserinfo="getAuthorize">确定</button>
      </view>
    </view>
  </view> 
isHiddenAuth: false

getAuthorize() {
    if (this.data.isShowAuth) {
        wx.getSetting({
            success: res => {
                if (res.authSetting['scope.userInfo']) {
                    // 已经授权,可以直接调用getUserInfo静默授权
                    this.getUserInfoFromServer();
                    this.setData({
                        isShowAuth: false
                    });
                } else {
                    // 还未授权,调出弹窗引导用户授权
                    this.setData({
                        isShowAuth: true
                    });
                }
            }
        })
    } else {
        // 如果已经授权就直接登录,在这里可以保存用户信息
    }
}
getUserInfoFromServer() {
    wx.getUserInfo({
        withCredentials: true,
        success: async res => {
            // 向后端发出请求,获取openid和unionid
            const data = await request({
                encryptedData: res.encryptedData,
                iv: res.iv
            });
            // 保存用户信息
            app.globalData.userInfo = data;
        }
    });
},
cancelAuthroize() {
    this.setData({
        isShowAuth: false,

    });
    app.globalData.userInfo = null;
}

微信小程序生命周期

微信小程序的生命周期可以大致分为3类,App入口的生命周期,页面的生命周期以及组件的生命周期。

关于onLaunch和onShow的说明:
小程序的启动方式有两种:冷启动和热启动,在用户首次打开小程序的时候,小程序的启动方式就是冷启动,此时会触发onLaunch和onShow回调;当用户将该小程序切至后台,但是并没有被销毁,然后再次打开,小程序的加载时间一般会首次打开的时间更短,此时不会触发onLaunch,但是会触发onShow

1.lifetimes:

2.pageLifetimes:

请求Promise化

var wxPromise = function (fn) {
    return function (opt = {}) {
        return new Promise((resolve, reject) => {
            opt.success = function(res) {
                resolve(res);
            };
            opt.fail = function(res) {
                reject(res);
            }
            fn(opt);
        })
    }
}

// 使用
var wxRequest = wxPromise(wx.request);
wxRequest({ url, param }).then(res => {
    console.log(res.data);
});

监控或埋点

拦截请求

在web开发中,我们可以对原生ajax方法或者axios拦截器做一些修改,来实现网络请求的监听。如下:

window.XMLHttpRequest.prototype.open = (originMethod => {
    return function (method, url, async) {
        console.log('发送请求,接口是:', url);
        return originMethod.apply(this, arguments);
    };
})(window.XMLHttpRequest.prototype.open);

在微信小程序中,我们同样可以通过拦截wx.request方法来实现请求的监听。不过,和上面的方式不一样,我们不能直接对wx.request进行赋值来改写,它只有get方法而没有set方法。所以,我们要用Object.defineProperty来修改:

const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
    configurable: true,
    enumerable: true,
    writable: true,
    valud: function() {
        const config = arguments[0] || {};
        const url = config.url;
        console.log('发送请求,接口是:', url);
        return originRequest.apply(this, arguments);
    }
});

使用发布订阅模型来进行全局事件通信

function PubSub() {
    // 订阅列表
    this.eventList = null;
}
// 订阅事件
PubSub.prototype.subscribe = function(eventName, func) {
    this.initEventList(eventName);
    this.eventList[eventName].push(func);
};
// 移除订阅
PubSub.prototype.unsubscribe = function(eventName,  func) {
    this.initEventlist(eventName);
    this.eventList[eventName] = this.eventList[eventName].filter(function (item) {
        return item != func;
    });
    if (!this.eventList[eventName].length) {
        delete this.eventList[eventName];
    }
};
// 发布事件
PubSub.prototype.publish = function(eventName, data) {
    this.initEventList();
    if (this.eventName[eventName]) {
        for (var i = 0, event = this.eventList[eventName]; i < eventList.length; i += 1) {
            var func = event[i];
            func(data);
        }
    }
};
// 初始化事件列表
PubSub.prototype.initEventList = function(eventName) {
    if (!this.eventList) {
        this.eventList = {};
    }
    if (eventName && !this.eventList[eventName]) {
        this.eventList[eventName] = [];
    }
};

module.exports = PubSub;

使用:

var PubSub = require('../utils/pubSub');
App({
  onLaunch: function(e) {
    //  注册发布订阅模式
    event: new PubSub(),
  }
});

// 页面A - 发布
var app = getApp()
Page({
    publish: function() {
        app.event.publish('test',  'test')
    },
})

// 页面B - 订阅
var app = getApp()
Page({
    onLoad: function(){
        app.event.subscribe('test', this.subFunc.bind(this))
    },
    onUnload: function() {
       // 移除所有订阅事件
       app.event.unsubscribe('test');
       // 移除单个订阅事件
        app.event.unsubscribe('test', this.subFunc.bind(this))
    },
    subFunc: function(param) {
        // 监听事件
       console.log(param)
    },
})

小程序设置缓存有效期

微信小程序通过自带API设置的客户端缓存是不存在有效期的概念的,长期有效,如果需要设置带有效期的缓存,就需要同时对一个目标同时设置目标本身的数据,以及对应自身的设置时间:

// 基本原理:通过微信官方的API分别对数据进行两份缓存,一份是原数据的缓存,另外一份是对应的时间进行缓存,时间缓存中带有原数据的key值
class Storage {
    constructor(props) {
        this.props = props || {};
        this.source = wx || this.props.source;
    }
    /**
     * 获取缓存
     * @param {String} key key值
     * @return 缓存内容
     */
    getStorage(key) {
        const timeout = parseInt(this.source.getStorageSync(`${key}__time`) || 0, 10);
        // 有对应的时间缓存
        if (timeout) {
            // 如果超出时间
            if (Date.now() > timeout) {
                this.removeStorage(key);
                return;
            }
        }
        const value = this.source.getStorageSync(key);
        return value;
    }
    /**
     * 设置缓存
     * @param {String} key key值
     * @param value value值
     * @param {Number} timeout 过期时间
     * @return value
     */
    setStorage(key, value, timeout = 0) {
        const _timeout = parseInt(timeout, 10);
        this.source.setStorageSync(key, value);
        if (_timeout) {
            this.source.setStorageSync(`${key}__time`, Date.now() + 1000 * 60 * _timeout);
        } else {
            this.source.removeStorageSync(`${key}__time`);
        }
        return value;
    }
    removeStorageSync(key) {
        this.source.removeStorageSync(key);
        this.source.removeStorageSync(`${key}__time`);
        return undefined;
    }
}
const storage = new Storage();
wx.$storage = storage;
export default storage;




参考:

chiwent commented 4 years ago

小程序优化

一些在前端开发中通用的优化策略就不谈了,主要是针对小程序这种特殊情况的。

分包加载

小程序主包的大小限制为2M,超过该大小后无法上传。所以,对于一些较为独立的业务功能,应该考虑分包开发,分包的文件不会占用主包的空间。

不要滥用setData

通过setData设置的数据,是小程序数据层和视图层的中间介质。微信开发者文档上有这么一句话:

  小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。
而 evaluateJavascript 的执行会受很多方面的影响,数据到达视图层并不是实时的。

大量数据、频繁地setData,会增加数据层和视图层通信的压力,影响小程序的性能。所以,对于setData的操作,我们应该要有以下的基本准则:

官方文档上也提供了一些关于setData的优化tips,可以参考: https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html

补充说明: 不要用setData将data值设置为undefined,否则可能会出现一些问题(控制台也会警告)

前面提到了数据层和视图层的通信问题,实际上可以展开为,不仅仅是setData,一切涉及到此类通信的问题都需要谨慎考虑数据的大小和设置频率。比如,wxml中的dataset属性,最好也是不要绑定数据大的复杂对象;减少事件绑定......

做好防抖和节流

对于频繁触发的事件,要做好防抖或节流的控制,比如在onPageScroll中,每次滚动都会触发该事件,如果在里面直接进行setData,是一件恐怖的事。

用好wxs

wxs的执行效率比js高,所以data中的一些数据操作可以考虑在wxs中处理。




参考: