AngularJS Node.js는 온라인 채팅방을 구현합니다_AngularJS
AngularJS를 시작하는 것이 생각보다 훨씬 어렵다고 말씀드리고 싶습니다. 공식 홈페이지에서 제공하는 PhoneCat 예제를 읽다가 MOOC에 가서 Da Mo Qiongqiu의 AngularJS Practical Series를 읽었습니다. 아직 기본적인 사용법에 대해 불명확한 질문들이 많아 이해를 돕기 위해 온라인 채팅방을 만들어보기로 했습니다. DEMO는 →채팅방을 클릭하시면 되고, 코드는 →ChatRoom-AngularJS를 클릭하시면 됩니다.
선명한 사진을 찍을 수 있습니다http://files.jb51.net/file_images/article/201508/201508281040051.gif
기능
개발을 시작하기 전에 먼저 구현해야 할 기능을 명확히 하세요.
신규 사용자가 로그인하고 방송하여 다른 사용자에게 알립니다
사용자가 오프라인 상태가 되어 다른 사용자에게 알리기 위해 방송합니다
온라인 인원수 및 목록 표시 가능
그룹 채팅 및 비공개 메시지 사용 가능
사용자가 그룹 메시지를 보내면 방송에서 다른 모든 사용자에게 알립니다
사용자가 비공개 메시지를 보내는 경우 수신자 인터페이스에 별도로 알림이 전송됩니다
저는 미적인 쓰레기이기 때문에 전적으로 부트스트랩에 의존했습니다. 게다가 위챗 채팅 기록의 버블 디자인도 모방했습니다.
인터페이스는 왼쪽과 오른쪽의 두 섹션으로 나누어져 있으며 각각 온라인 목록과 채팅 콘텐츠를 표시하는 데 사용됩니다.
왼쪽 온라인 목록에서 다양한 항목을 클릭하여 오른쪽 섹션에서 채팅 파트너를 전환하세요.
오른쪽에는 현재 채팅 상대와의 대화 기록이 표시되며, 최근 30개만 표시됩니다. 각 채팅 기록의 내용에는 보낸 사람의 닉네임과 아바타, 보낸 시간, 메시지 내용이 포함됩니다. 아바타에 관해서는 여기서 간단한 과정을 거쳐 임의의 색상으로 채워진 사각형으로 대체하겠습니다. 또한, 보내는 메시지와 자연스럽게 받는 메시지의 스타일도 다르게 디자인되어야 합니다. 모든 효과는 아래 그림에서 볼 수 있습니다.
선명한 사진을 찍을 수 있습니다http://files.jb51.net/file_images/article/201508/201508281040052.png
서버
Node.js를 사용하고 express와 socket.io를 혼합하여 서버를 개발합니다. 프로그램 루트 디렉터리에서 터미널을 열고 다음을 실행합니다.
npm init
안내에 따라 package.json 파일을 생성합니다. 종속성 열기 및 구성:
"dependencies": { "express": "^4.13.3", "socket.io": "^1.3.6" }
그런 다음 npm install을 실행하여 종속 모듈을 설치합니다.
다음으로 루트 디렉터리에 새 app.js를 만들고 그 안에 서버측 코드를 작성합니다. 클라이언트 코드를 저장할 새 공용 폴더를 만듭니다.
app.js의 주요 내용은 다음과 같습니다.
var express = require('express'); var app = require('express')(); var http = require('http').createServer(app); var io = require('socket.io')(http); app.use(express.static(__dirname + '/public')); app.get('/', function (req, res) { res.sendfile('index.html'); }); io.on('connection',function(socket){ socket.on('addUser',function(data){ //有新用户进入聊天室 }); socket.on('addMessage',function(data){ //有用户发送新消息 }); socket.on('disconnect', function () { //有用户退出聊天室 ); }); http.listen(3002, function () { console.log('listening on *:3002'); });
위 코드에서는 다음 이벤트에 대한 리스너를 추가했습니다.
-addUser, 새로운 사용자가 채팅방에 입장했습니다
이 이벤트는 클라이언트가 닉네임을 입력하면 발생합니다. 서버는 닉네임이 이미 존재하는지 여부를 판단합니다. 닉네임이 유효하지 않음을 클라이언트에 알립니다.
socket.emit('userAddingResult',{result:false});
반대로 클라이언트에게 닉네임이 유효함과 현재 연결된 모든 사용자 정보를 알리고, 연결된 다른 사용자에게 새로운 사용자 정보를 브로드캐스트합니다.
socket.emit('userAddingResult',{result:true}); allUsers.push(data);//allUsers保存了所有用户 socket.emit('allUser',allUsers);//将所有在线用户发给新用户 socket.broadcast.emit('userAdded',data);//广播欢迎新用户,除新用户外都可看到
'socket.emit'과 'socket.broadcast.emit'의 차이점에 주의해야 합니다. 이 블로그 게시물 socket.io 방출의 여러 사용법에 대한 설명을 확인하세요. >
// send to current request socket client socket.emit('message', "this is a test"); // sending to all clients except sender socket.broadcast.emit('message', "this is a test");
-addMessage, 사용자가 새 메시지를 보냅니다.
1. 비공개 메시지
메시지가 특정 사용자 A에게 전송되면 A에 해당하는 소켓 인스턴스를 얻은 다음 해당 방출 메서드를 호출해야 합니다. 따라서 클라이언트가 서버에 연결할 때마다 후속 요구 사항을 위해 소켓 인스턴스를 저장해야 합니다.
connectedSockets[nickname]=socket;//以昵称作下标,保存每个socket实例,发私信需要用
需要发私信时,取出socket实例做操作即可:
connectedSockets[nickname].emit('messageAdded',data)
2.群发
群发就比较简单了,用broadcast方法即可:
socket.broadcast.emit('messageAdded',data);//广播消息,除原发送者外都可看到
-disconnect,有用户退出聊天室
需要做三件事情:
1.通知其他用户“某用户下线”
socket.broadcast.emit('userRemoved', data);
2.将用户从保存了所有用户的数组中移除
3.将其socket实例从保存了所有客户端socket实例的数组中移除
delete connectedSockets[nickname]; //删除对应的socket实例
运行一下服务端代码,观察有无错误:
node app.js
若没什么问题,继续编写客户端的代码。
客户端
在public目录下新建'index.html',客户端需要用到bootstrap、angularjs、socket.io、jQuery以及我们自己的js和css文件,先把这些文件用标签引入。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="./assets/style/app.css"/> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="//cdn.bootcss.com/angular.js/1.4.3/angular.min.js"></script> <script src="./assets/js/app.js"></script> </head> <body></body> </html>
我们并不立即深入逻辑细节,把框架搭好先。
首先,在body上加上ng-app属性,标记一下angularjs的“管辖范围”。这个练习中我们只用到了一个控制器,同样将ng-controller属性加到body标签。
<body ng-app="chatRoom" ng-controller="chatCtrl">
接下来在js中,我们来创建module及controller。
var app=angular.module("chatRoom",[]); app.controller("chatCtrl",['$scope','socket','randomColor',function($scope,socket,randomColor){}]);
注意这里,我们用内联注入添加了socket和randomColor服务依赖。这里我们不用推断式注入,以防部署的时候用uglify或其他工具进行了混淆,变量经过了重命名导致注入失效。
在这个练习中,我们自定义了两个服务,socket和randomColor,前者是对socket.io的包装,让其事件进入angular context,后者是个可以生成随机色的服务,用来给头像指定颜色。
//socket服务 app.factory('socket', function($rootScope) { var socket = io(); //默认连接部署网站的服务器 return { on: function(eventName, callback) {...}, emit: function(eventName, data, callback) {...} }; }); //randomcolor服务 app.factory('randomColor', function($rootScope) { return { newColor: function() { return '#'+('00000'+(Math.random()*0x1000000<<0).toString(16)).slice(-6);//返回一个随机色 } }; });
注意socket服务中连接的语句“var socket = io();”,我们并没有传入任何url,是因为其默认连接部署这个网站的服务器。
考虑到聊天记录以及在线人员列表都是一个个逻辑及结构重复的条目,且html结构较复杂,为了其复用性,我们把它们封装成两个指令:
app.directive('message', ['$timeout',function($timeout) {}]) .directive('user', ['$timeout',function($timeout) {}]);
注意这里两个指令都注入了'$timeout'依赖,其作用后文会解释。
这样一个外层框架就搭好了,现在我们来完成内部的细节。
登录
页面刚加载时只显示登录界面,只有当输入昵称提交后且收到服务端通知昵称有效方可跳转到聊天室。我们将ng-show指令添加到登录界面和聊天室各自的dom节点上,来帮助我们显示或隐藏元素。用'hasLogined'的值控制是显示或隐藏。
<!-- chat room --> <div class="chat-room-wrapper" ng-show="hasLogined"> ... </div> <!-- end of chat room --> <!-- login form --> <div class="userform-wrapper" ng-show="!hasLogined"> ... </div> <!-- end of login form -->
JS部分
$scope.login = function() { //登录 socket.emit("addUser", {...}); } //收到登录结果 socket.on('userAddingResult', function(data) { if (data.result) { $scope.hasLogined = true; } else { //昵称被占用 $scope.hasLogined = false; } });
这里监听了socket连接上的'userAddingResult'事件,接收服务端的通知,确认是否登录成功。
socket连接监听
成功登录以后,我们还监听socket连接上的其他事件:
//接收到欢迎新用户消息,显示系统欢迎辞,刷新在线列表<br />
socket.on('userAdded', function(data) {});<br />
//接收到所有用户信息,初始化在线列表<br />
socket.on('allUser', function(data) {});<br />
//接收到用户退出消息,刷新在线列表<br />
socket.on('userRemoved', function(data) {});<br />
//接收到新消息,添加到聊天记录<br />
socket.on('messageAdded', function(data) {});<br />
接收到事件以后,做相应的刷新动作,这里的socket是socket.io经过包装的服务,内部仅包装了我们需要用到的两个函数on和emit。我们在事件监听里对model做的修改,都会在AngularJS内部得到通知和处理,UI才会得到及时刷新。
监听内做的事情太具体和琐碎了,这里就不列出了,接下来介绍一下message指令。
message 指令
最后分享一下我在写message指令时遇到的问题。首先看一下其代码:
app.directive('message', ['$timeout',function($timeout) { return { restrict: 'E', templateUrl: 'message.html', scope:{ info:"=", self:"=", scrolltothis:"&" }, link:function(scope, elem, attrs){ $timeout(scope.scrolltothis); } }; }])
以及其模板message.html:
<div ng-switch on="info.type"> <!-- 欢迎消息 --> <div class="system-notification" ng-switch-when="welcome">系统{{info.text}}来啦,大家不要放过他~</div> <!-- 退出消息 --> <div class="system-notification" ng-switch-when="bye">系统:byebye,{{info.text}}</div> <!-- 普通消息 --> <div class="normal-message" ng-switch-when="normal" ng-class="{others:self!==info.from,self:self===info.from}"> <div class="name-wrapper">{{info.from}} @ {{time | date: 'HH:mm:ss' }}</div> <div class="content-wrapper">{{info.text}}<span class="avatar"></span></div> </div> </div>
模板中我们用ng-switch指令监听info.type变量的值,根据其值的不同显示不同内容。比如,当info.type值为"welcome"时,创建第一个dom节点,删除下方另外两个div。
另外,普通消息下,为了在UI上区分自己发出去的和收到的消息,需要给他们应用不同的样式,这里用ng-class指令实现。
ng-class="{others:self!==info.from,self:self===info.from}"
当'self===info.from'返回true时,应用'self'类,否则,应用'others'类。
在此指令中,我们创建了独立作用域,并绑定了三个属性,绑定完后还必须在父作用域的HTML标签上添加相应属性。
scope:{ info:"=", self:"=", scrolltothis:"&" } <message self="nickname" scrolltothis="scrollToBottom()" info="message" ng-repeat="message in messages"></message>
在link函数中,执行一个动作:每当一个message被加到页面上时,将聊天记录滚动到最下方,一开始我是这样写的:
link:function(scope, elem, attrs){ scope.scrolltothis();}
结果发生了一个很奇怪的现象,总是滚动到上一条位置,而不是最新这条。调试之后发现是因为'scrolltothis'函数执行的时候,DOM还没渲染,所以在函数内部获取scrollHeight的时候获得的总是添加DOM节点之前的状态。这时候,可以把代码放到$timeout里延迟0秒执行,延迟0秒并不意味着会立即执行,因为js的单线程特性,代码实际会等到dom渲染完再执行。
$timeout(scope.scrolltothis);
完整代码可以戳我的GitHub→ChatRoom-AngularJS,DEMO可以戳→chat room
有任何不妥之处或错误欢迎各位指出,不胜感激~

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Non-Blocking, Event-Driven 기반으로 구축된 Node 서비스는 메모리 소모가 적다는 장점이 있으며, 대규모 네트워크 요청을 처리하는데 매우 적합합니다. 대규모 요청을 전제로 '메모리 제어'와 관련된 문제를 고려해야 합니다. 1. V8의 가비지 수집 메커니즘과 메모리 제한 Js는 가비지 수집 기계에 의해 제어됩니다.

이 기사는 NodeJS V8 엔진의 메모리 및 가비지 수집기(GC)에 대한 심층적인 이해를 제공할 것입니다. 도움이 되기를 바랍니다.

파일 모듈은 파일 읽기/쓰기/열기/닫기/삭제 추가 등과 같은 기본 파일 작업을 캡슐화한 것입니다. 파일 모듈의 가장 큰 특징은 모든 메소드가 **동기** 및 ** 두 가지 버전을 제공한다는 것입니다. 비동기**, sync 접미사가 있는 메서드는 모두 동기화 메서드이고, 없는 메서드는 모두 이기종 메서드입니다.

Node용 Docker 이미지를 선택하는 것은 사소한 문제처럼 보일 수 있지만 이미지의 크기와 잠재적인 취약점은 CI/CD 프로세스와 보안에 상당한 영향을 미칠 수 있습니다. 그렇다면 최고의 Node.js Docker 이미지를 어떻게 선택합니까?

PHP를 사용하여 간단한 온라인 채팅방을 구현하는 방법 소개: 인터넷이 발전하면서 사람들은 다른 사람과 의사소통하기 위해 온라인 채팅 도구에 점점 더 의존하고 있습니다. 일상생활에서 우리는 친구, 가족, 동료와 소통하기 위해 온라인 채팅 도구를 자주 사용할 수 있습니다. 이 기사에서는 PHP를 사용하여 간단한 온라인 채팅방을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. 데이터베이스 및 테이블을 생성합니다. 먼저 로컬 또는 원격 서버에 데이터베이스를 생성하고 해당 데이터베이스 아래에 "chatroom"이라는 파일을 생성합니다.

Go 언어와 Redis를 사용하여 온라인 채팅방을 구현하는 방법 소개: 인터넷의 급속한 발전으로 소셜 네트워크는 사람들의 일상 생활에 없어서는 안 될 부분이 되었습니다. 소셜 네트워크의 중요한 부분인 온라인 채팅방은 편의성, 실시간 및 강력한 상호작용성으로 인해 사람들 사이에서 인기가 높습니다. 이 글은 Go 언어와 Redis를 기반으로 작성되었으며 이 두 도구를 사용하여 간단한 온라인 채팅방을 구현하는 방법을 소개합니다. 1. Go 언어 소개: Go 언어는 최신 운영 체제를 위한 오픈 소스 시스템 프로그래밍 언어입니다.

Node.js는 GC(가비지 수집)를 어떻게 수행하나요? 다음 기사에서는 이에 대해 설명합니다.

노드가 npm 명령을 사용할 수 없는 이유는 환경 변수가 올바르게 구성되지 않았기 때문입니다. 해결 방법은 다음과 같습니다. 1. "시스템 속성"을 엽니다. 2. "환경 변수" -> "시스템 변수"를 찾은 다음 환경을 편집합니다. 3. nodejs 폴더의 위치를 찾습니다. 4. "확인"을 클릭합니다.
