如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
但如果,我们将一个页面拆分成一个个的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
任何的应用都会被抽象成一棵组件树
<div id="app"> // 3. 使用组件 <my-cpn></my-cpn> </div> <script src="../js/vue.js"></script> <script> // 1. 创建组件构造器对象 const contConstructor = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容哈哈哈哈哈</p> <p>我是内容呵呵呵呵呵</p> </div>` }) // 2. 注册组件 // component有两个参数 1-组建的标签名 2-组件构造器 Vue.component('my-cpn', contConstructor) const app = new Vue({ el : "#app", data : { message : "你好", } }) </script>
全局组件,意味着可以在多个Vue的实例下面使用
上面的例子即是全局组件,验证:
<div id="app"> <my-cpn></my-cpn> <my-cpn></my-cpn> <my-cpn></my-cpn> </div> <div id="ppa"> <my-cpn></my-cpn> </div> <script src="../js/vue.js"></script> <script> // 1. 创建组件构造器对象 const contConstructor = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容哈哈哈哈哈</p> <p>我是内容呵呵呵呵呵</p> </div>` }) // 2. 注册组件 // component有两个参数 1-组建的标签名 2-组件构造器 Vue.component('my-cpn', contConstructor) const ppa = new Vue({ el : '#ppa' }) const app = new Vue({ el : "#app", data : { message : "你好", } }) </script>
局部组件:(开发最常用,而且一般只有一个Vue实例)
<script> // 1. 创建组件构造器对象 const contConstructor = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容哈哈哈哈哈</p> <p>我是内容呵呵呵呵呵</p> </div>` }) // 2. 注册组件 // component有两个参数 1-组建的标签名 2-组件构造器 // Vue.component('my-cpn', contConstructor) const ppa = new Vue({ el : '#ppa' }) const app = new Vue({ el : "#app", data : { message : "你好", }, components : { cpn : contConstructor } }) </script>
<div id="app"> <cpn2></cpn2> </div> <script src="../js/vue.js"></script> <script> // 子组件 const cpnC1 = Vue.extend({ template : ` <div> <h2>我是标题1</h2> <p>我是内容,哈哈哈</p> </div> ` }) // 父组件 const cpnC2 = Vue.extend({ template : ` <div> <h2>我是标题2</h2> <p>我是内容,呵呵呵</p> <cpn1></cpn1> </div> `, components : { cpn1 : cpnC1 } }) // root组件 const app = new Vue({ el : "#app", data : { message : "你好", }, components : { cpn2 : cpnC2 } }) </script>
<div id="app"> <cpn1></cpn1> <cpn2></cpn2> </div> <script src="../js/vue.js"></script> <script> // 全局组件语法糖 Vue.component('cpn1', { template : ` <div> <h2>我是标题1</h2> <p>我是内容,哈哈哈</p> </div> ` }) const app = new Vue({ el : "#app", data : { message : "你好", }, // 局部组件语法糖 components : { 'cpn2' : { template : ` <div> <h2>我是标题2</h2> <p>我是内容,呵呵呵</p> </div> ` } } }) </script>
<div id="app"> <cpn1></cpn1> <cpn2></cpn2> </div> <!--1. script标签--> <script type="text/x-template" id="cpn1"> <div> <h2>{{message}}1</h2> <p>我是内容,哈哈哈</p> </div> </script> <!--2.template标签--> <template id="cpn2"> <div> <h2>我是标题2</h2> <p>我是内容,呵呵呵</p> </div> </template> <script src="../js/vue.js"></script> <script> // 全局组件语法糖 Vue.component('cpn1', { template : `#cpn1` }) const app = new Vue({ el : "#app", data : { message : "你好", }, components : { 'cpn2' : { template : `#cpn2` } } }) </script>
组件是一个单独功能模块的封装:
组件中的数据是保存在哪里?顶层的Vue实例中吗?
对于组件中的data为什么是一个函数,是保证每个组件每次返回都是独立的对象
注:其实组件原型就是指向Vue对象,所以基本上Vue有的组件都有
<div id="app"> <cpn1></cpn1> <cpn2></cpn2> </div> <!--1. script标签--> <script type="text/x-template" id="cpn1"> <div> <h2>{{message1}}1</h2> <p>我是内容,哈哈哈</p> </div> </script> <!--2.template标签--> <template id="cpn2"> <div> <h2>{{message2}}</h2> <p>我是内容,呵呵呵</p> </div> </template> <script src="../js/vue.js"></script> <script> // 全局组件语法糖 Vue.component('cpn1', { template : `#cpn1`, data() { return { message1 : '我是标题1' } } }) const app = new Vue({ el : "#app", data : { message : "你好", }, components : { 'cpn2' : { template : `#cpn2`, data() { return { message2 : '我是标题2' } } } } }) </script>
子组件是不能引用父组件或者Vue实例的数据的。
但是,在开发中,往往一些数据需要从上层传递到下层
比如,在一个页面中,我们从服务器请求到了很多的数据。
其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件将数据传递给小组件。
如何进行父子组件间的通信?Vue官方提到:
在组件中,使用选项props来声明需要从父级接收到的数据。
props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
方式一:字符串数组
方式二:对象
一方面明明是一个对象,却传一个数组很奇怪,另一方面当需要对props进行类型等验证时,就需要对象写法了。
<div id="app"> <!--<cpn :cmovies="movies" :cmessage="message" ></cpn>--> <cpn :cmessage="message" ></cpn> </div> <template id="cpn"> <div> <p>{{cmessage}}</p> <ul> <li v-for="item in cmovies"> {{item}} </li> </ul> </div> </template> <script src="../js/vue.js"></script> <script> // 父组件传子组件 props const cpn = { template : `#cpn`, // 1-字符串类型 // props : ['cmovies', 'cmessage'] // 2-对象类型 props : { // 1. 类型限制 // cmovies : Array, cmessage : String // 2. 类型限制+默认值 cmovies : { type : Array, // default : [] 如果是Array类型,或Object类型,默认值必须给一个工厂方法 default() { return [] } }, cmessage : { type : String, default: '我不想看', required : true // 动态绑定处为必传项 } } } const app = new Vue({ el : "#app", data : { message : '我要去看', movies : ['海王', '海贼王', '海尔兄弟'] }, components : { cpn } }) </script>
·注意:当在props中使用驼峰命名法的时候,在动态绑定父类对象的时候一定用’-‘连接·
<div id="app"> <cpn :c-info="info" :child-my-message="message"></cpn></div><template id="cpn"> <div> <h2>{{cInfo}}</h2> <h2>{{childMyMessage}}</h2> </div></template>
在props中定义为cInfo,childMyMessage
通常是子组件中产生一些事件,希望父组件知道,这时需要使用自定义事件来完成。
v-on不仅可以用于监听DOM事件,也可用于组件间的自定义事件。
在子组件中,通过$emit()来触发事件
在父组件中,通过v-on来监听子组件事件
<div id="app"> <cpn @itemclick="cpnClick"></cpn> </div> <template id="cpn"> <div> <ul> <li v-for="item in categories"> <button @click="btnClick(item)">{{item.name}}</button> </li> </ul> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template : `#cpn`, data() { return { categories: [ {id : 1, name : '热门推荐'}, {id : 2, name : '手机数码'}, {id : 3, name : '家用家电'}, {id : 4, name : '电脑办公'} ] } }, methods : { btnClick(item) { console.log(item.name); this.$emit('itemclick', item) } } } const app = new Vue({ el : "#app", data : { info : { name : 'lidx', age : 25, height : 1.88 }, message : '你好' }, methods : { cpnClick(item) { console.log('父组件'+item.name); } }, components : { cpn } }) </script>
要求实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>父{{num1}}</h2> <h2>父{{num2}}</h2> <cpn :number1="num1" :number2="num2" @num1change="num1haschange" @num2change="num2haschange"></cpn> </div> <template id="cpn"> <div> <h2>props:{{number1}}</h2> <h2>data:{{dnumber1}}</h2> <input type="text" v-model="dnumber1"> <!--<input type="text" :value="dnumber1" @input="num1input">--> <h2>props:{{number2}}</h2> <h2>data:{{dnumber2}}</h2> <input type="text" v-model="dnumber2"> <!--<input type="text" :value="dnumber2" @input="num2input">--> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el : "#app", data : { num1 : 1, num2 : 2 }, methods : { num1haschange (value) { this.num1 = parseInt(value) }, num2haschange (value) { this.num2 = parseInt(value) } }, components : { cpn : { template : `#cpn`, props : { number1 : { type : Number }, number2 : { type : Number } }, data () { return { dnumber1 : this.number1, dnumber2 : this.number2 } }, watch : { dnumber1(newValue) { this.dnumber2 = newValue * 100 this.$emit('num1change', newValue) }, dnumber2(newValue) { this.dnumber1 = newValue / 100 this.$emit('num2change', newValue) } } /*methods : { num1input ( event ) { this.dnumber1 = event.target.value this.$emit('num1change', this.dnumber1) this.dnumber2 = this.dnumber1 * 100 this.$emit('num2change', this.dnumber2) }, num2input (event) { this.dnumber2 = event.target.value this.$emit('num2change', this.dnumber2) this.dnumber1 = this.dnumber2 / 100 this.$emit('num1change', this.dnumber1) } }*/ } } }) </script> </body> </html>
watch:检测对象的变化
两种方式:
<div id="app"> <cpn ref="cpn1"></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 : { }, methods : { btnClick() { console.log(this.$children[0].name); console.log(this.$children); // 方式一,获取所有子组件 this.$children[0].showMessage() // 方式一,执行子组件方法 console.log(this.$refs.cpn1.name); console.log(this.$refs); // 方式二,获取所有有ref属性的子组件 this.$refs.cpn1.showMessage() // 方式二,执行子组件方法 } }, components : { cpn : { template : `#cpn`, data() { return { name : 'cpn' } }, methods : { showMessage() { console.log("showMessage()"); } } } } }) </script>
开发当中不常用,也有两种
<div id="app"> <parentcpn></parentcpn> </div> <template id="cpn"> <div> <h2>我是子组件</h2> <button @click="btnClick">按钮</button> </div> </template> <tempalte id="cpn_parent"> <div> <h2>二级组件</h2> <cpn></cpn> </div> </tempalte> <script src="../js/vue.js"></script> <script> const app = new Vue({ el : "#app", data : { message : "你好", }, methods : { showMessage2() { console.log('我是父组件Vue的方法'); } }, components : { parentcpn : { template : `#cpn_parent`, methods : { showMessage1() { console.log('我是二级组件的方法'); } }, components: { cpn : { template : `#cpn`, methods : { btnClick() { console.log(this.$parent); console.log(this.$root); console.log('我是子组件的方法'); this.$parent.showMessage1() this.$root.showMessage2() } } } } } } }) </script>