目录
一. 项目概述
二、项目初始化
移动端 REM 适配:
关于 PostCSS 配置文件:
Autoprefixer 插件的配置 :
postcss-pxtorem 插件的配置:
关于字体图标:
配置路由:
封装请求模块:
三:登录注册:
存储用户 Token:
关于 Token 过期问题:
四:个人中心
五、首页—文章列表:
关于第三方图片资源403问题:
处理时间:
六、首页—频道编辑:
频道数据持久化:
正确的获取首页频道列表数据图:
七、文章搜索
八、文章详情
关于后端返回数据中的大数字问题:
关于文章正文的样式:
九、文章评论
十、 编辑资料
图片裁切:
方式一:结合服务器方式
方式二:纯客户端方式
十一. 小智同学
WebSocket:
使用原生 WebSocket(了解):
Socket.IO(了解体验):
十二: 功能优化
组件缓存:
响应拦截器:
一. 项目概述
1. 有哪些业务:
本项目涵盖了新闻资讯类 App 的重点核心功能,其中包括:用户登录 / 注册、文章列表、文章详情、文章评论、个人资料、用户中心、频道管理、文章搜索、用户收藏 / 历史 / 作品,以及机器人聊天等功能。
2. 用的什么技术栈?
本项目采用完全前后端分离的开发模式,使用 Vue.js 技术栈(全家桶)构建的移动端 SPA 单页面应用程序。
3. 业务功能:
本项目的目标是完成一个新闻资讯类 App 的核心功能,但项目中所覆盖的核心业务却非常具有普适性,在众多 App 中都有体现,诸如:
基于 Token 的验证方案处理用户登录
移动端表单验证方案及交互提示
短信验证码的发送与验证
用户中心、个人资料展示
对文章内容的收藏、点赞和分享
编辑用户资料,其中包括基本信息修改及头像裁切上传等功能
列表类页面的加载、缓存及优化
列表类页面的下拉刷新和上拉加载更多
资讯类文章详情展示和文章评论
完整的搜索业务:输入联想建议、搜索关键词高亮、搜索历史记录、搜索结果列表等
即时通信核心业务及基于 WebSocket 的数据通信
4. 本项目采用了以下项技术解决方案:
基于 Vue.js 的前端框架
基于 webpack 工程化开发解决方案
基于 Vant 的前端 UI 组件库,开发效率更高
基于 axios 的请求库,功能强大性能高效
基于 RESTful 风格的数据 API 解决方案(90% 的后端均采用这种风格提供数据 API,全 栈级能力,升职加薪不在话下)
基于 JWT 的 Token 状态维持解决方案(能够强化更多服务器知识)
基于 Vue Router 的路由管理方案
基于 Vuex 的状态共享方案
基于 Vue CLI 的脚手架工具,快速创建项目快速开发
基于 Socket.IO 的实时通信解决方案
基于 PostCSS 的移动端 REM 适配解决方案
基于 DCloud 平台的移动 App 开发解决方案
二、项目初始化
删除初始化的默认文件
新增调整我们需要的目录结构,下载vuex,router路由,less,vant组件库,axios
├── src
│ ├── api 存储 API 请求模块
│ ├── router Vue Router 路由模块
│ ├── store Vuex 容器模块
│ ├── styles 存储全局样式资源目录,在main.js中引入
│ ├── utils 存储工具模块
│ ├── views 存储视图组件
│ ├── App.vue 根组件
│ └── main.js 入口模块
移动端 REM 适配:
Vant 中的样式默认使用 px
作为单位,如果需要使用 rem
单位,推荐使用以下两个工具:
- postcss-pxtorem 是一款 postcss 插件,用于将 px 单位转化为 rem
- lib-flexible 用于设置 rem 基准值
下面我们分别将这两个工具配置到项目中完成 REM 适配。
1. 使用 lib-flexible 动态设置 REM 基准值(html 标签的字体大小)
安装依赖:
# yarn add amfe-flexible npm i amfe-flexible
然后在
main.js
中加载执行该模块:import 'amfe-flexible'
最后测试:在浏览器中切换不同的手机设备尺寸,观察 html 标签
font-size
的变化。
2. 使用 postcss-pxtorem 将
px
转为rem
安装依赖:
# yarn add -D postcss-pxtorem
# -D 是 --save-dev 的简写
npm install postcss-pxtorem -D然后在项目根目录中创建
postcss.config.js
文件:module.exports = { plugins: { 'autoprefixer': { browsers: ['Android >= 4.0', 'iOS >= 8'] }, 'postcss-pxtorem': { rootValue: 37.5, propList: ['*'] } } }
配置完毕,重新启动服务。
最后测试:刷新页面,审查元素样式查看是否已将
px
转换为rem
。需要注意的是:
- 该插件不能转换行内样式中的
px
,例如<div style="width: 200px;"></div>
关于 PostCSS 配置文件:
postcss.config.js
是 PostCSS 的配置文件。
PostCSS 是一个处理 CSS 的处理工具,本身功能比较单一,它主要负责解析 CSS 代码,再交由插件来进行处理,它的插件体系非常强大,所能进行的操作是多种多样的,例如:
- Autoprefixer 插件可以实现自动添加浏览器相关的声明前缀
- PostCSS Preset Env 插件可以让你使用更新的 CSS 语法特性并实现向下兼容
- postcss-pxtorem 可以实现将 px 转换为 rem
- ...
目前 PostCSS 已经有 200 多个功能各异的插件。开发人员也可以根据项目的需要,开发出自己的 PostCSS 插件。
PostCSS 一般不单独使用,而是与已有的构建工具进行集成。
Vue CLI 默认集成了 PostCSS,并且默认开启了 autoprefixer 插件。
Vue CLI 内部使用了 PostCSS。
可以通过
.postcssrc
或任何 postcss-load-config 支持的配置源来配置 PostCSS。也可以通过vue.config.js
中的css.loaderOptions.postcss
配置 postcss-loader。我们默认开启了 autoprefixer。如果要配置目标浏览器,可使用
package.json
的 browserslist 字段。
Autoprefixer 插件的配置 :
autoprefixer 是一个自动添加浏览器前缀的 PostCss 插件,browsers
用来配置兼容的浏览器版本信息,但是写在这里的话会引起以下编译器警告:
Replace Autoprefixer browsers option to Browserslist config.
Use browserslist key in package.json or .browserslistrc file.
Using browsers option can cause errors. Browserslist config
can be used for Babel, Autoprefixer, postcss-normalize and other tools.
If you really need to use option, rename it to overrideBrowserslist.
Learn more at:
https://github.com/browserslist/browserslist#readme
https://twitter.com/browserslist
警告意思就是说应该将 browsers
选项写到 package.json
或 .browserlistrc
文件中。
package.json
文件里的browserslist
字段 (或一个单独的.browserslistrc
文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。参考官方文档中的语法,我们将
package.json
文件里的browserslist
字段 (或一个单独的.browserslistrc
文件) 修改如下:"browserslist": [ "Android >= 4.0", "iOS >= 8" ] 或 Android >= 4.0 iOS >= 8
并将postcss.config.js中的此代码删掉
'autoprefixer': { browsers: ['Android >= 4.0', 'iOS >= 8'] },
保存之后就不报错了
postcss-pxtorem 插件的配置:
rootValue
:表示根元素字体大小,它会根据根元素大小进行单位转换
propList
用来设定可以从 px 转为 rem 的属性例如
*
就是所有属性都要转换,width
就是仅转换width
属性
rootValue
应该如何设置呢?
如果使用的是基于lib-flexable的REM适配方案,则应该设置为你的设计稿的十分之一。
例如设计稿是 750 宽,则应该设置为 75。大多数设计稿的原型都是以 iPhone 6 为原型,iPhone 6 设备的宽是 750,我们的设计稿也是这样。
但是 Vant 建议设置为 37.5,为什么呢?
因为 Vant 是基于逻辑像素 375 写的,所以如果你设置为 75 的话,Vant 的样式就小了一半。
所以如果设置为
37.5
的话,Vant 的样式是没有问题的,但是我们在测量设计稿的时候都必须除2才能使用,否则就会变得很大。
有没有更好的办法不用除以2呢?
1. 不用写代码的方式
在 Photoshop 中打开单位与标尺设置面板:菜单栏 -> 编辑 -> 首选项 -> 单位与标尺。
将单位中的标尺和文字的单位修改为点
打开设置图像大小面板:
菜单栏 -> 图像 -> 图像大小 快捷键:Alt + Ctrl + I
关闭重新采样
将宽度单位设置为
点
将高度单位设置为
点
将宽度修改为
375
,高度不用动(它会适应宽度自动调整)点击确定完成修改。
调整之后,我们可以看到图像的大小变成了 375 点 x 667 点(144 ppi)。
在 iPhone 6/7/8 设备下,1个点 = 2个物理像素,所以导出的图片还是原来的二倍图。
2. 写代码的方式:
通过查阅文档可以看到 rootValue
支持两种参数类型:
数字:固定值
函数:动态计算返回
有一个默认参数:一个对象,其中包含一个 file 属性(编译的文件路径)
所以我们可以这样来处理它:
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue ({ file }) {
// 如果是 Vant 的样式就按照 37.5 处理转换
// 如果是我们自己的样式就按照 75 处理转换
return file.indexOf('vant') !== -1 ? 37.5 : 75
},
propList: ['*']
}
}
}
这种方式不方便调试。因为在调试面板中看到的都是逻辑像素大小,它和 750 物理像素设计稿不一致,无法很好的利用调试工具。
关于字体图标:
使用 iconfont 制作字体图标:
登录 iconfont
创建项目
上传图标到项目中 图标下载
去除颜色并提交
点击生成代码
将css代码引入public/index.html中就可直接使用
或在项目中创建
src/styles/icon.less
并写入上面css中复制到的代码,并引入main.js
注意:
@import用于在css中引入css文件
配置路由:
封装请求模块:
创建 src/utils/request.js
:
/**
* 封装 axios 请求模块
*/
import axios from "axios"
const request = axios.create({
baseURL: "http://ttapi.research.itcast.cn/" // 基础路径
})
export default request
哪里使用,哪里加载:
import request from '@/utils/request'
request({
method: 'xxx',
url: 'xxx',
...
})
三:登录注册:
存储用户 Token:
登陆成功后后台为了区分用户是谁,服务器会下发token(令牌:唯一标识)
我们需要把它存储到一个公共的位置,方便随时取用
往哪儿存?
本地存储
获取麻烦
数据不是响应式
Vuex 容器(推荐)
获取方便
响应式的
使用容器存储 Token 的思路:
登录成功,将 Token 存储到 Vuex 容器中
获取方便
响应式
为了持久化,还需要把 Token 放到本地存储
持久化
关于 Token 过期问题:
见最后
四:个人中心
项目中的接口除了登录之外大多数都需要提供 token 才有访问权限。
后端接口要求我们将 token 放到请求头中发送
方式一:在每次请求的时候手动添加(麻烦)
axios({
method: "",
url: "",
headers: {
Authorization(后端与前端商量好的字段): "Bearer token"
}
})
方式二:使用请求拦截器统一添加(推荐,更方便)
import axios from "axios"; 引入axios
import store from "@/store/index"; 引入store
const request = axios.create({ 创建axios实例对象
baseURL: "http://toutiao.itheima.net",
});
request.interceptors.request.use((config) => { 请求拦截器
let token = store.state.mytoken;
if (token) { 如果用户已登录,统一给接口设置 token 信息
config.headers.Authorization = `Bearer ${token.token}`;
}
return config; 处理完之后一定要把 config 返回,否则请求就会停在这里
});
export default request; 导出
五、首页—文章列表:
记住列表的滚动位置:
问题:
假如把a列表的滚动条滚动到10的位置,然后切换到b列表并把滚动条滚动到20的位置,再次切换到a列表发现滚动条也在20的位置
为什么列表滚动会相互影响?
因为它们并不是在自己内部滚动,而是整个body页面在滚动。无论你是在a频道还是在b频道,其实滚动的都是body 元素。
怎么解决:
让每一个标签内容文章列表产生自己的滚动容器,这样就不会相互影响了。
如何让标签内容文章列表产生自己的滚动容器?
固定高度:height:xxx;溢出滚动:overflow-y:auto;
如果设置高100%的话没有作用,为什么?
因为百分比是相对于父元素,如果审查元素发现它所有的父元素都没有高,那肯定没有作用了。也可以用vw或vh
关于第三方图片资源403问题:
为什么文章列表数据中的好多图片资源请求失败返回 403?
这是因为我们项目的接口数据是后端通过爬虫抓取的第三方平台内容,而第三方平台对图片资源做了防盗链保护处理。
第三方平台怎么处理图片资源保护的?
服务端一般使用 Referer 请求头识别访问来源,然后处理资源访问。
Referer 是什么东西?
扩展参考:HTTP Referer 教程 - 阮一峰的网络日志
Referer 是 HTTP 请求头的一部分,当浏览器向 Web 服务器发送请求的时候,一般会带上 Referer,它包含了当前请求资源的来源页面的地址。服务端一般使用 Referer 请求头识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。需要注意的是 referer 实际上是 "referrer" 误拼写。参见 HTTP referer on Wikipedia (HTTP referer 在维基百科上的条目)来获取更详细的信息。
怎么解决?
不要发送 referrer ,对方服务端就不知道你从哪来的了,姑且认为是你是自己人吧。
如何设置不发送 referrer?
1. 用 <a>、<area>、<img>、<iframe>、<script> 或者 <link> 元素上的 referrerpolicy 属性为其设置独立的请求策略,例如:
<img src="http://……" referrerPolicy="no-referrer">
2. 或者直接在 HTMl 页面头中通过 meta 属性全局配置:
<meta name="referrer" content="no-referrer" />
处理时间:
- Moment.js
- Day.js
六、首页—频道编辑:
频道数据持久化:
频道编辑这个功能,无论用户是否登录用户都可以使用。
不登录也能使用
- 数据存储在本地
- 不支持同步功能
登录也能使用
- 数据存储在线上后台服务器
- 更换不同的设备可以同步数据
添加,删除频道
思路:
- 如果未登录,则存储到本地
- 如果已登录,则存储到线上
正确的获取首页频道列表数据图:
提示:获取登录用户的频道列表和获取默认推荐的频道列表是同一个数据接口。后端会根据接口中的 token 来判定返回数据。
七、文章搜索
略
八、文章详情
关于后端返回数据中的大数字问题:
JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JavaScript 不适合进行科学和金融方面的精确计算。
json-bigint 是一个第三方包,它可以帮我们很好的处理这个问题。
使用它的第一步就是把它安装到你的项目中。
npm i json-bigint
下面是使用它的一个简单示例。
const jsonStr = '{ "art_id": 1245953273786007552 }'
console.log(JSON.parse(jsonStr)) // 1245953273786007600
// JSON.stringify()
// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象
// 使用的时候需要把 BigNumber 类型的数据转为字符串来使用
console.log(JSONBig.parse(jsonStr).art_id.toString()) // 1245953273786007552
console.log(JSON.stringify(JSONBig.parse(jsonStr)))
console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转
json-bigint 会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。
通过 Axios 请求得到的数据都是 Axios 处理(JSON.parse)之后的,我们应该在 Axios 执行处理之前手动使用 json-bigint 来解析处理。Axios 提供了自定义处理原始后端返回数据的 API:transformResponse
。
import axios from 'axios'
import jsonBig from 'json-bigint'
var json = '{ "value" : 9223372036854775807, "v2": 123 }'
console.log(jsonBig.parse(json))
const request = axios.create({
baseURL: 'http://ttapi.research.itcast.cn/', // 接口基础路径
// transformResponse 允许自定义原始的响应数据(字符串)
transformResponse: [function (data) {
try {
// 如果转换成功则返回转换的数据结果
return jsonBig.parse(data)
} catch (err) {
// 如果转换失败,则包装为统一数据格式并返回
return {
data
}
}
}]
})
export default request
关于文章正文的样式:
文章正文包括各种数据:段落、标题、列表、链接、图片、视频等资源。
github风格的内容样式:
- 将 github-markdown-css 样式文件下载到项目中,并引入css
- 配置不要转换样式文件中的字号
九、文章评论
略
十、 编辑资料
图片裁切:
方式一:结合服务器方式
图片上传预览:
图片裁切上传流程:
方式二:纯客户端方式
image的accept属性: 可以限制上传文件的类型
<input type="file" accept="image/png"> 只能上传png格式图片 <input type="file" accept="image/*">能上传所有格式图片 <input type="file" accept="image/jpeg">只能上传jpeg格式图片 <input type="file" accept="image/*" hidden> hidden表示隐藏input
在input的change事件中如果选择了和上次相同的文件,那么则不会触发它的change
解决:
在每次触发change事件时清空input的value
this.$refs.xxx.value = ""
获取上传到input的图片文件
window.URL.createObjectURL(this.$refs.xxx.files[0])
见文档:URL.createObjectURL() - Web API 接口参考 | MDN
图片裁剪工具cropper.js:
GitHub - fengyuanchen/cropperjs: JavaScript image cropper.
演示地址:
Cropper
使用步骤:
1. 下载 npm install cropperjs 2. 引入css和js import 'cropperjs/dist/cropper.css'; import Cropper from 'cropperjs'; 3. 在mounted中写: data() { return { cropper: null } }, mounted() { const image = this.$refs.xxx //获取图像 this.cropper = new Cropper(image, { viewMode: 1, //查看模式 dragMode: 'move', //拖动模式 aspectRatio: 1, //截图比例 autoCropArea: 1, //自动截取区域 cropBoxMovable: false, //截图区域是否可以移动 cropBoxResizable: false, //截图区域的缩放 background: false, //背景 movable: true //画布是否可以移动 }); }, 注意: <div> <img id="image" src="picture.jpg"> //图像img必须用块元素包裹 </div> img { display: block; //图像要设为块元素,最大宽设为父元素百分百,防止图像宽度大于父元素 max-width: 100%; }
保存更新:
如果是基于服务端的裁切,则使用:getData 方法,该方法得到裁切的区域参数。
this.cropper.getData() 此函数会返回裁剪的一些参数,把此参数传给服务端
如果是纯客户端的图片裁切,则使用:getCroppedCanvas 方法,该方法得到裁切之后的图片对象(类似于URL.createObjectURL 方法得到的文件对象)。
this.cropper.getCroppedCanvas().toBlob(async (blob) => { console.log(blob) blob是裁切的文件对象,将文件对象传给服务端即可 })
如果要求 Content-Type 是 multipart/form-data,则一定要提交 FormData数据对象(input提交文件的原始对象),专门用于文件上传的,不能提交{xxx:xxx},没用
var fd = new FormData() 创建FormData对象
fd.append("文件名", 文件对象) 将用户选择的文件,添加到FormData中
xxx(fd) 发送ajax请求
十一. 小智同学
WebSocket:
WebSocket 是一种数据通信协议,也是用于客户端和服务端数据通信,类似于我们常见的 http
既然有 http,为啥还要WebSocket
http通信是单向的
- 请求 + 响应
- 没有请求也就没有响应
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。
WebSocket 协议在 2008 年诞生,2011 年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
简单理解:
HTTP 打电话:
- 我问一句
- 你回答一句
- 没有提问就没有回答,即便对方主动给你说话,我也是个聋子听不见
WebSocket 打电话:
- 双向对话
HTTP 和 WebSocket 通信模型:
其他特点包括:
- 和 HTTP 一样属于应用层协议,也是建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段(第 1 次建立连接)采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源跨域限制,客户端可以与任意服务器通信。
- WebSocket 不是用来代替 HTTP 的,它是用来解决实时通信的业务场景。如果业务不需要实时性,那就没必要使用 WebSocket。
- WebSocket 也是有资源消耗的,因为它要实时通信,也是需要和服务端保持一定的通信连接。
- WebSocket 也是需要服务端配合才能使用。
- 协议标识符是
ws
(如果加密,则为wss
),服务器网址就是 URL。- 浏览器专门为 WebSocket 通信提供了一个请求对象
WebSocket
怎么查看 WebSocket 请求日志:
朝上的绿色箭头是发出去的消息
朝下的红色箭头是收到的消息
使用原生 WebSocket(了解):
参考文档:
- MDN - WebSocket
浏览器为 HTTP 通信提供了
XMLHttpRequest
对象,同样的,也为 WebSocket 通信提供了一个通信操作接口:WebSocket
。通信模型:
- 拨号(建立连接)
- 通话(双向通信)
- 结束通话(关闭连接)
<script> // WebSocet 通信模型 // 1. 拨打电话(建立连接) // 注意:wss://echo.websocket.org 是一个在线的测试接口,仅用于 WebSocket 协议通信测试使用 var ws = new WebSocket("wss://echo.websocket.org"); // 当连接建立成功,触发 open 事件 ws.onopen = function(evt) { console.log("建立连接成功 ..."); // 连接建立成功以后,就可以使用这个连接对象通信了 // send 方法发送数据 ws.send("Hello WebSockets!"); }; // 当接收到对方发送的消息的时候,触发 message 事件 // 我们可以通过回调函数的 evt.data 获取对方发送的数据内容 ws.onmessage = function(evt) { console.log("接收到消息: " + evt.data); // 当不需要通信的时候,可以手动的关闭连接 // ws.close(); }; // 当连接断开的时候触发 close 事件 ws.onclose = function(evt) { console.log("连接已关闭."); }; </script>
Socket.IO(了解体验):
介绍
原生的 WebSocket 使用比较麻烦,所以推荐使用一个封装好的解决方案:socket.io
socket.io 提供了服务端 + 客户端的实现
- 客户端:浏览器
- 服务端:Java、Python、PHP、。。。。Node.js
对于前端开发者来说,只需要关心他的客户端如何使用即可
怎么建立连接,怎么发送消息,怎么接收消息
注意:Socket.io 必须前后端配套使用
- 实际工作中,socket.io 已经成为了各大后端开发的 WebSocket 通信主流解决方案
GitHub 仓库
官网
Socket.io 和 WebSocket 就好比它就好比 axios 和 XMLHTTPRequest 的关系。
官网:https://socket.io/get-started/chat/
Socket.IO使用:
1. 下载 npm install socket.io-client
2. 在需要使用的模块中引入
import io from "socket.io-client";
3. 在created()或mounted()中
data() {
return {
socket: null,
};
},
created() {
this.socket = io("http://toutiao.itheima.net", {
query: {
token: this.$store.state.user.token
},
transports: ['websocket'] //建立连接,第一个参数是路径,第二个参数是要求携带的参数
})
this.socket.on("disconnec", () => {
console.log("断开连接了") //断开连接的回调
})
this.socket.on("connect", () => {
console.log("建立连接成功!"); //连接成功的回调
});
this.socket.on('message', data => {
console.log('收到服务器消息:', data)//服务器收到消息的回调,这里的message是接口要求的
})
}
在methods中写:
点击发送后触发发送事件:this.socket.emit("message",消息内容) message为消息类型
消息类型和消息内容要按文档要求写
十二: 功能优化
组件缓存:
从首页切换到我的,再从我的回到首页,我们发现首页重新渲染原来的状态没有了。
首先,这是正常的状态,并非问题,路由在切换的时候会销毁切出去的页面组件,然后渲染匹配到的页面组件。
但是我想要某些页面保持状态,而不会随着路由切换导致重新渲染。
解决:
keep-alive 缓存组件
- 组件缓存不是持久化,它只是在应用运行期间不会重新渲染,如果页面刷新还是会回到初始状态。
<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的name
选项还是局部/全局注册。注意:keep-alive 仅对该路由出口渲染的组件有效,二级路由无效
组件缓存造成的问题:
1. 退出登陆后再换另一个账户重新登陆,因为缓存的问题用户信息的数据没有变,还是上一 个用户的信息
解决:
在登陆成功之后跳转路由之前把缓存删除,在跳转路由之后把缓存加上
步骤:
把要缓存的组件数组存在vuex中,在mutations中设置删除缓存和添加缓存的函数,设置缓存时读取vuex中设置的值,在登陆成功之后和路由跳转之前分别调用删除和添加缓存的函数
2. 点击详情页面在返回时,滚动条回到的最顶部
解决:
给要滚动的dom添加滚动事件,添加一个变量保存在每次滚动完之后滚动条滚动的垂直距离(利用scrollTop),点击详情页返回后利用生命周期钩子activated(路由组件被激活时触发)设置滚动条的垂直滚动距离
响应拦截器:
非以 2xx 开头的状态码都会触发,都会触发响应拦截器失败的回调
console.dir() 可以打印出该对象的所有属性和属性值.
解决token过期问题:
在响应拦截器失败的回调中先判断返回的值是多少,如果是非401那么就给出相应的提示,如果返回的是401,那么继续判断是否已登录,如果未登录直接跳转到登陆页面,如果已登陆那么用已有的refresh_token作为参数发送请求去获取新的token(这里发送请求要新建一个axios的实例去发),如果发送请求成功则把返回的新的token保存在vuex中并发送请求(参数是error.config响应拦截器失败的对象),如果发送请求失败那么就跳转到登陆页面
登陆成功跳转回之前页面:
在处理token过期时(响应拦截器失败时),在跳转到登陆页面时同时加上一个query参数,
query:{xxx: router.currentRoute.fullPath}
router.currentRoute和我们在组件中获取的this.$route 是一样的,router.currentRoute.fullPath就是当前的路径
在登陆成功后判断query参数上是否有这个属性,如果有就跳转到这个页面,如果没有就跳转到首页面("/")
统一处理页面访问权限:
给每个路由都配一个路由元信息
通过路由元信息判断是否需要登陆才能访问
使用路由导航配合路由元信息控制权限访问