如何实现一个长尾关键词优化的Vue选项卡组件示例详解?

2026-04-02 21:091阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1901个文字,预计阅读时间需要8分钟。

如何实现一个长尾关键词优化的Vue选项卡组件示例详解?

目录+概述+效果图+实现过程+组件分析+所需知识+项目组件文件夹+Tabs.vue+TabPane.vue+render.js+index.js+使用+总结+概述+前端项目中,多页面涉及包括选项卡切换,涵盖路由切换等,指令v-

目录
  • 概述
  • 效果图
  • 实现过程
    • 组件分析
    • 所需的前置知识
    • 项目组件文件夹
    • Tabs.vue
    • TabPane.vue
    • render.js
    • index.js
    • 使用
  • 总结

    概述

    前端项目中,多数页面涉及到选项卡切换,包括路由切换,指令v-if等,本质上其实和选项卡切换思想差不多,如果是个简单的选项卡,还是很简单的,我们也不需要什么组件库的组件,自己也能几行代码写出来,但是涉及到动画,尺寸计算,拖拽的功能的时候,多数情况下,自己写还是要花点时间的,组件库就提供了现成的,拿来改改样式就行,为了对这个组件更加深入的理解,这里自己实现一个带拖拽,过渡的tabs组件。

    效果图

    实现过程

    组件分析

    • 组件包含两部分:Tabs组件和TabPane组件,参考绝大多数组件库的习惯
    • 组件主要分为需要点击的tab栏和下面对应的内容块
    • 我们需要对内容区和选项卡点击区分别加上过渡动画,提升用户体验
    • 最后需要加上拖拽调整选项卡顺序的功能

    所需的前置知识

    • 熟悉vue内置transition组件
    • 深入掌握vue父子组件通信,除开emit和props,还需要掌握inject,emit和props,还需要掌握inject,emit和props,还需要掌握inject,parent,vnode,渲染函数等等,这些业务开发中用的不多,但是组件库里面比较常见。
    • 了解dom中位置计算和尺寸的基本计算
    • 熟悉html5新增拖拽相关事件

    项目组件文件夹

    如何实现一个长尾关键词优化的Vue选项卡组件示例详解?

    Tabs.vue

    <template> <div class="gnip-tab"> <div class="gnip-tab-nav"> <div v-for="(item, index) in tabNavList" @click.stop="handleTabNavClick(item, index)" :class="['tab-nav-item', item.name == activeName ? 'active' : '']" ref="tabNavItemRefs" @drop="handleDrop(item, $event, index)" @dragstart="handelDragstart(item, $event, index)" @dragover="handleDragOver(item, $event, index)" draggable="true" > <span v-if="item.text">{{ item.text }}</span> <render v-if="item.renderFun" :renderFn="item.renderFun"></render> </div> </div> <!-- 滚动滑块 --> <div class="tab-nav-track" :style="{ background: showTrackBg ? '#e5e7eb' : '', }" > <span class="track-line" :style="{ width: trackLineWidht + 'px', left: left + 'px' }" ></span> </div> <div class="tab-content-wrap"> <slot></slot> </div> </div> </template> <script> // render组件,label为render函数的时候进行渲染 import Render from "./render"; export default { props: { // v-model的那项 value: { type: String, }, // 是否显示滑块背景 showTrackBg: { type: Boolean, default: false, }, }, components: { Render, }, data() { return { // tab数组 tabNavList: [], // 当前活跃项 activeName: "", // 滑块的宽度 trackLineWidht: 0, // 当前活跃索引 currentIndex: 0, // 滑块偏移量 left: 0, // 拖拽开始的哪项 dragOriginItemIndex: null, // 拖拽活跃项的索引 dragStartIndex: null, }; }, mounted() { this.init(); }, methods: { // 初始化 init() { // 默认当前活跃项为外部v-model的值 this.activeName = this.value; // 页面渲染任务之后计算滑块偏移量和宽度 this.$nextTick(() => { this.currentIndex = this.$children.findIndex( (component) => component.name == this.value ); this.computedTrackWidth(); }); }, // 设置tab点击栏 setTabBar(tabsPaneInstance) { // tab的描述信息可以是字符串也可以是render函数 const label = tabsPaneInstance.label, type = typeof label; // 添加到数组项中,根据添加条件渲染 this.tabNavList.push({ text: type == "function" ? "" : label, renderFun: type == "function" ? label : "", name: tabsPaneInstance.name, }); }, handleTabNavClick(item, index) { if (item.name == this.activeName) return; // 更新当前活跃项 this.activeName = item.name; // 活跃项的索引 this.currentIndex = index; // 计算滑块的偏移量和宽度 this.computedTrackWidth(); }, // 计算滑块的偏移量和宽度 computedTrackWidth() { // 插槽子组件的索引集合 const tabNavItemRefsList = this.$refs.tabNavItemRefs; // 导航tab项的宽度 const scrollWidth = tabNavItemRefsList[this.currentIndex].scrollWidth; // 滑块的宽度为scrollWidth this.trackLineWidht = scrollWidth; // 定位的偏移量为offsetLeft this.left = tabNavItemRefsList[this.currentIndex].offsetLeft; }, /* 关于拖拽请参考MDN文档: developer.mozilla.org/zh-CN/docs/Web/API/DragEvent,实现拖拽需要清楚关于拖拽相关的几个事件 */ // 开始拖拽 handelDragstart(item, event, index) { // 说明是拖拽的当前活跃的哪一项,记录这一项的索引位置 if (item.name == this.activeName) { this.dragStartIndex = index; } this.dragOriginItemIndex = index; }, // 推拽进入目标区域 handleDragOver(item, event) { // 阻止默认事件 event.preventDefault(); }, //拖拽进入有效item handleDrop(item, event, index) { event.preventDefault(); // 说明拖动的位置是变了的 if (this.dragOriginItemIndex != index) { // 交换数据,重新渲染生成tab栏 this.swap(this.dragOriginItemIndex, index); // 重新计算滑块的偏移量 if (this.dragStartIndex !== null) { this.currentIndex = index; // 记住,数据更新为异步操作,因此我们这里需要用到nextTick,将计算任务放到渲染任务完成之后执行,避免计算不准确 this.$nextTick(() => { this.computedTrackWidth(); this.dragStartIndex = null; }); } else { // 不是点击拖拽当前活跃项,也要重新计算滑块跨度和位置,因为每个tab项的宽度不一致,因此,每次拖拽都需要重新计算 this.$nextTick(() => { this.computedTrackWidth(); }); } // 这里还可以根据需要,发布一个拖拽完成事件 } }, // 交换tab数据项 swap(start, end) { let startItem = this.tabNavList[start]; let endItem = this.tabNavList[end]; // 由于直接通过索引修改数组,无法触发响应式,因此需要$set this.$set(this.tabNavList, start, endItem); this.$set(this.tabNavList, end, startItem); }, }, }; </script> <style lang="less"> .gnip-tab { .gnip-tab-nav { display: flex; position: relative; .tab-nav-item { padding: 0 20px; cursor: pointer; line-height: 2; } } .tab-nav-item.active { color: #2d8cf0; } .tab-nav-track { width: 100%; position: relative; height: 2px; .track-line { height: 2px; background-color: #2d8cf0; position: absolute; transition: left 0.35s; } } } </style>

    TabPane.vue

    <template> <div class="gnip-tabs-pane"> <transition :name="paneTransitionName"> <div class="tab-pane-content" v-show="$parent.activeName == name"> <slot name="default"></slot> </div> </transition> </div> </template> <script> export default { props: { // tab项的文本或者render函数 label: { type: [String, Function], }, // 每项标识 name: { type: String, }, // 是否禁用当前项 disabled: { type: Boolean, default: false, }, }, data() { return { paneTransitionName: "enter-right", }; }, created() { // 统一tab的数据给父组件进行处理和渲染 this.$parent.setTabBar(this); }, }; </script> <style lang="less"> .gnip-tabs-pane { overflow-x: hidden; .enter-right-enter-active { transition: transform 0.35s; } .enter-right-enter { transform: translateX(100%); } .enter-right-to { transform: translateX(0); } } </style>

    render.js

    主要用于将函数通过转化为render函数形式的组件(前提未提供模板)

    export default { name: "RenderCell", props: { renderFn: Function, }, render(h) { return this.renderFn(h); }, };

    index.js

    按需导出组件

    import TabPane from "./TabPane.vue"; export { Tabs, TabPane };

    使用

    App.vue

    <template> <div class="app"> <div class="aline"> <Tabs v-model="tabName" show-track-bg> <TabPane label="首页" name="name1">首页</TabPane> <TabPane label="图书详情页" name="name2" disabled>图书详情页</TabPane> <TabPane label="个人主页" name="name3">个人主页</TabPane> <TabPane :label="labelRender" name="name4">购物车</TabPane> </Tabs> </div> </div> </div> </template> <script> import { Tabs, TabPane } from "@/components/Tabs"; export default { components: { Tabs, TabPane }, data() { return { tabName: "name1", labelRender(h) { return h("div", "购物车"); }, }; }, }; </script> <style lang="less"> * { margin: 0; padding: 0; } .app { padding: 20px; button { padding: 10px; background-color: #008c8c; color: #fff; margin: 20px 0; } .container { .operate { text-align: center; } .aline { width: 50%; } h2 { font-weight: bold; font-size: 20px; } .aline { &:nth-child(1) { margin-right: 20px; } } display: flex; justify-content: space-between; } } .aline { display: flex; justify-content: center; } .item { margin: 40px; img { width: 250px; height: 200px; } ul { margin: 0 auto; li { border: 1px solid red; height: 200px; width: 250px; } } } </style>

    总结

    通过上述组件的实现,对于HTML5拖拽事件的应用更加熟悉,关于拖拽请参考MDN文档: developer.mozilla.org/zh-CN/docs/…

    以上就是vue选项卡Tabs组件实现示例详解的详细内容,更多关于vue选项卡Tabs组件的资料请关注自由互联其它相关文章!

    本文共计1901个文字,预计阅读时间需要8分钟。

    如何实现一个长尾关键词优化的Vue选项卡组件示例详解?

    目录+概述+效果图+实现过程+组件分析+所需知识+项目组件文件夹+Tabs.vue+TabPane.vue+render.js+index.js+使用+总结+概述+前端项目中,多页面涉及包括选项卡切换,涵盖路由切换等,指令v-

    目录
    • 概述
    • 效果图
    • 实现过程
      • 组件分析
      • 所需的前置知识
      • 项目组件文件夹
      • Tabs.vue
      • TabPane.vue
      • render.js
      • index.js
      • 使用
    • 总结

      概述

      前端项目中,多数页面涉及到选项卡切换,包括路由切换,指令v-if等,本质上其实和选项卡切换思想差不多,如果是个简单的选项卡,还是很简单的,我们也不需要什么组件库的组件,自己也能几行代码写出来,但是涉及到动画,尺寸计算,拖拽的功能的时候,多数情况下,自己写还是要花点时间的,组件库就提供了现成的,拿来改改样式就行,为了对这个组件更加深入的理解,这里自己实现一个带拖拽,过渡的tabs组件。

      效果图

      实现过程

      组件分析

      • 组件包含两部分:Tabs组件和TabPane组件,参考绝大多数组件库的习惯
      • 组件主要分为需要点击的tab栏和下面对应的内容块
      • 我们需要对内容区和选项卡点击区分别加上过渡动画,提升用户体验
      • 最后需要加上拖拽调整选项卡顺序的功能

      所需的前置知识

      • 熟悉vue内置transition组件
      • 深入掌握vue父子组件通信,除开emit和props,还需要掌握inject,emit和props,还需要掌握inject,emit和props,还需要掌握inject,parent,vnode,渲染函数等等,这些业务开发中用的不多,但是组件库里面比较常见。
      • 了解dom中位置计算和尺寸的基本计算
      • 熟悉html5新增拖拽相关事件

      项目组件文件夹

      如何实现一个长尾关键词优化的Vue选项卡组件示例详解?

      Tabs.vue

      <template> <div class="gnip-tab"> <div class="gnip-tab-nav"> <div v-for="(item, index) in tabNavList" @click.stop="handleTabNavClick(item, index)" :class="['tab-nav-item', item.name == activeName ? 'active' : '']" ref="tabNavItemRefs" @drop="handleDrop(item, $event, index)" @dragstart="handelDragstart(item, $event, index)" @dragover="handleDragOver(item, $event, index)" draggable="true" > <span v-if="item.text">{{ item.text }}</span> <render v-if="item.renderFun" :renderFn="item.renderFun"></render> </div> </div> <!-- 滚动滑块 --> <div class="tab-nav-track" :style="{ background: showTrackBg ? '#e5e7eb' : '', }" > <span class="track-line" :style="{ width: trackLineWidht + 'px', left: left + 'px' }" ></span> </div> <div class="tab-content-wrap"> <slot></slot> </div> </div> </template> <script> // render组件,label为render函数的时候进行渲染 import Render from "./render"; export default { props: { // v-model的那项 value: { type: String, }, // 是否显示滑块背景 showTrackBg: { type: Boolean, default: false, }, }, components: { Render, }, data() { return { // tab数组 tabNavList: [], // 当前活跃项 activeName: "", // 滑块的宽度 trackLineWidht: 0, // 当前活跃索引 currentIndex: 0, // 滑块偏移量 left: 0, // 拖拽开始的哪项 dragOriginItemIndex: null, // 拖拽活跃项的索引 dragStartIndex: null, }; }, mounted() { this.init(); }, methods: { // 初始化 init() { // 默认当前活跃项为外部v-model的值 this.activeName = this.value; // 页面渲染任务之后计算滑块偏移量和宽度 this.$nextTick(() => { this.currentIndex = this.$children.findIndex( (component) => component.name == this.value ); this.computedTrackWidth(); }); }, // 设置tab点击栏 setTabBar(tabsPaneInstance) { // tab的描述信息可以是字符串也可以是render函数 const label = tabsPaneInstance.label, type = typeof label; // 添加到数组项中,根据添加条件渲染 this.tabNavList.push({ text: type == "function" ? "" : label, renderFun: type == "function" ? label : "", name: tabsPaneInstance.name, }); }, handleTabNavClick(item, index) { if (item.name == this.activeName) return; // 更新当前活跃项 this.activeName = item.name; // 活跃项的索引 this.currentIndex = index; // 计算滑块的偏移量和宽度 this.computedTrackWidth(); }, // 计算滑块的偏移量和宽度 computedTrackWidth() { // 插槽子组件的索引集合 const tabNavItemRefsList = this.$refs.tabNavItemRefs; // 导航tab项的宽度 const scrollWidth = tabNavItemRefsList[this.currentIndex].scrollWidth; // 滑块的宽度为scrollWidth this.trackLineWidht = scrollWidth; // 定位的偏移量为offsetLeft this.left = tabNavItemRefsList[this.currentIndex].offsetLeft; }, /* 关于拖拽请参考MDN文档: developer.mozilla.org/zh-CN/docs/Web/API/DragEvent,实现拖拽需要清楚关于拖拽相关的几个事件 */ // 开始拖拽 handelDragstart(item, event, index) { // 说明是拖拽的当前活跃的哪一项,记录这一项的索引位置 if (item.name == this.activeName) { this.dragStartIndex = index; } this.dragOriginItemIndex = index; }, // 推拽进入目标区域 handleDragOver(item, event) { // 阻止默认事件 event.preventDefault(); }, //拖拽进入有效item handleDrop(item, event, index) { event.preventDefault(); // 说明拖动的位置是变了的 if (this.dragOriginItemIndex != index) { // 交换数据,重新渲染生成tab栏 this.swap(this.dragOriginItemIndex, index); // 重新计算滑块的偏移量 if (this.dragStartIndex !== null) { this.currentIndex = index; // 记住,数据更新为异步操作,因此我们这里需要用到nextTick,将计算任务放到渲染任务完成之后执行,避免计算不准确 this.$nextTick(() => { this.computedTrackWidth(); this.dragStartIndex = null; }); } else { // 不是点击拖拽当前活跃项,也要重新计算滑块跨度和位置,因为每个tab项的宽度不一致,因此,每次拖拽都需要重新计算 this.$nextTick(() => { this.computedTrackWidth(); }); } // 这里还可以根据需要,发布一个拖拽完成事件 } }, // 交换tab数据项 swap(start, end) { let startItem = this.tabNavList[start]; let endItem = this.tabNavList[end]; // 由于直接通过索引修改数组,无法触发响应式,因此需要$set this.$set(this.tabNavList, start, endItem); this.$set(this.tabNavList, end, startItem); }, }, }; </script> <style lang="less"> .gnip-tab { .gnip-tab-nav { display: flex; position: relative; .tab-nav-item { padding: 0 20px; cursor: pointer; line-height: 2; } } .tab-nav-item.active { color: #2d8cf0; } .tab-nav-track { width: 100%; position: relative; height: 2px; .track-line { height: 2px; background-color: #2d8cf0; position: absolute; transition: left 0.35s; } } } </style>

      TabPane.vue

      <template> <div class="gnip-tabs-pane"> <transition :name="paneTransitionName"> <div class="tab-pane-content" v-show="$parent.activeName == name"> <slot name="default"></slot> </div> </transition> </div> </template> <script> export default { props: { // tab项的文本或者render函数 label: { type: [String, Function], }, // 每项标识 name: { type: String, }, // 是否禁用当前项 disabled: { type: Boolean, default: false, }, }, data() { return { paneTransitionName: "enter-right", }; }, created() { // 统一tab的数据给父组件进行处理和渲染 this.$parent.setTabBar(this); }, }; </script> <style lang="less"> .gnip-tabs-pane { overflow-x: hidden; .enter-right-enter-active { transition: transform 0.35s; } .enter-right-enter { transform: translateX(100%); } .enter-right-to { transform: translateX(0); } } </style>

      render.js

      主要用于将函数通过转化为render函数形式的组件(前提未提供模板)

      export default { name: "RenderCell", props: { renderFn: Function, }, render(h) { return this.renderFn(h); }, };

      index.js

      按需导出组件

      import TabPane from "./TabPane.vue"; export { Tabs, TabPane };

      使用

      App.vue

      <template> <div class="app"> <div class="aline"> <Tabs v-model="tabName" show-track-bg> <TabPane label="首页" name="name1">首页</TabPane> <TabPane label="图书详情页" name="name2" disabled>图书详情页</TabPane> <TabPane label="个人主页" name="name3">个人主页</TabPane> <TabPane :label="labelRender" name="name4">购物车</TabPane> </Tabs> </div> </div> </div> </template> <script> import { Tabs, TabPane } from "@/components/Tabs"; export default { components: { Tabs, TabPane }, data() { return { tabName: "name1", labelRender(h) { return h("div", "购物车"); }, }; }, }; </script> <style lang="less"> * { margin: 0; padding: 0; } .app { padding: 20px; button { padding: 10px; background-color: #008c8c; color: #fff; margin: 20px 0; } .container { .operate { text-align: center; } .aline { width: 50%; } h2 { font-weight: bold; font-size: 20px; } .aline { &:nth-child(1) { margin-right: 20px; } } display: flex; justify-content: space-between; } } .aline { display: flex; justify-content: center; } .item { margin: 40px; img { width: 250px; height: 200px; } ul { margin: 0 auto; li { border: 1px solid red; height: 200px; width: 250px; } } } </style>

      总结

      通过上述组件的实现,对于HTML5拖拽事件的应用更加熟悉,关于拖拽请参考MDN文档: developer.mozilla.org/zh-CN/docs/…

      以上就是vue选项卡Tabs组件实现示例详解的详细内容,更多关于vue选项卡Tabs组件的资料请关注自由互联其它相关文章!