当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
有时我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个
元素将其动态组件包裹起来。
** 切换tag时保存标签页状态
注意这个
要求被切换到的组件都有自己的名字,不论是通过组件的
name
选项还是局部/全局注册。
当组件在
内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
// 模拟异步组件
Vue.component('async-example', function (resolve, reject) {
// 向 `resolve` 回调传递组件定义
setTimeout(function () {
resolve({template: 'I am async!'})
}, 1000)
})
// 结合webpack使用
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法将会告诉 webpack自动将你的构建代码切割成多个包,这些包会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
// 动态导入 --这个动态导入会返回一个 `Promise` 对象。
Vue.component('async-webpack-example',() => import('./my-async-component'))
new Vue({components: {'my-component': () => import('./my-async-component')}})
const AsyncComponent = () => ({
component: import('./MyComponent.vue'),// 需要加载的组件 (应该是一个 `Promise` 对象)
loading: LoadingComponent,// 异步组件加载时使用的组件
error: ErrorComponent,// 加载失败时使用的组件
delay: 200,// 展示加载时组件的延时时间。默认值是 200 (毫秒)
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
注意如果你希望在 Vue Router 的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。
在每个 new Vue
实例的子组件中,其根实例可以通过 $root property
进行访问。
所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。
// Vue 根实例
new Vue({
data: {foo: 1},
computed: {
bar: function () { /* ... */ }
},
methods: {
baz: function () { /* ... */ }
}
})
this.$root.foo // 获取根组件的数据
$parent property
可以用来从一个子组件访问父组件的实例。
// 在层级嵌套组件里会失控。因此向任意更深层级的组件提供上下文信息时推荐依赖注入
var map = this.$parent.map || this.$parent.$parent.map
在 JavaScript 里直接访问一个子组件可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用。
现在在 JS 里可以通过 this.$refs.usernameInput
引用这个组件
$refs
只会在组件渲染完成之后生效,并且它们不是响应式的。
使用 $parent property
无法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地,它用到了两个新的实例选项:provide
和 inject
。
provide: [object | ()=>object]
inject: Array
// 父级组件提供 'getMap'
provide: function () {
return {getMap: this.getMap}
}
// 子组件注入 'getMap'
inject: ['getMap']
// 用 from 来表示其源 property
const Child = {
inject: {
foo: {from: 'bar',default: () => [1, 2, 3]}
}
}
相比 $parent 来说,这个用法可以让我们在任意后代组件中访问 getMap,而不需要暴露整个
实例。
provide
和 inject
绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
$on(eventName, eventHandler)
侦听一个事件$once(eventName, eventHandler)
一次性侦听一个事件$off(eventName, eventHandler)
停止侦听一个事件// 一次性将这个日期选择器附加到一个输入框上,它会被挂载到 DOM 上。
// Pikaday 是一个第三方日期选择器的库
mounted: function () {
this.picker = new Pikaday({field: this.$refs.input,format: 'YYYY-MM-DD'})
},
// 在组件被销毁之前,也销毁这个日期选择器。
beforeDestroy: function () {
this.picker.destroy()
}
上述的picker挂载到了Vue实例的data上,而实际我们 只需要在生命周期钩子函数上使用并销毁
一次侦听可以解决这样的问题,每个新的实例都程序化地在后期清理它自己。
mounted: function () {
var picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
// 封装picker,每个picker在实例销毁之前销毁自身
mounted: function () {
this.attachDatepicker('startDateInput');
this.attachDatepicker('endDateInput')},
methods: {attachDatepicker: function (refName) {
var picker = new Pikaday({
field: this.$refs[refName],
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {picker.destroy()})
}
}
组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事
// 使用一个最终会得到 false 的 v-if
Vue.component('stack', {
template: ' '
})
如果你使用一个模块系统依赖/导入组件,例如通过 webpack 或 Browserify,你会遇到一个错误
{{ folder.name }}
{{ child.name }}
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default}
components: {TreeFolderContents: () => import('./tree-folder-contents.vue')}
当 inline-template 这个特殊的 attribute 出现在一个子组件上时,这个组件将会使用其里面的内容作为模板,而不是将其作为被分发的内容。
These are compiled as the component's own template.
内联模板需要定义在 Vue 所属的 DOM 元素内。
在一个 元素中,并为其带上 text/x-template 的类型,然后通过一个 id 将模板引用过去。
x-template 需要定义在 Vue 所属的 DOM 元素外。
$forceUpdate
:迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
有的时候你可能有一个组件,这个组件包含了大量静态内容。在这种情况下,你可以在根元素上添加 v-once attribute 以确保这些内容只计算一次然后缓存起来。
Vue.component('terms-of-service', {
template: `Terms of Service
... a lot of static content ...`
})
可能导致无法正确更新。