3、生命周期(大致可分为4个阶段:创建、挂载、更新、销毁)
生命周期钩子
在vue实例的创建时需要一系列的初始化过程,在这个过程中除了设置数据监听、编译模板、实例挂载到DOM等操作外,还会运行一些生命周期钩子的函数,作用是可以让用户在不同阶段添加代码的机会。
4个阶段每个阶段对应两个接口函数,分别表示在此阶段前与在此阶段后。
创建:beforeCreate 和 created
beforecreate:此时el和data尚未初始化 created:完成了data数据的初始化,但是el依旧尚未初始化
挂载:beforemount 和 mounted
beforemount:完成了data和el的初始化,但是el处还是{{message}},这是应用了Virtual DOM(虚拟DOM技术)先把坑占住,在挂载完成后再把值渲染进去 mounted:完成挂载
更新:beforeupdate 和 update
销毁:beforeDestroy 和 destroy
4、计算属性与侦听器
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } })
这里就声明了一个计算属性reversedMessage,再提供函数computed做vm.reversedMessage的getter函数,自然我们就可以绑定message与message的计算过后的值,无论message如何变化,reversedMessage都会同步计算。
虽然我们也可以在表达式中直接调用方法来达到同样的效果
<p>Reversed message: "{{ reversedMessage() }}"</p> // 在组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } }
但是这样做在每次渲染时,函数都会再执行一遍,而计算方法只要message没有改变,就不会再次计算,相比之下可以节约缓存。
侦听属性:
5、条件渲染
v-if方式
v-if用于条件性地渲染一块内容,只有在指令的表达式返回truth时才会渲染
<h1 v-if="awesome">Vue is awesome!</h1>
也可以用v-else添加一个else块,但v-else块必须跟在v-if或v-else-if块后,否则不会被识别
<h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no 😢</h1>
若想使用v-if渲染多个元素,可以在<template>标签内使用v-if,这样就会渲染除了template自身外,包含的所有元素了。
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-else-if块就是跟在v-if后的选择块,可以重复使用
用key管理可复用元素:在用户登陆界面,如果用户要切换登陆方式,就会更改标题和更改输入框,但是输入的内容却没有发生改变,这样不满足大多数情况,那么使用key就可以使两个输入框独立出来。
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template>
这段代码运行结果是,切换登陆方式后,上次输入的内容还存在于输入框
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template>
这段代码的效果是,每次切换登陆方式,输入框内容都会清空(重新渲染)
v-show方式
用法与v-if大致一样,但是不支持<templat>元素,也不支持v-else
不同之处
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
6、事件处理
监听事件:v-on监听DOM事件,在触发时运行一些JavaScript代码
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div>
事件处理方法:在某些情况下,事件的处理逻辑较为复杂,所以在v-on中直接写JavaScript代码是不行的,所以需要在v-on中调用方法,所以v-on后也可以接方法名称
事件修饰符
事件修饰符是由点开头的指令后缀表示
-
.stop
-
.prevent
-
.capture
-
.self
-
.once
-
.passive
<!-- 阻止单击事件继续传播(事件冒泡) --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件监听器时使用事件捕获模式 --> <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 --> <div v-on:click.capture="doThis">...</div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div>
新增:Vue对应addEventListener 中的 passive 选项提供了 .passive修饰符
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> <!-- 而不会等待 `onScroll` 完成 --> <!-- 这其中包含 `event.preventDefault()` 的情况 --> <div v-on:scroll.passive="onScroll">...</div>
若是没有.passive的话,那么会先完成onScroll行为以后再执行默认行为(滚轮事件)
.passive修饰符尤其能提升移动端性能
按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit">
可以通过全局config.keyCodes对象来自定义按键修饰符别名
// 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
系统修饰键
用来实现按下相应按键时才出发鼠标或键盘时间的监听器
.ctrl .alt .shift .meta
.exact修饰符你允许控制由精确的系统修饰符组合触发的事件
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 --> <button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 --> <button v-on:click.ctrl.exact="onCtrlClick">A</button> <!-- 没有任何系统修饰符被按下的时候才触发 --> <button v-on:click.exact="onClick">A</button>
7、组件基础
组件是可复用的Vue实例,且带有一个名字,我们可以在一个通过new Vue创建的Vue根实例中,来将这个组件作为自定义的元素使用。
// 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) <div id="components-demo"> <button-counter></button-counter> </div> new Vue({ el: '#components-demo' })
由于组件是可复用的Vue实例,所以它们与new Vue接受的选项相同,例如data、computed、methods、watch以及生命周期钩子等,除了像el这样根特有的选项外。
一个组件的data必须要是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,这是为了保证复用的独立性,否则一个实例改变就会影响到其他所有的实例
组件注册(有全局注册与局部注册)
之前我们的组件都是通过Vue.component来全局注册的
Vue.component('my-component-name', { // ... options ... })
全局注册:组件可以用在其被注册之后的任何(通过new Vue)新创建的根实例中,也包括其组件树中所有子组件的模板中。
通过prop向子组件传递数据
prop是你能再组件上注册的一些自定义的属性,当一个值传递给一个prop时,它就成了那个组件中的一个属性。我们可以用一个props选项将其包含在该组件能接受的prop列表中。
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
一个组件默认可以拥有任意数量的prop,任何值都能传递给prop。我们能在组件的实例中访问这个值,就像访问data中的值一样。
如果在一些典型(特殊)应用中,可能在data中有一个需要传递的内容形成的数组
new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
而你又想要为数组的每一个选项渲染一个组件,那就需要使用v-bind来动态传递prop
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
单个根元素
当构建一个论文组件时,组件的所包含的内容远不止一个标题,最起码还要有论文正文
但是如果你这样设置:
<h3>{{ title }}</h3> <div v-html="content"></div>
那么就会报错,因为每个组件只能有一个根元素。这种情况我们可以将所有内容包裹在一个父元素里,来修复这个问题。
<div class="blog-post"> <h3>{{ title }}</h3> <div v-html="content"></div> </div>
但是当组件内容很多的时候就会很麻烦,所以我们需要重构这个组件,让它变成一个单独的论文prop:
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" ></blog-post>
Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <div v-html="post.content"></div> </div> ` })
使用事件来抛出一个值
如果我们想让论文组件<blog-post>组件来决定它的文本要放大多少,我们可以用$的第二个参数来提供这个值:
<button v-on:click="$emit('enlarge-text', 0.1)"> Enlarge text </button>
当我们在父级组件来监听这个事件时,可以通过$event访问到被抛出的这个值:
<blog-post ... v-on:enlarge-text="postFontSize += $event" ></blog-post>
在组件上使用v-model
自定义事件也可以用于创建支持v-model的自定义输入组件
<input v-model="searchText">
在用到组件上时,则等价于
<custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input>
为了让其正常工作,此组件内的<input>必须要:
将其value 的属性绑定到一个名叫value的prop上
在其input事件被触发时,将新的值通过自定义的input事件抛出
Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > ` })
这样v-model就能在这个组件上工作起来了
<custom-input v-model="searchText"></custom-input>
8、数据代理
Object.defineProperty(obj , prop ,descriptor )方法返回指定对象上一个自有属性的属性描述符,comfigurable代表可操作的,enumerable代表可遍历的,writable代表可改写的。
obj目标对象, prop需要定义或修改的属性名,descriptor目标属性的特性(true or false)
getter:访问该属性时,该方法会被执行,函数的返回值会作为该属性的值返回;
setter:当属性值修改时,该方法将被执行,该方法将接受唯一的参数,即该属性的新参数值
在vue中,数据代理是通过Vue的实例对象vm来代理data对象中属性的操作(读/写)的,这样的好处是使操作data里的数据变得更加方便。
基本原理:是通过Object.defineProperty()将data的对象中的所有属性添加到vm上,然后为每一个添加到vm上的属性,指定一个getter/setter,然后在getter/setter的内部去操作(读/写)data中的属性。