赞
踩
我们打包一下上一篇的项目
yarn build
生成dist
目录
跑起来
npx http-server ./dist -c-l
这块代码小迪就不上传了,大家自己clone下来上一篇最新的代码,自己打包吧!
大体没啥问题,我们看一下加载的问题
我们看一下打包文件下的js
,有一个chunk
开头的文件,其实它是一些公共库(如:vue
、vue-router
等),app
其实是打包我们所写的js
文件了。
我们刚刚写的三个页面Home
、About
、Detail
页面最终会转成js
组件,但是会打包成一个js
文件。即所有代码均在js
下的app
文件中。
我们可以想象一下,如果页面很多的话,把所有的页面都打包到一起,致使这个js
文件非常庞大。非常不利用我们首屏加载速度,第一次打开的时候会非常卡顿,这个时候就需要我们本篇所学的东西了,路由懒加载
。
它其实就是按需加载,我们只有访问了这个路由(组件)以后才会加载,而不是一上来全部加载完,用import
解决,它是我们浏览器中新增的一个东西,称为动态加载
,也即懒加载
。
它其实还有一个对应的东西,叫做静态加载
。
平时开发时候,我们在文件头部或script
标签下的头部用import
导入其他库或者文件
lazyImport\js\a.js
import b from './b.js';
console.log(b);
lazyImport\js\b.js
export default 100;
lazyImport\1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="module" src="./js/a.js"></script>
</body>
</html>
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.35
Branch: branch07commit description:a2.35(example01-1——原生js常规静态导入)
tag:a2.35
以上直接引用没有任何问题,我们试试点击之后再加载b
\lazyImport\js\a.js
import b from './b.js';
// console.log(b);
document.onclick = async function() {
console.log(b);
};
实际上这种语法静态加载 => 不能根据需求动态地去加载,而是import
先写好。
我们其实很容易发现,页面刷新的时候,其实就把a、b
文件都加载了,如果项目庞大,我们加载上万个文件,这首屏显示慢到炸了,用户最多等几秒可能就不想再看你写的网页了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.36
Branch: branch07commit description:a2.36(example01-2——原生js常规静态导入-点击之后加载b)
tag:a2.36
我们正常开发时为了更好的用户体验,通常需要懒加载的 ,即按需加载。
document.onclick = async function() {
import b from './b.js';
console.log(b)
};
静态加载实现不了懒加载,点击以后报错了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.37
Branch: branch07commit description:a2.37(example01-3——静态加载无法按需引入)
tag:a2.37
实际上静态加载不太好,管你用不用,直接全部加载进来,试想象一下,如果引入上百万多个文件,这首屏渲染不卡爆了,估计用户在看这个网页的时候,都想把显示器砸了。
那我们利用原生js
实现一下懒加载。
懒加载就没必要再用type
属性了,因为它并不通过静态加载的import
语法了,而是通过import
函数实现加载
lazyImport\2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="./js/a.js"></script>
</body>
</html>
\lazyImport\js\a.js
document.onclick = function() {
import('./b.js')
};
一上来,network
没有b.js
,点击以后才会b.js
,并且没有报错。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.38
Branch: branch07commit description:a2.38(example02-1——原生js实现懒加载)
tag:a2.38
并且import
函数的返回值一个promise
,我们不用then
方法了,可以把函数变为async
=> 打印一下返回的对象
document.onclick = async function() {
// console.log(b);
let result = await import('./b.js');
console.log(result)
};
得到的是一个Moudle
对象,它底下有一个default
属性,它就是导入的默认值。
如果不是通过默认值导出,而是导出一个对象,则可以通过obj
属性获取了。
\lazyImport\js\b.js
export default 100;
export let obj = {
x: 1,
y: 2
}
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.39
Branch: branch07commit description:a2.39(example02-2——原生js实现懒加载-获取导出对象和默认值)
tag:a2.39
以上就是原生js的模块懒加载方式了。
当打包构建应用时,JavaScript
包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
import Vue from 'vue' import Router from 'vue-router' import NProgress from 'nprogress' import 'nprogress/nprogress.css' // import Home from './views/Home.vue' // import About from './views/About.vue' // import Item from './views/Item.vue' // import User from './views/User.vue' // import Profile from './views/User/Profile' // import Cart from './views/User/Cart' // import BookChoose from './views/Book/BookChoose' // import BookBoy from './views/Book/BookBoy' // import BookGirl from './views/Book/BookGirl' Vue.use(Router) const router = new Router({ mode: 'history', base: process.env.BASE_URL, scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }, routes: [ { path: '/', name: 'home', component: () => import('./views/Home.vue') }, { path: '/about', name: 'about', component: () => import('./views/About.vue') }, { path: '/item/:itemId', name: 'item', component: () => import('./views/Item.vue'), props: r => ({ itemId: Number(r.params.itemId) }) }, { path: '/user', component: () => import(/* webpackChunkName: "user" */ './views/User.vue'), meta: { requiresAuth: true }, children: [ { path: '', name: 'user', component: () => import(/* webpackChunkName: "user" */ './views/User/Profile.vue') }, { path: 'cart', name: 'user-cart', component: () => import(/* webpackChunkName: "user" */ './views/User/Cart.vue') } ] }, { path: '/book', name: 'book', // redirect: { name: 'book-choose' } redirect: to => { let type = localStorage.getItem('book-type') return { name: type || 'book-choose' } } }, { path: '/book-choose', name: 'book-choose', component: () => import(/* webpackChunkName: "book" */ './views/Book/BookChoose.vue') }, { path: '/book-boy', name: 'book-boy', component: () => import(/* webpackChunkName: "book" */ './views/Book/BookBoy.vue') }, { path: '/book-girl', name: 'book-girl', component: () => import(/* webpackChunkName: "book" */ './views/Book/BookGirl.vue') } ] }) router.beforeEach((to, from, next) => { NProgress.start() if (to.matched.some(record => record.meta.requiresAuth) && !isLogin) { next({ name: 'login', }) } else { next() } next() }) router.afterEach((to, from, next) => { NProgress.done() }) export default router
路由懒加载
=>
当打包构建应用时,JavaScript
包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合Vue
的异步组件和 Webpack
的代码分割功能,轻松实现路由组件的懒加载。
首先,可以将异步组件定义为返回一个Promise
的工厂函数 (该函数返回的Promise
应该resolve
组件本身):
const Foo = () => Promise.resolve({ /* 组件定义对象 */ })
第二,在Webpack 2
中,我们可以使用动态 import语法来定义代码分块点 (split point
):
import('./Foo.vue') // 返回 Promise
注意
如果您使用的是 Babel,你将需要添加
syntax-dynamic-import
插件,才能使 Babel 可以正确地解析语法。
结合这两者,这就是如何定义一个能够被Webpack
自动代码分割的异步组件。
const Foo = () => import('./Foo.vue')
在路由配置中什么都不需要改变,只需要像往常一样使用 Foo
:
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
回过头,我们看vue
=>
注意 import
是一个函数,不是对象,把这个函数赋给当前的组件,这个函数来加载组件。
它现在是加载的函数给了当前的组件,而不是静态加载,一下就加载了。
而是当我们访问路由的时候,这个加载函数才会执行。 => 动态记载路由
const Home = function() {
return import('@/views/Home');
};
可以箭头函数简写:
const Home = () => import('@/views/Home');
const About = () => import('@/views/About');
const Detail = () => import('@/views/Detail');
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a2.40
Branch: branch07commit description:a2.40(路由懒加载)
tag:a2.40
再打包
yarn build
我们发现打包后生成的文件也不一样了,一个import
引入就为我们生成一个js
文件了。
再去访问
npx http-server ./dist -c-l
但是感觉一上来还是全部加载了,其实本质上它不会影响我们懒加载的,
我们看一下打包的index.html
能发现它是通过link
标签去加载的,而且有一个rel属性,值为preload
=>
预加载
,而不是真实加载,它不会阻塞和影响当前我们的整个页面的后续渲染。
我们首屏渲染的目的是,如果你用script
标签的话,多少肯定会影响首屏渲染的性能,这种方式就可以减少首屏渲染加载的时间了,它是一种预处理。
我们点击对应组件它才会真正去加载
我们最开始加载的时候,我们看到他大小都是0kb
,只有点击之后,才能正在加载,才有了大小。
除了加快首屏渲染,它还有一个特性即缓存特性,我们下一次调用的时候直接去缓存取了,不会重复加载,所以速度会提高很多。
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk
) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name
(需要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
Webpack
会将任何一个异步模块与相同的块名称组合到相同的异步块中。
import
里加一个注释 =>
默认情况下是一个import
函数打一个包,但有的时候不希望这么做。
如现在user
底下有很多组件,但是呢?
user
底下想打成一个js
包,不要分开那么多,我们可以让其在打包过程中加如下注释,把如下三个组件webpackChunkName
起一样的名称即可,这样就把三个组件相关的代码打包到user
的js
里了,即合并到一块。
component: () => import(/* webpackChunkName: "user" */ './views/User/Profile.vue')
当然除了以上加载外,还可以在组件当中使用它。
比如在Home
当中有一个page
,在page
中也可以进行懒加载
components: {
Page: () => import('@/components/Page')
},
考虑到在blog中不好体现代码更改的位置,小迪才用github托管代码,大家可以查看github,看到详细版本修改过程,搭配博客学习。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。