使用异步组件优化 VUE 程序的性能

2019-04-24 19:13:06 +08:00
 powertoolsteam

引言

单页应用( SPA )的初始加载速度很慢,有时会有点笨拙。这是因为传统上,服务器将向客户端发送大量的 JavaScript,在屏幕上显示任何内容之前,必须先下载和加载这些代码。正如你所能想象的,随着你的应用规模的增长,这会变得越来越有问题。

幸运的是,当使用 Vue CLI (底层使用了 Webpack )构建 Vue 应用程序时,可以采取许多措施来避免这种情况。在本文中,我将演示如何利用异步组件和 Webpack 的代码拆分功能在应用程序初始化渲染后仅加载页面的一部分内容。这将使初始加载时间保持在最小值,并使你的应用程序运行起来感觉更加快速。

至此之前,你需要对 vue.js 和 node.js 有一个基本的了解。

异步组件( Async Components )

在开始创建异步组件之前,让我们先看看我们平时如何加载组件。为此,我们将使用一个非常简单的消息组件:

<!-- Message.vue -->
<template>
<h1>New message!</h1>
</template>

现在我们已经创建了组件,让我们将其加载到 app.vue 文件中并显示它。我们只需导入组件并将其添加到组件选项,以便在模板中使用它:

<!-- App.vue -->
<template>
<div>
    <message></message>
</div>
</template>

<script>
import Message from "./Message";
export default {
    components: {
        Message
    }
};
</script>

但这样带来了什么?每当加载应用程序时,都会加载消息组件,于是它包含在初始化加载中。

对于一个简单的应用程序来说,这可能不是一个大问题,但是考虑一些更复杂的东西,比如一个网络商店。假设用户将项目添加到一个购物篮中,然后希望签出,因此单击“签出”按钮,该按钮将呈现一个包含所选项目所有详细信息的包。如果使用上面的方法,签出相关组件也将包含在这个包中,尽管我们只需要在用户点击签出按钮时调用签出相关的组件。甚至有可能用户不用点击签出按钮就可以浏览网站,这也就意味着加载这个有可能不会被用到的组件是没有意义的的。

为了提高应用程序的效率,我们可以将延迟加载和代码拆分技术结合起来。

延迟加载就是延迟组件的初始加载。你可以在 medium.com 这样的网站上看到懒加载的操作,在那里图片恰恰在需要之时才被加载。这是很有用的,因为我们不必浪费资源来预先加载所有的内容,因为读者可能会开始看的时候就跳过这篇文章的一半。

Webpack 提供的代码拆分功能允许您将代码拆分为各种捆绑包( bundles ),然后可以按需或在以后的时间点并行加载。它只能用于在需要或使用特定代码时加载它们。

动态加载

Vue 中提供一个称之为动态导入的功能用来满足这样的场景。此功能引入了一种方法函数的形式,它将返回包含请求的 promise。因为 import 是一个接收类型是字符串的方法,所以我们可以做一些功能强大的事情,比如使用表达式加载模块。从 Chrome 的 61 版本开始,动态导入被支持。更多关于它们的信息可以在谷歌开发者网站上找到。

代码拆分可以由 Webpack、Rollup 或 Parcel 等绑定程序完成,它们支持动态导入语法,并可以为每个动态导入的模块创建单独的文件。稍后我们将在 F12 控制台的“ network ”选项中看到这一点。但首先,让我们看看静态导入和动态导入之间的区别:

// static import
import Message from "./Message";

// dynamic import
import("./Message").then(Message => {
// Message module is available here...
});

现在,让我们将上述功能应用到我们的消息组件中,我们将得到一个 app.vue 组件,如下所示:

<!-- App.vue -->
<template>
<div>
    <message></message>
</div>
</template>

<script>
import Message from "./Message";
export default {
    components: {
        Message: () => import("./Message")
    }
};
</script>

如您所见,import ()函数将解决返回组件的 promise,这意味着我们已经成功地异步加载了组件。如果您 F12 查看控制台的网络项,您会注意到一个名为 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

接下来,在 src/main.js 中引入 Ant Design 相关 CSS:

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>

我们将显示一个购物车图标,其中包含当前购买的商品数量。商品本身是从数组中提取的。如果单击某个项目的“ Buy ”按钮,将调用 additem 方法,该方法会将商品相关内容推送到 ShoppingList 数组中。继而增加购物车的总数。

我们还向页面添加了一个签出按钮

<Button @click="show = true" id="checkout">Checkout</Button>

接下来事情开始变得有趣起来,当用户单击此按钮时,我们将参数 show 设置为 true。这个布尔值对于有条件地加载异步组件非常重要。

下面几行,您可以找到一个 v-if 语句,当 show 设置为 true 时,它只显示<div>的内容。这个<div>标签包含 checkout 组件,只有在用户点击"Checkout"按钮时才加载它。

checkout 组件在<script>中异步加载。更牛的是,我们甚至可以通过 v-bind 语句将参数传递给组件。综上所述,我们可以很容易的创建一个带有条件的异步组件。

<div v-if="show">
    <checkout v-bind:shoppingList="shoppingList"></checkout>
</div>

让我们快速在 src/component s/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 的内容输出到屏幕上。

F12 打开控制台“ network ”选项的情况下在商店周围单击,以确保仅在单击“ Checkout ”按钮时才加载 Checkout 组件。

可以在github 上找到上述工程

异步加载负向应用

我们甚至可以定义一个错误组件,当异步组件需要一些时间来加载或无法加载时进行显示。加载动画是一个非常实用的应用场景,但请记住,这会再次减慢应用程序的速度。异步组件应该小而快速地加载。下面是一个例子:

const Message = () => ({
    component: import("./Message"),
    loading: LoadingAnimation,
    error: ErrorComponent
});

总结

创建和实现异步组件非常简单,这应该是开发流程的一部分。从用户体验的角度来看,尽可能减少初始加载时间以保持用户的吸引力是很重要的。希望本教程能够帮助您实现异步加载自己的组件,并找到他们合适的实际使用场景。

2488 次点击
所在节点    推广
3 条回复
powertoolsteam
2019-04-24 19:14:38 +08:00
本文由葡萄城进行翻译,原文地址: https://www.sitepoint.com/vue-async-components/ 转载请注明出处:葡萄城官网( https://www.grapecity.com.cn/developer ),葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。
strugglexiang
2019-04-25 08:48:14 +08:00
已阅
meepo3927
2019-04-25 11:53:06 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/558365

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX