这个是因为飞书文档无法满足笔记需求,想到写博客记录笔记,前面的笔记看情况决定是不是补上
部分内容参考博主毛毛虫呜呜的笔记
笔记目录
git地址
https://gitee.com/juneathena/vue
路由传参
路由跳转方式:
声明式导航:router-link
,必须要加to:<router-link to="home">Home</router-link>
编程式导航:利用组件实例对象的$router.push/replace
方法,跳转之前可以书写一下自己的 业务。
query、params两个属性可以传递参数:
query参数:不属于路径当中的一部分,类似于get请求,地址栏表现为 /search?k1=v1&k2=v2
query参数对应的路由信息 path: "/search"
params参数:属于路径当中的一部分,需要注意,在配置路由的时候,需要占位 ,地址栏表现为 /search/v1/v2
params参数对应的路由信息要修改为path: "/search/:keyword"
这里的/:keyword就是一个params参数的占位符
路由传参:
1、字符串形式
this.$router.push("/search/" + this.keyWord + "?k=" + this.keyWord.toUpperCase());
2、模板字符串
this.$router.push(`/search/${
this.keyWord}?k=${
this.keyWord.toUpperCase()}`)
3、对象写法
this.$router.push({
name: "search", params: {
keyWord: this.keyWord}, query: {
k: this.keyWord.toUpperCase()}})
路由常问问题
Q:路由传递参数(对象写法)path是否可以结合params参数一起使用?
A:不可以,路由跳转传参的时候,对象的写法可以是name、path形式,但是需要注意的是,path这种写法不能与params参数一起使用
Q:如何指定params可传可不传?
A: 1. 在配置路由的时候,在配置后面加一个问号path: "/search/:keyWord?"
Q:params参数可以传递也可不传递,但是如果传递空串,如何解决?
A: 使用undefined解决
Q:路由组件能不能传递props数据?
A: 可以,三种写法:
1. 布尔值写法:props: true
2. 对象写法:props: {a:1, b:1}
3. 函数写法:props:($router)=>{ return {keyword:$router.params.keyword, k:$router.query.k} }
二次封装axios
axios中文文档地址
Axios 是一个基于 promise 的 HTTP 库,vue比较适合Axios。
请求拦截器:可以在发送请求之前处理一些业务。
响应拦截器:可以在数据返回后处理一些事情。
// 对axios进行二次封装
import axios from "axios";
import nprogress from "nprogress";
import "nprogress/nprogress.css"
//1、利用axios对象的方法create去创建一个axios实例
const requests = axios.create({
//配置对象
// 基础路径,发请求的时候,路径中会出现/api
baseURL: "/api",
// 设置超时
timeout: 5000,
});
// 请求拦截器:在发送请求之前,请求拦截器可以检测到,可以在发出请求之前处理一些业务
requests.interceptors.request.use((config) => {
//config:配置对象,主要是对请求头headers配置
//比如添加token
// 进度条开始
nprogress.start();
return config;
})
// 响应拦截器
requests.interceptors.response.use((res) => {
// 进度条结束
nprogress.done();
// 成功回调函数
return res.data;
}, (error) => {
//响应失败的回调函数
return Promise.reject(new Error(error));
})
export default requests;
通过配置代理解决跨域问题(前端)
通过配置代理,让请求都指向指定服务器
module.exports = {
devServer: {
proxy: {
'/api': {
target: '目标服务器',
},
}
}
}
来自群内大佬讲解的代理:
我们在发起请求的时候,如果不加请求的地址,浏览器会默认加上我们本机localhost地址,如果这样去发送请求,实际上,浏览器会默认请求,localhost:8080。跨域是受到浏览器同源策略影响,简单理解就是我们像服务端发送请求,其实别人数据给我们了,但是浏览器同源策略阻挡了服务端返回的数据,但是服务器与服务器之间是没有跨域问题的。他是通过创建一个我们本地的服务器,然后用我们本地的服务器去请求服务端服务器的内容,这样一来,我们本地服务器就能拿到服务端返回的内容了。然后我们再去拿到本地服务器的返回的数据,就把跨域问题处理了。在weback的devServe
里面怎么处理的呢?他有一个配置项,叫proxy
。处理跨域的核心配置就是 target
,所以我们需要把需要代理的地址写上去,前提是给一个节点,这个节点就是,我在什么情况下才会去代理,所以我们需要写一个节点(上方代码里面的/api
),当我80端口请求的接口中间有api
的时候,webpack就知道我们需要代理了,然后80端口就会去请求target
这个地址的内容。
webpack/devServer中文文档
个人理解:
proxy: {
// 一旦代理服务器devServer服务器接收到 /api/xxx 的请求,就会把请求转发到远端服务器服务器(3000)
// 浏览器和服务器之间有跨域,但是服务器和服务器之间没有跨域
'/api': {
target: 'http://localhost:3000',
// 重写的路径是发送到远端服务的路径
// 发送请求时,请求路径重写:将 /api/todo--> /test/todo(/api重写成/test)
// http://localhost:3000/api/todo -> http://localhost:3000/test/todo
pathRewrite: {
'^/api': '/test'
}
}
}
loadsh插件防抖和节流
在快速划过三级导航或者搜索框输入搜索文字的时候,如果事件处理函数调用的频率无限制,会造成浏览器卡顿。因此会采用节流(throttle)和防抖(debounce)。
节流:在规定的时间间隔范围内不会重复触发函数的回调,只有大于这个时间间隔才会触发回调,把频繁变为少量触发
防抖:前面的所有触发都被取消,最后一次执行在规定时间之后才会触发,也就是说如果连续快速触发,只会执行一次
该项目采用第三方库实现节流:
// 按需引入throttle
import {
throttle } from 'lodash/throttle.js'
methods: {
//采用键值对形式创建函数
changeIndex: throttle(function (index){
this.currentIndex = index
},100),
}
编程式导航
需要实现的部分的截图:(功能:点击菜单跳转链接,url带上参数)
实现路由跳转有两种方式:
声明式导航:router-link
编程式导航:push|replace
对于导航式路由,我们有多少个a
标签就会循环出多少个router-link
标签,这样当我们频繁操作时浏览器会出现卡顿现象。
对于编程式路由,我们是通过触发点击事件实现路由跳转。同理有多少个a
标签就会有多少个触发函数。虽然不会出现卡顿,但是也会影响性能。
最好的解决方案就是:编程时导航+事件委派。事件委派就是把子节点的触发时间都委派给父节点,这样就可以只调用一次回调函数goSearch
利用事件委派也存在一些问题:
(1)把全部的子节点【h3、dt、dl、em】的事件都委派给了父亲节点,如何确定点击的一定是a标签?
(2)如何确定区分是一级还是二、三级的标签(一、二、三级分类产品的名字、id需要在url中传递)
解决方案:
(1)给三个a标签添加自定义属性:data-categoryName
,只有这三个a
标签拥有,其他子节点没有.
(2)添加自定义属性:data-category1Id
、:data-category2Id
、:data-category3Id
来获取三个不同级别的a标签内的id来实现跳转。
(*)可以使用event
来获取当前点击事件,通过event.target
属性获取当前点击节点,再通过dataset
属性获取节点的属性信息。
<!--3级联动-->
<div class="sort">
<div class="all-sort-list2" @click="goSearch">
<!--1级分类-->
<div class="item" v-for="(c1, index) in categoryList" :key="c1.categoryId" :class="{ cur: currentIndex == index }">
<h3 @mouseenter="changeIndex(index)">
<a :data-categoryName="c1.categoryName" :data-category1Id="c1.categoryId">{
{ c1.categoryName }}</a>
</h3>
<!--2、3级分类-->
<div class="item-list clearfix" :style="{
display:currentIndex == index?'block':'none' }">
<div class="subitem" v-for="c2 in c1.categoryChild" :key="c2.categoryId">
<dl class="fore">
<dt>
<a :data-categoryName="c2.categoryName" :data-category2Id="c2.categoryId">{
{ c2.categoryName }}</a>
</dt>
<dd>
<em v-for="c3 in c2.categoryChild" :key="c3.categoryId">
<a :data-categoryName="c3.categoryName" :data-category3Id="c3.categoryId">{
{ c3.categoryName }}</a>
</em>
</dd>
</dl>
</div>
</div>
</div>
</div>
节点中有一个属性dataset
属性可以获取当前节点的自定义属性与属性值
goSearch
函数代码:
goSearch(event) {
let element = event.target;
/**
* html中会把大写转为小写
* categoryname:a标签
* category1id:一级类目id
* category2id:二级类目id
* category3id:三级类目id
*/
//节点中有一个属性dataset(火狐、谷歌支持),可以获取节点的自定义属性与属性值
let {
categoryname,category1id,category2id,category3id} = element.dataset;
if (categoryname) {
// 整理路由跳转的参数
let location = {
name:'search'};
let query = {
categoryName:categoryname};
// 一、二、三级
if (category1id) {
query.category1Id = category1id;
} else if (category2id){
query.category2Id = category2id;
} else {
query.category3Id = category3id;
}
// 完整参数
location.query = query;
// 路由跳转
this.$router.push(location);
}
},
},
Vue路由组件销毁优化
Vue在路由切换的时候会销毁旧路由。
我们在三级列表全局组件TypeNav中的mounted进行了请求一次商品分类列表数据。
由于Vue在路由切换的时候会销毁旧路由,当我们再次使用三级列表全局组件时还会发一次请求。
如下图所示:当我们在包含三级列表全局组件的不同组件之间进行切换时,都会进行一次信息请求。
每次请求的内容都是一样的,处于性能的考虑,想要只请求一次。所以我们把这次请求放在App.vue的mounted中。(根组件App.vue的mounted只会执行一次)
注意:虽然main.js也是只执行一次,但是不可以放在main.js中,main.js不是组件。只有组件的身上才会有$store
属性。
合并参数
合并路由中的参数:如果跳转路由的时候带有params参数,需要一起传递过去
TypeNav.vue的goSearch
方法:
// 如果跳转路由的时候带有params参数,需要一起传递过去
if (this.$route.params) {
// 给location添加一个params参数
location.params = this.$route.params;
location.query = query;
// 跳转
this.$router.push(location);
console.log(location)
}
Header.vue的goSearch
方法:
goSearch() {
let location = {
name: "search", params: {
keyword: this.keyWord || undefined },};
if (this.$route.query) {
location.query = this.$route.query;
}
this.$router.push(location);
},
mock.js模拟数据
服务器返回的数据只有商品分类,对于轮播图(ListContainer)组件和推荐商品(Floor)组件数据服务器没有提供。前端mock的数据不会和服务器进行任何通信
mock.js生成随机数组,拦截ajax请求。
使用步骤:
1、在src目录下创建一个mock文件夹
2、准备需要的json数据
3、把mock需要的图片放到public文件中
4、开始mock虚拟数据,通过mock.js
模块
5、mockServe.js文件在入口文件引入(至少需要执行一次,才能模拟数据)
mockServe.js
// 引入mockjs模块
import Mock from 'mockjs';
// 引入准备好的json数据
import banner from './banner.json';
import floor from './floor.json';
// mock数据:第一个参数请求地址,第二个参数请求数据
Mock.mock("/mock/banner", {
code:200, date:banner});
Mock.mock("mock/floor", {
code:200, data:floor});
需要在入口文件main.js
中引入mockServe.js
import "@/mock/mockServe"
webpack默认对外暴露的:图片、JSON数据;
通过Vuex来实现数据存储与管理(复习,上面用过)
我们会把公共的数据放在store中,然后使用时再去store中取。
以我们的首页轮播图数据为例。
1、在轮播图组件ListContainer.vue
组件加载完毕后发起轮播图数据请求。
mounted() {
// 派发action,通过Vuex像Ajax发起请求