Vue.js 基本语法

问题导向清单

普通 JS 式常规操作

作为 .js 文件,与其他常规 JS 文件操作无异。

					
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>MyVueWeb</title>
    <script src="js/vue.js"></script>
  </head>
  <body>
    <p id="demo">{{ msg }}</p>
    <script>
      new Vue({
        el: '#demo',
        data: {
          msg: 'Hello World!'
        }
      })
    </script>
  </body>
</html>
					
				

Vue.js 举例

声明式渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。

					
<div id="app">
  {{ message }}
</div>
					
				
					
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  }
})
					
				

说明:

el 代表 element。

数据 和 DOM 已经建立了关联,所有东西都是响应式的。打开浏览器的 JavaScript 控制台,并修改 app.message 的值,将看到上例相应地更新。

					
> app.message = "Hello Vue!"
< "Hello Vue!"
					
				

此时应当注意的是:直接修改 app.message 的值,页面的显示内容也会随之改变。也就是说,不再和 HTML 直接交互,一个 Vue 应用(new Vue())会将其挂载到一个 DOM 元素(#id)上,然后对其进行完全控制。HTML 元素只是切入点/入口,所有的改变/控制都会发生在新创建的 Vue 实例内部。

除了文本插值,还可以像这样来绑定元素 attribute(属性):

					
<div id="app"
  <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>
					
				
					
var app = new Vue({
  el: '#app',
  data: {
    message: '页面加载于 ' + new Date().toLocaleString()
  }
})
					
				

说明:

v-bind attribute 被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute。它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 title attribute 和 Vue 实例的 message property 保持一致”。

条件与循环

					
<div id="app">
  <p>正常文本。</p>
  <p v-if="seen">
    现在你能看到我。
  </p>
  <p>正常文本。</p>
</div>
					
				
					
var app = new Vue({
  el: '#app',
  data: {
    seen: true
  }
})
					
				

说明:

继续在控制台输入 app.seen = false,会发现之前显示的消息消失了。

					
正常文本。
正常文本。
					
				

不仅可以把数据绑定到 DOM 文本或 attribute,还可以绑定到 DOM 结构。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果。

v-for 指令可以绑定数组的数据来渲染一个项目列表:

					
<div id="app">
  <ol> 
    <li v-for="bookname in booklist">
      {{ bookname.text}}
    </li>
  </ol>
</div>
					
				
					
var app = new Vue({ 
  el: '#app',
  data: {
    booklist: [
      { text: '学习 HTML' },
      { text: '学习 JavaScript' },
      { text: '学习 Vue' } 
    ]
  }
})
					
				

说明:

在控制台里输入 app.booklist.push({ text: '新项目' }),会发现列表最后添加了一个新项目。

处理用户输入

为了让用户和应用进行交互,可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:

					
<div id="app">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">反转消息</button>
</div>

					
				
					
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  },
  methods: {
    reverseMessage: function() {
      this.message = this.message.split('').reverse().join('')
    }
  }
})
					
				

说明:

在 reverseMessage 方法中更新了应用的状态,但没有触碰 DOM —— 所有的 DOM 操作都由 Vue 来处理,所编写的代码只需要关注逻辑层面即可。

Vue 还提供了 v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定。

					
<div id="app">
  <p>{{ message }}</p>
  <input v-model="message">
</div>
					
				
					
var app = new Vue({ 
  el: 'app',
  data: {
    message: 'Hello World!'
  }
})
					
				

组件化应用构建

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:

在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:

					
<div id="app">
  <ol>
    <book-item></book-item>
    <book-item></book-item>
    <book-item></book-item>
  </ol>
</div>

<script type="text/javascript">
  //定义名为 book-item 的组件
  Vue.component('book-item', {
    template: '<li>《学好 Vue》</li>'
  })

  var app = new Vue({
    el: '#app'
  })
</script>
					
				
					
1.《学好 Vue》
2.《学好 Vue》
3.《学好 Vue》
					
				

从父作用域将数据传到子组件:

					
<div id="app">
  <ol>
    <book-item v-for="book in bookList" v-bind:book="book"></book-item>
  </ol>
</div>

<script type="text/javascript">
  //定义名为 book-item 的组件
  Vue.component('book-item', {
    //props(property 自定义属性集合/数组)
    props: ['book'],
    template: '<li>{{ book.bookname }} ¥{{ book.price }}元</li>'
  })

  var app = new Vue({
    el: '#app',
    data: {
      bookList: [
        { bookname:'《学好 HTML》', price:'18' },
        { bookname:'《学好 JavaScript》', price:'20' },
        { bookname:'《学好 Vue》', price:'25' }
      ]
    }
  })
</script>
					
				
					
1.《学习 HTML》 ¥18元
2.《学习 JavaScript》 ¥20元
3.《学习 Vue》 ¥25元
					
				

说明:

尽管这只是一个刻意设计的例子,但是已经设法将应用分割成了两个更小的单元。子单元通过 prop 接口与父单元进行了良好的解耦。可以进一步改进 <book-item> 组件,提供更为复杂的模板和逻辑,而不会影响到父单元。

在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理:

					
<div id="app">
  <app-nav>
  <app-view>
    <app-sidebar>
    <app-content>
  </app-view>
</div>
					
				

与自定义元素的关系

注意到 Vue 组件非常类似于自定义元素 —— 它是 Web 组件规范的一部分,这是因为 Vue 的组件语法部分参考了该规范。例如 Vue 组件实现了 Slot API 与 is attribute。但是,还是有几个关键差别:

虽然 Vue 内部没有使用自定义元素,不过在应用使用自定义元素、或以自定义元素形式发布时,依然有很好的互操作性。Vue CLI 也支持将 Vue 组件构建成为原生的自定义元素。

Vue 实例

创建一个 Vue 实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

					
var vm = new Vue({
  //选项对象
})
					
				

虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

当创建一个 Vue 实例时,你可以传入一个选项对象。使用选项对象来创建需要的行为。

数据与方法

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

					
var data = { 
  a: 1
}

var vm = new Vue({
  data: data
})

vm.a == data.a     //true

vm.a = 2
data.a             //=>2

data.a = 3
vm.a               //=>3
					
				

说明:

当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。也就是说如果你添加一个新的 property,比如:

					
vm.b = 'hi'
					
				

那么对 b 的改动将不会触发任何视图的更新。如果知道会在晚些时候需要一个 property,但是一开始它为空或不存在,那么仅需要设置一些初始值。比如:

					
data: {
  text: '',
  age: 0,
  bookList: [],
  isOK: false,
  error: null
}
					
				

这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

					
var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})
					
				
					
<div id="app">
  <p>{{ foo }}</p>
  <-- 这里的 'foo' 不会更新 -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>
					
				

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:

					
var data = {
  a: 1
}
var vm = new Vue({
  el: '#app',
  data: data
})

vm.$data === data   //=>true
vm.$el === document.getElementById('app')  //=>true

//$watch 是一个实例方法
vm.$watch('a', function(newValue, oldValue) {
  //这个回调函数将在 'vm.a' 改变后调用
}) 
					
				

实例生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程 —— 例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

比如 created 钩子可以用来在一个实例被创建之后执行代码:

					
new Vue({
  data: {
    a: 1
  },
  created: function() {
    //'this' 指向 vm 实例
    console.log('a is: ' + this.a);      //=>"a is: 1"
  }
})
					
				

说明:

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted、updated 和 destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。

注意:

不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。

生命周期图示

模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

如果熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,也可以不用模板,直接写渲染(render)函数,使用可选的 JSX 语法。

插值

文本

数据绑定最常见的形式就是使用 Mustache 语法(双大括号)的文本插值:

					
<span>Message: {{ msg }}</span>
					
				

Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。

通过使用 v-once 指令,也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

					
<span v-once>不会改变: {{ msg }}</span>
					
				

原始 HTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

					
<p>Using mustaches: {{ rawHTML }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

data: {
  rawHTML: '<span style="color: red">This should be red.</span>'
}
					
				

这个 span 的内容将会被替换成为 property 值(rawHtml 的值),直接作为 HTML —— 会忽略解析 property 值中的数据绑定。注意,不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面(UI),组件(component)更适合作为可重用和可组合的基本单位。

注意:

站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

Attribute

Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:

					
<div v-bind:id = "dynamicID"></div>
					
				

对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:

					
<button v-bind:disabled="isBtnDisabled">Btn</button>
					
				

说明:

如果 isBtnDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。

使用 JavaScript 表达式