懶載入– 端對端分享功能 –微信釘釘

L6zt發表於2019-02-16

改造起點

github L6zt

  1. 專案前端與後端緊耦合,分享資訊 放在jsp裡,例如:
<script>
    window.$shareInformation = {
        title: `....`,
        desc: `.....`,
        link: `.....`,
    }
</script>

前端還要獲取全域性變數裡的值,初始化分享
2.分享功能沒有進行統一的合併,所以每個頁面都需要重寫分享邏輯,很low低效的很,而且不利於維護。
3.每次都需要 引入 釘釘 和 微信 分享的 sdk js 感覺浪費流量,而且 影響頁面載入速度, 這個兩個js都需要同時引入。

技術細節

對以上問題解決方案,我採用:
端對端懶載入 不同平臺的 sdk, 分享邏輯 統一由後端控制 即 頁面裡 只需要引入一個js指令碼。

總體引用方法

// data-ajax 放置獲取分享資訊 ajax的入參
//data-exclud 排除 不需要 那種分享
<script src="/js/share.js" data-ajax="{`a`: `1`, `b`: `2`}" data-exclud="[`wx`, `dd`]" data-slector="share"></script>

。。。。。。部分程式碼
公共函式

    var noop = function() {};
    var hasOwnP = function(obj, prop) {
        return Object.hasOwnProperty.call(obj, prop);
    };
    var toString = function(obj) {
        return Object.prototype.toString.call(obj);
    };
    var isHardVar = function(obj) {
        var result = toString(obj);
        return result === `[object Object]` || result === `[object Array]`;
    }
    var merge = function() {
        var lg = arguments.length;
        var result = {};
        if (lg < 2) {
            return arguments[0]
        };
        for (var i = 0; i < lg; i++) {
            for (var key in arguments[i]) {
                console.log(hasOwnP(arguments[i], key), key);
                if (hasOwnP(arguments[i], key)) {
                    var value = arguments[i][key];
                    result[key] = value;
                }
            }
        }
        return result;
    };

對非同步處理 自己模擬的promise實現

    var JcPromise = (function() {
        function JcPromise(fn) {
            fn = fn || noop;
            var statusList = [`start`, `pending`, `succeed`, `err`];
            var cbStatus = [0, 1];
            var status = statusList[0];
            var data = null;
            var err = null;
            var that = this;
            var successFn = [];
            var errFn = [];

            function resolve(d) {
                data = d;
                that._changeStatus(2);
            };

            function reject(e) {
                err = e;
                that._changeStatus(3);
            };
            this.getData = function() {
                return data;
            };
            this.getErr = function() {
                return err
            };
            this.getStatus = function() {
                return status
            };
            this._changeStatus = function(idx) {
                switch (status) {
                    case statusList[2]:
                    case statusList[3]:
                        {
                            return false
                        }
                };
                status = statusList[idx];
                if (status === statusList[3]) {
                    setTimeout(function() {
                        that._triggerCatch();
                    }, 0)
                }
                if (status === statusList[2]) {
                    setTimeout(function() {
                        that._triggerThen();
                    }, 0)
                }
            };
            this._pushThenCb = function(cb) {
                successFn.push({
                    status: cbStatus[0],
                    cb: cb
                });
                if (status === statusList[2]) {
                    this._triggerThen();
                }
            };
            this._pushCatchCb = function(cb) {
                errFn.push({
                    status: cbStatus[0],
                    cb: cb
                });
                if (status === statusList[3]) {
                    this._triggerCatch();
                }
            };
            this._triggerThen = function() {
                successFn.map(function(item) {
                    if (item.status === cbStatus[0]) {
                        item.cb(data);
                        item.status = cbStatus[1];
                    }
                })
            };
            this._triggerCatch = function() {
                errFn.map(function(item) {
                    if (item.status === cbStatus[0]) {
                        item.cb(err);
                        item.status = cbStatus[1];
                    }
                })
            };
            this._changeStatus(1);
            this.uuid = uuid++;
            try {
                fn(resolve, reject);
            } catch (e) {
                reject(e)
            }
            return this
        };
        JcPromise.fn = JcPromise.prototype;
        // 返回一個promise
        JcPromise.fn.then = function(cb) {
            var promiseR = null;
            var promiseJ = null;
            var result = null;
            var that = this;
            var fn = function() {
                setTimeout(function() {
                    try {
                        var data = that.getData();
                        result = cb(data);
                        if (typeof result === `object` && result !== null && result.constructor === JcPromise) {
                            result.then(function(data) {
                                promiseR(data)
                            }).catch(function(e) {
                                promiseJ(e)
                            })
                        } else {
                            promiseR(result)
                        }
                    } catch (e) {
                        promiseJ(e)
                    }
                }, 0);
            };
            this._pushThenCb(fn);
            // 觸發promise
            return new JcPromise(function(r, j) {
                promiseR = r;
                promiseJ = j;
            });
        };
        // 返回一個promise
        JcPromise.fn.catch = function(cb) {
            var promiseR = null;
            var promiseJ = null;
            var result = null;
            var that = this;
            var fn = function() {
                setTimeout(function() {
                    try {
                        var data = that.getErr();
                        result = cb(data);
                        if (typeof result === `object` && result !== null && result.constructor === JcPromise) {
                            result.then(function(data) {
                                promiseR(data)
                            }).catch(function(e) {
                                promiseJ(e)
                            })
                        } else {
                            promiseR(result)
                        }
                    } catch (e) {
                        promiseJ(e)
                    }
                }, 0)
            };
            this._pushCatchCb(fn);
            // 觸發promise
            return new JcPromise(function(r, j) {
                promiseR = r;
                promiseJ = j;
            });
        };
        return JcPromise
    })();

懶載入載入js方法

    function loadScript(src) {
        return new JcPromise(function(r, j) {
            var el = document.createElement(`script`);
            el.onload = function() {
                r(el);
            };
            el.onerror = function() {
                j();
            };
            el.src = src;
            document.body.appendChild(el);
        });
    };

判斷 是釘?還是微信

    function checkIsWx() {
        var userAgent = global.navigator.userAgent;
        var checkWx = /MicroMessenger/i;
        var splitWxVersion = /MicroMessenger/([d.]+)/i;
        var version = 0;
        var flag = false;
        if (checkWx.test(userAgent)) {
            flag = true;
            version = splitWxVersion.exec(userAgent)[1];
            version = parseFloat(version.replace(`.`, ``));
        }
        return {
            type: `wx`,
            flag: flag,
            version: version
        }
    };
    // 檢查是不是釘釘
    function checkIsDd() {
        var userAgent = global.navigator.userAgent;
        var checkDd = /DingTalk/i;
        var splitDdVersion = /DingTalk/([a-zA-Z0-9.-]+)/;
        var version = 0;
        var flag = false;
        if (checkDd.test(userAgent)) {
            flag = true;
            version = splitDdVersion.exec(userAgent)[1];
        }
        return {
            type: `dd`,
            flag: flag,
            version: version
        }
    };

// 自己照著 mdn 和其他文件 寫的 ajax

    var jcAjax = (function() {
        /*
            @data
            @type
            @url
            @setHeader
            @createXhr
      */
        function jcAjax(options) {
            var defaultHeaders = {
                `X-Requested-With`: `XMLHttpRequest`
            };
            var type = options.type,
                headers = options.headers,
                data = options.data,
                cb = options.cb,
                url = options.url,
                data = options.data;
            this.defaultHeaders = defaultHeaders;
            this.type = (type || `get`).toUpperCase();
            this.headers = options.headers || {};
            this.cb = cb || noop;
            this.url = url;
            this.data = data || {};
            this.init();
        };
        jcAjax.fn = jcAjax.prototype;
        jcAjax.fn.setRequestHeader = function() {
            var headers = this.headers;
            var defaultHeaders = this.defaultHeaders;
            var xhr = this.xhr;
            var type = this.type;
            this.headers = headers = merge(defaultHeaders, headers);
            type === `POST` && !headers[`Content-Type`] && (headers[`Content-Type`] = `application/x-www-form-urlencoded`);
            if (headers) {
                for (var key in headers) {
                    if (hasOwnP(headers, key)) {
                        xhr.setRequestHeader(key, headers[key]);
                    }
                }
            }
        };
        jcAjax.fn.createXhr = function() {
            this.xhr = new XMLHttpRequest();
        };
        jcAjax.fn.listenAjax = function() {
            var xhr = this.xhr;
            var cb = this.cb;
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    var data = JSON.parse(xhr.responseText);
                    cb(data)
                }
            };
        };
        jcAjax.fn.openHttp = function(type, url) {
            var xhr = this.xhr;
            xhr.open(type, url);
        };
        jcAjax.fn.sendData = function(data) {
            var xhr = this.xhr;
            var type = this.type;
            data = data || null;
            type === `GET` && (data === null)
            xhr.send(data);
        };
        jcAjax.fn.transSendData = function() {
            var result = null;
            var headers = this.headers;
            var contentType = headers[`Content-Type`];
            var data = this.data;
            switch (contentType) {
                case `multipart/form-data`:
                    {
                        result = new FormData();
                        for (var key in data) {
                            var value = data[key];
                            if (hasOwnP(data, key)) {
                                result.append(key, value)
                            }
                        }
                        return result;
                    }
                case `application/json`:
                    {
                        result = data;
                        return result
                    }
                default:
                    {
                        result = ``;
                        for (var key in data) {
                            var value = data[key];
                            if (hasOwnP(data, key)) {
                                isHardVar(value) && (value = JSON.stringify(value))
                                result = result + key + `=` + value + `&`
                            }
                        }
                        result = result.replace(/&$/, ``)
                        return result
                    }
            }
        };
        jcAjax.fn.get = function() {
            var data = this.data;
            var url = this.url;
            var mdUrl = ``;
            for (var key in data) {
                var value = data[key];
                if (hasOwnP(data, key)) {
                    mdUrl = mdUrl + encodeURIComponent(key) + `=` + encodeURIComponent(value) + `&`
                }
            }
            mdUrl.replace(/&$/, ``);
            this.createXhr();
            this.openHttp(`GET`, mdUrl);
            this.setRequestHeader();
            this.listenAjax();
            this.sendData();
        };
        jcAjax.fn.post = function() {
            var sendData = null;
            var url = this.url;
            console.log(url);
            this.createXhr();
            this.openHttp(`POST`, url);
            this.setRequestHeader();
            this.listenAjax();
            sendData = this.transSendData();
            this.sendData(sendData);
        };
        jcAjax.fn.init = function() {
            var type = this.type.toLowerCase();
            this[type]();
        }
        return function(options) {
            return new jcAjax(options);
        }
    })()

微信分享 釘?分享具體 api

   // 微信分享 由版本問題相容問題
    function wxShare(baseConfig, shareData, wxJsVersion) {
        function findWxSupportApi(wxJsVersion) {
            var jsApiList = null;
            var bindEvent = null;
            var api = null;
            switch (wxJsVersion) {
                case 1.3:
                    {
                        api = `onMenuShareTimeline|onMenuShareAppMessage|onMenuShareQQ|onMenuShareWeibo|onMenuShareQZone`;
                        jsApiList = api.split(`|`);
                        bindEvent = api.split(`|`);
                        break;

                    }
                case 1.4:
                    {
                        api = `updateAppMessageShareData|updateTimelineShareData|onMenuShareWeibo`;
                        jsApiList = api.split(`|`);
                        bindEvent = api.split(`|`);
                        break;
                    }
            };
            return {
                jsApiList: jsApiList,
                bindEvent: bindEvent
            }
        };
        var wxSupport = findWxSupportApi(wxJsVersion);
        var jsApiList = wxSupport.jsApiList;
        var bindEvent = wxSupport.bindEvent;
        var title = shareData.title;
        var desc = shareData.desc;
        var link = shareData.link;
        var imgUrl = shareData.imgUrl;
        wx.config({
            appId: baseConfig.appId,
            timestamp: baseConfig.timestamp,
            nonceStr: baseConfig.nonceStr,
            signature: baseConfig.signature,
            jsApiList: jsApiList
        });
        wx.ready(function() {
            bindEvent.forEach(function(key) {
                console.log(key);
                wx[key] && wx[key]({
                    title,
                    desc,
                    link,
                    imgUrl,
                    success() {
                        var fnKey = key + `TriggerSuccess`;
                        var cb = wxShareTriggerCb[fnKey];
                        cb && cb();
                    },
                    error() {
                        var fnKey = key + `TriggerError`;
                        var cb = wxShareTriggerCb[fnKey];
                        cb && cb();
                    }
                })
            })
        })
    };

    function ddShare(shareMsgData) {
        dd.ready(function() {
            var title = shareMsgData.title;
            var desc = shareMsgData.desc;
            var link = shareMsgData.link;
            var imgUrl = shareMsgData.imgUrl;
            dd.biz.navigation.setRight({  
                show: true,
                control: true,
                onSuccess: function(result) {
                    dd.biz.util.share({
                        url: link,
                        title: title,
                        content: desc,
                        image: imgUrl
                    });
                }
            })
        });
    };

初始化分享


    function initShare() {
        try {
            ajaxData = eval(`(` + getAttr(scriptElem, `data-ajax`) + `)`);
            excludeUa = eval(`(` + getAttr(scriptElem, `data-exclude`) + `)`) || [];
        } catch (e) {
            console.log(e)
        }
        if (isWx.flag && excludeUa.indexOf(`wx`) === -1) {
            var wxVersion = isWx.version;
            var wxUrl = null;
            var wxJsVersion = 0;
            if (wxVersion >= 672) {
                wxUrl = `//res.wx.qq.com/open/js/jweixin-1.4.0.js`;
                wxJsVersion = 1.4;
            } else {
                wxUrl = `//res.wx.qq.com/open/js/jweixin-1.3.0.js`;
                wxJsVersion = 1.3;
            }
            loadScript(wxUrl).then(function(data) {
                // 獲取 微信簽名 --- 不知本公司那個屌絲想         
                return loadScript(getWxShareSignatureApi)
            }).then(function(baseConfig) {
                getShareUserMsg(ajaxData).then(function(data) {
                    var shareFrom = data.shareH5Form;
                    var shareData = {
                        title: shareFrom.title,
                        link: shareFrom.link,
                        imgUrl: shareFrom.msgImg,
                        desc: shareFrom.desc
                    };
                    wxShare(global.weixinConfigInfo, shareData, wxJsVersion);
                    hadGetShareMsgHook(data);
                })
            })
        }
        if (isDd.flag && excludeUa.indexOf(`dd`) === -1) {
            loadScript(ddUrl).then(function(data) {
                 //獲取分享資訊
                getShareUserMsg(ajaxData).then(function(data) {
                    var shareFrom = data.shareH5Form;
                    var shareData = {
                        title: shareFrom.title,
                        link: shareFrom.link,
                        imgUrl: shareFrom.msgImg,
                        desc: shareFrom.desc
                    };
                    ddShare(shareData)
                    hadGetShareMsgHook(data)
                })
            })
        }
    }

相關文章