Vue--小知识

1.Vue的双向绑定其实是单向绑定

双向绑定的假设

假设,我有两个儿子,大儿子和二儿子

我有500元

大儿子要花500买衣服,把钱花了

二儿子要花200买书籍,钱没了。

原因:大儿子花钱的时候没有告诉我。

所以:我定了规矩,以后花钱必须先跟我说,然后我再把钱给你!

  • 因此,Vue的 v-model 变成了两个单项绑定组成的双向绑定,把 v-model 拆分可以变成这样
  • 现在我加一个子组件child ,有一个属性 selected ,通过 button 切换值
  • 这样能改,但是会报出警告,告诉你别在子组件里自己改,你要通过父亲修改(上面的假设)

​ 所以我改成这样

这样子组件的修改需要通过父组件,流程

Click button =>

触发@click事件=>

$emit触发当前实例上的事件“xxx”,传递$events=>

父组件监听 xxx方法,并将 value = $events =>

data中的value改变 =>

selected改变为当前value值

  • 上面的实例更加证明了,Vue是单向数据流

    但是双向绑定很爽,于是作者给了我们一个语法糖 .sync语法糖

原理写在下面注释里了,其实还是 监听了 @updata:selected 方法

2.created()和mounted() 父子传参

1
2
var div = document.createElement('div') //这是created ,将div写入内存
document.body.appendChild(div) //这是mounted ,将div挂到页面里

问题来了,如果div里有子元素,那么是什么顺序呢?

1
2
3
4
var div = document.createElement('div') // div created
var chilid = document.createElement('div') // child created
document.body.appendChild(chilid) // child mounted
document.body.appendChild(div) // div mouted

所以我们可以看到顺序是

创建父亲 —-> 创建儿子 —> mounted儿子 —>mounted 父亲

因此,得到在父子组件传参的时候,父亲的mouted()里面,一定能拿到所有儿子:this.$children

3.data和computed

当一个属性需要跟随另一个属性变化的时候使用 computed

data里的属性不会跟随其他属性变化

4.Vue开发插件

在写Toast组件的时候使用到了这个方法。

Vue.js的插件应当有一个公开方法install,第一个参数是vue,第二个参数是可选的选项对象options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Plugin.js
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}

// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})

// 3. 注入组件
Vue.mixin({
created: function () {
// 逻辑...
}
...
})

// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}

然后在 app.js 里注册

1
2
3
import Vue from 'vue'
import Plugin from './plugin'
Vue.use(Plugin,{someOptions:true})

然后就可以使用了

5.Element.getBoundingClientRect()

返回Element的大小以及相对与视口的位置。

在使用vm.$el.style.height获取不到高度等属性的时候可以尝试使用它

文档

6.ref

在Toast里使用了ref。

在普通的DOM 元素上使用,引用指向的就是 DOM 元素

1
2
3
4
5
6
7
<template> 
<div>
<p ref='test'>
123
</p>
</div>
</template>
1
2
//获取	
this.$refs.test // <p>123</p> 获取node元素

文档

如果用在子组件上,引用就指向组件实例:

1
<child ref='child'></child>

7.vm.$slots

1
2
3
<div slot = 'header'>
header
</div>
1
vm.$slot.header//header

文档

8.元素放到最后面

1
.a{margin-left:auto}

9.EventBus

当我们写父子传参,爷儿孙传参的时候,可以使用EventBus,会很方便.

因为Vue是不允许儿子改变父亲的值,只能告诉父亲我要改了,父亲再改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//爷爷组件
import Vue form 'vue'
export default{
name:'爷爷',
props:['selected'], //当前选择的tab
data(){
return{
eventBus:new Vue() //通过data生成一个eventBus
}
},
provide(){
return {
eventBus:this.eventBus //使用provide给所有子组件注入这个值
}
},
mounted(){
this.eventBus.$emit('update:selected',this.selected) //初始化,也可以不初始化,看实际代码需不需要
}
}
}
//子组件1
export default{
name:'儿子1号',
inject:['eventBus'],
props:['name']
created(){
this.eventBus.$on('update:selected',(name)=>{
console.log(name) //儿子1号监听事件.
})
},
methods:{
change(){
this.eventBus.$emit('update:selected',this.name) //儿子1号主动触发事件
}
}
}
//子组件2
export default{
name:'儿子2号',
inject:['eventBus'],
props:['name']
created(){
this.eventBus.$on('update:selected',(name)=>{
console.log(name) //儿子2号监听事件.
})
},
}
//孙子组件同理
//........
//........
  1. 创建一个EventBus为啥要用 new Vue()呢?

    因为我们使用了 $on $emit,只要符合这个条件就可以了,当然有时候还要用到$off

  2. EventBus的作用(不仅是父子,还有子子)

    我们在爷爷里声明了EventBus后,他就是我们的一个数据处理中心,例如上面的例子

    儿子1号通过EventBus触发update事件:selected='儿子1号',

    与此同时,儿子2号一直在监听update事件,他就知道了,现在selected === ‘儿子一号’(子组件之前的传递),

    与此同时,爷爷也在监听update事件(父子传递),

    与此同时,孙子也在监听update事件(父子传递),

  3. EventBus 永远是 让爸爸更新自己,永远不要自己更新自己,让爸爸操控所有的事情,参考collapse组件的eventBus。

10.vue 里面绑定 class

推荐使用对象加数组的形式。

计算属性里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
computed:{
itemclass(){
return `position-${this.position}`
}
}
computed:{
someClasses(){
return [`position-${this.position}`,`text-${this.text}`]
}
}
computed:{
someClasses(){
return {[`position-${this.position}`]:true,[`text-${this.text}`]:true}
}
}

template里直接放进 :class 里就行。

11.组件的递归

这个需求是做多级联动选择是出现的,因为你无法判断用户提供给你的数组的深度,有可能是5层,也有可能是6层,这时候就用到了组建的递归。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
{{source.item}}
<cascader v-if='source.children'
v-for='item in source.children'
:source='item'>
</cascader>
</div>
</template>
<script>
export default{
name:'cascader',
props:{
source:{
type:Object
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// source 格式 这里是三层 省-市-区
source:[
{
name:'山东',
children:[
{
name:'济南',
children:[
{
name:'市中区'
},
{
name:'历下区'
}
]
},
{
name:'淄博',
children:[
{
name:'张店区'
},
{
name:'临淄区'
}
]
}
]
}
]

注意:该组件 template 中使用的标签名要和 name 相同,这样 Vue 才能知道这是一个递归组件。

该组件,接收一个类型为 Object 的 source 数据,展示 source.item 。同时在组件里面继续调用自己,并进行三个操作。

1. 判断是否还有下一层 children。
   2. 循环下一层。
   3. 将下一层的数据绑定到source。

这样就形成了组件的复用。

12.轮子中写函数(名字没想好,瞎起的)

在制作级联组件的时候遇到一个需求,就是用户需要ajax传输信息,用户获取了数据,怎么能放到组件上应用呢?这个就是需要轮子制作者做的了。

用户使用时需要做的事

  1. 定义一个函数,获取数据后通过 callback 调用
  2. 在标签上绑定定义的函数,传给子组件,传过去的值就是一个 Function
1
2
3
<template>
<div :load-data='loadData'></div>
</template>
1
2
3
4
5
6
7
methods:{
loadData(callback){
axios.get('xxx/data.php',(res)=>{
callback(res)
})
}
}

我需要做的事情

  1. 定义好 props
  2. 定义好 callback 函数
  3. 调用 this.loadData(callback)
1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
props:{
loadData:{
type:Function
}
},
methods:{
resovleData(){
let callback = (result)=>{return result+'some'};
this.loadData(callback)
}
}
}

回调 : 把别人给我的函数调用一下。

总结:其实就是用户再前面写好获取数据的方法,然后把这个方法传到子组件,这样我们就能在子组件中获取到用户的数据,然后处理数据。