博客原文地址:http://www.cnblogs.com/qiufenghua/p/6772949.html,轉載請注明出處。
在原來基礎上增加了多個聊天室以及發送圖片【vue+websocket+express+MongoDB實戰項目(實時聊天)(二)】
http://www.cnblogs.com/qiufenghua/p/7018886.html
舊版聊天室地址:
https://github.com/hua1995116/webchat/tree/08ff845a2ca46c27a9024138d5b4173c89dd8056
新版地址:
https://github.com/hua1995116/webchat
----------------------------------------------------------------------------------------------------------------------------------------------------------
繼上一個項目用vuejs仿網易雲音樂(實現聽歌以及搜索功能)后,發現上一個項目單純用vue的model管理十分混亂,然后我去看了看vuex,打算做一個項目練練手,又不想做一個重復的項目,這次我就放棄顏值,打算走心派。結合了后台nodejs,以及數據庫MongoDB來開發了一個實時聊天系統。這個系統可以說是一統江山,也算是實現前端程序員的一個夢了,前后通吃。自認為是一個比全的項目。項目地址:https://github.com/hua1995116/webchat 覺得好的請順手來個star。

技術棧
- 前端 vue,vue-router ,vuex
- 后端 nodejs,express
- 數據庫 mongodb
- 通訊 websocket
- 腳手架工具 vue-cli
結構
├─build
├─config
├─models(存放mongoose對象)
├─schemas(存放mongoose的schemas模型)
├─src
│ │ App.vue
│ │ main.js(主入口)
│ ├─assets
│ ├─components (組件)
│ ├─router(vue-router路由)
│ └─store(vuex)
└─static(靜態資源)
首先用腳手架工具構建一個項目。像這樣:
vue init webpack my-project-name
結構大致是這樣的

好!既然我們是實戰項目,我就不多說這些配置問題。不然又跑題了。不然又要被小哥哥小姐姐們打了。

前端
首先看src目錄下的頁面布局。
main.js// 主入口
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
// 使用museui組件
import MuseUI from 'muse-ui'
import 'muse-ui/dist/muse-ui.css'
Vue.use(MuseUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: {App}
})
我們為了讓整個項目看起來漂漂亮亮的,所以選擇了muse-ui,別說,這個UI框架是真的漂亮。不信大家可以看muse-ui。其余都是腳手架的默認配置。
route/router.js
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/components/Index'
import Robot from '@/components/Robot'
import Home from '@/components/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/robot',
name: 'Robot',
component: Robot
},
{
path: '/home',
name: 'Home',
component: Home
}
]
})
大家可以看到一共有三個路由,沒錯,我們就是寫了三塊,第一塊是和大家一起的聊天室,第二塊是和我們可愛的大白聊天,也就是我們的圖靈機器人。有空你也去搞一個耍耍。第三塊就是我們的個人中心,雖然這一塊沒什么東西。但是畢竟好看,哦~忘了,我們是走心的,怎么可以只看臉。
components/Chat.vue
created() {
const that = this
this.socket = io.connect('http://qiufengh.com:8081')
this.socket.on('message', function(obj) {
that.$store.commit('addroomdetailinfos', obj)
window.scrollTo(0, 900000)
})
this.socket.on('logout', function (obj) {
that.$store.commit('setusers', obj)
})
},
this.socket = io.connect('http://qiufengh.com:8081')
這一句,主要用於連接你當前的服務,到時候下載后面的項目時,記得改成自己的服務以及端口。因為是在Index和Chat都有設置,所以你需要在Index.vue和Chat里的connect都改成你自己的服務。socket.on()用於接受消息。socket.emit() 用於發送消息。不懂的socket.io的看這里socket.io。有了這個就可以和服務端進行交互。等會講解服務端。
store/index.js
state: {
//存放用戶
user: {
name: '',
src: ''
},
//存放歷史記錄
messhistory: {
infos: []
},
//存放房間信息,為了方便以后做多房間
roomdetail: {
id: '',
users: {},
infos: []
},
//存放機器人開場白
robotmsg: [{
message: 'Hi~有什么想知道的可以問我',
user: 'robot'
}],
//聊天頁面顯示控制
chattoggle: false,
//登錄頁面顯示控制
logintoggle: false,
//注冊頁面顯示控制
registertoggle: true,
//提示框顯示控制
dialog: false,
//提示框內容
dialoginfo: ''
}
由於控制代碼太多,所以之后的內容請大家移步,我的github地址。https://github.com/hua1995116/webchat/
服務器端
由於build下dev-server.js中webpack代碼太多,太雜亂,不易於講解。主要來看看用於打包后的服務器端。兩份代碼是一樣的。
prod.server.js(根目錄下)
var express = require('express');
var config = require('./config/index');
var port = process.env.PORT || config.dev.port;
var app = express();
var router = express.Router();
//用於靜態展示入口
router.get('/',function(req,res,next){
req.url = './index.html';
next();
});
app.use(router);
require('./config/routes')(app)
/*引入*/
var mongoose = require('mongoose')
//日志文件
var morgan = require('morgan')
//sesstion 存儲
var bodyParser = require('body-parser')
var cookieParser = require('cookie-parser')
var session = require('cookie-session')
//用於異步回調
mongoose.Promise = require('bluebird')
global.db = mongoose.connect("mongodb://localhost:27017/vuechat")
//服務器提交的數據json化
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
//sesstion 存儲
app.use(cookieParser())
app.use(session({
secret: 'vuechat',
resave: false,
saveUninitialized: true
}))
var env = process.env.NODE_ENV || 'development'
if ('development' === app.get('env')) {
app.set('showStackError', true)
app.use(morgan(':method :url :status'))
app.locals.pretty = true
mongoose.set('debug', true)
}
var server = app.listen(port)
//websocket
// var http = require('http').Server(app);
var io = require('socket.io')(server);
var Message = require('./models/message')
var users = {}
io.on('connection', function (socket) {
//監聽用戶發布聊天內容
socket.on('message', function (obj) {
//向所有客戶端廣播發布的消息
io.emit('message', obj)
var mess = {
username: obj.username,
src:obj.src,
msg: obj.msg,
roomid:'room1'
}
var message = new Message(mess)
//將發送過來的消息進行儲存
message.save(function (err, mess) {
if (err) {
console.log(err)
}
console.log(mess)
})
console.log(obj.username + '說:' + obj.msg)
})
socket.on('login',function (obj) {
users[obj.name] = obj
//用於監聽用戶進行聊天室
io.emit('login', users)
})
socket.on('logout',function (name) {
delete users[name]
//用戶監聽用退出聊天室
io.emit('logout', users)
})
})
//聲明靜態資源地址
app.use(express.static('./dist'));
schema模型
schema/user.js
var mongoose = require('mongoose')
//用於md5加密
var bcrypt = require('bcryptjs')
//加鹽數
var SALT_WORK_FACTOR = 10
var UserSchema = new mongoose.Schema({
name: {
unique: true,
type: String
},
password: String,
src: String,
meta: {
createAt: {
type: Date,
default: Date.now()
},
updateAt: {
type: Date,
default: Date.now()
}
}
});
//對密碼進行加密
UserSchema.pre('save', function (next) {
var user = this
if (this.isNew) {
this.createAt = this.updateAt = Date.now()
}
else {
this.updateAt = Date.now()
}
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err)
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err)
user.password = hash
next()
})
})
})
//用於比較密碼是否正確
UserSchema.methods = {
comparePassword: function (_password, cb) {
bcrypt.compare(_password, this.password, function (err, isMatch) {
if (err) return cb(err)
cb(null, isMatch)
})
}
}
UserSchema.statics = {
fetch: function (cb) {
return this
.find({})
.sort('meta.updateAt')
.exec(cb)
},
findById: function (id, cb) {
return this
.findOne({_id: id})
.exec(cb)
}
}
module.exports = UserSchema
這里主要用到一個md5的模塊,可以查看 bcryptjs
schema/message.js
var mongoose = require('mongoose')
//聊天記錄模型
var MessageSchema = new mongoose.Schema({
username: String,
src:String,
msg: String,
roomid:String,
time: {
type: Date,
default: Date.now()
}
})
//靜態方法
MessageSchema.statics = {
fetch: function (cb) {
return this
.find({})
.sort('time')
.exec(cb)
},
findById: function (id, cb) {
return this
.findOne({_id: id})
.exec(cb)
}
}
module.exports = MessageSchema
服務器的routes
config/routes.js 講一個注冊的把,其他請前往項目查看
app.post('/user/signup', function (req, res) {
//獲取提交數據
var _user = req.body
console.log(_user)
User.findOne({name: _user.name}, function (err, user) {
if (err) {
console.log(err)
}
if (user) {
res.json({
errno: 1,
data: '用戶名已存在'
})
} else {
var user = new User(_user)
user.save(function (err, user) {
if (err) {
console.log(err)
}
res.json({
errno: 0,
data: '注冊成功'
})
})
}
})
})
主要用於驗證用戶名是否重復。
核心部分就講到這里啦,。其他具體的請查看我的github地址(具有詳細的注釋,不明白的可以提問,需要改進的請各位前輩指出):
地址:https://github.com/hua1995116/webchat
在線觀看地址:http://www.qiufengh.com:8081/#/
npm install -----安裝依賴
npm run dev -----運行
npm run build -----打包
node prod.server.js -----打包后運行
//記得替換
Index.vue和Chat.vue下的io.connect('http://qiufengh.com:8081')
http://qiufengh.com:8081改成自己的項目地址。
最后上幾張圖。




