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 ...
随机推荐
- JAVA核心技术I---JAVA基础知识(二进制文件读写和zip文件读写)
一:二进制文件读写 (一)写文件 –先创建文件,写入数据,关闭文件 –FileOutputStream, BufferedOutputStream,DataOutputStream –DataOutp ...
- VMware 无法打开内核设备 \\.\Global\vmx86
无法打开内核设备 \\.\Global\vmx86: 系统找不到指定的文件.你想要在安装 VMware Workstation 前重启吗? vmware 安装完成后,打开现有虚拟系统时,报错. 无法打 ...
- Sublime Text 3 Mac常用快捷键与注意事项
大多数情况下容易忘记的快捷键,在此整理了一下. 编辑快捷键:cmd+L:选择行(重复按下将下一行加入选择):cmd+D:选择词(重复按下时多重选择相同的词进行多重编辑):cmd+shift+D 复制光 ...
- Android开发入门经典实例
开发实例概述 今天带大家做一个简单的Android App,这个App会显示创新工程实践老师们的照片和信息,不妨先看一看效果: 虽然这个App非常简单,但是涉及到了Android开发中的一些关键知识, ...
- eDEX-UI
eDEX-UI A science fiction terminal emulator disigned for large touchscreen that runs on all major OS ...
- es6 javascript对象方法Object.assign() 对象的合并复制等
Object.assign方法用于对象的合并,将源对象( source )的所有可枚举属性,复制到目标对象( target ). 详细使用稳步到前辈: http://blog.csdn.net/qq_ ...
- JDK8新特性04 方法引用与构造器引用
import java.io.PrintStream; import java.util.Comparator; import java.util.function.*; /** * 一.方法引用 * ...
- Spring整合redis配置文件详解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- JVM学习(一)简介
一.java程序编译到运行大概流程 1.Source Code Files为.java文件 2.通过编译产生可执行的字节码. 3.通过jvm得到机器可以执行的机器码 4.操作系统运行机器码,并与硬件进 ...
- UE4源码笔记
找编辑器LOG,找相应代码.(改相应LOG 重编译后有反应)GenerateProjectFiles 寻找配置,生成VS文件. 有一些小工具项目默认是没打开的.API宏是较旧的代码,新的代码会设计 ...