前端开发社区的繁荣,造就了很多优秀的基于 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 子组件的异步加载及其生命周期控制的更多相关文章

  1. Angular.JS + Require.JS + angular-async-loader 来实现异步加载 angular 模块

    传统的 angular 应用不支持异步加载模块,必须在 module 启动的时候,所有模块必须预加载进来. 通过使用 angular-async-loader 库,我们可以使用 requirejs 等 ...

  2. asp.net C#母版页和内容页事件排版加载顺序生命周期

    asp.net C#母版页和内容页事件排版加载顺序生命周期 关于ASP页面Page_Load发生在事件之前而导致的问题已经喜闻乐见,对于问题的解释也很全面,但是如何解决问题则较少有人说明,我就再 简单 ...

  3. H5+JS+JQuery+ECharts实现异步加载

    这几天,看了一下ECharts官网的API和Demo发现很有意思,于是就利用模型预测产生的数据做一个伪实时的动态数据显示 . 首先,创建一个index.html的文件,我用的vscode打开的,进行编 ...

  4. JS的同步和异步加载

    引言 JS的“加载”不能理解为下载,它是分为两个部分:下载,执行.默认的JS加载是同步的,因为浏览器需要一个稳定的DOM结构,而执行JS时可能会对DOM造成改变,所以在执行JS时一定会阻塞HTML的渲 ...

  5. JS文件延迟和异步加载:defer和async属性

    -般情况下,在文档的 <head> 标签中包含 JavaScript 脚本,或者导入的 JavaScript 文件.这意味着必须等到全部 JavaScript 代码都被加载.解析和执行完以 ...

  6. Vue路由(组件)懒加载(异步)

    传统的引入方式 import test from '@/components/test' { path: '/test', name: '测试页面', component:test }, 懒加载的方式 ...

  7. Vue.js笔记 — vue-router路由懒加载

    用vue.js写单页面应用时,会出现打包后的JavaScript包非常大,影响页面加载,我们可以利用路由的懒加载去优化这个问题,当我们用到某个路由后,才去加载对应的组件,这样就会更加高效,实现代码如下 ...

  8. [js开源组件开发]loading加载效果

    loading加载效果 由于程序和网络的原因,常常我们需要在交互的时候,给用户一个正在加载中的动画,于是,loading组件横空出世.不需要复杂的代码,也能完成大多数业务,这就是我做组件的原则. 效果 ...

  9. jquery.datatable.js与CI整合 异步加载(大数据量处理)

    http://blog.csdn.net/kingsix7/article/details/38928685 1.CI 控制器添加方法 $this->show_fields_array=arra ...

随机推荐

  1. zookeeper 介绍与集群安装

    zookeeper 介绍 ZooKeeper是一个分布式开源框架,提供了协调分布式应用的基本服务,它向外部应用暴露一组通用服务——分布式同步(Distributed Synchronization). ...

  2. Nlog日志组件简介

    NLog简介 NLog是一个简单灵活的.NET日志记录类库,NLog的API非常类似于log4net,配置方式非常简单.支持多种形式输出日志:文本文件.系统日志.数据库.控制台.邮箱等 1.NLog简 ...

  3. 清理sql2012数据库日志

    --1.先把数据库设置为简单模式(右击数据库名->点'属性'->点'选项'->恢复模式改成'简单'->点'确定'按钮,--2.再执行下面的语句(或者右击数据库点'任务'-> ...

  4. Ubuntu 开启SSH服务以及有关设置:安装,指定端口号、免密登录、远程拷贝

    本文所用系统为 Ubuntu 18.04   什么是SSH?     简单说,SSH是一种网络协议,用于计算机之间的加密登录.全名为:安全外壳协议.为Secure Shell的缩写.SSH为建立在应用 ...

  5. TIMESTAMP使用遇到得麻烦

    mysql按日期查询报空,怎么查看日志发现是14:36:01.709(Timestamp), 参数出了问题 2018-04-17 14:36:16,887 [http-nio-8080-exec-5] ...

  6. impala系列: 字符串函数

    --=======================常用字符串函数--=======================base64decode(string str) : base64 解码.base64 ...

  7. Linux下的解压命令

    Linux下常见的压缩包格式有5种:zip tar.gz tar.bz2 tar.xz tar.Z 其中tar是种打包格式,gz和bz2等后缀才是指代压缩方式:gzip和bzip2 filename. ...

  8. java7 java MethodHandle解析

    简介 JDK6之前我们会使用java反射来实现动态方法调用,多数框架用反射的比较多,例如mybatis.spring等.在JDK7中,新增了java.lang.invoke.MethodHandle( ...

  9. 五、文件IO——dup 函数

    5.1 dup 函数---复制文件描述符 5.1.1 简单cat实现及输入输出重定向 io.c #include <sys/types.h> #include <sys/stat.h ...

  10. 嵌入式-迅为iTOP-4418开发板Flash空间问题

    我的4418开发板 是4G版本 16G存储空间的.u-boot和文件系统.内核都是光盘自带的,进入linux系统之后 我使用df -h命令看到的存储空间不对,我用U盘做了测试:u盘里面放1G的内容往a ...