===重点重点开始 ==========================
(三) 组件化开发
1.创建组件构造器: Vue.extends()
2.注册组件: Vue.component()
3.使用组件(全局和局部组件)
一般方式:全局和局部组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 3.使用组件(要放到一个签中(<div>)) --> <div> <zjm></zjm> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1. 创建组件模版(一般方式) const 组件名 = Vue.extend({ template: `<div><h2>一般方式的组件内容</h2></div>` }) // 2.注册全局组件(标签名+组件名),可以在多个vue实例中使用,开如中一般不用全局组件. Vue.component('zjm', 组件名) // Vue实例对象(开发中一般只有一个vue实例) const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 2.注册局部组件(标签名+组件名) // components: { // zjm: 组件名 // } }) </script> </body> </html> 一般方式:全局和局部组件
语法糖方式:全局和局部组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 3.使用组件 --> <div> <qjzjm></qjzjm> </div> <div> <jbzjm></jbzjm> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1-2.创建注册全部组件(语法糖方式) Vue.component('qjzjm', { template: `<div><h2>全局组件内容-语法糖</h2></div>` }) // root根组件 const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 1-2.创建注册局部组件(语法糖方式) components: { 'jbzjm': { template: `<div><h2>局部组件内容-语法糖</h2></div>` } } }) </script> </body> </html> 语法糖方式
4.模板分离(2种方法)
方法1: <script>标签方法(一般不用)
方法2: 使用<template>标签方法(常用)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--4.使用组件--> <div> <qjcpn></qjcpn> <jbcpn></jbcpn> </div> </div> <!--3.分离组件中的模板,使用template标签方法,设id对应相应的组件,通过id联系在一起--> <template id="qjzjm"> <div> <h1>全部组件:使用template标签方法,分离组件中的模板!</h1> </div> </template> <template id="jbzjm"> <div> <h1>局部组件:使用template标签方法,分离组件中的模板!</h1> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1-2.创建注册全部组件(语法糖方式),取个名字,后面跟个对象即可创建. Vue.component('qjcpn', { template: '#qjzjm', data() { return { msg: '组件中的msg数据' } } }) const app = new Vue({ el: '#app', data: {}, // 1-2.创建注册局部组件(语法糖方式) components: { 'jbcpn': { template: `#jbzjm` } } }) </script> </body> </html> 使用template标签方法
5.组件中数据,组件中的data必需是一个函数,同时返回是一个对象,vue实例中是一个属性不是一个函数,二者的数据不能直接访问,每次使用组件时就会调用data()函数,如果不是函数,组件在二次使用时,就会出现同一个数据.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--4.使用组件--> <cpn></cpn> <cpn></cpn> <h1>{{msg}}</h1> </div> <template id="mytemplate"> <div> <h1>方法2:使用template标签方法,分离组件中的模板!</h1> <h1>{{msg}}</h1> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1.注册一个全局组件,取个名字,后面跟个对象即可创建. Vue.component('cpn',{ // 2.分离模板要对应模板中的id(#mytemplate) template:'#mytemplate', // 这里必需是一个函数,同时返回是一个对象. data(){ return { msg: '组件中的msg数据' } } }) const app = new Vue({ el: '#app', data: { msg: '实例中的msg数据' } }) </script> </body> </html>
加减实例(组件内数据data为什么是一个函数实例说明)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--4.使用组件,重复使用不相互影响--> <cpn></cpn> <cpn></cpn> </div> <template id="mytemplate"> <div> <h1>当前计数:{{counter}}</h1> <button @click="zj">增加+</button> <button @click="js">减少-</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1.注册一个全局组件,取个名字,后面跟个对象即可创建. Vue.component('cpn',{ // 2.分离模板要对应模板中的id(#mytemplate) template:'#mytemplate', // 这里必需是一个函数,同时返回是一个对象,这样重复使用就不相互影响. data(){ return { msg: '组件中的msg数据', counter: 0 } }, methods:{ zj(){this.counter++}, js(){this.counter--} }, }) const app = new Vue({ el: '#app', data: { msg: '实例中的msg数据' } }) </script> </body> </html>
6.父子组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 4.使用组件 --> <fzjm></fzjm> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1. 创建组件模版(子组件) const zzjm = Vue.extend({ template: `<div><h2>组件内容1-子组件名zzjm</h2></div>` }) // 2. 创建组件模版(父组件,将子组件放到父组件中) const fzjm = Vue.extend({ template: ` <div> <h2>组件内容2-父组件名fzjm</h2> <!--// 在父组件中使用子组件--> <zzjm></zzjm> </div> `, components: { zzjm: zzjm, } }) // root根组件 const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 3.注册局部组件(标签名+组件名) components: { // 将父组件放到Vue实例root根组件中,没有在根组件中注册的组件不能直接使用(组件名1不能直接使用). fzjm: fzjm, } }) </script> </body> </html> 父子组件一般方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 4.使用组件 --> <fzjm></fzjm> </div> <template id="zzjmb"> <div> <h2>子组件在父组件中使用:使用template标签方法,分离组件中的模板!</h2> </div> </template> <template id="fzjmb"> <div> <h1>父组件</h1> <!--// 在父组件中使用子组件--> <zzjm></zzjm> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 1. 创建组件模版(子组件) const zzjm = Vue.extend({ template: '#zzjmb' }) // 2. 创建组件模版(父组件,将子组件放到父组件中) const fzjm = Vue.extend({ template: '#fzjmb', components: { zzjm: zzjm, } }) // root根组件 const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 3.注册局部组件(标签名+组件名) components: { // 将父组件放到Vue实例root根组件中,没有在根组件中注册的组件不能直接使用(组件名1不能直接使用). fzjm: fzjm, } }) </script> </body> </html> 父子组件模版分离方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 4.使用子组件 --> <zzjm></zzjm> </div> <!--3.子组件模板--> <template id="zzjmb"> <div> <h2>常用子组件在根组件(父组件)中使用</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // root根组件(父组件,开发中一般是这种写法) const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 1-2.创建注册局部组件(语法糖方式) components: { 'zzjm': { template: '#zzjmb' } } }) </script> </body> </html> 父子组件在根组件中使用(常用)
7.组件模板中的数据,不能直接在根组件中显示
<body> <div id="app"> <!-- 4.使用组件 --> <zjm2></zjm2> <!-- 不能直接用组件2中的数据的,同时要报错! --> {{title}} </div> <template id="zjm2"> <div> <h2> 组件2内容</h2> <h2> {{title}}</h2> </div> </template> <script> // root根组件 const app = new Vue({ el: '#app', //挂载管理元素 data: {}, // 1-2.创建注册局部组件(语法糖方式) components: { zjm2: { template: '#zjm2', data() { return { title: '组件2中的数据' } } } } }) </script> </body>
8.父子组件的通信(数据的传递)
A:父传子: props
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 5.绑定父子组件中的值,绑定后子组件中就可以使用父组件中的数据了,===> 7.展示子组件中数据 --> <zzj1 v-bind:users1='users' v-bind:msg1='msg'> </zzj1> </div> <!-- 3.子组件模版 --> <template id="zzjmb"> <div> <!-- 6.在子组件中使用父组件的值users和msg,必需用子组件中的变量users1和msg1 --> <h2>props父组件中的值:{{users1}}</h2> <h2>props父组件中的值:{{msg1}}</h2> <h2>props父组件中的值:循环对象中的值</h2> <ul> <li v-for='item in users1'>{{item}}</li> </ul> <!--子组件data中的数据--> <h2>{{title}}</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // root根组件(父组件) const app = new Vue({ el: '#app', // 将下面data中数据传给子组件展示 data: { msg:'长安镖局', users: ['特朗普1', '特朗普2', '特朗普3', '特朗普4', '特朗普5', '特朗普6', ] }, // 1-2.创建注册局部组件(语法糖方式) components: { zzj1: { template: '#zzjmb', data() {return {title: '子组件data中的数据'} }, // 4.设置绑定父组件中的变量名users1,msg1 // 方法一:数组的写法,很少用. props: ['users1', 'msg1'] } } }) </script> </body> </html> 数组写法(很少用)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 5.绑定父子组件中的值,绑定后子组件中就可以使用父组件中的数据了,===> 7.展示子组件中数据 --> <zzj1 v-bind:users1='users' v-bind:msg1='msg'> </zzj1> </div> <!-- 3.子组件模版 --> <template id="zzjmb"> <div> <!-- 6.在子组件中使用父组件的值users和msg,必需用子组件中的变量users1和msg1 --> <h2>props父组件中的值:{{users1}}</h2> <h2>props父组件中的值:{{msg1}}</h2> <h2>props父组件中的值:循环对象中的值</h2> <ul> <li v-for='item in users1'>{{item}}</li> </ul> <!--子组件data中的数据--> <h2>{{title}}</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // root根组件(父组件) const app = new Vue({ el: '#app', // 将下面data中数据传给子组件展示 data: { msg:'长安镖局', users: ['特朗普1', '特朗普2', '特朗普3', '特朗普4', '特朗普5', '特朗普6', ] }, // 1-2.创建注册局部组件(语法糖方式) components: { zzj1: { template: '#zzjmb', data() {return {title: '子组件data中的数据'} }, // 4.设置绑定父组件中的变量名users1,msg1 // 方法一:数组的写法,很少用. // props: ['users1', 'msg1'] // 方法二:对象的写法 props: { users1: Array, //Array数组类型,String字符型,还可以指定验证要求,设置默认值,变量名不能写骆峰格式,不然上面展示时要转换格式才能绑定. msg1:Array } } } }) </script> </body> </html> 对象写法(常用)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 5.绑定父子组件中的值,绑定后子组件中就可以使用父组件中的数据了,===> 7.展示子组件中数据 --> <zzj1 v-bind:msg1='msg' v-bind:users1='users'> </zzj1> </div> <!-- 3.子组件模版 --> <template id="zzjmb"> <div> <!-- 6.在子组件中使用父组件的值users和msg,必需用子组件中的变量users1和msg1 --> <h2>props父组件中的值:{{users1}},{{msg1}}</h2> <h2>props父组件中的值:循环显示对象中的值</h2> <ul><li v-for='item in users1'>{{item}}</li></ul> <!--子组件data中的数据--> <h2>{{title}}</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', // 将下面data中数据传给子组件展示 data: { users: ['特朗普1', '特朗普2', '特朗普3', '特朗普4', '特朗普5', '特朗普6', ], msg:'长安镖局' }, // 1-2.创建注册局部组件(语法糖方式) components: { zzj1: { template: '#zzjmb', data() {return {title: '子组件data中的数据'} }, // 4.设置绑定父组件中的变量名users1,msg1 // 方法一:数组的写法,很少用,里面是一个变量名. // props: ['users1', 'msg1'] // 方法二:对象的写法,设置变量名同时设定他的类型. // 变量名为users1: 变量名类型为数组Array, 变量名为msg1: 变量名类型为字符型String. // props: { // users1: Array, // 变量名不能写骆峰格式,不然上面展示时要转换格式才能绑定. // msg1:String // } props: { users1: Array, // 变量名不能写骆峰格式,不然上面展示时要转换格式才能绑定. msg1: { type:String, default:'我是长安镖局!' // 如果前面没有传值就显示默认的值 }, // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) // propA: Number, // 多个可能的类型 // propB: [String, Number], // 必填的字符串 // propC: { // type: String, // required: true // }, // 带有默认值的数字 // propD: { // type: Number, // default: 100 // }, // 带有默认值的对象 // propE: { // type: Object, // 对象或数组默认值必须从一个工厂函数获取 // default: function () { // return { message: 'hello' } // } // }, // 自定义验证函数 // propF: { // validator: function (value) { // // 这个值必须匹配下列字符串中的一个 // return ['success', 'warning', 'danger'].indexOf(value) !== -1 // } // } } } } }) </script> </body> </html> 对象写法prop验证类型说明
A:子传父(自定义事件)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 父组件模版 --> <div id="app"> <!-- 6.将子组件传递的事件绑定到父组件中,默认自带子组件的参数值 --> <zzj1 @zzjjiajia='fjiajia'></zzj1> <!-- 8.展示数据--> 点击次数:{{total}} </div> <!-- 3.子组件模版 --> <template id="zzjmb"> <div> <!-- 4.子组件绑定事件 --> <button @click='jiajia'>+1</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // root根组件(父组件) const app = new Vue({ el: '#app', //挂载管理元素 data: {total: 0}, // 1-2.创建注册局部组件(语法糖方式) components: { 'zzj1':{ template: '#zzjmb', data() {return {num: 0}}, methods: { jiajia() { this.num++; // 5.用$emit方法,发射绑定事件和值 this.$emit('zzjjiajia', this.num) // 发射出的事件名称,自已取名,发射的值this.num. } }, } }, methods: { // 7.在父组件中处理子组件传递的事件 fjiajia(num) { this.total = num // 将子组件中的num赋值给total } } }) </script> </body> </html> 子传父加加例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--父组件模版--> <!-- 5.监听子组件item-click发射的事件,自定义接收事件名称(zzjitemclick),这里默认把值item传了过来--> <zzj @item-click='zzjitemclick'></zzj> </div> <template id="zzjmb"> <!--子组件模版 --> <div> <!--3.绑定点击事件和要传的值(item),自定义事件名称(btnclick),循环分类(fenlei)展示数据--> <button v-for="item in fenlei" @click='btnclick(item)'>{{item.name}}</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: {}, // 6.在父组件中使用子组件事件和值,这里item和msg都是形参,按传入的位置取值. methods: { zzjitemclick(item, msg) { console.log(item, msg); }, }, // 1-2.创建注册局部组件(组件名+对象) components: { 'zzj': { template: '#zzjmb', // 子组件模版id data() { return { fenlei: [ {id: 1, name: '企业用户信息'}, {id: 2, name: '业务基础信息'} ], msg: '长安镖局', } }, methods: { btnclick(item) { // 4.用$emit方法发射点击事件和值,自定义发射事件的名称(item-click). this.$emit('item-click', item, this.msg) // 传模版中的数据不能写成this.item,传data中的数据就要加this } } } } }) </script> </body> </html> 子传父分类例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父组件模板--> <div id="app"> <!-- 3.绑定子父组件中值的变量(zusers对应父的users,msg对应zmsg),子组件就可以使用父组件中的数据了,===> 5.展示子组件中数据 --> <!-- 8.子传父:3.监听子组件item-click发射的事件,定义一个父组件中使用的事件名zzjitemclick,这里默认把值item传了过来--> <zzj v-bind:zusers='users' v-bind:zmsg='msg' @item-click='zzjitemclick'></zzj> </div> <!--子组件模版 --> <template id="zzjmb"> <div> <!-- 4.在子组件中使用父组件的值users和msg,必需用子组件中自定义的变量zusers和zmsg--> <!-- 6.子传父:1.将点击事件和值传给父组件,绑定子组件中点击事件@click和item值--> <button v-for='item in zusers' @click='btnclick(item,zmsg)'>{{item.name}}</button> <h2>父组件中的数据:{{zmsg}}</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', // 将下面data中数据传给子组件展示 data: { users: [ {id: 1, name: '企业用户信息'}, {id: 2, name: '业务基础信息'} ], msg:'长安镖局' }, // 9.子传父:4.在父组件中使用子组件事件和值,这里item和title都是形参,按传入的位置取值. methods: { zzjitemclick(item,zmsg,title){ console.log(item,zmsg,title); }, }, // 1.创建注册局部子组件(组件名+对象) components: { zzj: { template: '#zzjmb', //子组件模版id data() { return { title: '子组件data中的数据', } }, // 2.设置父组件中值对应子组件中的变量名,自定义子数据变量名zusers对应父的users,msg对应zmsg. props: { zusers: Array, // 变量名不能写骆峰格式,不然上面展示时要转换格式才能绑定. zmsg: { type: String, default: '我是长安镖局!' // 如果前面没有传值就显示默认的值 } }, methods: { btnclick(item,zmsg) { // console.log(item); // 7.子传父:2.用$emit方法,发射点击绑定事件和值,传模版中的数据不加this,传data中的数据就要加this. this.$emit('item-click',item, zmsg,this.title) //自定义发射的事件名称item-click } } } } }) </script> </body> </html> 父传子再传父分类例子
9.父子组件的访问方式($children)
A:父组件访问子组件:使用$children或$refs
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父组件模版--> <div id="app"> <zzj1></zzj1> <!--使用子组件1--> <zzj1></zzj1> <!--使用子组件2--> <button @click="btnclick">按钮</button> </div> <!--子组件模版--> <template id="zzjmb"> <div><h2>子组件data中的数据:{{title}}</h2></div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: {}, methods:{ btnclick(){ console.log(this.$children) // 在父组件中执行子组件1中的方法, this.$children[0].showmsg() // 在父组件中执行子组件2中的方法, this.$children[1].showmsg() } }, // 1.创建注册局部组件(组件名+对象) components: { zzj1: { template: '#zzjmb', data() {return {title: '子组件data中的数据'} }, methods: { // 子组件里的方法 showmsg(){ console.log('我是子组件里的方法') } } } } }) </script> </body> </html> 父访问子($children)不常用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父组件模版--> <div id="app"> <zzj1 ref="ref1"></zzj1> <!--使用子组件1,加一个属性--> <zzj1 ref="ref2"></zzj1> <!--使用子组件2,加一个属性--> <button @click="btnclick">按钮</button> </div> <!--子组件模版--> <template id="zzjmb"> <div><h2>子组件data中的数据:{{title}}</h2></div> </template> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: {}, methods:{ btnclick(){ // $refs对象类型,默认空 console.log(this.$refs) // 在父组件中执行子组件1中的方法, this.$refs.ref1.showmsg() // 在父组件中执行子组件2中的方法, this.$refs.ref2.showmsg() } }, // 1.创建注册局部组件(组件名+对象) components: { zzj1: { template: '#zzjmb', data() {return {title: '子组件data中的数据'} }, methods: { // 子组件里的方法 showmsg(){ console.log('我是子组件里的方法') } } } } }) </script> </body> </html> 父访问子($refs)常用
B:子组件访问父组件:使用$parent和$root 实际开发中不建义用.