Vue2.5去哪儿学习笔记【三】
一、组件使用的细节
1.使用模板标签出现的bug
当使用模板定义一个组件显示tr及tr里的内容放在表格标签时,会出现以下bug:
发现模板跑到了table标签外面,并且和table同级这一层。如果在tbody里直接使用模板这样一点都不符合h5的编码规范(h5的规范里面说明,table里面必须放tbody,tbody里面必须要放tr)
解决方案:需要使用is属性,才能正确被浏览器正确解析。is属于指定要在内部使用的标签。
<tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr>h5的规范里某些标签下只能使用指定的标签,否则无法被浏览器正确解析,例如ul、table、a、select这些。(例如ul一般跟着li,select一般跟着option这样),这时候就要用is代替一下,让html语法符合规则验证。
2.子组件的data必须是一个函数
在非根组件(即子组件)里定义data的时候,data不能这样定义:运行时浏览器会报错提示:子组件里data必须是一个函数且要求返回一个对象 目的:子组件不像根组件只调用一次,子组件可能在页面的多个位置都有调用,这样可以让每个子组件有独立的数据存储,不会出现多个子组件互相影响的情况
3.vue中的ref引用
有时候在处理相对复杂的动画情况的时候,我们不可免的要去操作dom,那么,在Vue中怎么样才能操作dom呢? 通过ref这种引用获取dom来操作dom
ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例.
例子:
①点击时普通标签时获取其文本内容
②自定义2个计数器器组件,每点击一次计数器数字自增1,使用一个普通标签获取这2个计数器的总和
二、父子组件的数据传递
本小节复习回顾之前学的父子组件的数据传递。
上述第二个例子两个计时器通过ref这种引用实现了每点击一次计数器数字自增1并获取其总和的功能。当不使用ref引用的时候,我们可以使用父子组件的数据传递来实现该项功能。
①父组件向子组件传递数据:通过属性的形式向子组件传递数据,使用 props 把数据传给子组件
例子:两个计数器,每点击一下自增1
代码解析:使用了局部组件注册了两个计数器,父组件向两个计时器分别传递了起始数据1和2,由于vue单向数据流概念,子组件不能直接修改父组件传递过来的数据(否则浏览器会返回一个警告),所以将父组件传递过来的数据赋值给子组件自己的data里面,最后定义一个方法,每点击一下就使得子组件自己的计数数据自增1
vue单向数据流概念:父组件可以向子组件通过属性形式传递参数,子组件绝对不能反过来修改父组件传递过来的参数。
因为如果子组件接收的是引用形式数据{data}而不是基础类型数据"data",一旦修改其内容,影响的不仅是该子组件还有可能对其他使用了该引用形式数据的子组件造成影响
②子组件向父组件传递数据:子组件使用 $emit 触发父组件的自定义事件
例子:两个计数器,每点击一下自增1,使用一个普通标签获取这2个计数器的总和
代码解析:子组件使用 $emit ('事件名',参数)来触发父组件的自定义事件,此处子组件使用了$emit('inc',1)触发父组件的自定义事件并传递计数器每点击所增加的参数值(该例子设定每次改变增加的值为1),父组件则在页面模板的dom标签添加一个监听事件,即 @事件名="执行方法名",那么一旦子组件做了修改,在父组件就能监听到其改变,在页面就会做出相应的改变。此处为@inc="handleIncrease"。计数器一旦发生改变,页面也会做出相应的改变。父组件在实例里定义了一个方法handleIncrease,当子组件做了修改时就会执行该方法,该方法接收到了计数器每点击所增加的参数值,实现每次增加1的计数器总数和。
三、组件参数校验与非Props特性
1.组件参数校验
父组件向子组件传递参数 ,子组件有权对这些参数进行约束,就叫做组件参数校验。子组件可以为组件的 prop 指定验证要求,例如指定为String或Number类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中返回警告例子:①基础类型检查:子组件要求父组件传递过来的必须是个字符串,对其参数进行校验②多个可能的类型检查:子组件要求父组件传递过来的必须是个字符串或数字,对其参数进行校验③复杂的校验:子组件要求父组件传递过来参数且必须是个字符串并且长度大于5,若没有父组件参数传递则使用子组件的默认值
2.props与非props的特性
props特性:父组件向子组件传递参数,子组件声明接收这个参数①接收的属性不会在dom标签的html属性里面显示②子组件接收父组件传递的参数,在子组件里通过插值表达式或this.xx去取得数据内容非props特性:父组件向子组件传递参数,子组件没有声明接收这个参数①父组件向子组件传递属性,子组件没有声明要接收这个内容 子组件就无法使用这个内容,一旦使用浏览器就会报错②声明一个非props特性,这个属性会展示在子组件最外层的dom标签的html属性里面
四、给组件绑定原生事件
父组件中给子组件所绑定事件 @click="事件方法名" ,事件绑定为 自定义事件所以在下面这个例子里,点击子组件,并不会有弹窗提示。①如何触发自定义事件?
子组件想触发父组件在这块的监听就得对 template 的dom元素上绑定一个 原生事件,并调用this.$emit('click')去触发自定义事件,②如何触发原生事件?
触发原生事件有2个方法:(1)在子组件里对 template 的dom元素绑定原生事件,在子组件methods上执行原生事件,然后调用this.$emit(event,tag)再触发父组件的自定义事件上述例子既触发了子组件的原生事件也触发了父组件的自定义事件,但在开发的过程中这样写有点太麻烦(2)在子组件上监听原生事件需要在事件绑定的后面加上一个native这样的事件修饰符, 例如:@click.native="执行方法名"
五、非父子组件间传值
非父子组件间传值有两种方案:
①借助Vue官方提供的一个数据层的框架——Vuex
②发布订阅模式(又称为:总线机制/Bus/观察者模式)
这里先介绍总线机制的使用。
例子:每个子组件被点击的时候,希望另一个子组件跟着改变自己的内容 (两个组件之间进行传值,但两个组件之间不具备父子关系)
代码解析:
①Vue.prototype.bus = new Vue()
这句话的意思是,在 Vue 的prototype
挂载了一个bus
属性,这个属性指向 Vue 的实例,只要我们之后调用 Vue 或者new Vue
时,每个组件都会有一个bus
属性(prototype 属性使我们有能力向对象添加属性和方法),因为以后不管是 Vue 的属性还是 Vue 的实例,都是通过 Vue 来创建的。②组件被挂载之前会执行mounted钩子函数,所以可以在mounted中对change事件进行监听。
③因为单向数据流缘故,子组件对父组件传递过来的数据值不能乱更改,所以需要使用data存储父组件传递过来的数据值。
④
this.bus.$on()
会被执行两次,因为两个child
的组件都进行了同一个事件的监听,所以两个child
的组件都会执行一遍this.bus.$on()
⑤this.bus.$on里的function 的this作用域发生了变化,此时的this指的并不是子组件child,而是bus,一个被new 出来的空的Vue对象,所以需要保存指向子组件对象的this,即var this_ = this.
六、Vue中的插槽
如果子组件除了展示p标签之外还要展示一段内容,而这段内容是由父组件传递过来的,这样一个代码需求该如何实现呢?
我们可能会这样子写:父组件向子组件传递参数,子组件声明接收这个参数,展示父组件传递过来的内容
但运行结果可以看到:发现数据中的html标签不能被解析,而是当作字符显示出来。
为了解决这个问题,我们可能会想到之前所学的vue指令:v-html,使用它可以解决Vue.js渲染过程中html标签不能被解析的问题
使用v-html可以正常显示,但是在dom结构上会赋予div元素:
使用插槽就能解决以上问题。
插槽
vue中的插槽,指的是子组件中提供给父组件使用的一个占位符,用<slot></slot>标签表示,父组件可以在这个占位符中填充任何模板代码,比如HTML、组件等,填充的内容会替换掉子组件的<slot></slot>标签(替换占位符)。
vue中的插槽大致可以分为默认插槽、具名插槽和作用域插槽三种。
默认插槽
默认插槽是最简单的一种插槽,指没有名字的插槽,通过在子组件中放置一个替换占位符<slot></slot>达到在父组件中更改子组件中内容的效果。
使用插槽可以很轻松地解决上述例子出现的问题:
运行结果:既解析了html标签也没有在dom结构上赋予div元素
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的组件:在内容区域部分的头部和尾部加上由父组件传递过来的header和footer
如果我们使用默认插槽:
可以看到运行结果:header和footer显示了2遍,因为<slot></slot>代表显示插槽的内容,有多少个插槽就会全部显示出来。
对于这样的情况,
<slot>
元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽,这种插槽就叫做具名插槽。我只想把部分插槽放在相应的位置,应使用具名插槽(给插槽起一个名字)使用方法如下:
作用域插槽
有这样一个代码需求:子组件实现一个功能 可以循环显示一个列表内容
还没学过作用域插槽的话,我们可能会这样简单写:
但如果有可能在很多地方调用child 希望在不同的地方调用child组件的时候,列表怎么循环,列表的样式是由外部告诉的 ,而不是有组件自己决定的,该如何实现呢?这时候作用域插槽就派上用场啦
作用域插槽允许你传递一个模板而不是已经渲染好的元素给插槽。之所以叫做”作用域“插槽,是因为模板虽然是在父级作用域中渲染的,却能拿到子组件的数据。
使用方法如下:
v-slot :插槽名称
当子组件使用slot的时候,会往slot里面传递一个item数据 item数据放在v-slot写的这个插槽之中,所以v-slot后边是组件内部绑定作用域值的映射。
七、动态组件与v-once指令
动态组件
有这样一个需求:当点击change的时候 2个组件做一个toggle(切换)展示隐藏的效果 ,该怎么实现呢?<div id="root"> <child-one></child-one> <child-two></child-two> <button>change</button> </div> Vue.component('child-one', { template: `<div>child-one</div>`, }) Vue.component('child-two', { template: `<div>child-two</div>`, }) var vm = new Vue({ el: '#root' })有以下2个方案:
①通过下面handleBtnClick函数的实现,配合v-if指令就能实现toggle效果
②动态组件:动态组件里面有个属性是is 。用is绑定type数据 动态组件会根据is里面数据的变化自动加载不同的组件vue自带的标签<component> 指的就是动态组件,它会根据is里面数据的变化,会自动的加载不同的组件。
v-once指令
在上述代码中,每一次切换的时候,底层都要销毁一个组件又要创建一个组件 这种操作其实耗费了一定的性能。如果我们的组件内容每一次都一样,可以在这之上加v-once指令
Vue.component('child-one', { template: `<div v-once>child-one</div>`, }) Vue.component('child-two', { template: `<div v-once>child-two</div>`, })v-once 第一次展示的时候放在了缓存里,再次切换展示的时候并不需要重新创建新的组件,而是从内存里拿出以前的组件使用使用v-once可以有效提高一些静态内容的展示效率