Vue-Cli 项目总结
1. Vue 项目目录结构分析
public 文件夹:静态资源,webpack 进行打包的时候会原封不动打包到dist文件夹中。
pubilc / index.html:是一个模板文件,作用是生成项目的入口文件,webpack打包的js,css也会自动注入到该页面中。我们浏览器访问项目的时候就会默认打开生成好的index.html。\
src 文件夹(程序员代码文件夹)
assets: 存放公用的静态资源(放置在assets文件夹里面的静态资源,在webpack打包的时候,webpack会把静态资源当做一个模块,打包到JS文件里面)
components: 非路由组件(全局组件),其他组件放在views或者pages文件夹中
App.vue: 唯一的根组件
main.js: 程序入口文件,最先执行的文件
babel.config.js:配置文件(babel相关)
package.json:项目的详细信息记录
package-lock.json:缓存性文件(各种包的来源)
2. 项目配置
2.1 项目运行,浏览器自动打开
// package.json
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
2.2 关闭 eslint 校验工具(不关闭会有各种规范,不按照规范就会报错)
- 根目录下创建vue.config.js,进行配置
// vue.config.js
module.exports = {
//关闭eslint
lintOnSave: false
}
2.3 配置 alias 别名
const path = require('path'); //引入path 要用到他的resolve 方法
const resolve = dir => path.resolve(__dirname, dir); //这里为了方便直接引入了最里层
module.exports = {
resolve: {
// 设置别名
alias: {
'@': resolve('src'),// 这样配置后 @ 可以指向 src 目录
'api': resolve('src/api')// 这样配置后 api 可以指向 src 目录下的api目录
}
},
devServer: {
// .....
},
};
3. 路由
3.1 路由组件 和 非路由组件
- 非路由组件放在components中,路由组件放在 pages 或 views 文件夹下
- 非路由组件通过标签使用,路由组件通过路由
<router-view/>
3.2 $route 和 $router
- 在 main.js 中注册完路由,所有的路由和非路由组件身上都会拥有
$route和$router属性 $route: 一般获取路由信息(name、path、params等),this.$route--当前路由信息$router:是“路由实例”对象包括了路由的跳转方法,钩子函数等。一般进行编程式导航进行路由跳转
3.3 路由跳转方式
- 声明式导航:
<router-link/>标签,可以把它理解为一个<a>标签,它也可以加 class 修饰 - 编程式导航 :声明式导航能做的编程式都能做
this.$router.push()、this.$router.replace(),而且还可以处理一些业务
3.4 路由元信息 (meta)
应用场景:
例子:判断路由的显示与隐藏
// router.js 文件中
onst routes = [
{
path: "/home",
component: Home,
meta: {isShow:true} // 定义路由的时候可以配置 `meta` 字段:
},
{
path: "/register",
component: Register,
meta: {isShow:false}
}
];
// 在 vue 组件中
<Footer v-show = "$route.meta.isShow"></Footer>
3.5 路由传参
3.5.1 应用场景
一般都是应用在父路由跳转到子路由时,携带参数跳转。
3.5.2 传参方式 (2种)
params 传参和 query 传参
params 参数:属于路径当中的一部分(配置传参目的地路由时,需要占位符),地址栏表现形式如:
/search/123
query 参数:不属于路径当中的一部分,地址栏表现形式如:/search?a=123&b=456
3.5.3 书写方式 (3种)
// Home.vue 组件中
<template>
<div class="searchArea">
<input v-model="keyword"/>
<button @click="goSearch"> 搜索</button>
</div>
</template>
<script>
export default {
data() {
return {
keyword: "", // 搜索输入框中的值 动态绑定
};
},
methods: {
// 点击搜索按钮的回调
goSearch() {
// 点击搜索,将 keyword 参数传递到 /search 路由中
this.$router.push(
// 书写形式:
// 1、字符串
// "/search/" + this.keyword + "?k=" + this.keyword.toUpperCase()
// 2、模板字符串
// `/search/${this.keyword}` + `?k=${this.keyword.toUpperCase()}`
// 注意:params参数和‘/’结合,query参数和‘?’结合
// 3、对象形式(推荐)
{
name: "search", // 需要在 search 路由配置项中,添加 name 字段
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() }
});
},
},
};
</script>
// router.js 中的配置
const routes = [
{
path: "/home",
component: Home,
},
{
name:'search', // 如果以对象的方式跳转路由,并传递参数时,需要配置 name 字段
path: "/search/:keyword", // 用 params 传递参数时,需要在这里配置占位符(/:keyword)来接收参数 keyword
component: Search,
}]
// Search.vue 组件中
<template>
<div>
<h1>我是搜索页</h1>
<h3>{{ "params参数: " + $route.params.keyword }}</h3>
<h3>{{ "query参数: " + $route.query.k }}</h3>
</div>
</template>
3.5.4 params 传参问题(4个)
(1)、路由传参(对象写法)path 是否可以结合 params 参数一起使用?
// 路由跳转传参的时候,对象写法可以使path、name形式,但是需要注意的是,path这种写法不能与params参数一起出现
this.$router.push({
path: "/search", // 错误
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
});
(2)、如何指定 params 参数可传可不传?
如果路由path要求传递params参数,但是没有传递,会发现地址栏URL有问题,详情如下:
Search路由项的path已经指定要传一个keyword的params参数,如下所示:
path: "/search/:keyword",
执行下面进行路由跳转的代码:
this.$router.push({name:"Search",query:{k:this.keyword}})
当前跳转代码没有传递params参数
地址栏信息:http://localhost:8080/#/?k=asd
此时的地址信息少了/search
正常的地址栏信息: http://localhost:8080/#/search?k=asd
解决方法:可以通过改变path来指定params参数可传可不传
path: "/search/:keyword?",?表示该参数可传可不传
(3)、由(2)可知 params 可传可不传,但是如果传递的是空串,如何解决?
this.$router.push({name:"Search",params:{keyword:''},query:{k:this.keyword}})
出现的问题和1中的问题相同,地址信息少了/search
解决方法: 加入||undefined,当我们传递的参数为空串时地址栏url也可以保持正常
this.$router.push({name:"Search",params:{keyword:''||undefined},query:{k:this.keyword}})
(4)、路由组件能不能传递 props 数据?
路由组件传递props数据,可以像使用 props 数据一样(使用起来更加简便)
// Home.vue 组件中
<template>
<div class="searchArea">
<input v-model="keyword"/>
<button @click="goSearch"> 搜索</button>
</div>
</template>
<script>
export default {
data() {
return {
keyword: "", // 搜索输入框中的值 动态绑定
};
},
methods: {
// 点击搜索按钮的回调
goSearch() {
// 点击搜索,将 keyword 参数传递到 /search 路由中
this.$router.push(
// 书写形式:
// 1、字符串
// "/search/" + this.keyword + "?k=" + this.keyword.toUpperCase()
// 2、模板字符串
// `/search/${this.keyword}` + `?k=${this.keyword.toUpperCase()}`
// 注意:params参数和‘/’结合,query参数和‘?’结合
// 3、对象形式(推荐)
{
name: "search", // 需要在 search 路由配置项中,添加 name 字段
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() }
});
},
},
};
</script>
// router.js 中的配置
const routes = [
{
path: "/home",
component: Home,
},
{
name:'search', // 如果以对象的方式跳转路由,并传递参数时,需要配置 name 字段
path: "/search/:keyword", // 用 params 传递参数时,需要在这里配置占位符(/:keyword)来接收参数 keyword
component: Search,
// 路由组件能不能传递参数 props 数据?
// 1.布尔写法:只能传递params参数
// props:true,
// 2.对象写法:除了 params 参数,还能额外的给路由组件传递一些 props
// props:{a:1,b:2},
// 3.函数写法:可以把 params参数、query参数,通过props传递给路由组件
// props:()=>{
// return {keyword:$route.params.keyword,k:$route.query.key}
// }
// 简写
props:($route)=>({keyword:$route.params.keyword,k:$route.query.key})
}]
// Search.vue 组件中
<template>
<div>
<h1>我是搜索页</h1>
<h3>{{ "params参数: " + $route.params.keyword }}</h3>
<h3>{{ "query参数: " + $route.query.k }}</h3>
// 书写更简洁
<h3>{{ "props参数: " + "params参数: " + keyword + "," + "query参数: " +k }}</h3>
</div>
</template>
3.5.5 多次执行相同的 push 问题
- 问题
this.$router.push({name:"search",params:{keyword:aaa},query:{k:bbb}})
多次执行出现警告:
- 原因
push 的结果是 一个
promise,promise需要传递成功和失败两个参数,我们的 push 中没有传递。
// 验证
let result= this.$router.push({
name: "search",
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
});
console.log(result);
控制台输出结果:
- 解决方法
push 是
VueRouter.prototype的一个方法,在 router.js 中重写该方法即可
// 在 router.js 文件中
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
// 1、先把VueRouter原型对象的push,保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
// 2、重写push|replace
// 第1个参数:告诉原来的push,跳转的目标位置和传递了哪些参数
// 第2个参数:成功的回调
// 第3个参数:失败的回调
VueRouter.prototype.push = function (location, resolve, reject) {
console.log(this); // this 是 VueRouter的一个实例,即可以调用 VueRouter原型上的方法
if(resolve && reject){
originPush.call(this,location,resolve,reject)
}else{
originPush.call(this,location,() => {},() => {})
}
}
VueRouter.prototype.replace = function (location, resolve, reject) {
console.log(this); // this 是 VueRouter的一个实例,即可以调用 VueRouter原型上的方法
if(resolve && reject){
originReplace.call(this,location,resolve,reject)
}else{
originReplace.call(this,location,() => {},() => {})
}
}
4. 全局组件
4.1 创建全局组件
// TypeNav 组件
<template>
<div>
我是全局组件
</div>
</template>
<script>
export default {
name: "TypeNav", // 组件名,必填
created() {},
data() {
return {};
},
methods: {},
};
</script>
4.2 注册全局组件
// 全局的配置都需要在main.js中配置
// 引入全局组件,并注册
import TypeNav from "@views/Home/TypeNav/Index.vue";
Vue.component(TypeNav.name,TypeNav) // 参数 1 为在全局组件中定义好的name, 参数 2 为要注册的组件
4.3 使用全局组件
// Home 组件中
<template>
<div>
<!-- 注册好的全局组件,不需要再引入,可直接使用 -->
<TypeNav></TypeNav>
</div>
</template>
5. Axios 二次封装
5.1 为什么要对 Axios 进行二次封装?
- api统一管理,不管接口有多少,所有的接口都可以非常清晰,容易维护
- 请求拦截器、响应拦截器:请求拦截器,可以在发起请求之前可以处理一些业务;响应拦截器,当服务器数据返回以后,可以处理一些事情;
5.2 封装 Axios
在根目录
/src/api文件夹下创建一个文件request.js,并加入以下代码
// request.js 文件中
import axios from "axios";
// 创建一个 axios 实例
const instance = axios.create({
// 这里面都是该实例的配置项
baseURL: "/api", // 基础路径,发起请求的时候,路径当中会出现 api , "/api" 已经在vue.config.js中配置好了
timeout: 30000, // 请求超时为 30s
})
// 请求拦截器:在发起请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
instance.interceptors.request.use((config) => {
// config:配置对象,对象里面有一个属性很重要, headers 请求头
(config) => {
console.log(config);
return config;
},
(error) => {
console.log(error);
}
})
// 响应拦截器:
instance.interceptors.response.use((res) => {
// 响应成功的回调函数:服务器相应数据过来以后,响应拦截器可以检测到,这里可以做一些事情
return res.data
}, (error) => {
// 响应失败的回调函数:
return Promise.reject(new Error('faile')) // 终止 Promise 链
})
// 把实例暴露出去
export default instance;
5.3 请求接口统一封装
将每个请求封装为一个函数,并暴露出去,组件只需要调用相应函数即可,这样当我们的接口比较多时,如果需要修改只需要修改该文件即可。
在 src/api/index.js 文件中,封装所有请求
// 当前这个模块: 对 API 进行统一管理
import instance from "./request"
// 三级联动接口
// 发送请求:axios 发请求返回的结果是 Promise 对象
export const getCategoryList = () => instance({
url:"/product/getBaseCategoryList",
method:"GET"
})
可以在 main.js 中进行单元测试
import { getCategoryList } from "@api/index.js";
getCategoryList().then(res => {
console.log(res.data);
})
6. nprogress 进度条插件
在 src/api/request.js 中
// 二次封装 axios
import axiox from "axios";
// 创建一个axios实例
const instance = axiox.create({
baseURL: "/api",
timeout:3000
})
// 引入 nprogress 进度条
import nprogress from "nprogress";
// 引入 nprogress 进度条样式
import "nprogress/nprogress.css"
// 请求拦截器:在发起请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
instance.interceptors.request.use((config) => {
// config:配置对象,对象里面有一个属性很重要, headers 请求头
// 进度条开始动
nprogress.start()
return config;
})
// 响应拦截器:服务器相应数据过来以后,响应拦截器可以检测到,这里可以做一些事情
instance.interceptors.response.use((res) => {
// 进度条结束
nprogress.done()
return res.data
}, (error) => {
// 响应失败的回调函数:
return Promise.reject(new Error('faile')) // 终止 Promise 链
})
// 把实例暴露出去
export default instance;
7. Vuex 状态管理模式
7.1 Vuex 模块化开发
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
src/store/home/index.js 文件中 (小仓库 home)
// 存储仓库数据的地方
const state = {a:"home 模块的数据"}
// 修改 state 的唯一方式
const mutations = {}
// 处理 actions 可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}
export default {
state,
mutations,
actions,
getters
}
src/store/search/index.js 文件中 (小仓库 search)
// 存储仓库数据的地方
const state = {b:"search 模块的数据"}
// 修改 state 的唯一方式
const mutations = {}
// 处理 actions 可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}
export default {
state,
mutations,
actions,
getters
}
src/store/index.js 文件中 (模块集中管理仓库)
import Vue from "vue";
import Vuex from "vuex";
// 需要使用插件一次
Vue.use(Vuex);
// 引入小仓库
import home from "@store/home";
import search from "@store/search";
// 对外暴露 Store 类的一个实例
export default new Vuex.Store({
// 实现 Vuex 仓库模块式开发存储数据
modules: {
home,
search
},
});
在 Vue.js devtools 中查看数据
7.2 State (在 Vue 组件中获得 Vuex 状态 )
// src/store/home/index.js
// 存储仓库数据的地方
const state = {
a:1
}
// 修改 state 的唯一方式
const mutations = {}
// 处理 actions 可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}
export default {
state,
mutations,
actions,
getters
}
// index.vue 组件中
<template>
<div>
<h1>{{ a }}</h1>
<h2>{{ localComputed }}</h2>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
// 局部计算属性
localComputed() {
return "局部计算属性";
},
// 方法一:
// 如果 vuex 没有模块化,即可用以下方法获取 state
// ...mapState(['a', 'b']),
// 方法二(推荐):更多灵活处理
...mapState({
// 子模块的属性因有了命名空间 无法直接使用字符串数组
a: (state) => state.home.a, // 注意:此处拿到的是 home 模块的数据
}),
},
};
</script>
7.3 Mutations (同步更改 Store 中的状态)
// src/store/home/index.js
// 存储仓库数据的地方
const state = {
a:1
}
// 修改 state 的唯一方式
const mutations = {
increment(state, payload) {
state.a += payload.num;
}
}
// 处理 actions 可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}
export default {
state,
mutations,
actions,
getters
}
// index.vue 组件中
<template>
<div>
<el-button @click="add(params)">每次+1</el-button>
<h1>{{ "home 模块中 state.a=" + a }}</h1>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
data() {
return {
params: { num: 1 },
};
},
computed: {
// 方法二(推荐):更多灵活处理
...mapState({
// 子模块的属性因有了命名空间 无法直接使用字符串数组
a: (state) => state.home.a, // 注意:此处拿到的是 home 模块的数据
}),
methods: {
// 常规方法:
// add() {
// this.$store.commit("increment", { num: 1 });
// },
// 推荐方法:
...mapMutations({
add: "increment", // 需要传递参数直接把参数放在 add()方法的实参中
}),
},
},
};
</script>
7.3 Actions (异步更改 Store 中的状态)
// src/store/home/index.js 文件中
import { getCategoryList } from "@api";
// 存储仓库数据的地方
const state = {
// state 中数据默认初始值
categoryList:[]
}
// 修改 state 的唯一方式
const mutations = {
CATEGORYLIST(state, payload) {
state.categoryList = payload;
}
}
// 处理 actions 可以书写自己的业务逻辑,也可以处理异步
const actions = {
// 调用接口,发起请求
// 方法的形参可以直接将commit解构出来,这样可以方便后续操作
async categoryList({commit}) {
let result = await getCategoryList();
if (result.code == 200) {
// 提交到 mutstions 中
commit("CATEGORYLIST",result.data)
}
}
}
// 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}
export default {
state,
mutations,
actions,
getters
}
// index.vue 组件中
<template>
<div></div>
</template>
<script>
export default {
mounted() {
// 通知 Vuex 发起请求,存储于仓库中
this.$store.dispatch("categoryList"); // categoryList 是 Actions 中的方法名
},
};
</script>
8. Lodash 插件 防抖和节流
8.1 防抖(debounce)
防抖:前面的所有触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发,只会执行最后一次
应用场景:
- DOM 元素的拖拽功能实现(mousemove)
- 搜索联想(keyup)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lodash.js"></script>
</head>
<body>
搜索:<input type="text">
<script>
let inp =document.querySelector("input")
// 应用场景:在搜索引擎中输入时,合理的业务逻辑应该是 在最后一个字输入后等待 1s,再发起Ajax请求获取搜索建议
// oninput 事件在用户输入时触发,它是在元素值发生变化时立即触发----显然需要做相应的逻辑处理;
inp.oninput = _.debounce(function () {
console.log('用户输入完毕,1s后发起Ajax请求!');
},1000);
</script>
</body>
</html>
8.2 节流(throttle)
节流:在规定的时间间隔范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发。
应用场景:
- scroll事件滚动触发事件
- 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 表单验证
- 按钮提交事件。
- 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lodash.js"></script>
</head>
<body>
<h2>0</h2>
<button>加1</button>
<script>
let h2 = document.querySelector("h2");
let btn = document.querySelector("button");
let num =0;
// 场景:点击按钮每次+1,不能太频繁,(1s 内点击按钮最多能加一次)
// btn.onclick=function(){
// h2.innerHTML = num++
// }
btn.onclick = _.throttle(function(){
h2.innerHTML = num++
},1500)
</script>
</body>
</html>
9. 编程式跳转 + 事件委托
场景: 我想点击 1/2/3级菜单,然后进行跳转,并获取该级别菜单的 categoryName、categoryId
路由跳转问题:
对于导航式路由,我们有多少个 a 标签就会生成多少个 router-link 标签,这样当我们频繁操作时会出现卡顿现象。 对于编程式路由,我们是通过触发点击事件实现路由跳转。同理有多少个 a 标签就会有多少个触发函数。虽然不会出现卡顿,但是也会影响性能。
事件委派问题:
(1)如何确定我们点击的一定是a标签呢?如何保证我们只能通过点击 a 标签才跳转呢? (2)如何获取子节点标签的商品名称和商品 id (我们是通过商品名称和商品 id 进行页面跳转的)
解决方法:编程式导航 + 事件委托
对于问题1:为三个等级的a标签添加自定义属性 date-categoryName 绑定商品标签名称来标识 a 标签(其余的标签是没有该属性的)。
对于问题2:为三个等级的a标签再添加自定义属性 data-category1Id、data-category2Id、data-category3Id 来获取三个等级 a 标签的商品id,用于路由跳转。
我们可以通过在函数中传入 event 参数,获取当前的点击事件,通过 event.target 属性获取当前点击节点,再通过 dataset 属性获取节点的属性信息。
<template>
<div>
<div class="box" @click="goSearch" style="display: flex">
<h1 v-for="c1 in menu" :key="c1.categoryId">
<!-- 循环出来的一级菜单 -->
<a
:data-categoryName="c1.categoryName"
:data-category1Id="c1.categoryId"
style="color: red"
>{{ c1.categoryName }}</a
>
<h2 v-for="c2 in c1" :key="c2.categoryId">
<!-- 循环出来的二级菜单 -->
<a
:data-categoryName="c2.categoryName"
:data-category2Id="c2.categoryId"
style="color: green"
>{{ c2.categoryName }}</a
>
<h3 v-for="c3 in c2.categoryChild" :key="c3.categoryId">
<!-- 循环出来的三级菜单 -->
<a
:data-categoryName="c3.categoryName"
:data-category3Id="c3.categoryId"
>{{ c3.categoryName }}</a
>
</h3>
</h2>
</h1>
</div>
</div>
</template>
<script>
export default {
name: "",
created() {},
data() {
return {
menu: [
{
categoryChild: {
categoryChild: [
{
categoryName: "电子书",
categoryId: 5,
},
{
categoryName: "网络原创",
categoryId: 6,
},
{
categoryName: "数字杂志",
categoryId: 7,
},
{
categoryName: "多媒体图书",
categoryId: 8,
},
],
categoryName: "电子书刊",
categoryId: 3,
},
categoryName: "书籍",
categoryId: 1,
},
{
categoryChild: {
categoryChild: [
{
categoryName: "游戏机",
categoryId: 9,
},
{
categoryName: "游戏耳机",
categoryId: 10,
},
{
categoryName: "手柄/方向盘",
categoryId: 11,
},
{
categoryName: "游戏软件",
categoryId: 12,
},
{
categoryName: "游戏周边",
categoryId: 13,
},
],
categoryName: "游戏设备",
categoryId: 4,
},
categoryName: "电子设备",
categoryId: 2,
},
],
};
},
methods: {
goSearch(event) {
// event.currentTarget : 指的是绑定了事件监听的元素(可以理解为触发事件元素的父级元素)
// event.target 获取触发特定事件的元素
// element.dataset 获取 HTML元素自定义的 data-* 属性
// 当前点击的 DOM元素
let element = event.target;
// 注意:data-* 属性 在 html 中会把大写转为小写
//获取目前鼠标点击标签的categoryname,category1id,category2id,category3id,
// 通过四个属性是否存在来判断是否为a标签,以及属于哪一个等级的a标签
let { categoryname, category1id, category2id, category3id } =
element.dataset;
// categoryname 存在,表示为 a 标签
if (categoryname) {
let location = { name: "Search" }; // 跳转路由 name
let query = { categoryName: categoryname }; // 路由参数
if (category1id) {
//category2id 一级a标签
query.category1Id = category1id;
} else if (category2id) {
//category2id 二级a标签
query.category2Id = category2id;
} else {
//category2id 三级a标签
query.category3Id = category3id;
}
//整理完参数
location.query = query;
this.$router.push(location);
}
},
},
};
</script>