blog

Vueを使ったロンユンとのインスタントメッセージング

この記事では、Vueを使ってRongyunでインスタントメッセージを実装する方法を紹介します。具体的には、Rongyun SDKへのアクセス、プロジェクトの設定、インスタントメッセージ機能の初期化につ...

Mar 27, 2019 · 10 min. read
シェア

はじめに

準備

Rongyunに登録するとAPPKEYとTOKENがもらえます。

コードを書くには、まず ロンユンに登録し、Rongyunから提供されたappkeyとtokenを取得する必要があります。

appkeyとtokenを取得したら、忘れずに保存してください。

Vue CLIのインストール

この新しいパッケージは、以下のいずれかのコマンドを使用してインストールできます:

$ npm install -g @vue/cli
# OR
$ yarn global add @vue/cli

プロジェクトの作成

以下のコマンドを実行して、新しいプロジェクトを作成します:

$ vue create hello-world
# OR
$ vue ui // UI インターフェイスでプロジェクトを作成する

このプロジェクトはVUEXを使用しているため、プリセットの選択を促されます。その後の手間を避けるために、必要な機能を手動で選択することを選択してください。

デフォルトのオプションに加えて、RouterとVuexを選択する必要があります。

プロジェクトが作成されると、おそらく次のようになるでしょう。

コードを書き始める

Web SDK 開発者ガイド

Rongyun Web SDKの紹介

まず、ルートディレクトリのpublicディレクトリにあるindex.htmlでRongyun SDKを紹介します。

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0">
 <link rel="icon" href="<%= BASE_URL %>favicon.ico">
 <title>im</title>
 </head>
 <body>
 <noscript>
 <strong>We're sorry but im doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 </noscript>
 <div id="app"></div>
 <script src="https://..com/RongIMLib-....js"></script>
 <!-- built files will be auto injected -->
 </body>
</html>

まず、ルートディレクトリsrcに好きな名前でファイルをいくつか作成します。

  • utilsファイルを作成します。.js
  • scssディレクトリの作成 このディレクトリにutils.scssを作成します。
  • scrディレクトリにinitを作成.vue
  • srcディレクトリにRongCloudを作成します。.vue
  • srcディレクトリにメッセージを作成.vue

コードを書き始めましょう。

utils.js このファイルはRongyunの初期化用です。

export const init = (params, addPromptInfo) => {
 var appkey = params.appkey
 var token = params.token
 RongIMClient.init(appkey)
 RongIMClient.setConnectionStatusListener({
 onChanged: function (status) {
 switch (status) {
 case RongIMLib.ConnectionStatus['CONNECTED']:
 case 0:
 addPromptInfo('正常に接続された')
 break
 case RongIMLib.ConnectionStatus['CONNECTING']:
 case 1:
 addPromptInfo('接続する')
 break
 case RongIMLib.ConnectionStatus['DISCONNECTED']:
 case 2:
 addPromptInfo('現在のユーザーが積極的にリンクを切断する')
 break
 case RongIMLib.ConnectionStatus['NETWORK_UNAVAILABLE']:
 case 3:
 addPromptInfo('ネットワークは利用できない。)
 break
 case RongIMLib.ConnectionStatus['CONNECTION_CLOSED']:
 case 4:
 addPromptInfo('接続が切断された。)
 break
 case RongIMLib.ConnectionStatus['KICKED_OFFLINE_BY_OTHER_CLIENT']:
 case 6:
 addPromptInfo('ユーザーアカウントが別のデバイスでログインしている場合、このマシンはオフラインになる')
 break
 case RongIMLib.ConnectionStatus['DOMAIN_INCORRECT']:
 case 12:
 addPromptInfo('現在、ドメイン・エラーを実行している。)
 break
 }
 }
 })
 // メッセージリスナー
 RongIMClient.setOnReceiveMessageListener({
 // メッセージを受け取る
 onReceived: function (message) {
 // メッセージタイプの決定
 switch (message.messageType) {
 case RongIMClient.MessageType.TextMessage:
 // message.content.content => テキスト
 addPromptInfo('新しいメッセージ + message.targetId + ':' + JSON.stringify(message))
 break
 case RongIMClient.MessageType.VoiceMessage:
 // message.content.content => AMR形式の音声base64
 break
 case RongIMClient.MessageType.ImageMessage:
 // message.content.content => サムネイル base64
 // message.content.imageUri => オリジナルURL
 break
 case RongIMClient.MessageType.LocationMessage:
 // message.content.latiude =>  
 // message.content.longitude =>  
 // message.content.content => 位置イメージ base64
 break
 case RongIMClient.MessageType.RichContentMessage:
 // message.content.content => テキストメッセージの内容
 // message.content.imageUri => イメージ base64
 // message.content.url => オリジナルURL
 break
 case RongIMClient.MessageType.InformationNotificationMessage:
 // do something
 break
 case RongIMClient.MessageType.ContactNotificationMessage:
 // do something
 break
 case RongIMClient.MessageType.ProfileNotificationMessage:
 // do something
 break
 case RongIMClient.MessageType.CommandNotificationMessage:
 // do something
 break
 case RongIMClient.MessageType.CommandMessage:
 // do something
 break
 case RongIMClient.MessageType.UnknownMessage:
 // do something
 break
 default:
 // do something
 }
 }
 })
 RongIMClient.connect(token, {
 onSuccess: function (userId) {
 addPromptInfo('接続に成功した。 + userId, userId)
 },
 onTokenIncorrect: function () {
 addPromptInfo('token無効')
 },
 onError: function (errorCode) {
 switch (errorCode) {
 case RongIMLib.ErrorCode.TIMEOUT:
 addPromptInfo('タイムアウト')
 //再リンク開始のタイムアウト
 var callback = {
 onSuccess: function (userId) {
 console.log("Reconnect successfully." + userId);
 },
 onTokenIncorrect: function () {
 console.log('token無効');
 },
 onError: function (errorCode) {
 console.log(errorcode);
 }
 };
 var config = {
 // デフォルトはfalse、trueは自動再接続を有効にする。
 auto: true,
 // リトライ頻度 [100, 1000, 3000, 6000, 10000, 18000] 単位はミリ秒、オプション
 url: 'cdn.ronghub.com/RongIMLib-2.5.0.min.js',
 rate: [100, 1000, 3000, 6000, 10000]
 };
 RongIMClient.reconnect(callback, config);
 //再リンクのタイムアウト
 break;
 case RongIMLib.ErrorCode.UNKNOWN_ERROR:
 addPromptInfo('不明なエラー')
 break;
 case RongIMLib.ErrorCode.UNACCEPTABLE_PaROTOCOL_VERSION:
 addPromptInfo('使用できないプロトコルのバージョン')
 break;
 case RongIMLib.ErrorCode.IDENTIFIER_REJECTED:
 console.log('ddd')
 addPromptInfo('appkey不正解')
 break;
 case RongIMLib.ErrorCode.SERVER_UNAVAILABLE:
 addPromptInfo('サーバーが利用できない。)
 break;
 }
 addPromptInfo(errorCode)
 }
 }, null)
}

utils.scss このファイルは非常にシンプルです。

@function vw ($px) {
 @return $px / 750px * 100vw;
}

init.vue ユーザー入力との接続を初期化します。

<template>
 <div class="init">
 <van-nav-bar title="クラウドに接続する>
 <van-cell-group>
 <van-field v-model="appkey"
 required
 label="appkey"
 placeholder="appkeyを入力してね。>
 <van-field v-model="token"
 label="token"
 placeholder="トークンを入力する
 required />
 <van-field v-model="targetId"
 label="targetId"
 placeholder="targetIdを入力する"
 required />
 </van-cell-group>
 <van-button class="init-button"
 type="info"
 @click="initRongCloud">接続の初期化</van-button>
 <div class="rong-show-box">
 <p v-for="data in showDatas"
 v-bind:key="data">
 {{data}}
 </p>
 </div>
 </div>
</template>
<script>
import { init } from '@/utils.js'
export default {
 data () {
 return {
 appkey: '', // 保存したappkeyはこちら *重要
 token: '', // token 複数回生成可能
 targetId: '', // メッセージを送りたい相手 ターゲットID
 showDatas: [], // 初期化情報
 }
 },
 methods: {
 addPromptInfo (prompt, userId = null) {
 const _this = this
 const avatarList = [
 'https://..com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4100987808,2324741924&fm=26&gp=.jpg',
 'https://..com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2988245209,2476612762&fm=26&gp=.jpg',
 'https://..com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4259300811,497831842&fm=26&gp=.jpg',
 'https://..com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3684587473,1286660191&fm=26&gp=.jpg',
 'https://..com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2884107401,3797902000&fm=26&gp=.jpg'
 ]
 
 // 現実の世界では、ログイン・バックエンドのインターフェイスが返すトークンからユーザーの情報を得るので、シミュレーションのために、初期化されたユーザーにはランダムなアバターを生成する。
 const avatar = avatarList[Math.floor(Math.random() * (3 + 1))] 
 _this.showDatas.push(prompt)
 const timer = setInterval(() => {
 if (userId) {
 clearInterval(timer) // ルートジャンプ後にタイマーを破壊する
 _this.$store.commit('SET_MEMBER', { // ユーザー情報を保存する
 userId: userId,
 avatar: avatar
 })
 _this.$store.commit('SET_TARGETID', _this.targetId) // ターゲットIDを保存する
 _this.$router.push({ name: 'RongCloud' })
 }
 }, 500)
 },
 initRongCloud () {
 var appkey = this.appkey
 var token = this.token
 if (!appkey || !token) {
 alert('appkey トークンは空にはできない')
 } else {
 // このinitはジャックアップされている `utils.js`
 init({
 appkey: appkey,
 token: token
 }, this.addPromptInfo)
 }
 }
 }
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.init-button {
 position: fixed !important;
 bottom: vw(30px);
 left: 50%;
 transform: translateX(-50%);
}
.rong-show-box {
 margin-top: vw(100px);
 text-align: center;
}
</style>

RongCloud.vue メッセージの送信

<template>
 <div id='rongcloud'>
 <van-nav-bar title="クラウドチャット
 fixed
 left-text=" 
 left-arrow
 @click-left="onClickLeft" />
 <div class="wrapper">
 <Message v-for="(item, index) in answer"
 :key="index"
 :data='item' />
 </div>
 <div class="send-message">
 <van-field v-model="say"
 class="message-textarea"
 type="textarea"
 placeholder=" ..." />
 <van-button class="send-button"
 type="info"
 size="small"
 @click="sendMessage"> </van-button>
 </div>
 </div>
</template>
<script>
import { mapState } from 'vuex'
import Message from './Message'
export default {
 components: {
 Message 
 },
 data () {
 return {
 say: '' //  
 }
 },
 created () {
 this.$nextTick(() => {
 const list = document.getElementById('rongcloud')
 document.documentElement.scrollTop = list.scrollHeight
 //うまくいかない場合は、-を試してみてほしい。> list.scrollTop = list.scrollHeight
 })
 },
 watch: {
 answer () {
 this.$nextTick(() => {
 const list = document.getElementById('rongcloud')
 document.documentElement.scrollTop = list.scrollHeight
 //うまくいかない場合は、-を試してみてほしい。> list.scrollTop = list.scrollHeight
 })
 }
 },
 computed: {
 ...mapState({
 answer: 'answer', // メッセージリスト
 memberInfo: 'memberInfo', // ユーザー情報
 targetId: 'targetId' // ターゲットID
 })
 },
 methods: {
 onClickLeft () {
 this.$router.go(-1)
 },
 sendMessage () {
 const _this = this
 var msg = new RongIMLib.TextMessage({ content: _this.say, extra: _this.memberInfo.avatar });
 var conversationType = RongIMLib.ConversationType.PRIVATE // 一対一のチャットでは、他のセッションに適切なメッセージタイプを選択するだけでよい。
 var targetId = this.targetId // ターゲットID
 RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
 onSuccess: function (message) {
 // message これは送信されたメッセージ・オブジェクトで、サーバーから返されたメッセージのユニークIDと、送信されたメッセージのタイムスタンプを含んでいる。
 const say = {
 css: 'right',
 txt: message.content.content,
 headImg: _this.memberInfo.avatar
 }
 _this.answer.push(say)
 _this.say = ''
 },
 onError: function (errorCode, message) {
 var info = ''
 switch (errorCode) {
 case RongIMLib.ErrorCode.TIMEOUT:
 info = ' 
 break
 case RongIMLib.ErrorCode.UNKNOWN:
 info = '不明なバグ
 break
 case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
 info = 'ブラックリストに入るとメッセージを送りあえない '
 break
 case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
 info = 'ディスカッショングループではない
 break
 case RongIMLib.ErrorCode.NOT_IN_GROUP:
 info = 'グループのものではない
 break
 case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
 info = 'チャットルームではない
 break
 }
 console.log('送信に失敗した: ' + info + errorCode)
 }
 })
 }
 }
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.wrapper {
 padding-top: vw(92px);
 padding-bottom: vw(200px);
}
.send-message {
 width: 100vw;
 height: vw(200px);
 position: fixed !important;
 bottom: 0;
 left: 0;
 .message-textarea {
 height: 100%;
 }
 .send-button {
 position: fixed;
 right: vw(30px);
 bottom: vw(30px);
 }
}
</style>

Message.vueはメッセージを表示します。 ロジックは単純なので、ここでは説明しません。

<template>
 <div>
 <div v-if="data.css === 'left'">
 <div class="message left">
 <van-image round
 fit="cover"
 width="2rem"
 height="2rem"
 :src="data.headImg" />
 <span>{{data.txt}}</span>
 </div>
 </div>
 <div v-if="data.css === 'right'">
 <div class="message right">
 <span>{{data.txt}}</span>
 <van-image round
 fit="cover"
 width="2rem"
 height="2rem"
 :src="data.headImg" />
 </div>
 </div>
 </div>
</template>
<script>
export default {
 props: ['data']
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.message {
 display: flex;
 align-items: center;
 padding: vw(10px);
}
.left {
 justify-content: flex-start;
 span {
 margin-left: vw(20px);
 }
}
.right {
 justify-content: flex-end;
 span {
 margin-right: vw(20px);
 }
}
</style>

store.js このデモのための状態管理データ

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
 memberInfo: undefined, // ユーザー情報
 targetId: undefined, // ターゲットID
 answer: [] // メッセージリスト
}
const mutations = {
 SET_MEMBER (state, memberInfo) {
 state.memberInfo = memberInfo
 },
 SET_TARGETID (state, targetId) {
 state.targetId = targetId
 },
 SET_ANSWER (state, playload) {
 let say = {
 css: 'left', // css  
 txt: playload.content, // テキストコンテンツ
 headImg: playload.extra //  
 }
 state.answer.push(say)
 },
};
export default new Vuex.Store({
 state,
 mutations,
 actions
})

最後のステップは、utils.jsファイルにストアを導入し、送信されたメッセージをanswerに保存することです。

import store from './store' export const init = (params, addPromptInfo) => { ... // メッセージリスナー RongIMClient.setOnReceiveMessageListener({ // メッセージを受け取る onReceived: function (message) { // メッセージタイプの決定 switch (message.messageType) { case RongIMClient.MessageType.TextMessage: // message.content.content => テキスト store.commit('SET_ANSWER', message.content) ... } } }) ... }

まとめ

このデモは、あなたに基本的なアイデアを与えるために、単に1対1のチャット機能を実装しています。あなたがより多くの機能が必要な場合は、自分で深く調べてください、ありがとうございました。

Read next

Unixはサーバー市場において健在である。

Unixのサーバー市場シェアは過去最低を記録していますが、すぐに市場から撤退することはないでしょう。

Mar 25, 2019 · 1 min read