Vue 笔记 2 - Vue组件

Vue 笔记 2 - 组件

组件是可复用的 Vue 实例,且带有一个名字。

非单文件组件

一个文件中包含多个组件。
Vue中使用组件的三大步骤:

  1. 定义组件
  2. 注册组件
  3. 使用组件(写组件标签)

1. 如何定义一个组件

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,
区别如下:

  1. el不要写,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器;
  2. data必须写成函数,为什么? – 避免组件被复用时,数据存在引用关系。
    备注:使用template可以配置组件结构

2. 如何注册组件

  1. 局部注册:new Vue的时候传入components选项
  2. 全局注册:Vue.component(‘组件名’,组件)

3. 使用组件

<component></component>

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<div id="root">
<h2>{{msg}}</h2>
<!-- <xuexiao></xuexiao> -->
<school></school>
<hr>
<!-- <xuesheng></xuesheng> -->
<!-- <xuesheng></xuesheng> -->
<!-- <xuesheng></xuesheng> -->
<student></student>
<student></student>
<hello></hello>
</div>

<hr>

<div id="root2">
<hello></hello>
</div>
<hr>

<script>
Vue.config.productionTip = false

// 1. 创建school组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">显示学校名称</button>
</div>
`,
// 组件不要写el配置项
data() {
return {
schoolName: '极客堂',
address: '北京'
}
},
methods: {
showName(){
console.log(this.schoolName)
}
}
})

// 1. 创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
// 组件不要写el配置项
data() {
return {
studentName: '月白',
age: 18
}
}
})

const hello = Vue.extend({
template: `
<div>
<h2>Hello {{name}}</h2>
</div>
`,
data(){
return {
name: 'Component'
}
}
})

// 全局注册组件
Vue.component('hello', hello)
// 创建vm
new Vue({
el: '#root',
data: {
msg: "组件基础"
},
// 2. 注册组件(非简写形式)
// components: {
// xuexiao: school,
// xuesheng: student
// },
// 2. 注册组件(简写形式)
components: {
school,
student,
// hello // 因为全局已经注册过hello组件,所以这里不用注册。
}
})

new Vue({
el: '#root2',
data: {
msg: "root2"
},
components: {
// hello
}
})
</script>

几个注意点

  • 1.关于组件名

组件的嵌套

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
<div id="root">
</div>

<script>
Vue.config.productionTip = false

// 1. 创建student组件
const student = Vue.extend({
name: 'student',
template:`
<div>
<h2>学生名称:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data() {
return {
name: '月白',
age: 18
}
}
})

// 1. 创建school组件
const school = Vue.extend({
name: 'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">显示学校名称</button>
<student></student>
</div>
`,
data() {
return {
name: '极客堂',
address: '北京'
}
},
methods: {
showName(){
console.log(this.name)
}
},
components: {
student
}
})

const hello = Vue.extend({
name: 'hello',
template: `
<div>
<h2>Hello {{name}}</h2>
</div>
`,
data(){
return {
name: 'Component'
}
}
})
const app = Vue.extend({
name: 'app',
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components: {
school,
hello
}
})
// 创建vm
new Vue({
el: '#root',
template: `<app></app>`
data: {
msg: "组件基础"
},
components: {
app
}
})
</script>

单文件组件

一个文件中只有一个组件。

VueCLI

安装

1
npm install -g @vue/cli

切换到想要创建项目的目录,创建项目

1
vue create xxxx

启动项目

1
2
3
4
5
cd project_dir
yarn serve
或者
npm run serve

安装插件

1
2
npm i less-loader@7

关于脚手架

  1. vue.js 与 vue.runtime.xxx.js 的区别:
    • vue.js 是完整版的Vue,包含:核心功能+ 模板解析器。
    • vue.runtime.xxx.js 是运行版的Vue,只包含核心功能,没有模板解析器。
  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数取指定具体内容。
  3. Vue默认隐藏了WebPack的默认配置,可以使用vue inspect > output.js 来导出默认配置,需要将配置文件命名为vue.config.js放到项目根目录下(与src目录同级)。

配置项props

功能: 让组件接收外部传过来的数据

  1. 传递数据:

    1
    <Demo name="xxx"/>
  2. 接收数据:

    • 第一种方式(只接收):props: ['name']
    • 第二种方式(限制类型):
1
2
3
4
props:{
name: String,
age : Number
}
  • 第三种方式(限制类型、必要性、默认值):
1
2
3
4
5
6
7
props:{
name: {
type: Number,
required: true,
default: 99
}
}

注意: props是只读的,Vue底层会检测你对props的修改,如果进行了修改,就会发出警告,
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

mixin(混入)

功能:可以吧多个组件共用的配置提取成一个混入对象。
使用方式:

  1. 定义:

    1
    2
    3
    4
    {
    data(){...},
    methods(){...}
    }
  2. 使用:

    • 全局混入: Vue.mixin(xxx)
    • 局部混入: mixins:[‘xxx’]

插件

功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是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
export default {
install(Vue) {
// console.log('@@@ my install', Vue)
// 全局过滤器
Vue.filter('myGlobalSlice', function (value) {
return value.slice(0, 4)
})

// 全局自定义指令
Vue.directive('fbind', {
bind(element, binding){
element.value = binding.value
},
// inserted(element, binding){
// element.focus()
// },
// update(element, binding){
// element.value = binding.value
// }
})

// 全局Mixin
Vue.mixin({
data(){
return {
x:100,
y:200
}
}
})

// 给Vue原型上添加一个方法(vm和vc就都可以使用了)
Vue.prototype.hello = () => {alert('Greetings from plugins')}
}
}

scoped样式

作用:让样式拒不生效,防止冲突

写法:<style scoped>

组件自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件
  2. 使用场景: A是父组件,B是子组件,B想给A传递数据,那么就要在A中给B绑定自定义事件(事件的回调函数在A中)
  3. 父组件通过v-on:eventname="fn"或者@eventname="fn"绑定自定义事件,并在methods中添加事件方法
1
2
3
4
5
6
7
<Student v-on:geekhall="getStudentName"></Student>
// ...
methods:{
getStudentName(name){
console.log('App收到了学生名:', name)
}
}
  1. 在子组件中通过this.$emit('eventname')来触发自定义事件
1
2
3
4
5
6
methods: {
sendStudentName(){
// 触发Student组件实例身上的geekhall自定义事件
this.$emit('geekhall', this.name)
}
}
  1. 解绑定自定义事件this.$off('eventname')
  2. 组件上也可以使用原生DOM事件,需要使用native修饰符。
  3. 注意:通过this.$refs.xxx.$on('eventname', 回调函数) 绑定自定义事件时,回调要么配置在metiods中,要么使用箭头函数,否则this指向会出问题。

全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信;

  2. 安装全局事件总线:

    1
    2
    3
    4
    5
    6
    7
    new Vue({
    // ....
    beforeCreate() {
    Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vm
    }
    // ....
    })
  3. 使用事件总线:

    • 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
    1
    2
    3
    4
    5
    6
    7
    methods(){
    demo(){....}
    }
    // ....
    mounted(){
    this.$bus.$on('xxxx', this.demo)
    }
    • 提供数据: this.$bus.$emit('xxxx', 数据)
  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

消息订阅与发布

一种组件间通信的方式,适用于任意组件间通信

使用步骤:

  1. 安装pubsub: npm i pubsub-js

  2. 引入: import pubsub from 'pubsub-js'

  3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

    1
    2
    3
    4
    5
    6
    7
    methods(){
    demo(data){....}
    }
    ...
    mounted(){
    this.pid = pubsub.publish('xxx',数据)
    }
  4. 提供数据:pubsub.publish('xxx', 数据)

  5. 最好在beforeDestroy钩子中,使用pubsub.unsubscribe(pid)来取消订阅。

nextTIck回调

  1. 语法: this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调。
  3. 什么时候使用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

动画

  1. 进入的样式

    • v-enter: 进入的起点
    • v-enter-active:进入的过程中
    • v-enter-to: 进入的终点
  2. 离开的样式:

    • v-leave: 离开的起点
    • v-leave-active: 离开的过程中
    • v-leave-to: 离开的终点
  3. 使用<transition>包裹要过度的元素,并配置name属性:

    1
    2
    3
    <transition name="hello">
    <h1 v-show="isShow">Animate.css</h1>
    </transition>
  4. 若有多个元素需要过度,则需要使用:<transition-group>,并且每个元素都需要指定key值