Vue Devtools
在浏览器上安装Vue Devtools有助于我们查看组件以及组间的传值,可以说是相当方便。
计算属性 vs 方法
什么时候用计算属性,什么时候用方法?
计算属性跟方法最大的不同在于:计算属性是基于它们的响应式依赖进行缓存的。
这句话就是说,只有在相关响应式依赖发生改变时,计算属性才会重新求值,而方法是每次都会去调用执行。总的来说,计算属性在某些时候是比方法更能够提高性能。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!
计算属性 vs 侦听器
计算属性跟侦听器也很像,那什么时候用计算属性,什么时候用侦听器呢?(以下都是官网的例子,挺通俗易懂的)
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
//用侦听器时
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
//用计算属性时
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
通过上述代码,是不是感觉计算属性更适合呢?
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
总的来说,当需要执行异步操作或者开销较大的操作时,使用侦听器是更为合适的。
计算属性的 setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
用 key 管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:(以下采用了官网的例子)
<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>
像上面的代码一样,当你在input输入框中输入文本后,当loginType不为username时,切换模板,由于两个模板都使用了相同的元素,因此vue只是替换掉input中的placeholder值,并不会改变input输入框中的已输入的文本。
然而这样也不总是符合实际需求,因此 Vue 为我们提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:
<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>
加上key属性后,现在每次改变都会重新进行渲染,也就是input框中输入的文本不会进行复用啦(label元素仍然会被高效地复用,因为没有添加key属性)。
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:可以通过 Vue 的 component 元素加一个特殊的 is 属性来实现
<div id="dynamic-component-demo" class="demo">
<button
v-for="tab in tabs"
:key="tab"
@click="currentTab = tab">
{{ tab }}
</button>
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
</div>
<script>
Vue.component("tab-home", {
template: "<div>Home component</div>"
});
Vue.component("tab-posts", {
template: "<div>Posts component</div>"
});
Vue.component("tab-archive", {
template: "<div>Archive component</div>"
});
new Vue({
el: "#dynamic-component-demo",
data: {
currentTab: "Home",
tabs: ["Home", "Posts", "Archive"]
},
computed: {
currentTabComponent: function() {
return "tab-" + this.currentTab.toLowerCase();
}
}
});
</script>
props
类型:Array <string> | Object
详细:
props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
你可以基于对象的语法使用以下选项:
type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。
default:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
required:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
validator:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。
// 简单语法
Vue.component('props-demo-simple', {
props: ['size', 'myMessage']
})
// 对象语法,提供验证
Vue.component('props-demo-advanced', {
props: {
// 检测类型
height: Number,
// 检测类型 + 其他验证
age: {
type: Number,
default: 0,
required: true,
validator: function (value) {
return value >= 0
}
}
}
})
禁用 Attribute 继承
我们通过一个例子来理解一下。
父组件 parent-component.vue
<template>
<div class="parent">
<child-component aaa="1111"></child-component>
</div>
</template>
<script>
import ChildComponent from './child-component'
export default {
components: {
ChildComponent
}
}
</script>
子组件 child-component.vue 设置 inheritAttrs: true(默认)
<template>
<div class="child">子组件</div>
</template>
<script>
export default {
inheritAttrs: true,
mounted() {
console.log('this.$attrs', this.$attrs)
}
}
</script>
最后渲染的结果:
Elements
Console
子组件 child-component.vue 设置 inheritAttrs: false
<template>
<div class="child">子组件</div>
</template>
<script>
export default {
inheritAttrs: fasle,
mounted() {
console.log('this.$attrs', this.$attrs)
}
}
</script>
最后渲染的结果:
Elements
Console
总结:
由上述例子可以看出,前提:子组件的props中未注册父组件传递过来的属性。
1.当设置inheritAttrs: true(默认)时,子组件的顶层标签元素中(本例子的div元素)会渲染出父组件传递过来的属性(本例子的aaa="1111")。
2.当设置inheritAttrs: false时,子组件的顶层标签元素中(本例子的div元素)不会渲染出父组件传递过来的属性(本例子的aaa="1111")。
3.不管inheritAttrs为true或者false,子组件中都能通过$attrs属性获取到父组件中传递过来的属性。
这个例子和总结转自这篇文章,原文章写得很好懂:
.sync 修饰符
在Vue中,父子组件最常用的通信方式就是通过props进行数据传递,props值只能从父组件中更新并传递给子组件,在子组件内部,是不允许改变传递进来的props值,这样做是为了保证数据单向流通。但有时候,我们会遇到一些场景,需要在子组件内部改变props属性值并更新到父组件中,这时就需要用到.sync修饰符。
<text-document
:title="doc.title"
@update:title="doc.title = $event"
></text-document>
//使用.sync修饰符
<text-document :title.sync="doc.title">
</text-document>
又上述代码可知,:title.sync="doc.title" 相当于 :title="doc.title" @update:title="doc.title = $event" ,vue为这种模式提供了缩写。
.native 修饰符
.native修饰符用于将原生事件绑定到组件上(可以理解成.native就是把一个vue组件转化为一个普通的HTML标签),使得该组件的根元素可以监听到原生事件。
<base-input v-on:focus.native="onFocus"></base-input>
在动态组件上使用 keep-alive
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
keep-alive能够使组件实例能够被它们第一次被创建的时候缓存下来,具体可以看下这个例子keep-alive缓存组件实例的例子 ,在Posts和Archive来回切换的过程中,可以保持组件先前的状态。
provide / inject
作用:用于父组件向子孙组件传递数据,provide在父组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。
// 父组件传值
provide: function () {
return {
getMap: this.getMap
}
}
// 子孙辈组件接收值
inject: ['getMap']
⚠️注意:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
渲染函数 & JSX
render是字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。
JSX 是 JavaScript 的类似XML的语法扩展,没有任何定义的语义,也就是说,JSX 允许我们在 JS 中使用类似 Html 的语法。 下面是一个JSX语法结合render函数的例子。
new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
⚠️注意:将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。
过滤器
过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | filterMsg }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | filterInfo"></div>
同时我们也可以在组件中定义过滤器。
filters: {
filterMsg: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
过滤器可以串联:
{{ message | filterA | filterB }}
vm.$nextTick( [callback] )
作用:将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
Vue.compile( template )
作用: 将一个模板字符串编译成 render 函数。只在完整版时可用。
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: {
msg: 'hello'
},
render: res.render,
staticRenderFns: res.staticRenderFns
})