2021-09-09 黑马移动端头条项目-day10

目录

小爱同学模块 

一、WebSocket介绍

二、使用原生WebSocket(了解)

三、Socket.IO(了解)

3.1 介绍

3.2 基本使用

3.3 总结

四、小爱同学

4.1 准备

4.2 布局

4.3 建立连接 

4.4 收发消息并展示消息列表

4.5 消息列表自动滚动到底部

 功能优化补充:

1.组件缓存

1.1 介绍

1.2 使用keep-alive缓存组件

1.3 解决缓存带来的滚动问题

 2.处理token过期 ***

 3.登录成功跳转回原来的位置

4.统一处理页面访问权限

5.打包移动APP


小爱同学模块 

一、WebSocket介绍

WebSocket是一种数据通信协议,也是用于客户端和服务端数据通信,类似于常见的http。

但是http通信是单向的,即请求+响应,没有请求就没有响应,并且通信只能由客户端发起

,服务端返回查询结果,http做不到服务器向客户端推送信息。

如果服务器有了连续的状态变化,客户端要获知比较麻烦,只能使用“轮询”,每隔一段时间就发出一个询问去了解服务器有没有新的消息。“轮询”效率低,浪费资源,因为需要不停连接或者http连接始终打开。

WebSocket

服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话

ws://exmaple.com:80/some/path

二、使用原生WebSocket(了解)

三、Socket.IO(了解)

3.1 介绍

原生的WebSocket使用比较麻烦,推荐使用封装好的解决方案:socket.io

socket.io提供了服务端+客户端的实现

  • 客户端:浏览器
  • 服务端:JAVA PYTHON PHP .... NODE.JS

对于前端开发者来说只需要关心它的客户端即可

注意:socket.io必须前后端配套使用

3.2 基本使用

安装: npm i socket.io-client

加载:

  import io from 'socket.io-client'
  const socket = io('http://localhost')

3.3 总结

四、小爱同学

4.1 准备

新建user-chat/index.vue

配置路由

// 小爱同学
  {
    path: '/user/chat',
    name: 'user-chat',
    component: () => import('@/views/user-chat/')
  }

4.2 布局

<template>
  <div class="user-chat">
    <!-- 导航栏 -->
    <van-nav-bar class="page-nav-bar" title="小爱同学"> </van-nav-bar>
    <!-- 消息列表 -->
    <van-cell-group class="message-list">
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
      <van-cell title="单元格"> </van-cell>
    </van-cell-group>
    <!-- 发送消息 -->
    <van-cell-group class="send-message-wrap">
      <van-field v-model="message" placeholder="请输入消息" :border="false">
      </van-field>
      <van-button type="primary" size="small">发送</van-button>
    </van-cell-group>
  </div>
</template>
<script>
export default {
  name: "index",
  components: {},
  props: {},
  data() {
    return {
      message: ""
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {}
};
</script>
<style scoped lang="less">
.message-list {
  position: fixed;
  left: 0;
  right: 0;
  top: 1.3rem;
  bottom: 1.2rem;
  overflow-y: auto;
}
.send-message-wrap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  padding: 0 14px;
  align-items: center;
}
</style>

4.3 建立连接 

  created() {
    // 建立连接的通讯地址
    const socket = io("http://ttapi.research.itcast.cn");
    this.socket = socket;
    socket.on("connect", () => {
      console.log(连接建立成功);
    });
    // 断开连接
    socket.on("disconnect", () => {
      console.log(断开连接);
    });
  },

4.4 收发消息并展示消息列表

把用户发出去的消息存储到数组中并清空输入框

        this.messages.push(data);

        this.message = "";        

把对方发给我的消息放到数组中  this.messages.push(data);

数据在页面上展示遍历

    <van-cell
        :title="item.msg"
        v-for="(item, index) in messages"
        :key="index"
      >
      </van-cell>

数据存储到本地

import { getItem, setItem } from "@/utils/storage";

data初始化数据

messages: getItem("chat-message") || [] // 消息列表

监听数据是否有变化 

watch: {
    // 监听到改变的时候存储到本地存储
    messages() {
      setItem("chat-message", this.messages);
    }
  },

4.5 消息列表自动滚动到底部

给标签绑定一个ref属性

 ref="message-list"

在监听到数据有变化时调用scrollTopBottom方法,如果要在操作数据之后立即操作数据影响的视图DOM,最好把代码放到nextTick函数中,因为数据改变影响视图更新这件事并不是立即发生的

    messages() {
      setItem("chat-message", this.messages);
      // 如果要在操作数据之后立即操作数据影响的视图DOM
      // 最好把代码放到nextTick函数中
      // 数据改变影响视图更新这件事并不是立即发生的
      this.$nextTick(() => {
        this.scrollTopBottom();
      });
    }

列表滚动到询问的方法,涉及到scrollTop和scrollHeight属性

    scrollTopBottom() {
      // 消息列表自动滚动到底部
      const list = this.$refs["message-list"];
      list.scrollTop = list.scrollHeight;
    }

效果如下:

 功能优化补充:

1.组件缓存

1.1 介绍

从首页切换到我的,再从我的回到首页,发现首页重新渲染原来的状态没有了,首先这是正常的状态,并非问题,路由在切换时候会销毁切出去的页面组件,然后渲染匹配到的页面组件,但是我想要某些页面保持状态,而不会随着路由切换导致重新渲染。

这里可以在App.vue里用keep-alive标签包裹住路由的出口标签<router-view />,router-view实际是动态组件,但这是只对一级路由有效。但keep-alive也会带来一些问题,如:

1.部分页面缓存后再次点击进去查看内容时,内容仍是上次的,但不同的列表项是有不同的内容的,这会导致一些生命周期无法重新渲染,如created;

2.部分页面缓存后点击进去查看内容时,再回退上一级时页面会自动定位到头部位置;实际上我想要的效果是从哪个列表项点击进去查看内容的,再退回时应该显示当前列表项位置,而不是直接显示顶部位置 

1.2 使用keep-alive缓存组件

1. 组件有条件缓存

若只缓存一级路由Layout组件,只需要给router-view加Inclue属性,值为数组,数组中的值是一个组件的name属性

    <keep-alive>
      <router-view :include="['LayoutIndex']" />
    </keep-alive>

2. 控制缓存及在项目中的缓存配置

用户在使用另一个账号进行登录页面时,登录成功后仍会显示上一个用户的个人信息,只有刷新过后才会显示新的账号的个人信息。而我们又想只缓存Layout组件中的数据,新用户登录后Layout组件里的数据也是上一个用户的。

因此我们先要在App.vue里进行有 条件缓存Layout组件中的数据,并引入store/index.js的vuex文件

<template>
  <div id="app">
    <!-- 路由的出口,一级路由,router-view是动态组件 -->
    <keep-alive>
      <router-view :include="['cachePages']" />
    </keep-alive>
  </div>
</template>

import { mapState } from "vuex";
computed: {
    ...mapState(["cachePages"])
  }

在store/index.js中设置添加缓存页面和移出缓存页面,state定义一个cachePages变量设为LayoutIndex组件,mutations中去修改state对象的值,addCachePage和removeCachePage方法

import Vue from 'vue'
import Vuex from 'vuex'
import { getItem, setItem } from '@/utils/storage'
Vue.use(Vuex)

const USER_KEY = 'toutiao-user'

export default new Vuex.Store({
  state: {
    // user: null,
    // 还原为对象
    // user: JSON.parse(window.localStorage.getItem('user')) // 当前登录用户的登录状态(token等数据)
    // 优化为:
    user: getItem(USER_KEY),

    cachePages: ['LayoutIndex']
  },
  // 修改state对象的值
  mutations: {
    // 参数:state对象,data所传入的参数
    setUser (state, data) {
      state.user = data

      // 容器中的数据不是持久化的,刷新后就不存在了
      // 为了防止页面刷新数据丢失,还需要把数据存储到本地存储中,这里仅仅是为了持久化数据
      // 对象state.user不能直接存入本地存储中,要转换为JSON字符串
      // window.localStorage.setItem('user', JSON.stringify(state.user))
      // 优化为:
      console.log(data)
      setItem(USER_KEY, state.user)
    },
    // 添加缓存页面
    addCachePage (state, pageName) {
      // 如果不包含缓存页面
      if (!state.cachePages.includes(pageName)) {
        state.cachePages.push(pageName)
      }
    },
    // 移出缓存页面
    removeCachePage (state, pageName) {
      // 如果包含缓存页面
      const index = state.cachePages.indexOf(pageName)
      if (index !== -1) {
        state.cachePages.splice(index, 1)
      }
    }
  },
  actions: {

  },
  modules: {}
})

在登录组件login/index.vue组件中,在用户登录成功后未跳转原来的页面之前需要清除layout组件的缓存,让它重新渲染

 this.$store.commit("removeCachePage", "LayoutIndex");

但是上方清除缓存后,新用户成功登录页面后LayoutIndex组件中的数据就不能缓存了,但是与我们原先想要的功能不一样了:每个用户登录账号后LayoutIndex组件都会有缓存,所以在layout/index.vue组件的mounted生命周期里添加缓存

 mounted() {
    this.$store.commit("addCachePage", "LayoutIndex");
  }

1.3 解决缓存带来的滚动问题

在article-list.vue中添加一个 ref="article-list"属性,页面加载mounted时记录到顶部的位置

mounted() {
    const articleList = this.$refs["article-list"];
    articleList.onscroll = debounce(() => {
      this.scrollTop = articleList.scrollTop;
    }, 50);
  },

keep-alive缓存时activated是激活状态,把记录的到顶部的距离重新设置回去

activated() {
    // 把记录的到顶部的距离重新设置回去
    this.$refs["article-list"].scrollTop = this.scrollTop;
  },

钩子函数:

 2.处理token过期 ***

token用于访问需要身份认证的普通接口,有效期2个小时,超过2小时就无效了,如果继续请求数据的话会报错 401,可以让用户重新登录 

 3.登录成功跳转回原来的位置

用户从哪个页面进来,再登录成功之后就回到哪个页面,原先用的 this.$router.back(); 实现的,但这个方式存在一个问题,若用户第一次访问时是从tab地址栏进来的,登录成功之后会回到原先的tab地址栏,如未登录状态下,当前我是从“我的”页面点击登录的,在登录成功之后仍然会跳转到“我的”页面,不像原先登录完后直接跳转到“首页”页面上了,此时tab地址栏会有路径记录

实现: 

修改utils/request.js文件 

function redirectLogin () {
  router.replace({
    name: '/login',
    // 传递查询参数,查询参数会以 ? 作为分隔符放到 url后面
    query: {
      // router.currentRoute 当前路由路径
      // fullPath 属性
      // 数据名是自己起的
      redirect: router.currentRoute.fullPath
    }
  })
}

 将原先login/index.vue中this.$router.back();替换为this.$router.push(this.$route.query.redirect || "/");

上述我是以“我的”页面为例的,所以将my/index.vue里的@click="$router.push('./login')"修改为

    @click="
          $router.push({
            name: 'login',
            query: {
              redirect: '/my'
            }
          })
        "

4.统一处理页面访问权限

需求:有的页面需要登录才能访问,有的不需要登录就可以访问 

1.给每个路由配置meta属性,用requiresAuth控制页面是否需要登录才能访问,requiresAuth是自定义的属性名,后面要做路由导航守卫的配置。这里让“小爱同学”在登录状态下才能访问为例

2.配置路由导航守卫

// 路由导航守卫
// to 要访问的页面路由信息
// from 来自哪个页面的路由信息
// next 旅行的标记
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth) {
    // 需要登录状态,提示用户
    // 如果已登录,则直接放行
    if (store.state.user) {
      return next()
    }
    // 没有登录则提示是否登录
    Dialog.confirm({
      title: '访问提示',
      message: '该功能需要登录才能访问,确认登录吗?'
    }).then(() => {
      // 确认执行这里
      router.replace({
        name: 'login',
        query: {
          redirect: router.currentRoute.fullPath
        }
      })
    }).catch(() => {
      // 取消执行这里,中断路由导航
      next(false)
    })
  } else {
    // 不需要登录则直接过去
    next()
  }
})

5.打包移动APP

1)下载安装

2)创建项目

3)真机调试支行

Android:

1.在手机的设置中找到并打开开发者选项

2.打开USB调试

3.使用数据线连接手机和电脑

4.在HBuildX菜单中:运行-运行到手机或模拟器-你的手机设备

备注:

笔记链接 一、课程介绍 · 语雀

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值