Vue.js 简介:Vue-cli 和生命周期钩子

Avatar of Sarah Drasner
Sarah Drasner

DigitalOcean 为您旅程的每个阶段提供云产品。从 $200 的免费积分 开始!

这是关于 JavaScript 框架 Vue.js 的五个部分系列的第三部分。我们将介绍 Vue-cli,并更多地谈论现实生活中的开发流程。 这不打算成为一个完整的指南,而是一个概述基本知识,让你快速入门,以便你能够了解 Vue.js 并理解该框架提供的功能。

文章系列

  1. 渲染、指令和事件
  2. 组件、道具和插槽
  3. Vue-cli (您就在这里!)
  4. Vuex
  5. 动画

Vue-cli 和构建过程

如果您还没有阅读上一节关于 Vue.js 组件和道具的内容,我强烈建议您在阅读本节之前先阅读上一节,否则,我们将要介绍的某些内容将缺乏上下文。

Vue 提供了一个 非常好的 cli,它可以让你快速入门,并根据你的选择使用一些构建工具,以及非常简单易用的入门样板。这是一个很棒的工具。在安装 vue-cli 之前,您可能需要检查您的 node、npm 或 yarn 版本是否为最新版本。您首先需要安装 vue-cli(-g 用于全局安装)

$ npm install -g vue-cli

有很多可用的构建版本,但在我们的示例中,我们将使用 webpack

$ vue init webpack <project-name>

您可以按照输出中给出的命令操作,这些命令将帮助您进入目录、安装所有内容、设置您的 `package.json` 文件,最后使用以下命令在 localhost:8080 上启动本地开发服务器

$ npm run dev

您已经准备就绪!我喜欢这个设置如此干净。您将从 `/src/` 目录中的 App 文件开始,并在 `/components/` 目录中有一个 `Hello.vue` 文件。这很好,因为您已经可以看到如何设置这些文件,以及导入和导出是如何工作的。

让我们花点时间看一下这个新的 `.vue` 文件扩展名,因为如果您以前没有使用 vue,您之前不会遇到它。

在您的 `.vue` 文件中,您可以放置组件所需的一切。我们不再需要将模板包装在 <script type="text/x-template"> 中,现在我们将更语义化地创建遵循这种逻辑的文件

<template>
  <div>
     <!-- Write your HTML with Vue in here -->	
  </div>
</template>

<script>
  export default {
     // Write your Vue component logic here
  }
</script>

<style scoped>
  /* Write your styles for the component in here */
</style>

我已经为 Sublime Text 创建了一个 Vue 代码片段仓库,以便快速创建像这样用于 `.vue` 文件的样板代码(这就是代码片段 vbase 将输出的内容)。还有 用于 atom 的代码片段(尽管它指定了版本 1+,而 Vue 现在是 v2),以及 用于 vscode 的代码片段

这里需要注意几点:就像 React 一样,您必须返回一个唯一的封闭标签,这里我使用了 div。我也在 SVG 中使用了 <g> 元素。它可以是任何东西,但整个模板必须包装在该一个标签中。

您会看到,我们将使用 export default 编写脚本,例如数据函数或之前使用的函数,但如果我们要在该 `.vue` 文档中使用组件作为子组件,我们也必须导入它们(稍后会详细介绍)。

您还会看到,我们在 style 标签上有一个特殊的 scoped 值。这使我们能够非常轻松地将该组件的样式范围限定到该组件本身。 我们也可以只使用 <style>,它将为整个应用程序创建样式。我通常喜欢为整个应用程序创建一个包含常用样式(如字体和行高)的基本样式表,然后使用 vue-style-loader 将其 @import 到 App.vue 文件的 <style> 标签中。然后,当需要时,我会为模板使用 <style scoped> 标签,用于非常特殊的样式,但实际上每个人都有自己的方法!好处是,Vue-cli 允许您决定如何组织它,而且您不必添加任何其他依赖项或模块来以这种方式限定我们的样式。*心形眼*

我们之前简要地介绍过插槽,当我们在带有范围限定样式标签的 Vue 组件中使用插槽时,它们会应用于包含这些插槽的组件。这非常有用,因为您可以轻松地替换组件并更改外观。*更加心形眼*

我不得不说,就开发工作流程而言,在每个 `.vue` 文件中为我的 HTML、样式和 JS 工作,是非常有帮助的。我喜欢每个部分都足够分开,以便清晰地查看每个部分,但又足够接近,以至于我不需要进行上下文切换。它加速了我的开发,而且我注意到标记保持了相当的语义化。

您可能还会注意到,您的语法高亮器不会自动识别 `.vue` 文件,因此我为 Sublime Text 安装了这个插件

以下是以最基本的方式将组件导入/导出到文件中的方法(在 vue-sublime 代码片段中使用 vimport:c)

import New from './components/New.vue';

export default {
  components: {
    appNew: New
  }
}

为了更好地了解现实生活中的示例,让我们看一下我们使用的最后一个葡萄酒标签演示的示例,其中组件已分离到它们自己的模板中

App.vue

<template>
  <div class="container">

  <main>
      <component :is="selected">
        <svg class="winebottle" aria-labelledby="title" xmlns="http://www.w3.org/2000/svg" viewBox="0 155 140 300">
          ...
      </svg>
      </component>
    </main>

    <aside>
      <h4>Name your Wine</h4>
      <input v-model="label" maxlength="18">
      <div class="button-row">
        <h4>Color</h4>
        <button @click="selected ='appBlack', labelColor = '#000000'">Black Label</button>
        <button @click="selected ='appWhite', labelColor = '#ffffff'">White Label</button>
        <input type="color" v-model="labelColor" defaultValue="#ff0000">
      </div>
    </aside>

  </div>
</template>

<script>
  import Black from './components/Black.vue'
  import White from './components/White.vue'
  ...
  export default {
      data: function () {
        return {
          selected: 'appBlack',
          label: 'Label Name',
          ...
        };
      },
      components: {
          appBlack: Black,
          appWhite: White,
          ...
      }
  }
</script>

<style>
  @import "./assets/style.css";
</style>

黑色组件

<template>
  <div>
    <slot></slot>
  </div>
</template>

<style scoped>
  .label {
    fill: black;
  }
  .bottle, .wine-text {
    fill: white;
  }
  .flor {
    fill: #ccc;
  }
  .bkimg {
    filter:url(#inverse)
  }
</style>

请注意,我在这里使用组件以不同的方式对每个插槽进行样式化,这是一种非常好的工作方式,但它只是其中一种方式。您可以使用组件、插槽和道具以无数种方式组合应用程序。这里面的代码也只展示了发生的事情的一部分。 我已经创建了一个仓库供您探索,该仓库从一开始就使用 Vue-cli 构建。我强烈建议您将 Vue-cli 与阅读本指南和构建一些组件并以简单的方式传递状态(使用道具)结合起来使用,以便熟悉工作流程。一旦您克服了初始设置,它就会变得非常直观和快捷!

生命周期钩子

在我们讨论生命周期钩子之前,我们需要稍微回溯一下,谈谈我在第一篇文章中提到的虚拟 DOM。我注意到 Vue.js 具有虚拟 DOM,但没有真正说明它是做什么的。

当您使用 jQuery 之类的东西时,本质上您是在监听 DOM,并根据这些更新进行更改。我们最终会花很多时间检查 DOM 的情况,并在那里存储状态。相反,虚拟 DOM 是 DOM 的抽象表示,有点像副本,但在这种情况下,它将成为我们的主副本。当我们使用这些文章中 Vue 的方式处理状态时,我们是在自己创建状态,然后观察状态何时发生变化。

当 Vue 实例更新时,Vue 将检查它是否与我们之前的内容不同。如果确实不同,它将调用其中一些生命周期方法,并将实际 DOM 与更改进行修补。这样做是为了提高效率,这样 DOM 只更新绝对必要的更改。

生命周期钩子为您提供了一种方法,使您能够在组件生命周期的不同阶段精确地触发某些操作。当我们实例化组件时,组件会被挂载,反过来也会被卸载,例如,当我们在 v-if/v-else 语句中切换它们时。

您可以使用的一些钩子包括:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroydestroyed。如果您想更深入地了解,API 文档对每一个钩子都做了 很好的描述。以下是一个演示一些钩子如何工作的简单示例(查看控制台)

const Child = {
  template: '#childarea',
  beforeCreate() {
    console.log("beforeCreate!");
  }, 
 ...
};

new Vue({
  el: '#app',
  data() {
    return {
      isShowing: false 
    }
  },
  methods: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  },
  components: {
    appChild: Child
  }
});
<div v-if="isShowing">
  <app-child></app-child>
</div>

查看 Sarah Dransner 的 Pen

lifecycle hooks in console

请注意,我们这里使用的是 v-if 而不是 v-show,因为 v-if 实际上会挂载和卸载组件,而 v-show 只会切换可见性(但它将保持挂载状态并停留在 DOM 中)。同样,<keep-alive></keep-alive> 不会被挂载或卸载,而是会变为激活和停用状态——因为组件保持挂载状态,但没有使用。

就像组件上可用的方法会自动绑定 this 一样,生命周期钩子也会自动绑定到实例,这样您就可以使用组件的状态和方法。再次强调,您不必使用 console.log 来找出 this 指向什么!*最心形眼* 因此,您不应该在生命周期方法中使用箭头函数,因为它将返回父级而不是开箱即用地为您提供良好的绑定。

在下面的代码中,我在每个组件最初挂载时移动大量元素,因此我将使用 mounted 钩子触发每个组件的相应动画。您可能需要点击左下角的重新运行按钮才能看到开始动画。

查看 Sarah Drasner 在 CodePen 上的 Pen Vue 天气通知 (@sdras)。

 mounted() {
    let audio = new Audio('https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/rain.mp3'),
        tl = new TimelineMax();

    audio.play();
    tl.add("drops");

    //drops in
    tl.staggerFromTo("#droplet-groups g path", 0.3, {
      drawSVG: "0% -10%"
    }, {
      drawSVG: "100% 110%",
      repeat: 3,
      repeatDelay: 1,
      ease: Sine.easeIn
    }, 0.5, "drops");
 …
}

Vue 还提供了美观而复杂的 <transition><transition-group> 组件,我们在本演示的其他地方使用了这些组件,我们将在该系列的最后一篇关于动画的文章中介绍它们,以及为什么以及何时使用它们。

文章系列

  1. 渲染、指令和事件
  2. 组件、道具和插槽
  3. Vue-cli (您就在这里!)
  4. Vuex
  5. 动画