1.组件化思想
将一个完整的页面分成很多个组件,每个组件都用于实现页面的一个功能块。而每一个组件又可以进行细分,整个页面都将变得容易管理维护。
组件化是Vue.js中的重要思想
它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
任何的应用都会被抽象成一颗组件树。
组件化思想的应用:
有了组件化的思想,我们在之后的开发中就要充分的利用它。
尽可能的将页面拆分成一个个小的、可复用的组件。
这样让我们的代码更加方便组织和管理,并且扩展性也更强。
2.注册组件
2.1注册组件的基本步骤
组件的使用分成三个步骤:
- 创建组件构造器
- 注册组件
- 使用组件。
2.2注册组件步骤解析
1.Vue.extend():
调用Vue.extend()创建的是一个组件构造器。
通常在创建组件构造器时,传入template代表我们自定义组件的模板。
该模板就是在使用到组件的地方,要显示的HTML代码。
事实上,这种写法现在几乎已经看不到了,它会直接使用语法糖。
2.Vue.component():
调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
3.组件必须挂载在某个Vue实例下,否则它不会生效。
如上图,实际使用了两次,而第二次其实并没有生效
2.3组件的其他补充
**
1.全局组件和局部组件
**
- 调用Vue.component()注册组件时,组件的注册是全局的 这意味着该组件可以在任意Vue示例下使用。
- 如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
**
2.父组件和子组件
**
组件和组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系
3.注册组件语法糖
注册组件的方式,有些繁琐。Vue为了简化这个过程,提供了注册的语法糖。主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
4.模板的分离写法
将其中的HTML分离出来写,然后挂载到对应的组件上,使结构变得非常清晰
Vue提供了两种方案来定义HTML模块内容:
1.使用<script>标签
<!--1.script标签, 注意:类型必须是text/x-template-->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈</p>
</div>
</script>
2.使用<script>标签
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,呵呵呵</p>
</div>
</template>
3.父子组件的通信
如何进行父子组件间的通信呢?
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
3.1父级向子级传递 props
在组件中,使用选项props来声明需要从父级接收到的数据。
props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称。
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
当需要对props进行类型等验证时,就需要对象写法了。
验证支持的据类型:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 当我们有自定义构造函数时,验证也支持自定义的类型
<div id="app">
<cpn :c-info="info" :child-my-message="message" v-bind:class></cpn>
</div>
<template id="cpn">
<div>
<h2>{{cInfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {}
}
},
childMyMessage: {
type: String,
default: ''
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: '妍华',
age: 21,
height: 1.66
},
message: 'aaaaaa'
},
components: {
cpn
}
})
</script>
3.2子级向父级传递 自定义事件
自定义事件的流程:
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。
3.3父子组件的访问方式
1.$children
父组件访问子组件:使用$children或$refs
子组件访问父组件:使用$parent
$children的访问
this.$children是一个数组类型,它包含所有子组件对象。
我们这里通过一个遍历,取出所有子组件的message状态。
$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
2.$refs
$refs的使用:
$refs和ref指令通常是一起使用的。
首先,我们通过ref给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID就可以访问到该组件了。
3.$parents
在子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
4.插槽 v-slot
官方文件:https://cn.vuejs.org/v2/guide/components-slots.html
特别注意:在 2.6.0 中,官方为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC。
v-slot
:后边是插槽名称=后边是组件内部绑定作用域值的映射。
4.1编译作用域
官方给出了一条准则:
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
组件的插槽:
- 组件的插槽也是为了让我们封装的组件更加具有扩展性。
- 让使用者可以决定组件内部的一些内容到底展示什么。
举例:移动网站中的导航栏
- 移动开发中,几乎每个页面都有导航栏。
- 导航栏必然会封装成一个插件,比如nav-bar组件。
- 一旦有了这个组件,我们就可以在多个页面中复用了。
以京东为例,每个页面的导航并不全然相同
如何封装呢?抽取共性,保留不同。
- 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
- 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
- 是搜索框,还是文字,还是菜单。由调用者自己来决定
4.2 具名插槽
在指定的位置输出我们的子元素,这时候具名插槽就排上了用场。该插槽插入什么内容取决于父组件如何使用
在其他组件中使用子组件
<child>
<template v-slot:slotName>
hello world
</template>
</child>
子组件
<div>
<slot name="slotName"></slot>
</div>
上面是具名插槽的用法,省略插槽名:name属性
或插槽名为default
表示匿名插槽
4.3插槽作用域
用法一:常规用法,slotProps为自定义作用域名
<child>
<template v-slot:slotName=“slotProps”>
hello world
<span>{{ slotProps.user.firstName }}</span>
</template>
</child>
用法二:解构插槽Prop,实际上就是ES6解构JSON,可以使用:替换参数名
<child>
<template v-slot:slotName=“{ user, age:nianling }”>
hello world
<span>{{ user.firstName }}</span>
<span>{{ nianling }}</span>
</template>
</child>
child组件,定义时将需要使用的作用域数据绑定在<slot>
即可,:user="user" :age="age"
<template>
<div>
<slot name="slotName" :user="user" :age="age"></slot>
</div>
</template>
<script>
export default {
data () {
return {
user: {
firstName: 'Cliff',
lastName: 'Rhine'
},
age: 24
}
}
}
</script>
作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰,其实最好的理解就是:
父组件替换插槽的标签,但是内容由子组件来提供。
在上面这个例子中:
在父组件使用我们的子组件时,从子组件中拿到数据:
- 我们通过获取到slotProps属性
- 在通过slotProps.data就可以获取到刚才我们传入的data了
(注:部分图片来源见水印 微博:coderwhy)