nuxt-link
是 nuxtjs 提供的一个组件,基于 vue-router 的 router-link
做了一层封装,实现了预加载的功能。本文来简单聊聊这个“预加载”的原理。
一点准备工作
如果直接打开 nuxt
项目中 nuxt-link的源码,会发现里面使用了很多类似于 <%...%>
这样的模板语法。这其实是 lodash.template 提供的模板语法。如果觉得影响阅读,可以按照 nuxt
的文档,新建一个 nuxt 应用,然后构建一次,再找到 .nuxt/components/nuxt-link.client.js
,这个就是编译过后的 nuxt-link
代码。
前置条件
要想触发 nuxt-link
的预加载,需要同时满足以下几个条件:
-
nuxt-link
的prefetch
属性值是true
。默认值是 nuxt 配置里的
router.prefetchLinks
。prefetchLinks
的默认值是true
,所以prefetch
的默认值也是true
。 -
nuxt-link
的noPrefetch
属性值是false
。默认值是false
。 -
当
nuxt-link
组件出现在可见区域内。 -
nuxt-link
的to
属性指向的页面是同一个应用的页面。 -
有网络链接并且不是
2g
网络。
1、2 两点控制是否启动预加载功能(默认是开启预加载),3、4、5是决定什么时候执行预加载的逻辑。
原理
接下来梳理一下预加载的大致流程。为了说明原理,nuxt-link
源码里有些条件分支会省略,但不影响理解。下面会以 /cart
这个链接作为 nuxt-link
的跳转地址来做说明。/cart
页面对应的文件路径是 /pages/cart/index.vue
。
<nuxt-link to="/cart">购物车</nuxt-link>
事件监听
在 nuxt-link
的 mounted
阶段,如果设置了开启预加载,即 this.prefetch && !this.noPrefetch
,就会开始监听 nuxt-link
元素是否进入了可视区域。
当元素进入了可视区域,就会调用 prefetchLink
方法进行预加载。
监听过程中使用了 window.requestIdleCallback 和 window.IntersectionObserver 这两个 API。
预加载
核心的逻辑在 prefetchLink()
函数里:
prefetchLink () {
if (!this.canPrefetch()) {
return
}
// Stop observing this link (in case of internet connection changes)
observer.unobserve(this.$el)
const Components = this.getPrefetchComponents()
for (const Component of Components) {
const componentOrPromise = Component()
if (componentOrPromise instanceof Promise) {
componentOrPromise.catch(() => {})
}
Component.__prefetched = true
}
}
首先要获取需要预加载的组件。
在 getPrefetchComponents()
方法里,使用了 vue-router
的 router.resolve
方法,找到将要跳转的页面对应的页面入口,即 Components
。
getPrefetchComponents () {
const ref = this.$router.resolve(this.to, this.$route, this.append)
const Components = ref.resolved.matched.map(r => r.components.default)
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
},
在浏览器开发工具里打印一下 Components
,看到以下信息:
可以看到 Components
里的每个元素都是一个函数,点击 [[FunctionLocation]]
的值,router.js
,可以跳到函数定义的位置。然后你就会发现打开的这个 router.js
其实就是我们的项目构建完之后得到的 .nuxt/router.js
。
通过 _30f4c240()
这个函数的定义可以发现它的核心功能其实就是引入了 /cart
对应的页面文件。如果调用这个方法,就会在浏览器开发者工具的 network 面板看到浏览器请求了 /_nuxt/pages/cart/index.js
这个文件,即加载了 /cart
这个页面对应的代码。具体的加载过程属于 webpack
的功能,这里不做说明。
// .nuxt/router.js
const _30f4c240 = () => interopDefault(import('../src/pages/cart/index.vue' /* webpackChunkName: "pages/cart/index" */))
在获取到上面的加载函数之后,prefetchLink
就会调用这些函数,达到预加载的效果。之后,还会设置一个 __prefetch
的标识,防止重复加载。
以上就是 nuxt-link
预加载的大致过程。
延伸
在 nuxt-link
的源码里,还能找到一些有趣的点:
自定义预加载
有些时候我们没有使用 nuxt-link
来做页面跳转,但又想预加载这个页面,就可以这样做:
myPrefetch(path) {
const ref = this.$router.resolve(path, this.$route, false);
const Components = ref.resolved.matched.map((r) => r.components.default);
Components
.filter((Component) => typeof Component === 'function' && !Component.options && !Component.__prefetched)
.forEach((Component) => {
const componentOrPromise = Component();
if (componentOrPromise instanceof Promise) {
componentOrPromise.catch(() => {});
}
Component.__prefetched = true;
});
},
可以使用以下方法简化一下 Components
的获取:
const Components = this.$router.getMatchedComponents(path)
navigator.connection
可以使用 navigator.connection 来判断网络状态,但是要注意兼容性。
quicklink
在 react
项目里,可以使用 quicklink 实现预加载的功能。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!