二、引擎盖下——教程说明

在上一章中,我们从头构建了一个简单的单页应用程序。我们使用 Vue.js 实现应用程序的功能,使用 Bootstrap 使其美观,使用 Firebase 管理应用程序的后端部分。

在本章中,我们将深入了解所有这些技术,并了解它们如何以及为什么能够很好地协同工作。我们将主要讨论 Vue.js,因为这将是我们构建应用程序的首要框架。然后,我们将讨论 Bootstrap 和 Firebase,以基本了解这些技术的强大功能。话虽如此,在本章中,我们将:

  • 讨论 Vue.js 框架、反应性和数据绑定。我们不仅将介绍 Vue.js 的基本知识,还将深入探讨指令、组件、路由等主题。
  • 讨论引导框架。我们将看到使用它可以实现什么,讨论如何布局应用程序,并讨论其组件如何使用有用的自包含功能丰富应用程序。
  • 讨论 Firebase 平台。我们将看到它是什么,它提供了什么功能,以及如何使用它的 API 将这些功能带到应用程序中。
  • 检查如何将所有提到的技术结合在一起,以实现复杂事物开发的简单性。

视图.js

官方 Vue.js 网站建议 Vue 是一个渐进式 JavaScript 框架:

Vue.js

Vue.js 官方网站截图

这是什么意思?通过一种非常简单的方式,我可以将 Vue.js 描述为一个 JavaScript 框架,它为 web 应用程序带来了反应能力。

不可否认,每个应用程序都有一些数据和一些接口。不知何故,接口负责显示数据。数据在运行时可能会更改,也可能不会更改。接口通常必须以某种方式对这些更改作出反应。界面可能有也可能没有一些交互元素,这些元素可能被应用程序的用户使用,也可能不被应用程序的用户使用。数据通常必须对这些交互作出反应,因此,其他接口元素必须对数据所做的更改作出反应。所有这些听起来都很复杂。这个复杂体系结构的一部分可以在后端实现,更接近数据所在的位置;它的另一部分可能在前端实现,更靠近接口。

js 允许我们简单地将数据绑定到接口并放松。数据和接口之间必须发生的所有反应都将自行发生。让我们看一个非常简单的例子,我们将把一条消息绑定到页面标题。首先定义一个简单的 HTML 结构:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
    <title>Vue.js - binding data</title>
  </head>
  <body>
    <div id="app">
      <h1>Hello, reader! Let's learn Vue.js</h1>
    </div>
  </body>
</html>

现在,让我们在此页面上初始化一个Vue.js实例,并将其数据绑定到<h1>元素。对于这个简单的例子,我们将使用一个独立的Vue.js文件。从 Vue.js 官方页面下载 https://vuejs.org/js/vue.js 。将其导入到<script>标记中。现在让我们初始化一个Vue实例。Vue.js 实例需要的最小值是要附加到的元素data对象。我们希望将我们的 Vue 实例附加到具有[T5]ID 的主[T4]标记。我们还将创建一个包含名称项的数据对象:

var data = {name:'Olga'}

让我们使用以下数据创建我们的 Vue.js 实例:

new Vue({
  el: '#app',
 data
})

现在让我们将[T0]绑定到 HTML 元素。我们将使用双花括号({{}}完成此操作。一旦元素被附加到Vue实例,它里面的所有东西都会变得特别,甚至是花括号。您放在双花括号内的所有内容都将被解释和评估。例如,如果将[T3]放在花括号内,[T4]将显示在页面上。试试看。任何表达式、任何语句都将被编译和计算。不过不要太激动;不要开始在这些括号内编写 JavaScript 代码块。让我们将计算留给脚本所在位置编写的脚本逻辑。使用括号访问传递给Vue实例的数据。因此,在我们的例子中,如果您在 HTML 标记的任何位置插入[T6],您将看到我们传递给数据对象中的[T7]实例的名称。例如,让我们将<h1>元素中的reader替换为{{name}}

<h1>Hello, {{name}}! Let's learn Vue.js</h1>

如果刷新页面,您将看到我们传递给 Vue 实例的名称已呈现。尝试在开发者工具控制台中更改data.name属性。您将看到更改立即传播。我们在这里看到的是一个单向数据绑定——发生在数据上的更改会反应性地传播到数据绑定到的元素。Vue.js 还支持双向数据绑定;因此,页面上元素发生的更改也会传播到元素绑定到的数据。

要实现这一点,只需使用v-model属性将给定的数据绑定到元素。例如,让我们向页面添加一个文本输入,并将其绑定到数据属性name

<input type="text"v-model="name">

现在,一旦开始键入文本输入,更改将立即传播到绑定到此数据段的任何其他元素:

Vue.js

数据更改通过所有绑定元素进行反应性传播

HTML 标记和 JavaScript 代码的完整代码如下所示:

<body>
  <div id="app">
    <div>
      <label for="name">What's your name? </label>
      <input id="name" type="text" v-model="name">
    </div>
    <h1>Hello, <strong>{{name}}</strong>! Let's learn Vue.js</h1>
  </div>
  <script src="vue.js"></script>
  <script>
 var data = {name:'Olga'}

 new Vue({
 el: '#app',
 data
 })
  </script>
</body>

正如你所看到的,没有什么困难。您只需将data传递给Vue实例并将其绑定到页面的元素。Vue 框架完成了所有其他功能。在接下来的章节中,我们将了解使用 Vue.js 还可以做些什么,以及如何引导 Vue.js 项目。

Vue 项目–启动

因此,现在我们知道了 Vue.js 的用途和它的主要关注点是什么,我们想动手开始一个 Vue.js 项目,并用它探索 Vue.js 的所有功能。有很多方法可以将 Vue 包含到项目中。让我们来探索所有这些。

直接包含在脚本中

您只需下载 Vue.js 并将其包含在<script>标记中即可使用它。事实上,我们在上一节已经做过了。因此,如果您已经有一个项目正在运行,并且希望使用一些 Vue.js 功能,那么您可以简单地包含[T1]文件并使用它。

CDN

如果您不想亲自下载和管理 Vue 版本,您可以简单地使用 CDN 版本。仅包括https://unpkg.com/vue 在你的项目中加入脚本,你就可以开始了!它将始终与最新的 Vue 版本同步:

<script src="https://unpkg.com/vue"></script>

NPM

如果您都在 Node.js 开发中,只需在package.json文件中添加npm依赖项即可。只需在项目的根目录上运行npm install

npm install vue --save

视图-CLI

Vue 提供了一个漂亮干净的命令行界面,非常适合引导新项目。首先,您必须安装vue cli

npm install --global vue-cli

现在,您可以使用 Vue 命令行界面启动一个新项目。在查看vue cli存储库以获取详细的文档 https://github.com/vuejs/vue-cli

如您所见,可以使用不同的模板设置项目,从简单的单个 HTML 页面项目开始,到复杂的网页项目设置。应用于搭建 Vue 项目的命令如下:

vue init <template-name><project-name>

以下模板可用:

  • 网页:这是一个带有vue-loader的全功能网页设置。它支持热重新加载、脱毛、测试、所有类型的预处理器等。
  • 网页包简单:这是一个简单的网页包设置,有助于快速原型制作。
  • browserify:这是一个带有 vueify 的全功能 browserify 设置,还支持热重新加载、脱毛和单元测试。
  • browserify simple:这是一个带有 vueify 的简单 browserify 设置,可用于快速原型制作。
  • 简单:此生成一个包含 Vue.js 的简单 HTML 页面。它是快速特征探索的完美选择。

也可以创建自定义模板。在查看文档 https://github.com/vuejs/vue-cli#custom-模板并尝试一下。

在本书中,我们将使用webpack模板。我们将包括一些装载机,我们将使用过梁、单元和端到端测试技术。要使用webpack模板引导项目,只需运行以下代码行:

vue init webpack my-project

既然我们已经知道了如何使用vue cli构建一个项目,那么让我们检查一下 vue 除了在上一节中已经介绍的内容之外还提供了哪些内容。

查看指南

Vue 指令只不过是附加到 HTML 元素的属性。这些指令为模板提供了一些额外的功能。

所有这些指令都以前缀v-开头。为什么?因为它是Vue!您已经在上一节中使用了其中的一些。现在,我们将看到存在哪些指令以及您可以使用它们做什么。

条件呈现

打开我们的 Hello 页面并删除用户输入。一些不太美好的事情正在发生:

Conditional rendering

“你好

根据用户输入,有条件地呈现Hello、name 消息将是非常有趣的。如果有名称,则渲染它;如果没有名字,不要渲染。

例如,如果有名称,则仅呈现Hello、name 消息。指令v-showv-if完全用于条件渲染。打开这个例子的index.html文件,让我们更改它。将Hello, <strong>{{name}}</strong>!部分包装到span中,并添加一个具有name值的v-show属性:

<h1><span v-show="name">Hello, <strong>{{name}}</strong>! </span>Let's learn Vue.js</h1>

现在,如果您刷新页面并完全删除输入,则消息只会说让我们学习 Vue.js

Conditional rendering

v-show 属性允许条件渲染

尝试将[T0]指令替换为[T1]指令。最终结果将完全相同。为什么两者都存在呢?查看开发人员工具的“元素”选项卡,并尝试添加或删除输入中的文本。您将看到,在v-show的情况下,如果条件未验证,则条件范围将只获得一个display:none属性。在v-if的情况下,元素完全消失:

Conditional rendering

使用 v-show 属性可以操纵 displaycss 属性,而使用 v-if 属性可以完全添加/删除元素

我们什么时候使用[T2]这两个属性?如果有很多元素根据某些数据而应该可见(这些数据实际上是动态的,因此在运行时会发生很多),我建议使用v-show属性,因为在 DOM 中添加或删除元素是一个相当昂贵的操作,可能会影响应用程序的性能甚至 DOM 本身。另一方面,如果元素只需要有条件地呈现一次,比如说,在应用程序启动时,请使用v-if属性。如果某些元素不应该出现,它们将不会被渲染。因此,页面上的元素数量将减少。因此,应用程序的计算成本也将降低,因为现在,它需要通过和计算的元素更少。

文本与 HTML

我相信您在上一章中非常清楚如何使用 mustache 语法{{}}绑定一些数据。

因为这是一本关于编程的技术书,我们这里必须有一只猫Text versus HTML!猫很容易画。其 Unicode 为U+1F638;因此,我们只需在 HTML 中添加&#x1f638; 代码:

<div>&#x1f638;</div>

当然,我们会有一只猫:

Text versus HTML

表情猫向我们打招呼

这很好,但是如果我们想用狗来代替猫,我们将不得不使用谷歌来寻找另一个代表狗的 Unicode 并替换它。如果在某个时候我们想用独角兽来替换它,我们将不得不运行相同的过程。此外,仅仅通过查看我们的代码,我们将无法说出我们实际呈现的内容,除非我们通过&hearts;了解所有表情符号代码。将表情符号的名称映射到它们的代码可能是个好主意。

让我们添加其中一些的地图。打开 HTML 文件,将以下代码行添加到<script>区域:

//index.html
<script>
  const animalCodes = {
    dog: '&#x1f436;',
    cat: '&#x1f638;',
    monkey: '&#x1f435;',
    unicorn: '&#x1f984;'
  }
  const data = {
    animalCodes
  }

  new Vue({
    el: '#app',
    data
  })
</script>

现在,您可以将此映射的值绑定到 HTML 元素。让我们尝试使用小胡子注释来完成此操作:

<div>{{animalCodes.cat}}</div>

刷新页面。结果与我们预期的不完全相同,是吗?

Text versus HTML

代码被呈现,而不是实际的猫表情符号

这是因为胡须插值实际上是对文本进行插值。使用胡须插值与使用v-text指令相同:

<div v-text="animalCodes.cat"></div>

我们实际上想要呈现的不是文本;我们希望表情符号的 Unicode 值呈现为 HTML!这在 Vue.js 中也是可能的。只需将v-text指令替换为v-html指令:

<div v-html="animalCodes.cat"></div>

现在,我们将取回我们的 cat,当我们查看代码时,我们确切地知道我们正在呈现什么。

因此,记住使用[T0]指令或 mustache 注释进行文本插值,使用[T1]指令插值纯 HTML。

回路

在上一节中,我们在页面上放了一只猫。在这一部分,我想要一个完整的动物园!假设我们的动物园有一只猫Loops、一只狗Loops、一只猴子Loops,当然还有一只独角兽Loops。我们希望以有序列表的形式展示我们的动物园。当然,您可以编写一个简单的标记,如下所示:

<ol>
  <li>&#x1f638;</li>
  <li>&#x1f436;</li>
  <li>&#x1f435;</li>
  <li>&#x1f984;</li>
</ol>

然而,这会使您的代码不可读,如果您想向动物园添加更多动物或删除其中一个,您必须记住所有这些代码。在上一节中,我们为表情符号添加了一个地图。让我们在标记中使用它。您已经了解到,我们必须使用v-html指令,以便将代码插入为 HTML。因此,我们的标记将如下所示:

<div id="app">
  <ol>
    <li v-html="animalCodes.cat"></li>
    <li v-html="animalCodes.dog"></li>
    <li v-html="animalCodes.monkey"></li>
    <li v-html="animalCodes.unicorn"></li>
  </ol>
</div>

看起来好多了,但我们还有一些地方需要改进。想象一下,如果你想渲染表情世界中的所有动物!他们有很多。对于每只动物,您必须重复列表项的代码。每当您想要对列表重新排序、删除某些元素或添加新元素时,都必须处理此标记。如果我们只需要渲染一组动物,然后以某种方式对其进行迭代并渲染其中的内容,这不是很好吗?当然会的!可以使用v-for指令。使用以下代码行创建动物数组:

const animals = ['dog', 'cat', 'monkey', 'unicorn']

将其导出到vue data对象中:

var data = {
  name:'Olga',
 animals,
  animalCodes
}

现在,您可以在v-for指令中使用这个数组,并用一个元素替换多个<li>元素:

<ol>
<h2><span>{{name}}! </span>Here's your Zoo</h2> 
  <li v-for="animal in animals" v-html="animalCodes[animal]"></li>
</ol>

结果会很好:

Loops

使用 v-for 指令呈现表情动物园

绑定数据

在上一节中,我们使用 Vue.js 大量处理了呈现不同的数据;现在,您已经熟悉了绑定它的不同方法。您知道如何将数据插入为文本HTML,并且您知道如何迭代数据数组。

我们还看到,双向数据绑定是使用[T0]指令实现的。我们使用它将名称绑定到输入元素:

<input id="name" type="text" v-model="name">

v-model指令只能与inputselecttextarea元素一起使用。它还接受一些要与一起使用的修饰符。修饰符是以某种方式影响输入的特殊关键字。此指令可使用三个修饰符:

  • .lazy:这只会更新更改事件的数据(用我们的输入尝试一下,您会发现输入的更改只会影响到在按下Enter按钮时使用名称的其他部分,而不是每次按键时)
  • .number:这将把您的输入转换为数字
  • .trim:这将修剪用户的输入

也可以链接修改器:

<input id="name" type="text"v-model.lazy.trim="name">

现在,我们几乎了解了将数据绑定到元素的所有内容。如果我们想将一些数据绑定到元素的属性,该怎么办?例如,想象图像的源属性或类属性的动态值取决于某些数据值。我们怎么能那样做?

为此,Vue 提供了一个v-bind指令。有了这个指令,你可以绑定任何你想要的东西!

作为一个例子,让我们在未定义名称时显示一幅悲伤的图片,在定义名称时显示一幅高兴的图片。为此,我创建了两张图片,glad.pngsad.png,并将它们放入我的应用程序的images文件夹中。我还将它们的路径导出到数据对象中:

//index.html
var data = {
  name:'Olga',
  animals,
  animalCodes,
 sadSrc: 'img/sad.png',
 gladSrc: 'img/glad.png'
}

现在,我可以创建一个图像并使用v-bind:src绑定其源代码,我将提供一个 JavaScript 表达式作为值。此表达式将检查名称的值。如果已定义,则应用glad图像,如果未定义,则应用sad图像:

<img width="100%" v-bind:src="name ? gladSrc : sadSrc">

v-bind指令的快捷方式是:,因此我们只需编写以下代码行:

<img width="100%" :src="name ? gladSrc : sadSrc">

下面是定义了name的值时我们的页面的外观:

Binding data

定义名称时会显示笑脸图像

如果从输入字段中删除名称,图像将自动更改!打开页面,尝试从输入字段中删除文本,然后再次添加。继续删除和添加,您将看到图像更改为相应图像的速度有多快。这就是名称未定义时页面的外观:

Binding data

一旦输入被清除,图像源将立即更改

基本上,您可以对任何属性绑定执行完全相同的操作,例如,类:

<label for="name" v-bind:class="{green: name, red: !name}">What's your name? </label>

还可以绑定属性以传递给子组件。我们将在关于组件的一节中看到如何做到这一点。

事件处理

除了数据绑定到元素的直接形式之外,我们还希望处理一些事件,因为这是我们的用户在页面上所做的,会触发一些事件,从而使它们发生。他们点击,他们悬停,他们提交表格,所有这些事件都必须由我们来处理。Vue 提供了一种非常好的方法,可以将侦听器附加到任何 DOM 元素上的事件,并提供了可以处理这些事件的方法。这些方法的优点在于,它们可以使用this 关键字直接访问 Vue 数据。通过这种方式,我们可以使用方法来操作数据,并且由于该数据是被动的,所有更改都将立即传播到该数据绑定到的元素。

为了创建一个方法,您只需向 Vue 应用程序的导出部分添加一个methods对象。要将此方法附加到任何事件侦听器,请使用带有冒号后相应事件的v-on指令。以下是一个例子:

v-on:sumbit="handleSubmit"
v-on:click="handleClick"
v-on:hover="handleHover"

该指令的快捷方式是@,因此我们可以将所有这些指令重写如下:

@sumbit="handleSubmit"
@click="handleClick"
@hover="handleHover"

听起来你应该很熟悉。你还记得我们在第 1 章中遵循的教程吗?请自我介绍-教程?你还记得我们在听信息的submit方法,添加form并呼叫addMessage吗?过来看。我们的表格及其submit指令如下所示:

//please-introduce-yourself/src/App.vue
<template>
  <form @submit="addMessage">
  <...>
  </form>
</template>

然后,在methods部分中,我们实际上定义了addMessage方法:

//please-introduce-yourself/src/App.vue
<script>
  <...>
  export default {
  <...>
    methods: {
      addMessage (e) {
      <...>
      },
    },
  }
</script>

它现在开始变得更有意义了吗?

为了更好地理解它,让我们添加一些方法到我们的动物园页面!如果你能组成自己的动物园,那不是很好吗?让我们添加一个包含所有可能选项的 multiple select 元素,您的 zoo 将根据您实际选择的内容填充!那么,让我们做以下工作:

  • 将更多动物添加到我们的animalCodes地图中
  • 添加另一个名为animalsForZoo的数组
  • 此 zoo 列表中显示的新数组已排序
  • 添加一个由animalCodes地图的键组成的多select
  • 将一个@change侦听器连接到此选择框,该框将调用populateAnimalsForZoo方法
  • 创建一个populateAnimalsForZoo方法,该方法将使用从我们的 multiple select 元素中选择的选项填充animalsForZoo数组

听起来不容易吗?当然,是的!让我们开始吧。因此,首先,在我们的animalCodes地图中添加更多动物:

var animalCodes = {
  dog     : '&#x1f436;',
  cat     : '&#x1f638;',
  monkey  : '&#x1f435;',
  unicorn : '&#x1f984;',
  tiger   : '&#x1f42f;',
  mouse   : '&#x1f42d;',
  rabbit  : '&#x1f430;',
  cow     : '&#x1f42e;',
  whale   : '&#x1f433;',
  horse   : '&#x1f434;',
  pig     : '&#x1f437;',
  frog    : '&#x1f438;',
  koala   : '&#x1f43c;'
}

让我们重新思考一下[T0]数组,并从地图中生成它。这样,每次我们需要添加一些新的动物时,我们只需将其键值名称 unicode 添加到映射对象中,而不是同时维护对象和数组。那么我们的animals数组会是这样的:

var animals = Object.keys(animalCodes)

现在,我们需要另一个空数组。让我们称之为animalsForZoo,让我们用这个新阵列填充我们的动物园。既然是空的,我们的动物园也将是空的。但是,我们将创建一个方法来填充此数组。所以,创建一个数组很容易,不要忘记将它导出到数据对象中:

<script>
<...>
 var animalsForZoo = []
  var data = {
    name:'Olga',
    animals,
    animalCodes,
 animalsForZoo,
    sadSrc: 'img/sad.png',
    gladSrc: 'img/glad.png'
  }
  new Vue({
    el: '#app',
 data
  })
</script>

别忘了用新的animalsForZoo阵列替换我们动物园展示中使用的animals阵列:

<ol>
  <li v-for="animal in animalsForZoo"><span class="animal" v-html="animalCodes[animal]"></span></li>
</ol>

我知道现在你担心页面上的动物园是空的,但给我们几分钟,我们会处理好的!

首先,让我们创建一个基于animals数组填充的多select元素:

<select multiple="multiple" name="animals" id="animals">
  <option v-for="animal in animals" :value="animal">{{animal}}</option>
</select>

现在,最后,我们将在选择框中附加一个事件侦听器。让我们将侦听器附加到更改事件。让我们告诉它调用populateAnimalsForZoo方法。我们的指令如下所示:

@change="populateAnimalsForZoo"

整个select元素将获得一个新属性:

<select @change="populateAnimalsForZoo" multiple="multiple" name="animals" id="animals">
  <option v-for="animal in animals" :value="animal">{{animal}}</option>
</select>

伟大的但是没有populateAnimalsForZoo这样的方法。但是我们在这里!让我们创建它。此方法只需迭代选择作为输入的动物的选中选项,并将它们推送到animalsForZoo数组中:

new Vue({
  el: '#app',
  data,
  methods: {
 populateAnimalsForZoo(ev) {
 this.animalsForZoo = []
 const selected = document.querySelectorAll('#animals option:checked')
 for (var i = 0; i < selected.length; i++) {
 this.animalsForZoo.push(selected[i].value)
 }
 }
  }
})

查看整个 HTML 和 JavaScript 代码在chapter2/example1-vue-intro/index.html文件中所有这些更改后的外观。以下是我们的测试页面如何处理更改:

Handling events

动物园正在根据用户的选择进行填充

这一页很乱,对吧?然而,看看你通过使用这个页面已经学到了多少东西。而且,承认这是一个有趣的学习过程!我们还没有完成。

现在,您已经学会了如何添加方法和事件侦听器,我将教您如何在没有此方法和[T0]的情况下完成完全相同的事情。删除我们刚才添加的所有代码,只需在我们的select元素中添加v-modelanimalsForZoo值:

<select v-model="animalsForZoo" multiple="multiple" name="animals" id="animals">
  <option v-for="animal in animals" :value="animal">{{animal}}</option>
</select>

现在,我们刚才在方法中所做的一切都由 Vue 自动处理!这不是很棒吗?

查看组件

我们来到这个章节,手中有一个中型 HTML 页面,包含许多不同的部分。我们本可以想到更多的事情,例如,为我们动物园的每只动物增加互动性,增加喂养动物的可能性,或者每次你在动物图标上悬停时,每只动物都会出现一些有趣的事实。在某个时刻,让我们面对它,HTML 文件及其 JavaScript 将变得无法维护。

你能看到我们的可视化层(HTML)和逻辑层(JavaScript)一起工作吗?因此,它们形成了块、项、块……例如,我们有一段代码负责Helloname 部分。我们还有一个街区,里面有我们的动物园。动物园里的每一只动物都是另一个项目。

不管你想怎么称呼这些东西,但不可否认,它们是结构和逻辑的独立部分,当它们结合在一起时,就构成了整个谜题。如果您使用独特的材质建造墙,并决定更改墙的某些部分,那么这将不是最简单的任务。

所以,想象一下,你建造了这堵墙,并将一些黄色的星星、蓝色的多边形、红色的正方形等等融入其中。然后,你决定你的黄色星星应该是黑色的。你必须改变你所有的明星。然后,您决定将绿色省略号改为笑脸。现在怎么办?更改所有椭圆,但首先必须找到墙中包含这些椭圆的所有位置。这是您的墙,请尝试查找其中的所有椭圆:

Vue components

整面墙由不同颜色和形式的部分组成

现在,想象一下,每一块实际上都存在于它各自的砖块上。您可以随意更改、添加和删除它们。如果你想改变一些墙元素的外观,你只需改变这一块砖,所有包含这一块砖的墙块都会改变,因为总之,它只是墙中的另一块砖。因此,你没有让墙充满了奇怪的碎片,而是有四块砖,你可以在需要改变依赖于那块砖的那块墙的时候改变它们:

Vue components

如果需要修改墙中图元的外观,只需修改相应的砖

这堵墙是用砖砌成的。这些砖是我们的部件。如果我们也可以使用 HTML、CSS 和 JavaScript 构建组件,并且我们的应用程序可以使用这些组件构建,那会怎么样?我刚才是说“如果”吗?没有“如果”这个词,我们已经有了。Vue.js 支持基于组件的应用程序结构。使用 Vue.js 创建组件非常简单。您只需做以下三件事:

  1. 创建一个组件,并为其提供一个模板、数据、方法以及您需要提供的任何内容。
  2. 在 Vue 应用程序的components对象下注册。
  3. 在应用程序的模板中使用它。

例如,让我们创建一个组件,该组件将简单地呈现一个标题元素,表示Hello。我们叫它HelloComponent。它将仅包含模板字符串:

var HelloComponent = {
  template: '<h1>Hello!</h1>'
}

现在,我们可以在 Vue 应用程序初始化代码中注册此组件:

new Vue({
  el: '#app',
  components: {
 HelloComponent
  }
})

现在,这个组件实际上可以在 Vue 应用程序元素的 HTML 部分中使用:

<div id="app">
 <hello-component></hello-component>
</div>

因此,整个部分将如下所示:

<body>
  <div id="app">
    <hello-component></hello-component>
  </div>
  <script src="vue.js"></script>
  <script>
    var HelloComponent = {
      template: '<h1>Hello!</h1>'
    }
    new Vue({
      el: '#app',
      components: {
        HelloComponent
      }
    })
  </script>
</body>

有人可能会问,“在这些组件中,有什么强大之处?”编写的代码量实际上与我编写的 HTML 代码量相同。重点是什么?是的,当然,但在这个例子中,我们的组件只有一个模板。仅由一行组成的模板。我们可以在那里有一个巨大的模板,我们可以在这个组件中有一些方法,还有它自己的数据!例如,让我们为该组件的名称和其数据对象添加一个输入:

var HelloComponent = {
  template: '<div>' +
  '<input v-model="name" />' +
  '<h1>Hello! <strong>{{name}}</strong></h1>' +
  '</div>',
 data() {
 return {
 name: ''
 }
 }
}

如果需要重用此组件,可以根据需要多次执行此操作:

<div id="app">
 <hello-component></hello-component>
 <hello-component></hello-component>
 <hello-component></hello-component>
</div>

然后,您将在页面上看到三个独立的组件:

Vue components

使用组件有助于避免重复代码

这些组件非常好,但是在相同的 JavaScript 代码块中仍然编写了大量代码。我们在一个地方声明所有组件,如果组件太多,应用程序将再次变得不可管理。此外,模板字符串中的 HTML 代码也不是最容易维护的。

好吧,如果你这么想的话,我有一些好消息要告诉你。每个组件都可以使用自己的 HTML、JavaScript 和 CSS 代码存储在自己的文件中。这些是扩展名为.vue的特殊文件。在每个文件中,有一个用于 JavaScript 代码的<script>部分,一个用于 CSS 代码的<style>部分,以及一个用于 HTML 代码的<template>部分。这不方便吗?这种组件称为单文件组件。看看第一章的代码,有一个主要的组件叫做App.vue,还有我们创建的MessageCard.vue组件。这不是很好吗?

如果您想在应用程序中使用单文件组件,则必须使用一些模块化绑定器(例如,【T0)】构建此应用程序。我们已经讨论过vue-cli以及使用webpack模板引导 Vue 应用程序是多么容易。让我们将带有 zoo 的凌乱页面移植到webpack捆绑应用程序。运行初始化和安装脚本:

vue init webpack zoo
cd zoo
npm install
npm run dev

现在,打开App.vue文件,让我们用混乱的动物园应用程序填充它。<script>部分如下所示:

<script>
  <...>
  var data = {
    name: 'Olga',
    animals,
    animalCodes,
    animalsForZoo,
 sadSrc: '../statimg/sad.png',
 gladSrc: '../statimg/glad.png'
  }
  export default {
    name: 'app',
 data () {
 return data
 }
  }
</script>

注意突出显示的区域。我已将图像复制到static文件夹中。另一件重要的事情是,组件中的数据应该用作返回对象的函数,而不是对象本身。由于数据对象仍然是跨多个组件的单个实例,因此必须在专用函数中组装整个数据对象及其属性。

脚本的其余部分完全相同。

组件的模板区域与上一个示例中的 HTML 结构基本相同。查看chapter2/example3-components-started文件夹中的代码。

让我们将一些功能提取到单个组件中。如果我们将动物园提取到其单独的组成部分,您认为如何?在components文件夹中创建一个Zoo.vue文件。将动物列表的模板复制到此组件的<template>区域:

//Zoo.vue
<template>
  <div v-if="animals.length > 0">
    <h2><span v-if="name">{{name}}! </span>Here's your Zoo</h2>
    <ol>
      <li v-for="animal in animals"><span class="animal"v-html="animalCodes[animal]"></span></li>
    </ol>
  </div>
</template>

现在,我们应该告诉这个组件,它将从调用以下组件的父组件接收animalsnameanimalCodes属性:

//Zoo.vue
<script>
  export default {
 props: ['animals', 'animalCodes', 'name']
  }
</script>

现在,打开主App.vue组件,导入Zoo组件,并将其导出到components对象中:

//App.vue
<script>
 import Zoo from './components/Zoo'
  <...>
  export default {
    name: 'app',
 components: {
 Zoo
 }
  }
</script>

现在,我们可以在模板中使用此组件!因此,用以下代码替换包含我们动物园的整个div标签:

//App.vue
<template>
  <...>
 <zoo :animals="animalsForZoo" :animalCodes="animalCodes":name="name"></zoo>
  <...>
</template>

查看页面!一切都和以前一样!

运动

根据v-for指令,将动物提取到单个组件中,并在动物园内调用。每只动物都必须有一个小功能,当点击它的脸时(在click上)会显示一个小的描述。我相信你会很容易地解决这个问题。如果您需要帮助,请查看example4-components/zoo目录中的本章代码。

查看路由

单页申请SPA很棒。他们来让我们的生活更轻松。确实如此。通过一点 JavaScript 代码,您可以实现之前必须在服务器端完成的所有功能,整个页面应该被替换,以显示该功能的结果。现在是 web 开发人员的黄金时代。然而,水疗中心正试图解决一个问题——导航。历史 API 和pushState方法(https://developer.mozilla.org/en-US/docs/Web/API/History_API 已经解决了这一问题,但这是一个漫长的过程,直到它成为一项成熟的技术。

我们的用户习惯于使用浏览器的导航按钮来控制他们的我在哪里以及我想去哪里。如果整个功能位于同一页面上,这些按钮将如何帮助导航?您如何使用 Google analytics 检查哪个页面(实际上是相同的页面)被您的用户访问得更多?整个概念完全不同。当然,这类应用程序的速度要快得多,因为请求的数量大大减少了。当然,我们的用户对此表示感谢,但他们不会因为我们改变了实现方式而改变他们的上网习惯。他们仍然想回去。他们希望,如果他们刷新页面,页面将在点击刷新按钮之前的相同位置打开。他们希望通过查看页面的 URL 并检查斜杠后面的内容,就能了解自己的位置。例如,如果它是http://mySite/store,那么它就是一家商店;如果是http://mySite/settings,那么我很可能在某个地方可以检查当前设置并更改它们。

有很多方法可以实现导航,而不必将单页应用程序转换为多页应用程序。您可以在应用程序上添加一层额外的逻辑,并在每次需要不同的 URL 时更改window.location.href,这将导致页面刷新,这并不好。你也可以使用 HTML5historyAPI。这不是最简单的维护方法,但它可能会起作用。

我们都知道优秀的开发人员是懒惰的,对吗?懒惰意味着不解决别人已经解决的问题。许多框架和库正在解决这个导航问题。您不仅可以使用一些第三方库来帮助您处理应用程序中的路由,还可以使用您选择的框架提供的机制。Vue.js 是提供处理路由的方法的框架之一。您只需将 URL 路径映射到您的组件,一切都正常工作!在查阅vue-router图书馆的官方文件 https://router.vuejs.org/en/

为了能够使用vue-router,您必须为您的项目安装它:

npm install vue-router –save

或者,可以在 Vue 项目初始化时使用vue init选择vue-router用法。

现在,您可以在应用程序中使用 Vue 路由。只需告诉 Vue 使用它:

//main.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

让我们创建一个简单的路由示例。我们将有三个组件,其中一个我们认为是 ToeT0}组件,这意味着当有人导航到根路由 ORT T1 时,应该显示它。让我们分别调用第二个Hello组件和第三个Bye组件。打开引擎盖下第 2 章中的example5-router-started代码文件–教程说明。您将在components目录中找到所有描述的组件:

Vue router

我们将尝试 Vue 路由的示例应用程序的结构

现在,我们必须创建一个router实例。构造函数接收options对象作为参数。此对象可以包含不同的可配置值。最重要的是routes的数组。该数组的每个条目都应该包含一个表示路由的path及其对应的component的对象。

首先,我们将导入所有需要的组件,然后我们的router实例将如下所示:

//main.js
import Home from '@/components/Home'
import Hello from '@/components/Hello'
import Bye from '@/components/Bye'
<...>
var router = new Router({
  mode: 'history',
  routes: [
    {
      name: 'home',
 component: Home,
 path: '/'
    },
    {
      name: 'hello',
 component: Hello,
 path: '/hello'
    },
    {
      name: 'bye',
 component: Bye,
 path: '/bye'
    }
  ]
})

如果您想更好地理解什么是mode: history选项,请查看的文档页面 https://router.vuejs.org/en/essentials/history-mode.html 这很好地解释了这一点。现在,我们必须将路由选项传递给我们的 Vue 应用程序。此选项将指向我们的新router实例:

//main.js
new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
 router
})

现在,整个应用程序都知道我们使用这个路由。还有一个更重要的步骤:我们需要将路由组件包含到主组件的模板中。为此,只需在App.vue组件的模板中包含<router-view>标记即可:

//App.vue
<template>
  <div id="app">
    <img src=img/logo.png">
 <router-view></router-view>
  </div>
</template>

更详细地查看位于router-view组件 https://router.vuejs.org/en/api/router-view.html

瞧!如果尚未运行应用程序,请执行以下操作:

npm run dev

http://localhost:8080处打开页面,检查是否显示了我们的主页组件。然后,在浏览器的地址栏中键入http://localhost:8080/hellohttp://localhost:8080/bye。检查页面内容是否根据 URL 路径实际更改:

Vue router

使用 vue 路由的基本路由

当然,您已经在考虑如何创建一个简单的菜单,将锚元素指向路由中定义的路径。不要想太多。只需使用一个<router-link>组件,该组件的to属性指向您选择的路径。例如,要为路由示例应用程序显示一个简单的导航菜单,我们可以编写如下内容:

//App.vue
<template>
  <div id="app">
 <router-link to="/">Home</router-link>
 <router-link to="hello">Hello</router-link>
 <router-link to="bye">Bye</router-link>
    <router-view></router-view>
  </div>
</template>

或者,如果您不想再次写入路径,您可以按名称引用您的路径,并使用[T0]指令或简单地使用[T1]:

//App.vue
<template>
  <div id="app">
 <router-link :to="{name: 'home'}">Home</router-link>
 <router-link :to="{name: 'hello'}">Hello</router-link>
 <router-link :to="{name: 'bye'}">Bye</router-link>
    <router-view></router-view>
  </div>
</template>

检查example6-router文件夹中的代码外观。

打开页面,检查所有链接是否都有效!单击几次,然后检查如果单击浏览器的“返回”按钮,是否会返回。这不是很棒吗?

Vuex 状态管理架构

您还记得我们的例子中的Zooanimal组件吗?有些数据必须从主组件传播到子组件的子组件。如果这个孙子组件有可能以某种方式更改数据,则必须将此更改从子组件传播到其父组件,依此类推,直到数据到达主组件。不要以为你只需要一个v-model绑定属性就可以做到这一点。Vue 对通过props将数据绑定到子组件有一些限制。严格来说,这是一种方式。因此,如果父组件更改了数据,子组件的绑定将受到影响,但不会反过来发生。请在查阅 Vue 的官方文档 https://vuejs.org/v2/guide/components.html#One-双向数据流

如果你不相信我,我们试试看。想象一下,在我们的 zoo 页面示例中,我们将把简介部分提取到单独的组件中。我说的是我们混乱的动物园页面的这一部分:

Vuex state management architecture

如果我们想将此部分提取到单独的组件中,该怎么办?

这似乎很容易。我们必须声明一个组件,比如说Introduction,告诉它将接收name属性,然后将粘贴 HTML 从App.vue复制到这个新组件。在App.vue中,我们将导入此新组件,并将其导出到 Vue 实例的components对象中。当然,我们将用[T5]标记替换已经复制到新组件中的 HTML,并将[T6]属性绑定到它。这不容易吗?我们的Introduction.vue文件将如下所示:

//Introduction.vue
<template>
  <div>
    <label for="name" :class="{green: name, red: !name}">What's your name? </label>
    <input id="name" type="text" v-model.trim="name">
  </div>
</template>
<script>
  export default {
 props: ['name']
  }
</script>

我们的App.vue文件将导入、导出并调用:

//App.vue
<template>
  <div id="app" class="jumbotron">
    <...>
 <introduction :name="name"></introduction>
    <...>
  </div>
</template>

<script>
  <...>
 import Introduction from './components/Introduction'

  <...>
  export default {
    components: {
      Zoo,
 Introduction
    }
    <...>
  }
</script>  

第 2 章的代码包中,在example7-events-started/zoo文件夹中的第 2 章下查看此代码–教程说明。在此文件夹中运行npm installnpm run

cd example7-events-started/zoo
npm install
npm run dev

查看页面。看起来像以前一样。尝试更改输入中的名称。首先,它在其他应该更改的地方不会更改,其次,我们的开发工具控制台充满了警告和错误:

Vuex state management architecture

该名称未在应该更新的位置更新,控制台中充满了错误

文档似乎是对的:我们不能更改作为属性传递给子组件的数据的值。那我们怎么办呢?我们可以发出事件,将事件侦听器附加到组件,并更改事件上的数据。我们如何做到这一点?很简单。首先,让我们调用由非[T0]的对象传递的属性,例如,[T1]。然后,打开Introduction组件,创建一个data函数,将该组件的name对象绑定到initialValueprops。通过这种方式,我们至少告诉 Vue,我们无意尝试更改孩子的家长数据。所以,Introduction.vue组件的script将如下所示:

//Introduction.vue
<script>
  export default {
 props: ['initialName'],
    data () {
      return {
 name: this.initialName
      }
    }
  }
</script>

我们还必须更改将名称绑定到App.vue中组件的方式:

//App.vue
<introduction :initialName="name"></introduction>

现在,如果您查看该页面,您至少会看到 Vue 不再抱怨我们试图做的违法行为。但是,如果我们试图更改名称,更改不会传播到父级,这是可以理解的;这些更改仅影响组件本身的数据。现在,我们必须将event连接到input元素。此事件将调用一个方法,该方法将最终向父组件发出事件:

//Introduction.vue
<template>
  <div>
    <...>
    <input id="name" type="text" v-model.trim="name"@input="onInput">
  </div>
</template>
<script>
  export default {
    <...>
    methods: {
 onInput () {
 this.$emit('nameChanged', this.name)
 }
    }
  }
</script>

现在,我们要做的唯一一件事就是将nameChanged事件侦听器绑定到<introduction>组件,并调用将更改App.vue数据对象名称的方法:

//App.vue
<template>
<...>
<introduction @nameChanged="onNameChanged" :initialName="name"></introduction>
<...>
</template>
<script>
  export default {
<...>
 methods: {
 onNameChanged (newName) {
 this.name = newName
 }
 }
  }
</script>

检查页面。现在,一切照旧!检查本章example7-events/zoo代码文件夹中该解决方案的代码。

嗯,这不是很难,但我们是否希望在每次需要更新状态时都发出所有这些事件?如果组件中有组件呢?如果我们在这些组件中还有其他组件呢?会是地狱吗?如果我们必须改变一些东西,我们会去所有这些组件吗?啊!将应用程序的数据放在某种集中式存储中,为其管理提供一个简单的 API,然后我们可以调用此存储的方法来检索和更新数据,这不是很好吗?这正是 Vuex 的用途!Vuex 是一种受 Redux 启发的集中式状态管理。在查看其官方文件 http://vuex.vuejs.org/en/

现在,简而言之,Vuex 存储的三个最重要的部分是状态、getter 和突变:

  • 状态:这是应用的初始状态,基本上是应用的数据
  • Getters:这些正是您所认为的,从存储返回数据的函数
  • 突变:这些是可以突变存储上数据的功能

商店也可以有操作。这些东西就像是更大容量的突变包装。如果您想查看它们是关于什么的,请参阅上的官方文件 http://vuex.vuejs.org/en/mutations.html

让我们将 Vuex 存储添加到Zoo应用程序中,以检查其工作方式。首先,我们需要安装vuex。打开example8-store-started/zoo文件夹中的第 2 章的代码,然后运行npm install

cd example8-store-started/zoo
npm install vuex --save

让我们创建我们的商店。首先创建一个名为store的文件夹,其中包含index.js文件。我们将把所有存储数据放在这个文件中。执行此操作之前,请告诉 Vue 我们将使用 Vuex:

//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

现在,我们可以创建一个新的 Vuex 实例。它应该接收stategettersmutations。让我们来定义它们:

//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
}

const getters = {
}

const mutations = {
}

export default new Vuex.Store({
  state,
  getters,
  mutations
})

美好的现在,让我们将驻留在应用程序中的所有数据添加到状态:

//store/index.js
const animalCodes = {
  dog: '&#x1f436;',
  <...>
  koala: '&#x1f43c;'
}
const animalsDescriptions = {
  dog: 'I am a dog, I bark',
  <...>
  koala: 'I am a koala, I love eucalyptus!'
}
const animals = Object.keys(animalCodes)
const state = {
  name: 'Olga',
  animals,
  animalCodes,
  animalsDescriptions,
  animalsForZoo: [],
  sadSrc: '../statimg/sad.png',
  gladSrc: '../statimg/glad.png'
}

现在,如果在 Vue 应用程序初始化时注入存储,那么所有组件及其子组件都可以访问this.$store实例。让我们注入它:

//main.js
import Vue from 'vue'
import App from './App'
import store from './store'

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
 store
})

现在,如果我们使用App.vue中存储的计算属性替换所有数据(除了animalsForZoo,它被绑定为我们动物园的属性),那么应用程序看起来将完全相同:

//App.vue
<script>
  import Zoo from './components/Zoo'
  import Introduction from './components/Introduction'

  export default {
    name: 'app',
    components: {
      Zoo,
      Introduction
    },
    data () {
      return {
        animalsForZoo: []
      }
    },
 computed: {
 name () {
 return this.$store.state.name
 },
 animals () {
 return this.$store.state.animals
 },
 animalCodes () {
 return this.$store.state.animalCodes
 },
 sadSrc () {
 return this.$store.state.sadSrc
 },
 gladSrc () {
 return this.$store.state.gladSrc
 }
 },
    methods: {
      onNameChanged (newName) {
        this.name = newName
      }
    }
  }
</script>

如果你打开页面,一切都没有改变。然而,我们的更名交互不再有效!

让我们添加mutation来更改名称。突变只是将状态作为第一个参数,并将任何调用它们的内容作为其他参数的方法。那么,让我们将突变称为updateName并将newName作为第二个参数传递给它:

//store/index.js
const mutations = {
 updateName (state, newName) {
 state.name = newName
 }
}

现在,我们可以使用这个变异来访问负责更新名称的组件内的this.$store.mutation属性-Introduction.vue。我们只需更改onInput方法:

//Introduction.vue
methods: {
  onInput (ev) {
 this.$store.commit('updateName', ev.currentTarget.value)
  }
}

顺便说一下,我们还可以删除属性并直接从存储中传递名称,就像我们在App.vue组件中所做的那样。然后,您可以移除与App.vue组件模板内introduction组件的name绑定。现在,您可以将绑定到 Zoo 组件的属性替换为来自存储区的计算属性。看看代码变得多么优雅!例如,请看这行代码:

<introduction></introduction>

它看起来不是比下面的代码行更好吗:

<introduction @nameChanged="onNameChanged" :initialName="name"></introduction>

第 2 章example8-store/zoo代码文件夹中,在引擎盖下查看本章的最终代码–教程解释。请注意,我们使用了一个非常简化的版本。我们甚至没有使用任何吸气剂。对于更复杂的用途,我们将创建gettersactions,它们将位于它们自己的actions.jsgetters.js文件中。我们还将使用mapGettersmapActions助手。然而,就基本的理解而言,我们所做的已经足够了。请参阅官方文档,了解有关 Vuex 应用商店以及如何使用它的更多信息。

自举

现在我们已经了解了 Vue.js 的几乎所有内容,让我们来谈谈引导。在查看官方引导页面 https://v4-alpha.getbootstrap.com/

Bootstrap

响应性项目的引导框架

简而言之,Bootstrap 为您提供了一组广泛的类,这些类允许以轻松的方式使用任何布局构建几乎所有内容。

Bootstrap 为您提供了四件最重要的事情:

如何安装引导程序?它可以从 CDN 安装:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>

事实上,这正是我们在第 1 章PleaseIntroduceYourself应用程序中所拥有的,请自我介绍–教程以及本章混乱的动物园应用程序中所拥有的。

引导组件

Bootstrap 有很多组件,可以直接使用。

在这一章中,我将不谈所有这些,因为在本书的过程中,我们将有几个机会去发现它们。让我们看看其中的一些只是为了有个想法。

让我们看看警报组件。正如您可能知道的,当您成功填写某个表单时,警报是显示在页面上的好元素。警报也是那些愤怒的红色元素,它们告诉你你做错了什么。您需要在页面上创建一个警报元素,该元素在一段时间后会消失,或者允许用户通过点击x按钮来关闭它?您可能会创建一个[T0],向其中添加一些类,并添加一点 JavaScript,在宽限期后从 DOM 树中删除该元素。使用 Bootstrap,您只需将alert类添加到div中,然后添加另一个类,如alert-warningalert-info来指定它是哪种警报:

<div class="alert alert-success" role="alert">
  <strong>Hello!</strong> You have successfully opened this page!
</div>
<div class="alert alert-info" role="alert">
  <strong>Hey!</strong> Important information - this alert cannot be closed.
</div>
<div class="alert alert-warning" role="alert">
  <strong>Warning!</strong> It might be raining tonight, take your umbrella!
</div>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
    <span aria-hidden="true">&times;</span>
  </button>
  <strong>Failure!</strong> Since you don't like this failure alert you can simply close it.
</div>

此代码将生成漂亮的警报框,如下所示:

Bootstrap components

引导警报成功、信息、警告和危险

即使是按钮这样的简单元素,也可以使用 Bootstrap 以数百种不同的方式设置样式。同样,您可以有指示成功、危险区域、信息丰富或只是灰色的按钮。可以将按钮分组,使其看起来像链接。代码非常简单:

<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-link">Link</button>
<button type="button" class="btn btn-primary btn-sm">Small button</button>

此代码将生成如下所示的按钮:

Bootstrap components

引导按钮

icial 文件页位于https://v4-alpha.getbootstrap.com/components/buttons/

关于 Bootstrap,我最喜欢的一点是,你可能有一个微不足道的元素,但是你添加了一些 Bootstrap 的类,它突然变得干净漂亮。例如,创建一个包含一些<h1><p>元素的简单页面:

<div>
  <h1>Jumbotron</h1>
  <p>
    Lorem ipsum dolor sit amet…
  </p>
</div>

看起来很正常,很简单。现在,将container类添加到父div中。不是更好吗?另外,将[T2]类添加到它。

页面在前面是这样的:

Bootstrap components

添加引导类之前,div 中的内容

突然,同一页的看起来是这样的:

Bootstrap components

添加 Bootstra 后 div 内的内容

实际上,如果您查看第 1 章中的PleaseIntroduceYourself示例请自我介绍–教程chapter1/please-introuce-yourself/src/App.vue,您将看到这个类正是用于父元素的。

有很多不同的组件:弹出框、工具提示、模态等等。在本书的学习过程中,我们将使用所有这些工具。

引导实用程序

您想要响应浮动(元素向左或向右流动)?只需将float-leftfloat-right类添加到元素中,您就不必再担心了:

<div class="float-left">Float left on all viewport sizes</div><br>
<div class="float-right">Float right on all viewport sizes</div><br>
<div class="float-none">Don't float on all viewport sizes</div><br>

只需将此代码插入 HTML 页面(或者只需签出example11-responsive-floats文件夹中的index.html文件),打开它,然后调整窗口大小。

您可以使用简单的类轻松控制大小和间距。退房https://v4-alpha.getbootstrap.com/utilities/sizing/https://v4-alpha.getbootstrap.com/utilities/spacing/.

您甚至可以通过向容器中添加[T0]类来启用 flex box[T1]行为。d来自显示器。随着更多类附加到 flex 元素,您可以控制 flex 框的对齐和方向。请在查看 https://v4-alpha.getbootstrap.com/utilities/flexbox/

还有很多实用程序需要探索,我们将在旅途中了解其中的大部分。

自举布局

使用 Bootstrap,很容易控制您系统的布局:

|   | Bootstrap 包括几个用于规划项目的组件和选项,包括包装容器、功能强大的 flexbox 网格系统、灵活的媒体对象和响应性实用程序类。-(【t0])https://v4-alpha.getbootstrap.com/layout/overview/ |   | |   | --来自引导 |

Bootstrap 的网格系统功能强大,易于理解。它只是由列组成的一行。所有内容都由具有非常自描述性名称的类控制,例如rowcol。如果只给列[T2]类,[T3]元素中的每一列都具有相同的大小。如果您想拥有不同大小的列,请考虑行可以由 12 列组成这一事实。所以,如果你想做一些列,比如说你行的一半,给它一个类col-6

<div class="row">
  <div class="col">this is a column with class col</div>
  <div class="col-6">this is a column with class col-6</div>
  <div class="col-2">this is a column with class col-2</div>
</div>

此代码将产生与以下类似的结果:

Bootstrap layout

结合行和列类的网格布局系统

有趣的是如果你调整你的窗口大小,你的布局不会被破坏。它将相应地调整大小。为了实现这一点,您不必实现任何 CSS 黑魔法!这就是为什么 Bootstrap 是一个大的Bootstrap layout

结合 Vue.js 和 Bootstrap

当我们谈论 Vue 时,我们用了很大一部分讨论它的组件。当我们谈到引导时,我们也谈到了组件。是不是也一样?也许我们可以用引导组件创建 Vue 组件?也许我们可以!事实上,我们已经做到了!打开第一章PleaseIntroduceYourself申请的代码。检查components文件夹中的内容。我们称之为MessageCard.vue。实际上,这是为卡引导的组件(实现的 Vue 组件 https://v4-alpha.getbootstrap.com/components/card/

打开example13-vue-bootstrap-components-started/components文件夹。让我们将此项目用作基于引导警报组件创建 Vue 组件的平台。运行npm installrun

cd example13-vue-bootstrap-components-started/components
npm install
npm run dev

让我们创建一个名为Alert的 Vue 组件。此组件将包含模拟引导的警报组件行为所需的代码。

components文件夹中创建一个名为Alert.vue的文件,并添加template标记。我们的警报肯定会有alert类。但是,它的附加类(alert-dangeralert-info等)应该是可配置的。此外,它的标题和文本应该是由父组件的绑定属性传递的内容。因此,警报组件的模板如下所示:

//Alert.vue
<template>
  <div class="alert":class="additionalClass" role="alert">
    <strong>{{title}}</strong>{{text}}
  </div>
</template>

让我们将additionalClass属性实现为一个计算属性,该属性将基于父组件传递的type属性进行计算。因此,Alert组件的脚本如下所示:

//Alert.vue
<script>
export default {
 props: ['type', 'title', 'text'],
  computed: {
 additionalClass () {
 if (!this.type) {
 return 'alert-success'
 }
 return 'alert-' + this.type
 }
  },
  name: 'alert'
}
</script>

然后,我们可以从我们的主App.vue组件中调用它:

//App.vue
<template>
  <div id="app" class="container">
    <img src=img/logo.png">
 <alert :title="title" :text="text"></alert>
  </div>
</template>

<script>
 import Alert from './components/Alert'
  export default {
    data () {
      return {
 title: 'Vue Bootstrap Component',
 text: 'Isn\'t it easy?'
      }
    },
    name: 'app',
    components: {
 Alert
    }
  }
</script>

您将在页面上显示良好的警报:

Combining Vue.js and Bootstrap

我们刚刚创建了 Alert Vue 引导组件

运动

启用警报组件标题的默认值。因此,如果title未通过,则默认为成功。另外,在App.vue父组件内部创建组件时,将 type 属性绑定到该组件。根据某些任意值将此属性导出为计算属性。例如,基于某个随机数,如果可被3整除,则类型应为危险;如果可以被5整除,则类型为信息;等等

你自己看看吧。进入example13-vue-bootstrap-components/components文件夹,查看App.vuecomponents/Alert.vue组件。

结合 Vue.js 和 Bootstrap 继续

因此,我们知道如何基于引导组件创建 Vue 组件。现在,将所有引导组件创建为 Vue 组件并在我们的 Vue 应用程序中使用它们,而不必考虑任何引导类,这不是很好吗?想象一下 Vue 组件,比如[T0]或[T1]。我们甚至可以基于引导创建一个完整的 Vue 组件库!问题是,如果它已经存在,我们是否应该这样做?是的,有人已经为我们做了所有的工作。这些人已经完成了这项工作:

Combining Vue.js and Bootstrap continued

bootstrap vue 的核心团队

这些善良的人开发了一种叫做 Bootstrap Vue 的东西,这种东西正是你所认为的,它包含了一整套作为 Vue.js 组件实现的 Bootstrap 组件。在查看 https://bootstrap-vue.github.io/

让我们检查一下,例如,警报组件是如何在实现的 https://bootstrap-vue.github.io/docs/components/alert 。这比我们的警报要详细一点。数据是在组件的标记中传递的,而不是像在我们的例子中那样作为属性传递的,这也使得它更加灵活。在本书中,我们将在开发应用程序时大量使用它。

什么是 Firebase?

为了了解什么是 Firebase,让我们打开它的网站https://firebase.google.com/ 。这就是我们看到的:

What is Firebase?

谷歌 Firebase 登录页

Firebase forGoogle 是另一种云服务,就像 AWS for Amazon 或 Azure for Microsoft 一样,不过要简单一点,因为 Google 已经拥有庞大的 Google 云平台。

如果你想在 Firebase 和 AWS 之间做出选择,别忘了你很可能会用谷歌搜索它。在任何情况下,有人已经为你做了这件事,所以你在有这个关于 Quora 的问题 https://www.quora.com/Which-is-better-cloud-server-Amazon-AWS-or-Firebase

我想说的是,它更类似于 Heroku,它允许您轻松部署应用程序并将其与分析工具集成。如果您已阅读学习 Vue.js 2 手册(https://www.packtpub.com/web-development/learning-vuejs-2 ),那么你已经知道我有多爱希罗库了。我甚至有 Heroku 袜子!

What is Firebase?

我漂亮的 Heroku 袜子

然而,我发现 GoogleFirebase 控制台也非常好用而且简单易用。它还提供后端即服务。这个后端是为您的 web 和移动应用程序共享的,这在开发跨平台和跨设备应用程序时是一个巨大的帮助。Firebase 提供以下服务:

  • 认证:此使用 Firebase API 对使用不同提供商(Facebook、谷歌、电子邮件等)的用户进行认证。
  • 数据库:使用 Firebase 数据库 API 存储和检索您的数据。无需在不同的数据库提供程序之间进行选择,也无需建立连接。只需使用开箱即用的 API。
  • 托管:这个使用简单的 shell 命令托管和部署您的应用程序。
  • 存储:使用简单的 API 托管静态文件。

同样,如果您考虑如何将 Vue 应用程序与 Firebase API 集成,请不要再考虑它,因为有人已经为您完成了这项工作。使用 Firebase 控制台创建项目后,只需使用 Firebase 的vuefire包装器即可连接到数据库并获取数据。在查看中的内容 https://github.com/vuejs/vuefire 。实际上,这正是我们在第一章的PleaseIntroduceYourself应用程序中所做的。检查位于App.vue部件内部的代码:

//PleaseIntroduceYourself/src/App.vue
<script>
import Firebase from 'firebase'

let config = {
  apiKey: '... ',
  ...
  messagingSenderId: '...'
}

let app = Firebase.initializeApp(config)
let db = app.database()
let messagesRef = db.ref('messages')

export default {
  ...
 firebase: {
 messages: messagesRef.limitToLast(100)
 }
}
</script>

Firebase 对象中导出的所有内容都可以通过this关键字进行访问,就像我们访问datacomputed属性一样。我们将在本书中开发的应用程序中使用vuefire,以更好地了解其工作原理。

总结

在本章中,我们熟悉了 Vue.js、Bootstrap 和 Firebase。我们还分析了将 Vue.js 与 Bootstrap 和 Vue.js 与 Firebase 集成的工具。

因此,现在,我们已经熟悉了 Vue.js 应用程序,这些应用程序是使用单文件组件、Bootstrap 的网格系统、组件和 CSS 助手构建的,以使我们的生活更轻松,并使 Google Firebase 控制台具有其可能性。

此外,我们还知道如何初始化 Vue.js 项目,以及如何使用 Vue 指令、组件、存储和路由。

您还学习了如何利用 Bootstrap 的网格系统来实现应用程序布局的责任。

最后但并非最不重要的一点是,您学习了如何使用vuefire绑定在 Vue 应用程序中使用 Firebase API。

随着本章的结束,我们旅程的第一个导言部分也结束了。

在下一章中,我们将深入了解实现。作为一个带水肺的潜水池,我们将接受你迄今为止所学到的一切!

因此,我们将开始开发我们将在整本书中构建的应用程序,直到它准备好部署。我们将:

  • 定义应用程序将执行的操作及其要求
  • 定义我们为谁构建应用程序
  • 为应用程序构建基本模型
  • 使用 Vue 命令行界面构建应用程序

你和我一样兴奋吗?那么,让我们进入下一章!