看JQ时代过来的前端,如何转换思路用Vue打造选项卡组件
前言
在Vue还未流行的时候,我们都是用JQuery来封装一个选项卡插件,如今Vue当道,让我们一起来看看从JQ时代过来的前端是如何转换思路,用数据驱动DOM的思想打造一个Vue选项卡组件。
接下来,正文从这开始~
先来看一下用Vue写的选项卡组件在浏览器上的展示效果:

其实,你在浏览器上看到的UI界面效果也就是那么回事,中规中矩。当点开Chrome的Devtools下面的Elements选项,你看到的dom节点其实和jQuery的dom节点如出一辙,不同的是,现在你看到的dom树是在Vue组件生命周期mounted之后渲染得到的真实DOM

首先我们来聊一聊关于选项卡组件的业务需求,每个标签页的主体内容是由使用组件的父级控制,所以这部分应该是一个slot。slot的数量决定了标签切换按钮的数量。假设我们有3个标签页,点击每个标签按钮时,另外的两个标签对应的slot将被隐藏掉。
先来贴出html结构:
<div id="app" v-cloak>
<tabs>
<pane label="标签一" name="1">
<p>标签一的内容</p>
</pane>
<pane label="标签二" name="2">
<p>标签二的内容</p>
</pane>
<pane label="标签三" name="3">
<p>标签三的内容</p>
</pane>
</tabs>
</div>
由html结构可知,我们需要定义两个组件tabs和子组件pane,pane嵌套在标签组件tabs里面。由于tabs和pane两个组件是分离的,但是tabs的组件上的标题应该由pane组件来定义,因为slot是写在pane里,因此在组件初始化(以及标签标题动态改变)时,tabs要从pane里获取标题,并保存起来,自己使用。
接下来,先初始化各个组件:
Vue.component('tabs',{
template:`
<div class="tabs">
<div class="tabs-bar">
<!-- 标签页标题,这里要用v-for -->
</div>
<div class="tabs-content">
<!-- 这里的slot就是嵌套的pane -->
<slot></slot>
</div>
</div>
`
})
Vue.component('pane',{
name:'pane',
template:`
<div class="pane" v-show="show">
<slot></slot>
</div>
`,
data:function(){
return {
show:true
}
}
})
pane组件中需要控制标签页内容的显示与隐藏,所以设置一个data:show,并且用v-show指令来控制元素。
如果你是用jQuery写过选项卡,应该知道,在写完控制tabs标题显示与隐藏后,接下来该控制tabs内容的显示与隐藏了。那么如果要点击对应的标签页标题显示对应的标签页内容,此时应该有一个唯一的值来标识这个pane,我们可以设置一个prop:name让用户来设置,其实这个name也就是索引index。除了name,还需要设置标签页的标题prop:label。
props:{
name:{
type:String
},
label:{
type:String,
default:''
}
}
上面的标题prop:label 用户是可以动态调整的,所以在pane初始化及label更新时,都要通知父组件也更新,因为是独立独立组件,我们可以直接通过this.$parent访问tabs组件的实例来调用它的方法更新标题,这个方法名暂定为updateNav:
methods:{
updateNav(){
this.$parent.updateNav();
}
},
watch:{
label(){
this.updateNav();
}
},
mounted(){
this.updateNav();
}
通过以上代码可以看到,在生命周期mounted,也就是pane初始化时,调用一遍tabs的updateNav方法,同时监听了prop:label,在label更新时,同样调用。
说完了pane组件,剩下的任务就是完成tabs组件。
首先就是需要把pane组件设置的标题动态渲染出来,也就是当pane触发tabs的updateNav方法时,更新标题内容。
Vue.component('tabs',{
template:`
<div class="tabs">
<div class="tabs-bar">
<div
:class="tabCls(item)"
v-for="(item,index) in navList"
@click="handleChange(index)">
{{item.label}}
</div>
</div>
<div class="tabs-content">
<slot></slot>
</div>
</div>
`,
props:{
// 这里的value是为了可以使用v-model
value:{
type:[String, Number]
}
},
data:function(){
return {
// 因为不能修改value,所以复制一份自己维护
currentValue:this.value,
navList:[] // 用于渲染tabs的标题
}
},
methods:{
tabCls:function(item){
return [
'tabs-tab',
{
// 给当前选中的tab加一个class
'tabs-tab-active': item.name === this.currentValue
}
]
},
getTabs(){
// 通过遍历子组件,得到所有的pane组件
return this.$children.filter(function(item){
return item.$options.name === 'pane';
});
},
updateNav(){
this.navList=[];
// 设置对this的引用,在function回调里,this指向的并不是vue实例
var _this = this;
this.getTabs().forEach(function(pane,index){
_this.navList.push({
label:pane.label,
name:pane.name || index
});
// 如果没有给pane设置那么,默认设置它的索引
if(!pane.name) pane.name = index;
// 设置当前选中的tab的索引
if(index === 0){
if(!_this.currentValue){
_this.currentValue = pane.name || index;
}
}
});
// console.log(this.navList);
this.updateStatus();
},
updateStatus(){
var tabs = this.getTabs();
// console.log(tabs);
var _this = this;
// 显示当前选中的tab对应的pane组件,隐藏没有选中的
tabs.forEach(function(tab){
// console.log(tab.name === _this.currentValue);
return tab.show = tab.name === _this.currentValue;
})
},
// 点击tab标题时触发
handleChange:function(index){
var nav = this.navList[index];
var name = nav.name;
console.log(name);
// 改变当前选中的tab,并触发下面的watch
this.currentValue = name;
}
},
watch:{
value:function(val){
this.currentValue = val;
},
currentValue:function(){
this.updateStatus();
}
}
})
getTabs是一个公用的方法,使用this.$children来拿到所有的pane组件实例。在遍历了每一个pane组件后,把他的label和name提取出来,构建成一个Object并添加到数据navList数组里。拿到navList后,就可以用v-for把tab的标题渲染出来,并且判断每个tab当前的状态。
通过以上代码,大家可以看到,在使用v-for指令循环显示tab标题时,使用v-bind:class指向了一个名为tabCls的methods来动态设置class名称。
点击每个tab标题时,会触发handleChange方法来改变当前选中的tab的索引,也就是pane组件的name。在watch选项里,我们监听了currentValue,当其发生变化时,触发了updateStatus方法来更新了pane组件的显示状态。
后记
总结一下:该选项卡组件,使用了组件嵌套的方式,将一系列pane组件作为tabs组件的slot;tabs组件和pane组件通信上,使用了$parent 和 $children 的方法访问父链和子链;定义了prop:value 和 data: currentValue。
以上就是Vue标签页组件的所有实现过程。
看JQ时代过来的前端,如何转换思路用Vue打造选项卡组件的更多相关文章
- 基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记【ThinkSNS+研发日记系列】
在上一篇文章< ThinkSNS+基于Laravel master分支,从1到 0,再到0.1>,简单的介绍了 社群系统ThinkSNS+ ,这里分享在开发过程中,前端选择的心理活动. L ...
- 基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记
在上一篇文章< ThinkSNS+基于Laravel master分支,从1到 0,再到0.1>,简单的介绍了 ThinkSNS+ ,这里分享在开发过程中,前端选择的心理活动. Larav ...
- web前端开发面试题(Vue.js)
1.active-class是哪个组件的属性?嵌套路由怎么定义? 答:vue-router模块的router-link组件. 2.怎么定义vue-router的动态路由?怎么获取传过来的动态参数? ...
- 前端工程化(三)---Vue的开发模式
从0开始,构建前后端分离应用 导航 前端工程化(一)---工程基础目录搭建 前端工程化(二)---webpack配置 前端工程化(三)---Vue的开发模式 前端工程化(四)---helloWord ...
- vue仓库、组件间通信、前后台数据交互、前端储存数据大汇总
目录 路由重定向 仓库介绍 vuex插件:可以完成任意组件间信息交互(移动端) 前端存储数据大汇总 前后台交互方式(重点) 前后台数据交互 axios插件:完成前后台ajax交互的 同源策略 - 前后 ...
- 在Vue前端项目中,附件展示的自定义组件开发
在Vue前端界面中,自定义组件很重要,也很方便,我们一般是把一些通用的界面模块进行拆分,创建自己的自定义组件,这样操作可以大大降低页面的代码量,以及提高功能模块的开发效率,本篇随笔继续介绍在Vue&a ...
- 从互联网进化的角度看AI+时代的巨头竞争
今天几乎所有的互联网公司在谈论和布局人工智能,收购相关企业.人工智能和AI+成为当今科技领域最灸手可热的名词,关于什么是AI+,其概念就是用以表达将"人工智能"作为当前行业科技化发 ...
- HTML5时代的纯前端上传图片预览及严格图片格式验证函数(转载)
原文地址:http://www.2cto.com/kf/201401/274752.html 一.要解决什么样的问题? 在写这个函数之前,有们童鞋在群里问如何纯前端严格验证图片格式.这在html5时代 ...
- 一文带你看懂cookie,面试前端不用愁
本文由云+社区发表 在前端面试中,有一个必问的问题:请你谈谈cookie和localStorage有什么区别啊? localStorage是H5中的一种浏览器本地存储方式,而实际上,cookie本身并 ...
随机推荐
- canvas绘制多边形
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JSR系列开篇
JSR是Java Specification Requests的缩写,意思是Java 规范提案.是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求.任何人 ...
- Mixed Reality-宁波市VR/AR技术应用高研班总结
年,全球AR与VR市场规模将达到1500亿美元,而根据市场研究机构BI Intelligence的统计,2020年仅头戴式VR硬件市场规模将达到28亿美元,未来5年复合增长率超过100%.本次培训从V ...
- HTML基础教程-简介
关于html5笔记前言 之前有在W3school学习过html5以及javascript.为了和大家一块学习,为了回顾这些遗忘的基础,现在我把之前自己整理的笔记共享给大家.希望大家共同进步. HTML ...
- Parallels Desktop 12 for Mac 破解版
Parallels Desktop for Mac 是功能最强大灵活度最高的虚拟化方案,无需重启即可在同一台电脑上随时访问Windows和Mac两个系统上的众多应用程序.从仅限于PC的游戏到生产力软件 ...
- iOS 如何优化 App 的启动时间
App 运行理论 main() 执行前发生的事 Mach-O 格式 虚拟内存基础 Mach-O 二进制的加载 理论速成 Mach-O 术语 Mach-O 是针对不同运行时可执行文件的文件类型. 文件类 ...
- 【model模型传入view的数据类型错误】传入字典的模型项的类型为“System.Data.Entity.Infrastructure.DbQuery`1[MapScience.PovertyAlleviation.Web.Models.Qu
出现这个问题的原因是控制器中传给view的数据类型与View中设置的model类型不一致导致,比如控制器返回的IList类型的,而你在View里面model设置的是IEnumerable<> ...
- hbase rest api接口链接管理【golang语言版】
# go-hbase-resthbase rest api接口链接管理[golang语言版]关于hbase的rest接口的详细信息可以到官网查看[http://hbase.apache.org/boo ...
- python中的virtualenv是干嘛的?
众所周知,python的各种库跨度比较大,比如如果你开发web的话,一个项目使用的Django是1.8, 而另一个项目使用的Django版本是1.7, 这就给开发人员带来了很大的困扰. 因此,pyth ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...