文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

早在9月份,流行的JavaScript框架Vue.js就发布了第2版,自从发布以来,我一直都想试试看它是如何工作的。作为一名对Angular和React都非常熟悉的人,我很期待看到Vue和它们之间的异同。

Vue.js 2.0号称有非常卓越的性能统计数据,相对较小的有效载荷(通过压缩和gzip,Vue 打包运行时的版本仅有16kb),并且一同更新了配件库,例如vue-router, vuex, 它们是Vue状态管理库。仅仅一篇文章远远不够讲述Vue, 所以请留意后续的文章,我们将更多的关注一些库如何与核心框架巧妙结合。

第 1 段(可获 1.71 积分)

来自其他库的灵感

在本教程中,您将看到许多Vue受其他框架影响的特性。 很高兴能看到这些新的框架吸取了其他框架的优点并加以改进,这一点真的很不错。尤其是,你会看到Vue的模板与Angular非常相似,但它的组件以及组件生命周期的方法名又和React非常相似 (也和Angular 2相似)。

其中一个例子是,与React 以及现代的JavaScript框架非常相似, Vue使用了虚拟DOM来提高渲染效率。 Vue使用了snabbdom的一个分支,一个流行的虚拟DOM 库。 The Vue 网站上有关于 基于虚拟DOM渲染的文档介绍,但作为用户你只需要知道Vue渲染很快 (事实上,在许多情况下它比React有更好的性能),同时意味着你可以放心,因为你建立在一个可靠的平台之上。

第 2 段(可获 2.15 积分)

组件,组件,组件(重要的事情说三遍)

就像其他框架,Vue的核心构件是组件。你应该使用一系列彼此关联的组件相互联结,并最终构建出你的应用。 Vue.js 更进一步约定组件应该定义在一个个单一的 .vue 文件中, 然后可以通过构建工具解析 (后面我们会马上谈到)。尽管以上约定并不是强制要求的,但鉴于这篇文章的目的是充分探索Vue是如何运行的,我会在我的应用中遵循本约定。

第 3 段(可获 1.26 积分)

一个Vue 文件如下所示:

<template>
  <p>This is my HTML for my component</p>
</template>

<script>
export default {
  // 组件的所有代码
}
</script>

<style scoped>
  /* 这里写CSS
   * 通过使用`scoped`,
   * 我们确保把所有CSS都限定在这个组件之中
   */
</style>

另外, 如果你不喜欢将组件所有的部件放在一个文件里,你也可以给定每个元素的`src`属性,并且分别指向单独的HTML,JS或者CSS文件.

建立一个项目

尽管从已有的优秀项目[Vue CLI][https://github.com/vuejs/vue-cli] 能够很容易建立一个完整的项目, 但当开始研究一个新的库的时候,我更喜欢从头开始,这样我可以更加深刻的理解这些工具。

第 4 段(可获 1.09 积分)

Webpack 是我的首选的构建工具,因此需要使用vue-loader 插件来支持我之前提到的Vue.js 组件格式。最后我们还需要Babel 和 ES2015 ,这样我们就可以使用 ES2015语法了。

设置Webpack配置方法如下:

  • 让Webpack从src/main.js开始构建,输出到build/main.js
  • 告知Webpack使用 vue-loader来解析 .vue 文件
  • 告知Webpack 使用 Babel 解析.js 文件
  • 告知 vue-loader应该使用 Babel转换所有的JavaScript。这意味着 *.vue文件中的JavaScript会使用Babel进行转译。
第 5 段(可获 1.41 积分)
module.exports = {
  entry: './src/main',
  output: {
    path: './build',
    filename: 'main.js',
  },
  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue',
      },
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/,
      },
    ],
  },
  vue: {
    loaders: {
      js: 'babel',
    },
  },
}

最后,我们将创建一个HTML文件,准备工作完成了!

<!DOCTYPE html>
<html>
  <head>
    <title>My Vue App</title>
  </head>
  <body>
    <div id="app">
    </div>
    <script src="/build/main.js"></script>
  </body>
</html>
第 6 段(可获 0.16 积分)

我们创建了一个空的div元素,它的ID命名为app,这是我们vue应用的入口。我总是喜欢用一个div,而不是body元素, 因为这让我控制了页面的其余部分。

编写第一个Vue.js 2.0 应用程序

我们将保存教程中的每一个程序,在我们深入了解其它复杂的东西之前,我们想编写一个在屏幕上显示"Hello World"的Vue程序。

每一个Vue应用程序都必须引入库且实例化Vue :

import Vue from 'vue'

const vm = new Vue({
  el: '#app',
})
第 7 段(可获 1.26 积分)

我们给Vue一个元素用于渲染到页面上,我们创建了一个Vue的应用程序!我们通过选择器选中的元素作为Vue替换我们应用程序的容器。这意味着当Vue运行后它把div#app 替换为我们创建的应用程序。

我们使用vm作为变量名是因为它表示 “视图模型”。虽然不能严格的与 “模型 视图 视图模型” (MVVM) 模式,关联, Vue的部分灵感就来自于它,且使用vm作为变量名是一种约定俗成。当然你可以使用你喜欢的其它名称。

第 8 段(可获 1.36 积分)

到目前为止,我们的应用程序没做任何事,因此让我们开始创建第一个组件App.vue,它可以渲染一些东西在页面上。

Vue没有硬性的规定应用程序的结构,所以完全取决你自己。最终在本例的APP中我对每一个组件创建了一个文件夹 (我喜欢用大写字母,表示一个组件),有如下四个文件:

  • index.vue
  • template.html
  • script.js
  • style.css

App/index.vue 只是简单引用了其它三个文件:

<template src="./template.html"></template>
<script src="./script.js"></script>
<style scoped src="./style.css"></style>
第 9 段(可获 0.88 积分)

我喜欢叫它index.vue, 但你可能想把命名为 app.vue ,这是更容易搜索。与App/app.vue相比较而言我更喜欢App/index.vue,但你可能会不同意,所以你可以自由选择任何你或你的团队最喜欢的方式。

就目前而言,我们的模板里只有 <p>Hello World</p>,和一个空的CSS文件。主要工作在script.js文件中,内容如下:

export default {
  name: 'App',
  data() {
    return {}
  },
}

我们创建了一个组件并命名为App,这主要是用于调试目的,然后定义该组件所拥有并负责的数据。现在,我们没有任何数据,因此我们仅需要告诉Vue返回一个空的对象。稍后我们将看到一个使用数据的组件。

第 10 段(可获 1.65 积分)

现在我们回到src/main.js,告诉Vue实例渲染我们的App组件:

import Vue from 'vue'

import AppComponent from './App/index.vue'

const vm = new Vue({
  el: '#app',
  components: {
    app: AppComponent,
  },
  render: h => h('app'),
})

首先,我们引入这个组件,相信Webpack 和vue-loader 可以解析它。接着我们声明了这个组件。这个步骤很重要:默认情况下,Vue组件不是全局变量。每个组件必须有一个它使用到的所有组件的列表,它将映射到标签。在本例中,我们像这样注册了我们的组件:

第 11 段(可获 1.04 积分)
components: {
  app: AppComponent,
}

这意味着在我们的模板中我们可以使用app元素引用我们的组件。

最后,我们定义了函数 render 。这是一个辅助函数, 通常被称为h,这能够创建元素。它与React使用的 React.createElement函数不一样。在本例中,我们使用了一个字符串 'app',因为我们想渲染的组件被注册为了一个标签app。

由于定义了HTML模板,所以通常(本教程的其它地方)我们不会再其它组件中使用render 函数,但如果你想了解更多渲染相关的东西,请阅读Vue.js渲染函数指南 。

第 12 段(可获 1.48 积分)

一旦我们做完这些,你应该就能在屏幕上看到“Hello World”,现在我们启动运行Vue.js!

Vue Devtools

在我们开始了解复杂应用程序之前,现在正是安装Vue Devtools工具的时候。 这些内置的Chrome 开发者工具将是查看每一个组件状态的很好方式。

Screenshot of the Vue Devtools

构建App

作为例子程序,我们将使用GitHub API来构建一个输入用户名就可以看到该用户的一些统计数据的应用程序。我选了大多数人都熟悉的、不需要身份认证、能获取很多信息的Github API 。

第 13 段(可获 1.79 积分)

在开始一个应用程序之前,我喜欢快速思考一下我们需要哪些组件,并且我认为我们的应用还应该提供额外的两个组件:GithubInput,用来接受用户的输入,以及GithubOutput,在屏幕上显示用户信息。我们从输入开始。

Vue.js的形式

首先,我们创建 src/GithubInput/index.vue,用来为我们的组件加载模板,脚本以及CSS文件。当创建这个组件时我们做的不同的一件事情是创建一段与组件相关联的数据-这与React中状态的概念非常相似:

第 14 段(可获 1.29 积分)
export default {
  name: 'GithubInput',
  data() {
    return {
      username: '',
    }
  }
}

这段表明该组件有一段数据,拥有username字段。稍后,我们会根据用户输入来更新它。

模板src/GithubInput/template.html, 目前进包含简单的<p>github input</p>. 稍后我们会填入合适的字串。 我喜欢写入一些伪HTML,这样的话当创建一个新的组件时,我可以检查模板能正常连接。

最后,为了将组件显示到屏幕上,我需要注册它到应用组件,因为应用组件将会启动它。

第 15 段(可获 1.14 积分)

为了做到这些,我更新了 src/App/script.js,并且告诉它GithubInput组件:

import GithubInput from '../GithubInput/index.vue'

export default {
  name: 'App',
  components: {
    'github-input': GithubInput,
  },
  data() {
    return {}
  },
}

接着,我更新了app 组件模板:

<div>
  <p>Hello World</p>
  <github-input></github-input>
</div>

Vue组件的一个限制(Angular和React也有这个限制)是每个组件必须有一个根节点,所以当一个组件必须提供多个元素时,重要的一点就是记住将这些元素包裹起来,通常使用一个div标记。

第 16 段(可获 0.8 积分)

跟踪一个表单输入

我们的GithubInput组件需要做两件事情:

  • 跟踪输入的当前值
  • 报告当前值发生变化,以便其它组件可以知道并更新它们的状态

我们可以创建一个带有输入元素的表单作为第一个版本。Vue的内置指令能够使得我们跟踪表单值。GithubInput模板看起来像这样:

<form v-on:submit.prevent="onSubmit">
  <input type="text" v-model="username" placeholder="Enter a github username here" />
  <button type="submit">Go!</button>
</form>
第 17 段(可获 0.93 积分)

有两个重要的属性你将注意到: v-on 和 v-model.

v-on 用于Vue中如何绑定到DOM事件以及调用函数。例如 <p v-on:click="foo">Click me!</p> ,每次Click me!被点击时都会调用组件的foo方法。如果你想要非常详细的浏览事件处理,我强烈推荐 Vue 文档事件处理.

v-model 创建了表单输入和数据段之间的双向绑定。 在这个场景背后, v-model 不停的监听表单输入的变化事件并且不断更新Vue组件的数据使之匹配。

第 18 段(可获 1.19 积分)

考虑到上文我们的模板,这里给出了我们如何使用v-on和v-model处理表单中的数据:

  • v-on:submit.prevent="onSubmit" 绑定的方法onSubmit将会在表单提交时运行。增加.prevent意味着Vue会自动阻止默认行为的发生 (如果不想让Vue这样做,我们可以在代码中调用event.preventDefault(),但是我们不妨利用Vue的特性)。
  • v-model:username 绑定输入的值到一个值username,在我们的代码中. 对于熟悉Angular的人会认为这同ng-model非常相似。 当我们创建GithubInput时我们声明它拥有一个数据段username, 并且我们在这里将该数据段绑定到一个输入域。 两者会自动保持同步。
第 19 段(可获 1.6 积分)

现在,回到组件中,我们可以声明一个方法onSubmit。 注意:这里的方法名完全是随意的 –你可以按理喜欢的方式命名 – 但我喜欢坚持使用事件触发动作进行函数命名的约定。

name: 'GithubInput',
methods: {
  onSubmit(event) {
    if (this.username && this.username !== '') {
    }
  }
},

我们可以直接使用this进行数据引用,因此this.username将能获取到文本输入框中的最新的值。如果该值不为空,我们想让其它组件知道数据发生了变化。基于这个目的,我们将使用消息总线。即让组件触发和监听事件的功能。 如果你的应用程序越来越大时你就应该考虑使用更佳好的方式,比如Vuex,在后面的教程中,我们将具体讲解,目前,使用消息总线就可以了。

第 20 段(可获 1.74 积分)

好消息是我们可以使用一个空的Vue实例作为消息总线, 正如Vue文档中推荐的那样。我们将创建 src/bus.js,它只是简单的创建Vue实例并export导出:

import Vue from 'vue'
const bus = new Vue()

export default bus

GithubInput中我们可以导入该模块,当username发生变化时使用它发送一个事件。

import bus from '../bus'

export default {
  ...,
  methods: {
    onSubmit(event) {
      if (this.username && this.username !== '') {
        bus.$emit('new-username', this.username)
      }
    }
  },
  ...
}
第 21 段(可获 0.65 积分)

随着表单的完成,我们准备好开始做结果数据相关的事情。

显示来自Github的结果

我们将创建 GithubOutput组件,使用我们以前用过的相同的结构。 在 GithubOutput/script.js 中,我们也同样引入总线模块,因为我们需要它知道什么时候username发生改变。 该组件负责的数据将是一个对象,用来将Github的username 映射到从GitHub API获得的数据。 这意味着我们不必每次都从API请求数据; 如果我们之前已经获取了数据,我们可以简单地重用它。 我们还将存储最新得到的username数据, 因此我们知道什么数据会显示在屏幕上。

第 22 段(可获 1.59 积分)
import bus from '../bus'

export default {
  name: 'GithubOutput',
  data() {
    return {
      currentUsername: null,
      githubData: {}
    }
  }
}

当组件创建后我们就想监听在消息总线中触发的new-username 事件。幸运的是 Vue支持很多生命周期的回调,如created。 我们是负责任的开发人员因此我们使用了destroyed 事件在组件销毁的时候停止事件监听:

export default {
  name: 'GithubOutput',
  created() {
    bus.$on('new-username', this.onUsernameChange)
  },
  destroyed() {
    bus.$off('new-username', this.onUsernameChange)
  },
  ...
}
第 23 段(可获 0.61 积分)

我们定义了上面要调用的方法 onUsernameChange,它对currentUsername属性进行了赋值:

methods: {
  onUsernameChange(name) {
    this.currentUsername = name
  }
},

请注意我们没有显式的把方法onUsernameChange绑定到当前作用域上。你在Vue组件中定义的方法,Vue自动调用了myMethod.bind(this) ,因此他们总是绑定到组件上的。这也就是为什么要把组件的方法定义在 methods 对象中的原因之一,因为Vue能完全识别且可以设置他们。

第 24 段(可获 1.01 积分)

条件渲染

如果没有username,比如 组件第一次创建后此时它还没值,我们可能想要给用户显示一个消息。Vue 拥有一些条件渲染技术,但最简单的是 v-if 指令,它又一个条件表达式,只在条件为true时才渲染元素。它可以和 v-else成对使用:

<div>
  <p v-if="currentUsername == null">
    Enter a username above to see their Github data
  </p>
  <p v-else>
    Below are the results for {{ currentUsername }}
  </p>
</div>

 这看起来和Angular开发非常类似。我们使用双等号而不是三等号,因为当 currentUsername为null 或者 undefined,null == undefined 表达式为true.

第 25 段(可获 1.26 积分)

从GitHub获取数据

Vue.js 没有内置HTTP库,是有充分的理由的。因为有很多浏览器开始提供fetch API实现(虽然写作本文时, IE11, Safari  iOS Safari还未提供)。基于本教程的目的我不会使用到polyfill,但如果你需要还是可以在浏览器中使用 polyfill 。如果你不喜欢fetch API,有很多第三方库,比如 在Vue文档中提到的 Axios

我是非常支持框架不内置HTTP库的,比如 Vue;它可以保证框架的大小较小且能让开发者自己选择他们熟悉的库,也更容易定制它们的API。 在本文中我坚持使用 fetch API,但你可以自由的选择一个你喜欢的。

第 26 段(可获 2.01 积分)

如果你需要了解fetch API, 看看SitePoint网站上Ludovico Fischer的文章,将可以让你更快的了解。

为了执行HTTP请求,我们在组件中定义另外一个方法 fetchGithubData,它可以调用GitHub API 和存储结果。它会先检查看看我们是否已经有这个用户的数据,而不是直接请求:

fetchGithubData(name) {
  // if we have data already, don't request again
  if (this.githubData.hasOwnProperty(name)) return

  const url = `https://api.github.com/users/${name}`
  fetch(url)
    .then(r => r.json())
    .then(data => {
      // in here we need to update the githubData object
    })
}
第 27 段(可获 0.88 积分)

当用户名发生改变时调用这个方法:

onUsernameChange(name) {
  this.currentUsername = name
  this.fetchGithubData(name)
}

还有一件事要注意的,由于Vue跟踪的是你的数据,因此它知道何时更新视图。这有一个非常好的 Reactivity指南,它解释了其中的细节,但Vue无法感知到你在一个对象上添加或删除属性,比如像这样:

this.githubData[name] = data

Vue无法识别也就无法更新视图。因此我们可以使用特殊的方法 Vue.set ,这就明确的告知Vue 我们添加了一个key。 上面的代码看起来会像这样:

第 28 段(可获 1.48 积分)
Vue.set(this.githubData, name, data)

这代码将修改this.githubData,对其添加了一个key和值。它能通知Vue发生了变化因而它就能渲染了。

现在我们的代码看起来像这样:

const url = `https://api.github.com/users/${name}`
fetch(url)
  .then(r => r.json())
  .then(data => {
    Vue.set(this.githubData, name, data)
  })

虽然我们还没有编写视图代码在屏幕上显示这些数据, 你应该能够在填写用户名后,使用Vue开发工具看到从Github请求的数据。这说明,这些开发工具是非常有用的和强大的; 你可以检查任何组件的本地状态,看看到底发生了什么。

第 29 段(可获 1.21 积分)

在视图中显示出统计数据

现在我们修改模板来用于显示数据。我们把代码包裹在 v-if 指令中,因此我们只在请求结束后才渲染数据:

<div v-if="githubData[currentUsername]">
  <h4>{{ githubData[currentUsername].name }}</h4>
  <p>{{ githubData[currentUsername].company }}</p>
  <p>Number of repos: {{ githubData[currentUsername].public_repos }}
</div>

这样,我们现在可以显示GitHub上的详细数据到屏幕上了,我们的程序也算是完成了!

重构

现在我们肯定还可以做一些改进。上面的HTML只呈现了 GitHub 数据中的一小部分– 当前用户数据。在另外一个组件中显示用户数据是非常完美的。

第 30 段(可获 1.34 积分)

我们创建一个组件 GithubUserData,和其它组件的结构相同。但有一点不同 –这个组件有个属性data, 它表示用户数据。 Properties (or, “props”)表示这个数据来自它的父组件传递来的, 这一点Vue与React非常相似。在Vue中你必须显式的声明组件需要的属性,因此在这里我们的组件只需要一个属性 data:

export default {
  name: 'GithubUserData',
  props: ['data'],
  data() {
    return {}
  }
}
第 31 段(可获 1.09 积分)

我真正喜欢 Vue 的其中一个原因是必须显式的声明,所有使用到的属性、数据以及组件都必须先声明才能使用。当项目变得越来越大和越来越复杂时,代码仍然很清晰。

在新的模板中,我们有着完全相同的 HTML,虽然我们可以使用 data 进行引用,而不是 githubData[currentUsername]:

<div v-if="data">
  <h4>{{ data.name }}</h4>
  <p>{{ data.company }}</p>
  <p>Number of repos: {{ data.public_repos }}
</div>

为了使用这个组件,我们需要更新 GithubOutput 组件。首先,我们导入并注册 GithubUserData:

第 32 段(可获 1.03 积分)
import bus from '../bus'
import Vue from 'vue'
import GithubUserData from '../GithubUserData/index.vue'

export default {
  name: 'GithubOutput',
  components: {
    'github-user-data': GithubUserData,
  },
  ...
}

你可以为这个组件指定任意的名称进行定义,在这里我用的是 github-user-data, 你可以用你想要的任意名称。建议你用破折号来隔开名称中有意义的单词。Vue 并不强调这个,但是 W3C 规范在对自定义元素状态要求必须有一个破折号,以避免名称和将来版本的 HTML 中存在命名冲突。

第 33 段(可获 0.83 积分)

一旦我们定义完组件,就可以在例如下的模板中使用:

<p v-else>
  Below are the results for {{ currentUsername }}:
  <github-user-data :data="githubData[currentUsername]"></github-user-data>
</p>

这里最关键的地方是我们如何传递 data 属性到组件中:

:data="githubData[currentUsername]"

属性前开始的分号至关重要,它告诉 Vue 我们所传递的属性时动态的,组件将在每次数据有变更时自动更新。Vue 会计算 githubData[currentUsername] 的值并确保 GithubUserData 组件在每次数据更改时候得以更新。

第 34 段(可获 1.01 积分)

如果你觉得:data 有点短,不可思,你可以使用较长的v-bind: 语法

v-bind:data="githubData[currentUsername]"

这两种方法是等价的,所以使用你喜欢的方式。

结论

通过这些,我们的Vue.js 2.0 GitHub 应用程序已经相当好了!你可以在GitHub找到所有的代码,甚至可以查看在线运行的应用程序

开始使用Vue时,我有很高的期望,因为我只听说了它好的一面,但现在我很高兴的说它真的达到了我的预期。使用Vue的感觉就像是采用React最好的部分并且将它们同Angular最好的部分融合。一些指令(像v-if,v-else,v-model等等)是非常容易开始使用的(也比React JSX语法中的条件更易快速理解),但是 Vue 组件系统非常类似于React的。

第 35 段(可获 1.81 积分)

我们鼓励把现有系统拆分成小的组件,现在我的经验可以直白的告诉你这一过程是很轻松的。而且我不得不称赞 Vue 团队写的文档太棒了。文档指南相当优秀,API 文档也非常清晰,很容易找到你需要找的内容。

如果你喜欢这篇文章并且想要学习更多的只是,那么一定要去 Vue.js 官网。如果你对 Vue 的其它库感兴趣,比如 状态控制的库 Vuex,以及路由库 Vue router,那么请锁定 SitePoiint 的 JavaScript 频道,我将在未来的文章里继续我的 Vue 探险之路!

第 36 段(可获 1.56 积分)

文章评论

广州访客
火得不行不行的