单页应用其一个问题是首屏屏渲染速度较慢。这是由于页面首次加载时服务器将向客户端发送大量JavaScript,在屏幕上显示任何内容以前必须下载并解析。能够想象,随着应用程序规模的扩大,这个问题对用户体验的影响也会愈来愈突出。css
如今幸运的是,当使用Vue CLI构建Vue应用程序时(使用webpack),能够采起一些措施来抵消这种状况。在本文中,我将演示如何在应用程序的初始渲染以后使用异步组件
和webpack的代码分割功能加载到页面的某些部分。这将使初始加载时间降至最低,并为您的应用程序提供更好的用户体验。vue
在咱们开始建立异步组件以前,让咱们看一下咱们一般如何加载组件。为此,咱们将使用一个很是简单的消息组件做为例子:webpack
<!-- Message.vue -->
<template>
<h1>New message!</h1>
</template>
复制代码
如今咱们已经建立了咱们的Message
组件,让咱们将它加载到咱们的文件中并显示它。咱们能够导入组件并将其添加到组件选项中,以便咱们能够在模板中使用它:git
<!-- App.vue -->
<template>
<div>
<message></message>
</div>
</template>
<script>
import Message from "./Message";
export default {
components: {
Message
}
};
</script>
复制代码
但如今发生了什么?只要应用程序加载,就会加载Message
组件,所以它包含在初始加载过程当中。github
对于一个简单的应用程序来讲,这看起来可能不是一个大问题,但能够考虑像电商网站这样复杂的场景。想象一下,用户将项目添加到购物车,而后想要结账,所以单击结账按钮会生成一个包含所选商品的全部详细信息的框。使用上述方法,此结账框将包含在初始包中,但咱们只需在用户单击结账按钮时使用该组件。用户甚至能够在不点击结帐按钮的状况下浏览网站,这意味着在加载这个可能不会使用的组件时浪费资源是没有意义的。web
为了提升应用程序的效率,咱们能够结合延迟加载和代码分割技术。npm
Webpack提供的代码拆分功能容许您将代码拆分为各类捆绑包,而后能够按需加载或稍后并行加载。它只能在须要或使用时加载特定的代码片断。数组
Vue使用Dynamic Imports解决这种状况。此功能引入了一种新的相似函数的导入形式,它将返回包含(Vue)组件的Promise。因为import是一个接收字符串的函数,咱们能够作一些强大的事情,好比使用表达式加载模块。自版本61以来,Chrome中已提供动态导入。有关这些内容的详细信息,请访问Google Developers网站。bash
代码拆分由webpack,Rollup或Parcel等捆绑器处理,它们解析动态导入语法并为每一个动态导入的模块建立单独的文件。稍后咱们将在控制台的网络选项卡中看到这一点。但首先,咱们来看看静态和动态导入之间的区别:服务器
// static import
import Message from "./Message";
// dynamic import
import("./Message").then(Message => {
// Message module is available here...
});
复制代码
如今,让咱们将这些知识应用到咱们的Message
组件中,咱们将获得一个以下所示的组件:App.vue
<!-- App.vue -->
<template>
<div>
<message></message>
</div>
</template>
<script>
import Message from "./Message";
export default {
components: {
Message: () => import("./Message")
}
};
</script>
复制代码
如你所见,函数import()将解析返回组件的Promise,这意味着咱们已成功异步加载组件。若是您查看devtools
的network
选项卡,您会注意到一个名为0.js
包含异步组件的文件。
如今咱们已经掌握了异步组件,让咱们仅在真正须要时加载它们。在本文的上一节中,我解释了仅在用户点击结账按钮时才加载的结账框的用例。让咱们把它构建出来。
若是您没有安装vue/cli
,首先应该安装它:
npm i -g @vue/cli
复制代码
接下来,使用CLI建立新项目,在出现提示时选择默认预设便可:
vue create my-store
复制代码
转到项目目录,而后安装咱们将用于样式的ant-design-vue库:
cd my-store
npm i ant-design-vue
复制代码
接下来,导入Ant设计库:src/main.js
import 'ant-design-vue/dist/antd.css'
复制代码
最后咱们在src/comonents
里建立两个新组件Checkout.vue
和Items.vue
:
touch src/components/{Checkout.vue,Items.vue}
复制代码
打开src/App.vue
并用如下代码替换文件里代码:
<template>
<div id="app">
<h1>{{ msg }}</h1>
<items></items>
</div>
</template>
<script>
import items from "./components/Items"
export default {
components: {
items
},
name: 'app',
data () {
return {
msg: 'My Fancy T-Shirt Store'
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
复制代码
这里没有什么花哨的东西。咱们所作的只是显示一条消息并渲染一个<items>
组件。
接下来,打开src/components/Items.vue
并添加如下代码:
<template>
<div>
<div style="padding: 20px;">
<Row :gutter="16">
<Col :span="24" style="padding:5px">
<Icon type="shopping-cart" style="margin-right:5px"/>{{shoppingList.length}} item(s)
<Button @click="show = true" id="checkout">Checkout</Button>
</Col>
</Row>
</div>
<div v-if="show">
<Row :gutter="16" style="margin:0 400px 50px 400px">
<checkout v-bind:shoppingList="shoppingList"></checkout>
</Row>
</div>
<div style="background-color: #ececec; padding: 20px;">
<Row :gutter="16">
<Col :span="6" v-for="(item, key) in items" v-bind:key="key" style="padding:5px">
<Card v-bind:title="item.msg" v-bind:key="key">
<Button type="primary" @click="addItem(key)">Buy ${{item.price}}</Button>
</Card>
</Col>
</Row>
</div>
</div>
</template>
<script>
import { Card, Col, Row, Button, Icon } from 'ant-design-vue';
export default {
methods: {
addItem (key) {
if(!this.shoppingList.includes(key)) {
this.shoppingList.push(key);
}
}
},
components: {
Card, Col, Row, Button, Icon,
checkout: () => import('./Checkout')
},
data: () => ({
items: [
{ msg: 'First Product', price: 9.99 },
{ msg: 'Second Product', price: 19.99 },
{ msg: 'Third Product', price: 15.00 },
{ msg: 'Fancy Shirt', price: 137.00 },
{ msg: 'More Fancy', price: 109.99 },
{ msg: 'Extreme', price: 3.00 },
{ msg: 'Super Shirt', price: 109.99 },
{ msg: 'Epic Shirt', price: 3.00 },
],
shoppingList: [],
show: false
})
}
</script>
<style>
#checkout {
background-color:#e55242;
color:white;
margin-left: 10px;
}
</style>
复制代码
在此文件中,咱们显示一个带有商品数量的购物车图标。商品是从items
数组中提取的。若是单击项目的Buy
按钮,将会调用addItem
方法,该方法会将相关商品push到shoppingList
数组中。从而增长购物车的总数。
咱们还在页面中添加了一个Checkout
按钮:
<Button @click="show = true" id="checkout">Checkout</Button>
复制代码
当用户点击这个按钮,咱们设置的参数show
是true
。true
是很是重要对于有经过条件地加载咱们的异步组件。
在接下来的几行中,您能够找到v-if
的声明,这个语句仅用来显示咱们checkout
组件的<div>
,可是咱们只想在用户点击Checkout
按钮时显示结帐组件,咱们该怎么办?
这里咱们将checkout
组件在components
选项里异步加载。这里v-bind
将参数传递给组件。正如你看的的这样,建立条件异步组件是很容易的:
<div v-if="show">
<checkout v-bind:shoppingList="shoppingList"></checkout>
</div>
复制代码
让咱们快速Checkout
组件添加下面的代码在src/components/Checkout.vue
里:
<template>
<Card title="Checkout Items" key="checkout">
<p v-for="(k, i) in this.shoppingList" :key="i">
Item: {{items[Number(k)].msg}} for ${{items[Number(k)].price}}
</p>
</Card>
</template>
<script>
import { Card } from 'ant-design-vue';
export default {
props: ['shoppingList'],
components: {
Card
},
data: () => ({
items: [
{ msg: 'First Product', price: 9.99 },
{ msg: 'Second Product', price: 19.99 },
{ msg: 'Third Product', price: 15.00 },
{ msg: 'Fancy Shirt', price: 137.00 },
{ msg: 'More Fancy', price: 109.99 },
{ msg: 'Extreme', price: 3.00 },
{ msg: 'Super Shirt', price: 109.99 },
{ msg: 'Epic Shirt', price: 3.00 },
]
})
}
</script>
复制代码
在这里,咱们将接收一个shoppingList
并把他输出到屏幕上。
您可使用该npm run serve
命令运行该应用程序。而后导航到http:// localhost:8080
。若是一切按计划进行,你应该会看到以下图所示的内容。
能够尝试打开在network
选项卡,点击Checkout按钮,能够发现network
里将异步加载结帐组件
。
您还能够在GitHub上查看此演示的代码。
有时异步组件加载过长或加载时。显示加载动画或错误信息可能颇有用,但要支持这会再次下降应用程序的速度。异步组件应该小并且加载速度快。这是一个例子:
const Message = () => ({
component: import("./Message"),
loading: LoadingAnimation,
error: ErrorComponent
});
复制代码
建立和实现异步组件很是简单,应该成为标准开发例程的一部分。从用户体验的角度来看,尽量减小初始加载时间以保持用户的注意力是很是重要。但愿本教程能够帮助您构建异步加载组件。