目录
一、vue组件的三个API:prop、event、slot
二、Vue生命周期中的钩子函数
三、自定义指令
四、mixins基础概况
五、修饰符
一、vue组件的三个API:prop、event、slot
prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。
组件里定义的prop都是单向数据流,只能通过父级组件对齐进行修改,组件本身不能修改props的值,只能修改定义在data里的数据,非要修改,也是通过后面介绍的自定义事件通知父级,由父级来修改;
在子组件定义prop是,使用了camelCase的命名法,由于html特性不区分大小写。camelCase的prop用于特性时,会转为短横线隔开(比如availableValue)
因为数组或对象是地址引用,vue不会检测到props发生改变
但官方不建议在子组件内改变父组件的值,因为这违反了vue中props单向绑定的思想。
solt(插槽)
<template>
<button>
<slot name="icon"></slot>
<slot></slot>
</button>
</template>
这里的节点就是指定的一个插槽的位置,这样在组件内部就可以扩展内容了;
这样,父级内定义的内容,就会出现在组件对应的 slot 里,没有写名字的,就是默认的 slot;
event(事件)
this.$emit('on-click', event);
在组件中可以通过$emit触发自定义事件on-click,在父组件通过@on-click来监听组件之间的通信方式
父组件向子组件的通信方式可以通过props传递, 子组件向父组件传递数据则可以通过event传递:
非父子组件之间的通信方式:eventBus
我是使用的通过在根组件,也就是#app组件上定义了一个所有组件都可以访问到的组件,具体使用方式如下;
使用eventBus传递数据,我们一共需要做3件事情
1.给app组件添加Bus属性 (这样所有组件都可以通过this.$root.Bus访问到它,而且不需要引入任何文件);
2.在组件1里,this. emit触发事件;
3.在组件2里,this. on监听事件;
this.$nextTick:
在某个动作有可能改变DOM元素结构的时候,对DOM一系列的js操作都要放进Vue.nextTick()的回调函数中,Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 。
因为数据在页面的加载是有延迟,而$nextTick是在下次DOM更新时执行,$nextTick正好符合我们的要求。
vue中本身是不支持ajax的,要想进行异步请求,则需要通过axios来进行操作。但是再使用Echarts的时候,则需要获取所有的数据后,在进行更新图标数据。
这是可以通过async/ await来处理异步/同步请求:
给对象赋值
由1可以引申出,地址引用类型的数据,例如对象obj ={a:1},如果想要修改obj中的a属性,通过obj.a = 2这样赋值,页面不会更新,需使用vue.set方法更改才会起作用, Vue.set(this,obj,a,2) 或者 this.$set(obj,'a',2);或者this,$forceupdate()
Vue 实例的数据都保存在 data 对象中,Vue 将会递归将 data 的属性转换为 getter/setter,
从而让 data 的属性能够响应数据变化。
同样,如果要给obj增加一个新属性,如果该属性未在data中声明,页面也不会刷新。也就是vue文档中声明的“Vue 不能检测到对象属性的添加或删除”,同样需要使用vue.set 或者this.$set方法进行赋值才好使。
深拷贝/浅拷贝
先来看一个简单的例子:
let obj = {name:'fiona-SUN'};
let copyObj = obj;
copyObj.name = 'fiona';
console.log(copyObj.name); // 'fiona'
console.log(obj.name); // 'fiona'
在js中也有栈(stack)和堆(heap)的概念:
栈:自动分配的内存空间,大小确定会自动释放。存放变量/局部变量/形参等。在js中存放简单数据段(五种基本数据类型:Number、String、Boolean、Null、Undefined),他们是按值存放的,可以直接访问。
堆:动态分配的内存,大小不定并且不会自动释放。存放在堆内存中的对象,栈中的变量实际保存的是一个指针,这个指针指向堆中的某一个位置。
所以上述例子中,属于浅拷贝,当我们声明一个对象,由于他不属于五种基本数据类型(即非简单数据段
),栈中会存放一个我们声明的obj
变量,它指向了堆中实际的这个对象的地址
。当我们把这个引用地址赋值给了copyObj
,实际它获得的是一个与obj
一致的指向堆中的地址。当copyOjb
改变了指向的对象地址的实际的值的时候,obj
拿到的值也就自然而然变化了。看图理解⬇
es6之展开Object.assign(拷贝obj的内容到一个新的堆内存,copyObj存储新内存的引用)
复制一个对象
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
为大家精心准备了vue课程,微信搜索关注公众号:【zhulin1028】,或者扫描以下二维码,回复【vue课程】即可免费获取。
二、Vue生命周期中的钩子函数
1). 初始化显示(只执行一次)
* beforeCreate()el 和 data 并未初始化
* created() 完成了 data 数据的初始化,el没有
* beforeMount()完成了 el 和 data 初始化
* mounted()完成挂载,获取到DOM节点
2). 更新状态(可执行多次)
* beforeUpdate()
* updated():我们单击页面中的“更新数据”按钮,将数据更新。下面就能看到data里的值被修改后,将会触发update的操作
3). 销毁vue实例: vm.$destory()(只执行一次)
* beforeDestory()
* destoryed()
created()/mounted(): 发送api请求, 启动定时器等异步任务
beforeDestory(): 做收尾工作, 如: 清除定时器,音频播放器
destoryed :销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,可以这么理解,执行了destroy操作,后续就不再受vue控制了。因为这个Vue实例已经不存在了。
实例:
vue中的setInterval在页面离开之后仍会执行,在切换多个路由之后,定时器的速度会越来越快
beforeDestory(){
if(this.timer){
clearInterval(this.timer)
}}
总结:用created还是mounted方法看情况,一般放到created里面就可以了,这样可以及早发请求获取数据,如果有依赖dom必须存在的情况,就放到mounted(){this.$nextTick(() => { /* code */ })}
ref
ref
被用来给元素或子组件注册引用信息的静态节点,引用信息将会注册在父组件的 $refs
对象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件,ref和$refs其实就是用来获取/操作DOM元素的;类似于jquey中的$(".xxx");
缓存问题
ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
vue中过滤器filters的使用
组件内写法
filters:{
filter:function(data,arg1,arg2){
return ....
}
}
1.在html中使用
{{
msg |
filter(
'arg1',
'arg2') }}
2.methods中使用,并传参
methods:{
fn(){
let filter = this.$options.filters['filter']
let data = filter(this.msg,arg1,arg2)
}
}
3.在v-html中使用filters
<p><v-html="$options.filters.filter(this.msg,arg1,arg2)"></p>
Directive:指令
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换,不用在效验必选项
v-show是通过css的display:none进行隐藏控制,所以一开始就会渲染,肯定能够取到元素
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
三、自定义指令
组件:一般是指一个独立实体,组件之间的关系通常都是树状。
Vue
指令:用以改写某个组件的默认行为,或者增强使其获得额外功能,一般来说可以在同一个组件上叠加若干个指令,使其获得多种功能。比如 v-if,它可以安装或者卸载组件。
Vue 指令生命周期
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
生命周期函数中的参数
el: 指令所绑定的元素,可以用来直接操作 DOM,就是放置指令的那个元素。
binding: 一个对象,里面包含了几个属性
name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
<div v-demo="model">自定义指令</div>
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
这样,任何页面或组件,只要通过 inject 注入 app 后,就可以直接访问 userInfo 的数据了,比如:
四、mixins基础概况
vue中的解释是这样的,如果觉得语言枯燥的可以自行跳过嘿~
混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
总结
vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
Mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。
与公共组件的区别
组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。
Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。
利用本地化缓存机制
这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护;
通过window.localStorage.getItem(key) 获取数据; 通过window.localStorage.setItem(key,value) 存储数据;
注意用JSON.parse() / JSON.stringify() 做数据格式转换;
五、修饰符
vue中的修饰符主要分为两类:
1.事件修饰符:
.stop 阻止单机事件冒泡
.prevent 阻止默认行为(比如 @submit.prevent 会阻止提交后刷新页面)(类似a标签javascript:void(0))
.capture 添加事件侦听器时使用捕获模式
.self 只有事件在元素本身(而不是子元素)触发时触发回调
.once 只触发一次(组件也适用)
.key 触发事件的按键
修饰符可以串联
.native
现在在组件上使用 v-on
只会监听自定义事件 (组件用 $emit
触发的事件)。如果要监听根元素的原生事件,可以使用 .native
修饰符
就是在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加'. native'事件是无法触 发的。
@keyup.enter.native
2.v-model 修饰符:
.lazy
在各种情况下,v-model
在input
事件中同步输入框的值与数据,但你可以添加一个修饰符lazy
,从而转变为在change
事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转换为Number类型(如果原值的转换结果为NaN则返回原值),可以添加一个修饰符number
给v-model
来处理输入值:
<input v-model.number="age" type="number">
这通常很有用,因为在type="number"
时HTML中输入的值也总是会返回字符串类型。
.trim
如果要自动过滤用户输入的首尾空格,可以添加trim
修饰符到v-model
上过滤输入:
插槽
除非子组件模板包含至少一个<slot>插口,否则父组件的内容将被替换。当子组件模板只有一个没有属性的slot时,父组件整个内容片段将插入到slot所在的DOM位置,并替换掉slot标签本身。
最初在<slot>标签中的任何内容都被替换为内容。备用内容在子组件的作用域内编译,并且仅在容纳元素为空,且没有要插入的内容时才显示备用内容。
假定my-component组件有下面模板:
<div>
<h2>我是子组件的标题</h2>
<slot>
只有在没有要分发的内容时才会显示。
</slot>
</div>
父组件模版:
<div>
<h1>我是父组件的标题</h1>
<my-component>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</my-component>
</div>
渲染结果:
<div>
<h1>我是父组件的标题</h1>
<div>
<h2>我是子组件的标题</h2>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</div>
</div>
具名插槽
<slot>可以元素用一个特殊的属性name来配置如何配给物内容。多个槽可以有不同的名字。具名时隙匹配将内容片段中有对应slot特性的元素。
仍然可以有一个匿名slot,它是替换slot,作为找回匹配的内容片段的备用插槽。如果没有替换的slot,这些发现匹配的内容片段将被抛弃。
例如,我们假定一个有app-layout组件,它的模板为:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
父组件模版:
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
渲染结果为:
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>
在组合组件时,内容分发API是非常有用的机制。
作用域插槽
2.1.0补充
作用域插槽是一种特殊类型的插槽,利用使用一个(能够传递数据到)可重用模板替换已渲染元素。
在子组件中,只需将数据传递到插槽,就像你将prop传递给组件一样:
<div class="child">
<slot text="hello from child"></slot>
</div>
在父级中,具有特殊属性scope的<template>元素,表示它是作用域插槽的模板。scope的值对应一个临时变量名,此变量接收从子组件中传递的prop对象:
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>s
</child>
</div>
如果我们渲染以上结果,得到的输出会是:
<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>
作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项:
<my-awesome-list :items="items">
<!-- 作用域插槽也可以在这里命名 -->
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list>
列表组件的模板:
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- fallback content here -->
</slot>
</ul>