注册组件
组件其他补充
组件数据存放
父子组件通信
父级向子级传递信息
子级向父级传递信息
插槽slot
1.1什么是组件化
1.2
注册组件的基本步骤
创建组件构造器
注册组件
使用组件
<div id="app"> <mycpn></mycpn> </div> <script src="../js/vue.js"></script> <script> //1.创建组件构造器对象 const cpn = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容,哈哈哈哈</p> <p>我是内容,呵呵呵呵呵呵</p> </div>` }) Vue.component(‘mycpn‘,cpn) const app = new Vue({ el: ‘#app‘, data: { } }) </script>
1.3全局组件和局部组件
当我们通过调用Vue.component()注册组件时,组件的注册是全局的
这意味着该组件可以在任意Vue实例下使用
如果我们注册的组件时挂载在某个实例中,那么就是局部组件
<div id="app"> <mycpn></mycpn> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> //1.创建组件构造器 const cpn = Vue.extend({ template: ` <div> <h2>我是标题1</h2> <p>我是内容 哈哈哈哈</p> </div> ` }) // 2.注册组件(全局组件,意味着可以再多个vue实例中使用) Vue.component(‘mycpn‘,cpn) const app = new Vue({ el: ‘#app‘, data: { message: ‘你好啊‘ }, components: { cpn:cpn } }) </script>
1.4父组件和子组件:
<div id="app"> <cpn2></cpn2> <h2>--------</h2> <cpn1></cpn1> </div> <script src="../js/vue.js"></script> <script> // 1.创建第一个组件构造器 const cpn1 = Vue.extend({ template: `<div> <h2>我是子组件标题</h2> <p>我是子组件内容 哈哈哈</p> </div>` }) //2.创建第二个组件构造器 const cpn2 = Vue.extend({ template:`<div> <h2>我是父组件标题</h2> <p>我是父组件内容 呵呵呵呵呵</p> <cpn1></cpn1> </div>`, //在组件内部使用带s的components components:{ cpn1: cpn1 } }) const app = new Vue({ el:‘#app‘, data:{ }, components:{ cpn2: cpn2, cpn1: cpn1 } }) </script>
1.5注册组件语法糖:
<div id="app"> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> //一.全局组件注册的语法糖 //1.创造组件构造器 // const cpn1 = Vue.extend() //2.注册组件 // Vue.component(‘cpn‘,{ // template: `<div> // <h2>我是全局组件标题</h2> // <p>我是全局组件内容 呵呵呵呵呵</p> // </div>` // }) const app = new Vue({ el: ‘#app‘, data: { }, components:{ cpn: { template: `<div> <h2>我是局部组件标题</h2> <p>我是局部组件内容 呵呵呵呵呵</p> </div>` } } }) </script>
1.6模板的分离写法:
<div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <!-- 第一种模板 注意:必须是text/x-template类型--> <!-- <script type="text/x-template" id="cpn"> <div> <h2>我是标题</h2> <p>我是内容</p> </div> </script> --> <!-- 第二种模板:template --> <template id="cpn"> <div> <h2>这是标题哈哈</h2> <p>这是内容哈哈</p> </div> </template> <script src="../js/vue.js"></script> <script> // 1.注册一个全局组件 Vue.component(‘cpn‘,{ template: ‘#cpn‘ }) const app = new Vue({ el: ‘#app‘, data: { message: ‘你好啊‘ } }) </script>
1.7组件数据的存放
组件自己的数据存放在哪里呢?
组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)
只是这个data属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据
<div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <!-- 第一种模板 注意:必须是text/x-template类型--> <!-- <script type="text/x-template" id="cpn"> <div> <h2>我是标题</h2> <p>我是内容</p> </div> </script> --> <!-- 第二种模板:template --> <template id="cpn"> <div> <h2>{{title}}</h2> <p>这是内容哈哈</p> </div> </template> <script src="../js/vue.js"></script> <script> // 1.注册一个全局组件 Vue.component(‘cpn‘,{ template: ‘#cpn‘, data() { return { title: ‘测试文件‘ } } }) const app = new Vue({ el: ‘#app‘, data: { message: ‘你好啊‘ } }) </script>
1.8演示:为什么data是一个函数?
首先,如果不是一个函数,vue直接回报错
其次,原因在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后相互影响
<div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <template id="cpn"> <div> <h2>当前计数是 : {{counter}}</h2> <button @click=‘innerment‘>+</button> <button @click=‘denerment‘>-</button> </div> </template> <script src="../js/vue.js"></script> <script> Vue.component(‘cpn‘,{ template: ‘#cpn‘, data () { return { counter: 0 } }, methods:{ innerment(){ this.counter++ }, denerment(){ this.counter-- }, } }) const app = new Vue({ el: ‘#app‘, data: { message: ‘你好啊‘ } }) </script>
1.9.1父子组件的通信
一些数据需要从上层传递到下层
vue官方提到:
通过props向子组件传递数据
通过事件向父组件发送消息
props基本用法
在组件中,使用选项props来声明需要从父级接收到的数据
props的值有两种方式
方式一: 字符串数组,数组中的字符串就是传递时的名称
方式二: 对象,对象可以设置传递时的类型,也可以设置默认值等
<body> <div id="app"> <!-- 使用cpn子组件 动态绑定movies和message --> <cpn :cmovies="movies" :cmessage="message"></cpn> </div> <!-- 子组件模板 --> <template id="cpn"> <div> <ul> <!-- 循环cmovies的电影 --> <li v-for="item in cmovies">{{item}}</li> </ul> <h2>{{cmessage}}</h2> </div> </template> <!-- 引入vue文件 --> <script src="../js/vue.js"></script> <script> //实例化子组件 const cpn = { //模板对象,和el挂载点相似,注意:template必须要有id为cpn的 template: ‘#cpn‘, //子组件data必须是一个函数,每个组件调用的时候不能是同一个对象, //只能是一个函数,必须带有return返回值 data(){ return {} }, methods:{}, //父组件向子组件传递信息使用props方法 props:{ cmovies:{ type:Array, default(){ return [] }, required: true }, cmessage: { type:String, default: ‘aaaa‘, required: true } } } //根组件,也可以算父组件,实例化vue对象 const app = new Vue({ //挂载点:#app el: ‘#app‘, //对象中数据现在不用函数 data: { message: ‘测试文件‘, movies:[‘肖申克的救赎‘,‘当幸福来敲门‘,‘加勒比海盗‘,‘了不起的盖茨比‘] }, //注册组件 components:{ cpn } }) </script>
除了数组之外,也可以使用对象,当需要对props进行类型验证时,就需要对象写法了
支持的类型:
String(字符串) Number(数字) Boolean(布尔值) Array(数组)
Object(对象) Date(时间) Function(函数) Symbol
1.9.2子级向父级传递
方式:自定义事件
流程:
在子组件中,通过$emit() 来出发事件
在父组件中,通过v-on来监听子组件事件
<div id="app"> <cpn @itembtn="cpnclick"></cpn> </div> <template id="cpn"> <div> <button v-for="item in categories" @click="itemclick(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: ‘保存应用‘}, {id: ‘eee‘,name: ‘关闭应用‘}, ] } }, methods: { itemclick (item) { console.log(item.id) this.$emit(‘itembtn‘,item) } } } const app = new Vue({ el: ‘#app‘, data: { message: ‘你好啊‘ }, components: { cpn }, methods:{ cpnclick (item) { console.log(item.id) } } }) </script>
2.0父子组件的访问方式: $children
有时候需要父组件直接访问子组件,子组件直接访问父组件,或者子组件访问根组件
父组件访问子组件: 使用$children或者$refs
子组件访问父组件: 使用$parent
<div id="app"> <cpn ref="ccc"></cpn> <cpn ref="bbb"></cpn> <cpn ref="aaa"></cpn> <button @click=‘btnClick‘>测试按钮</button> </div> <template id="cpn"> <div> <h2>我是子组件</h2> </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) // // this.$children[0].showMessage() // for(let c of this.$children){ // c.showMessage() // console.log(c.name) // } //2 $refs 用的最多 注意此处写的 是refs ,上面写的是ref="" // $refs => 对象类型 ,默认是一个空的对象 如果使用上面要用 ref="bbb" console.log(this.$refs.aaa.name) this.$refs.aaa.showMessage() console.log(this.$refs.bbb.name) this.$refs.bbb.showMessage() console.log(this.$refs.ccc.name) this.$refs.ccc.showMessage() } }, components: { cpn: { template: ‘#cpn‘, data() { return { name: `中级的欲望 通过努力可以获得 低级的欲望 通过放纵可以获得` } }, methods: { showMessage() { console.log(‘*的欲望 通过煎熬才能获得‘) } } } } }) </script>
this.$children是一个数组类型,它包含了所有子组件对象
$children的缺陷
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值.
但是当子组件过多,我们需要拿到其中一个时,往往不能确定他的索引值,甚至还可能发生变化
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
$refs的使用:
$refs和ref指令通常是一起使用的
首先,我们通过ref 给某一个子组件绑定一个特定的ID
其次,通过this.$refs.ID就可以访问到该组件了
2.1父子组件的访问方法: $parent
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做
子组件应该尽量避免访问父组件的数据,因为这样耦合度太高了
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
另外,更不好做的是用过$parent直接改父组件的状态,那么父组件中的状态将变得不稳定
<div id="app"> <h2>这是vue实例</h2> <cpn></cpn> </div> <template id="cpn"> <div> <h2>这是自组件</h2> <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: ` 低级的欲望 通过放纵就可获得 高级的欲望 通过自律方可获得 *的欲望 通过骄傲才可获得` } }, components: { ccpn: { template: ‘#ccpn‘, methods: { btnClick() { //1第一种 $parent 只能访问父级组件的数据,但是耦合性很高,不能扩展 // console.log(this.$parent) // console.log(this.$parent.name) //2第二种,通过root 获取根组件的数据 console.log(this.$root) console.log(this.$root.message) } } } } } } }) </script>
2.2插槽slot
编译作用域:
父组件模板的所有东西都会在父级作用域内编译,子组件模板的所有东西都会在子级作用域内编译
插槽的作用:为了让我们封装的组件更加具有扩展性
如何使用slot:
在子组件中,使用特殊的元素<slot>就可以为子组件开启一个插槽
该插槽的插入什么内容取决于父组件如何使用
<div id="app"> <cpn> <span>姓名</span> <input type="text" name=‘username‘ placeholder="请输入姓名"> </cpn> <cpn> <span>密码</span> <input type="text" name="password" placeholder="请输入密码"> </cpn> <cpn> <button>登录</button> <button>注册</button> </cpn> </div> <template id="cpn"> <div> <!-- <h2>使用slot</h2> <button>slot</button> --> <slot> <button>初始按钮</button> </slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: ‘#app‘, data: { message: ‘hell Vue.js‘ }, components: { cpn: { template: ‘#cpn‘, data() { return {} }, methods:{} } } }) </script>
具名插槽slot
当子组件的功能复杂时,插槽并非一个
只要给slot元素一个name属性即可
<slot name="myslot"></slot>
<div id="app"> <cpn> <button slot="left">返回</button> </cpn> <cpn> <span slot="left">姓名</span> <input type="text" slot="center" placeholder="请输入姓名"> </cpn> <cpn> <span slot="left">密码</span> <input type="text" slot="center" placeholder="请输入密码"> </cpn> <cpn> <button slot="left">登录</button> <button slot="right">注册</button> </cpn> </div> <template id="cpn"> <div> <slot name="left"></slot> <slot name="center"></slot> <slot name="right"></slot> <slot name="noname"></slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: ‘#app‘, data: { message: ‘hello Vue.js‘ }, components:{ cpn: { template: ‘#cpn‘, methods:{}, data() { return {} }, props:{}, computed:{} } } }) </script>
作用域插槽:
父组件替换插槽的标签,但是内容由子组件来提供
<div id="app"> <cpn v-show="isShow"></cpn> </div> <template id="cpn"> <div> <h2>我是子组件</h2> <h3>你有为自己努力过吗</h3> <button v-show="isShow"></button> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: ‘#app‘, data: { message: ‘hello Vue.js‘, isShow: true }, components: { cpn: { template: ‘#cpn‘, props:{}, methods:{}, data () { return { isShow: false } } } } }) </script>
插槽作用域的使用:
核心是在slot中加上属性: :data
然后在父组件中调用自组件数据: slot-scope="slot"
<div id="app"> <cpn></cpn> <cpn> <span slot="aaa">测试文件</span> </cpn> <cpn> <div slot="aaa" slot-scope="slot"> <span v-for="item in slot.data">{{item}} -</span> </div> </cpn> <cpn> <div slot="aaa" slot-scope="slot"> <span v-for="item in slot.data">{{item}} $</span> </div> </cpn> <cpn> <div slot="aaa" slot-scope=‘slot‘> <!-- <span v-for="item in slot.data">{{item}}</span> --> <span>{{slot.data.join(‘--‘)}}</span> </div> </cpn> </div> <template id="cpn"> <div> <slot name=‘aaa‘ :data="pLanges"> <ul> <li v-for="item in pLanges">{{item}}</li> </ul> </slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: ‘#app‘, data: { message: ‘hello Vue.js‘ }, components:{ cpn: { template: ‘#cpn‘, data() { return { pLanges: [‘C++‘,‘python‘,‘java‘,‘js‘,‘php‘] } } } } }) </script>