最开始写时也只是打算使用原本的方法,各个页面与后端直接交互数据,后来发现很麻烦,而且在先不写后端的情况下,我们每周汇报时要展示成果,也必须有数据,所以最终就选择了vuex管理前端数据。上一周写的那些界面的js部分都是用来与vuex收发数据的,具体代码在第十周的博客,都是一些流程性的固定的东西。接下来展示存储在store.js里的东西
项目结构
src/
├── assets/ # 资源文件夹
│ └── chat/ # 聊天相关资源
│ └── images/ # 图片资源
├── components/ # 组件文件夹
│ └── Chat.vue # 聊天组件
├── router.js # 路由配置文件
├── store.js # Vuex 状态管理文件
├── WebSocketService.js # WebSocket 服务
└── EventBus.js # 事件总线
vuex简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它通过集中式存储管理应用的所有组件的状态,并以规则保证状态以一种可预测的方式发生变化。Vuex 主要用于中大型单页面应用(SPA),可以有效管理共享状态,解决跨组件通信复杂的问题。
核心概念
-
State(状态):
- Vuex 使用单一状态树,即一个对象包含了全部的应用层级状态。这个状态树作为唯一的数据源,非常便于追踪和调试。
- 组件可以通过
this.$store.state
访问状态。
-
Getters(获取器):
- Getters 类似于组件的计算属性,允许从 state 派生出一些状态。它们可以缓存并在依赖的状态发生变化时重新计算。
- 组件可以通过
this.$store.getters
访问 getters。
-
Mutations(突变):
- Mutations 是同步函数,用于修改 Vuex 的 state。每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler)。
- 组件可以通过
this.$store.commit('mutationType', payload)
触发 mutations。
-
Actions(动作):
- Actions 类似于 mutations,不同在于:
- Actions 提交的是 mutations,而不是直接变更状态。
- Actions 可以包含任意异步操作。
- 组件可以通过
this.$store.dispatch('actionType', payload)
触发 actions。
- Actions 类似于 mutations,不同在于:
-
Modules(模块):
- 当应用变得非常复杂时,可以将 Vuex 的 store 分割成模块(module)。每个模块拥有自己的 state、mutations、actions 和 getters。
我的代码
state
const store = new Vuex.Store({
state: {
// 输入的搜索值
searchText: '',
// 当前登录用户
user: {
name: '',
img: '',
},
// 对话好友列表
chatlist: [
],
// 好友列表
friendlist: [
{
id: 0,
wxid: "", //微信号
initial: '新的朋友', //姓名首字母
img: require('@/assets/chat/images/1.jpg'), //头像
signature: "", //个性签名
nickname: "新的朋友", //昵称
sex: 0, //性别 1为男,0为女
remark: "新的朋友", //备注
area: "", //地区
},
{
id: 1,
wxid: "AmorAres-", //微信号
initial: 'A', //姓名首字母
img: require('@/assets/chat/images/1.jpg'), //头像
signature: "每天我都很快乐", //个性签名
nickname: "Amor", //昵称
sex: 0, //性别 1为男,0为女
remark: "Amor", //备注
area: "浙江 宁波", //地区
},
{
id: 2,
wxid: "fly",
initial: 'B',
img: require('@/assets/chat/images/1.jpg'),
signature: "你不知道的js",
nickname: "fly",
sex: 1,
remark: "大飞",
area: "奥地利 布尔根兰",
}
],
//医生列表
doctor: [
{
id: 9,
wxid: "Seto_L",
img: require('@/assets/chat/images/4.jpg'),
signature: "自强不息",
nickname: "王医生",
sex: 1,
remark: "王",
area: "北京",
},
{
id: 10,
wxid: "hj960503",
img: require('@/assets/chat/images/3.jpg'),
signature: "哈哈哈",
nickname: "李医生",
sex: 1,
remark: "YYY",
area: "大连",
}
],
//emoji表情
emojis: [
{ file: '100.gif', code: '/::)', title: '微笑', reg: /\/::\)/g },
{ file: '101.gif', code: '/::~', title: '伤心', reg: /\/::~/g },
{ file: '102.gif', code: '/::B', title: '美女', reg: /\/::B/g },
{ file: '103.gif', code: '/::|', title: '发呆', reg: /\/::\|/g },
{ file: '104.gif', code: '/:8-)', title: '墨镜', reg: /\/:8-\)/g },
{ file: '105.gif', code: '/::<', title: '哭', reg: /\/::</g },
{ file: '106.gif', code: '/::$', title: '羞', reg: /\/::\$/g },
{ file: '107.gif', code: '/::X', title: '哑', reg: /\/::X/g },
{ file: '108.gif', code: '/::Z', title: '睡', reg: /\/::Z/g },
{ file: '109.gif', code: '/::\'(', title: '哭', reg: /\/::'\(/g },
{ file: '110.gif', code: '/::-|', title: '囧', reg: /\/::-\|/g },
{ file: '111.gif', code: '/::@', title: '怒', reg: /\/::@/g },
{ file: '112.gif', code: '/::P', title: '调皮', reg: /\/::P/g },
{ file: '113.gif', code: '/::D', title: '笑', reg: /\/::D/g },
{ file: '114.gif', code: '/::O', title: '惊讶', reg: /\/::O/g },
{ file: '115.gif', code: '/::(', title: '难过', reg: /\/::\(/g },
{ file: '116.gif', code: '/::+', title: '酷', reg: /\/::\+/g },
{ file: '117.gif', code: '/:--b', title: '汗', reg: /\/:--b/g },
{ file: '118.gif', code: '/::Q', title: '抓狂', reg: /\/::Q/g },
{ file: '119.gif', code: '/::T', title: '吐', reg: /\/::T/g },
{ file: '120.gif', code: '/:,@P', title: '笑', reg: /\/:,@P/g },
{ file: '121.gif', code: '/:,@-D', title: '快乐', reg: /\/:,@-D/g },
{ file: '122.gif', code: '/::d', title: '奇', reg: /\/::d/g },
{ file: '123.gif', code: '/:,@o', title: '傲', reg: /\/:,@o/g },
{ file: '124.gif', code: '/::g', title: '饿', reg: /\/::g/g },
{ file: '125.gif', code: '/:|-)', title: '累', reg: /\/:\|-\)/g },
{ file: '126.gif', code: '/::!', title: '吓', reg: /\/::!/g },
{ file: '127.gif', code: '/::L', title: '汗', reg: /\/::L/g },
{ file: '128.gif', code: '/::>', title: '高兴', reg: /\/::>/g },
{ file: '129.gif', code: '/::,@', title: '闲', reg: /\/::,@/g },
{ file: '130.gif', code: '/:,@f', title: '努力', reg: /\/:,@f/g },
{ file: '131.gif', code: '/::-S', title: '骂', reg: /\/::-S/g },
{ file: '133.gif', code: '/:,@x', title: '秘密', reg: /\/:,@x/g },
{ file: '134.gif', code: '/:,@@', title: '乱', reg: /\/:,@@/g },
{ file: '135.gif', code: '/::8', title: '疯', reg: /\/::8/g },
{ file: '136.gif', code: '/:,@!', title: '哀', reg: /\/:,@!/g },
{ file: '137.gif', code: '/:!!!', title: '鬼', reg: /\/:!!!/g },
{ file: '138.gif', code: '/:xx', title: '打击', reg: /\/:xx/g },
{ file: '139.gif', code: '/:bye', title: 'bye', reg: /\/:bye/g },
{ file: '142.gif', code: '/:handclap', title: '鼓掌', reg: /\/:handclap/g },
{ file: '145.gif', code: '/:<@', title: '什么', reg: /\/:<@/g },
{ file: '147.gif', code: '/::-O', title: '累', reg: /\/::-O/g },
{ file: '153.gif', code: '/:@x', title: '吓', reg: /\/:@x/g },
{ file: '155.gif', code: '/:pd', title: '刀', reg: /\/:pd/g },
{ file: '156.gif', code: '/:<W>', title: '水果', reg: /\/:<W>/g },
{ file: '157.gif', code: '/:beer', title: '酒', reg: /\/:beer/g },
{ file: '158.gif', code: '/:basketb', title: '篮球', reg: /\/:basketb/g },
{ file: '159.gif', code: '/:oo', title: '乒乓', reg: /\/:oo/g },
{ file: '195.gif', code: '/:circle', title: '跳舞', reg: /\/:circle/g },
{ file: '160.gif', code: '/:coffee', title: '咖啡', reg: /\/:coffee/g }
],
icon: [
"😀",
"😃",
"😄",
"😁",
"😆",
"😅",
"🤣",
"😂",
"🙂",
"🙃",
"😉",
"😊",
"😇",
"😍",
"🤩",
"😘",
"😗",
"😚",
"😙",
"😋",
"😛",
"😜",
"🤪",
"😝",
"🤑",
"🤗",
"🤭",
"🤫",
"🤔",
"🤐",
"🤨",
"😐",
"😑",
"😶",
"😏",
"😒",
"🙄",
"😬",
"🤥",
"😌",
"😔",
"😪",
"🤤",
"😴",
"😷",
"🤒",
"🤕",
"🤢",
"🤮",
"🤧",
"😵",
"🤯",
"🤠",
"😎",
"🤓",
"🧐",
"😕",
"😟",
"🙁",
"😮",
"😯",
"😲",
"😳",
"😦",
"😧",
"😨",
"😰",
"😥",
"😢",
"😭",
"😱",
"😖",
"😣",
"😞",
"😓",
"😩",
"😫",
"😤",
"😡",
"😠",
"🤬",
"😈",
"👿",
"💀",
"💩",
"🤡",
"👹",
"👺",
"👻",
"👽",
"👾",
"🤖",
"😺",
"😸",
"😹",
"😻",
"😼",
"😽",
"🙀",
"😿",
"😾",
"💋",
"👋",
"🤚",
"🖐",
"✋",
"🖖",
"👌",
"🤞",
"🤟",
"🤘",
"🤙",
"👈",
"👉",
"👆",
"🖕",
"👇",
"👍",
"👎",
"✊",
"👊",
"🤛",
"🤜",
"👏",
"🙌",
"👐",
"🤲",
"🤝",
"🙏",
"💅",
"🤳",
"💪",
"👂",
"👃",
"🧠",
"👀",
"👁",
"👅",
"👄",
"👶",
"🧒",
"👦",
"👧",
"🧑",
"👱",
"👨",
"🧔",
"👱",
"👨",
"👨",
"👨",
"👨",
"👩",
"👱",
"👩",
"👩",
"👩",
"👩",
"🧓",
"👴",
"👵",
"🙍",
"🙍",
"🙍",
"🙎",
"🙎",
"🙎",
"🙅",
"🙅",
"🙅",
"🙆",
"🙆",
"🙆",
"💁",
"💁",
"💁",
"🙋",
"🙋",
"🙋",
"🙇",
"🙇",
"🙇",
"🤦",
"🤦",
"🤦",
"🤷",
"🤷",
"🤷",
"👨⚕️",
"👩⚕️",
"👨🎓",
"👩🎓",
"👨🏫",
"👩🏫",
"👨⚖️",
"👩⚖️",
"👨🌾",
"👩🌾",
"👨🍳",
"👩🍳",
"👨🔧",
"👩🔧",
"👨🏭",
"👩🏭",
"👨💼",
"👩💼",
"👨🔬",
"👩🔬",
"👨💻",
"👩💻",
"👨🎤",
"👩🎤",
"👨🎨",
"👩🎨",
"👨✈️",
"👩✈️",
"👨🚀",
"👩🚀",
"👨🚒",
"👩🚒",
"👮",
"👮♂️",
"👮♀️",
"🕵",
"🕵️♂️",
"🕵️♀️",
"💂",
"💂",
"💂",
"👷",
"👷",
"👷",
"🤴",
"👸",
"👳",
"👳",
"👳",
"👲",
"🧕",
"🤵",
"👰",
"🤰",
"🤱",
"👼",
"🎅",
"🤶",
"🧙",
"🧙",
"🧙",
"🧚",
"🧚",
"🧚",
"🧛",
"🧛",
"🧛",
"🧜",
"🧜",
"🧜",
"🧝",
"🧝",
"🧝",
"🧞",
"🧞",
"🧞",
"🧟",
"🧟",
"🧟",
"💆",
"💆",
"💆",
"💇",
"💇",
"💇",
"🚶",
"🚶",
"🚶",
"🏃",
"🏃",
"🏃",
"💃",
"🕺",
"🕴",
"👯",
"👯",
"👯",
"🧖",
"🧖",
"🧖",
"🧘",
"👭",
"👫",
"👬",
"💏",
"👨",
"👩",
"💑",
"👨",
"👩",
"👪",
"👨👩👦",
"👨👩👧",
"👨👩👧👦",
"👨👩👦👦",
"👨👩👧👧",
"👨👨👦",
"👨👨👧",
"👨👨👧👦",
"👨👨👦👦",
"👨👨👧👧",
"👩👩👦",
"👩👩👧",
"👩👩👧👦",
"👩👩👦👦",
"👩👩👧👧",
"👨👦",
"👨👦👦",
"👨👧",
"👨👧👦",
"👨👧👧",
"👩👦",
"👩👦👦",
"👩👧",
"👩👧👦",
"👩👧👧",
"🗣",
"👤",
"👥",
"👣",
"🌂",
"☂",
"👓",
"🕶",
"👔",
"👕",
"👖",
"🧣",
"🧤",
"🧥",
"🧦",
"👗",
"👘",
"👙",
"👚",
"👛",
"👜",
"👝",
"🎒",
"👞",
"👟",
"👠",
"👡",
"👢",
"👑",
"👒",
"🎩",
"🎓",
"🧢",
"⛑",
"💄",
"💍",
"💼"
],
// 得知当前选择的是哪个对话
selectId: 1,
// 得知当前选择的是哪个好友
selectFriendId: 0,
//当前选择的是哪个医生
selectDoctorId: 0
},
主要是朋友列表,聊天列表,表情,还有几个变量负责控制等等
mutation
addMessagesToList(state, mes) {
console.log(state.user.uname)
// 遍历后端返回的消息列表,并添加到chatlist中
mes.forEach(message => {
if (message.fid !== state.user.id) {
state.chatlist.push({
id: message.fid, // 假设后端有fid字段
user: {
name: message.name,
img: message.imgUrl, // 假设后端有imgUrl字段
},
messages: message.messages.map(msg => ({
content: msg.content,
date: new Date(msg.time), // 假设后端返回的是时间字符串,转换为Date对象
self: msg.self
}))
});
}
});
},
setUsername(state) {
const user = JSON.parse(sessionStorage.getItem('user'));
state.user.uname = user.uname;
console.log(state.user.uname)
},
// 设置好友列表
addMessage(state, mes) {
state.chatlist.push(mes);
},
addChatMessage(state, msg) {
let result = state.chatlist.find(session => session.user.name === msg.from);
result.messages.push({
content: msg.text,
date: new Date(),
self: false
})
},
// 从localStorage 中获取数据
//initData(state) {
//state.user = [nk]
/// let data = localStorage.getItem('vue-chat');
//if (data) {
// state.chatlist = JSON.parse(data);
/// }
// },
// 获取搜索值
search(state, value) {
state.searchText = value
},
// 得知用户当前选择的是哪个对话。便于匹配对应的对话框
selectSession(state, value) {
state.selectId = value
},
// 得知用户当前选择的是哪个好友。
selectFriend(state, value) {
state.selectFriendId = value
},
// 得知用户当前选择的是哪个医生。
selectDoctor(state, value) {
state.selectDoctorId = value
},
// 发送信息
sendMessage(state, msg) {
let result = state.chatlist.find(session => session.id === state.selectId);
const user = JSON.parse(localStorage.getItem('user'));
let message = { from: user.uname, to: result.user.name, content: msg.content }
WebSocketService.sendMessage(message);
result.messages.push({
content: msg.content,
date: new Date(),
self: true
});
axios.post('http://localhost:9966/chat/saveMessage', {
from: user.uname,
to: result.user.name,
message: msg.content
}, {
headers: {
'Content-Type': 'application/json'
}
}).catch(error => {
console.log(error)
});
},
// 选择好友后,点击发送信息。判断在聊天列表中是否有该好友,有的话跳到该好友对话。没有的话
// 添加该好友的对话 并置顶
send(state) {
let result = state.friendlist.find(friend => friend.id === state.selectFriendId)
let msg = state.chatlist.find(msg => msg.user.name === result.remark)
if (!msg) {
state.selectId = 1
for (let i = 0; i < state.chatlist.length; i++) {
state.chatlist[i].id++;
state.chatlist[i].index++;
}
state.chatlist.unshift({
id: 1,
user: {
name: result.remark,
img: result.img
},
messages: [
{
content: '已经置顶聊天,可以给我发信息啦!',
date: new Date()
}
],
index: 1
})
} else {
state.selectId = msg.index
router.push({ path: '/friends' })
}
},
//医生
sendDo(state) {
let result = state.doctor.find(doctor => doctor.id === state.selectDoctorId)
let msg = state.chatlist.find(msg => msg.user.name === result.remark)
if (!msg) {
state.selectId = 1
for (let i = 0; i < state.chatlist.length; i++) {
state.chatlist[i].id++;
state.chatlist[i].index++;
}
state.chatlist.unshift({
id: 1,
user: {
name: result.remark,
img: result.img
},
messages: [
{
content: '已经置顶聊天,可以给我发信息啦!',
date: new Date()
}
],
index: 1
})
} else {
state.selectId = msg.index
router.push({ path: '/friends' })
}
}
基本上每个函数上面都有注释,我就不赘述了。简单的提几个函数,一个是接收webSocket返回数据的函数addMessagesToList,这个用来接收每次聊天的返回值,并用来push进message列表
一个是send(), 这个函数用来当点击朋友时,如果已经创建聊天,它会自动跳转到聊天,否则它自动创建一个新聊天。
getters
getters: {
// 筛选出含有搜索值的聊天列表
searchedChatlist(state) {
let sessions = state.chatlist.filter(sessions => sessions.user.name.includes(state.searchText));
return sessions
},
// 筛选出含有搜索值的好友列表
searchedFriendlist(state) {
let friends = state.friendlist.filter(friends => friends.remark.includes(state.searchText));
return friends
},
// 筛选出含有搜索值的医生列表
searchedDoctor(state) {
let doctor = state.doctor.filter(doctor => doctor.remark.includes(state.searchText));
return doctor
},
// 通过当前选择是哪个对话匹配相应的对话
selectedChat(state) {
let session = state.chatlist.find(session => session.id === state.selectId);
return session
},
// 通过当前选择是哪个好友匹配相应的好友
selectedFriend(state) {
let friend = state.friendlist.find(friend => friend.id === state.selectFriendId);
return friend
},
// 通过当前选择是哪个好友匹配相应的医生
selectedDoctor(state) {
let doctor = state.doctor.find(doctor => doctor.id === state.selectDoctorId);
return doctor
},
messages(state) {
let session = state.chatlist.find(session => session.id === state.selectId);
return session.messages
}
},
actions
search: ({ commit }, value) => {
setTimeout(() => {
commit('search', value)
}, 100)
},
selectSession: ({ commit }, value) => commit('selectSession', value),
selectFriend: ({ commit }, value) => commit('selectFriend', value),
selectDoctor: ({ commit }, value) => commit('selectDoctor', value),
sendMessage: ({ commit }, msg) => commit('sendMessage', msg),
send: ({ commit }) => commit('send'),
sendDo: ({ commit }) => commit('sendDo'),
这两部分都没什么好说的,固定套路
监听
用来监听当前选中对话
// 监听聊天列表的值, 发生变化就保存在localStorage中
store.watch(
(state) => state.chatlist,
(val) => {
localStorage.setItem('vue-chat', JSON.stringify(val));
},
{
deep: true
}
)