欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解vue-socket.io使用教程與踩坑記錄

 更新時間:2022年04月21日 14:19:23   作者:Dreamsqin  
本文主要介紹了vue-socket.io使用教程與踩坑記錄,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

請先允許我狠狠吐個槽:vue-socket.io相關(guān)中文博客實在太少太少,來來去去就那么幾篇,教程也比較零散,版本也比較老,就算我有暴風(fēng)式搜索還是找不到解決問題的方案,然后我怒了,開始看源碼、寫測試demo、幾乎把相關(guān)的issues都看了一遍,折騰1天后終于。。。搞定了,下面總結(jié)一下~

考慮到很多小伙伴看完文章還是一頭霧水或者無法復(fù)現(xiàn)方案,附加demo源碼https://github.com/dreamsqin/demo-vue-socket.io一份,耗時一天~滿意到話給我個start~感謝

前言

vue-socket.io其實是在socket.io-client基礎(chǔ)上做了一層封裝,將$socket掛載到vue實例上,同時你可以使用sockets對象輕松實現(xiàn)組件化的事件監(jiān)聽,讓你在vue項目中使用起來更方便。我目前用的vue-socket.io:3.0.7,可以在其package.json中看到它依賴于socket.io-client:2.1.1。

我遇到的問題

websocket連接地址是從后端動態(tài)獲取,所以導(dǎo)致頁面加載時VueSocketIO實例還未創(chuàng)建,頁面中通過this.$socket.emit發(fā)起訂閱報錯,同時無法找到vue實例的sockets對象(寫在內(nèi)部的事件將無法監(jiān)聽到,就算后面已經(jīng)連接成功)

如果你的websocket連接地址是靜態(tài)的(寫死的),可以只看使用教程,如果你跟我遇到了同樣的問題,那就跳躍到解決方案

console報錯如下:

使用教程

先拋開可能遇到的問題,按照官網(wǎng)的教程我們走一遍:

安裝

npm install vue-socket.io --save

引入(main.js)

import Vue from 'vue'
import store from './store'
import App from './App.vue'
import VueSocketIO from 'vue-socket.io'

Vue.use(new VueSocketIO({
    debug: true,
    connection: 'http://metinseylan.com:1992',
    vuex: {
        store,
        actionPrefix: 'SOCKET_',
        mutationPrefix: 'SOCKET_'
    },
    options: { path: "/my-app/" } //Optional options
}))

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

debug:生產(chǎn)環(huán)境建議關(guān)閉,開發(fā)環(huán)境可以打開,這樣你就可以在控制臺看到socket連接和事件監(jiān)聽的一些信息,例如下面這樣:

connection:連接地址前綴,注意!這里只有前綴,我之前被坑過,因為明明后端有給我返回上下文,但莫名其妙的被去除了,vue-socket.io這里用到的是socket.io-clientManager api,關(guān)鍵源碼如下(只看我寫中文備注的部分就好):

vue-socket.io(index.js)

import SocketIO from "socket.io-client";
export default class VueSocketIO {

    /**
     * lets take all resource
     * @param io
     * @param vuex
     * @param debug
     * @param options
     */
    constructor({connection, vuex, debug, options}){

        Logger.debug = debug;
        this.io = this.connect(connection, options); // 獲取到你設(shè)定的參數(shù)后就調(diào)用了connect方法
        this.useConnectionNamespace = (options && options.useConnectionNamespace);
        this.namespaceName = (options && options.namespaceName);
        this.emitter = new Emitter(vuex);
        this.listener = new Listener(this.io, this.emitter);
    }
    /**
   * registering SocketIO instance
   * @param connection
   * @param options
   */
  connect(connection, options) {
    if (connection && typeof connection === "object") {
      Logger.info(`Received socket.io-client instance`);
      return connection;
    } else if (typeof connection === "string") {
      const io = SocketIO(connection, options);// 其實用的是socket.io-client的Manager API
      Logger.info(`Received connection string`);
      return (this.io = io);
    } else {
      throw new Error("Unsupported connection type");
    }
  }

socket.io-client(index.js)

var url = require('./url');
function lookup (uri, opts) {
  if (typeof uri === 'object') {
    opts = uri;
    uri = undefined;
  }

  opts = opts || {};

  var parsed = url(uri); // 通過url.js對connection前綴進行截取
  var source = parsed.source;
  var id = parsed.id;
  var path = parsed.path;
  var sameNamespace = cache[id] && path in cache[id].nsps;
  var newConnection = opts.forceNew || opts['force new connection'] ||
                      false === opts.multiplex || sameNamespace;

  var io;

  if (newConnection) {
    debug('ignoring socket cache for %s', source);
    io = Manager(source, opts);
  } else {
    if (!cache[id]) {
      debug('new io instance for %s', source);
      cache[id] = Manager(source, opts);
    }
    io = cache[id];
  }
  if (parsed.query && !opts.query) {
    opts.query = parsed.query;
  }
  return io.socket(parsed.path, opts);// 實際調(diào)用的是解析后的前綴地址
}

options.path: 這里就可以填websocket連接地址的后綴,如果不填會被默認添加/socket.io,關(guān)鍵源碼如下(只看我寫中文備注的部分就好):

其他的options配置可以參見https://socket.io/docs/client-api/

socket.io-client(manager.js)

function Manager (uri, opts) {
  if (!(this instanceof Manager)) return new Manager(uri, opts);
  if (uri && ('object' === typeof uri)) {
    opts = uri;
    uri = undefined;
  }
  opts = opts || {};

  opts.path = opts.path || '/socket.io'; // 看到?jīng)]有,如果你不傳遞options.path參數(shù)的話會被默認安一個尾巴"/socket.io"
  this.nsps = {};
  this.subs = [];
  this.opts = opts;
  this.reconnection(opts.reconnection !== false);
  this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
  this.reconnectionDelay(opts.reconnectionDelay || 1000);
  this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
  this.randomizationFactor(opts.randomizationFactor || 0.5);
  this.backoff = new Backoff({
    min: this.reconnectionDelay(),
    max: this.reconnectionDelayMax(),
    jitter: this.randomizationFactor()
  });
  this.timeout(null == opts.timeout ? 20000 : opts.timeout);
  this.readyState = 'closed';
  this.uri = uri;
  this.connecting = [];
  this.lastPing = null;
  this.encoding = false;
  this.packetBuffer = [];
  var _parser = opts.parser || parser;
  this.encoder = new _parser.Encoder();
  this.decoder = new _parser.Decoder();
  this.autoConnect = opts.autoConnect !== false;
  if (this.autoConnect) this.open();
}

vuex: 配置后可以在store.jsmutations或者actions監(jiān)聽到Vue-Socket.io事件(例如:connect、disconnect、reconnect等),這部分目前用得比較少,也挺簡單,如果有疑問可以給我留言我再單獨提供教程。

使用(Page.vue)

注意:熟悉socket.io-client的應(yīng)該知道,默認情況下,websocket在創(chuàng)建實例的時候就會自動發(fā)起連接了,所以切記不要在組件中重復(fù)發(fā)起連接。如果你想自己控制發(fā)起連接的時機可以將options.autoConnect設(shè)置為false。

export default {
    name: 'Page',
    sockets: {// 通過vue實例對象sockets實現(xiàn)組件中的事件監(jiān)聽
      connect: function () {// socket的connect事件
        console.log('socket connected from Page')
      },
      STREAM_STATUS(data) {// 后端按主題名推送的消息數(shù)據(jù)
          console.log('Page:' + data)
      }
    },
    mounted() {
      console.log('page mounted')
      this.$socket.emit('STREAM_STATUS', { subscribe: true })// 在頁面加載時發(fā)起訂閱,“STREAM_STATUS”是你跟后端約定好的主題名
    }
  }

事件除了在sockets對象中默認監(jiān)聽,你還可以在外部單獨注冊事件監(jiān)聽或取消注冊:

this.sockets.subscribe('EVENT_NAME', (data) => {
    this.msg = data.message;
});

this.sockets.unsubscribe('EVENT_NAME');

但這種方式從源碼上看是不支持參數(shù)傳遞的,只支持傳遞事件名及回調(diào)函數(shù)(部分源碼如下):

vue-Socket.io(mixin.js)

beforeCreate(){
        if(!this.sockets) this.sockets = {};

        if (typeof this.$vueSocketIo === 'object') {
            for (const namespace of Object.keys(this.$vueSocketIo)) {
                this.sockets[namespace] = {
                    subscribe: (event, callback) => {
                        this.$vueSocketIo[namespace].emitter.addListener(event, callback, this);
                    },
                    unsubscribe: (event) => {
                        this.$vueSocketIo[namespace].emitter.removeListener(event, this);
                    }
                }
            }
        } else {
            this.$vueSocketIo.emitter.addListener(event, callback, this);
            this.$vueSocketIo.emitter.removeListener(event, this);
        }
    }

解決方案

針對我上面描述的問題,最大原因就在于獲取socket連接地址是異步請求,如文章開頭的截圖,page mounted打印時,this.$socket還是undefined。所以我們要做的就是怎么樣讓頁面加載在VueSocketIO實例創(chuàng)建之后。
我提供兩種解決方案,具體怎么選擇看你們的需求~

保證拿到socket連接地址后再將vue實例掛載到app

缺點:如果你獲取socket地址的請求失敗了,整個項目的頁面都加載不出來(一般服務(wù)器出現(xiàn)問題才會有這種情況產(chǎn)生)
優(yōu)點:實現(xiàn)簡單,一小段代碼挪個位置就好

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ParentApi from '@/api/Parent'
import VueSocketIO from 'vue-socket.io'

/* 使用vue-socket.io */
ParentApi.getSocketUrl().then((res) => {
    Vue.use(new VueSocketIO({
        debug: false,
        connection: res.data.path,
        options: { path: '/my-project/socket.io' }
    }))
    new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount('#app')
})

控制臺打印如下圖:

結(jié)合connect事件+store+路由守衛(wèi)實現(xiàn)攔截

原理:異步請求回調(diào)中創(chuàng)建VueSocketIO實例并監(jiān)聽connect事件,監(jiān)聽回調(diào)中修改isSuccessConnect參數(shù)的值,在Page頁面路由中增加beforeEnter守衛(wèi),利用setInterval周期性判斷isSuccessConnect的值,滿足條件則取消定時執(zhí)行并路由跳轉(zhuǎn)。
缺點:實現(xiàn)起來稍微復(fù)雜一點
優(yōu)點:不會影響其他頁面的加載

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ParentApi from '@/api/Parent'
import VueSocketIO from 'vue-socket.io'

ParentApi.getSocketUrl().then((res) => {
  let vueSocketIo = new VueSocketIO({
    debug: false,
    connection: res.data.path,
    options: { path: '/my-project/socket.io' }
  })
  // 監(jiān)聽connect事件,設(shè)置isSuccessConnect為true
  vueSocketIo.io.on('connect', () => {
    console.log('socket connect from main.js')
    store.commit('newIsSuccessConnect', true)
  })
  Vue.use(vueSocketIo)
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    // socket連接狀態(tài)
    isSuccessConnect: false
  },
  mutations: {
    newIsSuccessConnect(state, value) {
      state.isSuccessConnect = value
    }
  },
  getters: {
    getIsSuccessConnect: state => {
      return state.isSuccessConnect
    }
  },
  actions: {
  }
})

router.js

import Vue from 'vue'
import Router from 'vue-router'
import store from './store'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/page',
      name: 'Page',
      component: () => import(/* webpackChunkName: "Page" */ './pages/Page.vue'),
      beforeEnter: (to, from, next) => {
        let intervalId = setInterval(() => {
         // 直到store中isSuccessConnect為true時才能進入/page
          if (store.getters.getIsSuccessConnect) {
            clearInterval(intervalId)
            next()
          }
        }, 500)
      }
    }
  ]
})

控制臺打印如下圖:

參考資料:

1、vue-socket.io:https://github.com/MetinSeylan/Vue-Socket.io
2、socket.io-client:https://github.com/socketio/socket.io-client
3、vue-router守衛(wèi):https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

到此這篇關(guān)于詳解vue-socket.io使用教程與踩坑記錄 的文章就介紹到這了,更多相關(guān)vue-socket.io使用教程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue前端如何向后端傳遞參數(shù)

    vue前端如何向后端傳遞參數(shù)

    這篇文章主要介紹了vue前端如何向后端傳遞參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • vue中前進刷新、后退緩存用戶瀏覽數(shù)據(jù)和瀏覽位置的實例講解

    vue中前進刷新、后退緩存用戶瀏覽數(shù)據(jù)和瀏覽位置的實例講解

    今天小編就為大家分享一篇vue中前進刷新、后退緩存用戶瀏覽數(shù)據(jù)和瀏覽位置的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Element?table?上下移需求的實現(xiàn)

    Element?table?上下移需求的實現(xiàn)

    本文主要介紹了Element?table?上下移需求的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • vant中l(wèi)ist的使用以及首次加載觸發(fā)兩次解決問題

    vant中l(wèi)ist的使用以及首次加載觸發(fā)兩次解決問題

    這篇文章主要介紹了vant中l(wèi)ist的使用以及首次加載觸發(fā)兩次解決問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • ElementUI修改實現(xiàn)更好用圖片上傳預(yù)覽組件

    ElementUI修改實現(xiàn)更好用圖片上傳預(yù)覽組件

    這篇文章主要為大家介紹了ElementUI修改實現(xiàn)更好用圖片上傳預(yù)覽組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 案例實操vue事件修飾符帶你快速了解與應(yīng)用

    案例實操vue事件修飾符帶你快速了解與應(yīng)用

    這篇文章主要介紹了vue常見的事件修飾符,在平時無論是面試還是學(xué)習(xí)工作中都會經(jīng)常遇到的,本文就帶你快速上手,需要的朋友可以參考下
    2023-03-03
  • Vue屏幕自適應(yīng)三種實現(xiàn)方法詳解

    Vue屏幕自適應(yīng)三種實現(xiàn)方法詳解

    在實際業(yè)務(wù)中,我們常用圖表來做數(shù)據(jù)統(tǒng)計,數(shù)據(jù)展示,數(shù)據(jù)可視化等比較直觀的方式來達到一目了然的數(shù)據(jù)查看,但在大屏開發(fā)過程中,常會因為適配不同屏幕而感到困擾,下面我們來解決一下這個不算難題的難題
    2022-11-11
  • vue導(dǎo)出word純前端的實現(xiàn)方式

    vue導(dǎo)出word純前端的實現(xiàn)方式

    這篇文章主要介紹了vue導(dǎo)出word純前端的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue3動態(tài)路由刷新出現(xiàn)空白頁的原因與最優(yōu)解

    vue3動態(tài)路由刷新出現(xiàn)空白頁的原因與最優(yōu)解

    頁面刷新白屏其實是因為vuex引起的,由于刷新頁面vuex數(shù)據(jù)會丟失,這篇文章主要給大家介紹了關(guān)于vue3動態(tài)路由刷新出現(xiàn)空白頁的原因與最優(yōu)解的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • el-form組件使用resetFields重置失效的問題解決

    el-form組件使用resetFields重置失效的問題解決

    用el-form寫了包含三個字段的表單,使用resetFields方法進行重置,發(fā)現(xiàn)點擊重置或要清空校驗時是失效的,所以本文給大家介紹了el-form組件使用resetFields重置失效的問題解決,需要的朋友可以參考下
    2023-12-12

最新評論