# vue 的组件

组件:一段特定功能的可以复用的代码块。

页面:只是模块(组件)的一个容器。

组件的好处:模块化、把耦合度降低,可以真正实现像搭积木那样写页面。

1
2
3
4
5
6
//使用组件的好处:
1、提高开发效率
2、方便重复使用
3、简化调试步骤
4、提升项目可维护性
5、便于协同开发

vue 的乞丐版的组件分两部分:(今天只要知道怎么写即可)

  1. 全局组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //在JS中定义组件(定义函数)
    Vue.component("组件名",{
    template:`<div>组件代码</div>`
    });
    //组件名本质上就是一个自定义的标签
    Vue.component('my-div',{
    template:`<div>这是我的第一个组件</div>`;
    })
    //使用该组件,在vue所控制的视图范围内使用
    <div id="app">
    //当做普通标签那样使用它
    <my-div></my-div>
    </div>
  2. 局部组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    new Vue({
    ...
    //局部组件在vue的实例中写,第6个葫芦娃
    components:{
    //这里写定义组件和内容
    "组件名":{
    //在这里使用template存放组件内容
    template:`
    <div>
    组件内容,可以写任意的HTML或JS或CSS代码
    </div>
    `
    }
    }
    });
    //调用跟全局组件一样,但是它的作用域只能在定义时的视图中使用
    <div id="app">
    <组件名></组件名>
    </div>

3 个注意事项:

  1. 不能使用已有标签来命名,比如不能使用 nav 来命名

  2. 组件的根元素只能有一个

  3. 注册时只能使用烤串命名法,但是使用时烤串、驼峰都可以使用

# 组件的通信问题

为什么存在该问题:vue 的组件是一个可复用的实例,是独立的模块,会形成封闭的空间,有自己的一套小宇宙(拥有 new vue 那样的配置对象)。类似于函数作用域一样,组件之间的数据互相不共享。

通信:传递数据。A 组件与 B 组件之间如何传递或共享数据。

回顾我们之前遇到的通信问题:

  1. 后端与前端

    a. 通过表单,get, 通过 url 传参到后台,后台通过 GET 方式接收

    b. ajax

  2. 页面间的通信问题 (前端)
    本地离线存储,同一个域名下,A 页面存数据,可以在 B 页面取数据

# 父组件–> 子组件

官网:https://cn.vuejs.org/v2/guide/components.html

组件实例的作用域是孤立的,不能在子组件直接用父组件的数据。

可以在组件上使用自定义属性绑定数据,在组件中需要显式的用 props 声明自定义属性名。

说明:
a、最外层的 APP,即 vue 控制的顶层结构为父组件,而自定义标签为子组件。
b、组件嵌套组件还是子组件。
c、作用域是孤立的,也就是说在自定义标签内部是拿不到全局的变量的值的。

父级 -> 子级通信(传值)步骤:

  1. 在组件所代表的自定义标签中写自定义属性(该属性的值就是你要传到子组件的值)

  2. 在组件定义的地方,使用 props 属性来接收传过来的值,
    格式:

    1
    props:["自定义属性名1","自定义属性名2",...]
  3. 像普通 data 属性那样使用传进来的数据 (即自定义属性)

关于 props 属性的概念:https://cn.vuejs.org/v2/guide/components-props.html

# 子组件–> 父组件

需要用到自定义事件,父组件用on监听自定义事件,on监听自定义事件,emit(触发,发出的意思)触发父组件所关系的自定义事件。

类似于订阅发布模式。比如 jquery 中也可以使用自定义事件,使用 trigger 来触发该事件。例子:jquery.html

注意:组件里面的 this 指的不再是 vue 实例,而是该组件的实例

需求:将子组件的比如搜索建议传递给父组件的输入框

子级 —> 父级步骤:

  1. 在子级中使用函数获取本身的数据

  2. 在该函数中通过订阅发布函数(自定义事件)

    1
    this.$emit("自定义事件名",要传给父级的数据1,要传给父级的数据2,...)

    通知给父级

  3. 在父级中子级所代表的自定义标签这里通过调用自定义事件名来接收数据

    1
    <son @自定义事件名="事件处理函数"></son>
  4. 在父级的 methods 中定义事件处理函数处理传过来的数据

# 组件中的 data 问题

结论:组件中的 data 必须是函数,否则会导致数据共享,而不是独立的数据

1
2
3
4
5
6
//语法:
data:function(){
return{
//这里写需要用于组件中的属性名和属性值
}
}

每个组件都是相互独立的,如果它们共用一个对象,在更改一个组件数据的时候会影响其他组件。如果是函数的话,每个组件都有自己独立的数据,相互之间不会影响。

# 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>仿select</title>
<link rel="stylesheet" type="text/css" href="./style.css">
<script src="vue.v2.6.12.js"></script>
</head>

<body>
<section class="warp" id="app">
<my-search tit="城市列表选择" btn="搜城市" :list="citylist" bgc="bgc1" ph="请输入城市名称"></my-search>
<my-search tit="年份列表选择" btn="搜年份" :list="yearlist" bgc="bgc2" ph="请输入年份名称"></my-search>
<my-search tit="手机品牌选择" btn="搜品牌" :list="phonelist" bgc="bgc3" ph="请输入手机品牌"></my-search>
</section>

<script>
Vue.component("my-search", {
props:["tit","btn","list","bgc","ph"],
data(){
return{
flag:false,
ipt:""
}
},
methods:{
getData(i){
this.ipt=i
}
},
template: `<div class="my-search">
<h3>{{tit}}</h3>
<div class="searchIpt clearFix" :class="bgc">
<div class="clearFix">
<input type="text" class="keyWord" value="" v-model="ipt" @click="flag=!flag" :placeholder="ph"/>
<input type="button" :value="btn">
<span></span>
</div>
<my-list v-show="flag" :mylist="list" @myevent="getData"></my-list>
</div></div>`
})
Vue.component("my-list", {
props:["mylist"],
methods:{
tofa(item){
this.$emit("myevent",item)
}
},
template: `<ul class="list">
<li v-for="item in mylist" @click="tofa(item)">{{item}}</li>
</ul>`
})
let vm = new Vue({
el: "#app",
data: {
citylist:["北京","上海","长沙"],
yearlist:["18-08-17","18-08-18","18-08-19"],
phonelist:["苹果","华为","小米"],
}
})
//原生JS写的交互效果,写vue时删除
// var keyWord = document.querySelector('.keyWord');
// var list = document.querySelector('.list');
// var lis = list.querySelectorAll('li');
// keyWord.onfocus = function () {
// list.style.display = 'block';
// }
// keyWord.onblur = function () {
// setTimeout(function () {
// list.style.display = 'none';
// }, 80);
// }
// for (var i = 0; i < lis.length; i++) {
// lis[i].onclick = function () {
// for (var i = 0; i < lis.length; i++) {
// lis[i].className = '';
// }
// this.className = 'active';
// console.log(this.innerHTML);
// keyWord.value = this.innerHTML;
// }
// }
</script>
</body>

</html>