Vue实例的生命周期
生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称生命周期
生命hz周期钩子:生命周期事件的别名而已
组件创建期间的4个钩子函数
beforeCreate
:实例刚在内存中被创建出来,此时,还没有初始化好 data和methods属性created
:实例已经在内存中创建好,此时data和methods也已经创建好。但,此时还没有开始编译模板beforeMount
:此时已经完成了模板编译,但是还没有挂载到页面中mounted
:此时,已将将编译好的模板,挂载到页面指定的容器中显示。它是实例创建期间的最后一个生命周期函数,当执行完mounted就表示实例已经被完全创建好了。此时,如果没有其他操作的话,这个实例就在内存中。此时,组件脱离了创建阶段,进入运行阶段。所以,如果要操作页面上的DOM 节点,最早可以在mounted中进行。
组件运行阶段的钩子函数
beforeUpdate
:状态更新之前执行此函数,此时data中的状态值是最新的,但是页面上显示的数据还是旧的,因此此时还没有开始重新渲染DOM节点updated
:实例更新完毕之后调用此函数,此时data中的状态值和页面上显示的数据,都已经完成了更新,页面也已经被重新渲染好了
这两个事件,会根据data数据的改变,有选择的触发0次或多次。
组件销毁阶段的钩子函数
beforeDestory
:实例销毁之前调用。在这一步,实例(包括:过滤器、指令、Data、methods等)仍然完全可用。destoryed
:Vue实例销毁之后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。例如: v-if 绑定了一个Data的值是true, 通过调用方法,设置为false,此时会执行销毁钩子函数。
用户离开页面的时候,也会调用
//通常用来销毁一些监听事件和定时函数: destroyed() { window.removeEventListener('resize', this.resizeWin) }
- 你也可以手动调用
$destoryed
进行销毁。
Vue 组件
组件ed 出现,是为了拆分Vue实例的代码量,能够让我们以不同的组件,来划分不同的功能模块,将来需要什么样的功能,就可以去调用对应的组件即可。
- 模块化:从代码逻辑的角度进行划分,方便代码的分层开发,保证每个功能模块的职能单一;
- 组件化:从UI界面的角度进行划分,方便UI组件的重用;
全局组件定义的三种方式
- (1)使用Vue.extend来创建全局的Vue组件
var com1 = Vue.extend({ template: '<h3>使用Vue.extend创建的组件</h3>' //指定组件要展示的html结构 }) //使用Vue.component('组件名称',创建出来的组件模板对象) //如果使用 Vue.component定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把大写的驼峰改成小写的字母,同时,两个单词之间,使用 '-'连接; //如果不使用 驼峰,则直接拿名称来使用即可; Vue.component('myCom1',com1); Vue.component('mycom1',com1);//不使用驼峰
<div id="app"> <!--如果要使用组件,直接把组件名称以html标签的形式,引入到页面中,名称以小写加'-'连接命名--> <my-com1></my-com1> <!--<mycom1></mycom1>不使用驼峰的方式--> </div>
合并使用: Vue.component 第一个参数:组件名称,将来在引用组件的时候,就是一个标签形式来引入它的;第二个参数:Vue.extend创建的组件,其中template就是组件要展示的HTML内容
Vue.component('mycom1',Vue.extend({ template: '<h3>使用Vue.extend创建的组件</h3>' }))
- (2)字面量对象
Vue.component('mycom2',{ //注意:不论哪种方式创建出来的组件,组件的template属性指向的模板内容,必须有且只有唯一一个根元素 template: '<div><h3>使用Vue.component创建的组件</h3><span>123</spann></div>' })
<mycom2></mycom2>
- (3)template元素
Vue.component('mycom3',{ template: '#tmp1' })
<div id="app"> <mycom3></mycom3> </div> //在被控制的 #app 外面,使用 template 元素,定义组件的html模板结构 <template id="tmp1"> <div> <h1> 通过template元素,在外部定义的组件结构 </h1> </div> </template>
定义实例内部私有组件
- components属性
var vm = new Vue({ el: '#app1', components: { login: { template: '<h1>app1的私有组件login</h1>'//可将其使用<template></template>方式定义 } } })
<div id="app1"> <login></login> </div>
组件里的data要定义为一个function
- 组件可以有自己的data
- 组件的data和实例的data有点不同,实例中的data可以为一个对象,但是组件中的data必须是一个方法,为了保持组件之间的独立性
- 组件中的data除了必须为一个方法,还必须返回一个对象
- 组件的data数据,使用方式和实例的方式一样
Vue.component('mycom1',{ template: '<h1>{{msg}}</h1>',//使用data data: function() { return { msg: '组件中的data' } }, methods: { add() { } } })
组件切换的方式
(1)指令方式 v-if, v-else
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> </head> <body> <div id="app"> <a @click.prevent="flag=true">登录</a> <a @click.prevent="flag=false">注册</a> <login v-if="flag"></login> <register v-else="flag"></register> </div> <script> Vue.component('login',{ template: '<h3>登录组件</h3>' }) Vue.component('register',{ template: '<h3>注册组件</h3>' }) var vm = new Vue({ el: '#app', data: { flag: true } }) </script> </body> </html>
(2)使用vue 提供 component 来展示对应名称的组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> </head> <body> <div id="app"> <a @click.prevent="flag='login'">登录</a> <a @click.prevent="flag='register'">注册</a> <!-- vue 提供 component 来展示对应名称的组件 --> <!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 --> <component :is="comName"></component> </div> <script> Vue.component('login',{ template: '<h3>登录组件</h3>' }) Vue.component('register',{ template: '<h3>注册组件</h3>' }) var vm = new Vue({ el: '#app', data: { comName: 'login' } }) </script> </body> </html>
组件切换动画 < transition >及其mode属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> <style> .v-enter, .v-leave-to { opacity: 0; transform: translateX(150px); } .v-enter-active, .v-leave-active { transition: all .5s ease; } </style> </head> <body> <div id="app"> <a @click.prevent="flag='login'">登录</a> <a @click.prevent="flag='register'">注册</a> <!-- 通过 mode 属性设置组件切换时候的模式 --> <transition mode="out-in"> <component :is="flag"></component> </transition> </div> <script> Vue.component('login',{ template: '<h3>登录组件</h3>' }) Vue.component('register',{ template: '<h3>注册组件</h3>' }) var vm = new Vue({ el: '#app', data: { flag: 'login' } }) </script> </body> </html>
Vue 把一个完整的动画,使用钩子函数,拆分为两部分。
父子组件之间的传值
(1)父组件向子组件传值
- 父组件中使用v-bind属性绑定
- 子组件中使用props定义父组件传递过来的名称
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件,可以在引用子组件的时候,通过属性绑定的(v-bind:)形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 --> <com1 v-bind:parentmsg="msg"></com1> </div> <script> var vm = new Vue({ el: '#app', data: { msg: '父组件数据' }, methods: {}, components: { com1: { // 注意:组件中所有props中的数据,都是通过父组件传递给子组件的 //props中的数据,都是只读的,重新赋值会报错。建议放在data属性中 props: ['parentmsg'],//把父组件传递过来的parentmsg属性,先在props数组中定义,才能使用这个数据 template: '<h1>这是子组件 --- {{ parentmsg }} </h1>' } } }) </script> </body> </html>
(2)父组件把方法传递给子组件:通过事件调用方式
- 事件绑定机制
- 子组件方法中使用
this.$emit('name',参数列表)
调用父组件方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件向子组件传递方法,使用的是事件绑定机制:v-on, 当我们自定义了一个事件属性后, 那么子组件就能够通过某些方式来调用传递金曲的这个方法了 --> <com2 v-on:func="show"></com2> </div> <template id="tmpl"> <div> <h1>这是个子组件</h1> <input type="button" value="触发父组件传递过来的func方法" @click="myclick"> </div> </template> <script> // 定义一个字面量类型 var com2 = { template: '#tmpl', //通过制定一个 ID,表示说要去加载这个制定ID的template元素中的内容,当作组件的HTML结构 data() { return { msg: {name:'imagincode'} } }, methods: { myclick() { //当点击子组件的按钮时候,如何拿到父组件传递过来的func方法,并调用这个方法? //emit : 触发、调用 this.$emit('func',this.msg)//给父组件传递参数 } }, } var vm = new Vue({ el: '#app', data: { msg: '父组件数据', datamsgFromChild: null }, methods: { show(data) { console.log('调用父组件的show方法'+data) this.datamsgFromChild = data;//父组件拿到子组件传递过来的值 } }, components: { com2 } }) </script> </body> </html>
refs 获取DOM元素和组件
- ref是reference的缩写
<h2 id="id-h2" ref="h2el"></h2> //DOM <Login ref="mylogin"></Login> //组件 this.$refs.h2el; //获取DOM this.$refs.mylogin; //获取组件的引用
Watch , Methods, Computed
使用Watch监听路由地址的改变
watch: { '$route.path': function(newVal,oldVal) { console.log(oldVal+'-->'+newVal) } }
Computed 计算属性
在computed中,可以定义一些属性,这些属性,叫做【计算属性】,计算属性的本质,就是一个方法。只不过,在使用这些计算属性的时候,是把它们的名称,直接当做属性来使用,并不会把计算属性当做方法去调用
data: { firstname: '', lastname: '' }, computed: { 'fullname': function() { return this.firstname + '-' + this.lastname } }
- 计算属性,在引用的时候,一定不要加()去调用,直接把它当做普通属性来使用。
- 计算属性内部所用到的任何data中的数据发生了变化,就会重新计算这个属性的值。
- 计算属性的求值结果会被缓存起来,方便下次直接使用。如果计算属性方法中,所依赖的任何数据,都没有发生过变化,则不会重新对计算属性求值。
Watch , Methods, Computed的对比
computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用。methods
方法表示一个具体的操作,主要用于书写业务逻辑。watch
是一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作。可以看做是computed
和methods
的结合体。