组件
-
组件化的基本使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 3.组件的基本使用--> <my-cpn></my-cpn> </div> <script src="../js/vue.js"></script> <script> //1.创建组件构造器对象 const cpnConstructor = Vue.extend({ template: `<div> <h2>我是标题</h2> <p>我是内容,哈哈哈哈</p> </div>` }); //2.注册组件 Vue.component('my-cpn',cpnConstructor); const app = new Vue({ el: '#app', data: { message: '你好' } }) </script> </body> </html>
这里的步骤都代表什么含义呢?1.Vue.extend() :调用 Vue.extend () 创建的是一个组件构造器。通常在创建组件构造器时,传入 template 代表我们自定义组件的模板。该模板就是在使用到组件的地方,要显示的 HTML 代码。事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。2.Vue.component() :调用 Vue.component () 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数: 1 、注册组件的标签名 2 、组件构造器3. 组件必须挂载在某个 Vue 实例下,否则它不会生效。我们来看下面我使用了三次 <my- cpn ></my- cpn >而第三次其实并没有生效:
-
全局组件和局部组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <dada></dada> </div> <div id="app2"> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> //1.创建组件构造器 const cpn = Vue.extend({ template: `<div> <h2>你好呀!H2</h2> <div>我是div</div> </div>` }) //2.注册组件(全局组件) //Vue.component('cpn',cpn); //疑问:怎么注册的组件才是局部组件? const app = new Vue({ el: '#app', data: { message: '你好' }, //局部组件 components: { // 前者cpn:使用组件时的标签名 cpn: cpn } }) const app2 = new Vue({ el: '#app2', data: { message: '你好' } }) </script> </body> </html>
-
父组件和子组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> { {message}} <cpn2></cpn2> </div> <script src="../js/vue.js"></script> <script> //1.创建第一个组件构造器(子组件) const cpnC1 = Vue.extend({ template: ` <div> <h2>我是标题11</h2> <div>我是内容呵呵呵呵</div> </div> ` }) //2.创建第二个组件构造器(父组件) const cpnC2 = Vue.extend({ template: ` <div> <h2>我是标题22</h2> <div>我是内容呵呵呵呵22222</div> <cpn1></cpn1> </div> `, //在父组件中注册子组件 components: { cpn1: cpnC1 } }) //root组件 const app = new Vue({ el: '#app', data: { message: '你好' }, components: { cpn2: cpnC2 } }) </script> </body> </html>
-
组件的语法糖注册方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册组件语法糖</title> </head> <body> <div id="app"> <my-cpn></my-cpn> <my-cpn-ju></my-cpn-ju> </div> <script src="../js/vue.js"></script> <script> //全局注册组件 Vue.component("my-cpn", { template: `<div>hello,my-cpn</div>` }) const app = new Vue({ el: '#app', data: { message: '你好' }, //局部注册组件 components: { "my-cpn-ju": { template: `<div>hello,my-cpn-ju</div>` } } }) </script> </body> </html>
-
组件模板分离写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册组件语法糖</title> </head> <body> <div id="app"> <cpn></cpn> </div> <!--1.script标签,注意:类型必须是text/x-template--> <!--<script type="text/x-template" id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,哈哈哈</p> </div> </script>--> <!--2.template标签--> <template id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,template标签</p> </div> </template> <script src="../js/vue.js"></script> <script> //全局注册组件 Vue.component("cpn", { template: `#cpn` }) const app = new Vue({ el: '#app', data: { message: '你好' } }) </script> </body> </html>
-
组件中数据存放问题
组件不可以访问Vue实例数据!
组件自己的数据存放在哪里呢?
组件对象也有一个data属性(也可以有methods等属性,下面我们有用到),只是这个data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据
为什么data在组件中必须是一个函数呢?
首先,如果不是一个函数,Vue直接就会报错。其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册组件语法糖</title> </head> <body> <div id="app"> <cpn></cpn> </div> <!--1.script标签,注意:类型必须是text/x-template--> <!--<script type="text/x-template" id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,哈哈哈</p> </div> </script>--> <!--2.template标签--> <template id="cpn"> <div> <h2>{ {title}}</h2> <p>我是内容,template标签</p> </div> </template> <script src="../js/vue.js"></script> <script> //全局注册组件 Vue.component("cpn", { template: `#cpn`, data() { return { title: 'hello' } } }) const app = new Vue({ el: '#app', data: { message: '你好', title: '我是标题' } }) </script> </body> </html>
-
组件中的data为什么是函数
首先,如果不是一个函数,Vue直接就会报错。
其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--组件实例对象--> <div id="app"> <cpn></cpn> </div> <template id="cpn"> <div> <h2>当前计数:{ {counter}}</h2> <button @click="increment">+</button> <button @click="decrement">-</button> </div> </template> <script src="../js/vue.js"></script> <script> //1.注册组件 Vue.component(`cpn`,{ template: `#cpn`, data() { return { counter: 0 } }, methods: { increment(){ this.counter++; }, decrement(){ this.counter--; } } }) const app = new Vue({ el: '#app', data: { message : '你好' } }) </script> <script> // const obj = { // name: 'why', // age: 18 // } // // function abc() { // return obj; // } // // let obj1 = abc(); // let obj2 = abc(); // let obj3 = abc(); // // obj1.name = 'kobe'; // console.log(obj2); // console.log(obj3); </script> </body> </html>
-
父子组件的通信
注意:子组件是不能引用父组件或者Vue实例的数据的
如何进行父子组件间的通信呢?Vue官方提到
1.通过props向子组件传递数据
2.通过事件向父组件发送消息
- 父级向子级传递(通过props传递)
props的值有两种方式:方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
方式一:字符串数组,数组中的字符串就是传递时的名称。
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn v-bind:cmovies="movies" :cmessage="message"></cpn> </div> <template id="cpn"> <div> <ul> <li v-for="(item,index) in cmovies"> { {index+1}}.{ {item}} </li> </ul> <h2>{ {cmessage}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> //父传子:props const cpn = { template: `#cpn`, //props: ['cmovies','cmessage'], props: { //验证所支持的数据类型:String Number Boolean Array Object Date Function Symbol //1.类型限制 // cmovies: Array, // cmessage: String //2.提供一些默认值,以及必传值 cmessage: { type: String, //类型 default: 'aaaaaa', //默认值 required: true //必传 }, //类型是对象或者数组时,默认值必须是一个函数 cmovies: { type: Array, default() { return ['黑马','传智'] } } }, data() { return { } }, methods: { } } //root组件 const app = new Vue({ el: '#app', data: { message: '你好', movies: ['海王','哪吒'] }, components: { //增强写法 cpn } }) </script> </body> </html>
- 子级向父级传递
自定义事件的流程:
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<!--
事件对象参数 event 的处理。不加括号时,函数第一个参数为 event,加了括号后,需要手动传入 $event 才能获得事件对象。
-->
<div id="app">
<cpn @item-click="cpnClick" ></cpn>
<!--<cpn @item-click="cpnClick($event)" ></cpn>-->
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories"
@click="btnClick(item)">
{
{item.name}}
</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//子组件
const cpn = {
template: `#cpn`,
data() {
return {
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'},
{id: 'ddd', name: '电脑办公'}
]
}
},
methods: {
btnClick(item) {
// 发射事件
this.$emit('item-click',item);
}
}
}
//父组件
const app = new Vue({
el: '#app',
data: {
message: 'Hello,Panghl',
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log('itemClick',item);
console.log(item.id);
}
}
})
</script>
</body>
</html>
9.父子组件的访问方式
父组件访问子组件 : 使用$children或 $refs preference(引用)
子组件访问父组件:使用$parent
$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
$refs的使用:
$refs和ref指令通常是一起使用的。
首先,我们通过ref给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID就可以访问到该组件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$children $refs</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好'
},
methods: {
btnClick() {
//1.$children
// console.log(this.$children);
// for (let c of this.$children){
// console.log(c.name);
// c.showMessage()
// }
//2.$refs => 对象类型,默认时一个空的对象 ref='bbb'
console.log(this.$refs.aaa.name);
this.$refs.aaa.showMessage();
}
},
components: {
cpn: {
template: `#cpn`,
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
}
}
}
})
</script>
</body>
</html>
子组件访问父组件的访问方式: $parent
如果我们想在子组件中直接访问父组件,可以通过
$parent
注意事项:
尽管在
Vue
开发中,我们允许通过
$parent
来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过
$parent
直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$children $refs</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是孙子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好'
},
components: {
cpn: {
template: `#cpn`,
data(){
return{
name: '我是子组件的name'
}
},
components: {
ccpn: {
template: `#ccpn`,
methods: {
btnClick() {
//1.访问父组件$parent
console.log(this.$parent.$parent.message);
console.log(this.$parent.name);
//2.访问根组件 $root
console.log(this.$root);
}
},
}
}
}
}
})
</script>
</body>
</html>
组件化高级
-
slot-插槽的基本使用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 1.插槽的基本使用 <slot></slot> 2.插槽的默认值 <slot>button</slot> 3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素 --> <div id="app"> <cpn></cpn> <cpn><div>你好啊 <p>nnihao</p></div></cpn> </div> <template id="cpn"> <div> <h2>我是组件</h2> <p>我是组件,哈哈哈</p> <slot><button>按钮</button></slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message : '你好' }, components: { cpn: { template: `#cpn` } } }) </script> </body> </html>
-
slot-具名插槽的使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn><span slot="mid">标题</span></cpn> <cpn><button slot="left">左边按钮</button></cpn> <cpn><button slot="right">返回</button></cpn> </div> <template id="cpn"> <div> <slot name="left">左边</slot> <slot name="mid">中间</slot> <slot name="right">右边</slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message : '你好' }, components: { cpn: { template: `#cpn` } } }) </script> </body> </html>
-
什么是编译的作用域
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn v-show="isShow"></cpn> <button v-show="isShow">按钮</button> </div> <template id="cpn"> <div> <h2>hello,p</h2> <p>我是内容,哈哈哈</p> <button v-show="isShow">组件按钮</button> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好', isShow: true }, components: { cpn: { template: `#cpn`, data(){ return { isShow: false } } } } }) </script> </body> </html>
-
作用域插槽的案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn>
<!--目的是获取子组件中的pLanguage-->
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{
{item}}-</span>-->
<span>{
{slot.data.join(' - ')}}</span>
</template>
</cpn>
<cpn>
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{
{item}} * </span>-->
<span>{
{slot.data.join(' * ')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguage">
<ul>
<li v-for="item in pLanguage">{
{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好'
},
components: {
cpn: {
template: `#cpn`,
data() {
return {
pLanguage: ['JAVA', 'C++', 'C#', 'C', 'Javascript', 'go']
}
}
}
}
})
</script>
</body>
</html>