vue异步组件与按需加载

应用场景

由于最近项目的需要,接触了vue的按需加载,也就是我们常说的vue异步组件,尤其是项目比较庞大的情况下,使用异步组件实现按需加载是非常重要的,假如我们不做按需加载优化,那么工程最后统统都打包到固定的几个bundle.js里,当我们去访问某个页面时,可能仅仅使用到一个组件,但是会将所有打包后的代码统统进行加载,这样就大大增加了加载成本,假如加载的这个js非常大,那么网页的白屏时间 ( js文件是阻塞加载的 ) 。
基于上面所说的情况,我们就可以考虑在访问某个页面时,只加载这个页面时用到的组件代码,这样的话就不必再把整体打包的bundle.js都加载完,从而大大提升加载效率。
就像图片的懒加载一样,如果客户根本就没有看到那些图片,而我们却在打开页面的时候全部给加载完了,这样会大大的增加请求的时间,降低用户的体验程度。懒加载在很多的网站都有用到,比如淘宝、京东等等这样的购物网站,上面的图片链接等等都很多,如果你把滚轴迅速的往下拉的时候,你可能会看到图片加载的情况。

按需加载的方式

首先先来讲讲按需加载的方式,主要有下面几种方法:
1. require.ensure()
require.ensure()是webpack自带的api,它的作用就是对代码模块进行按需加载,也可以理解为代码分割,异步加载,平时我们导入一个模块时通常是这么做的:

import {MyComponent} from '../src/components/myComponent'

require.ensure()是这么做的:

const MyComponent = r => require.ensure([], () => r(require('../src/components/myComponent'))) 

然而require.ensure是怎么实现按需加载的呢?其实说白了,它就是把js模块给独立打包出一个js文件的,然后使用这个模块的时候,webpack会构造script dom元素,由浏览器发起异步请求这个js文件。
我们可以构想一个场景,比如说我们需要加载一个公共库js,比如高德地图的js(后面简称gaode.js),但是我们并不是已进入页面就用到它,假如进入页面就直接全部加载的话,这完全就是一种带宽上的浪费,同时也降低了我们网页的体验性。假设地图组件是在首屏以下的地方进行显示,也就是说网页首次加载我们并看不到地图,只有scroll到页面下面才会看到地图。这种情况下,我们就需要考虑在需要用户看到地图的时候再去加载gaode.js。
解决方案1:
思路是监听document.documentElement的scroll事件,当充当地图容器的div出现在浏览器可视区域时,我们去异步加载gaode.js:

//地图容器
const wrap = document.getElementById('#map-wrap')
document.documentElement.onscroll = function () {
	const topDistance = wrap.offsetTop
	const delta = window.innerWidth + document.documentElement.scrollTop - topDistance
	//delata > 0表示地图容器进入可视区域
	if (delta > 0) {
		const script = document.createElement('script')
		const head = document.getElementsByTagName('head')[0]
		script.async = true
		script.src = 'gaode.js'
		head.appendChild(script)
	}
}

如果考虑到性能问题,可以增加一个节流函数。
当head标签中插入< script >节点后,就会向src中的url发送请求( script不受同源策略限制 ),我们可以为< script >增加一个回调函数,当请求的数据成功拿到后,再进行对地图相关内容的操作。
解决方案2:
利用require.ensure:

//地图容器
const wrap = document.getElementById('#map-wrap')
document.documentElement.onscroll = function () {
	const topDistance = wrap.offsetTop
	const delta = window.innerWidth + document.documentElement.scrollTop - topDistance
	//delata > 0表示地图容器进入可视区域
	if (delta > 0) {
		require.ensure([], () => {
			const AMap = require('gaode.js')
			//用AMap去做你想做的事情
			//...
		})
	}
}

感觉就像是在写同步代码一样,实际上是异步加载的,某种意义上有点儿类似于async / await?
使用require.ensure进行require的代码会在打包的时候分离出单独的一个js文件,使用到的时候才会去加载这个js文件,当然有一种特殊情况,比如如下代码:

//地图容器
const AMap = require('a.js')

const wrap = document.getElementById('#map-wrap')
document.documentElement.onscroll = function () {
	const topDistance = wrap.offsetTop
	const delta = window.innerWidth + document.documentElement.scrollTop - topDistance
	//delata > 0表示地图容器进入可视区域
	if (delta > 0) {
		require.ensure([], () => {
			const AMap = require('gaode.js') //单独打包出一个js文件
			//用AMap去做你想做的事情
			//...
			const a = require('a.js') //不会独立出去,因为它已经加载到模块缓存中了
		})
	}
}

'a.js’被require了两次,但是在最上面的require已经生成了文件模块的克隆对象,并生成了模块缓存,再次require同一个文件模块会直接从模块缓存取值,因此不会将a.js单独打包出去!!
也就是说,require.ensure中的require只会将没有require过的文件模块打包成一个独立的js文件。
细心的的你可能注意到,require.ensure还有一个参数,也就是第一个参数 [],那么这个参数是干什么的呢?这个参数主要是用来声明require.ensure第二个参数回调里,require的异步模块又依赖其他的异步模块,而这些被依赖的模块声明就是放在第一个参数数组里的,打个比方,我们想在require.ensure里加载b.js,而b.js里需要先加载a.js,因此需要先异步加载a.js,把a.js放到第一个参数里就可以了,代码:

require.ensure(['a.js'], () => {
	const b = require('b.js') //异步加载b.js,并独立打包,b.js依赖a.js
})

但是a.js和b.js会打包成同一个js文件!!这是需要注意的一点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值