组件的注册
粗略地说,一个vue的组件是由一堆标签所组成的
细致一些的话,除了标签,组件也有属于自己的东西,比如说:数据、方法
在此之前,我们需要知道,组件分为两种
- 根组件:通过new Vue() 来创建的 通常应用中只有一个,特殊情况可以有两个或多个
- 可复用性组件:通过Vue.component()来创建,分为 全局/局部组件
根组件其实就是我们实例化的vue
let app = new Vue({})
可复用性组件就是
Vue.component("组件名", {组件选项})
注意点:
- 组件名称遵循自定义组件命名规范:全小写、连字符(虽然驼峰式一般也没问题)
- 组件选项与
new Vue
选项配置基本一致(也有一些细节的不同)
我们来看一下全局组件怎么创建
Vue.component("b-div", {
template: ` <div>
<div>恕瑞玛</div>
<div>你们的皇帝回来了</div>
</div>`,
})
let app = new Vue({
el: "#app",
})
注意点 全局组件,要写在实例化之前
局部组件
let app = new Vue({
el: "#app",
components: {
"b-div": {
template: ` <div>
<div>恕瑞玛</div>
<cc-div></cc-div>
<div>你们的皇帝回来了</div>
</div>`,
}
}
})
简单来看下 全局与局部 的书写区别
- 全局
Vue.component('组件名称', {组件选项})
- 局部
new Vue({
...,
components: {
'组件名称': {组件选项}
}
})
只有一个根组件时,全局组件和局部组件没有区别,但是有多个组件时,全局和局部组件就有了区别
使用组件的话,直接在挂载点写你自定义的名称。
<div id="app">
<b-div></b-div>
</div>
下边我们来看一下怎么在组件里定义下一级
Vue.component("b-div", {
template: ` <div>
<div>恕瑞玛</div>
<div>你们的皇帝回来了</div>
</div>`,
components: {
// 组件名称:组件选项
"cc-div": {
template: `<p>我是全局注册的组件里的局部注册组件</p>`
}
}
})
let app = new Vue({
el: "#app",
})
调用时,我们需要在上一级的模板里调用
如果去根组件的话,就违反了它调用的要求
Vue.component("b-div", {
template: ` <div>
<div>恕瑞玛</div>
<cc-div></cc-div>
<div>你们的皇帝回来了</div>
</div>`,
components: {
// 组件名称:组件选项
"cc-div": {
template: `<p>我是全局注册的组件里的局部注册组件</p>`
}
}
})
let app = new Vue({
el: "#app",
})
组件的data
上边我们说了组件的模板,其实组件除了没有根组件的el 其他的都有,他和用户产生交互后就会产生事件和数据
data的写法
Vue.component("vc-div",{
template:` <div>噗嗤</div>
<div>你笑我?</div>`,
data(){
return{
name:"儿子"
}
}
})
这里的data要写成一个方法,并且要return一个对象,这个对象才是你去写data的位置,
根组件里直接就是data:{}就可以了,但是这里需要写一个方法,方法的返回值里边去写数据。
为什么:说白了就是内存空间的问题,如果直接像根组件一样写,用的时候这块内存空间会和跟组件的内存空间会有一个共享问题!比如说重名之类的,vue是不允许这样的,因为根组件只有一个复用性组件有很多,如果根组件被随意修改,会造成数据混乱
所以我们这里采用一个函数的方式,接受一个返回值,函数的返回值就是一个新值,需要一个新的变量来接收
组件的传参
传参分为两种,父传子与子传父,我们先说父传子
我们先去把他们的父子关系通过代码展现出来
<body>
<div id="app">
<vc-div></vc-div>
</div>
<script>
Vue.component("vc-div",{
template:` <div>
<div> {{wa}} </div>
<div>你笑我?{{fuchuan}}</div>
</div>`,
})
let app = new Vue({
el: "#app",
})
</script>
</body>
这个代码内可以看出 id为app的是 父组件 vc-div为子组件
我们需要给子组件传参数
<div id="app">
<!-- 父传 使用v-bind绑定属性的方式-->
<vc-div :fuchuan="10"></vc-div>
</div>
id为app的div开始标签到结束标签都是父组件的作用域,所以我们在父传子接的时候直接在 vc-div 这个组件里绑定事件就可以了
子级接收
props:["fuchuan"],
子级接收的时候,我们需要props这个属性
props用来存储数据,数据的使用方式和data一致,在方法里直接通过this来调用,在模板里直接通过大胡子语法来调用
不过data是以对象来存储,而props是用数组来存储,且里边存储的是父传哪里的属性名
注意点 模板里 有且只能有一个根节点,换句话来说,你的所有内容需要在一个元素内包裹着,比如说都在一个div里
<body>
<div id="app">
<p v-text="wa"></p>
<!-- {{wa}} -->
<!-- 父传 使用v-bind绑定属性的方式-->
<vc-div :fuchuan="10"></vc-div>
</div>
<script>
Vue.component("vc-div",{
/*
子接 使用属性props
props: 使用存储数据
组件内部使用数据的使用方式和data一样,直接通过this调用
*/
props:["fuchuan"],
template:` <div>
<div> {{wa}} </div>
<div>你笑我?{{fuchuan}}</div>
</div>`,
data(){
return {
wa:"儿子"
}
}
})
let app = new Vue({
el: "#app",
data:{
wa:"哇"
},
methods: {
getName(){
return this.wa
}
},
})
</script>
</body>
浏览器反馈
通过页面反馈 我们可以知道,父子级的内存都是独立的,没有任何报错,也没有冲突,而且父组件还能给自组件传参,彼此独立 (data里即使名字相同也不会互相冲突),又相互连接 (通过参数 父级传递 子级接收,data处理,如果有多个的话,props里逗号间隔)
组件通信:
- 父->子
父级调用子元素,通过子组件的属性传入数据
子元素内部通过props配置项(数组),来接收对应的数据 - 子->父
注意:vue中的数据默认的单向流动,只能父到子直接传递,子到父不能直接修改
原因: 因为父级的数据,不一定只是某个子级使用,或许还有其他的子级也在使用
那么如果一个子级内部随意去修改了父级的数据,很容易导致数据混乱
我们通过代码学习子传父
<body>
<div id="app">
<vc-div></vc-div>
</div>
<script>
Vue.component("vc-div",{
data(){
return{
name:"小郭"
}
},
template:` <div>
<p>我老大</p>
<p>我老二</p>
</div>`,
methods: {
go(){
this.$emit("hello",this.name)
}
},
})
let app = new Vue({
el:"#app",
data:{
name:""
},
methods: {
fn(n){
// console.log("我是监听子组件的函数");
console.log(n)
this.name = n
}
},
})
</script>
</body>
</html>
浏览器反馈
如果我们现在需要去给父组件传递data里的name
data:{
name:""
},
传递完了之后在父组件里调用一下
<p> 我是子组件传递过来的{{name}} </p>
子传父顺序
- 在子组件触发一个事件
template:` <div>
<p>我老大</p>
<p>我老二</p>
<button @click="go">123123</button>
</div>`,
- 当满足某些触发条件时去监听一个事件这个事件触发时传入一个参数
methods: {
go(){
this.$emit("hello",this.name)
}
},
- 监听的事件触发时,接受一个参数,并且执行一个函数
<vc-div @hello="fn"></vc-div>
let app = new Vue({
el:"#app",
data:{
name:"我是实例化里的name"
},
methods: {
fn(n){
console.log("我是监听子组件的函数");
console.log(n)
this.name = n
}
},
})
这时我们的子传父就完成了,我们来理一下思路和流程
如果子级想修改数据:
- 子级执行 $emit() 来触发自定义事件
- 父级监听 自己出发的 自定义事件
- 监听到触发后 执行父级的回调函数
首先是我们要去在子组件内写一个事件来触发子传父,也就是我们按钮上边的点击事件,然后在这个点击事件触发时调用go()这个函数,go()会监听到hello这个事件,当hello被触发时,会把子组件内的this.name传递进去,然后同时调用fn这个函数,这个函数会输出一下接收到的值,并且把接收到的值,重新复制到自己data里的name里,然后我们就完成了子传父
子级在特定条件下,触发自定义事件
父级通过监听接收到通知后,自己决定是否 改变数据 / 如何改变