65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Emitter、Vue 和 Bootstrap 实现的无服务器 HTML5 聊天

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (13投票s)

2016 年 7 月 20 日

CPOL

5分钟阅读

viewsIcon

39045

介绍一个使用 emitter.io、vuejs 和 bootstrap 实现的无服务器实时 HTML5 聊天应用程序。

 

引言

在本文中,我将向您展示如何创建一个可以托管在您的博客或网站上的完全无服务器的聊天应用程序。如果您想开发自己的聊天平台,这可以作为一个很好的起点。下面是我们即将创建的聊天应用程序。

[查看实时演示]

背景

为了在客户端之间传输消息,我们需要依赖一些允许我们实时分发事件的外部服务。为此,我们将使用 emitter.io,一个可扩展、经济实惠且简单的服务,它还提供了一个不错的免费套餐供我们使用。Emitter 是一个云发布/订阅服务,使用开放且标准化的 MQTT 协议。

本质上,emitter.io(我们在这里使用托管版本)将充当消息总线,允许我们的 javascript 客户端(例如浏览器)

  1. 订阅特定频道,该频道可以表示为一个简单的字符串
  2. 发布消息到该频道。
  3. 存储发布的消息有限的时间,使我们能够检索数据,即使页面意外刷新。
  4. 在一段时间后过期消息,允许系统“忘记”旧的对话。
  5. 通过 TLS/SSL 加密通信,这样我们就可以避免中间人攻击。
  6. 使用特定的 emitter.io 密钥保护频道,该密钥只能被允许发布和/或订阅特定频道。这使得我们可以简单地将聊天频道的密钥嵌入到网页本身中。

 

应用程序 DOM

好了,让我们直接开始编码。首先,让我们看看我们的 index.html 页面。

<div v-for="message in messages" class="row animated bounceInRight">
   <div class="col-sm-12">
       <canvas width="80" height="80" data-jdenticon-hash="{{message.hash}}" class="img-thumbnail img-responsive img-circle"></canvas>
       <div class="panel panel-default"><div class="panel-body">{{message.text}}</div></div>
   </div>
</div>

上面的代码中有几个有趣的点

  1. 我们设置了一个 vue.js 循环属性(v-for="message in messages"),它允许我们为每条消息重复内部 DOM。本质上,我们在那里设置了渲染消息列表的模板。
  2. 我们分配了一个 jdenticon 哈希值(data-jdenticon-hash="{{message.hash}}"),它允许 jdenticon 插件渲染 identicon。请注意,实际的哈希值将由 vue.js 本身进行数据绑定。这意味着我们只需要在每次收到新消息时重新运行 jdenticon。
  3. 我们还设置了一个 CSS 类(class="row animated bounceInRight"),它来自一个非常好的 animate.css 库,允许我们在新消息出现时对其进行动画处理。这非常容易,因为设置起来几乎没有什么别的了。

我们 HTML DOM 的第二个有趣部分是由一个简单的输入区域组成,我们可以在其中提交消息

<form v-on:submit.prevent class="col-sm-12 col-md-6 col-md-offset-3">
   <div class="input-group">
      <input type="text" class="form-control" v-model="message" placeholder="Say Something">
      <span class="input-group-btn">
          <button class="btn btn-default form-control" type="submit" v-on:click="sendMessage()">Say it! <i class="fa fa-commenting-o"></i></button>
      </span>
   </div>
   <ul class="emoji">
       <li v-for="em in emoji"><a v-on:click="append(em)" >{{em}}</a></li>
   </ul>
</form>
  1. 再次,我们使用 vue.js 进行数据绑定(v-model="message")。但是,这次执行的是双向数据绑定。双向数据绑定机制实现了模型和视图之间的“始终最新”关系,这允许我们的模型检索文本框的值。
  2. 我们还将一个“on click”方法(v-on:click="sendMessage()")附加到按钮上,允许我们在用户单击或提交表单时执行一些 javascript。
  3. 最后,我们打印出一系列表情符号,同样使用 vue.js 数据绑定(v-for="em in emoji")并在每个链接上设置一个“on click”函数(v-on:click="append(em)")。

JavaScript 实现

app.js 页面中包含的 javascript 代码实现了所有魔力。

var emitter = emitter.connect({
    secure: true
}); 

首先,我们需要连接到 emitter 服务。这通过简单的 emitter.connect() 函数调用完成,该函数返回一个我们可以使用的客户端。我们还指定我们希望通过加密连接(使用 TLS/SSL)进行通信。

emitter.on('connect', function(){
    // once we're connected, subscribe to the 'chat' channel
    console.log('emitter: connected');
    emitter.subscribe({
        key: key,
        channel: "article1",
        last: 5
    });

    jdenticon.update(".img-circle");
})
  1. 连接后,我们将通过调用 emitter.subscribe() 函数来订阅频道。我们提供了一个我之前在 emitter.io 网站上生成的密钥。此密钥允许我们发布和订阅“article1”频道。但是,它不允许发布或订阅任何其他频道。这限制了它的使用,并允许我们在网上分发此密钥。如果愿意,我们还可以为密钥指定一个过期时间(例如 1 天),之后密钥将自动过期且无效。
  2. 此外,在订阅期间,我们还指定了 last: 5 参数,它允许我们自动从频道检索最后 5 条消息。
emitter.on('message', function(msg){

    // log that we've received a message
    console.log('emitter: received ' + msg.asString() );

    // If we have already 5 messages, remove the oldest one (first)
    if (vue.$data.messages.length >= 5){
        vue.$data.messages.shift();
    }

    // Push the message we've received and update an identicon once it's there
    vue.$data.messages.push(msg.asObject());
    setTimeout(function(){ 
        jdenticon.update(".img-circle");
    },5);
});
  1. 收到消息后,我们将通过调用 msg.asObject() 函数将消息解析为 JSON,然后将消息推送到我们的 vuejs 数组中(vue.$data.messages.push(msg.asObject()))。
  2. 我们删除第一条消息,以便让我们的 UI 保持简洁美观。
  3. 我们需要稍微等待数据绑定执行,因此我们在稍后安排一个 jdenticon 更新。
var vue = new Vue({
    el: '#app',
    data: {
        messages: [],
        message: '',
        emoji: [
            "😀", "😬", "😁", "😂", "😃", "😄", "😅", "😆", "😇", "😉", "😊",
            "🙂", "😋", "😌", "😍", "😘", "😗", "😙", "😚", "😜", "😝", "😛", 
            "😎", "😏", "😶", "😐", "😑", "😒", "😳", "😞", "😟", "😠", "😡",
            "😔", "😕", "🙁", "☹", "😣", "😖", "😫", "😩", "😤", "😮", "😱",
            "😨", "😰", "😯", "😦", "😧", "😢", "😥", "😪", "😓", "😭", "😵", 
            "😲", "😷"
        ]
    },
    methods: {
        sendMessage: function () {
            var message = this.$data.message;
            this.$data.message = '';

            // publish a message to the chat channel
            console.log('emitter: publishing');
            emitter.publish({
                key: key,
                channel: "article1/" + getPersistentVisitorId(),
                ttl: 1200,
                message: JSON.stringify({
                    name: 'test',
                    hash: getPersistentVisitorId(),
                    text: message,
                    date: new Date()
                })
            });
        },

        append: function(emoji) {
            this.$data.message += ' ' + emoji + ' ';
        }
    }
});

最后一部分(上面显示)是我们实际的视图模型(vue.js)。这一部分非常直接,因为它声明了一些要绑定到数据和一些要执行的方法。

  1. messages 是我们要显示的我的消息数组。
  2. message 包含我们文本框中的输入文本。
  3. emoji 包含一列可爱的表情符号字符。有趣的是,所有操作系统都支持表情符号:iOS、Android、OS X、Windows 和 Windows Phone,我们使用了一些可以直接复制粘贴的字符(参见 http://getemoji.com)。

最有趣的是,sendMessage 方法将一条消息发布到 emitter 服务上的 article1 频道。我们指定消息应在 1200 秒后自动过期,以便服务可以“忘记”旧消息。在发布时,我们还传递了一个访客的哈希值(一种唯一值),它允许远程客户端生成合适的头像。

历史

  • 2018/01/21 - 小改动。
  • 2016/08/09 - 澄清了一些事情。
  • 2016/07/22 - 修正了一些拼写错误。
  • 2016/07/20 - 初始文章。
© . All rights reserved.