分类 JavaScript 下的文章

直接上js代码,如下:

function Push(options) {
    this.doNotConnect = 0;
    options = options || {};
    options.heartbeat  = options.heartbeat || 25000;
    options.pingTimeout = options.pingTimeout || 10000;
    this.config = options;
    this.uid = 0;
    this.channels = {};
    this.connection = null;
    this.pingTimeoutTimer = 0;
    Push.instances.push(this);
    this.createConnection();
  }
  
  Push.prototype.checkoutPing = function() {
    var _this = this;
    setTimeout(function () {
        if (_this.connection.state === 'connected') {
            _this.connection.send('{"event":"pusher:ping","data":{}}');
            if (_this.pingTimeoutTimer) {
                clearTimeout(_this.pingTimeoutTimer);
                _this.pingTimeoutTimer = 0;
            }
            _this.pingTimeoutTimer = setTimeout(function () {
                _this.connection.closeAndClean();
                if (!_this.connection.doNotConnect) {
                    _this.connection.waitReconnect();
                }
            }, _this.config.pingTimeout);
        }
    }, this.config.heartbeat);
  };
  
  Push.prototype.channel = function (name) {
    return this.channels.find(name);
  };
  Push.prototype.allChannels = function () {
    return this.channels.all();
  };
  Push.prototype.createConnection = function () {
    if (this.connection) {
        throw Error('Connection already exist');
    }
    var _this = this;
    var url = this.config.url;
    function updateSubscribed () {
        for (var i in _this.channels) {
            _this.channels[i].subscribed = false;
        }
    }
    this.connection = new Connection({
        url: url,
        app_key: this.config.app_key,
        onOpen: function () {
            _this.connection.state  = 'connecting';
            _this.checkoutPing();
        },
        onMessage: function(params) {
            if(_this.pingTimeoutTimer) {
                clearTimeout(_this.pingTimeoutTimer);
                _this.pingTimeoutTimer = 0;
            }
  
            params = JSON.parse(params.data);
            var event = params.event;
            var channel_name = params.channel;
  
            if (event === 'pusher:pong') {
                _this.checkoutPing();
                return;
            }
            if (event === 'pusher:error') {
                throw Error(params.data.message);
            }
            var data = JSON.parse(params.data), channel;
            if (event === 'pusher_internal:subscription_succeeded') {
                channel = _this.channels[channel_name];
                channel.subscribed = true;
                channel.processQueue();
                channel.emit('pusher:subscription_succeeded');
                return;
            }
            if (event === 'pusher:connection_established') {
                _this.connection.socket_id = data.socket_id;
                _this.connection.state = 'connected';
                _this.subscribeAll();
            }
            if (event.indexOf('pusher_internal') !== -1) {
                console.log("Event '"+event+"' not implement");
                return;
            }
            channel = _this.channels[channel_name];
            if (channel) {
                channel.emit(event, data);
            }
        },
        onClose: function () {
            updateSubscribed();
        },
        onError: function () {
            updateSubscribed();
        }
    });
  };
  Push.prototype.disconnect = function () {
    this.connection.doNotConnect = 1;
    this.connection.close();
  };
  
  Push.prototype.subscribeAll = function () {
    if (this.connection.state !== 'connected') {
        return;
    }
    for (var channel_name in this.channels) {
        //this.connection.send(JSON.stringify({event:"pusher:subscribe", data:{channel:channel_name}}));
        this.channels[channel_name].processSubscribe();
    }
  };
  
  Push.prototype.unsubscribe = function (channel_name) {
    if (this.channels[channel_name]) {
        delete this.channels[channel_name];
        if (this.connection.state === 'connected') {
            this.connection.send(JSON.stringify({event:"pusher:unsubscribe", data:{channel:channel_name}}));
        }
    }
  };
  Push.prototype.unsubscribeAll = function () {
    var channels = Object.keys(this.channels);
    if (channels.length) {
        if (this.connection.state === 'connected') {
            for (var channel_name in this.channels) {
                this.unsubscribe(channel_name);
            }
        }
    }
    this.channels = {};
  };
  Push.prototype.subscribe = function (channel_name) {
    if (this.channels[channel_name]) {
        return this.channels[channel_name];
    }
    if (channel_name.indexOf('private-') === 0) {
        return createPrivateChannel(channel_name, this);
    }
    if (channel_name.indexOf('presence-') === 0) {
        return createPresenceChannel(channel_name, this);
    }
    return createChannel(channel_name, this);
  };
  Push.instances = [];
  
  function createChannel(channel_name, push)
  {
    var channel = new Channel(push.connection, channel_name);
    push.channels[channel_name] = channel;
    channel.subscribeCb = function () {
        push.connection.send(JSON.stringify({event:"pusher:subscribe", data:{channel:channel_name}}));
    }
    return channel;
  }
  
  function createPrivateChannel(channel_name, push)
  {
    var channel = new Channel(push.connection, channel_name);
    push.channels[channel_name] = channel;
    channel.subscribeCb = function () {
        __ajax({
            url: push.config.auth,
            type: 'POST',
            data: {channel_name: channel_name, socket_id: push.connection.socket_id},
            success: function (data) {
                data = JSON.parse(data);
                data.channel = channel_name;
                push.connection.send(JSON.stringify({event:"pusher:subscribe", data:data}));
            },
            error: function (e) {
                throw Error(e);
            }
        });
    };
    channel.processSubscribe();
    return channel;
  }
  
  function createPresenceChannel(channel_name, push)
  {
    return createPrivateChannel(channel_name, push);
  }
  
  /*window.addEventListener('online',  function(){
    var con;
    for (var i in Push.instances) {
        con = Push.instances[i].connection;
        con.reconnectInterval = 1;
        if (con.state === 'connecting') {
            con.connect();
        }
    }
  });*/
  
  
  function Connection(options) {
    this.dispatcher = new Dispatcher();
    __extends(this, this.dispatcher);
    var properies = ['on', 'off', 'emit'];
    for (var i in properies) {
        this[properies[i]] = this.dispatcher[properies[i]];
    }
    this.options = options;
    this.state = 'initialized'; //initialized connecting connected disconnected
    this.doNotConnect = 0;
    this.reconnectInterval = 1;
    this.connection = null;
    this.reconnectTimer = 0;
    this.connect();
  }
  
  Connection.prototype.updateNetworkState = function(state){
    var old_state = this.state;
    this.state = state;
    if (old_state !== state) {
        this.emit('state_change', { previous: old_state, current: state });
    }
  };
  
  Connection.prototype.connect = function () {
    this.doNotConnect = 0;
    if (this.networkState == 'connecting' || this.networkState == 'established') {
        console.log('networkState is ' + this.networkState + ' and do not need connect');
        return;
    }
    if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = 0;
    }
  
    this.closeAndClean();
  
    var options = this.options;
    var _this = this;
    _this.updateNetworkState('connecting');
    var cb = function(){
         wx.onSocketOpen(function (res) {
            _this.reconnectInterval = 1;
            if (_this.doNotConnect) {
                _this.updateNetworkState('closing');
                wx.closeSocket();
                return;
            }
            _this.updateNetworkState('established');
            if (options.onOpen) {
                options.onOpen(res);
            }
        });
  
        if (options.onMessage) {
            wx.onSocketMessage(options.onMessage);
        }
  
        wx.onSocketClose(function (res) {
            _this.updateNetworkState('disconnected');
            if (!_this.doNotConnect) {
                _this.waitReconnect();
            }
            if (options.onClose) {
                options.onClose(res);
            }
        });
  
        wx.onSocketError(function (res) {
            _this.close();
            if (!_this.doNotConnect) {
                _this.waitReconnect();
            }
            if (options.onError) {
                options.onError(res);
            }
        });
    };
    wx.connectSocket({
        url: options.url,
        fail: function (res) {
            console.log('wx.connectSocket fail');
            console.log(res);
            _this.updateNetworkState('disconnected');
            _this.waitReconnect();
        },
        success: function() {
  
        }
    });
    cb();
  }
  
  Connection.prototype.connect = function () {
    this.doNotConnect = 0;
    if (this.state === 'connected') {
        console.log('networkState is "' + this.state + '" and do not need connect');
        return;
    }
    if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = 0;
    }
  
    this.closeAndClean();
  
    var options = this.options;
  
    this.updateNetworkState('connecting');
  
    var _this = this;
    var cb = function(){
         wx.onSocketOpen(function (res) {
            _this.reconnectInterval = 1;
            if (_this.doNotConnect) {
                _this.updateNetworkState('disconnected');
                wx.closeSocket();
                return;
            }
            if (options.onOpen) {
                options.onOpen(res);
            }
        });
  
        if (options.onMessage) {
            wx.onSocketMessage(options.onMessage);
        }
  
        wx.onSocketClose(function (res) {
            _this.updateNetworkState('disconnected');
            if (!_this.doNotConnect) {
                _this.waitReconnect();
            }
            if (options.onClose) {
                options.onClose(res);
            }
        });
  
        wx.onSocketError(function (res) {
            _this.close();
            if (!_this.doNotConnect) {
                _this.waitReconnect();
            }
            if (options.onError) {
                options.onError(res);
            }
        });
    };
    wx.connectSocket({
        url: options.url+'/app/'+options.app_key,
        fail: function (res) {
            console.log('wx.connectSocket fail');
            console.log(res);
            _this.updateNetworkState('disconnected');
            _this.waitReconnect();
        },
        success: function() {
  
        }
    });
    cb();
  }
  
  Connection.prototype.closeAndClean = function () {
    if (this.state === 'connected') {
        wx.closeSocket();
    }
    this.updateNetworkState('disconnected');
  };
  
  Connection.prototype.waitReconnect = function () {
    if (this.state === 'connected' || this.state === 'connecting') {
        return;
    }
    if (!this.doNotConnect) {
        this.updateNetworkState('connecting');
        var _this = this;
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
        }
        this.reconnectTimer = setTimeout(function(){
            _this.connect();
        }, this.reconnectInterval);
        if (this.reconnectInterval < 1000) {
            this.reconnectInterval = 1000;
        } else {
            // 每次重连间隔增大一倍
            this.reconnectInterval = this.reconnectInterval * 2;
        }

        let online = false;
        wx.getNetworkType({
            success (res) {
              console.log(res)
              if(res.networkType !== 'none'){
                online = true;
              }
            }
        })
        // 有网络的状态下,重连间隔最大2秒
        if (this.reconnectInterval > 2000 && online) {
            _this.reconnectInterval = 2000;
        }
    }
  }
  
  Connection.prototype.send = function(data) {
    if (this.state !== 'connected') {
        console.trace('networkState is "' + this.state + '", can not send ' + data);
        return;
    }
    wx.sendSocketMessage({
        data: data
    });
  }
  
  Connection.prototype.close = function(){
    this.updateNetworkState('disconnected');
    wx.closeSocket();
  }
  
  var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) {d[p] = b[p];}
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  };
  
  function Channel(connection, channel_name) {
    this.subscribed = false;
    this.dispatcher = new Dispatcher();
    this.connection = connection;
    this.channelName = channel_name;
    this.subscribeCb = null;
    this.queue = [];
    __extends(this, this.dispatcher);
    var properies = ['on', 'off', 'emit'];
    for (var i in properies) {
        this[properies[i]] = this.dispatcher[properies[i]];
    }
  }
  
  Channel.prototype.processSubscribe = function () {
    if (this.connection.state !== 'connected') {
        return;
    }
    this.subscribeCb();
  };
  
  Channel.prototype.processQueue = function () {
    if (this.connection.state !== 'connected' || !this.subscribed) {
        return;
    }
    for (var i in this.queue) {
        this.queue[i]();
    }
    this.queue = [];
  };
  
  Channel.prototype.trigger = function (event, data) {
    if (event.indexOf('client-') !== 0) {
        throw new Error("Event '" + event + "' should start with 'client-'");
    }
    var _this = this;
    this.queue.push(function () {
        _this.connection.send(JSON.stringify({ event: event, data: data, channel: _this.channelName }));
    });
    this.processQueue();
  };
  
  ////////////////
  var Collections = (function () {
    var exports = {};
    function extend(target) {
        var sources = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            sources[_i - 1] = arguments[_i];
        }
        for (var i = 0; i < sources.length; i++) {
            var extensions = sources[i];
            for (var property in extensions) {
                if (extensions[property] && extensions[property].constructor &&
                    extensions[property].constructor === Object) {
                    target[property] = extend(target[property] || {}, extensions[property]);
                }
                else {
                    target[property] = extensions[property];
                }
            }
        }
        return target;
    }
  
    exports.extend = extend;
    function stringify() {
        var m = ["Push"];
        for (var i = 0; i < arguments.length; i++) {
            if (typeof arguments[i] === "string") {
                m.push(arguments[i]);
            }
            else {
                m.push(safeJSONStringify(arguments[i]));
            }
        }
        return m.join(" : ");
    }
  
    exports.stringify = stringify;
    function arrayIndexOf(array, item) {
        var nativeIndexOf = Array.prototype.indexOf;
        if (array === null) {
            return -1;
        }
        if (nativeIndexOf && array.indexOf === nativeIndexOf) {
            return array.indexOf(item);
        }
        for (var i = 0, l = array.length; i < l; i++) {
            if (array[i] === item) {
                return i;
            }
        }
        return -1;
    }
  
    exports.arrayIndexOf = arrayIndexOf;
    function objectApply(object, f) {
        for (var key in object) {
            if (Object.prototype.hasOwnProperty.call(object, key)) {
                f(object[key], key, object);
            }
        }
    }
  
    exports.objectApply = objectApply;
    function keys(object) {
        var keys = [];
        objectApply(object, function (_, key) {
            keys.push(key);
        });
        return keys;
    }
  
    exports.keys = keys;
    function values(object) {
        var values = [];
        objectApply(object, function (value) {
            values.push(value);
        });
        return values;
    }
  
    exports.values = values;
    function apply(array, f, context) {
        for (var i = 0; i < array.length; i++) {
            f.call(context || (window), array[i], i, array);
        }
    }
  
    exports.apply = apply;
    function map(array, f) {
        var result = [];
        for (var i = 0; i < array.length; i++) {
            result.push(f(array[i], i, array, result));
        }
        return result;
    }
  
    exports.map = map;
    function mapObject(object, f) {
        var result = {};
        objectApply(object, function (value, key) {
            result[key] = f(value);
        });
        return result;
    }
  
    exports.mapObject = mapObject;
    function filter(array, test) {
        test = test || function (value) {
            return !!value;
        };
        var result = [];
        for (var i = 0; i < array.length; i++) {
            if (test(array[i], i, array, result)) {
                result.push(array[i]);
            }
        }
        return result;
    }
  
    exports.filter = filter;
    function filterObject(object, test) {
        var result = {};
        objectApply(object, function (value, key) {
            if ((test && test(value, key, object, result)) || Boolean(value)) {
                result[key] = value;
            }
        });
        return result;
    }
  
    exports.filterObject = filterObject;
    function flatten(object) {
        var result = [];
        objectApply(object, function (value, key) {
            result.push([key, value]);
        });
        return result;
    }
  
    exports.flatten = flatten;
    function any(array, test) {
        for (var i = 0; i < array.length; i++) {
            if (test(array[i], i, array)) {
                return true;
            }
        }
        return false;
    }
  
    exports.any = any;
    function all(array, test) {
        for (var i = 0; i < array.length; i++) {
            if (!test(array[i], i, array)) {
                return false;
            }
        }
        return true;
    }
  
    exports.all = all;
    function encodeParamsObject(data) {
        return mapObject(data, function (value) {
            if (typeof value === "object") {
                value = safeJSONStringify(value);
            }
            return encodeURIComponent(base64_1["default"](value.toString()));
        });
    }
  
    exports.encodeParamsObject = encodeParamsObject;
    function buildQueryString(data) {
        var params = filterObject(data, function (value) {
            return value !== undefined;
        });
        return map(flatten(encodeParamsObject(params)), util_1["default"].method("join", "=")).join("&");
    }
  
    exports.buildQueryString = buildQueryString;
    function decycleObject(object) {
        var objects = [], paths = [];
        return (function derez(value, path) {
            var i, name, nu;
            switch (typeof value) {
                case 'object':
                    if (!value) {
                        return null;
                    }
                    for (i = 0; i < objects.length; i += 1) {
                        if (objects[i] === value) {
                            return {$ref: paths[i]};
                        }
                    }
                    objects.push(value);
                    paths.push(path);
                    if (Object.prototype.toString.apply(value) === '[object Array]') {
                        nu = [];
                        for (i = 0; i < value.length; i += 1) {
                            nu[i] = derez(value[i], path + '[' + i + ']');
                        }
                    }
                    else {
                        nu = {};
                        for (name in value) {
                            if (Object.prototype.hasOwnProperty.call(value, name)) {
                                nu[name] = derez(value[name], path + '[' + JSON.stringify(name) + ']');
                            }
                        }
                    }
                    return nu;
                case 'number':
                case 'string':
                case 'boolean':
                    return value;
            }
        }(object, '$'));
    }
  
    exports.decycleObject = decycleObject;
    function safeJSONStringify(source) {
        try {
            return JSON.stringify(source);
        }
        catch (e) {
            return JSON.stringify(decycleObject(source));
        }
    }
  
    exports.safeJSONStringify = safeJSONStringify;
    return exports;
  })();
  
  var Dispatcher = (function () {
    function Dispatcher(failThrough) {
        this.callbacks = new CallbackRegistry();
        this.global_callbacks = [];
        this.failThrough = failThrough;
    }
    Dispatcher.prototype.on = function (eventName, callback, context) {
        this.callbacks.add(eventName, callback, context);
        return this;
    };
    Dispatcher.prototype.on_global = function (callback) {
        this.global_callbacks.push(callback);
        return this;
    };
    Dispatcher.prototype.off = function (eventName, callback, context) {
        this.callbacks.remove(eventName, callback, context);
        return this;
    };
    Dispatcher.prototype.emit = function (eventName, data) {
        var i;
        for (i = 0; i < this.global_callbacks.length; i++) {
            this.global_callbacks[i](eventName, data);
        }
        var callbacks = this.callbacks.get(eventName);
        if (callbacks && callbacks.length > 0) {
            for (i = 0; i < callbacks.length; i++) {
                callbacks[i].fn.call(callbacks[i].context || (window), data);
            }
        }
        else if (this.failThrough) {
            this.failThrough(eventName, data);
        }
        return this;
    };
    return Dispatcher;
  }());
  
  var CallbackRegistry = (function () {
    function CallbackRegistry() {
        this._callbacks = {};
    }
    CallbackRegistry.prototype.get = function (name) {
        return this._callbacks[prefix(name)];
    };
    CallbackRegistry.prototype.add = function (name, callback, context) {
        var prefixedEventName = prefix(name);
        this._callbacks[prefixedEventName] = this._callbacks[prefixedEventName] || [];
        this._callbacks[prefixedEventName].push({
            fn: callback,
            context: context
        });
    };
    CallbackRegistry.prototype.remove = function (name, callback, context) {
        if (!name && !callback && !context) {
            this._callbacks = {};
            return;
        }
        var names = name ? [prefix(name)] : Collections.keys(this._callbacks);
        if (callback || context) {
            this.removeCallback(names, callback, context);
        }
        else {
            this.removeAllCallbacks(names);
        }
    };
    CallbackRegistry.prototype.removeCallback = function (names, callback, context) {
        Collections.apply(names, function (name) {
            this._callbacks[name] = Collections.filter(this._callbacks[name] || [], function (oning) {
                return (callback && callback !== oning.fn) ||
                    (context && context !== oning.context);
            });
            if (this._callbacks[name].length === 0) {
                delete this._callbacks[name];
            }
        }, this);
    };
    CallbackRegistry.prototype.removeAllCallbacks = function (names) {
        Collections.apply(names, function (name) {
            delete this._callbacks[name];
        }, this);
    };
    return CallbackRegistry;
  }());
  function prefix(name) {
    return "_" + name;
  }
  
  function __ajax(options){
    options=options||{};
    options.type=(options.type||'GET').toUpperCase();
    options.dataType=options.dataType||'json';
    params=formatParams(options.data);
  
    var xhr;
    if(window.XMLHttpRequest){
        xhr=new XMLHttpRequest();
    }else{
        xhr=ActiveXObject('Microsoft.XMLHTTP');
    }
  
    xhr.onreadystatechange=function(){
        if(xhr.readyState === 4){
            var status=xhr.status;
            if(status>=200 && status<300){
                options.success&&options.success(xhr.responseText,xhr.responseXML);
            }else{
                options.error&&options.error(status);
            }
        }
    }
  
    if(options.type==='GET'){
        xhr.open('GET',options.url+'?'+params,true);
        xhr.send(null);
    }else if(options.type==='POST'){
        xhr.open('POST',options.url,true);
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(params);
    }
  }
  
  function formatParams(data){
    var arr=[];
    for(var name in data){
        arr.push(encodeURIComponent(name)+'='+encodeURIComponent(data[name]));
    }
    return arr.join('&');
  }
  
  export default Push

感谢:
https://www.workerman.net/plugin/2
https://github.com/ljnchn/push/blob/main/src/push-wx.js

PC前端JS实现

<script>
    layui.use(['layer'], function () {
        var $ = layui.jquery;
        var layer = layui.layer;

        //长轮询实现
        var updateTimer = setInterval(() => {
            var videoId = window.btoa('<?=$video->id;?>');
            $.post('<?=route("member.xxx.time");?>', {video_id: videoId}, function (res) {
                if (res.code === 2023) {
                    clearInterval(updateTimer);//出现想要的结果,停止重复提交
                    layer.msg(res.msg, {icon: 1, time: 3000}, function () {
                        parent.location.reload();
                    });
                } else {
                    clearInterval(updateTimer);//出现想要的结果,停止重复提交
                    layer.msg(res.msg, {icon: 2, time: 3000}, function () {
                        parent.location.reload();
                    });
                }
            }, 'json');
        }, 1000 * 60) //每分钟请求一次
    });
</script>

后端PHP实现控制

....
//让前端停止再请求
if ($longTime == $learnedTime) {
    throw new \Exception('恭喜您,通过实践',2023);
}




$(document).ready(function () {
    var options = {
    };
    var player = videojs('demo-video', options, function onPlayerReady() {
        var time1;
        var t1 = 0;
        function aa() {
            t1 += 0.25;
            document.getElementById('aa').value = t1;
            console.log('aa-' + t1);
        }
        //开始播放视频时
        this.on('play', function () {
            console.log('开始播放'); 
        });
        //结束和暂时时清除定时器,并向后台发送数据
        this.on('ended', function () {
            console.log('结束播放');
            window.clearInterval(time1);
            countTime();   //向后台发数据
        });
        this.on('pause', function () {
            console.log('暂停播放');
            window.clearInterval(time1);
            countTime();  //向后台发数据
        });
        //被主动暂停或网络暂停缓冲后重新播放会触发该事件
        //开始播放视频时,设置一个定时器,每250毫秒调用一次aa(),观看时长加2.5秒,
        //定时器需要放在playing事件中,否则无法实现网络缓冲后继续计时的功能
        this.on("playing",function(){
            console.log('开始播放');
            time1 = setInterval(function () {
                aa();
            }, 250)
        }),
        //当因网络原因导致暂停时会触发该事件
        this.on("waiting",function(){
            window.clearInterval(time1);
                    countTime();   //向后台发数据
             console.log('waiting');
        })
    });
    //直接关闭页面,并向后台发送数据
    if(window.addEventListener){
        window.addEventListener("beforeunload",countTime,false);
    }else{
        window.attachEvent("onbeforeunload",countTime);
    }
    
    function countTime(){
        console.log('countTime',document.getElementById('aa').value);
    }
})

源码下载:video.zip

转载:https://blog.csdn.net/king2wang/article/details/83001087

HTML

<form class="layui-form" method="">
<input type="hidden" name="venue_info_id" value="1">
<div class="layui-row" style="padding-top: 40px">
    <div class="layui-form-item text-right">
       <button class="layui-btn" type="button" lay-filter="doCreateEvent" lay-submit>
          <i class="layui-icon">&#xe605;</i>提交保存
       </button>
     </div>
</div>
</form>


JS

form.on('submit(doCreateEvent)', function(data){
    layer.confirm('确定添加预约?', {
        skin: 'layui-layer-admin',
        shade: .1
    }, function (i) {
        layer.close(i);
        var loadIndex = layer.load(2);
        $.post("/api/xxx", data.field, function (res) {
            layer.close(loadIndex);
            if (200 === res.code) {
                layer.msg(res.msg, {icon: 1});
                setTimeout(function (){
                    parent.location.reload();
                }, 2500)
            } else {
                layer.msg(res.msg, {icon: 2});
            }
        });
    });
});



WXS

所有监听事件先在onload监听。
// pages/index/to_news/to_news.js  
var app = getApp();
var socketOpen = false;
var SocketTask = false;
var url = 'ws://192.168.0.120:7011';
Page({
  data: {
    inputValue: '',
    returnValue: '',
  },
  onLoad: function (options) {
  },
  onReady: function () {
    // 创建Socket
    SocketTask = wx.connectSocket({
      url: url,
      data: 'data',
      header: {
        'content-type': 'application/json'
      },
      method: 'post',
      success: function (res) {
        console.log('WebSocket连接创建', res)
      },
      fail: function (err) {
        wx.showToast({
          title: '网络异常!',
        })
        console.log(err)
      },
    })
    if (SocketTask) {
      SocketTask.onOpen(res => {
        console.log('监听 WebSocket 连接打开事件。', res)
      })
      SocketTask.onClose(onClose => {
        console.log('监听 WebSocket 连接关闭事件。', onClose)
      })
      SocketTask.onError(onError => {
        console.log('监听 WebSocket 错误。错误信息', onError)
      })
      SocketTask.onMessage(onMessage => {
        console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', onMessage)
      })
    }
  },

  // 提交文字
  submitTo: function (e) {
    let that = this;
    that.data.allContentList.push({that.data.inputValue });
    that.setData({
      allContentList: that.data.allContentList
    })
    var data = {
      text: that.data.inputValue
    }
    if (socketOpen) {
      // 如果打开了socket就发送数据给服务器
      sendSocketMessage(data)
    }
  },
  bindKeyInput: function (e) {
    this.setData({
      inputValue: e.detail.value
    })
  },

  onHide: function () {
      SocketTask.close(function (close) {
        console.log('关闭 WebSocket 连接。', close)
      })
  },
})

//通过 WebSocket 连接发送数据,需要先 wx.connectSocket,并在 wx.onSocketOpen 回调之后才能发送。
function sendSocketMessage(data) {
  console.log('通过 WebSocket 连接发送数据')
  if (socketOpen) {
    SocketTask.send({data: JSON.stringify(data)
    }, function (res) {
      console.log('已发送', res)
    })
  } else {
    socketMsgQueue.push(msg)
  }
} 

WXML

 <input type="text" bindinput="bindKeyInput" value='{
   {inputValue}}' placeholder="" />
  <button bindtap="submitTo" class='user_input_text'>发送</button>

app.js

在app.js下添加三个函数openSocket(), closeSocket(),sendMessage(),在app初始化的onLunch函数里面调用openSocket(),这样子用户一进入小程序就会自动连接websocket
App({
      globalData: {
        socketStatus: 'closed',
      },
    onLaunch: function() {   
        var that = this;
        if (that.globalData.socketStatus === 'closed') {
              that.openSocket();
        }
    }
    openSocket() {
       //打开时的动作
        wx.onSocketOpen(() => {
          console.log('WebSocket 已连接')
          this.globalData.socketStatus = 'connected';
          this.sendMessage();
        })
        //断开时的动作
        wx.onSocketClose(() => {
          console.log('WebSocket 已断开')
          this.globalData.socketStatus = 'closed'
        })
        //报错时的动作
        wx.onSocketError(error => {
          console.error('socket error:', error)
        })
        // 监听服务器推送的消息
        wx.onSocketMessage(message => {
          //把JSONStr转为JSON
          message = message.data.replace(" ", "");
          if (typeof message != 'object') {
            message = message.replace(/\ufeff/g, ""); //重点
            var jj = JSON.parse(message);
            message = jj;
          }
          console.log("【websocket监听到消息】内容如下:");
          console.log(message);
        })
        // 打开信道
        wx.connectSocket({
          url: "ws://" + "localhost" + ":8888",
        })
      },
        
    //关闭信道
      closeSocket() {
        if (this.globalData.socketStatus === 'connected') {
          wx.closeSocket({
            success: () => {
              this.globalData.socketStatus = 'closed'
            }
          })
        }
      },
        
     //发送消息函数
      sendMessage() {
        if (this.globalData.socketStatus === 'connected') {
        //自定义的发给后台识别的参数 ,我这里发送的是name
          wx.sendSocketMessage({
            data: "{\"name\":\"" + wx.getStorageSync('openid') + "\"}"  
          })
        }
      },
})



websocket API

wx.connectSocket:创建一个 WebSocket 连接 ;
sotk.onOpen:监听 WebSocket 连接打开事件 ;
sotk.onClose:监听 WebSocket 连接关闭事件 ;
sotk.onError:监听 WebSocket 错误事件 ;
sotk.onMessage:监听
WebSocket 接受到服务器的消息事件 ;(重要:负责监听接收数据)
sotk.send:通过
WebSocket 连接发送数据 。(重要:负责发送数据)
var sotk = null;
var socketOpen = false;
var wsbasePath = "ws://开发者服务器 wss 接口地址/";

//开始webSocket
  webSocketStart(e){
    sotk = wx.connectSocket({
      url: wsbasePath,
      success: res => {
        console.log('小程序连接成功:', res);
      },
      fail: err => {
        console.log('出现错误啦!!' + err);
        wx.showToast({
          title: '网络异常!',
        })
      }
    })

    this.webSokcketMethods();

  },

//监听指令
  webSokcketMethods(e){
    let that = this;
    sotk.onOpen(res => {
      socketOpen = true;
      console.log('监听 WebSocket 连接打开事件。', res);
    })
    sotk.onClose(onClose => {
      console.log('监听 WebSocket 连接关闭事件。', onClose)
      socketOpen = false;
    })
    sotk.onError(onError => {
      console.log('监听 WebSocket 错误。错误信息', onError)
      socketOpen = false
    })

    sotk.onMessage(onMessage => {
      var data = JSON.parse(onMessage.data);
      console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息',data);
     
    })
   
  },

//发送消息
  sendSocketMessage(msg) {
    let that = this;
    if (socketOpen){
      console.log('通过 WebSocket 连接发送数据', JSON.stringify(msg))
      sotk.send({
        data: JSON.stringify(msg)
      }, function (res) {
        console.log('已发送', res)
      })
    }
    
  },
 //关闭连接
  closeWebsocket(str){
    if (socketOpen) {
      sotk.close(
        {
          code: "1000",
          reason: str,
          success: function () {
            console.log("成功关闭websocket连接");
          }
        }
      )
    }
  },

实现逻辑

1、通过onMessage监听收到的信息,接收到的信息会返回一个type值,通过type值判断接收到的数据是什么数据,之后分别进行处理、赋值即可;
2、通过sendSocketMessage发送数据,根据后端定义的字段发送请求,字段中也包含type值,告诉websocket是处理什么类型的值。
webSokcketMethods(e) {
    this.websocket.onMessage(onMessage => {
      console.log(this.data)
      var data = JSON.parse(onMessage.data);
      console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', data);
      if (data.client_id) {
        this.setData({
          client_id: data.client_id
        })
      }
      if (data.msg_type === 'init') {
        this.joinLive()
      }
      if (data.msg_type === 'join') {
        wx.hideLoading()
        this.endtrain(data.msg_content)
        this.setData({
          is_join: true,
          lookNumber: data.online_num
        })
        if (data.message_list) {
          this.setData({
            commentList: data.message_list,
            toLast: 'item' + data.message_list.length,
            lookNumber: data.online_num,
            followNumber: data.subscribe_num
          })
        }

      }
      if (data.msg_type == 'message') {
        var commentItem = {
          name: data.from_name,
          content: data.content
        }
        var commentArr = this.data.commentList
        console.log(commentArr)
        commentArr.push(commentItem)
        this.setData({
          commentList: commentArr,
          toLast: 'item' + commentArr.length,
        })

        if (this.data.platform === 'android') {
          this.messageScroll()
        }
      }
      if (data.msg_type == 'subscribe') {
        this.setData({
          followNumber: data.subscribe_num
        })
        this.endtrain(data.msg_content)

      }

      if (data.msg_type == 'leave') {
        this.endtrain(data.msg_content)
      }

      if (data.msg_type == 'buy') {
        this.endtrain(data.msg_content)
      }
    })
  },
   // 加入直播间
  joinLive() {
    var data = {
      msg_type: 'join',
      room_id: this.data.liveRoomId,
      live_id: this.data.liveId,
      from_uid: this.data.anchorUid,
      client_id: this.data.client_id,
      is_anchor: true,
      from_name: this.data.anchorName
    }
    console.log('通过 WebSocket 连接发送数据', JSON.stringify(data))
    this.websocket.send({
      data: JSON.stringify(data),
      success: function (res) {
        console.log(res)
      },
      fail: function (err) {
        console.log(err)
      }
    })
  },
  // 商品上架
  goodsShelfOn(goodsId, goodsName, goodsPrice, defaultImage, status) {
    var data = {
      msg_type: 'on_off_shelf',
      room_id: this.data.liveRoomId,
      live_id: this.data.liveId,
      from_uid: this.data.anchorUid,
      client_id: this.data.client_id,
      from_name: this.data.anchorName,
      goods_id: goodsId,
      goods_name: goodsName,
      goods_price: goodsPrice,
      default_image: defaultImage,
      status: 'on'
    }
    console.log('通过 WebSocket 连接发送数据', JSON.stringify(data))
    this.websocket.send({
      data: JSON.stringify(data),
      success: function (res) {
        console.log(res)
      },
      fail: function (err) {
        console.log(err)
      }
    })
  }

心跳检测、断线重连

在实际应用中,需要不断判断websocket处于连接阶段。
wechat-websocket.js文件
export default class websocket {
  constructor({ heartCheck, isReconnection }) {
    // 是否连接
    this._isLogin = false;
    // 当前网络状态
    this._netWork = true;
    // 是否人为退出
    this._isClosed = false;
    // 心跳检测频率
    this._timeout = 3000;
    this._timeoutObj = null;
    // 当前重连次数
    this._connectNum = 0;
    // 心跳检测和断线重连开关,true为启用,false为关闭
    this._heartCheck = heartCheck;
    this._isReconnection = isReconnection;
    this._onSocketOpened();
  }
  // 心跳重置
  _reset() {
    clearTimeout(this._timeoutObj);
    return this;
  }
  // 心跳开始
  _start() {
    let _this = this;
    this._timeoutObj = setInterval(() => {
      wx.sendSocketMessage({
        // 心跳发送的信息应由前后端商量后决定
        data: JSON.stringify({
          "msg_type": 'ping'
        }),
        success(res) {
          console.log(res)
          console.log("发送心跳成功");
        },
        fail(err) {
          console.log(err)
          // wx.showToast({
          //   title: "websocket已断开",
          //   icon: 'none'
          // })
          _this._reset()
        }
      });
    }, this._timeout);
  }
  // 监听websocket连接关闭
  onSocketClosed(options) {
    wx.onSocketClose(err => {
      console.log('当前websocket连接已关闭,错误信息为:' + JSON.stringify(err));
      // 停止心跳连接
      if (this._heartCheck) {
        this._reset();
      }
      // 关闭已登录开关
      this._isLogin = false;
      // 检测是否是用户自己退出小程序
      console.log(this._isClosed)
      if (!this._isClosed) {
        // 进行重连
        if (this._isReconnection) {
          this._reConnect(options)
        }
      }
      
    })
  }
  // 检测网络变化
  onNetworkChange(options) {
    wx.onNetworkStatusChange(res => {
      console.log('当前网络状态:' + res.isConnected);
      if (!this._netWork) {
        this._isLogin = false;
        // 进行重连
        if (this._isReconnection) {
          this._reConnect(options)
        }
      }
    })
  }
  _onSocketOpened() {
    wx.onSocketOpen(res => {
      console.log('websocket已打开');
      // 打开已登录开关
      this._isLogin = true;
      // 发送心跳
      if (this._heartCheck) {
        this._reset()._start();
      }
      // 发送登录信息
      wx.sendSocketMessage({
        // 这里是第一次建立连接所发送的信息,应由前后端商量后决定
        data: JSON.stringify({
          "key": 'value'
        })
      })
      // 打开网络开关
      this._netWork = true;
    })
  }
  // 接收服务器返回的消息
  onReceivedMsg(callBack) {
    wx.onSocketMessage(msg => {
      if (typeof callBack == "function") {
        callBack(msg)
      } else {
        console.log('参数的类型必须为函数')
      }
    })
  }
 
  // 建立websocket连接
  initWebSocket(options) {
    let _this = this;
    if (this._isLogin) {
      console.log("您已经登录了");
    } else {
      console.log('未登录')
      // 检查网络
      wx.getNetworkType({
        success(result) {
          if (result.networkType != 'none') {
            console.log(result.networkType)
            // 开始建立连接
            wx.connectSocket({
              url: options.url,
              success(res) {
                if (typeof options.success == "function") {
                  options.success(res)
                } else {
                  console.log('参数的类型必须为函数')
                }
              },
              fail(err) {
                if (typeof options.fail == "function") {
                  options.fail(err)
                } else {
                  console.log('参数的类型必须为函数')
                }
              }
            })
          } else {
            console.log('网络已断开');
            _this._netWork = false;
            // 网络断开后显示model
            wx.showModal({
              title: '网络错误',
              content: '请重新打开网络',
              showCancel: false,
              success: function (res) {
                if (res.confirm) {
                  console.log('用户点击确定')
                }
              }
            })
          }
        }
      })
    }
  }
  // 发送websocket消息
  sendWebSocketMsg(options) {
    wx.sendSocketMessage({
      data: options.data,
      success(res) {
        if (typeof options.success == "function") {
          options.success(res)
        } else {
          console.log('参数的类型必须为函数')
        }
      },
      fail(err) {
        if (typeof options.fail == "function") {
          options.fail(err)
        } else {
          console.log('参数的类型必须为函数')
        }
      }
    })
  }
  // 重连方法,会根据时间频率越来越慢
  _reConnect(options) {
    let timer, _this = this;
    if (this._connectNum < 20) {
      timer = setTimeout(() => {
        this.initWebSocket(options)
      }, 3000)
      this._connectNum += 1;
    } else if (this._connectNum < 50) {
      timer = setTimeout(() => {
        this.initWebSocket(options)
      }, 10000)
      this._connectNum += 1;
    } else {
      timer = setTimeout(() => {
        this.initWebSocket(options)
      }, 450000)
      this._connectNum += 1;
    }
  }
  // 关闭websocket连接
  closeWebSocket(){
    wx.closeSocket();
    this._isClosed = true;
  }
}

微信官方文档

微信小程序心跳

微信websocket基础用法

转自:https://hehuiyun.github.io/2021/01/22/微信小程序websocket的用法/

webSocketInit () {
    const vm = this
    let token = ''
    let socketUrl = 'wss://websocket.test'
    // 建立 WebSocket 连接
    vm.socket = wx.connectSocket({
      url: socketUrl,
      header: {
        'content-type': 'application/json',
        'authorization': 'Bearer' + token,
        'x-wxapp-sockettype': 'ordertips'
      },
      success (res) {
        console.log('WebSocket 连接成功: ', res)
      },
      fail (err) {
        console.log('WebSocket 连接失败: ', err)
      }
    })
    // onOpen
    vm.socket.onOpen((ret) => {
      console.log('打开 WebSocket 连接')
    })
    // onMessage
    vm.socket.onMessage((ret) => {
      if (ret.data == 'pong') {
        return;
      }
      let data = JSON.parse(ret.data)
      wx.showToast({
        title: data.message,
        icon: 'none',
        duration: 3000
      })
    })
    // onError
    vm.socket.onError((err) => {
      console.log('WebSocket 连接失败:', err)
    })
    // onClose
    vm.socket.onClose((ret) => {
      console.log('断开 WebSocket 连接', ret)
    })
  },
  // send message
  send (msg) {
    const vm  = this
    vm.socket.send({
      data: msg,
      success (res) {
        console.log('WebSocket 消息发送成功', res)
      },
      fail (err) {
        console.log('WebSocket 消息发送失败', err)
      }
    })
  },
  // 心跳,由客户端发起
  ping () {
    const vm = this
    let times = 0
    // 每 10 秒钟由客户端发送一次心跳
    this.interval = setInterval(function () {
      if (vm.socket.readyState == 1) {
        vm.send('ping')
      } else if (vm.socket.readyState == 3) {
        times += 1
        // 超时重连,最多尝试 10 次
        if (times >= 10) {
          wx.showToast({
            title: 'WebSocket 连接已断开~',
            icon: 'none',
            duration: 3000
          })
          clearInterval(vm.interval)
        }
        vm.reconnect()
      }
    }, 10000)
  },
  // WebSocket 断线重连
  reconnect () {
    const vm = this
    vm.webSocketInit()
  },

HTML

<van-uploader file-list="{{ fileList }}" bind:after-read="uploadImage" accept="image" max-count="6" bind:delete="onDelImage" />

JS

  uploadImage(event) {
    const { file } = event.detail;
    // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    wx.uploadFile({
      url: '后台真实upload地址', // 仅为示例,非真实的接口地址
      filePath: file.url,
      name: 'file',
      formData: { authorize: app.getToken() },
      success:(res) => { //这里要用箭头函数,否则报变量为定义的错
        // 上传完成需要更新 fileList
        const { fileList = [] } = this.data;
        const resData = JSON.parse(res.data);
        fileList.push({ ...file, url: resData.path });
        this.setData({ fileList });
      },
    });
  },

  //点击删除图片
  onDelImage(event) {
    let id = event.detail.index //能获取到对应的下标
    let fileList = this.data.fileList //这里是前端页面展示的数组
    fileList.splice(id, 1) //删除1张刚刚点击的那图
    this.setData({
      fileList: fileList, //在这里进行重新赋值  删除后 图片剩几张就相当于给后台传几张
    })
  },