V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
powertoolsteam
V2EX  ›  推广

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

  •  1
     
  •   powertoolsteam · 2019-04-24 19:13:06 +08:00 · 2563 次点击
    这是一个创建于 2041 天前的主题,其中的信息可能已经有所发展或是发生改变。

    引言

    单页应用( 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
    });
    

    总结

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

    3 条回复    2019-04-25 11:53:06 +08:00
    powertoolsteam
        1
    powertoolsteam  
    OP
       2019-04-24 19:14:38 +08:00
    本文由葡萄城进行翻译,原文地址: https://www.sitepoint.com/vue-async-components/ 转载请注明出处:葡萄城官网( https://www.grapecity.com.cn/developer ),葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。
    strugglexiang
        2
    strugglexiang  
       2019-04-25 08:48:14 +08:00 via Android
    已阅
    meepo3927
        3
    meepo3927  
       2019-04-25 11:53:06 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5416 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 05:56 · PVG 13:56 · LAX 21:56 · JFK 00:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.