使用Ably和AWS构建您自己的即时聊天网络组件

199 阅读11分钟

Web组件是建立可重复使用的功能的一个好方法,你可以在不同的网页和Web应用程序中使用。想象一下,在React、Vue.js或Next.js等不同的框架之间共享组件吧在这篇文章中,我们将深入研究Web组件,并向您展示如何用Ably构建一个聊天Web组件,将其用于用AWS Amplify和AWS Lambda构建的应用程序中。

网络组件是网络技术的集合,在浏览器中被标准化,以方便编写可重复使用的标记和功能。它们是自定义元素阴影DOMHTML模板的组合。当它们被一起使用时,它们被统称为网络组件。它们是特定框架的组件重用方法的替代品,如React Components和Vue.js模板。它们之所以有趣,是因为它们不受任何运行时框架的约束,而且被所有现代浏览器所支持。在Web Components被广泛支持之前,网络组件的竞争方法已经建立,这导致很多框架开发了自己的重用和封装方法。网络组件提供了一种独立于框架的方法,为浏览器建立可重用的功能。

在谈论Web组件时,大多数人指的是自定义HTML元素,因为这些是最接近React或Vue中的组件模型的,自定义元素可以:

  • 通过设置自己的innerHTML属性,将HTML渲染到自己体内。
  • 通过将数据存储为自身实例上的属性来封装数据。
  • 跟踪存储在其属性中的数据变化,并触发重新渲染。
  • 允许你写代码,当它们从DOM中被添加和删除时被触发。

在这篇文章中,我们要做的第一件事是创建一个自定义元素的例子,显示一个按钮被点击的次数。为了创建这个自定义元素,我们需要创建一个新的JavaScript文件,我们可以从我们的网页上引用。首先,创建一个名为index.js的新文件。我们将为自定义元素定义一个类,并将其称为CountComponent 。这将扩展HTMLElement(浏览器的所有元素的基本类型):

classCountComponent extends HTMLElement {
}

在这个类中,我们需要为change tracking ,定义需要观察的属性。为了这个演示,我们将返回一个单一字符串的数组--属性名称count :

static get observedAttributes() {
  return ['count'];
}

接下来,我们需要定义一些自定义属性和它们的行为。我们为自定义的count 属性创建一个getter ,在该函数中,属性值count ,通过调用getAttribute 加载,默认为字符串 "0" 。一旦数据被加载,我们就从JSON解析它--我们这样做是因为我们存储的任何数据都必须是字符串:

get count() { 
    const val = this.getAttribute('count') || "0"; return JSON.parse(val);
}

我们还需要为我们的属性创建一个setter ,在这里我们将值序列化为JSON,并在我们的元素上设置它:

set count(value) { 
    this.setAttribute('count', JSON.stringify(value));
}

尽管自定义元素被定义为类,但除了调用super 来触发HTML元素类的基本逻辑外,你不能在它们的构造函数中加入任何逻辑。添加任何其他逻辑,或者错过调用super,你就会在控制台中看到一个错误,元素就无法工作:

constructor() { 
    super();
} 

因为我们不能在构造函数中做任何有意义的工作,所以自定义元素提供了生命周期回调--你可以在组件生命周期的不同部分实现这些函数。我们将使用
connectedCallback 函数。实现这个回调将导致代码在组件被添加到DOM时运行--它类似于React的componentDidMount 函数。在connectedCallback中,我们要为计数器设置一个默认值,并调用一个我们很快就会看到的名为setContent 的函数:

connectedCallback() { 
    this.count = 0; this.setContent();
}

我们可用的下一个生命周期回调函数是disconnectedCallback (这里显示的只是为了说明问题,因为我们没有运行任何代码)。当你的元素从DOM中被移除并销毁时,这个函数中的代码就会执行:

disconnectedCallback() { 
    // This is the web component version of react's "componentDidUnmount"
}

自定义元素也为我们提供了一个attributeChangedCallback 。我们将使用这个函数来执行代码,每当一个observedAttribute 的变化:

attributeChangedCallback(attrName, oldValue, newValue) { 
    // When an observed attribute changes this.setContent();
}

setContent() { 
    this.innerHTML = `
        <div>Count is: ${this.count}</div>
        <button>Increment Counter</div>
    `; 
    this.querySelector("button").addEventListener('click', () => { 
        this.count++; 
    });
}

setContent 是一个函数,当组件被添加到DOM中,以及发生更新时,组件会调用该函数。上述代码中的点击处理程序可以使用 关键字访问元素的属性,所以在每次点击时,我们都会增加值,反过来触发重新渲染。this 注意:这个演示并不是特别注重性能,因为它设置了整个innerHTML,并在每次调用按钮时为其连接了一个点击处理程序。这并不是为生产而建立一个应用程序的方式。在一个真正的应用程序中,你不会重置整个DOM。相反,你只会改变UI中需要更新的部分。此外,该 影子DOM APIs 可以用在对性能敏感的场景中。

最后,在JavaScript文件的最后,我们调用浏览器的customElements.define API--为元素提供一个标签名,以及对我们刚刚定义的类的引用:

customElements.define('count-component', CountComponent);

现在可以把自定义元素的JavaScript文件作为一个模块来引用,用我们定义的标签名把我们刚才做的元素添加到页面中:

<script src="component/CountComponent.js" type="module"></script>
<count-component></count-component>

然后组件就这样显示出来了

Build your own live chat web component with Ably and AWS

用Ably建立一个聊天网页组件

我们将通过建立两个自定义元素来建立一个文本聊天组件--Ably Chat组件:

  • AblyBaseComponent.js - 一个封装了Ably交互的基础组件
  • AblyChatComponent.js - 继承自AblyBaseComponent的元素,在Ably之上实现聊天逻辑。

这个基础组件的设计严格来说是为了在此基础上建立,并封装Ably API密钥管理和订阅频道的基本要求。使用这个基础组件,我们可以建立任何类型的依赖于实时消息传递的自定义元素。

为了连接到Ably,你需要一个Ably账户和一个 API密钥

构建基础组件

基础组件从标准的自定义元素模板开始--AblyBaseComponent扩展了HTMLElement,并调用其构造函数:

export default class AblyBaseComponent extends HTMLElement { 
    constructor() { 
        super(); 
    }
}

在connectedCallback函数中,我们调用this.connectToAblyChannel (我们很快会检查):

connectedCallback() { 
    this.connectToAblyChannel();
}

disconnectedCallback函数在this.subscribedChannels (我们将在connect 函数中创建)中循环,并从任何已经使用的Aply通道中取消订阅:

disconnectedCallback() { 
    this.subscribedChannels.forEach(ch => { 
        ch.unsubscribe(); 
    });
}

connectToAblyChannel 函数是很多真正工作发生的地方。我们首先加载一些配置--我们将设置一个惯例,即我们希望用户提供他们的Ably API密钥,或一个回调URL,以响应Ably令牌认证请求的API。为了提供这些值,并且因为我们正在创建HTML元素,我们希望在标记中创建元素时将它们设置为属性

为了在元素连接时读取数据属性,我们希望元素的外观如下:

<ably-component data-api-key="API_KEY_HERE" />

接下来我们需要设计一种方法来配置Ably客户端。在常规的JavaScript中,你可能会使用Document API与DOM中的元素进行交互--但由于我们实际上是在自定义元素内部 ,我们可以使用DOM API调用,如从this 对象中获取Attribute

connectToAblyChannel() {
    const ablyApiKey = this.getAttribute("data-api-key");
    const ablyTokenUrl = this.getAttribute("data-get-token-url");
    const ablyConfig = ablyApiKey ? ablyApiKey : { authUrl: ablyTokenUrl };
}

这段代码试图加载data-api-keydata-get-token-url 。如果找到了API密钥,它将优先使用,否则,我们创建一个包含 "authUrl "属性的Ably JavaScript SDK配置对象。现在我们有了一个配置对象(无论是API密钥还是URL),我们可以创建一个Ably Realtime JavaScript SDK的实例。需要指出的是,这个自定义元素依赖于包含的页面在执行前已经使用脚本元素包含了Ably JavaScript SDK。下面的代码是在配置之后,在connectToAblyChannel 函数里面:

   this.ably = new Ably.Realtime(ablyConfig); this.subscribedChannels = [];

一旦我们有了SDK实例,存储在变量this.ably ,我们还将创建一个名为
subscribedChannels
的空数组。

最后,我们将在AblyBaseComponent中增加两个函数--一个publish 函数和一个 subscribe 函数。一旦你调用 ably.channels.get(channelName)
,获得一个频道,常规的Ably SDK就会暴露出发布和订阅函数。
对于这个聊天例子,我们想让AblyChatComponent决定它将在哪些频道上发布和订阅,有效地扩展了Ably SDK的API表面积(API能做的事情的集合)。

增强的发布和订阅函数在开始时接受一个额外的参数--频道名称--然后将其余的参数传递给Ably SDK,让它为我们做所有的艰苦工作。我们通过对参数数组进行重构,并忽略第一个元素来做到这一点。这使得我们可以在我们新定义的变量args 中捕获所有的参数,除了第一个:

publish(channelName, ...publishArguments) {
    const [ ignored, ...args ] = arguments; // destructure the arguments array
    const channel = this.ably.channels.get(channelName);
    channel.publish.apply(channel, args);
    
    if (!this.subscribedChannels.includes(channel)) {
        this.subscribedChannels.push(channel);
    }
}

然后我们可以在Ably SDK的发布或订阅调用中使用.apply ,将其余的变量传递给SDK,以发布或订阅消息。

我们将使用第一个参数,channelName ,以获得正确的频道,并跟踪它,这样我们就可以在从DOM上卸载时取消订阅。下面的代码将放在publish 函数中,在参数分配的下面。订阅函数的工作原理与此类似:

subscribe(channelName, ...subscriptionArguments) { 
    const [ ignored, ...args ] = arguments; 
    const channel = this.ably.channels.get(channelName); 
    channel.subscribe.apply(channel, args);
    if (!this.subscribedChannels.includes(channel)) { 
        this.subscribedChannels.push(channel); 
    }
}

正如你所看到的,publishsubscribe 都是几乎相同的函数--除了我们分别传递给它们对channel.publishchannel.subscribe 的调用。

这一切可能看起来有点像框架,但它的意思是,在这个基类之上构建Aply组件的开发者可以在调用publishsubscribe ,并在调用开始时增加一个参数channelName ,而不用担心与Aply通道的连接或断开。

这个基类包含了我们所有的Aply代码,现在已经准备好让我们在它上面建立令人兴奋的组件,所以让我们创建第二个组件,AblyChatComponent

Build your own live chat web component with Ably and AWS hbspt.cta.load(6939709, '85f436af-7315-44c9-aa12-d764f40b294d', {"region":"na1"});

构建聊天组件

这些组件被设计为以ES6模块的形式导入--你在HTML中使用的脚本标签将type="module"作为一个属性。当使用ES6模块时,我们可以在浏览器组件中使用import ,所以在这里开始,我们要导入我们的AblyBaseComponent,以便扩展它:

import AblyBaseComponent from "./AblyBaseComponent";

接下来要做的是为该元素设置一些模板代码。定义一个名为 "messages "的属性,并将其设置为可观察。接下来,设置constructor ,它又通过调用super(); 来调用AblyBaseComponent的构造函数:

class AblyChat extends AblyBaseComponent { 
    static get observedAttributes() { 
        return ['messages'];
    }

    get messages() { 
        const val = this.getAttribute('messages') || "[]"; return JSON.parse(val);
    }
    
    set messages(messages) { 
        this.setAttribute('messages', JSON.stringify(messages)); 
    }

    constructor() { 
        super(); 
    }
}

我们可以使用connectedCallback 来配置聊天应用程序。我们调用super.connectedCallback ,从基础组件中触发所有的Ably配置逻辑。下面的代码在constructor 下面,在AblyChat 类的收尾括号内:

   connectedCallback() { super.connectedCallback();

 super.subscribe('chat', 'chat-message', (message) => { this.onAblyMessageReceived(message); }); 

我们调用在基础组件上定义的super.subscribe 函数,以订阅一个名为chat 的频道上的消息。其余的参数,正如我们前面指出的,是标准的Ably JavaScript SDK参数--我们只订阅主题chat-message 的消息。当收到一条消息时,我们会调用一个叫做this.onAblyMessageReceived 的函数(我们一会儿会实现这个函数),把收到的消息作为一个参数传过去。

为了确保应用于页面的任何CSS样式不会影响到组件,反之亦然,在connectedCallback 的正文中,我们将生成一个随机字符串并将其分配给一个名为id 的属性:

this.id = uuidv4();

接下来我们调用一个名为renderTemplateAndRegisterClickHandlers 的函数,我们很快就会看到它:

   this.renderTemplateAndRegisterClickHandlers();

最后,我们将浏览器的焦点放在一个叫做this.inputBox 的元素上,这个元素是在模板渲染时生成的,这样,使用聊天用户界面的人就能立即开始打字:

   this.inputBox.focus();

当收到消息时,我们使用attributeChangedCallback 来更新聊天窗口中的聊天气泡的innerHTML。当一个属性被改变时,this.chatTextinnerHTML 被设置并滚动到视图中。我们正在使用一个名为formatMessages 的函数,它接收消息历史,并将其转换为适合显示的HTML元素:

attributeChangedCallback(attrName, oldValue, newValue) { 
    if (oldValue != newValue) { 
        // Only update the chatText contents  
        // Don't wastefully re-render the entire template 
        // or re-register all the click handlers. 
        this.chatText.innerHTML = this.formatMessages(this.messages); 
        this.messageEnd.scrollIntoView(); 
    }
}

接下来,我们设置了renderTemplateAndRegisterClickHandlers 函数,这个函数的名字就是它的作用!这个函数的开头调用另一个名为defaultMarkup 的函数,该函数接收一个参数--元素的id ,并返回我们想在屏幕上显示的innerHTML ,即一个空的聊天框元素。

一旦该元素被渲染到DOM中,我们可以使用querySelectorAll ,找到chatText
inputBoxsendButtonmessageEnd 等元素,这样我们就可以在代码中使用它们:

renderTemplateAndRegisterClickHandlers() { 
    this.innerHTML = defaultMarkup(this.id);
    this.chatText = this.querySelectorAll(".chatText")[0];
    this.inputBox = this.querySelectorAll(".textarea")[0];
    this.sendButton = this.querySelectorAll(".button")[0];
    this.messageEnd = this.querySelectorAll(".messageEnd")[0];
}

在内部,我们还为点击sendButton和输入框的每个按键设置了事件监听器,这样我们就可以处理用户的输入:

this.sendButton.addEventListener('click', (event) => { 
    this.sendChatMessage(this.inputBox.value); 
});

this.inputBox.addEventListener('keypress', (event) => { 
    this.handleKeyPress(event);
});

当我们订阅Ably消息时,我们提到了onAblyMessageReceived 函数 - 这个函数从this.messages ,确保它最多有199条消息,并将最新的消息添加到数组的末端:

onAblyMessageReceived(message) {
    const history = this.messages.slice(-199);
    const updatedMessages = [...history, message];
    this.messages = updatedMessages;
}

这个新的数组然后被分配到this.messages ,这将触发用户界面重新渲染,因为this.messages 是一个观察到的属性。

当按下回车键,或者点击发送消息的按钮时,sendChatMessage 函数被调用。因为我们正在扩展AblyBaseComponent,它调用super.publish 函数,传递通道名称和Ably消息有效载荷:

sendChatMessage(messageText) { 
    super.publish("chat", { name: "chat-message", data: messageText }); 
    this.inputBox.value = ""; 
    this.inputBox.focus();
}

你可以看到,它还负责清除文本框,并将其作为焦点,以便用户可以继续聊天。

handleKeyPress 函数在每个按键上被触发。如果按键是回车键*,而且*聊天框里有信息,它就会发送聊天信息:

handleKeyPress(event) { 
    const messageText = this.inputBox.value; 
    const messageTextIsEmpty = messageText.trim().length === 0;
    if (event.key === "Enter" && !messageTextIsEmpty) { 
        this.sendChatMessage(messageText); event.preventDefault();
    }
}

formatMessages 函数负责将Ably消息历史映射到span元素中。这里有一点逻辑,通过检查message.connectionId 属性和ably.connection.id 属性,检测消息是否由应用程序的当前用户发送,并添加一个可以应用样式的meother CSS 类。来自消息的data 属性被用来承载收到的文本消息:

formatMessages(messages) { 
    const messageMarkup = messages.map((message, index) => { 
        const author = message.connectionId === this.ably.connection.id ? "me" : "other";
        return `<span class="message" data-author=${author}>${message.data}</span>`; 
    }); 
        
    return messageMarkup.join("\n");
}

以上就是自定义元素类的结束。自定义元素类结束后,我们有两个函数。第一个是uuidv4() 函数--它为组件生成了一个唯一的id :

function uuidv4() { 
    return "comp-" + 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); 
    });
}

第二个是defaultMarkup ,它接受一个参数--组件的ID--并使用它来设置生成的HTML上的id 属性。

一旦我们设置了这个id 属性,我们就可以将专门针对这个元素ID的CSS直接嵌入到输出代码中。这意味着,如果同一个页面上出现多个自定义元素的实例,它们就不会有冲突的ID样式

在下一个片段的底部,你可以看到该组件的标记--一个用于保存消息历史的div ,以及一个用于捕捉用户输入的form ,其中包含了我们先前调用的查询选择器中使用的类名:

customElements.define('ably-chat', AblyChat)

当然,和我们开始时的例子元素一样,我们正在调用customElements.define 来注册HTML标签:

<ably-chat></ably-chat>

现在自定义元素的功能已经完成,只要AblyBaseComponent.js和AblyChatComponent.js文件都包含在Web应用程序中,就可以通过引用我们的AblyChatComponent.js作为一个模块来使用它们:

<script src="component/AblyChatComponent.js" type="module"></script>
const defaultMarkup = (id) => (
    `<div id="${id}">
        <chat>
            <style>
                #${id}.chatHolder {
                    display: grid;
                    grid-template-rows: 1fr 100px;
                    background-color: white;
                }
                
                #${id}.chatText {
                    display: flex;
                    flex-direction: column;
                    align-items: flex-start;
                    gap: 1em;
                    height: calc(100vh – 40px – 100px – 100px – 100px);
                    padding: 1em;
                    overflow-y: auto;
                }
                
                #${id}.form {
                    display: grid;
                    grid-template-columns: 1fr 100px;
                    border-top: 1px solid #eee;
                }
                
                #${id}.textarea {
                    padding: 1em;
                    border: 0;
                    font-family: Arial, Helvetica, sans-serif;
                    font-size: 1.2em;
                }
                
                #${id}.button {
                    border: 0;
                    font-weight: bold;
                    font-size: 1.4em;
                    color: white;
                    background: linear-gradient(to right, #363795, #005C97);
                }
                
                #${id}.button:hover {
                    background: linear-gradient(90deg, rgba(54, 55, 149, 1) 0%, rgba(0, 92, 151, 1) 62%, rgba(0, 125, 205, 1) 100%);
                }
                
                #${id}.button:disabled,
                #${id}.button:hover:disabled {
                    background: linear-gradient(to right, #363795, #005C97);
                    opacity: 0.5;
                }
                
                #${id}.message {
                    flex-grow: 0;
                    padding: 1em;
                    border-radius: 10px;
                    border-bottom-left-radius: 0;
                    background-color: #eef5f8;
                }
            </style>
        
            <div class="chatHolder">
                <div class="chatText">
                    <div class="messageEnd"></div>
                </div>
                <div class="form">
                    <textarea class="textarea"></textarea>
                    <button class="button">Send</button>
                </div>
            </div>
        </chat>
    </div>`
);

为了使用现在注册的自定义元素,我们像使用任何旧的HTML标签一样使用它,并为它提供正确的属性:

<ably-chat 
    data-api-key="you-can-put-your-key-here-but-please-don't" 
    data-get-token-url="https://your-token-request-url/createTokenRequest"
></ably-chat>
<!-- You only need one or the other of these attributes --->

该元素在页面中的显示方式是这样的,当配置了API密钥或get-token-url后,它就可以正常工作了

API密钥管理

正如上面所暗示的,我们需要谈谈API密钥管理。虽然这个自定义元素支持直接从你的标记中读取Ably的API密钥--这对本地开发和调试很有帮助--但你绝对不应该在你的标记中存储你的Ably API密钥,否则它们会被盗用和滥用。

在前端使用API密钥的推荐方式是使用Aply Token Authentication。代币认证是一种交换机制,你使用你的真实API密钥来生成有限使用的代币,可以传回给你的客户在前端使用。

为了以这种方式使用令牌认证,我们需要在某处创建一个API,以便从前端调用,其中存储有你真正的Aply API密钥。然后,我们可以使用Ably SDK中的一个函数,将你真正的API密钥换成一个令牌,返回给Ably JavaScript SDK。Ably JavaScript SDK为你管理这个令牌交换过程。当你提供一个指向将返回令牌的API的URL时,SDK将根据需要管理和刷新令牌,所以你不需要担心这个问题。本演示将通过使用AWS Lambda函数和AWS API网关来实现这一目标。

下面的AWS Lambda函数示例提供了必要的令牌交换功能。我们需要做的是需要Ably JavaScript SDK,并创建一个Ably.Realtime 客户端的实例,将你的Ably API密钥从process.env.ABLY_API_KEY :

const Ably = require('ably/promises');
exports.handler = async (event, context) => {
    const client = new Ably.Realtime(process.env.ABLY_API_KEY);
    const tokenRequestData = await client.auth.createTokenRequest({ clientId: 'aws-client' });
    const statusCode = '200';
    const headers = { 'Content-Type': 'application/json' };
    const body = JSON.stringify(tokenRequestData);
    return { statusCode, body, headers };
};

我们使用client.auth.createTokenRequest 来生成一个临时令牌,并将其返回给客户端。

API密钥的所有者有责任确保请求临时令牌的用户可以访问聊天--你可以用任何你喜欢的方式来验证请求,这与其他lambda函数的验证没有区别。在下一节中,我们将在AWS Lambda上进行托管

使用AWS Lambda进行认证

为了部署到AWS Lambda,我们需要创建一个名为/api/createTokenRequest的新目录,里面有两个文件 - package.json和index.js 这是package.json文件:

{
    "name": "createtokenrequest",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "ably": "^1.2.9"
    }
}

而这是index.js文件

const Ably = require('ably/promises');
exports.handler = async (event, context) => {
    const client = new Ably.Realtime(process.env.ABLY_API_KEY);
    const tokenRequestData = await client.auth.createTokenRequest({ clientId: 'aws-client' });
    const statusCode = '200';
    const headers = { 'Content-Type': 'application/json' };
    const body = JSON.stringify(tokenRequestData);
    return { statusCode, body, headers };
};

这两个文件以及它们的node_modules是AWS Lambda运行时间所需要的。我们将使用npm来恢复node模块,然后将/createTokenRequest目录下的内容压缩成一个zip文件。在终端中执行以下内容:

    cd api/createTokenRequest
    npm install

之后,将createTokenRequest目录的内容压缩起来(这个过程取决于操作系统)。我们将使用AWS UI来创建一个Lambda函数,并将这个压缩文件作为源代码上传。

我们现在就来了解一下这个过程,你需要先登录到你的AWS账户:

  1. 在服务搜索栏中搜索lambda,并点击显示在结果中的Lambda服务框。
    Build your own live chat web component with Ably and AWS

  2. 点击 "创建函数 "按钮,创建一个新的Lambda函数
    Build your own live chat web component with Ably and AWS

  3. 选择 "从头开始创作",给你的函数起个名字,然后点击 "创建函数"。
    Build your own live chat web component with Ably and AWS

  4. 一旦函数被创建,点击 "添加触发器",使Lambda函数可以通过HTTP访问。
    Build your own live chat web component with Ably and AWS

  5. 在 "触发器配置 "下拉菜单中选择 "API网关"。
    Build your own live chat web component with Ably and AWS

  6. 在出现的页面中,从下拉菜单中选择你的函数,然后点击 "添加"。
    Build your own live chat web component with Ably and AWS

  7. 将部署阶段设置为默认,将安全性设置为开放,然后点击 "添加"。
    Build your own live chat web component with Ably and AWS

  8. 一旦你添加了你的触发器,用户界面会在配置下的触发器标签中显示一个URL。(当你在HTML中使用你的组件时,这就是你要添加的data-get-token-url 参数,但我们还有一些设置要做!)
    Build your own live chat web component with Ably and AWS

  9. 现在你需要上传我们之前创建的压缩文件。点击 "代码 "标签,然后点击 "上传自",选择".zip文件"。
    Build your own live chat web component with Ably and AWS

  10. 一旦上传了压缩文件,你将需要用你的Aply API密钥设置你的环境变量。在 "配置 "下,选择 "环境变量",然后点击 "编辑 "按钮。
    Build your own live chat web component with Ably and AWS

  11. 在 "环境变量 "设置中添加您的Ably API密钥
    Build your own live chat web component with Ably and AWS
    就这样,你的Lambda已经设置好了

在index.js文件中,我们从process.env.ABLY_API_KEY 。 你需要生成一个新的ably API密钥,然后定义这个环境变量,并在AWS UI(或使用你喜欢的自动化工具)中定义其密钥值。

一旦我们的Lambda函数被创建,我们将需要添加一个AWS API网关触发器,给我们的lambda一个外部可访问的URL。这是我们可以在HTML标记中安全配置的URL,以代替我们实际的API密钥。Ably SDK会处理剩下的事情。

   <ably-chat data-get-token-url="https://yourapigatewayurlhere/createTokenRequest"></ably-chat>

现在我们可以在AWS的静态网络托管服务Amplify上托管你的组件。

  1. 在服务搜索栏中搜索Amplify,并点击产生的AWS Amplify链接。
    Build your own live chat web component with Ably and AWS

  2. 点击 "GET STARTED"
    Build your own live chat web component with Ably and AWS

  3. 向下滚动结果页面到 "托管你的网络应用,并点击 "开始"。
    Build your own live chat web component with Ably and AWS

  4. 用你的GitHub账户认证AWS Amplify
    Build your own live chat web component with Ably and AWS

  5. 选择你的网络组件的存储库
    Build your own live chat web component with Ably and AWS

  6. 编辑你的构建设置,将npm run ci 作为预构建命令,并将baseDirectory设置为"/build"。
    Build your own live chat web component with Ably and AWS

  7. 点击 "保存和部署 "按钮来托管你的组件
    Build your own live chat web component with Ably and AWS

  8. 如果一切顺利,该组件将成功提供、构建和部署。UI将为你提供一个URL来查看你托管的组件。
    Build your own live chat web component with Ably and AWS

托管的Web组件将看起来像这样
Build your own live chat web component with Ably and AWS

使用NPM包

Build your own live chat web component with Ably and AWS

因为组件是以普通的JavaScript构建的,所以我们可以使用NPM和任何一种浏览器友好的方式将NPM包添加到你的前端,来分发和消费组件。

在Ably,我们已经将该组件作为ably-chat-component发布到NPM,你可以使用SkypackCDN直接引用它。这确保了包是与浏览器兼容的:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Ably Chat</title>
        <script src="https://cdn.ably.io/lib/ably.min-1.js" lang="text/javascript"></script>
        <script src="https://cdn.skypack.dev/ably-chat-component" type="module"></script>
    </head>
    <body>
        <ably-chat data-api-key="your-ably-api-key-here"></ably-chat>
    </body>
</html>

你需要引用客户端的Ably SDK来使该组件工作。然而,一旦你做到了这一点,你就可以引用我们组件的Skypack URL,并在你的页面中添加ably-chat标签,设置你的API密钥,一切都会正常工作。

这是在开发模式下使用该组件的最简单的支持方式。然而,如上所述。 你将 需要调出你的API密钥为你自己的token请求URL.

组件的架构

插图中,该组件的架构看起来是这样的:

Build your own live chat web component with Ably and AWS

有了这个相应的序列图

Build your own live chat web component with Ably and AWS

在这篇文章中,我们已经分解了网络组件的工作原理,探讨了Ably和网络组件,并走访了我们如何使用AWS Amplify和AWS Lambda来托管支持实时聊天的应用程序。

如果你已经有了一个网络应用,并且知道如何托管它,我们还谈到了如何使用Skypack直接从NPM中包含这个组件

聊天只是你使用实时消息和Web组件的一种方式,我们很想看看你能用这个代码库做什么。

关于Ably

Ably是一个企业级的pub/sub消息传递平台。我们使高效设计、快速发货和无缝扩展直接交付给最终用户的关键实时功能变得容易。我们每天为成千上万的公司向数百万用户提供数十亿条实时信息。

我们的平台在数学上是围绕 "可靠性的四大支柱 "而建立的,因此我们能够确保信息不会丢失,同时还能通过安全、可靠和高度可用的 全球边缘网络以低延迟交付。

试试我们的API,看看为什么从初创公司到工业巨头的开发者都选择在Ably上构建,因为它们简化了工程,最大限度地减少了DevOps的开销,并提高了开发速度。