使用异步组件优化Vue应用程序的性能
单页应用其一个问题是首屏屏渲染速度较慢。这是因为页面首次加载时服务器将向客户端发送大量javascript,在屏幕上显示任何内容之前必须下载并解析。可以想象,随着应用程序规模的扩大,这个问题对用户体验的影响也会越来越突出。
现在幸运的是,当使用vue cli构建vue应用程序时(使用webpack),可以采取一些措施来抵消这种情况。在本文中,我将演示如何在应用程序的初始渲染之后使用 异步组件
和webpack的代码分割功能加载到页面的某些部分。这将使初始加载时间降至最低,并为您的应用程序提供更好的用户体验。
认识异步组件
在我们开始创建异步组件之前,让我们看一下我们通常如何加载组件。为此,我们将使用一个非常简单的消息组件作为例子:
<!-- message.vue --> <template> <h1>new message!</h1> </template>
现在我们已经创建了我们的 message
组件,让我们将它加载到我们的文件中并显示它。我们可以导入组件并将其添加到组件选项中,以便我们可以在模板中使用它:
<!-- app.vue --> <template> <div> <message></message> </div> </template> <script> import message from "./message"; export default { components: { message } }; </script>
但现在发生了什么?只要应用程序加载,就会加载 message
组件,因此它包含在初始加载过程中。
对于一个简单的应用程序来说,这看起来可能不是一个大问题,但可以考虑像电商网站这样复杂的场景。想象一下,用户将项目添加到购物车,然后想要结帐,因此单击结帐按钮会生成一个包含所选商品的所有详细信息的框。使用上述方法,此结帐框将包含在初始包中,但我们只需在用户单击结帐按钮时使用该组件。用户甚至可以在不点击结账按钮的情况下浏览网站,这意味着在加载这个可能不会使用的组件时浪费资源是没有意义的。
为了提高应用程序的效率,我们可以结合延迟加载和代码分割技术。
webpack提供的代码拆分功能允许您将代码拆分为各种捆绑包,然后可以按需加载或稍后并行加载。它只能在需要或使用时加载特定的代码片段。
dynamic imports(动态导入)
vue使用dynamic imports解决这种情况。此功能引入了一种新的类似函数的导入形式,它将返回包含(vue)组件的promise。由于import是一个接收字符串的函数,我们可以做一些强大的事情,比如使用表达式加载模块。自版本61以来,chrome中已提供动态导入。有关这些内容的详细信息, 请访问google developers网站 。
代码拆分由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 });
总结
创建和实现异步组件非常简单,应该成为标准开发例程的一部分。从用户体验的角度来看,尽可能减少初始加载时间以保持用户的注意力是非常重要。希望本教程可以帮助您构建异步加载组件。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 无法获取隐藏元素宽度和高度的解决方案