Python全栈(六)项目前导之9.Vue自定义组件和生命周期函数
Vue自定义组件和生命周期函数
一、给组件添加属性和单一根元素
1.自定义组件添加属性
原始的html元素一般都有自己的一些属性,我们自己创建的组件,也可以通过props属性来添加自己的属性。这样在使用创建的组件的时候就可以传递不同的参数了。
测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <book-list v-bind:books="books"></book-list> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('book-list',{ // 给组件添加属性 props:['books'], // 多行代码时用``包含(不是单引号) template:` <table> <tr> <th>序号</th> <th>标题</th> </tr> <tr v-for="(book,index) in books"> <td>{{index + 1}}</td> <td>{{book.title}}</td> </tr> </table> `, }) new Vue({ el:'#app', data:{ books:[ { 'title':'Python', 'id':1 }, { 'title':'Java', 'id':2 }, { 'title':'PHP', 'id':3 }, ], }, }) </script>
显示:
在创建的组件中,不能直接使用Vue对象中的数据,需要将数据绑定到新建组件的prop属性中,并且在使用新创建的组件需要属性绑定到相应的数据才能正常引用数据。
2.单一根元素
自定义的组件中可以出现多个html元素,但是根元素只能有一个,其余的元素必须包含在这个根元素中。
比如以下是一个组件中模板的代码:
template:` <h3>{{ title }}</h3> <div v-html="content"></div> `
会报错,可以改成:
template:` <div> <h3>{{ title }}</h3> <div v-html="content"></div> </div> `
在新建组建的模板中添加多个标签并进行测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <book-list v-bind:books="books"></book-list> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('book-list',{ // 给组件添加属性 props:['books'], // 多行代码时用``包含(不是单引号) template:` <table> <tr> <th>序号</th> <th>标题</th> </tr> <tr v-for="(book,index) in books"> <td>{{index + 1}}</td> <td>{{book.title}}</td> </tr> </table> <div>返回顶部</div> `, }) new Vue({ el:'#app', data:{ books:[ { 'title':'Python', 'id':1 }, { 'title':'Java', 'id':2 }, { 'title':'PHP', 'id':3 }, ], }, }) </script>
与之前显示相同,即模板中的根元素有两个时是不能正常显示的,只能有一个根元素。
将它们放入一个根元素后再测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <book-list v-bind:books="books"></book-list> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('book-list',{ // 给组件添加属性 props:['books'], // 多行代码时用``包含(不是单引号) template:` <div> <table> <tr> <th>序号</th> <th>标题</th> </tr> <tr v-for="(book,index) in books"> <td>{{index + 1}}</td> <td>{{book.title}}</td> </tr> </table> <div>返回顶部</div> </div> `, }) new Vue({ el:'#app', data:{ books:[ { 'title':'Python', 'id':1 }, { 'title':'Java', 'id':2 }, { 'title':'PHP', 'id':3 }, ], }, }) </script>
显示:
此时可以正常显示,即要满足单一根元素原则。
二、子组件事件和传递事件到父组件
子组件中添加事件跟之前的方式是一样的,如果发生某个事件后想要通知父组件,可以使用
this.$emit函数来实现。
测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <book-list v-for="book in books" v-bind:book="book" @check-changed="check"></book-list> <div v-for="book in bookcomponent">{{book.title}}</div> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('book-list', { props: ['book'], template: ` <div> <span>{{book.title}}</span> <input type="checkbox" @click="onCheck"> </div> `, methods:{ onCheck:function(){ this.$emit('check-changed',this.book) } } }) new Vue({ el: '#app', data: { books: [{ 'title': 'Python', 'id': 1 }, { 'title': 'Java', 'id': 2 }, { 'title': 'PHP', 'id': 3 }, ], bookcomponent: [] }, methods:{ check:function(book){ // indexOf()函数返回元素的下标,大于等于0则表明数组中存在该元素 var index = this.bookcomponent.indexOf(book) if(index >= 0){ this.bookcomponent.splice(index,1) } else{ this.bookcomponent.push(book) } } } }) </script>
显示:
indexOf()函数返回元素在数组中的下标,如果值大于等于0,则说明该数组中存在该元素。
显然,上面子组件被选择后,会同步显示到父组件中,其中的时间流和数据流的流转原理如下:
其中,红色标示①→④表示事件流,即点击复选框触发的事件的流转;
蓝色标识⑤→⑩标识数据流,即父组件从子组件获取数据并即时同步的过程;
子组件触发的事件通过与数据流的交织传递最终到父组件,并达到需要的效果。
注意:
因为html中大小写是不敏感的,所以在定义子组件传给父组件事件名称的时候,尽量避免使用checkChanged这种驼峰命名法,而是使用check-changed这种规则。
三、自定义组件v-model
一个组件上的v-model默认会利用value属性和input事件,并且像单选框、复选框等类型的输入控件可能会将value特性用于不同的目的,这时候我们可以在定义组件的时候,通过设置model选项来实现不同的处理方式。
模拟一个计步器,测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <stepper v-model:value="stepcount"></stepper> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('stepper', { props: ['count'], model:{ // 触发v-model事件的情况 event:'count-changed', prop:'count' }, template: ` <div> <button @click="sub">-</button> <span>{{count}}</span> <button @click="add">+</button> </div> `, methods:{ sub:function(){ this.$emit('count-changed',this.count - 1) }, add:function(){ this.$emit('count-changed',this.count + 1) } } }) new Vue({ el: '#app', data: { stepcount:0 }, methods:{ } }) </script>
显示:
说明:
在自定义的组件中,
- props定义的属性是给组件外部调用组件的时候使用的;
- model中定义的
prop:'count'
是告诉后面使用v-model的时候,要修改哪个属性; event:'count-changed'
是告诉v-model,后面触发哪个事件的时候要修改属性。
数据流和时间流传递原理如下:
其中,红色表示事件流,蓝色表示数据流;
通过数据与事件的相互作用,达到了自定义组件中v-model的目的。
四、Vue插槽
1.插槽的定义和简单使用
我们定义完一个组件后,可能在使用的时候还需要往这个组件中插入新的元素或者文本,这时候就可以使用插槽来实现。
测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url">百度一下</navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot></slot> </a> ` }) new Vue({ el: '#app', data: { url:'https://www.baidu.com' }, methods:{ } }) </script>
显示:
即在新建的组件中插入的内容(百度一下)是通过插入到插槽标签
<slot>中实现的。
如果在模板中的标签中定义了文本,则在HTML正文中的定义出的组件中的文本的优先级更大,会显示出来,如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url">百度一下</navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot>小度小度</slot> </a> ` }) new Vue({ el: '#app', data: { url:'https://www.baidu.com' }, methods:{ } }) </script>
即百度一下的优先级更高,会显示,与之前测试的效果相同。
不仅是文本,插槽可以包含任何模板代码,包含HTML语句,例如:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url"> <span class="fa fa-user">不懂你就</span> 百度一下 </navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot></slot> </a> ` }) new Vue({ el: '#app', data: { url:'https://www.baidu.com' }, methods:{ } }) </script>
显示:
如果没有包含一个元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
即在自定义组件时,如果没有包含slot元素,在HTML中的自定义组件中插入任意的文本或HTML元素都是不会被渲染到网页的,如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url"> <span class="fa fa-user">不懂你就</span> 百度一下 </navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> </a> ` }) new Vue({ el: '#app', data: { url:'https://www.baidu.com' }, methods:{ } }) </script>
显示:
显示空白,即没有元素被渲染出来。
2.定义多个插槽
我们还可以通过给插槽命名来实现多个插槽,测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url"> <template v-slot:first>第一个插槽</template> <template v-slot:second>第二个插槽</template> <template v-slot:third>第三个插槽</template> </navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot name="first"></slot> <br> <slot name="second"></slot> <br> <slot name="third"></slot> </a> `, data:function(){ return { name:'Python' } } }) new Vue({ el: '#app', data: { url:'https://www.baidu.com', name:'Corley' }, methods:{ } }) </script>
显示:
如果有部分未命名,则为默认插槽,测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url"> <template v-slot:first>第一个插槽</template> <template v-slot:second>第二个插槽</template> <template>默认插槽</template> <template v-slot:third>第三个插槽</template> </navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot name="first"></slot> <br> <slot name="second"></slot> <br> <slot name="third"></slot> <br> <slot></slot> </a> `, data:function(){ return { name:'Python' } } }) new Vue({ el: '#app', data: { url:'https://www.baidu.com', name:'Corley' }, methods:{ } }) </script>
显示:
3.插槽的作用域
子组件中调用父组件属性测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url">{{name}}</navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot>小度小度</slot> </a> ` }) new Vue({ el: '#app', data: { url:'https://www.baidu.com', name:'Corley' }, methods:{ } }) </script>
显示:
很明显,定义的子组件可以访问到父组件中的属性。
在定义的组件中增加name属性进行测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <navigation-link :url="url">{{name}}</navigation-link> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('navigation-link',{ props:['url'], template:` <a v-bind:href="url"> <slot>小度小度</slot> </a> `, data:function(){ return { name:'Python' } } }) new Vue({ el: '#app', data: { url:'https://www.baidu.com', name:'Corley' }, methods:{ } }) </script>
与前者效果相同。
可以看到,自定义的组件的插槽只能访问到父组件中的属性,而不能访问到自己的属性。
在定义组件时绑定测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <container> <template v-slot:header="headerProps"> 顶部{{headerProps.navs}} </template> <template v-slot:footer="footerProps"> 底部{{footerProps.contact}} </template> </container> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('container',{ props:['url'], template:` <div> <div> <slot name='header' :navs="navs"></slot> </div> <div> <slot name='footer' :contact="contact"></slot> </div> </div> `, data:function(){ return { navs:['网页','咨询','视频'], contact:'xxx' } } }) new Vue({ el: '#app', }) </script>
显示:
可以总结:
默认情况下,slot可以直接使用父组件中的数据;
要想slot使用自定义组件中的数据,需要slot标签进行绑定(v-bind)。
五、生命周期函数
1.Vue生命周期定义
生命周期函数代表的是Vue实例或者是Vue组件,在网页中各个生命阶段所执行的函数。
Vue生命周期可以类比Python中的对象。
生命周期函数可以分为三个阶段:
- 创建阶段
- 运行阶段
- 销毁阶段
在生命周期的各个阶段的执行过程可以从下图(官方文档提供)得出:
2.创建阶段
创建期间的函数有
beforeCreate、
created、
beforeMount和
mounted。
beforeCreate
Vue或者组件刚刚实例化,data、methods都还没有被创建。
beforeCreate()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, beforeCreate(){ console.log('*************'), console.log(this.name), console.log(this.hello), console.log('*************') } }) </script>
显示:
created
此时data和methods已经被创建,可以使用了,但模板还没有被编译。
created()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, created(){ console.log('*************'), console.log(this.username), console.log(this.hello), console.log('*************') } }) </script>
显示:
beforeMount
created的下一阶段,此时模板已经被编译了,但是并没有被挂在到网页中。
beforeMount()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <p id='username'>{{username}}</p> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, beforeMount(){ console.log('*************'), console.log(document.getElementById('username').innerText), console.log('*************') } }) </script>
显示:
即在调用
beforeMount()方法时还是原生的HTML代码,未挂载到网页。
mounted
模板代码已经被加载到网页中,此时创建期间所有事情都已经准备好了,网页开始运行。
mounted()方法测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <p id='username'>{{username}}</p> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, mounted(){ console.log('*************'), console.log(document.getElementById('username').innerText), console.log('*************') } }) </script>
显示:
3.运行阶段
运行期间的函数有
beforeUpdate、
updated。
beforeUpdate
在网页网页运行期间,data中的数据可能会进行更新。
在这个阶段,数据只是在data中更新了,但是并没有在模板中进行更新,因此网页中显示的还是之前的。
beforeUpdate()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <input type="text" v-model="username"> <p id='username'>{{username}}</p> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, beforeUpdate(){ console.log('*************'), console.log(document.getElementById('username').innerText), console.log('*************') } }) </script>
显示:
显然,每次获取到的都是更新之前的值。
updated
此时数据在data中更新了,也在网页中更新了。
updated()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <input type="text" v-model="username"> <p id='username'>{{username}}</p> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data:{ username:'Corley' }, methods:{ hello:function(){ return 'hello worid' } }, updated(){ console.log('*************'), console.log(document.getElementById('username').innerText), console.log('*************') } }) </script>
显示:
此时显示的是更新之后的数据。
4.销毁阶段
销毁期间的函数有beforeDestroy、destroyed。
beforeDestroy
Vue实例或者是组件在被销毁之前执行的函数,在这一个函数中Vue或者组件中所有的属性都是可以使用的。
beforeDestroy()方法测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <p id='username'>{{username}}</p> <error-view v-bind:message="message" v-if="message"></error-view> <button @click="message=''">销毁</button> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('error-view',{ props:['message'], template:'<p style="color:red">{{message}}</p>', beforeDestroy(){ console.log('组件已销毁') } }) new Vue({ el: '#app', data:{ username:'Corley', message:'错误信息' }, methods:{ hello:function(){ return 'hello worid' } }, updated(){ console.log('*************'), console.log(document.getElementById('username').innerText), console.log('*************') } }) </script>
显示:
由于直接销毁Vue对象即相当于关闭当前网页,查看不到
beforeDestroy()方法调用时的现象,所以转化为查看销毁创建的组件时的现象。
destroyed
Vue实例或者是组件被销毁后执行的,此时Vue实例上所有东西都会解绑,所有事件都会被移除,所有子元素都会被销毁。
测试如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue模板语法</title> </head> <body> <div id='app'> <p id='username'>{{username}}</p> <error-view v-bind:message="message" v-if="message"></error-view> <button @click="message=''">销毁</button> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('error-view',{ props:['message'], template:'<p style="color:red">{{message}}</p>', destroyed() { this.$destroy() console.log("destoryed"); } }) new Vue({ el: '#app', data:{ username:'Corley', message:'错误信息' }, methods:{ hello:function(){ return 'hello worid' } }, }) </script>
显示:
调用系统的
destroy()方法来使组件销毁,从而可以观察到销毁的现象。
- 点赞 4
- 收藏
- 分享
- 文章举报
- Python全栈(六)项目前导之8.Vue事件绑定、计算属性、表单输入绑定和自定义组件
- Python全栈(六)项目前导之2.Redis数据类型和Python操作Redis
- weex 项目开发(五)自定义 过滤函数 和 混合 及 自定义 Header 组件
- Python全栈(六)项目前导之7.Vue模板语法
- Python全栈(六)项目前导之5.使用GitHub进行多人协同开发
- Python全栈(六)项目前导之1.Redis介绍及数据类型介绍
- Python全栈(六)项目前导之4.Git分支和GitHub的使用
- Python全栈(六)项目前导之3.初试Git
- 【Vue】零基础学习Vue: 第21课 Vue父子组件生命周期函数的执行顺序:
- Python基础11- 函数之自定义函数
- Python学习笔记之变量、自定义函数用法示例
- PHP项目开发中最常用的自定义函数整理
- python-自定义函数详解
- Python自定义函数的创建、调用和函数的参数详解 .argmax
- python学习之--自定义函数:
- python全栈开发-Day10 装饰器(闭合函数的应用场)
- 最新老男孩Python全栈开发16期VIP项目实战(完整)
- hive自定义函数的python实现
- Python写的创建文件夹自定义函数mkdir()
- 洗礼灵魂,修炼python(24)--自定义函数(5)—匿名函数lambda