从 0 到 1 开发一个聊天通讯 服务 覆盘总结分享

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🌟文章首发InfoQ平台🌟","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上个月初,接到一个需求,要开发一个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天通讯","attrs":{}}],"attrs":{}},{"type":"text","text":" 模块 并且 集成到 项目中的多个 入口,实现业务数据的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"记录追踪","attrs":{}}],"attrs":{}},{"type":"text","text":".","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接到需求后,还挺开心,这是我第一次 搞 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"通讯","attrs":{}}],"attrs":{}},{"type":"text","text":" 类的需求,之前一直是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"B 端","attrs":{}}],"attrs":{}},{"type":"text","text":" 的业务需求,不过现在也是在做这个方向,感觉 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"B","attrs":{}}],"attrs":{}},{"type":"text","text":" 端 方向 挺有意思,管理着项目的整个项目上游和下游,然后服务于 内部人员 和 外部人员 使用,感觉挺自豪的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面就就跟着我来看看 如何 开发一个 聊天通讯 服务吧 ! (","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"主要站在前端的角度来讲如何开发设计","attrs":{}},{"type":"text","text":" )","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"技术栈","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/90/90680f279de5480c9db4b921eb0c8265.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Vue 2.x","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Websoket","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Vuex","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Element","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"vue-at","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本项目是 以 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vue","attrs":{}}],"attrs":{}},{"type":"text","text":" 技术栈生态开发的,其实不管用什么","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"语言","attrs":{}}],"attrs":{}},{"type":"text","text":" , ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"思路是关键 !","attrs":{}}],"attrs":{}},{"type":"text","text":" 知道每一步需要干什么, 然后将每一步操作 整合起来 , 最终服务就跑起来了.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"每一步需要干什么","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是 编程 中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"function","attrs":{}}],"attrs":{}},{"type":"text","text":" 功能,根据这个功能然后在细化分析需要有到哪些","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"技术点","attrs":{}}],"attrs":{}},{"type":"text","text":" 。在开发的过程中,你不可能对整个链路的所有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"技术点","attrs":{}}],"attrs":{}},{"type":"text","text":" 熟悉,这就需要遇到啥困难,临时学习就可以了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"开始分析需求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我们要等待 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"UI","attrs":{}}],"attrs":{}},{"type":"text","text":" 设计师 的设计稿 画出来, 然后根据 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"UI","attrs":{}}],"attrs":{}},{"type":"text","text":" 设计师的 设计稿分析整体 聊天通讯 的结构,从","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"view","attrs":{}}],"attrs":{}},{"type":"text","text":" 结构 来 划分 应该 大体 包括哪些 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" , 每个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" 中 又包括哪些小的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" , 这样","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" 从 大 到 小","attrs":{}}],"attrs":{}},{"type":"text","text":" 的方向将 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"设计稿","attrs":{}}],"attrs":{}},{"type":"text","text":" 转化为 程序员视角的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" .","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"确立了有哪些","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" , 接下来 就是 确定 每个 小的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"component","attrs":{}}],"attrs":{}},{"type":"text","text":" 又有哪些 功能了。 现在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"UI","attrs":{}}],"attrs":{}},{"type":"text","text":" 设计师们,一般画完界面后,会通过","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"第三方软件 / 平台 ","attrs":{}}],"attrs":{}},{"type":"text","text":" 来将效果图 转化成网页,并且可以通过 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"URL","attrs":{}}],"attrs":{}},{"type":"text","text":" 可以直接访问,当光标放到页面中的某个元素时,可以获取到当前元素的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"css style","attrs":{}}],"attrs":{}},{"type":"text","text":" , 不过,我建议不之 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"copy","attrs":{}}],"attrs":{}},{"type":"text","text":" ,有时和自己写的布局代码会冲突,按需","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"copy","attrs":{}}],"attrs":{}},{"type":"text","text":" .","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"效果图","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"真实效果图,我就在这里不放出来了,为了保密性,只把整体结构,列出来,然后带着大家分析结构和功能,如何进行编码设计和组件设计。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/56/56b5f507223cf0c7ff3ca44cb1c030d0.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"功能分析图","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f9/f95068020d428bda41758f3e7faa6221.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根据效果图,在进行组件划分时,我要记住这个原则:","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高内聚,低耦合","attrs":{}}],"attrs":{}},{"type":"text","text":" , 组件职责单一性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们将组件划分为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"联系人组件","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"聊天组件","attrs":{}}],"attrs":{}},{"type":"text","text":" ---- 包括了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"历史记录组件","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能根据 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"UI","attrs":{}}],"attrs":{}},{"type":"text","text":" 设计师 提供的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"URL","attrs":{}}],"attrs":{}},{"type":"text","text":" 网页来看交互效果来定,并和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"组长","attrs":{}}],"attrs":{}},{"type":"text","text":" / ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"产品经理","attrs":{}}],"attrs":{}},{"type":"text","text":" 交流需求,确定需求,以及砍掉不合理需求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需求确定后,就是梳理组件部分的功能了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"组件构成","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分析组件之前,我们需要先了解一下","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vue Component","attrs":{}}],"attrs":{}},{"type":"text","text":" ,使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vue","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 朋友应该很熟悉了,一个组件的构成由以下组成:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"data","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件内部状态","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"computed","attrs":{}}],"attrs":{}},{"type":"text","text":" 计算属性,监听","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"data","attrs":{}}],"attrs":{}},{"type":"text","text":" 变化来实现对应的业务逻辑需求","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"watch","attrs":{}}],"attrs":{}},{"type":"text","text":" 监听","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"state","attrs":{}}],"attrs":{}},{"type":"text","text":" 变化","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"method","attrs":{}}],"attrs":{}},{"type":"text","text":" 组将的功能编写区","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"props","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件接受","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"父组件","attrs":{}}],"attrs":{}},{"type":"text","text":" 传递来的值,进行约束类型等","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"lifecycle","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件的生命周期, 可以在组件创建到销毁的过程中执行对应的业务逻辑","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/18/1899f5f21467a50565e6f3a8ef0239f0.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"联系人组件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个组件主要是用来在聊天的时候,可以通过分组快速的找到某个人联系它,功能相对简单。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"查找联系人","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"有通知某人操作","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"功能分析","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能1: 查找联系人","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过现有联系人","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"json","attrs":{}}],"attrs":{}},{"type":"text","text":" 数据来 查找输入的联系人进行匹配。 (简单)","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能2: 通知某人","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当用户点击到某个联系人时,将点击的人 放到输入框里 显示 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@xxx","attrs":{}}],"attrs":{}},{"type":"text","text":" [ 经过格式化处理 ] , 并将选中的联系人信息加入到发送消息的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"json","attrs":{}}],"attrs":{}},{"type":"text","text":" 对象中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有多种实现方案,当用户点击了某联系人时,将触发事件,携带值传递给","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"父组件[聊天组件的入口 index.vue ] 接收","attrs":{}}],"attrs":{}},{"type":"text","text":",然后将值传递给 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天主体组件","attrs":{}}],"attrs":{}},{"type":"text","text":" ,通过 在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天主体组件","attrs":{}}],"attrs":{}},{"type":"text","text":" 中 通过 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"$refs","attrs":{}}],"attrs":{}},{"type":"text","text":" 进行传递值。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面只提供示例代码","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从联系人列表获取选中联系人","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"//联系人组件 concat.vue\n\n\ngetLogname(val){\n this.$emit('toParent',{tag:'add',logname:val})\n},\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"聊天框显示选中的联系人","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在聊天入口组件 接收 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"子向父","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件传递 选中联系人数据,然后给 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天主体","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件绑定 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ref","attrs":{}}],"attrs":{}},{"type":"text","text":" , 通过","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"refs","attrs":{}}],"attrs":{}},{"type":"text","text":" 来将联系人数据传递到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天主体","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件显示。 [这块 数据传递有多种方法,例如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vuex","attrs":{}}],"attrs":{}},{"type":"text","text":"]","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"//聊天组件入口 index.vue 它包括 联系人组件 聊天主体组件 历史记录组件\n\n//联系人组件\n\n\n//聊天主体组件 \n\n\n\n \n // 接受\n innerHtmlToChat(data){\n this.$refs.chatRoom.$refs.inputConents.innerHTML+=` @ ${data.logname}` //拼接到聊天输入框里\n}, \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"效果展示","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e9/e935e894209ccdaddb7096ba954a02bb.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"从联系人列表选中人员,发送消息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6b/6bcfea54130d2c8da6545e0ba8c1d6fa.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"@人 接收到推送消息","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"聊天主体组件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个组件就负责的功能就多了,这块我主要把关键的功能带大家来分析过一遍","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关键功能;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 好友功能,实现推送通知(在线通知 / 离线-上线通知)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"聊天工具 [ ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"支持表情","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"支持大文件上传","attrs":{}}],"attrs":{}},{"type":"text","text":" ]","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"发送消息 [ ` 这块就可以跟业务挂钩了,发送信息时,并携带一些符合你项目需求的数据]","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"功能分析","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能1 : @ 实现","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"vue-at","attrs":{}}],"attrs":{}},{"type":"text","text":" 文档 : https://github.com/von7750/vue-at","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它的功能和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"微信","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"QQ ","attrs":{}}],"attrs":{}},{"type":"text","text":" **@ ** 功能一样,在聊天输入框里,当你 输入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 键时, 弹出好友列表,然后从中选择联系人进行聊天。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@ ","attrs":{}}],"attrs":{}},{"type":"text","text":" 功能必须包括以下3个关键功能;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以弹出联系人列表","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以监听输入字符内容进行过滤显示对应数据","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"删除 @ 联系人","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".......","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一开始, 我是 自己造了个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 功能 轮子 搞了搞,后来才发现市场上有相应的轮子,直接用第三方了,挺不错的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"vue-at","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面来跟着我,来捋一下思路如何实现这个轮子,此处就不放实现代码了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来分析一波:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当在编辑区,输入 @ 时, 弹出框","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"mounted","attrs":{}}],"attrs":{}},{"type":"text","text":" 生命周期中监听 按键 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"code","attrs":{}}],"attrs":{}},{"type":"text","text":" = 50 / 229 (中文/英文) 时,做出处理","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"由于我们这块采用的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"div 可编辑属性","attrs":{}}],"attrs":{}},{"type":"text","text":" ,那么就获取到 可编辑属性的光标位置","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"然后通过光标位置 动态来改变 弹出框联系人列表的样式 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"top","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"left","attrs":{}}],"attrs":{}},{"type":"text","text":" , 实现跟着光标的 位置显示联系人列表。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"然后 从列表中选择 联系人进行聊天,并将 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"联系人列表弹框","attrs":{}}],"attrs":{}},{"type":"text","text":" 隐藏掉。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面就实现了基本的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"选中联系人功能","attrs":{}}],"attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"删除选中的联系人","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于这块是采用的可编辑属性, 我们可以获取选中的人,但","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"无法直接判断是删除的哪个人","attrs":{}},{"type":"text","text":",这时,只能通过判断 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"innerHTML","attrs":{}}],"attrs":{}},{"type":"text","text":" 中是否包含某联系人,来进行删除已保存的联系人。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这时,已经基本满足了业务需求实现了。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三方插件已经的够好了,我们就没必要再造轮子,浪费时间了, 但 实现思路 必须的懂。 下面,我就来演示如何使用 第三方插件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"vue-at","attrs":{}}],"attrs":{}},{"type":"text","text":" 实现 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1. 安装插件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"npm i [email protected]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.组件 内部导入插件组件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import At from \"vue-at\";\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"3.注册插件组件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":" components: {\n At\n },\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"4. 页面中使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"At","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件 必须包括 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可编辑","attrs":{}}],"attrs":{}},{"type":"text","text":" 输入内容区域, 这样,当输入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 时,会弹出联系人列表框。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"members","attrs":{}}],"attrs":{}},{"type":"text","text":" : 数据源","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"filter-match","attrs":{}}],"attrs":{}},{"type":"text","text":" : 过滤数据","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"deleteMatch","attrs":{}}],"attrs":{}},{"type":"text","text":" : ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"删除的联系人","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"insert","attrs":{}}],"attrs":{}},{"type":"text","text":" : 获取联系人","attrs":{}}]}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n \n
\n
\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// 过滤联系人\nfilterMatch(name, chunk) {\n return name.toLowerCase().indexOf(chunk.toLowerCase()) === 0;\n},\n// 删除联系人\ndeleteMatch(name, chunk, suffix) {\n this.contactList = this.contactList.filter(\n item => item.logname != chunk.trim()\n );\n return chunk === name + suffix;\n},\n// 获取联系人\ngetValue(val) {\n this.contactList.push({ logname: val });\n},\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/43/437b3b7b29e6b3c0f8108efa81ab0535.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能2:聊天工具箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"聊天软件除了普通文字聊天,还有一些辅助服务来增加聊天的丰富性,例如: 表情 , 文件上传, 截图上传 .... 功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先来看看 市场 热门聊天软件它们有哪些 聊天工具。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"微信聊天工具箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"表情","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"文件上传","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"截屏","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"聊天记录","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"视频聊天 / 语音聊天","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34e651ee28ac70d29f3c1e1c119e7f85.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"微信聊天工具箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"QQ","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 聊天工具箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"表情","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"GIF 动图 ","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"截屏","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"文件上传","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"腾讯文档","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"图片发送","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"..... 腾讯业务相关功能","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/44cf5bacb16f11cf098ed4487595c864.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"QQ聊天工具箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"介绍了市场上热门聊天的工具箱有哪些工具,回归正题: 我们的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天工具箱","attrs":{}}],"attrs":{}},{"type":"text","text":" 有哪些功能呢, 其实有哪些功能根据 业务来定,后期工具箱可以不断扩充。 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"我们的工具箱基本上满足日常聊天需求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"表情","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"文件上传","attrs":{}}],"attrs":{}},{"type":"text","text":" 支持大文件 ( 几个G 都可以)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"截屏","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Ctrl + Alt + A ","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"历史记录","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我就来将比较几个重要的功能: ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"文件上传","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" 截屏","attrs":{}}],"attrs":{}},{"type":"text","text":" , 其它功能都很简单。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"文件上传","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上传组件我采用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Element el-upload","attrs":{}}],"attrs":{}},{"type":"text","text":" 组件,由于我业务 要求上传文件支持大文件, 采用的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"分片续传","attrs":{}}],"attrs":{}},{"type":"text","text":" 方式来实现。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分片续传思路","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"我们上传也是采用的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 上传,首次发送时,必须发送一些必要的文件基本信息","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"文件名","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"文件大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"发送者","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"一些跟业务相关的字段数据","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"时间","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"文件分片大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"文件分片片数","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"上传进度标识","attrs":{}}]}]}],"attrs":{}}],"attrs":{}}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":10,"align":null,"origin":null},"content":[{"type":"text","text":"首次发送完文件的基本信息后,开始发送分片文件信息,首先将文件分片后,然后依次读取片文件流,发送时携带文件流,等文件分片循环结束后,发送一个结束标识告诉后台发送完毕了 [","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"这块你可以和后端商量设计数据格式","attrs":{}}],"attrs":{}},{"type":"text","text":"]","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"示例代码演示","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n \n
\n 将文件拖到此处然后点击上传文件\n
\n
\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"覆盖掉 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Element","attrs":{}}],"attrs":{}},{"type":"text","text":" 默认上传方式,改用自定义上传方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开始分片上传","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":" // 上传文件\n httpRequest(options) {\n let that = this;\n\n //每个文件切片大小\n const bytesPerPiece = 1024 * 2048;\n // 文件必要的信息\n const { name, size } = options.file;\n // 文件分割片数\n const chunkCount = Math.ceil(size / bytesPerPiece);\n \n // 获取到文件后,发送文件的基本信息\n const fileBaseInfo = {\n fileName: name,\n fileSize: size,\n segments: \"historymessage\",\n loginName: localStorage.getItem(\"usrname\"),\n time: new Date().toLocaleString(),\n chunkSize: bytesPerPiece,\n chunkCount: chunkCount,\n messagetype: \"bufferfile\",\n process: \"begin\",\n \n \n ... 一些跟业务挂钩的 字段\n\n };\n\n\n that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));\n \n let start = 0;\n\n // 进行分片\n var blob = options.file.slice(start, start + bytesPerPiece);\n //创建`FileReader`\n var reader = new FileReader();\n //开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.\n reader.readAsArrayBuffer(blob);\n //读取操作完成时自动触发。\n reader.onload = function(e) {\n // 发送文件流\n that.$websoketGlobal.ws.send(reader.result);\n start += bytesPerPiece;\n if (start < size) {\n var blob = options.file.slice(start, start + bytesPerPiece);\n reader.readAsArrayBuffer(blob);\n } else {\n fileBaseInfo.process = \"end\";\n // 发送上传文件结束 标识\n that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));\n }\n that.uploadStatus = false;\n that.fileList = [];\n };\n },\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"效果演示","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/04/0433ba3974595db3236eab549b76b4f8.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3da0466f0f1165a3a144e48897e8a26d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"功能3: 截屏功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"PC","attrs":{}}],"attrs":{}},{"type":"text","text":" 中,这是一个很重要的业务,通过这种技术可以从网上截取下自己感兴趣的文章图片供自己使用观看,可以帮助人们更好的去理解使用知识。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于我们的输入内容区域采用的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可编辑","attrs":{}}],"attrs":{}},{"type":"text","text":" 区域,此处可以插入任意内容,也可以使用外部 的截图功能,粘贴到输入框区域,这块就没必要的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"造轮子了","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1. 可编辑区域","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们给 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"div","attrs":{}}],"attrs":{}},{"type":"text","text":" 加上 该属性 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contenteditable","attrs":{}}],"attrs":{}},{"type":"text","text":" 就可以控制 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"div","attrs":{}}],"attrs":{}},{"type":"text","text":" 中可输入哪些内容,外部复制过来内容也可以直接显示,还可以显示其带的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"css","attrs":{}}],"attrs":{}},{"type":"text","text":" 效果。我们先来看看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contenteditable","attrs":{}}],"attrs":{}},{"type":"text","text":" 有哪些属性吧 !","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/9120f72b1c1c887f4cdc40a2fee052fe.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"
描述
inherit默认值继承自父元素
true或空字符串,表示元素是可编辑的;
false表示元素不是可编辑的。
plaintext-only纯文本
caret符号
events
"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不允许简写为 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正确的用法是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"浏览器支持情况","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/59/59a8faacc53dd80aa69aff3c88f1236d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d5/d53c319e01a3f271edda8d67c675713d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"
\n
\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"效果展示","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c1c52548213645a2c5340be71522687e.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2. 截屏","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于采用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可编辑","attrs":{}}],"attrs":{}},{"type":"text","text":" ,那么就可以随意从外部 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"copy","attrs":{}}],"attrs":{}},{"type":"text","text":" , 哈哈,有意思的来了,支持 Windows 自带的截屏 + PC 第三方 截屏......","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"💥快捷操作方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"windows","attrs":{}}],"attrs":{}},{"type":"text","text":" 自带的的截屏快捷键","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"截取整个屏幕 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Print Screen","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"截取当前活动屏幕 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" Alt+Print Screen","attrs":{}}],"attrs":{}}]}],"attrs":{}}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"QQ","attrs":{}}],"attrs":{}},{"type":"text","text":" 截屏功能,支持个性化操作截图 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Ctrl + Alt + A ","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"微信","attrs":{}}],"attrs":{}},{"type":"text","text":" 截屏功能, 支持个性化操作截图 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Alt + A","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"专门的截屏工具....","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"站在巨人的肩膀上, 直接起飞。😄 , 不过确实站在用户角度想,这点确实有点不好😘。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"实际效果演示 ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.1 微信截屏 show time","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bf/bf14908356f465fa998a76ce3f23bf01.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.2 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"QQ","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 截屏 ","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/99/99e52cbc8b0d72eb56b87bc95d634be8.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"功能4: 发送功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个功能贯穿这个聊天项目,项目采用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 实现的通信服务,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"全双工通信","attrs":{}}],"attrs":{}},{"type":"text","text":" , 发送聊天内容时,需要携带一些很业务相关的数据,来实现业务跟踪分析。下面,来简单复习过一下 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" , 对没有使用过","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 同学也时学习。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/de/deb4781be795dcc2e65ea5fdd79014ef.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"WebSoket","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"WebSocket","attrs":{}}],"attrs":{}},{"type":"text","text":"是一种在单个TCP连接上进行全双工通信的协议。 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WebSocket","attrs":{}}],"attrs":{}},{"type":"text","text":"使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"在WebSocket API","attrs":{}}],"attrs":{}},{"type":"text","text":"中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"WebSoket","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 特点","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"属于服务器推送技术的一种。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数据格式比较轻量,性能开销小,通信高效。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以发送文本,也可以发送二进制数据。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"没有同源限制,客户端可以与任意服务器通信。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"协议标识符是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ws","attrs":{}}],"attrs":{}},{"type":"text","text":"(如果加密,则为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"wss","attrs":{}}],"attrs":{}},{"type":"text","text":"),服务器网址就是 URL。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"WebSoket","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 操作 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"API","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"创建Websoket连接🔗","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"let socket = new WebSocket(\"ws://域名/服务路径\")\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"连接 Websoket 成功触发","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"open()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法在连接成功时,触发","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"socket.onopen = function() {\n console.log(\"websocket连接成功\");\n};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"发送消息","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" send()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法并传入一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"字符串","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ArrayBuffer","attrs":{}}],"attrs":{}},{"type":"text","text":" 或 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Blob","attrs":{}}],"attrs":{}},{"type":"text","text":" .","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"socket.send(\"公众号: 前端自学社区\")\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"接收服务端返回的数据","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"message","attrs":{}}],"attrs":{}},{"type":"text","text":" 事件会在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WebSocket","attrs":{}}],"attrs":{}},{"type":"text","text":" 接收到新消息时被触发。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"socket.onmessage = function(res) { \n console.log(res.data)\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"关闭 WebSoket 连接 ","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"WebSocket.close()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法关闭 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WebSocke","attrs":{}}],"attrs":{}},{"type":"text","text":"连接或连接尝试(如果有的话)。 如果连接已经关闭,则此方法不执行任何操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"socket.onclose = function() {\n // 关闭 websocket\n console.log(\"连接已关闭...\");\n //断线重新连接\n setTimeout(() => {\n that.initWebsoket();\n }, 2000);\n};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"WebSoket 错误处理","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"websocket","attrs":{}}],"attrs":{}},{"type":"text","text":"的连接由于一些错误事件的发生 (例如无法发送一些数据)而被关闭时,一个","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"error","attrs":{}}],"attrs":{}},{"type":"text","text":"事件将被引发.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// 监听可能发生的错误\nsocket.addEventListener('error', function (event) {\n console.log('WebSocket error: ', event);\n});\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过上面我们了解了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 如何使用,接下来就是 实操了,下面走起!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"项目采用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vue","attrs":{}}],"attrs":{}},{"type":"text","text":" 技术栈,更多写法偏向于 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vue","attrs":{}}],"attrs":{}},{"type":"text","text":" 。 由于 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WebSoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 贯穿整个项目,而且需要实时推送 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" , 我们将 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 尽量放在全局入口,接收信息","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"onmessage","attrs":{}}],"attrs":{}},{"type":"text","text":" 事件也放在 入口文件中,这样全局都能接收到数据,接收到的数据 利用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vuex","attrs":{}}],"attrs":{}},{"type":"text","text":" 进行管理聊天的数据 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"[ 历史数据 推送数据 发送数据 ]","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1. 新建 一个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"websoket ","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"文件,用于全局使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export default {\n ws: {},\n setWs: function(wsUrl) {\n this.ws = wsUrl\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2. 在","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Vue ","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"入口文件","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"index.js","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"中 全局注册","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import Vue from 'vue'\nimport websoketGlobal from './utils/websoket'\n\n\nVue.prototype.$websoketGlobal = websoketGlobal\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"3. 在 App.vue 中 接收 Websoket 推送的消息","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这块的设计很关键,决定了聊天数据的存储和设计,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"过多细节代码就不放了","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大体思路我说说一下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"传输格式上定了,那么接收的数据结构也就定了,更多的就是在数据结构上下文章了, 前后端需要约束好字段属性。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从聊天页面显示状态来看:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"区分数据类型的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"字段","attrs":{}}],"attrs":{}},{"type":"text","text":",这样前端在接收到推送的消息时,知道在页面中该如何显示,例如(该显示图片样式还是文本样式)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"区分发送消息显示左右的字段, 前端通过接收到推送的消息时, 会首先判断是否为自己,不是的话显示在左边样式","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"区分 系统的推送字段, 根据这个字段显示对应的样式。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"........... 更多字段属性 需要根据你实际业务而来定","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从信息推送状态来看:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 推送全局 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Notification","attrs":{}}],"attrs":{}},{"type":"text","text":" 通知 和 聊天内部推送 设计","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@","attrs":{}}],"attrs":{}},{"type":"text","text":" 推送 根据指定字段类型判断 ,然后实现全局 推送","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":2,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"聊天内容推送","attrs":{}}],"attrs":{}},{"type":"text","text":": 由于它和具体某个聊天有关系,它也属于历史聊天数据,在聊天中根据 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"内容数据类型","attrs":{}}],"attrs":{}},{"type":"text","text":" 来确定如何显示","attrs":{}}]}]}]}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"mounted(){\n this.$websoketGlobal.ws.onmessage = res => {\n const result = JSON.parse(res.data);\n\n // 推送数据\n\n //聊天历史数据 新增加发送的数据\n\n\n // 获取聊天历史数据\n\n //聊天历史数据 新增加发送的数据\n\n };\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"4. 在聊天组件中使用 Websoket","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在聊天组件中,其实使用的就是 发送功能 和 获取 历史记录 功能,还有就是根据 推送的消息内容字段来决定页面中数据如何显示。下面聊天的样式代码就不放了,主要放一下 发送消息的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"示例代码","attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"send() {\n let that = this;\n\n // 定义数据结构: 传递什么内容是 前提 前端和后端商量好的 \n const obj = {\n messageId: Number(\n Math.random()\n .toString()\n .substr(3, length) + Date.now()\n ).toString(36),\n //文件类型 \n messagetype: \"textmessage\",\n //@ 联系热\n call: that.contactList,\n //聊天输入内容 \n inputConent: that.$refs.inputConents.innerHTML ,\n // 当前时间 \n time: currentDate,\n\n ..... 再定义一些符合你业务的字段 \n };\n \n // 发送消息\n that.$websoketGlobal.ws.send(JSON.stringify(obj));\n that.$refs.inputConents.innerHTML = \"\";\n that.contactList = []\n}\n},\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在每次进入聊天组件时,需要首先获取聊天的历史记录,聊天入口根据你的业务来定,传递必须参数.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"mounted(){\n this.$websoketGlobal.ws.send(\n JSON.stringify({\n id: 1\n messagetype: \"historymessage\"\n })\n );\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能5: 离线 / 在线推送","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个相当于 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"微信","attrs":{}}],"attrs":{}},{"type":"text","text":" / ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"QQ","attrs":{}}],"attrs":{}},{"type":"text","text":" 在线 和 上线 收到的消息。 当 A 用户 @ 了 B 用户 (","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"此时 B 用户 不在线","attrs":{}}],"attrs":{}},{"type":"text","text":"),当 B 用户 上线时,它会收到 一条信息。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"这个是怎么实现呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我就结合项目来大体说一下思路,具体实现就不说了,实现主要在后端。 当时,向后端大佬还特意请教了一下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当 A用户 登录了 系统,此时就会和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Websoket","attrs":{}}],"attrs":{}},{"type":"text","text":" 建立连接,后端会记录起来,该用户的标识,状态为登录。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当 A 用户 @ 了 B 用户 ,正常逻辑会推送给B用户一条信息,B 不在线,就不推给他?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"怎么知道B 用户是否在线呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面也说到了,登录系统就会建立连接,后端会暂时存储起来在线的用户,当A 用户 向 B 用户发送的消息后,后端看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"在线用户列表","attrs":{}}],"attrs":{}},{"type":"text","text":"里没有B 用户,那么他就不会推送。当B用户上线了,会自动推送,前端接收,直接提醒用户。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/543048aa36c8d48da9661eb301ff123f.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"聊天室入口组件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"聊天室入口组件包括: ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"联系人组件 ","attrs":{}}],"attrs":{}},{"type":"text","text":" + ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" 聊天主体组件","attrs":{}}],"attrs":{}},{"type":"text","text":" , 它做的事情其实很简单了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"如何打开聊天室 ?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如何给聊天室传递历史数据?","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"如何打开聊天室?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"外部可能通过多个入口来打开聊天室,通过一个状态来控制显示聊天室,传递类型为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Boolean","attrs":{}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"如何给聊天室传递历史数据?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"外部通过给聊天室组件传递必要数据,这些必要数据然后在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"联系人组件","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" 聊天主体组件","attrs":{}}],"attrs":{}},{"type":"text","text":" 内部消耗,获取各自需要的数据,这样聊天室入口组件的职责单一,很好进行管理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面来看看聊天室的入口组件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内部的通信主要是由 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Vuex","attrs":{}}],"attrs":{}},{"type":"text","text":" 来进行管理, 由于聊天室在全局都需要唤醒,可以将","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天入口组件","attrs":{}}],"attrs":{}},{"type":"text","text":"放到全局入口文件,这样,不管项目需要多少个入口,只需要传递唤醒","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天入口组件的状态","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"入口组件需要的必要参数","attrs":{}}],"attrs":{}},{"type":"text","text":" 来获取历史聊天数据。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样,当项目其它模块需要 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天室","attrs":{}}],"attrs":{}},{"type":"text","text":" 这个功能,只需要 **一行代码 ** 即可 接入,作为插槽接入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"openChat(row){\n this.$store.commit(\"Chat\", { status: true, data: row });\n},\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"总结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在开发这个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天服务","attrs":{}}],"attrs":{}},{"type":"text","text":" 中也遇到了很多难点和坑,不过一个一个踩过来了,越往后做思路越开。 开发完这个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"聊天服务","attrs":{}}],"attrs":{}},{"type":"text","text":" 对技术理解又有更深的认知了,在你感觉某个功能很难困难,不知道怎么实现,你先行动起来,按照自己的思路一步一步推理,推理的过程就会思路打开了,会有多种方式来实现了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"最后","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"聊天服务开发了一个月,写文章写了一个周左右,写作不易,如果文章学到了,点个赞👍👍👍关注,支持一下!","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章