组件(优化)

动态组件

keep-alive

当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。

有时我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>

** 切换tag时保存标签页状态

注意这个 <keep-alive> 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :exclude="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
<keep-alive :max="10">
<component :is="view"></component>
</keep-alive>

异步组件

Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

// 模拟异步组件
Vue.component('async-example', function (resolve, reject) {
// 向 `resolve` 回调传递组件定义
setTimeout(function () {
resolve({template: '<div>I am async!</div>'})
}, 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+ 版本。

$root

在每个 new Vue 实例的子组件中,其根实例可以通过 $root property 进行访问。

所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。

// Vue 根实例
new Vue({
data: {foo: 1},
computed: {
bar: function () { /* ... */ }
},
methods: {
baz: function () { /* ... */ }
}
})
this.$root.foo // 获取根组件的数据

$parent

$parent property 可以用来从一个子组件访问父组件的实例。

// 在层级嵌套组件里会失控。因此向任意更深层级的组件提供上下文信息时推荐依赖注入
var map = this.$parent.map || this.$parent.$parent.map

$refs

在 JavaScript 里直接访问一个子组件可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用。

<base-input ref="usernameInput"></base-input>

现在在 JS 里可以通过 this.$refs.usernameInput引用这个组件

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。

<div id="comp">
<base-input ref="usernameInput"></base-input>
</div>
<script>
Vue.component('base-input',template:`<input ref="input">`)
new Vue({
el:'#comp',
...
this.$refs.input.focus()
...
})
</script>

依赖注入 (provide & inject)

使用 $parent property 无法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地,它用到了两个新的实例选项:provideinject

  • provide 选项允许我们指定我们想要提供给后代组件的数据/方法。
  • inject 选项允许接收指定的我们想要添加在这个实例上的 property。
    • from property 是在可用的注入内容中搜索用的 key
    • default property 是降级情况下使用的 value
provide: [object | ()=>object]
inject: Array<String>
// 父级组件提供 'getMap' provide: function () {
return {getMap: this.getMap}
} // 子组件注入 'getMap' inject: ['getMap']
// 用 from 来表示其源 property
const Child = {
inject: {
foo: {from: 'bar',default: () => [1, 2, 3]}
}
}

相比 $parent 来说,这个用法可以让我们在任意后代组件中访问 getMap,而不需要暴露整个 <google-map> 实例。

  • 祖先组件不需要知道哪些后代组件使用它提供的 property
  • 后代组件不需要知道被注入的 property 来自哪里

provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 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: '<div><stack v-if="done"></stack></div>'
})

组件间的循环引用

如果你使用一个模块系统依赖/导入组件,例如通过 webpack 或 Browserify,你会遇到一个错误

<!-- <tree-folder> 组件 -->
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
<!-- <tree-folder-contents> 组件 -->
<ul>
<li v-for="child in children"><tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
  • Vue.component 全局注册组件的时候,这些组件在渲染树中互为对方的后代和祖先。
  • 等到生命周期钩子 beforeCreate 时去注册它
    beforeCreate: function () {
    this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default}
  • 使用 webpack 的异步 import
    components: {TreeFolderContents: () => import('./tree-folder-contents.vue')}

内联模板

当 inline-template 这个特殊的 attribute 出现在一个子组件上时,这个组件将会使用其里面的内容作为模板,而不是将其作为被分发的内容。

<my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
</div>
</my-component>

内联模板需要定义在 Vue 所属的 DOM 元素内。

X-Template

在一个 <script> 元素中,并为其带上 text/x-template 的类型,然后通过一个 id 将模板引用过去。

<script type="text/x-template" id="hello-world-template">
<p>Hello world</p>
</script>
<script>
Vue.component('hello-world', {template: '#hello-world-template'})
</script>

x-template 需要定义在 Vue 所属的 DOM 元素外。

强制更新

$forceUpdate :迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

v-once

有的时候你可能有一个组件,这个组件包含了大量静态内容。在这种情况下,你可以在根元素上添加 v-once attribute 以确保这些内容只计算一次然后缓存起来。

Vue.component('terms-of-service', {
template: `<div v-once><h1>Terms of Service</h1>... a lot of static content ...</div>`
})

可能导致无法正确更新。

最新文章

  1. Java职业生涯规划
  2. OpenStack 企业私有云的若干需求(2):自动扩展(Auto-scaling) 支持
  3. JS系列——Linq to js使用小结
  4. 透过代码理解python的静态方法、类方法与实例方法
  5. 绑定: 与 Element 绑定, 与 Indexer 绑定, TargetNullValue, FallbackValue
  6. pc端,自适应屏幕分辨率
  7. ~0u &gt;&gt; 1
  8. MongoDB的备份与恢复
  9. 4 weekend110的textinputformat对切片规划的源码分析 + 倒排索引的mr实现 + 多个job在同一个main方法中提交
  10. CodeForces 146E - Lucky Subsequence DP+扩展欧几里德求逆元
  11. MFC的GUI窗口使用Console输出函数printf
  12. linux下监视进程 崩溃挂掉后自动重启的shell脚本
  13. Hadoop优先级调度
  14. Unknown/unsupported storage engine: InnoDB
  15. 关于移动端的UI事件分类
  16. JS获得一个对象的所有属性和方法
  17. h5页面适配小结
  18. java中的正则表达式捕获组与引用的概念
  19. Python学习第十篇——函数初步
  20. Django中的csrf基础了解

热门文章

  1. mac下 idea 注释快捷键冲突
  2. 为什么网络I/O会被阻塞?
  3. 将 Timer 对象化
  4. 《STL源码剖析》STL迭代器分类
  5. 图解 Andrew 算法求凸包
  6. 从0开始学习VUE3--01-Vue2与Vue3的不同点
  7. C#移除字符串中的不可见Unicode字符
  8. OpenStack命令行参考
  9. python del 函数
  10. KStudio-Java程序连接KingbaseES数据库异常