手牵手学习vue之解读vue官方文档"/>
手牵手学习vue之解读vue官方文档
计算属性缓存与方法
- 计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。
- 方法每次都需要重新执行。
//计算属性
computed: {// 计算属性的 getter 仅读取reversedMessage: function () {// `this` 指向 vm 实例return this.message.split('').reverse().join('')},// 读取和设置aPlus: {get: function () {return this.a + 1},set: function (v) {this.a = v - 1}}}//方法
methods: {reversedMessage: function () {return this.message.split('').reverse().join('')}
}
描述:我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。
侦听器watch
- 当需要在数据变化时执行异步或开销较大的操作时,Vue 通过
watch
选项提供了一个更通用的方法,来响应数据的变化。
// watch各类用法
watch: {a: function (val, oldVal) {console.log('new: %s, old: %s', val, oldVal)},// 方法名b: 'someMethod',// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深c: {handler: function (val, oldVal) { /* ... */ },deep: true},// 该回调将会在侦听开始之后被立即调用d: {handler: 'someMethod',immediate: true},//对象执行'e.f': function (val, oldVal) { /* ... */ }
}
Vue中不能使用箭头函数地方
- 生命周期函数中不能使用箭头函数
不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a)
或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 this,
this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致
Uncaught TypeError:Cannot read property of undefined 或
Uncaught TypeError: this.myMethod is not a function 之类的错误。
- methods中不能使用箭头函数
注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。
理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,
this.a 将是 undefined。
- watch中不能使用箭头函数
注意,不应该使用箭头函数来定义 watcher 函数
(例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定
了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,
this.updateAutocomplete 将是 undefined。
- computed中不能使用箭头函数
注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。computed: {aDouble: vm => vm.a * 2
}
- data中不能使用箭头函数
注意,如果你为 data property 使用了箭头函数,则 this 不会指向这个组件的实例,
不过你仍然可以将其实例作为函数的第一个参数来访问。data: vm => ({ a: vm.myProp })
Class与Style绑定
绑定HTML Class
- 对象语法
<template><div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
</template>
<script>export defalut{data(){return{isActive: true,hasError: false}}
}
</script>
//最终渲染
<div class="static active"></div>==========================================================<template><div class="static" :class="classObject"></div>
</template>
<script>export defalut{data(){return{classObject: {active: true,'text-danger': false}}}
}
</script>
//最终渲染
<div class="static active"></div>==========================================================//绑定一个返回对象的计算属性
<template><div v-bind:class="classObject"></div>
</template>
<script>export defalut{data(){return{isActive: true,error: null}},computed: {classObject: function () {return {active: this.isActive && !this.error,'text-danger': this.error && this.error.type === 'fatal'}}}
}
</script>
//最终渲染
<div class="static active"></div>
- 数组语法
<template><div v-bind:class="[activeClass, errorClass]"></div>
</template>
<script>export defalut{data(){return{activeClass: 'active',errorClass: 'text-danger'}}}
</script>//最终渲染
<div class="active text-danger"></div>==============================================//用三元表达式,这样写将始终添加 errorClass,但是只有在 isActive 是 truthy 时才添加 activeClass
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>==============================================//数组语法中也可以使用对象语法
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
绑定内联样式
- 对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {activeColor: 'red',fontSize: 30
}
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div v-bind:style="styleObject"></div>
data: {styleObject: {color: 'red',fontSize: '13px'}
}
- 数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>
v-if VS v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
v-if与v-for一起使用
不推荐同时使用 v-if
和 v-for
。当 v-if
与 v-for
一起使用时,v-for
具有比 v-if
更高的优先级,这意味着 v-if
将分别被重复运行于每个 v-for
循环中。
当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">{{ todo }}
</li>
上面的代码将只渲染未完成的 todo。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或 <template>
) 上。如:
<ul v-if="todos.length"><li v-for="todo in todos">{{ todo }}</li>
</ul>
<p v-else>No todos left!</p>
事件修饰符
vue.js 为 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><!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a><!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
表单修饰符
- lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy
修饰符,从而转为在 change
事件_之后_进行同步:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
- number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符:
<input v-model.number="age" type="number">
- trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model
添加 trim
修饰符:
<input v-model.trim="msg">
基础组件
- data必须是一个函数
一个组件的 data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
data: function () {return {count: 0}
}
组件注册
- 全局注册
到目前为止,我们只用过 Vueponent
来创建组件:
Vueponent('my-component-name', {// ... 选项 ...
})
这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue
) 的模板中。
弊端:全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
- 局部注册
在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后在 components
选项中定义你想要使用的组件:
new Vue({el: '#app',components: {'component-a': ComponentA,'component-b': ComponentB}
})
注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentA
在 ComponentB
中可用,则你需要这样写:
如果你通过 Babel 和 webpack 使用 ES2015 模块,那么代码看起来更像:
import ComponentA from './ComponentA.vue'export default {components: {ComponentA},// ...
}
Prop类型
到这里,我们只看到了以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常你希望每个 prop 都有指定的值类型。
props: {title: String,likes: Number,isPublished: Boolean,commentIds: Array,author: Object,callback: Function,contactsPromise: Promise // or any other constructor
}
传递静态或动态Prop
像这样,你已经知道了可以像这样给 prop 传入一个静态的值:
<blog-post title="My journey with Vue"></blog-post>
你也知道 prop 可以通过 v-bind
动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。
这里有两种常见的试图变更一个 prop 的情形:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。
props: ['initialCounter'],
data: function () {return {counter: this.initialCounter}
}
- 这个 prop 以一种原始的值传入且需要进行转换。
props: ['size'],
computed: {normalizedSize: function () {return this.size.trim().toLowerCase()}
}
类型检查
type
可以是下列原生构造函数中的一个:
String
Number
Boolean
Array
Object
Date
Function
Symbol
全局API
- Vue.nextTick()
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
什么时候需要使用
- 你在
Vue
生命周期的created()钩子函数进行的DOM
操作一定要放在Vue.nextTick()
的回调函数中。原因是什么呢,原因是在created()
钩子函数执行的时候DOM
其实并未进行任何渲染,而此时进行DOM
操作无异于徒劳,所以此处一定要将DOM
操作的js
代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM
挂载和渲染都已完成,此时在该钩子函数中进行任何DOM
操作都不会有问题 。 - 在数据变化后要执行的某个操作,当你设置
vm.someData = 'new value'
,DOM
并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM
更新。
mounted
不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick
替换掉 mounted
:
mounted: function () {this.$nextTick(function () {})
}
生命周期钩子
- beforeCreate
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
- created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el
property 目前尚不可用。
- beforeMount
在挂载开始之前被调用:相关的 render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
- mounted
实例被挂载后调用,这时 el
被新创建的 vm.$el
替换了。如果根实例挂载到了一个文档内的元素上,当 mounted
被调用时 vm.$el
也在文档内。该钩子在服务器端渲染期间不被调用。
注意 mounted
不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted
内部使用 vm.$nextTick:
mounted: function () {this.$nextTick(function () {// Code that will run only after the// entire view has been rendered})
}
- beforeUpdate
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
- updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
注意 updated
不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated
里使用 vm.$nextTick:
updated: function () {this.$nextTick(function () {// Code that will run only after the// entire view has been re-rendered})
}
- beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
- destroyed
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。该钩子在服务器端渲染期间不被调用。
更多推荐
手牵手学习vue之解读vue官方文档
发布评论