尚品汇小结(22-51)

防抖和节流

防抖(比如用户在input中输入关键字,oninput会打印出你输入时的每一次)

let inp = document.querySelector('input')
		inp.oninput = function(){
			console.log(this.value)
		}

节流(比如用户拖动滚动条时,你console.log的时候会一直打印)

const content = document.getElementById('content')
		window.onscroll = function(){
			let count = 0
			console.log(count)
		}

感觉有点像

但是防抖和节流的概念不一样

防抖合并事件并在最后时间去触发事件

节流则是隔段时间触发一次

这里是使用node_modules自带的lodash,把typeNav中changeIndex节流掉

 import _ from 'lodash' 

,这里是全局引入

import {throttle} from 'lodash/throttle'

这个是按需引入

changeIndex:throttle(function(index){
				this.currentIndex = index
			},50),

这个是在按需引入下的使用

手写防抖和节流还不会。。

typeNav三级联动的路由跳转

开始了typeNav的路由跳转

路由跳转有两种方式

一种是   router-link 跳转   一种是  this.$router.push/replace

 1.        我们首先尝试用router-link to="/search"

此时发现在选择typeNav会出现卡顿,这是因为用了三次v-for嵌套,因此用了router-link之后,下面的第二层和第三层会遍历很多很多遍,router-link本身就是组件,组件遍历多多多次,出现卡顿

2.        选择用this.$router.push

这个呢就需要事件来触发,如果在每个a标签下面加@click,感觉也可以的样子,三个不同的点击事件,把store中的categoryList的数据拿出来存放到data里面,第一层点击就用

let categoryname =this.categorylist.categoryName

let query1 = this.categorylist.categoryId

this.$router.push({name:'search',query:{categoryname:categoryname,categoryid:categoryid1}以此类推--------------------------->(个人想法,不知道怎么把state拿出来放在data里面)

就是点击typeNav里的a标签跳转到/search?xxx

首先呢把a标签里面的href删掉,这个没用,我们需要自己定义事件跳转页面

但是有三层,我们如果点击哪一层的话,也不知道,这时候  事件委派 出场了

事件委派

就是把事件绑定给共同的祖先,当事件触发的时候,通过冒泡,确定子代是哪个发生

通俗来讲,就是把事件给祖先绑定,然后用事件形参为e,只要你在一堆子元素里,e.target都能把你捞出来

getSearch(e){
				console.log(e.target)
}

确定了你是哪层之后,现在要确定你叫什么(也就是啥都不知道的情况下,点击标签的时候要知道他是哪家的(v-for的哪一层),他叫什么(categoryName),他男的女的(categoryId))

dataset可以知道自定义属性的属性值

这里我们就使用自定义属性完成点击的是哪一个

给每一层的v-for加自定义属性categoryName,但是这样还不够,我们还得知道点击的typeNav是哪一层的,则再加一个自定义属性categoryId

<div class="item"  v-for="(c1,index) in categoryList" :key="c1.categoryId" @click="getSearch" @mouseleave="leaveIndex">
	                    <h3 @mouseenter="changeIndex(index)"  :class="currentIndex==index ? 'cur' : ''" >
	                        <a :data-category="c1.categoryName" :data-categoryid1="c1.categoryId">{{c1.categoryName}}</a>
							<!-- <router-link to="/search">{{c1.categoryName}}</router-link> -->
	                    </h3>
	                    <div class="item-list clearfix" :style="{display:currentIndex==index?'block':'none'}">
	                        <div class="subitem" v-for="(c2,indey) in c1.categoryChild" :key="c2.categoryId">
	                            <dl class="fore">
	                                <dt>
	                                    <a :data-category="c2.categoryName" :data-categoryid2="c2.categoryId">{{c2.categoryName}}</a>
										<!-- <router-link to="/search">{{c2.categoryName}}</router-link> -->
	                                </dt>
	                                <dd v-for="(c3,indez) in c2.categoryChild" :key="c3.categoryId">
	                                    <em>
	                                        <a :data-category="c3.categoryName" :data-categoryid3="c3.categoryId">{{c3.categoryName}}</a>
											<!-- <router-link to="/search">{{c3.categoryName}}</router-link> -->
	                                    </em>
	                                </dd>
	                            </dl>
	                        </div>
	                    </div>
	                </div>

然后通过解构赋值,把dataset的数据传递给categoryName、categoryId等

现在开始拼接

把categoryName赋给query,还需要知道categoryId,则需要if判断为哪一层,判断完之后在query添加属性用query.categoryId = xxx

最后再把name和query拼接起来完成整个url

getSearch(e){
				// console.log(e.target)
				let element = e.target
				console.log(element.dataset)
				// 节点中有一个属性dataset属性,可以获取节点的自定义属性和属性值
				const {category,categoryid1,categoryid2,categoryid3} = element.dataset
				const location = {name:'search'}
				const query = {categoryName:category}
				if(categoryid1){
					query.categoryid1 = categoryid1 
				}else if(categoryid2){
					query.categoryid2 = categoryid2
				}else{
					query.categoryid3 = categoryid3
				}
				// 整理参数
				location.query = query
				this.$router.push(location)
			}

实现url拼接输入框的文字和点击typeNav(检索这两个同时存在的url)

当我们点击typeNav之后搜索关键字的时候

    或者输入关键字再点击typeNav的时候

url为/关键字?typeNav的参数

header的index.js的代码为

				this.$router.push({name:'search',params:{keyword:this.keyword || undefined},query:{k:this.keyword.toUpperCase()}})

此时就会发现如果在输入关键字之后query和params都有值,而我们需要在输入关键字之后,url为/search/关键字,因此我们把query去掉,这样的话,只有name和params,当需要query时,我们再给他传值

this.$router.push({name:'search',params:{keyword:this.keyword || undefined}})

现在需要判断是否有query,如果有query的话,我们需要传入到location

                                              如果没有,直接输出location

if(this.$route.query){
					let location = {
						name:'search',
						params:{keyword:this.keyword || undefined},
					}
					location.query = this.$route.query
					this.$router.push(location)
				}else{
					let location = {
						name:'search',
						params:{keyword:this.keyword || undefined},
					}
					this.$router.push(location)
				}

模拟数据Mock

mock是前端伪造ajax请求,不发送给服务器,自己和自己请求

安装mockjs   npm i mockjs 

在文档中cv floors和banners的json文件(记得格式化,不然有空格的话文件就可能无法运行)

 floor和banners的json文件有图片,在public中的images引入(public文件夹在打包的时候,会把相应的资源原封不动打包到dist文件夹

在mock下新建一个mockServe文件,这个文件是用来引入mock和拿到数据,import Mock from 'mockjs' 记得前面的Mock要大写 ,引入floors和banners(图片、JSON文件是默认暴露的)

此时我们还没有在全局引入mock,需要在入口文件引入 import "mockServe" from "@/mock/mockServe"

使用mock数据

mock数据:第一个参数是请求地址,第二个参数是请求数据

Mock.mock('/mock/banners',{code:200,data:banners}) //模拟首页轮播图

模拟的数据写完了之后,就应该开始模拟请求了

在request下面新建一个目录叫做mockAjax里面存放

import axios from 'axios'

// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import "nprogress/nprogress.css"
// start:进度条开始			done:进度条结束

export function mockRequests(config) {
	const instance = axios.create({
		baseURL: '/mock',  // <-----很重要,不用向服务器发请求,是在本地发请求,所以直接写 /mock
		timeout: 3000
	})

	instance.interceptors.request.use(config => {
		// 进度条开始动
		nprogress.start()
		return config
	}, err => {
		// return Promise.reject(new Error(err))
	})

	instance.interceptors.response.use(res => {
		// 进度条停止
		nprogress.done()
		return res.data
	}, err => {
		return Promise.reject(new Error(err))
	})

	return instance(config)
}

之后需要使用这个请求,在index.js下面引入,并且一定要记得import mockRequests!!!

// 统一管理项目接口的模块
import { request } from "./request.js"
import { mockRequests } from './mockAjax.js'

export function reqCategoryList() {
	return request({
		url: '/product/getBaseCategoryList',
		methods: 'get'
	})
}
// export const reqCategoryList = () => requests.get('/product/getBaseCategoryList')

export function reqBannersList() {
	return mockRequests({
		url: '/banners',
		methods: 'get'
	})
}

请求网络也完成了,现在开始使用

因为轮播图只有在home里面使用,我们可以不在app.vue里面导入,直接在listContainer导入

首先派发action,在mounted里面写,this.$store.dispatch(’bannersList')

然后在store的home的index里面写

import {reqCategoryList} from '@/api/index.js'
import {reqBannersList} from '@/api/index.js'
const state = {
	// state 中数据默认初始值不能随便写,服务器返回对象就写对象
	categoryList:[],
	bannersList:[]
}
const mutations = {
	// CATEGORYLIST(state,categoryList)这里的categoryList只是传参,是从下面的commit(,res.data)里面传过来的
	CATEGORYLIST(state,categoryList){
		state.categoryList = categoryList.slice(0,16)
	},
	BANNERSLIST(state,bannersList){
		state.bannersList = bannersList
		// console.log()
	}
}
const actions = {
	
	// dispatch:异步操作,
	// commit :同步操作	
	// 首先,dispatch是去分发一个action的。而commit则是去调用mutations的,那这两个玩意儿又有啥区别呢
	// action: 用于调用mutations去变更state。
	// 自身会返回一个promise,
	// 支持异步操作

	// 通过api里面的接口函数调用,向服务器发请求,获取服务器的数据
	// 获取商品分类的数据
	async categoryList({commit}){
		// 这里要写async,否则显示的是一个promise对象,await相当于.then()
		const res = await reqCategoryList()
		// console.log(res)
		if(res.code == 200){
			commit('CATEGORYLIST',res.data)
		}
	},
	//获取轮播图数据
	async bannersList({commit}){
		const res = await reqBannersList()
		console.log(res)
		if(res.code == 200){
			commit('BANNERSLIST',res.data)
		}
	}
}
const getters = {}

export default {
	state,
	mutations,
	actions,
	getters
}

记得记得记得一定要导入在request下面写的请求,不然报错

现在state也有了,我们需要去使用他,就是在computed里面用mapstate映射数据

computed:{
			...mapState({
				bannersList:state => state.home.bannersList
			})
		}

你就会在浏览器的vuex里面看到bannersList下的4个数据

swiper

安装swiper(这里安装的是swipe5.4.5)npm i swiper@5.4.5

这里记得swiper的版本和使用相关,不同的版本,语法也有所不同

需要引入css和js

在home.vue里面导入js import Swiper from ‘swiper'

在main.js全局导入css import 'swiper/css/swiper.css'

能够获取bannersList的数据之后,用v-for循环出图片

现在还差初始化swiper

首先尝试在mounted中挂载初始swiper实例,发现行不通,此时的页面是静态的

原因是在mounted中有异步语句dispatch,当运行mounted时,dom还没有完全渲染完毕,v-for还没执行完毕,就开始执行初始swiper,这当然是行不通的

当mounted中有异步dispatch时,执行顺序:先执行mounted中同步的语句然后执行dispatch,dispatch发送给action,action执行完毕再执行初始swiper,最后执行mutations

我们需要在页面所有dom加载完毕之后再执行初始化swiper,也就是说初始化swiper需要异步

现在我们考虑把初始swiper放在updated中,但也不行,因为在updated中,假如有一个代码数据发生了变化都会调用一遍,整个页面都会刷新一遍,太消耗性能

watch+$nextTick(解决因为异步数据变化或者还没有完成,dom没有渲染完成的问题)

$nextTick经常与其他插件使用(需要dom存在)

考虑放在watch里面,watch监听某个数据的变化从而来执行一些操作

但是页面依旧没有变化,因此我们还需要$nextTick()

$nextTick所指定的回调函数,会在DOM节点更新完毕之后再执行

watch:{
			// 监听bannersLisr数据的变化,由空数组变4个元素
			bannersList:{
				handler(newValue,oldValue){
					this.$nextTick(()=>{
						var mySwiper = new Swiper ('.swiper-container', {
						   loop: true, // 循环模式选项
						   // nextTick所指定的回调函数,会在DOM节点更新完毕之后再执行
						   // 如果需要分页器
						   pagination: {
						     el: '.swiper-pagination',
										  clickable:true
						   },
						   
						   // 如果需要前进后退按钮
						   navigation: {
						     nextEl: '.swiper-button-next',
						     prevEl: '.swiper-button-prev',
						   },
						   
						   // 如果需要滚动条
						   scrollbar: {
						     el: '.swiper-scrollbar',
						   },
						 })
					})
					
				}
			}
		}

开始写floor的组件

跟listcontainer一样,来一个先派发dispatch到store的home.js中,action接收,接收之后传给mutation,mutation传递给state,因为有两个floor,而且里面的内容不同,所以遍历floor,自定义组件是可以被遍历的,在home.vue中接收的接收state的数据,再在floor中父传子得到数据,最后重复的在html中调用数据

由于floor也有轮播图,就考虑做一个全局组件,那样复用性高,不用重复代码

全局组件在components中新建一个目录carousel,里面导入swiper,把swiper的代码粘贴到carousel中,因为是轮播图,所以我们需要数据中的图片,也就是需要父传子(在floor和listContainer中props传递数据给carousel),而且我们需要把floor直接得到的数据改成watch+$nextTick的形式

这里由于是父传子得到的数据,watch的话,数据没有变化则不会返回里面的内容,

这里immediate就出现了,这个是立即执行的,不管你的数据有没有变化,他都会马上执行

然后把floor和listContainer中的数据父传子给carousel

<template>
	<div class="swiper-container" id="floor1Swiper" ref="cur">
	    <div class="swiper-wrapper">
	        <div class="swiper-slide" v-for="(carousel,index) in list" :key="carousel.id">
	            <img :src="carousel.imgUrl">
	        </div>
	    </div>
	    <!-- 如果需要分页器 -->
	    <div class="swiper-pagination"></div>
		
	    <!-- 如果需要导航按钮 -->
	    <div class="swiper-button-prev"></div>
	    <div class="swiper-button-next"></div>
	</div>
</template>

<script>
	import Swiper from 'swiper'
	export default{
		name:'carousel',
		props:{
			list:{
				type:Array
			}
		},
		watch:{
			list:{
				// 立即监听,不管数据有没有变化,上来就监听
				immediate:true,
				handler(newValue,oldValue){
					// 只能监听到数据已经有了,但是v-for动态渲染结构我们还是没有办法确定的,因此还是需要用nextTick
					// console.log('hhh ')
					this.$nextTick(()=>{
						var mySwiper = new Swiper (this.$refs.cur, {
						   loop: true, // 循环模式选项
						   
						   // 如果需要分页器
						   pagination: {
						     el: '.swiper-pagination',
							 clickable:true, // 点击分页器切换
						   },
						   
						   // 如果需要前进后退按钮
						   navigation: {
						     nextEl: '.swiper-button-next',
						     prevEl: '.swiper-button-prev',
						   },
						   
						   // 如果需要滚动条
						   scrollbar: {
						     el: '.swiper-scrollbar',
						   },
						 })
					})
				}
			}
		}
	}
</script>

<style>
</style>

开发模块的套路

1.先静态页面 + 静态组件拆分出来

2.发请求(API)

3.vuex(三连环)

4.组件获取仓库数据,动态展示数据

search模块的开发

先导入search的静态页面,并能使静态页面成功展示,search的index.vue由两部分组成,上面由SearchSelector构成,下面由search的商品列表以及分页器构成

发请求给search的商品列表,商品的请求地址为/list,请求方式为post,但是跟之前的不同的是,这次是有请求参数,把params放在data中,携带参数请求服务器

export function reqSearchList(params){
	return request({
		url:'/list',
		method:'post',
		data:params
	})
}
// export const reqSearchList = (params) => request({url:'/list',methods:'post',data:params})

跟之前的三连环的套路差不多,在search的index中派发actions的请求,后面要跟一个空对象

mounted() {
			this.$store.dispatch('searchList', {})
		},

在store的search中接收数据,params当作形参,而且至少要是一个空对象,

post请求用data来接收参数(走的是请求体),get请求用params来接收参数(这个走的是路径)

get用params,“请求参数”

post用data,“推送用户数据”

const actions = {
	async searchList({commit},params={}){
		// 当前这个reqSearchList这个函数在调用获取服务器数据的时候,至少传递一个参数(空对象)
		// params形参,是当用户派发actions的时候,第二个参数传递过来的,至少是一个空对象
		const res = await reqSearchList(params)
		console.log(res)
		if(res.code == 200)
		commit('SEARCHLIST',res.data)
	}
}

然后commit给mutation,在传给state

在actions里面的console可以知道这个数据里面还有三个对象,而且要在search使用,为了增加复用性,在getters里面把三个对象分别拆出来,在search的index用computed分别接收,

接收到goodslist的数据之后,把数据遍历出来

在input关键字和分类的query两层之中检索相关的商品

如果要检索的话,就必须要传递相关的query,把keyword和categoryName以及categoryId传递进去,之前的空对象就要被替换,用data把要传递进去的数据用searchParams传递

但问题是,如何在点击typeNav和输入关键字的搜索时更新商品的页面

mounted+beforMount只能把点击之后传递的数据能用vuetool显示出来,并不能完成页面的更新

我们需要用路由的变化完成这个需求

watch+$route

用watch监听变化,如果路由发生了变化,则就调用接口的数据

<script>
	import {mapGetters} from 'vuex'
	import SearchSelector from './SearchSelector/SearchSelector'
	export default {
		name: 'search',
		data(){
			return{
				searchParams:{
					category1Id:'',
					category2Id:'',
					category3Id:'',
					categoryName:'',
					keyword:'',
					// 筛选标签
					props:[],
					// 品牌
					trademark:'',
					// 排序
					order:'',
					pageNo:1,
					pageSize:10
				}
			}
		},
		components: {
			SearchSelector
		},
		beforeMount() {
			// this.searchParams.category1Id = this.$route.query.category1Id
			// this.searchParams.category2Id = this.$route.query.category2Id
			// this.searchParams.category3Id = this.$route.query.category3Id
			// this.searchParams.categoryName = this.$route.query.categoryName
			// this.searchParams.keyword = this.$route.params.keyword
			// console.log(this.$route.query)
			// Object.assign(this.searchParams,this.$route.query,this.$route.params)
			// console.log('发送请求之前',this.searchParams)
		},
		mounted() {
			// 在发请求之前带给服务器参数【searchParams参数发生变化有数值带给服务器】
			// this.getData()
		},
		computed: {
			...mapGetters(['goodsList'])
		},
		methods:{
			getData(){
				this.$store.dispatch('searchList', this.searchParams)
			}
		},
		watch:{
			$route(newValue,oldValue){
				// 发请求之前整理带给服务器参数
					Object.assign(this.searchParams,this.$route.query,this.$route.params)
					this.getData()
					console.log(this.searchParams)
					this.searchParams.category1Id = ''
					this.searchParams.category2Id = ''
					this.searchParams.category3Id = ''
				}	
		}
	}
</script>

以上是点击typeNav和输入input之后,url携带了params和query的过程

添加面包屑(typeNav分类/关键字/品牌)

添加typeNav分类的面包屑

当携带了参数后而且可以随时监听路由发生变化之后请求api接口,得到相应的数据

在search添加分类面包屑

<ul class="fl sui-tag">
	<!-- 分类的面包屑 -->
	<li class="with-x" v-show="searchParams.categoryName">{{searchParams.categoryName}}<i @click="removeCategoryName">×</i></li>
</ul>

当searchParams.categoryName不为空时,则显示

关闭分类绑定的事件,需要在关闭面包屑的同时把分类的query也去掉,并且清空之前留下来的数据,可以把undefined赋给这些数据,而不是用‘’,因为undefined可以不把这些数据带给服务器,页面的性能也就更高

// 分类li的移除按钮
			removeCategoryName(){
				// 带给服务器参数说明可有可无:如果属性值为空的字符串还是会把相应的字段带给服务器
				// 但是你把相应的字段变成undefined,这个字段不会带给服务器
				this.searchParams.categoryName = undefined
				this.searchParams.category1Id = undefined
				this.searchParams.category2Id = undefined
				this.searchParams.category3Id = undefined
				// this.getData()	
				this.$router.push({name:'search',params:{keyword:this.searchParams.keyword}})
			},

添加关键字的面包屑

在上面的ul里面添加关键字的面包屑

<li class="with-x" v-show="searchParams.keyword">{{searchParams.keyword}}<i @click="removeKeyword">×</i></li>

这个跟上面的同理,但是这个面包屑关闭的时候,需要把params去掉

注:这个关键字移除的需求并没有完整,完整的代码在下面点

// 关键字的移除按钮
			removeKeyword(){
				this.searchParams.keyword = undefined
				if(this.$route.query){
					this.$router.push({name:'search',query:this.$route.query})
				}
			},

此时我们发现面包屑关闭了,但是输入关键字的input的里面还是有关键字,我们需要清空

输入关键字的input在header组件里面,header组件和search是兄弟组件,我们需要兄弟组件通信,此时EventBus(事件总线)出场了

EventBus(事件总线)

在main.js里面配置$bus

new Vue({
  router,
  // 注册仓库:组件实例身上会多一个$store属性
  store,
  render: h => h(App),
  // 全局事件总线$bus配置
  beforeCreate() {
  	Vue.prototype.$bus = this
  }

因为是通过点击关闭关键字的那个按钮,所以在search中发射这个事件,在header中接收

完整的移除关键字的代码

search

// 关键字的移除按钮
			removeKeyword(){
				this.searchParams.keyword = undefined
				// 通知兄弟组件header清除关键字
				this.$bus.$emit('clear')
				if(this.$route.query){
					this.$router.push({name:'search',query:this.$route.query})
				}
			},

header中挂载

		mounted() {
			this.$bus.$on('clear',()=>{
				this.keyword = ''
			})
		}

品牌的面包屑

当点击品牌中的item时,item传到面包屑中,需要子(searchSelector)传父(search),用$emit

searchSelector

<li @click="trademarkItem(trademark)">{{trademark.tmName}}</li>
methods:{
			trademarkItem(item){
				this.$emit('TrademarkItem',item)
			}
		}

search

			// 品牌的添加li按钮
			getTrademark(item){
				console.log(item)
				// this.searchParams.trademark = item.tmName
				this.searchParams.trademark = `${item.tmId}:${item.tmName}`
				console.log(this.searchParams)
				this.getData()
			},
			removeTrademark(){
				this.searchParams.trademark = undefined
			}

商品点击参数添加props以及面包屑

props这个是在data里面的一个属性,

props是一个数组,而且要求格式为:商品属性的数组: ["属性ID:属性值:属性名"]

示例: ["2:6.0~6.24英寸:屏幕尺寸"]

是一个用:分割,有3个值的数组

data(){
			return{
				searchParams:{
					category1Id:'',
					category2Id:'',
					category3Id:'',
					categoryName:'',
					keyword:'',
					// 筛选标签
					props:[],
					// 品牌
					trademark:'',
					// 排序
					order:'1:asc',
					pageNo:1,
					pageSize:10
				}
			}
		},

因为这个是在searchSelector选择的,面包屑是在select里面的,涉及到了父子通信

<li v-for="(attr,index) in attrs.attrValueList"  @click="attrItem(attr,attrs)" :key="index">
						<a>{{attr}}</a>
</li>

当然这个通信是子传父,而且需要把三个值都传递出来,点击的时候传递了attrs这整个关于li的消息,所以考虑在子组件中就把那三个值给完整的传递给父组件

这时候需要模板字符串组成一个新的内容并且传递出去

attrItem(attr,attrs){
				// console.log(attrs)
				let attrList = `${attrs.attrId}:${attr}:${attrs.attrName}`
				// attrList.push(attrs.attrId,attr,attrs.attrName)
				this.$emit('attrItem',attrList)
			}

在父组件里面使用的子组件中把数据接收到

<SearchSelector @TrademarkItem="getTrademark" @attrItem="getProps"/>

然后在父组件中使用,需要切割数组,并且显示属性值,而且要记得去重,去重现在知道三种方法

数组去重

1.indexOf()

2.includes()

这个返回的不是一个数组,而是一个对象,数组去重不能使用这个方法   new Set()

<!-- 筛选标签的面包屑 -->
<li class="with-x" v-if="searchParams.props" v-for="(item,index) in searchParams.props">{{item.split(':')[1]}}<i @click="removeProps(index)">×</i></li>	
getProps(attrList){
				// if(this.searchParams.props.indexOf(attrList)==-1){
				// 	this.searchParams.props.push(attrList)
				// }
				if(this.searchParams.props.includes(attrList)==false){
					this.searchParams.props.push(attrList)
				}
				console.log(attrList)
				// let pushlist = this.searchParams.props.push(attrList)
				// console.log(pushlist)
				// this.searchParams.props = new Set(this.searchParams.props)
				// console.log(this.searchParams.props)
			},

关闭这个面包屑的时候,需要删除那个数组,所以也需要index来标识是哪一个面包屑

removeProps(index){
				// console.log(index)
				this.searchParams.props.splice(index,1)
				console.log(this.searchParams.props)
				// console.log()
			},

综合和价格排序

<li :class="{active:isOne}" @click="changeOrder(1)">
	<a href="#">综合 
		<span v-show="isOne&&isDesc">⬇</span>
		<span v-show="isOne&&isAsc">⬆</span>
	</a>
</li>
<li :class="{active:isTwo}" @click="changeOrder(2)">
	<a href="#">销量 
		<span v-show="isTwo&&isDesc">⬇</span>
		<span v-show="isTwo&&isAsc">⬆</span>
	</a>
</li>

这个需要点击哪一个li,哪一个li的颜色就发生变化,很明显用:class动态绑定颜色,但是为了提高性能,我们把后面的判断用计算属性,这个order里面是否含有1还是2来判断true还是false,记得computed的方法里面的数据需要return

isOne(){
				return this.searchParams.order.indexOf('1')!==-1
},
isTwo(){
				return this.searchParams.order.indexOf('2')!==-1
},

接下来用@click绑定事件,用传参的方式来表明是点击了综合还是价格,而且点击之后要取反,因为有升序还是降序两个箭头

changeOrder(flag){
				let flagNum = this.searchParams.order.split(':')[0]
				let flagText = this.searchParams.order.split(':')[1]
				let flagList = this.searchParams.order
				if(flagNum == flag){
					// if(flagText == 'asc'){
					// 	flagText = 'desc'
					// }else{
					// 	flagText = 'asc'
					// }	
					flagList = `${flagNum}:${flagText=='asc'?'desc':'asc'}`
				}else{
					// if(flagText == 'asc'){
					// 	flagNum = flag
					// 	flagText = 'desc'
					// }else{
					// 	flagNum = flag
					// 	flagText = 'asc'
					// }	
					flagList = `${flag}:${flagText=='asc'?'desc':'asc'}`
				}
				// this.searchParams.order = `${flagNum}:${flagText}`
				this.searchParams.order = flagList
				console.log(this.searchParams.order)
			}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值