前言: 这个篇章是这个项目的核心点,我会尽可能的在核心部分详细且准确,你跟着我的思路的话主要去理解数据的流向,不然你会在socket.io这块接入的时候感觉到一筹莫展。这个小篇章不会讲述到socket.io的使用,而是先搭好这部分的整体框架(使用vuex),还有各种的交互事件等等。然后我们慢慢的去实现这些功能。
chat页面代码如下
<template>
<!-- 总容器 -->
<view class="char-container" id="">
<!-- 聊天头部导航栏 -->
<view class="char-top-bar" id="topbar">
<view class="char-top-bar-left">
<image src="../../common/images/back.png" class="autoImg" mode="" @click="goBack"></image>
</view>
<view class="char-top-bar-center" >
<text class="name">洒家打开拉</text>
</view>
<view class="char-top-bar-right">
<image src="../../common/images/kana/11.jpg" class="autoImg" mode=""></image>
</view>
</view>
<view style="height: 88rpx;"></view>
<scroll-view id="scrollview" scroll-y="true" style="height: 100%;" >
<view id="chatC" class="chat-infor-container" >
<!-- 朋友聊天 -->
<view class="friends-char-informations-container" >
<view class="time-container">
<text class="time">2020/10/12</text>
</view>
<view class="info">
<view class="username">
帅哥1号
</view>
<view class="left">
<image src="../../common/images/kana/12.jpg" class="autoImg" mode=""></image>
</view>
<view class="right" >
<text class="informations">你在干嘛~?</text>
</view>
</view>
</view>
<!-- 用户聊天 -->
<view class="user-char-informaations-container" >
<view class="time-container" >
<text class="time">2020/11/11</text>
</view>
<view class="info">
<view class="username">
帅哥2号
</view>
<!-- 文本与表情 -->
<view class="left">
<text class="informations">别叫了,再叫就烦了</text>
</view>
<view class="right">
<image src="../../common/images/kana/12.jpg" class="autoImg" mode=""></image>
</view>
</view>
</view>
<!-- 感觉上图片的插入应该使用脚本比较好 -->
</view>
<view :style="{height: addHeight}"></view>
</scroll-view>
<!-- 底部容器 -->
<view class="bottom-all-container">
<!-- 底部表情 即输入框 -->
<view class="char-bottom-container" id="char_bottom">
<view class="left" >
<image src="../../common/images/jp.png" @click="talk" class="autoImg" mode=""></image>
</view>
<!-- <view class="left" >
<image src="../../common/images/talk.png" @click="talk" class="autoImg" mode=""></image>
</view> -->
<view class="center" >
<input type="text" class="inputText" value="" v-model="chatmsg" />
<!-- <button type="default" >按住说话</button> -->
</view>
<view class="right">
<image src="../../common/images/emoji.png" @click="clickEmoji" class="emoji" mode=""></image>
<image src="../../common/images/add.png" @click="clickMore" class="add" mode=""></image>
</view>
</view>
<!-- 点击图标加载表情 -->
<!-- <view class="emojiBlock" v-show="emojiStatus">
<swiper :indicator-dots="true" :duration="1000">
<swiper-item >
<view class="emojiList" v-for="(item,index) in emoji.slice(0,3)" :key="index">
<view class="emoji" v-for="(one,index) in item" :key="index" @click="addEmojiToInputs(one)">
{{one}}
</view>
</view>
</swiper-item>
<swiper-item>
<view class="emojiList" v-for="(item,index) in emoji.slice(3,6)" :key="index">
<view class="emoji" v-for="(one,index) in item" :key="index" @click="addEmojiToInputs(one)">
{{one}}
</view>
</view>
</swiper-item>
<swiper-item>
<view class="emojiList" v-for="(item,index) in emoji.slice(6,)" :key="index">
<view class="emoji" v-for="(one,index) in item" :key="index" @click="addEmojiToInputs(one)">
{{one}}
</view>
</view>
</swiper-item>
</swiper>
</view> -->
<!-- 点击图标加载更多 -->
<!-- 本来以为列表能省事些,发现并不行,因为我们得给每个不同的设置一个事件 -->
<!-- 但后来想想 用个中转站处理事件就行,就懒得改了。 -->
<!-- <view class="more" v-show="moreStatus">
<view class="container" v-for="(item,index) in more" :key="index" @click="Transfer(index)">
<view class="content">
<image :src="item.imgurl" class="autoImg"></image>
</view>
<view class="text">
{{item.descText}}
</view>
</view>
</view> -->
</view>
<!-- 用于撑开该脱离文档流的高度 -->
<!-- 这个应该要跟随点击时的高度变化 -->
<!-- 内容容器 -->
</view>
</template>
<script>
import transTime from '../../common/js/transTimestamp.js'
export default {
data() {
return {
}
},
methods: {
},
onLoad: function(){
}
}
</script>
<style lang="scss" scoped>
.autoImg {
height: 100%;
width: 100%;
overflow: hidden;
}
.char-container {
min-height: 100vh;
width: 100%;
background-color: #f4f4f4;
.char-top-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 88rpx;
padding: 0 32rpx;
background-color: #f4f4f4;
z-index: 999;
box-sizing: border-box;
display: flex;
justify-content: space-between;
border-bottom: 1px solid $uni-border-color;
.char-top-bar-left {
height: 48rpx;
width: 48rpx;
padding: 20rpx 0;
}
.char-top-bar-center {
padding: 16rpx 0;
font-family: PingFangSC-Regular;
font-size: 20px;
color: $uni-color-black;
line-height: 56rpx;
letter-spacing: -0.69px;
}
.char-top-bar-right {
padding: 8rpx 0 12rpx 0;
border-radius: 16rpx;
height: 68rpx;
width: 68rpx;
}
}
.chat-infor-container {
padding: 40rpx 32rpx 0 32rpx;
.friends-char-informations-container {
display: flex;
flex-direction: column;
.time-container {
padding: 10rpx 0;
text-align: center;
.time {
color: $uni-color-grey;
font-size: 24rpx;
line-height: 34rpx;
letter-spacing: -0.82rpx;
}
}
.info {
display: flex;
position: relative;
.left {
width: 80rpx;
height: 80rpx;
}
.username{
position: absolute;
font-size: 24rpx;
color: $uni-color-grey;
top: -40rpx;
left: 0;
max-width: 100rpx;
}
.right {
margin-left: 16rpx;
max-width: 480rpx;
background-color: #ffffff;
border-radius: 0px 10px 10px 10px;
.informations {
display: inline-block;
padding: 16rpx 22rpx 16rpx 24rpx;
font-family: PingFangSC-Regular;
font-size: 32rpx;
color: #272832;
text-overflow: ellipsis;
letter-spacing: -0.55px;
}
}
.img{
margin-left: 16rpx;
height: 320rpx;
width: 284rpx;
image{
border-radius: 20rpx;
}
}
}
}
}
.user-char-informaations-container {
display: flex;
flex-direction: column;
.time-container {
padding: 10rpx 0;
text-align: center;
.time {
color: $uni-color-grey;
font-size: 24rpx;
line-height: 34rpx;
letter-spacing: -0.82rpx;
}
}
.info {
display: flex;
justify-content: flex-end;
position: relative;
.username{
position: absolute;
font-size: 24rpx;
color: $uni-color-grey;
top: -40rpx;
right: 0;
max-width: 100rpx;
}
.left {
display: inline-block;
max-width: 480rpx;
margin-right: 16rpx;
border-radius: 10px 0px 10px 10px;
background-color: $uni-color-common;
.informations {
display: inline-block;
padding: 16rpx 22rpx 16rpx 24rpx;
font-family: PingFangSC-Regular;
font-size: 32rpx;
color: #272832;
text-overflow: ellipsis;
letter-spacing: -0.55px;
}
}
.right {
width: 80rpx;
height: 80rpx;
}
.img{
margin-right: 16rpx;
height: 320rpx;
width: 284rpx;
image{
border-radius: 20rpx;
}
}
.map{
margin-right: 16rpx;
}
.video{
margin-right: 16rpx;
}
}
}
.bottom-all-container {
display: flex;
flex-direction: column;
position: fixed;
bottom:0;
left: 0;
width: 100%;
padding: 0 32rpx;
box-sizing: border-box;
z-index: 999;
.char-bottom-container {
display: flex;
justify-content: space-between;
background-color: #f4f4f4;
z-index: 999;
.left {
width: 56rpx;
height: 56rpx;
padding: 24rpx 0 18rpx;
}
.center {
width: 454rpx;
padding: 14rpx 20rpx 12rpx 20rpx;
.inputText {
height: 72rpx;
background-color: #ffffff;
border-radius: 10rpx;
}
button {
height: 72rpx;
line-height: 72rpx;
text-align: center;
font-weight: bold;
background-color: #ffffff;
border-radius: 10rpx;
}
}
.right {
display: flex;
image {
width: 56rpx;
height: 56rpx;
}
.add {
padding: 24rpx 0 18rpx;
}
.emoji {
padding: 24rpx 24rpx 18rpx 0;
}
}
}
.emojiBlock {
background-color: #f4f4f4;
z-index: 999;
height: 400rpx;
width: 100%;
.emojiList{
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
.emoji{
width: 12.5%;
font-size: 30px;
}
}
}
.more{
background-color: #f4f4f4;
z-index: 999;
height: 400rpx;
width: 100%;
display: flex;
flex-wrap: wrap;
.container{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 224rpx;
box-sizing: border-box;
padding: 20rpx;
.content{
height: 120rpx;
width: 120rpx;
}
.text{
height: 30rpx;
font-size: 24rpx;
color: $uni-color-shen-grey;
line-height: 30rpx;
}
}
}
}
}
</style>
首先先渲染页面
我们先在首页的页面中对发来的消息添加点击事件
把当前item的user_id 传进这个页面,我们需要这个ID去进行渲染页面,同时还有索引(这个往下看 它的用处)。
到这里的时候我们应该意识到我们目前的项目的结构很大可能需要在聊天页面进行两次查询(对用户头像,朋友头像名字等)所以在这一点上我们应该开始意识到原有的结构会使这个项目变得缓慢。顺理成章的,我们开始思考有没有什么方法可以存储这个用户的头像名字等状态呢?这样子我们就可以省去重复的请求,一方面提高了性能,一方面也可以少写些代码,省去重复冗余的步骤。
至此,引入vuex进行状态管理势在必行,我们通过在index页面存储这些状态,当我们点击的时候,再通过当前的索引去获取我们想要的那个人的头像名字等信息。
引入vuex
main.js中将它挂载,同时绑定到vue实例中
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。我们是通过全局的store变量去提交这个状态的改变,mutation是同步的,所以我们必须在它前面加上await 使他 ‘同步’(await并不是真正意义上的同步,我们使用async声明了函数,这个函数就是异步执行的了),这里不再赘述,我只会把实现所用到的知识讲解,更多的请上官方文档自行学习。
如法炮制,index页面里也把用户的信息请求出来,保存到vuex中吧。
当你的store实例moduleIndex模块中的状态如上,我们的前期准备就已经完成了。接下来可以点击进去我们的chat页面进行数据渲染了。前端代码如下:
后端如下:
至此就只是页面渲染的问题了,学完此篇你的vuex已经入门了(能保存状态,使用这个状态),之后的vuex除非遇到新的使用方式都不再赘述。
这里说明下,左右显示的状态需要比对设置,所以我们无论如何也得使用一个for循环去操作这个渲染数组实现。
好了,进入下一个篇章把。