创建分析my-tabs
my-tabs是一个通用轮子,具备哪些能力
数据tab 不能写死,样式不能固定
1.可以在父组件中定制样式、
// 配置对象config: {type: Object,//default如果是复杂数据类型,那么需要指定value为一个函数,通过返回值的形式指定默认值default: () => {return {};}
2.可以在父组件中指定数据
// 父组件传入的 tabs 数据tabData: {type: Array,//默认空数组default: () => []},
3.可以在父组件中选中项
// 默认激活项defaultIndex: {type: Number,default: 0},
封装网络请求
调用服务端接口获得数据
微信开发者工具
wx.request
uni-app
创建utils 封装请求对象 request.js
const BASE_URL = 'https://api.imooc-blog.lgdsunday.club/api';function request({ url, data, method }) {return new Promise((resolve, reject) => {//uni.request 发起网络请求uni.request({url: BASE_URL + url,data,method,success: ({ data }) => {if (data.success) {//请求成功resolve(data);} else {//请求失败//给用户一个提示uni.showToast({title: data.message,icon: 'none',mask: true,duration: 3000});reject(data.message);}},fail: (error) => {reject(error);},complete: () => {// 关闭加载uni.hideLoading();}});});}export default request;
新建/api/hot.js
将放置所有和热搜相关的接口
如果不指定methods ,默认get请求
不指定data,默认不传递任何参数
import request from '../utils/request';export function getHotTabs() {return request({url: '/hot/tabs'});}
在hot.vue里面使用hot.js
使用hot.js里面暴露出来的方法
import { getHotTabs, getHotListFromTabType } from 'api/hot';
在methods里面定于获取数据的方法
methods: {//获取热搜文章类型async loadHotTabs(){const res = await getHotTabs()console.log(res)}}
created时候调用
//created: 组件实现配置完成,但DOM未渲染,进行网络请求,配置响应式数据created(){this.loadHotTabs()}
基本的数据展示
在hot.vue里面声明数据
tab数据源
data() {return {// tabs 数据源tabData: [],}
把服务端数据赋值给tabData
async loadHotTabs() {// uniapp 支持 async awaitconst { data: res } = await loadHotTabs();this.tabData = res.list;
这样打印出的数据就没有多余的data节点
在my-tabs里面进行数据接收
在hot.vue渲染tabs组件
/uniapp中进行父向子传递 ,遵循vue v-bind 指令 >
:tabData=”tabData” === bind:tabData =”{{ tabData}}”
<my-tabs tabData ="tabData"></my-tabs>
处理my-tabs的html结构
tabs需要横向滚动,使用scroll-view scroll-x
小程序里的for循环写法
wx:for ="{{arr}}" default 变量名 item, 下标 index
uniapp写法
v-for="(item,index) in arr" :key="index"
<!-- 处理my-tabs的html结构 --><view class="tab-container"><view class="tab-box"><scroll-viewid="_scroll"scroll-xclass="scroll-view"scroll-with-animation //对应动画:scroll-left="scrollLeft"><view class="scroll-content"><view class="tab-item-box"><block v-for="(item, index) in tabList" :key="index">
tabs样式
.tab-container {font-size: $uni-font-size-base;height: 45px;line-height: 45px;background-color: $uni-bg-color;.tab-box {width: 100%;height: 45px;display: flex;position: relative;.scroll-view {white-space: nowrap;width: 100%;height: 100%;box-sizing: border-box;.scroll-content {width: 100%;height: 100%;position: relative;.tab-item-box {height: 100%;.tab-item {height: 100%;display: inline-block;text-align: center;padding: 0 15px;position: relative;text-align: center;color: $uni-text-color;&-active {color: $uni-text-color-hot;}}}.underLine {height: 2px;width: 25px;background-color: #f01414;border-radius: 3px;transition: 0.5s;position: absolute;bottom: 0;}}}}
设置激活项
当我们defaultIndex 等于当前index 添加class tab-item-active
<block v-for="(item, index) in tabList" :key="index"><viewclass="tab-item":id="'_tab_' + index":class="{ 'tab-item-active': activeIndex === index }"
&-active {color: $uni-text-color-hot;}

会遇到其他问题,父组件传递的数据,不应该在子组件进行修改
在data里面声明激活项activeIndex 代替defaultIndex
通过watch监听器,监听defaultIndex的变化,相当于小程序里的observes
通过handler,让activeIndex跟随defaultIndex发生变化
data: () => {return { ////当前激活项的 indexactiveIndex: -1,watch: {//监听激活项目的变化defaultIndex:{//当defaultIndex发生变化时,回调的方法handler(val){this.activeIndex = val;},//该回调将会在侦听开始之后被立即调用immediate:true}}
添加点击事件,
@click 等于 bind:tap
@click="tabClick(index)"
methods:{tabClick(index) {this.activeIndex = index;// 发送通知,通知表示激活项发生变化了this.$emit('tabClick', index);},
总结
tab激活功能
后台传递一个0
my-tabs接收到了一个defaultIndex之后,通过activeIndex修改激活项
实现滑块
滑块和tab平级
横向移动
绑定一个transform:'translateX()'
<viewclass="underLine":style="{transform: 'translateX(' + slider.left + 'px)',width: defaultConfig.underLineWidth + 'px',height: defaultConfig.underLineHeight + 'px',backgroundColor: defaultConfig.underLineColor}"/>
在data之中声明滑块的对象
data(){return {slider: {// 距离左侧的距离left: 0},}}
.underLine {height: 2px;width: 25px;background-color: #f01414;border-radius: 3px;transition: 0.5s;position: absolute;bottom: 0;}
实现滑块的滚动
想要实现滑块的滚动
1,确定滚动的时机 ==>点击tab
2.计算滑块滚动的距离
1.计算滑块的滚动位置,根据当前的activeIndex
当前点击项item的位置,在点击项item的宽度,计算出当前item滚动的宽度
需要拿到tabItem的宽度,tabItem的left,s’lider的widthleft = tabItem.left + (tabItem.width -slider.width)/2
methods:{tabToIndex() {if (this.tabList.length === 0) return;// 获取当前的 activeIndexconst activeIndex = this.activeIndex;// 配置 滚动条 的数据this.slider = {// TODO:left 如何定义呢?// 1. 维护一个单独的数据对象 `tabList`// 2. 在 `tabList` 的 `item` 中为一个 `_slider` 属性// 3. 该属性保存了 【当前 `item` 下 的滑块位置】// 3.1. 计算公式:`滑块左侧位置 = item.left + (item.width - slider.width) / 2`left: this.tabList[activeIndex]._slider.left}
定义默认配置,
data(){return {defaultConfig: {// 默认的字体颜色textColor: '#333333',// 高亮字体颜色activeTextColor: '#f94d2a',// 下划线宽度 pxunderLineWidth: 24,// 下划线高度 pxunderLineHeight: 2,// 下划线颜色underLineColor: '#f94d2a'}}}<viewclass="underLine":style="{transform: 'translateX(' + slider.left + 'px)',width: defaultConfig.underLineWidth + 'px',height: defaultConfig.underLineHeight + 'px',backgroundColor: defaultConfig.underLineColor}
拿到tabitem的宽,要定义新的数据项tablist

tablist如何定值
在watch里面监听tabData变化,
维护tablist的每一个item,时机为DOM渲染完成之后
this.$nextTick() 兼容有问题
setTimeout
watch: {// 侦听数据的变化tabData: {handler(val) {this.tabList = val;//tablist数据和tabData同步setTimeout(() => {//计算宽度this.updateTabWidth();}, 0);},// 该回调将会在侦听开始之后被立即调用immediate: true},
methods: {/*** 更新 tab item 的宽度*/updateTabWidth() {// tabItem的宽度,tabItem的left ,为tabList中的每一个item维护一个slidelet data = this.tabList;if (data.length == 0) return false;//uniapp中拿到渲染后的dom//获取dom的固定写法创建去selectorQuery的实例,从当前的vue组件里里面in(this)const query = uni.createSelectorQuery().in(this);// 循环数据源data.forEach((item, index) => {// 获取 dom 的固定写法query//传入内容,在view里面加上:id="'_tab_'+ index".select('#_tab_' + index).boundingClientRect((res) => {// 为数据对象中每一个 item 都维护一个 _slider(滑动条) 对象item._slider = {// 当前的 tab 距离左侧的距离//left = tabItem.left + (tabItem.width -slider.width)/2left: res.left + (res.width - this.defaultConfig.underLineWidth) / 2};// 运算完成之后,执行一次 【滑块】位置运算if (data.length - 1 === index) {this.tabToIndex();}}).exec();

dom固定写法,res就是拿到的dom


计算tabtoindexleft: this.tabList[activeIndex]._slider.left
在用户点击时候调用tabToIndex()
scrollView的点击位移
当选中项发生变化时,希望scrollView进行对应的位移
提供了scroll-left属性
<scroll-view:scroll-left="scrollLeft">
在data里scrollView的横向滚动条位置
scrollLeft: 0
// 为 scrollView 设置滚动位置this.scrollLeft = this.activeIndex * this.defaultConfig.underLineWidth;
增加可配置项
defaultConfig: {// 默认的字体颜色textColor: '#333333',// 高亮字体颜色activeTextColor: '#f94d2a',// 下划线宽度 pxunderLineWidth: 24,// 下划线高度 pxunderLineHeight: 2,// 下划线颜色underLineColor: '#f94d2a'}
绑定行内样式
:style="{color:activeIndex === index ? defaultConfig.activeTextColor : defaultConfig.textColor}"
props传过来的config和defaultconfig没发生关联
watch去监听config的变化,用传过来的config覆盖defaultconfig里的相关属性,得到最新
// 监听 configconfig: {handler(val) {this.defaultConfig = { ...this.defaultConfig, ...val };},// 该回调将会在侦听开始之后被立即调用immediate: true}
