Vue.js 子组件的异步加载及其生命周期控制
前端开发社区的繁荣,造就了很多优秀的基于 MVVM 设计模式的框架,而组件化开发思想也越来越深入人心。这其中不得不提到 Vue.js 这个专注于 VM 层的框架。
本文主要对 Vue.js 组件化开发中子组件的异步加载和其生命周期进行一些探讨。阅读本文需要对 Vue.js 有一定的了解。
注意:本文中的一些例子代码,是以 vue-cli 采用 webpack 模板初始化的项目为基础。
异步组件
讨论异步加载,需要先了解下异步组件。Vue.js 的异步组件是把组件定义为一个工厂函数,在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。例如注册一个全局异步组件:
Vue.component('async-demo', function(resolve, reject) {
setTimeout(function() {
// 将组件定义传入 resolve 回调函数
resolve({
template: '<div>I am async!</div>'
// 组件的其他选项
})
}, 1000)
})
异步子组件和全局注册很类似:
Vue.component('parent-demo', {
// 父组件的其他选项
components: {
'async-demo': function(resolve, reject) {
setTimeout(function() {
// 将组件定义传入 resolve 回调函数
resolve({
template: '<div>I am async!</div>'
// 子组件的其他选项
})
}, 1000)
}
}
})
工厂函数的第一个参数 resolve
为成功后的回调,第二个参数 reject
为失败后的回调,可以在这里提示用户加载失败等。
这里使用 setTimeout
只是为了模拟异步,在实际项目中,应该配合 webpack 的代码分离功能来实现异步加载。
异步加载
在实际的项目中,如果不使用异步加载,则 Vue.js 组件的 JS、CSS 和模板都会打包到一个 .js 文件中,这个文件可能达到几 MB 甚至更多,严重影响首屏加载时间。所以在项目中我们需要启用组件的异步加载。
webpack 代码分离
webpack 的代码分离有两种,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案的 import()
语法。第二种,则是使用 webpack 特定的 require.ensure
。让我们先看看第一种:
import() 调用会在内部用到 promises。如果在旧有版本浏览器中使用 import(),记得使用一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise。
Vue.component(
'async-demo',
// 该 import 函数返回一个 Promise 对象。
() => import('./async-demo')
)
上面的例子中,前文提到的工厂函数支持返回一个 Promise 对象,所以可以使用 import()
这种代码分离方式。
局部注册也是类似的:
Vue.component('parent-demo', {
// 父组件的其他选项
components: {
'async-demo': () => import('./async-demo')
}
})
本质上,import()
函数返回一个 Promise 实例,你可以自定义这个过程,下文会有说明。
第二种 webpack 代码分离是这样的:
Vue.component('async-demo', function(resolve) {
require.ensure([], function(require) {
resolve(require('./async-demo'))
}, function(error) {
// 加载出错执行这里
})
})
看起来比较繁琐,如果你使用 webpack 2 及以上版本,则不建议使用第二种方式。
生命周期控制
在使用子组件(或者叫局部注册)时,我们可能需要在子组件实例化(或者叫创建完毕)后做某些事情。在非异步的子组件中,我们很容易做这件事:
<template>
<div>
<my-demo ref="demo"></my-demo>
</div>
</template>
<script>
import Demo from './Demo'
export default {
mounted() {
// 在这里可以通过组件的 $refs 获取到子组件的实例
// 可以认为,在这里子组件实例化完毕
console.log(this.$refs.demo)
},
components: {
MyDemo: Demo
}
}
</script>
上例中使用了 Vue.js 的子组件引用,所以可以在生命周期函数 mounted
中很方便的获取到子组件的实例,这样就可以在这个函数中处理一些子组件实例化后要做的事情。
但是在异步子组件中,mounted
函数中是无法获取到子组件的实例的,所以我们需要一些技巧来实现这个功能。
<template>
<div>
<my-demo ref="demo"></my-demo>
</div>
</template>
<script>
export default {
components: {
MyDemo: () => import('./Demo').then(component => {
// 清理已缓存的组件定义
component.default._Ctor = {}
if (!component.default.attached) {
// 保存原组件中的 created 生命周期函数
component.default.backupCreated = component.default.created
}
// 注入一个特殊的 created 生命周期函数
component.default.created = function() {
// 子组件已经实例化完毕
// this 即为子组件 vm 实例
console.log(this)
if (component.default.backupCreated) {
// 执行原组件中的 created 生命周期函数
component.default.backupCreated.call(this)
}
}
// 表示已经注入过了
component.default.attached = true
return component
})
}
}
</script>
上例中,可以看到我们对组件异步加载做了一些特殊的控制,其中 import().then()
则是在加载完子组件的 .js 文件后,实例化子组件之前的回调,如果需要处理出错的情况,则 import().then().catch()
即可。
以上代码只是注入了一个 created
函数,如果要注入其他生命周期函数,例如 mounted
,也是类似的:
<template>
<div>
<my-demo ref="demo"></my-demo>
</div>
</template>
<script>
export default {
components: {
MyDemo: () => import('./Demo').then(component => {
component.default._Ctor = {}
if (!component.default.attached) {
component.default.backupMounted = component.default.mounted
}
component.default.mounted = function() {
if (component.default.backupMounted) {
component.default.backupMounted.call(this)
}
}
component.default.attached = true
return component
})
}
}
</script>
通过上面的讨论,我们可以做到完全控制 Vue.js 组件的异步加载的全过程,这对于需要精确控制子组件加载的组件,会有很大的帮助。
演示项目
根据上面的思路,写了一个基于 Bootstrap 的异步弹窗演示项目:
https://github.com/hex-ci/vue-async-bootstrap-modal-demo
Vue.js 子组件的异步加载及其生命周期控制的更多相关文章
- Angular.JS + Require.JS + angular-async-loader 来实现异步加载 angular 模块
传统的 angular 应用不支持异步加载模块,必须在 module 启动的时候,所有模块必须预加载进来. 通过使用 angular-async-loader 库,我们可以使用 requirejs 等 ...
- asp.net C#母版页和内容页事件排版加载顺序生命周期
asp.net C#母版页和内容页事件排版加载顺序生命周期 关于ASP页面Page_Load发生在事件之前而导致的问题已经喜闻乐见,对于问题的解释也很全面,但是如何解决问题则较少有人说明,我就再 简单 ...
- H5+JS+JQuery+ECharts实现异步加载
这几天,看了一下ECharts官网的API和Demo发现很有意思,于是就利用模型预测产生的数据做一个伪实时的动态数据显示 . 首先,创建一个index.html的文件,我用的vscode打开的,进行编 ...
- JS的同步和异步加载
引言 JS的“加载”不能理解为下载,它是分为两个部分:下载,执行.默认的JS加载是同步的,因为浏览器需要一个稳定的DOM结构,而执行JS时可能会对DOM造成改变,所以在执行JS时一定会阻塞HTML的渲 ...
- JS文件延迟和异步加载:defer和async属性
-般情况下,在文档的 <head> 标签中包含 JavaScript 脚本,或者导入的 JavaScript 文件.这意味着必须等到全部 JavaScript 代码都被加载.解析和执行完以 ...
- Vue路由(组件)懒加载(异步)
传统的引入方式 import test from '@/components/test' { path: '/test', name: '测试页面', component:test }, 懒加载的方式 ...
- Vue.js笔记 — vue-router路由懒加载
用vue.js写单页面应用时,会出现打包后的JavaScript包非常大,影响页面加载,我们可以利用路由的懒加载去优化这个问题,当我们用到某个路由后,才去加载对应的组件,这样就会更加高效,实现代码如下 ...
- [js开源组件开发]loading加载效果
loading加载效果 由于程序和网络的原因,常常我们需要在交互的时候,给用户一个正在加载中的动画,于是,loading组件横空出世.不需要复杂的代码,也能完成大多数业务,这就是我做组件的原则. 效果 ...
- jquery.datatable.js与CI整合 异步加载(大数据量处理)
http://blog.csdn.net/kingsix7/article/details/38928685 1.CI 控制器添加方法 $this->show_fields_array=arra ...
随机推荐
- Linux_查找文件
1.查找文件 (1)通过文件名查找 find / -name jdbc.properties (2)根据部分文件名查找 find /etc -name *srm* find /etc -name sr ...
- urllib 学习一
说明:Urllib 是一个python用于操作URL的模块 python2.x ----> Urillib/Urllib2 python3.x ----> Urllib ...
- 固定footer在底部
作者:李宇链接:https://www.zhihu.com/question/23220983/answer/25880123来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...
- POJ - 2528 Mayor's posters (离散化+线段树区间修改)
https://cn.vjudge.net/problem/POJ-2528 题意 给定一些海报,可能相互重叠,告诉你每个海报的宽度(高度都一样的)和先后叠放顺序,问没有被完全盖住的有多少张? 分析 ...
- ZOJ - 3450 Doraemon's Railgun (dp)
https://vjudge.net/problem/ZOJ-3450 题意 一座位落(X0,Y0)的城市将遭受n个敌人的摧残.现在我们手上有某科学的超电磁炮,每次攻击都是一条射线,对于共线的敌人,必 ...
- vue中slot插槽
插槽就是vue实现的一套内容分发的API,将插槽元素作为承载分发内容的出口. 也就是说在组件模板中默认占用一个位置,当使用组件标签时候,组件标签的内容就会自动替换掉内容 slot中可以设置一些默认的内 ...
- Visio2016设置几个结构一样大小
1.点出视图,任务窗格,大小和位置 2.选择几个结构,在动作窗口上设置即可
- linux 下shell脚本备份文件
以下是shell自动备份用的: 主要功能: 1)将pathSrc目录中的文件拷贝到pathDst目录中去. 具体步骤:先查询源目录和目标目录中的文件,分别存在fileSrc和fileDst中. -&g ...
- idea JRebe插件激活方法
具体方法请看创始人博客及github
- 使用Nginx在windows和linux上搭建集群
Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器 特点:反向代理 负载均衡 动静分离… 反向代理(Reverse Pro ...