欢迎你们前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~javascript
本文由 蔡述雄发表于 云+社区专栏
组件库是作UI和前端平常需求中常常用到的,把一个按钮,导航,列表之类的元素封装起来,方便平常使用,调用方法只需直接写上<qui-button></qui-button>或者<qui-nav></qui-nav>这样的代码就能够,是否是很方便呢,接下来咱们将要完成如下页面:css
这是咱们组件库的首页,包含三个子页面,按钮页面、列表页面、导航页面;点击进去子页面会由路由来配置。先看咱们的目录结构:html
pages目录存放咱们的页面,包括首页和三个子页面;components目录存放咱们的具体组件,包括按钮组件,箭头组件,列表组件和导航组件(组件和页面实际上是同样的文件类型,只是因为功能不同,咱们就叫不一样的称呼)前端
先看路由配置的代码吧!vue
import Vue from 'vue' import Router from 'vue-router' // 引用页面模板->供路由使用 import index from '../pages/index.vue' import pageQuiButton from '../pages/pageQuiButton.vue' import pageQuiList from '../pages/pageQuiList.vue' import pageQuiNav from '../pages/pageQuiNav.vue' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'index', component: index }, { path: '/btn', name: 'btn', component: pageQuiButton }, { path: '/list', name: 'list', component: pageQuiList }, { path: '/nav', name: 'nav', component: pageQuiNav } ] })
有了上一篇的分析以后,这里应该很容易看出来几个路由地址java
首页:http://localhost:8080/#/
vue-router
按钮子页:http://localhost:8080/#/btn
数组
列表子页:http://localhost:8080/#/list
app
导航子页:http://localhost:8080/#/nav
框架
具体每一页的内容分别对应每一页的.vue文件,不知你们是否还记得入口页App.vue
,这个文件承载着一些公用的元素,还有就是一个路由容器,咱们的首页index.vue
到时候也是挂载在路由容器中的,看看App.vue
的代码
<template> <div id="app"> <h1 class="page-title"><a href="#/">开发组件库</a></h1> <router-view></router-view> </div> </template> <script> export default { name: 'app' } </script> <style scoped> @import './assets/css/App.css'; </style>
简单分析一下入口页的代码,h1标签是一个公用元素,也就是说到时候每一个子页面都会带着这个h1,他的做用就是方便咱们快速回到首页,子页面的内容会注入到router-view
中。这里值得关注的地方是style标签,咱们能够在style标签里面直接写样式,也能够直接引入一个样式文件,scoped关键字表示这个样式是私有的,也就是说,即便两个组件写着同样的#app{}
样式也不会冲突,程序会加上命名空间,这也就是为何在script标签中有个name参数。
<template> <div class="mod-module mod-parallel"> <div class="img-list type-full"> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/btn">按钮</a> </p> </div> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/list">列表</a> </p> </div> <div class="img-box"> <p class="img-item"> <a class="page-link" href="#/nav">导航</a> </p> </div> </div> </div> </template> <style scoped> @import './css/index.css'; </style>
首页的代码也是很是简单,和咱们平时写html差很少,就是几个跳转连接跳到对应的子页面,程序运行的时候,会将<template>
标签里面的内容都注入到App.vue页面中的router-view
标签中,从而实现无刷新的路由跳转。
从下面的内容开始,咱们的知识将会深刻一些。咱们先不急着看其余几个子页面,由于子页面里面只是引用对应的组件,因此咱们先从组件开始入手。
<template> <button class="qui-btn"> <span>{{msg}}</span> </button> </template> <script> export default { data:function(){ return { msg:'下载' } } } </script> <style scoped> @import './css/reset.import.css'; @import './css/qui-btn.import.css'; </style>
按钮组件很简单,就是一个正常的button标签,script标签中暴露这个组件的data属性(data是Vue的属性值,不是乱写的~~)。当按钮组件被初始化的时候,msg自定义属性会被绑定到<span>标签中的{{msg}}中,两个花括号用来绑定属性,这种写法学过模版化前端代码的人应该都比较熟悉。这里须要注意一个地方,若是不是组件的话,正常data的写法能够直接写一个对象,好比data:{ msg : ' 下载 ' },但因为组件是会在多个地方引用的,JS中直接共享对象会形成引用传递,也就是说修改了msg后全部按钮的msg都会跟着修改,因此这里用function来每次返回一个对象实例。
这就是一个很是简单的按钮组件,结构、样式+文案。
这时候问题来了,按钮中的文案我但愿能够异化,不能每次都初始化一个叫作“下载”文案的按钮吧,但愿能够以属性的方式来使用,好比这样子写就能够改变咱们的按钮文案:
<qui-btn msg="肯定" class="small"></qui-btn>
没问题,属性的接口暴露只须要写在prosp里面就能够了,以下所示修改下script标签的内容:
<script> export default { props: { msg: { default: '下载' } } } </script>
把属性写在props里面,就能够暴露给其余页面调用了,在组件中,props是专门用来暴露组件的属性接口的,这里给了一个默认值‘下载’
,后面咱们要使用的话,只须要<btn msg="确认"></btn>
就能够修改按钮的默认文案了。
咱们在上一篇文章的开头就讲了Vue是数据驱动模式的,当我在btn结构写上msg="确认"
的时候,对应script里面的msg属性就会自动修改了。
按钮总少不了点击事件吧,那在Vue中怎么绑定事件呢,用methods属性,看下代码:
<template> <button class="qui-btn" v-on:click="btnClickEvent"> <span>{{msg}}</span> </button> </template> <script> export default { props: { msg: { default: '下载' } }, methods: { //绑定事件的关键代码 btnClickEvent: function(){ alert(this.msg); } } } </script>
methods属性中能够写任何的自定义函数,写完以后绑定的方式也很简单,在button上写关键字v-on:click
,把对应的事件写上就能够了,以上代码实现的就是点击按钮弹出按钮中的文案,v-XXX
是Vue里的一些关键字,叫作指令,咱们后面会慢慢学到更多的指令;v-on:click
能够缩写为@click
,固然还有其余的事件好比v-on:tab
等等;
如今咱们大体作了一个按钮组件了,那么怎么调用它呢,去到咱们的pageQuiButton子页面。
//pageQuiButton.vue <template> <div id="pageQuiButton"> <!--使用--> <qui-btn msg="肯定" class="small"></qui-btn> </div> </template> <script> import quiBtn from '../components/quiButton.vue' /*引用*/ export default { name: 'pageQuiButton', components: { 'qui-btn': quiBtn /*注册自定义标签*/ } } </script>
从script开始解析,首先引入咱们的组件赋值给变量quiBtn,使用时候直接将quiBtn做为对象的一部分写进components属性,这是Vue用来存储引用组件的关键字,同时对应咱们自定义的标签 "qui-btn"
,完成这些操做以后,咱们就能够在template中使用自定义的按钮组件<qui-btn>
上面也说了用msg属性来自定义按钮的文案。完成以后,咱们就能够在页面中看到具体效果,点击按钮弹出对应的文案。
上述咱们将按钮事件写成默认的alert(this.msg)
,若是有些按钮想要异化怎么办。以前说了msg属性能够支持自定义,那么按钮的点击事件如何支持自定义呢?
//pageQuiButton.vue //监听子组件的事件 <qui-btn v-on:btnClickEvent="doSth" msg="我能够点击" ></qui-btn>
上面的代码在引用组件的时候,注册了一个事件,这个btnClickEvent事件是以前咱们在按钮组件中绑定到按钮的click事件中的,而后咱们给这个事件一个自定义的方法doSth,同时,在script中声明这个自定义的方法以下:
//pageQuiButton.vue //页面中引用子组件并监听子组件的事件 <script> import quiBtn from '../components/quiButton.vue' export default { name: 'pageQuiButton', components: { 'qui-btn': quiBtn }, methods: { doSth: function(){ alert('你点击了组件的click:btnClickEvent'); } } } </script>
专业一点的说,这种作法叫作监听,由引用方(暂且叫作父组件)监听子组件的内置方法;同时在子组件中,须要触发这个事件,如下是在子组件中的关键代码:
//quiButton.vue //子组件中的代码 <script> export default { props: { msg: { default: '下载' } }, methods: { btnClickEvent: function(){ alert("先弹出默认的文案"); this.$emit('btnClickEvent');//关键代码父组件触发自定义事件 } } } </script>
这里的关键代码就是$emit
,也叫触发机制,父组件监听,子组件触发。若是以为绕,如下描述可能会比较好理解:小B(子组件)有一个电话号码(子组件注册的事件),有一天小B把电话号码告诉了小A(父组件),让小A打电话给他,因而小A就拨打了小B的电话号码(监听),这时候整个沟通流程没有结束,必需要小B接听了电话(触发),两人之间才算完成了打电话这件事情。
完成这步以后,引用方(父组件)就能够给不一样子组件调用不一样的事件处理了:
<qui-btn v-on:btnClickEvent="doSth1" msg="肯定" ></qui-btn> <qui-btn v-on:btnClickEvent="doSth2" msg="取消" ></qui-btn> <script> /*这里只是简单展现*/ methods: { doSth1: function(){ alert('111'); }, doSth2: function(){ alert('222'); } } </script>
有时候单纯的文案异化还不够,好比一些按钮是图标+文字类型的,并且图标还可能不同,那应该怎么办呢?
若是按钮组件的结构除了开发时候预设的那些dom结构以外,容许咱们在调用的时候添加一些本身想要的结构,那是否是解决了呢,是的,Vue早就为咱们考虑了这一点,他就是slot标签。
下面给咱们的按钮组件加上一段结构
//quiButton.vue <template> <button class="qui-btn" v-on:click="btnClickEvent"> <slot name="icon"></slot><!--重点在这里--> <span>{{msg}}</span> </button> </template>
加入了关键字slot并赋予一个name值以后,咱们再看看引用如何使用
//pageQuiButton.vue <qui-btn msg="下载" class="with-icon"> <img slot="icon" class="ico" src="xxx.png" /> </qui-btn>
img上有个关键字slot="icon"
对应组件中的name="icon"
,渲染的时候,会将img整个替换掉组件中的对应name的<slot>
标签,其实很好理解,slot的翻译是插槽的意思,至关于把img这块内容插到一个名叫icon的插槽里面去。
学到这里,咱们已经学会了用props给按钮自定义文案,用on和emit给按钮自定义点击触发,用slot给按钮添加一些自定义结构。当你回头去翻文档的时候,你会发现props,事件,slot这三样恰好就是学习组件通信中最最最关键的三个环节。将这三个环节以实际案例解析出来后,好像也没有那么难了吧~!
上述咱们已经讨论了如何制做一个按钮组件,以及如何使用咱们的按钮组件。
接下来咱们经过制做一个导航组件,来了解Vue中对于for循环的巧妙使用。
咱们将完成这样一个导航组件,点击导航中的tab,能够给当前tab加上一个active类,同时切换底部的黄色滑条,而且输出当前tab的文案,同时支持自定义事件。
因为在现实项目中,咱们导航的tab个数是不定的,因此制做组件的时候,咱们但愿能够暴露一个属性来支持导航的tab个数,而tab的长相和应用实际上是同样的,那么这时候咱们能够用一个for循环来输出每个tab。先看看关键代码:
//quiNav.vue <template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" ><!--关键代码v-for--> <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { items:[ { text: '首页', active : true }, { text: '列表', active : false }, { text: '关于', active : false }, { text: '招聘', active : false } ] } } } } </script>
该段代码的关键地方在于a标签上v-for
关键字(还记得咱们在前面说过的v-on绑定事件吗,v-XXX关键字是Vue预留的)能够把它理解为js中的for in 循环,items是咱们在data里面定义的对象(还记得为何data要写在function中返回吗?)。v-for="(item,index) in items"
暴露了item和index两个接口,这是Vue提供的,表明items中的每一项以及该项对应的下标,接着咱们就能够在标签中使用绑定{{item.text}}
了。
这段代码理解了以后,咱们再延伸一个动态添加class的概念。咱们但愿每一个tab都有默认的class类名(好比nav-item
类),在点击每一个tab的时候,当前tab添加active类,其余的tab删除这个active类。在Vue怎么实现呢?
//quiNav.vue <template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" :class="[commonClass,item.active ? activeClass : '']" > <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { commonClass:'nav-item', activeClass:'active', items:[ ...//数据 ] } } } </script>
在template中添加了一句关键代码
:class="[commonClass,item.active ? activeClass : '']"
:class
给组件绑定一个class属性(相似jQuery中的attr方法),这里的写法是缩写,他的全拼应该是v-bind:(又一个v-XXX写法)。注意到最前面有个冒号,:class=XXX
和class=XXX
的区别在于不带冒号的是静态的字符串绑定,带冒号的是动态的变量绑定。咱们给class绑定了一个数组,这个数组带有变量,先看commonClass,这个变量在data中定义了,而后数组的第二个元素是一个JS的三元运算符:item.active?activeClass:''
,当每一个item中的active值为true时,绑定activeClass变量对应的类,若是为false,则为空。最后的结果是当item.active为true时候,tab的class值为'nav-item active'
,当为false,就只有'nav-item'
。
上面的代码能够理解的话,那么咱们切换tab的active类,就转换为修改每一个item里面的active的值(再次体现数据驱动)。
那么问题来了,怎么去修改每一个item里面的active值呢?没错,给每一个tab绑定一个点击事件,当点击事件触发的时候,修改当前tab对应item的active值。因而代码变成了以下:
<template> <div class="qui-nav nav-type-1"> <a v-for="(item, index) in items" :class="[commonClass,item.active ? activeClass : '']" v-on:click="navClickEvent(items,index)" > <span class="nav-txt">{{item.text}}</span> </a> </div> </template> <script> export default { data:function(){ return { commonClass:'nav-item', activeClass:'active', items:[ { text: '首页', active : true }, ...... ] } }, methods:{ navClickEvent:function(items,index){ /*默认切换类的动做*/ items.forEach(function(el){ el.active = false; }); items[index].active = true; /*开放用户自定义的接口*/ this.$emit('navClickEvent',items,index); } } } </script>
咱们利用for循环给每一个a标签绑定了一个click事件,对应methods中定义的navClickEvent,接收两个参数items和index(你也能够传人item和index,看我的代码喜爱),而后当点击的时候,把items中的每一个item.active置为false,把当前的tab的active值置为true,这样就能够动态切换active类了。最后再触发一次自定义事件(参考按钮制做自定义事件)。
以上就是咱们导航组件的内容了,回想下咱们作了啥?for循环输出每一个tab,为每一个tab绑定动态的class类名,同时在点击事件中动态切换类(底部的小黄条实际上是利用active类作的CSS)
回顾下咱们这一篇章都学了什么内容。
上述内容已经基本上涵盖了组件的重要知识点,主要是父组件(页面)和子组件之间的调用和通信(数据交互绑定),好好消耗一下咱们会发现,其实Vue的整体逻辑思想和jQuery是同样的,毕竟最后都回归到javascript,只是因为代码设计角度的不一样,咱们可能看到和之前调用jQuery时候的写法不一致,但其实都有对方的影子在里面,相信理解了Vue的代码思想以后,之后咱们学习React等其余相似的框架的时候,也会比较驾轻就熟了。
下一篇文章《包学会之浅入浅出Vue.js:结业篇》中,咱们将会学习如何用多个组件来组成一个大的组件,也就是真正意义上的父子组件之间的关系。再忍耐一下,就能够出山了,新领域的大门就在前面,让咱们大步往前跨吧。
文末附上全部相关代码和官方文档地址~~~
相关阅读
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识
此文已由做者受权腾讯云+社区发布,更多原文请点击
搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!
海量技术实践经验,尽在云加社区!