简单了解Vue.js

2017-02-15 | 阅读

介绍vue的基本内容

vue.js是一套构建用户见面的渐进式框架, 与其他框架的不同在于,vue采用自底向上增量开发的设计方式。 Vue的核心库只关注视图层。 Vue的目标是通过尽可能简单的API实现 数据的响应式处理和组件化。

使用命令行工具开始 vue :

# 全局安装 vue-cli
$ npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
# 安装依赖
$ cd my-project
$ npm install
$ npm run dev

声明式渲染

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

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

Vue实例

Vue受MVVM模式启发,所以使用vm表示Vue实例。每个vue.js应用都是通过构造函数Vue创建的一个Vue根实例启动的。

在Vue实例中,普通属性放在data对象中,每个Vue实例会代理其data属性,且这些属性是响应式的,与视图绑定后,属性变化可以直接触发视图更新 :

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

所有暴露给视图的方法都是放在methods对象中的。而如果想要访问Vue对象自己的实例属性和方法,需要加上$前缀。

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})
vm.$el === document.getElementById('example') 

实例的生命周期如下图所示 :

Vue提供了beforeCreate,created等钩子方法。

el和ref

el是挂载点, 如:

<div id="app"/>
 new Vue({
            el: '#app',
            })

新的vue对象挂载到 el指向的id中,进行替换内容。

ref指的是 dom对象的引用,用于dom中快速查找元素:

<input ref="input"/>
this.$refs.input.blur()

模板语法简单介绍

Vue.js使用基于HTML的模板语法,允许开发者声明式的将DOM绑定至底层Vue实例的数据。

文本插值 :

<span>Message: </span>

mustache双大括号标签会被替代为对应Vue实例数据对象上的msg属性的值,且会在数据变动时更新视图。

使用v-once指令,当数据改变时,插值处的内容不会更新:

<span v-once>This will never change: </span>

如果绑定的对象是一段html,想让其输出html而不是纯文本,使用v-html :

<div v-html="rawHtml"></div>

插入的内容会被当做HTML解析。

对于HTML标签的属性,使用v-bind进行绑定 :

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

上述将div的id属性与dynamicId属性进行了绑定,v-bind可以使用简写: ,即 :id="dynamicId".

对于所有的数据绑定,都是支持表达式的。

使用v-if,根据属性决定元素是否展示。

v-on用于监听事件:

<a v-on:click="doSomething">

这里监听了onClick事件,当事件触发时,调用Vue实例methods对象中的doSomething方法。可以使用简写@,即@click="doSomething"

过滤器

使用过滤器进行文本格式化,但是效果可以用计算属性替代,作用不大。过滤器用于mustable插值和v-bind表达式。 过滤器位于表达式后,使用管道的写法:

<!-- in mustaches -->

表示此处插值为 message 经过过滤器capitalize 函数处理的结果。

而在vue实例中,如此定义过滤器 :

new Vue({
  // ...
  filters: {
    capitalize: function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
})

计算属性

计算属性,即将属性通过gettersetter函数进行获取或设置,以将原始数据按照要求进行处理,然后再作用到元素上。基本示例 :

<div id="example">
  <p>Original message: ""</p>
  <p>Computed reversed message: ""</p>
</div>

new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // a computed getter
    reversedMessage: function () {
      return this.message.split('').reverse().join('')
    }
  }
})

计算属性的特点是拥有缓存,计算属性基于依赖进行缓存,如上例中,计算属性reversedMessage只在其依赖this.message发生变化时才会重新求值。这也意味着,像下面的计算属性是不能更新的 :

computed: {
  now: function () {
    return Date.now() // 没有依赖
  }
}

计算属性默认只有getter,需要显示声明setter :

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

对于数据响应时,需要执行异步操作或开销大的操作时,使用watch

样式绑定

我们使用v-bind来绑定classstyle,以动态切换样式。

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"/>

上述代码表示,当isActive为真时,触发activeclass,当hasError为真时,div拥有text-dangerclass。

也可以用数组来表示class :

<div v-bind:class="[isActive ? activeClass : '', errorClass]">

此时div始终存在errorClass , 如果isActive为真时,添加activeClass

v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

直接绑定到一个样式对象通常更好,让模板更清晰:

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

条件渲染

在vue.js中,我们主要用v-if来实现条件渲染 :

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

这里根据vue对象中得ok属性,决定显示Yes或者No

对于<template>中的v-if :

<template v-if="ok">
  <h1>Title</h1>
</template>

对于template,这个元素实际上是不存在的,只是相当于一层包装,所以这里的v-if切换的是template的所有子元素。

使用v-else或者v-else-if来进行链式地判断。但是两者必须紧跟在条件元素后。

Vue会尽可能高效地渲染元素,通常会复用已有元素,而不是从头渲染。如果不需要这样的复用,可以使用key属性指定元素唯一不可复用 :

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show也可以根据条件展示元素,但是该属性只切换css属性display,元素本身始终被渲染并保留在DOM中。

列表渲染

使用v-for指令,根据一组数组选项列表进行渲染。v-for指令需要以item in items形式的特殊语法,items是源数据数组,item是数组元素迭代的别名:

<ul id="example-1">
  <li v-for="item in items">
    
  </li>
</ul>
items: [
  {message: 'Foo' },
  {message: 'Bar' }
]

可以添加第二个参数,以获取当前项的index ,也可以将in改为of

<ul id="example-2">
  <li v-for="(item, index) of items">
     -  - 
  </li>
</ul>

也可以通过v-for,对一个对象的属性进行迭代:

<ul id="repeat-object" class="demo">
  <li v-for="value in object">
    
  </li>
</ul>

v-for也可以对整数进行迭代 :

<div>
  <span v-for="n in 10"></span>
</div>

组件与v-for 略

v-for中,vue.js依旧高性能的去复用元素,而如果赋予每个元素以key,就可以在更新时,重用和重新排序现有元素 :

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

数组的动态更新 , 对于v-for绑定的数组,需要通过指定的方法操作数组时,才会更新视图 ,这些方法是: push() ,pop(),shift() ,unshift(),splice(),sort(),reverse(), 如 :

items.push({ message: 'Baz' })

Vue不能检测到 :

  1. 当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如: vm.items.length = newLength

事件处理器

使用 v-on监听DOM事件 :

<button v-on:click="counter += 1">增加 1</button>

可以在事件处理表达式中,传递原生DOM事件:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">Submit</button>
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) event.preventDefault()
    alert(message)
  }
}

Vue.jsv-on提供了事件修饰符,以使methods可以专注于纯粹的逻辑处理,而将DOM事件细节处理归于这些修饰符:

<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联  -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

在监听键盘事件时,我们经常需要监测常见的键值,v-on支持在监听键盘事件时添加按键修饰符 :

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">

表单控件绑定

使用v-model,我们在表单控件元素上创建了双向数据绑定:

绑定文本:

<input v-model="message" placeholder="edit me">
<p>Message is: </p>

绑定复选框:

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox"></label>

多个复选框绑定到一个数组中:

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: </span>

绑定单选框结果到一个变量 :

<div id="example-4" class="demo">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: </span>
</div>

绑定单选列表结果 :

<div id="example-5" class="demo">
  <select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: </span>
</div>

对于单选按钮,v-model绑定的是逻辑值true和false,勾选框及选择列表选项,v-model绑定的是静态字符串value 。 也可以将value绑定到一个动态的属性中 :

<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a

v-model拥有以下修饰符:

  • .lazy : 不在input,而是在change事件中进行同步。
  • .number : 将用户的输入值处理为Number
  • .trim : 自动过滤用户输入的首尾空格。

组件

组件是vue.js最强大,也是最重要的功能。组件可以扩展HTML元素,封装可重用代码。

注册一个全局组件,使用 :Vue.component(tagName, options),组件注册后,就可以在父实例的模板中以自定义元素的形式来使用:

<my-component></my-component>

也可以在局部注册一个组件:

var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  components: {
    // <my-component> 将只在父模板可用
    'my-component': Child
  }
})

或者 :

import navTitle form 'title.vue'
 new Vue({
  components: {
    navTitle
  }
})

DOM模板解析说明

当使用DOM作为模板时,如将el选项挂载到一个已存在的元素上,但Vue只在浏览器解析和标准化HTML后才能获取模板内容。尤其这些元素<ul>,<ol>,<table>,<select>限制了能被它包裹的元素,<option>只能出现在其他元素内部。

如 :

<table>
  <my-row>...</my-row>
</table>

<my-row>被认为是无效的内容,因此在渲染时出错。 变通的方案是使用特殊的is属性 :

<table>
  <tr is="my-row"></tr>
</table>

组件中 data必须是一个函数 :

Vue.component('my-component', {
  template: '<span></span>',
  data() {
   	return {message: 'hello'}
  }
})

vue中组建交互方式

vue.js中,父子组件的关系为 props down,events up .即父组件通过 props属性来传递数据给子组件,而子组件通过events传递信息给父组件 :

prop是父组件用来传递数据的一个自定义属性,子组件需要显式地声明props

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 可以用在模板内
  // 同样也可以在 vm 实例中像 “this.message” 这样使用
  template: '<span></span>'
})

prop 校验与默认值

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

camelCase指驼峰式命名,而kebab-case指使用短横线隔开的命名方式。

HTML中不区分大小写,所以使用非字符串模板时,一些属性的名字会从 camelCase转换为kebab-case

注意字面量语法和动态语法,初学者常犯的一个错误是使用字面量语法传递数值:

<!-- 传递了一个字符串"1" -->
<comp some-prop="1"></comp>

因为它是一个字面 prop ,它的值以字符串 “1” 而不是以实际的数字传下去。如果想传递一个实际的 JavaScript 数字,需要使用 v-bind ,从而让它的值被当作 JavaScript 表达式计算:

<!-- 传递实际的数字 -->
<comp v-bind:some-prop="1"></comp>

通过自定义事件,获取子组件的反馈

我们使用 v-on来绑定自定义事件 , 在Vue的实例中,都实现了事件接口:

  • 通过 $on(eventName)监听事件
  • 通过$emit(eventName)触发事件

Vue的事件系统是分离自浏览器的EventTarget API的,尽管其运行相似,但是$on不是 addEventListener$emit也不是dispatchEvent.

在组件中 :

Vue.component('button-counter', {
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

父组件中则使用v-on来监听该事件:

<button-counter @increment="incrementTotal"/>

v-model进行双向绑定

自定义事件可以用来创建自定义的表单输入组件,v-model进行双向绑定,但事实上是一个语法糖 :

<input v-bind:value="something" v-on:input="something = $event.target.value">

而在其他组件中,相当于以下形式:

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>

所以要让组件的v-model生效,必须:

  • 接受一个value属性
  • 在有新的value时触发input事件。

编译作用域

假定模板为:

<child-component>
  
</child-component>

message绑定到父组件的数据,而不是子组件的数据。组件作用域:

父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译。

使用slot插槽

加入my-component组件中有一下模板:

<div>
  <h2>我是子组件的标题</h2>
  <slot>
    只有在没有要分发的内容时才会显示。
  </slot>
</div>

父模板如此定义:

<div>
  <h1>我是父组件的标题</h1>
  <my-component>
    <p>这是一些初始内容</p>
  </my-component>
</div>

渲染结果为 :

<div>
  <h1>我是父组件的标题</h1>
  <div>
    <h2>我是子组件的标题</h2>
    <p>这是一些初始内容</p>
  </div>
</div>

混合 mixins

混合是一种灵活的分布式复用vue组件的方式。混合对象中可以包含任意组件选项。而组件使用混合,以将这些混合对象的组件选项将被混入该组件本身的选项。如下:

// 定义一个混合对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}
// 定义一个使用混合对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})
var component = new Component() // -> "hello from mixin!"

当组件和混合对象含有同名选项时,这些选项将以恰当的方式混合。比如,同名钩子函数将混合为一个数组,因此都将被调用。另外,混合对象的 钩子将在组件自身钩子之前调用 :

var mixin = {
  created: function () {
    console.log('混合对象的钩子被调用')
  }
}
new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})
// -> "混合对象的钩子被调用"
// -> "组件钩子被调用"

值为对象的选项,例如 methods, components 和 directives,将被混合为同一个对象。 两个对象键名冲突时,取组件对象的键值对。

通过Vue.mixin来注册全局混合对象。