Vue2电商前台项目——完成Search搜索模块业务
Vue基础知识点击此处——Vue.js
文章目录
- Vue2电商前台项目——完成Search搜索模块业务
- 一、项目开发的步骤
- 二、各种请求数据并展示数据
- 1、写Search模块的接口
- 2、写Vuex中的search仓库
- 3、组件拿到search仓库的数据
- (1)观察数据结构
- (2)配置getters来简化数据
- 4、渲染商品数据到页面
- 5、search模块根据不同的参数获取数据展示
- (1)把派发actions的操作封装为函数
- (2)设置参数默认值
- (3)Object.assign合并对象
- (4)把三级联动或搜索的参数拿过来
- (5)点击三级联动或搜索就再次请求数据
- 6、渲染子组件数据到页面
- 三、面包屑部分开发
- 1、面包屑的展示
- 2、点击❌删除三级联动的相关数据
- 3、点击❌删除搜索关键字的相关数据
- 4、面包屑处理品牌信息
- (1)子传父:组件自定义事件
- (2)请求后台数据并添加品牌面包屑和❌
- 四、平台售卖属性的操作
- 1、子传父:组件自定义事件
- 2、请求后台数据并添加面包屑和❌
- 五、Search模块的排序操作(重点)
- 1、高亮样式的展示
- 2、升降序箭头的展示
- 3、切换高亮和箭头方向
- 六、实现分页器功能(重点)
- 1、分页器所需要的数据(参数)
- 2、连续页的起始与结束数字计算
- (1)计算总页数
- (2)计算连续页的开始和结束数字
- (3)展示连续页
- 3、首尾页和省略号什么时候显示?
- 4、把假数据替换为真数据
- 5、用户点击时把当前点击页码传给服务器发请求
- 6、禁用一些按钮
- 7、当前页高亮效果(动态添加类名)
一、项目开发的步骤
1、书写静态页面
2、拆分组件
3、获取服务器的数据动态展示:写api => Vuex调用api把数据放store => 组件拿到仓库store的数据 => 动态渲染
4、完成相应的动态业务逻辑
这里拆分组件比较简单,把中间这部分拆分出来,其他地方直接在Search组件中保留
建议大家多熟悉熟悉Vuex,熟能生巧,做多了发现并不难,都是一个套路,点击此处——Vuex
二、各种请求数据并展示数据
流程:写api => Vuex调用api请求数据放store => 组件拿到仓库store的数据 => 动态渲染
1、写Search模块的接口
这是向真正的服务器发请求,所以用封装好的axios实例:requests。带参数,且参数params至少有个默认值(空对象{}
)否则会请求失败。
src/api/index.js
//本文件用于:API的统一管理
import requests from './request'; //axios请求后台ajax数据
import mockRequests from './mockRequest'; //axios请求后台mock数据
........
........
//4.Search模块的接口
// /api/list post 需要带参数,且参数params至少有个默认值(空对象{})否则会请求失败
export const reqSearchInfo = function (params) {
return requests({
url: '/list',
method: 'post',
data: params //请求体参数
})
}
2、写Vuex中的search仓库
注意调用接口时要传参,默认值要设置为空对象,不然请求会失败的
src/store/search/index.js
//本文件用于配置search模块的数据
import { reqSearchInfo } from '@/api'
export const search = {
namespaced: true,
state: {
searchInfo: {}
},
actions: {
async getSearchInfo(context, params = {}) {
let result = await reqSearchInfo(params); //这里至少要传一个默认参数:空对象,否则请求不成功
if (result.code === 200) {
context.commit('GETSEARCHINFO', result.data);
}
}
},
mutations: {
GETSEARCHINFO(state, value) {
state.searchInfo = value;
}
},
getters: {}
}
3、组件拿到search仓库的数据
(1)观察数据结构
观察数据,我们要拿到searchInfo
中的attrsList,goodsList,trademarkList
对应的关系是这样的:
(2)配置getters来简化数据
如果不使用getters而是使用mapState来接收的话,实际上是很麻烦的,每次都要写state.search.searchList.XXX,而且很容易出错
所以这里我们选择在仓库中配置getters:
//getters类似计算属性,可以简化仓库中的数据
getters: {
//当前形参是当前仓库的state
goodsList(state) {
//要等searchInfo的数据拿回来了再返回值,要不然可能就直接拿空对象了
return state.searchList.goodsList || []; //如果没网返回空数组
},
attrsList(state) {
return state.searchList.attrsList || [];
},
trademarkList(state) {
return state.searchList.trademarkList || [];
},
}
注意这里如果没网的话请求不到数据,searchInfo就是空对象,再点儿什么什么会是undefined,这时候我们要加个或的条件,默认空数组,这样没网时遍历空数组就不会出问题
从组件读取getters数据:
注意:如果开启了命名空间,就要写成:...mapGetters('search', ['goodsList', 'attrsList', 'trademarkList'])
4、渲染商品数据到页面
v-for遍历数组,然后把对应的数据填进去就行了
5、search模块根据不同的参数获取数据展示
后端对数据处理完后,返回筛选后的响应体,然后仓库中拿着筛选后的响应体给state => state再给getters处理一下子 => 组件再用mapGetters接收 => 再把数据更新到页面
(1)把派发actions的操作封装为函数
当用户点击搜索或三级联动的时候,需要根据关键字再发一次请求来获取相应的数据,而我们派发action
请求数据的操作是放在Search的mounted
挂载函数里面的,而我们在Search
页再点搜索或三级联动时,mounted不会再执行了.
所以我们应该把派发actions请求的操作封装成函数,在需要时候调用。
(2)设置参数默认值
观察api接口文档,发现向服务器发请求时可以带10个参数,我们将这些参数的默认值配置在Search组件的data中,以对象的形式存储,然后在派发actions请求时把这个对象传过去,就能够作为axios发送ajax请求的请求体参数,然后后台拿着这些传过来的参数进行一些筛选排序之类的操作。
(3)Object.assign合并对象
复习一个api:Object.assign(target, source)
,它有个功能是可以合并具有相同属性的对象,返回修改后的对象
(4)把三级联动或搜索的参数拿过来
用上面那个api,可以把我们点击三级联动或搜索时query和params参数拿过来(注意名字要一致),然后重名的属性相继覆盖。
这里要在mounted
之前(created
或beforeMount
都行),因为要在派发请求之前拿到带三级联动或搜索带的query和params参数的searchParams
,这样就可以将我们从三级联动或搜索获取到的参数覆盖掉初始带给服务器的参数。
后面两个对象依次覆盖前面重复的属性值,最后第一个对象原值改变,返回第一个对象,这样就能把相应的参数带给后端,然后后端再一过滤啥的,就能实现搜索功能了
(5)点击三级联动或搜索就再次请求数据
我们目前呢还是只发了一次请求,因为只在
mounted
里面调用了getData()
,但是我们必须实现点击
三级联动或搜索就调用getData()
(发送Ajax请求)
我们观察开发者工具,会发现其实跳转到路由组件后,组件身上的data
中会有$route
这个数据
每次用户点击三级联动或者搜索,都会触发push
路由跳转和传参(如果已经跳了就只传参),那么我们在进入Search组件
后,每次点击三级联动或者搜索都会带来$route
中参数的变化,也就是$route
的变化。
那么这样的话我们就可以去监视$route的变化,每次只要参数发生变化,就重新整理数据并派发请求,然后服务器根据传过去的参数做一些操作(筛选),然后把处理过的数据响应过来,就可以在页面展示了
6、渲染子组件数据到页面
子组件用到的是getters中的attrsList
和 trademarkList
这里和前面Floor是不同的,这里可以直接让子组件从仓库中拿数据,这是因为这个数据的结构是{ [], [], [] ...}
,而且我们已经通过getters把每个数组分出来了,且没有组件复用的情况,所以不用父传子(当然父传子也可以,但是没必要)
拿到数据之后v-for对应生成列表就行
三、面包屑部分开发
所谓的面包屑有点像标签,也就是这个东西:
我们可以根据用户请求的数据来决定是否展示面包屑,且删除某个面包屑时,数据要重新发起请求返回新的后台筛选数据放到页面上。
1、面包屑的展示
我们首先要解决面包屑的展示问题,如果携带的参数searchParams.categoryName为空,就是没有面包屑,有的话,面包屑就是这个参数的值(参数searchParams.categoryName是否有值是由路由跳转时是否传入categoryName决定的,是结合Object.assign)。所以这里可以用v-if来判断是否展示面包屑。
然后在❌的地方分别配置删除三级联动和搜索回调
2、点击❌删除三级联动的相关数据
注意:只有searchParams里的某属性为空时,才会返回该属性对应所有数据
1、首先名字置空(或undefined,如果属性值为""还是会把相应的字段带给服务器。但是你把相应的字段变为undefined,当前这个字段不会带给服务器-------性能更好,省带宽),这样的话面包屑先取消显示(v-if控制)
2、其次所有Id也要置空,这样删除面包屑后才能返回全部的服务器数据(之前监视$route时id置空却不会返回全部数据是因为categoryName和keyword没有置空,只要这几个不是全空,服务器就会做一些筛选并返回相关数据)当然其实一步也可以不写,因为下一步👇
3、自己跳自己,去掉地址栏中的query参数(三级导航的数据),但是要保留params参数(搜索框的数据),因为这个回调是点击三级联动的面包屑才触发的,如果有搜索条件还是要保留keyword带来的数据的。其实仔细想想这段代码的目的就是去掉地址栏中的参数(如果不考虑监视$route会自动派发的话)
4、派发请求返回数据。当然如果写了第三步,第二步和第四步都可以省略,因为我们之前监视了$route的变化,一旦变化就id置空=>重新整理数据=>派发请求
5、具体的细节见如下代码
// 删除分类名字面包屑
deleteCategory() {
// 把带给服务器的参数置空了,还需要向服务器发请求
// 带给服务器参数说明可有可无,如果属性值为空的字符串还是会把相应的字段带给服务器,但如果把相应的字段变为undefined,当前这个字段不会带给服务器(省流)
//删除分类的名字和id,但置空的是本组件data内的数据,而不是$route中的query参数
this.searchParams.categoryName = undefined;
// this.searchParams.category1Id = undefined;
// this.searchParams.category2Id = undefined;
// this.searchParams.category3Id = undefined;
// 地址栏也需要修改:进行路由跳转
//去掉地址栏的query参数
this.$router.push({
name: "sousuo", //自己跳自己
params: this.$route.params, //只传params,query就被拿掉了
});
// this.getData(); //发请求返回全部数据,但是由于监视$route,有上边就不用这行了
},
3、点击❌删除搜索关键字的相关数据
和上边是类似的,一样的套路。这里有个需要注意的地方就是当我们去掉关键字对应的面包屑时,搜索框中的文字也要去除,所以这里我们要同步修改Header
中的keyword
使用的是全局事件总线:复习全局事件总线
先去main.js配置$bus
:
剩下的就是:
1、首先关键词置空(或undefined),这样的话面包屑先取消显示(v-if控制)
2、自己跳自己,去掉地址栏中的params参数(搜索关键字的数据),但是要保留query参数(三级联动的数据),因为这个回调是点击关键字的面包屑才触发的,如果有三级联动条件还是要保留categoryName带来的数据的。
3、派发请求返回数据。当然如果写了第二步,这一步可以省略,因为我们之前监视了$route的变化,一旦变化就重新整理数据=>派发请求
4、具体的细节见如下代码
deleteKeyword() {
//删除关键词,但置空的是本组件data内的数据,而不是$route中的params参数
this.searchParams.keyword = undefined;//点击x后,关键字置空,这样v-if就生效把节点弄没
this.$router.push({ //去掉地址栏的params参数
name: 'sousuo',
query: this.$route.query //只传query(有就筛选,没有就返回全部数据),params(华为)就被拿掉了
});
this.$bus.$emit('deleteKeyword'); //触发全局事件总线,把Header中的输入框关键字置空
// this.getData(); //发请求返回全部数据,但是由于监视$route,有上边就不用这行了
}
4、面包屑处理品牌信息
点击品牌信息出现面包屑,并且更新页面数据
观察:
1、这个品牌信息是在Search的子组件SearchSelector中的,而且是v-for遍历生成的。
2、后台接口可以接收一个trademark参数,用来进行筛选操作
3、trademark等参数我们是配置在Search组件中的,并以一个完整对象的方式通过dispatch发送给仓库,仓库再异步请求数据拿到响应结果
结论:我们应该把子组件SearchSelector
中的数据传给父组件Search
,父组件就可以拿着数据修改data
中传给后台的参数,再发送请求时就会把trademark
参数带给服务器了。
(1)子传父:组件自定义事件
1、父组件中给子组件标签添加自定义事件getTrademark
:
<SearchSelector @getTrademark="getTrademark" />
2、子组件通过点击事件触发自定义事件并把当前trademark传过去:
3、这样父组件就拿到了数据
(2)请求后台数据并添加品牌面包屑和❌
注意:只有searchParams里的某属性为空时,才会返回该属性对应所有数据
拿到数据之后按照api接口的格式调整数据给data中的searchParams.trademark
,然后发送请求传给服务器
getTrademark(trademark) {
//整理品牌字段参数 按照接口文档格式写"ID:品牌名称"
this.searchParams.trademark = `${trademark.tmId}:${trademark.tmName}`;
//要记住,只有searchParams里的某属性为空时,才会返回该属性对应所有数据
//所以下面这么写有bug是因为keyword置空了,但this.searchParams.trademark没有置空
// this.$router.push({
// name: 'sousuo',
// params: {keyword: trademark.tmName}
// });
this.getData();
}
点击❌时把trademark
置空:
<!-- 品牌面包屑 -->
<li class="with-x" v-if="searchParams.trademark">
{{searchParams.trademark.split(':')[1]}}<i @click="deleteTrademark">×</i>
</li>
deleteTrademark() {
//删除品牌的面包屑并重新请求
this.searchParams.trademark = undefined;
this.getData(); //这里边不涉及$route里的参数修改,所以不能靠监视来派发请求
},
四、平台售卖属性的操作
点击某个属性就会显示响应的面包屑,并发送相对应的数据给后台发请求,拿到新数据
1、子传父:组件自定义事件
大致的思路和上面的品牌信息面包屑是类似的,只是也有一些不同点。
1、先在父组件的子组件标签处写好自定义事件和它的回调
<SearchSelector @getTrademark="getTrademark" @getAttribute="getAttribute" />
......
<script>
......
getAttribute() {
//回调函数体
}
</script>
2、观察接口文档
观察接口文档,发现我们需要拿到的数据在以下位置,我们要拿到它们并整理成示例的格式
3、去子组件传值并触发自定义事件。根据上一步的分析,我们应该把a1(每个大对象)
和a2(每个attrValueList中的值)
都传给父组件,方便它整理数据。并给每个a2绑定点击事件
4、回调中触发自定义事件并传值:
5、父组件自定义事件的回调中接收值并整理数据
getAttribute(attr, attrValue) {
let prop = `${attr.attrId}:${attrValue}:${attr.attrName}`; //整理成api文档规定的格式
......
}
2、请求后台数据并添加面包屑和❌
分析:
1、props的参数格式默认为一个数组,需要存储多个属性元素
2、由于重复点击某个元素会重复添加,所以我们在往数组添加元素时应该进行重复判断
3、每次点击一个属性,都应该生成对应的面包屑且发送相应的请求
4、每次删除一个属性,都应该删除对应的面包屑且再次拿着props的剩余数据发送请求
通过以上分析,我们可以得出:
1、props的参数格式默认为一个数组,需要存储多个属性元素,所以在添加数据时应该使用数组的push
方法。
2、在添加时判断数组内有没有该元素即可,有就不添加,没有就添加,使用indexOf
所以父组件的自定义事件回调应该这么写:
getAttribute(attr, attrValue) {
let prop = `${attr.attrId}:${attrValue}:${attr.attrName}`;
if (this.searchParams.props.indexOf(prop) === -1) {
//加个判断:解决重复点击会重复显示多个面包屑的bug:props数组去重
//如果props里没有该元素,就添加进去并发请求,如果已经有了就不发请求了
this.searchParams.props.push(prop);
this.getData();
}
}
3、每次点击一个属性,都应该生成对应的面包屑且发送相应的请求,由于数组内有多个元素,页面生成面包屑不能再使用v-if
,而使用v-for
,页面展示时通过split
方法把字符串拆开拿到那个a2的值
4、每次删除一个属性,都应该删除对应的面包屑且再次拿着props的剩余数据发送请求,这里要删除数组中被点击的元素,我们用的方法是把index
传过去,并使用数组的splice
方法删除该元素,然后再次发送请求,这样就可以了
deleteAttr(index) {
//点击叉号时删除当前数组中的元素
this.searchParams.props.splice(index, 1);//(start,deletecount),改变原数组
this.getData();
},
五、Search模块的排序操作(重点)
需求:默认综合高亮降序,如果点击综合改变排序方式,点击价格则价格高亮,再点击价格改变排序方式:
查阅接口文档,后台默认收到的参数order
格式为:'排序字段 : 排序方式'
,其中1综合,2价格 asc升序 desc降序
,如order: '1:desc'
1、高亮样式的展示
高亮样式是否展示取决于当前order
中的排序字段是1还是2
,如果是1那么综合
有active
红色高亮样式,如果是2那么价格
有active
红色高亮样式,所以我们可以用计算属性来决定样式的展示
//决定排序属性是否高亮的两个计算属性
isOne() {
//如果order中包含1,那么就是当前按综合排序,返回true给avtive样式
return this.searchParams.order.includes('1');
},
isTwo() {
//如果order中包含2,那么就是当前按价格排序,返回true给avtive样式
return this.searchParams.order.includes('2');
},
2、升降序箭头的展示
1、箭头是否展示是和高亮同步的,如果高亮都没有,那么箭头肯定不展示,所以箭头这个标签外边要加上v-show
,值和高亮的那个一致
2、箭头是升序还是降序取决于当前order
中是asc
还是desc
,如果是asc那么上箭头显示,如果是desc就是下箭头展示。所以箭头这里的展示也要通过计算属性,方法是判断当前order
中是否包含asc
或desc
注:去阿里图标库找图标,字体图标样式在public/index.html
文件中引入
//决定排序箭头显示上箭头还是下箭头
isDesc() {
//如果order中包含desc,返回true,否则返回false
return this.searchParams.order.includes("desc");
},
isAsc() {
//如果order中包含asc,返回true,否则返回false
return this.searchParams.order.includes("asc");
},
3、切换高亮和箭头方向
通过以上分析我们发现,不管是高亮还是箭头是否展示,还是箭头的方向,都是由order中的数据决定的,只要data中的数据searchParams.order改变,Vue就会重新解析模板,就会影响这些东西的显示效果。所以我们操作数据并再次发送请求,就能够实现高亮、箭头、商品数据的同步。
1、绑定点击事件并传参用来表示当前点击的是哪个板块
2、把传过来的参数结合排序类型取反直接给 this.searchParams.order
重新赋值(通过模板字符串和三元表达式),然后再发请求就行了。其实直接改数据就行了,前面我们说了,数据改变Vue就会重新解析模板的。
changeOrder(orderNumber) {
//orderNumber是一个标记,代表用户点击的是综合(1)还是价格(2),用户点击时传过来
//1.获取当前状态的排序类型
let originOrderType = this.searchParams.order.split(':')[1];
//2.直接改数据,传入当前点击的参数,排序类型取反,然后就能引起Vue重新解析模板
this.searchParams.order = `${orderNumber}:${originOrderType === 'desc' ? 'asc' : 'desc'}`;
//3.再次发送请求
this.getData();
}
六、实现分页器功能(重点)
分页器因为好多地方都在用,所以我们将其封装为全局组件,步骤为创建引入注册使用
src/components/Pagination/index.vue
<template>
<div class="pagination">
<button>上一页</button>
<button>1</button>
<button>···</button>
<button>3</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>7</button>
<button>···</button>
<button>9</button>
<button>下一页</button>
<button style="margin-left: 30px">共 60 条</button>
</div>
</template>
<script>
export default {
name: 'Pagination',
};
</script>
<style lang="less" scoped>
.pagination {
text-align: center;
button {
margin: 0 5px;
background-color: #f4f4f5;
color: #606266;
outline: none;
border-radius: 2px;
padding: 0 4px;
vertical-align: top;
display: inline-block;
font-size: 13px;
min-width: 35.5px;
height: 28px;
line-height: 28px;
cursor: pointer;
box-sizing: border-box;
text-align: center;
border: 0;
&[disabled] {
color: #c0c4cc;
cursor: not-allowed;
}
&.active {
cursor: not-allowed;
background-color: #409eff;
color: #fff;
}
}
}
</style>
在Search组件使用:
1、分页器所需要的数据(参数)
- 需要知道当前是第几页:pageNo 字段代表当前页数
- 需要知道每一页需要展示多少条数据:pageSize字段进行代表
- 需要知道整个分页器一共有多少条数据:total字段进行代表----【通过和每页放几个能计算获取另外一条数据:一共有几页】
- 需要知道分页器连续页码个数:continues字段进行代表 ,一般是5 | 7 【为什么是奇数?因为对称好看】
2、连续页的起始与结束数字计算
页器在开发的时候先自己传递假的数据进行调试,调试成功后再用服务器数据
<div class="fr page">
分页器子组件,用props传数据给子组件,先用一些写死的数据用来调试逻辑
<Pagination :pageNo="27" :pageSize="3" :total="91" :continues="5" />
</div>
src/components/Pagination/index.vue
name: 'Pagination',
[当前页码,每页数据个数,总数据个数,连续页的个数]
props: ['pageNo', 'pageSize', 'total', 'continues'],
(1)计算总页数
computed: {
totalPage() {
//小学数学,计算总页数,向上取整
return Math.ceil(this.total / this.pageSize);
},
}
(2)计算连续页的开始和结束数字
经分析不难发现,首页和尾页都是固定的,但是连续页会不断变化,我们如果要展示连续页的内容,需要拿到连续页的首页start和尾页end,这样使用v-for遍历end生成1~end的全部按钮,再使用v-if把所有start之前的按钮删掉,连续页就表示出来了。所以我们首先要写一个计算属性来计算连续页的首尾数字。仔细看代码和注释,不难。
computed: {
totalPage() {
//小学数学,计算总页数,向上取整
return Math.ceil(this.total / this.pageSize);
},
//计算连续页的开始和结束页码,方便后续展示
startAndEndNum() {
let start = 0;
let end = 0;
//1.如果总页数小于连续的页码,那么start应该是1,end应该是总页数
//比如一共就4页,而连续页是5页
if (this.totalPage < this.continues) {
start = 1;
end = this.total;
}
//2.如果总页数>=连续页码数,就属于正常且复杂的现象了
else {
// 1 ... 5 6 7 8 9 ... 30
// 这里为什么不直接+2-2,是因为连续页码数不一定都是5,可以是7可以是9,当为7时应该+3-3,当为9时应该+4-4。直接写2就写死了
start = this.pageNo - Math.floor(this.continues / 2);
end = this.pageNo + Math.floor(this.continues / 2);
//bug1:当前页是1,start计算成负数或0
if (start < 1) {
start = 1; //如果到最左边的,start就应该是1
end = this.continues;
}
//bug2:当前页是尾页,end计算成比尾页还大的数
if (end > this.totalPage) {
start = this.totalPage - this.continues + 1;
end = this.totalPage; //如果到最右边,end就应该是总页数
}
}
let numObj = { start: start, end: end };
return numObj; //这个计算属性的值就是一个带有start和end的对象
},
},
(3)展示连续页
使用v-for
遍历end
生成1~end
的全部按钮,再使用v-if
把所有start
之前的按钮删掉,连续页就表示出来了。
3、首尾页和省略号什么时候显示?
先说第一页这部分,连续页展示时如果当前页过于靠前,可能就会出现这样的情况:
这样很明显不合理,我们应该给第一页和它旁边的省略号添加v-if
条件,在合适的时候显示。比较好的条件是:当连续页的首页start>1
时显示第一页;当连续页的首页start>2
时显示省略号。
当然,尾页这部分和它的省略号也是类似原理,连续页展示时如果当前页过于靠后,可能就会出现这样的情况:
4、把假数据替换为真数据
我们去给分页器子组件传值的Search
组件中把向后台请求数据的真实数据拿过来,方便实现响应式。pageNo
和pageSize
是服务器本来就接收的参数,continues先写5
<!-- 分页器 -->
<Pagination :pageNo="searchParams.pageNo"
:pageSize="searchParams.pageSize"
:total="total"
:continues="5"/>
而total
是从仓库里捞过来的数据,我们可以用State
把它搞过来
这样的话页面中用到的数据就和我们要拿着发请求的数据同步了,分页器可以实现响应式
5、用户点击时把当前点击页码传给服务器发请求
这里是子给父传值用到组件自定义事件
1、先在父组件中给分页器子组件绑定
需要传参的话父组件中绑定自定义事件不需要带(),传过来的参数会给它的回调
2、在分页器全局组件中传值吧,挨个儿传
3、然后父组件接过来把值传给服务器,然后发请求就欧了,这样在实现分页器响应式的基础上也实现了页面商品数据的同步。
6、禁用一些按钮
省略号需要一直禁用。
有一些按钮在特定情况下也要禁用,比如页面在第一页时的上一页按钮,页面在最后一页时的下一页按钮。
我们可以使用disabled禁用这些按钮。
7、当前页高亮效果(动态添加类名)
至此,Search模块笔记已整理完毕。