Ir al contenido principal
Latest blog post (July 25, 2024): npm package provenance.

How to use with Vue 3

This guide shows how to use Socket.IO within a Vue 3 application.

Example

Structure:

src
├── App.vue
├── components
│ ├── ConnectionManager.vue
│ ├── ConnectionState.vue
│ └── MyForm.vue
├── main.js
└── socket.js

The Socket.IO client is initialized in the src/socket.js file:

src/socket.js

import{ reactive }from"vue";
import{ io }from"socket.io-client";

exportconst state =reactive({
connected:false,
fooEvents:[],
barEvents:[]
});

// "undefined" means the URL will be computed from the `window.location` object
constURL= process.env.NODE_ENV==="production"?undefined:"http://localhost:3000";

exportconst socket =io(URL);

socket.on("connect",()=>{
state.connected=true;
});

socket.on("disconnect",()=>{
state.connected=false;
});

socket.on("foo",(...args)=>{
state.fooEvents.push(args);
});

socket.on("bar",(...args)=>{
state.barEvents.push(args);
});
información

During development, you will need to enable CORS on your server:

const io =newServer({
cors:{
origin:"http://localhost:8080"
}
});

Reference: Handling CORS

consejo

The event listeners are registered in the src/socket.js file, as we strongly advise against registering listeners in your components. More on that below.

You can then use it in your components:

  • src/components/ConnectionState.vue
<template>
<p>State: {{ connected }}</p>
</template>

<script>
import{ state }from"@/socket";

exportdefault{
name:"ConnectionState",

computed:{
connected(){
return state.connected;
}
}
}
</script>
  • src/components/ConnectionManager.vue
<template>
<button@click="connect()">Connect</button>
<button@click="disconnect()">Disconnect</button>
</template>

<script>
import{ socket }from"@/socket";

exportdefault{
name:"ConnectionManager",

methods:{
connect(){
socket.connect();
},
disconnect(){
socket.disconnect();
}
}
}
</script>
consejo

The socket object can also be initialized without connecting right away with the autoConnect option:

exportconst socket =io(URL,{
autoConnect:false
});

This can be useful for example when the user must provide some credentials before connecting.

  • src/components/MyForm.vue
<template>
<form@submit.prevent="onSubmit">
<inputv-model="value"/>

<buttontype="submit":disabled="isLoading">Submit</button>
</form>
</template>

<script>
import{ socket }from"@/socket";

exportdefault{
name:"MyForm",

data(){
return{
isLoading:false,
value:""
}
},

methods:{
onSubmit(){
this.isLoading=true;

socket.timeout(5000).emit("create-something",this.value,()=>{
this.isLoading=false;
});
},
}
}
</script>

Reference: https://vuejs.org/guide/scaling-up/state-management.html

Important notes

información

These remarks are valid for any front-end framework.

Hot module reloading

The hot reloading of a file that contains the initialization of a Socket.IO client (i.e. the src/socket.js file in the example above) might leave the previous Socket.IO connection alive, which means that:

  • you might have multiple connections on your Socket.IO server
  • you might receive events from the previous connection

The only known workaround is to do a full-page reload when this specific file is updated (or disable hot reloading altogether, but that might be a bit extreme).

Reference: https://vue-loader.vuejs.org/guide/hot-reload.html

Listeners in a child component

We strongly advise against registering event listeners in your child components, because it ties the state of the UI with the time of reception of the events: if the component is not mounted, then some messages might be missed.

src/components/MyComponent.vue

<script>
import{ socket }from"@/socket";

exportdefault{
name:"MyComponent",

data(){
return{
fooEvents:[]
}
},

mounted(){
// BAD
socket.on("foo",(...args)=>{
this.fooEvents.push(args);
});
}
}
</script>
nota

This is fine in your root component though (since it is always mounted).

Temporary disconnections

While very powerful, WebSocket connections are not always up and running:

  • anything between the user and the Socket.IO server may encounter a temporary failure or be restarted
  • the server itself may be killed as part of an autoscaling policy
  • the user may lose connection or switch from Wi-Fi to 4G, in case of a mobile browser

Which means you will need to properly handle the temporary disconnections, in order to provide a great experience to your users.

The good news is that Socket.IO includes some features that can help you. Please check:

With Pinia

información

Pinia is a store library for Vue, it allows you to share a state across components/pages.

More information can be found here.

Pinia's stores and Socket.IO connection can be synced with the following pattern:

stores/item.js
import{ defineStore }from"pinia";
import{ socket }from"@/socket";

exportconst useItemStore =defineStore("item",{
state:()=>({
items:[],
}),

actions:{
bindEvents(){
// sync the list of items upon connection
socket.on("connect",()=>{
socket.emit("item:list",(res)=>{
this.items= res.data;
});
});

// update the store when an item was created
socket.on("item:created",(item)=>{
this.items.push(item);
});
},

createItem(label){
const item ={
id:Date.now(),// temporary ID for v-for key
label
};
this.items.push(item);

socket.emit("item:create",{ label },(res)=>{
item.id= res.data;
});
},
},
});
stores/connection.js
import{ defineStore }from"pinia";
import{ socket }from"@/socket";

exportconst useConnectionStore =defineStore("connection",{
state:()=>({
isConnected:false,
}),

actions:{
bindEvents(){
socket.on("connect",()=>{
this.isConnected=true;
});

socket.on("disconnect",()=>{
this.isConnected=false;
});
},

connect(){
socket.connect();
}
},
});

And then in your root component:

App.vue
<scriptsetup>
import{ useItemStore }from"@/stores/item";
import{ useConnectionStore }from"@/stores/connection";
import{ socket }from"@/socket";

const itemStore =useItemStore();
const connectionStore =useConnectionStore();

// remove any existing listeners (after a hot module replacement)
socket.off();

itemStore.bindEvents();
connectionStore.bindEvents();
</script>

Sample projects

Back to the list of examples

AltStyle によって変換されたページ (->オリジナル) /