(四)文章的列表展示

文章的列表展示

完成文章首页的列表展示,当用户进入文章首页的时候展示文章数据,包括文章的图片,除此之外还包括上拉刷新和下拉加载的功能
分为两个部分

layout页面

完成主页的渲染,在这里使用vant当中的TabBar组件实现底部导航,并且配置路由,当点击首页的时候切换到主页,当点击个人中心的时候切换到个人中心

1、底部导航

在这里插入图片描述

  1. 查找Vant组件库文档, 全局注册, 在main.js

    import { Tabbar, TabbarItem } from 'vant'
    
    Vue.use(Tabbar)
    Vue.use(TabbarItem)
    
  2. 给Layout页面底部设置TabBar导航

    <template>
      <div>
        <!-- 二级路由页面 -->  
        <router-view></router-view>
        
        <!-- 底部导航 -->  
        <van-tabbar v-model="active" route>
          <van-tabbar-item icon="home-o" to="/layout/home">首页</van-tabbar-item>
          <van-tabbar-item icon="setting-o" to="/layout/user">我的</van-tabbar-item>
        </van-tabbar>
      </div>
    </template>
    
    <script>
    // 需求: 底部导航
    // 1. 复制tabbar组件过来
    // 2. 设置route集成路由功能
    // 3. 设置to属性, 点击跳转路由页面
    // 4. tabbar上设置route和to属性, 点击底部导航就可以切换路由页面
    export default {
      name: 'Layout',
      data () {
        return {
          active: 0
        }
      }
    }
    </script>
    
  3. 美化样式, 给router-view外面, 套一个div.home_container, 并且防止二级页面内容, 被底部导航挡住

    <template>
        <div class="home_container">
            <!-- 二级路由页面 -->  
            <router-view></router-view>
        </div>
    </template>
    
    <style scoped lang="less">
    /* 内容底部内边距(把内容往上挤压, 防止内部被底部导航挡住) */
    .home_container {
      padding-bottom: 50px;
    }
    /* 底部导航上边框 */
    .van-tabbar {
      border-top: 1px solid #f8f8f8;
    }
    </style>
    
  4. 运行, 测试点击底部导航, 页面是否跳转

2、Layout_登录跳转

实现

在Login/index.js内, 登陆成功逻辑处末尾

// 跳转到Layout页面
this.$router.replace({
    path: '/layout'
})

注意的点

  1. r o u t e r 和 router和 routerroute区别是?

    • $router下用于跳转路由

    • $route是路由信息对象

  2. 路由的push和replace方法区别?

    • push跳转后, 可以返回

    • replace跳转后, 无法返回

3.首页_头部标签

首页头部标签展示
在这里插入图片描述

头部标签

  1. 使用NavBar组件, 和图标Icon组件

  2. 查找文档, main.js - 全局注册

    之前已经注册过NavBar组件(做登录时), 所以这里只注册Icon组件

    import { Icon } from 'vant'
    
    Vue.use(Icon)
    
  3. 分析, 在Home页面头部使用

这里只在Home使用,因为只有Home主页拥有页面头部标签

<template>
  <div>
    <!-- 头部标签 -->
    <van-nav-bar fixed>
      <template #left>
        <!-- <img class="logo" src="../../assets/toutiao_logo.png" alt="" /> -->
        <img class="logo" :src="imgObj"/>
      </template>
      <template #right>
        <van-icon name="search" size="0.48rem" color="#fff"/>
      </template>
    </van-nav-bar>
  </div>
</template>

<script>
import logoPng from '../../assets/toutiao_logo.png'
export default {
  name: 'Home',
  data () {
    return {
      imgObj: logoPng
    }
  }
}
</script>

  1. 美化logo的样式

    <style scoped lang="less">
    .logo {
        width: 100px;
        height: 30px;
    }
    </style>
    
    

小结

  1. 为何插槽看不见slot标签?

    因为你用的vant组件, 组件内部写好了slot, 你只需要使用即可

  2. JS里引入图片为何import

    因为脚手架底层运作是webpack, 图片会认为是模块, 需要模块化导入

4、首页_频道铺设

  • 频道导航铺设

布局

在这里插入图片描述

  1. 文档查找类似组件 - Tab标签页 - main.js注册

    import { Tab, Tabs } from 'vant'
    
    Vue.use(Tab)
    Vue.use(Tabs)
    
  2. 注册组件后, 在首页对应位置使用, 切换动画设置

    <!-- 频道部分 -->
    <van-tabs v-model="active" animated>
      <van-tab title="标签 1">内容 1</van-tab>
      <van-tab title="标签 2">内容 2</van-tab>
      <van-tab title="标签 3">内容 3</van-tab>
      <van-tab title="标签 4">内容 4</van-tab>
    </van-tabs>
    

    发现: 内容1, 内容2, 内容3, 内容4, 对应的是tab标签下面对应的内容区域(插槽技术)

  3. 被定位的头部导航挡住, 给tabs设置固定定位/粘性定位, 距离上边46px(手动转rem)

    <van-tabs
          v-model="active"
          animated
          sticky
          offset-top="1.226667rem"
    >
    
  4. tab栏内容部分, 也要给tab导航定位留出距离

    /* tab内容距离tab导航的距离 */
    /deep/ .van-tabs__content{
        padding-top: 44px;
    }
    

小结

  1. tab组件和内容是什么关系?

    分别一一对应的关系, 分别独立, 互不影响

  2. 粘性定位是什么?

    正常随着页面滚动, 到达指定位置, 变成固定定位

首页频道数据

通过这个接口,得到首屏铺设的信息
请求地址: /v1_0/channels

请求方式: GET

请求头:

参数名称参数值是否必须示例备注
Content-Typeapplication/json如果用的axios, 默认data格式就是json的, 无需手动添加请求头了
// 频道 - 用户已选列表
export const userChannelAPI = () => request({
  url: '/v1_0/user/channels',
  headers: {
    Authorization: 'Bearer ' + store.state.token
  }
})
  1. Home/index.vue引入接口方法, created函数中发起请求

    import { userChannelAPI } from '@/api'
    
    export default {
        data () {
        	return {
                // ...省略其他
            	channelList: [] // 频道数据
        	}
    	},
        async created () {
    		const res = await userChannelAPI()
    		this.channelList = res.data.data.channels
    	}
    }
    
  2. 循环铺设频道

    <!-- 频道部分 -->
    <van-tabs v-model="active" animated sticky offset-top="1.226667rem">
        <van-tab v-for="item in channelList" :key="item.id" :title="item.name">
            {{ item.name }}
        </van-tab>
    </van-tabs>
    
  3. 覆盖导航标记条的颜色, styles/cover.less

    // Tabs标签页
    @tabs-bottom-bar-color:#007bff;
    

在这里通过v-for遍历渲染拥有的数据,并且通过vant组件的title绑定
在这里插入图片描述

  1. 登录和未登录返回的频道有什么区别?

登录就会传递token给后台, 返回对应用户的频道

未登录后台返回默认的频道;

5文章_列表布局

这里使用了van-cell和v-slot组件来实现定制化

<template>
  <!-- 一条文章单元格 -->
  <van-cell>
    <!-- 标题区域的插槽 -->
    <template #title>
      <div class="title-box">
        <!-- 标题 -->
        <span>文章标题</span>
      </div>
    </template>
    <!-- label 区域的插槽 -->
    <template #label>
      <div class="label-box">
        <div>
          <span>作者</span>
          <span>0评论</span>
          <span>8个月前</span>
        </div>
        <!-- 反馈按钮 -->
        <van-icon name="cross" />
      </div>
    </template>
  </van-cell>
</template>

<script>
export default {}
</script>

<style scoped lang="less">
/* 标题样式 */
.title-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

/* label描述样式 */
.label-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* 文章信息span */
.label-box span{
    margin: 0 3px;
    &:first-child{
        margin-left: 0;
    }
}
</style>

创建ArticleList组件

  • 创建Home/ArticleList.vue

  • 引入ArticleItem.vue组件, 注册, 多使用几次观察效果

    <template>
      <div>
        <!-- 文章列表 -->
        <article-item></article-item>
        <article-item></article-item>
        <article-item></article-item>
      </div>
    </template>
    
    <script>
    import ArticleItem from './components/ArticleItem.vue'
    export default {
      components: {
        ArticleItem
      }
    }
    </script>
    
    

Home页引入

  • ArticleList封装后, 要引入到路由页面Home/index.vue才可以看到

    <!-- 频道部分 -->
    <van-tabs v-model="active" 
              animated 
              sticky
              offset-top="1.226667rem">
        <van-tab v-for="item in channelList" 
                 :key="item.id" 
                 :title="item.name">
            <article-list></article-list>
        </van-tab>
    </van-tabs>
    
    <script>
    // 只有核心代码
    import ArticleList from '@/views/Home/ArticleList'
        
    export default {
        components: {
             
        }
    }
    </script>
    
    
  • 运行后, 观察网页效果是否出现内容一样的文章列表

  • van-tab有类似于懒加载的效果:“只有激活了某个tab标签,才会去加载ArticleList”

6、## 文章_列表数据

  • 请求文章列表,将数据传递给Attriitem来遍历,通过v-for的方法循环生成文章列表,并将数组中的数据传过去

定义接口

  • 查看接口文档, 定义接口方法在api下

    预约分页思想:

    第一次传递timestamp: 传递系统时间

    第二次传递timestamp: 用第一次响应结果的pre_timestamp的值

    // 文章 - 获取列表
    export const articlesListAPI = ({ channelId }) => {
      return request({
        url: '/v1_0/articles',
        params: {
          channel_id: channelId,
          timestamp: (new Date()).getTime()
        }
      })
    }
    

发起请求

  1. 在Home/ArticleList.vue引入, 并发起请求

    <template>
    	<div>
            <!-- 文章列表 -->
            <article-item></article-item>
            <article-item></article-item>
            <article-item></article-item>
        </div>
    </template>
    
    <script>
        import ArticleItem from './components/ArticleItem.vue'
        import { articlesListAPI } from '@/api'
        export default {
            components: {
                ArticleItem
            },
            async created () {
                const res = await articlesListAPI({
                    channelId: 0 // 默认推荐频道(id为0)
                })
                console.log(res)
            }
        }
    </script>
    

ArticleList循环文章

  1. 在ArticleList.vue中, data定义articleList变量接受

  2. v-for循环ArticleItem, 传入文章对象

    <template>
    	<div>
            <!-- 文章列表 -->
            <article-item
            	v-for="item in articleList"
            	:key="item.art_id"
            	:obj="item"
            ></article-item>
        </div>
    </template>
    
    <script>
        // 核心代码
        import { articlesListAPI } from '@/api'
        export default {
        data () {
            return {
                articleList: []
            }
        },
        async created () {
            const res = await articlesListAPI({
                channelId: 0 // 默认推荐频道(id为0)
            })
            this.articleList = res.data.data.results
        }
    }
    </script>
    

7 ArticleItem文章展示

通过接收props的数据进行动态展示

  1. 定义props变量obj接收, 从ArticleList传递过来的文章信息对象
  2. 替换标签内容
<template>
<!-- 一条文章单元格 -->
<van-cell>
    <!-- 标题区域的插槽 -->
    <template #title>
        <div class="title-box">
            <!-- 标题 -->
            <span>{{ obj.title }}</span>
    </div>
</template>
<!-- label 区域的插槽 -->
<template #label>
<div class="label-box">
    <div>
        <span>{{ obj.aut_name }}</span>
        <span>{{ obj.comm_count }} 评论</span>
        <span>{{ obj.pubdate }}</span>
    </div>
    <!-- 反馈按钮 -->
    <van-icon name="cross" />
    </div>
</template>
</van-cell>
</template>

<script>
    export default {
        props: {
            obj: { // 文章信息对象
                type: Object,
                default () {
                    return {}
                }
            }
        }
    }
</script>

8.文章_图片判断

目标

  • 文章图片铺设

  • 文章图片判断

分析

  1. 什么时候有图片, 什么时候是一张和三张呢?
  2. 观察数据字段, 发现有个cover.type的值控制数量
  3. cover.images数组, 里面都是图片的url

修改ArticleItem

  1. 修改标签结构, 添加单图和三图情况的 (无vue代码直接复制)

    <template #title>
    	<div class="title-box">
        	<!-- 标题 -->
        	<span>{{ obj.title }}</span>
        	<!-- 单图 -->
        	<img class="thumb" src="https://img01.yzcdn.cn/vant/cat.jpeg" />
        </div>
    	<!-- 三张图片 -->
    	<div class="thumb-box">
        	<img class="thumb" src="https://img01.yzcdn.cn/vant/cat.jpeg" />
            <img class="thumb" src="https://img01.yzcdn.cn/vant/cat.jpeg" />
            <img class="thumb" src="https://img01.yzcdn.cn/vant/cat.jpeg" />
        </div>
    </template>
    
  2. 美化样式

    /* 图片的样式, 矩形黄金比例:0.618 */
    .thumb {
      width: 113px;
      height: 70px;
      background-color: #f8f8f8;
      object-fit: cover;
    }
    
    /* 三图, 图片容器 */
    .thumb-box {
      display: flex;
      justify-content: space-between;
    }
    
  3. 根据文章信息对象, 判断是否应该出现

  4. 替换成真实的图片 / 循环真实图片

    <template #title>
          <div class="title-box">
            <!-- 标题 -->
            <span>{{ obj.title }}</span>
            <!-- 单图 -->
            <img class="thumb"
                 :src="obj.cover.images[0]"
                 v-if="obj.cover.type === 1"/>
            </div>
            <!-- 三张图片 -->
            <div class="thumb-box" v-if="obj.cover.type > 1">
              <img class="thumb"
                 v-for="(imgUrl, index) in obj.cover.images"
                 :key="index"
                 :src="imgUrl" />
            </div>
        </template>
    

小结

  1. 如何区分标签不同的部分

    找到数据区分, 如果没有自己想办法创造 / 与后台沟通分析

9.文章_时间处理

目标

  • 把时间转换成多久之前

查找工具

  • dayjs第三方库: https://dayjs.fenxianglu.cn/

  • 下载dayjs到工程中

    yarn add dayjs
    

工具函数

以后可能也要处理时间, 所以封装个工具函数

学生问: 为何不使用过滤器

答: 过滤器本身也是个函数, 而且vue3废弃了过滤器, 所以不使用

utils/date.js

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' // 到指定时间需要的插件
import 'dayjs/locale/zh' // 集成中文

/**
 * .....多久之前
 * @param {*} 之前的时间
 * @returns 系统时间到之前指定时间的距离值
 */
export const timeAgo = (targetTime) => {
  // 格式化时间
  dayjs.extend(relativeTime)
  dayjs.locale('zh')
  var a = dayjs()
  var b = dayjs(targetTime)
  return a.to(b) // 返回多久之前...
}

Home里使用

学生问: 为何不到ArticleItem里直接使用

答: 因为要讲数据预处理

在Home/ArticleList.vue中

import { timeAgo } from '@/utils/date'

export default {
  // 核心代码
  methods: {
    timeAgo: timeAgo
  },
  async created () {
    const res = await articlesListAPI({
      channelId: 0 // 默认推荐频道(id为0)
    })

    // 回来的数据进行预处理, 把时间转成多久多久之前
    res.data.data.results.forEach((obj) => {
      obj.pubdate = this.timeAgo(obj.pubdate)
    })

    this.articleList = res.data.data.results
  }
}

小结

  1. 什么是数据预处理?

    在使用数据之前, 对数据里的值进行修改

10.文章_切换数据

目标

  • 点击频道显示不同数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMHBMF8L-1654761477451)(images/频道切换数据.gif)]

实现

  1. 点击频道, 拿到频道ID

    频道是用tab 标签页组件, 看文档如何拿到,

    • 在van-tabs上修改v-model变量名(把active换成channelId)
    • 在van-tab绑定name属性和值(值为频道ID)
    • 点van-tab后,name值会自动绑定给v-model(vant组件内部实现的)
    <!-- 频道部分 -->
    <van-tabs v-model="channelId" animated sticky offset-top="1.226667rem">
    	<van-tab v-for="item in channelList" :key="item.id" :title="item.name" :name="item.id">
    		<article-list :cid="channelId"></article-list>
    	</van-tab>
    </van-tabs>
    
    <script>
    export default {
      data () {
        return {
          // ...省略其他(把active换成channelId)
          channelId: 0, // 频道ID(默认0是推荐)
        }
      }
    }
    </script>
    
    
  2. ArticleList内, props定义变量cid, 替换请求的频道

    默认会切换ArticleList, 组件创建后, 会重新进行网络请求

    export default {
      // ...核心代码
      props: {
        cid: Number
      },
      async created () {
        const res = await articlesListAPI({
          channelId: this.cid
        })
    
        // 回来的数据进行预处理, 把时间转成多久多久之前
        res.data.data.results.forEach((obj) => {
          obj.pubdate = this.timeAgo(obj.pubdate)
        })
    
        this.articleList = res.data.data.results
      }
    }
    
    

小结

  1. 点击频道切换数据如何做的?

    • 点击频道v-model变量绑定对应name值, 即频道ID
  • 然后传给ArticleList, 里面会重新请求频道

11.文章列表_加载更多-准备

目标

  • 手指往上拉, 底部加载更多

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wKt8qEmf-1654761477452)(images/首页文章_更多.gif)]

思路

  1. 要判断是否滚动到底部
  2. 给后台传递页码 / 偏移量, 拿到新数据
  3. 和现有数据合并到一个数组里
  4. 寻找vant组件, 叫list组件

步骤

  1. main.js - 注册list组件

    import { List } from 'vant'
    
    Vue.use(List)
    
  2. 套上文章列表组件, 在Home/ArticleList中

    <van-list
          v-model="loading"
          :finished="finished"
          finished-text="没有更多了"
          @load="onLoad"
        >
          <!-- 文章列表 -->
          <article-item
            v-for="item in articleList"
            :key="item.art_id"
            :obj="item"
          ></article-item>
        </van-list>
    
  3. 定义变量和方法

    export default {
      data () {
        return {
          articleList: [], // 文章列表
          loading: false, // 底部加载状态
          finished: false // 数据是否全部加载完成
        }
      },
      methods: {
        // 底部加载
        onLoad () {
    
        }
      }
    }
    
    
  4. 触底的距离有点远, 查文档, 设置list组件属性offset=“50”

    <van-list
          v-model="loading"
          :finished="finished"
          finished-text="没有更多了"
          @load="onLoad"
          offset="50"
        >
    
  5. 网页刚上来不监测是否触底, 不执行load事件

    问题: created和onLoad会分别上来走一次请求, 拿回来2次第一页数据

    <van-list
          v-model="loading"
          :finished="finished"
          finished-text="没有更多了"
          @load="onLoad"
          offset="50"
          :immediate-check="false"
        >
    

12.文章列表_加载更多-功能

目标

  1. 分析如何拿到下一页数据, 发现接口文档有个timestamp, 说明下一页要用上一次请求成功的pre_timestamp的值

  2. Home/ArticleList.vue, data里定义变量nextTime

  3. 在文章列表获取成功时, 接收pre_timestamp的值

  4. 调用接口传入nextTIme

  5. 接收数据合并到现有数组中

  6. 判断是否全部加载完成

    代码有点多了, 可以单独定义个getArticleList方法调用

步骤

  1. 修改api接口, 添加额外的时间戳

    // 文章 - 获取列表
    export const articlesListAPI = ({ channelId, timestamp }) => {
      return request({
        url: '/v1_0/articles',
        params: {
          channel_id: channelId,
          timestamp: timestamp || (new Date()).getTime()
        }
      })
    }
    
  2. 创建getArticleList方法, 在created里调用

    data () {
        return {
          // ...省略其他
          nextTime: null // 用于加载更多(分页)
        }
    }
    
    methods: {
        async getArticleList () {
          // 负责拿文章列表数据
          const res = await articlesListAPI({
            channelId: this.cid,
            timestamp: this.nextTime
          })
          console.log(res)
          // 回来的数据进行预处理, 把时间转成多久多久之前
          res.data.data.results.forEach((obj) => {
            obj.pubdate = this.timeAgo(obj.pubdate)
          })
    
          // 如果无数据
          if (res.data.data.results.length === 0) {
            this.finished = true // 告诉list组件直接底部显示没有更多数据
          } else {
            this.nextTime = res.data.data.pre_timestamp // 保存下一页数据的时间(做分页用)
            this.articleList = [...this.articleList, ...res.data.data.results]
            // 等待网络请求结果后
            this.loading = false // 底部加载更多状态
          }
        }
    }
    
    async created () {
        this.getArticleList()
    }
    

小结

  1. list组件2个变量控制什么?

    • loading控制底部是否显示加载中状态

    • finished控制底部是否显示全部加载完成

  2. list组件使用注意哪几点?

    • list组件触底后, 自动执行load事件和对应onLoad方法, 并把loading值改为true
    • 异步数据加载完毕, 合并数据, 把loading改为false, 以后再让list自动执行load事件
    • 异步数据加载完毕, 判断没有数据了, 把finished改为true, 以后再也不出load事件
  3. 为何要把新数组和就数组拆开合并起来?

    • 为了让页面数据更多, 撑高list组件, 让用户继续滚动

13.文章列表_加载更多-重复bug

目标

  1. 发现网络请求上来走了2次一样的
  2. 分析原因: list组件和created都走了 (list上来发现组件底部在可视区域, 就触发load事件)
  3. 解决: 在load事件方法中判断, 只有页面上有数据, 再触底才执行加载逻辑

解决

  1. 在ArticleList.vue中判断一下

    // 底部加载
    onLoad () {
        // 有第一页数据以后, onLoad再发请求
        if (this.articleList.length > 0) {
            this.getArticleList()
        }
    }
    

小结

  1. 为什么要给onLoad里加入判断?
    • 网页打开list组件自动检查会触发一次onLoad, 而我要在有数据后
    • 再让onLoad里请求数据

15.文章列表_下拉刷新

目标

  • 手指往下拉, 顶部刷新

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9AnHzxc-1654761477453)(images/首页文章_刷新.gif)]

思路

  1. 实现页面上下拉效果 - vant组件里的list组件
  2. 刷新要把旧数组清空, 时间回到系统时间, 重新发起一次请求, 第一页数据

步骤

  1. 找到Vant组件库文档, main.js - 全局注册PullRefresh组件

    import { PullRefresh } from 'vant'
    
    Vue.use(PullRefresh)
    
  2. 套在要刷新的文章列表外, van-list外层

    <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
          <van-list
            v-model="loading"
            :finished="finished"
            finished-text="没有更多了"
            @load="onLoad"
            offset="50"
          >
            <!-- 文章列表 -->
            <article-item
              v-for="item in articleList"
              :key="item.art_id"
              :obj="item"
            ></article-item>
          </van-list>
        </van-pull-refresh>
    
  3. 定义变量和方法

    export default {
        data () {
            return {
                // ...省略了其他变量(不要删除啊)
                isLoading: false // 顶部刷新状态
            }
        },
        methods: {
            // 顶部刷新事件方法
            onRefresh () {
                this.nextTime = null
          		this.articleList = []
          		this.getArticleList()
            }
        }
    }
    
  4. 一定注意, 数据异步刷新完毕, 要关闭isLoading状态

    this.isLoading = false // 顶部加载状态关闭
    

小结

  1. 刷新的原理是什么?

    • 获取手指移动距离, 然后transform移动标签
  2. 刷新流程是什么?

    • 页码回到第一页, 现有数据数组清空, 重新调用获取文章接口

这一节主要完成了以下功能
完成了 Layout、Home、Attralist、AttrItem等组件的铺设并通过v-for遍历
通过props传递给了AttrItem进行数据渲染
通过日期类day.js得到多久之前

注意的点

保证头部栏固定不能随意滑动,要使用vant的flex

  <div>
    <van-nav-bar title="新闻首页" class="page-nav-bar" fixed>
      <template v-slot:right>
        <van-icon name="search" size="0.5rem" />
      </template>
    </van-nav-bar>
    <div>
     <van-tabs 
    v-model="channelId"
    animated
    sticky
    offset-top="1.226667rem"
    class="main"
    >
      <van-tab :title="channels.name" v-for="channels in channelList" :key="channels.id">
    <ArticleList :cid="channelId"/>
   </van-tab>
    </van-tabs>
    </div>
  </div>
  1. 被定位的头部导航挡住, 给tabs设置固定定位/粘性定位, 距离上边46px(手动转rem)

    <van-tabs
          v-model="active"
          animated
          sticky
          offset-top="1.226667rem"
    >
    
  2. tab栏内容部分, 也要给tab导航定位留出距离

    /* tab内容距离tab导航的距离 */
    /deep/ .van-tabs__content{
        padding-top: 44px;
    }
    

或者


.main{
 padding-top: 44px; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值