MVVM:面向数据开发,传统MVP中,面向DOM开发
Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的。相比于Angular.js,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。
如果你之前已经习惯了用jQuery操作DOM,学习Vue.js时请先抛开手动操作DOM的思维,因为Vue.js是数据驱动的,你无需手动操作DOM。它通过一些特殊的HTML语法,将DOM和数据绑定起来。一旦你创建了绑定,DOM将和数据保持同步,每当变更了数据,DOM也会相应地更新。
当然了,在使用Vue.js时,你也可以结合其他库一起使用,比如jQuery。
Vue
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层
,
不仅易于上手,还便于与第三方库或既有项目整合
。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动
。
对比其他框架
MVVM模式
下图不仅概括了MVVM模式(Model-View-ViewModel),还描述了在Vue.js中ViewModel是如何和View以及Model进行交互的。
ViewModel是Vue.js的核心,它是一个Vue实例。Vue实例是作用于某一个HTML元素上的,这个元素可以是HTML的body元素,也可以是指定了id的某个元素。
当创建了ViewModel后,双向绑定是如何达成的呢?
首先,我们将上图中的DOM Listeners
和Data Bindings
看作两个工具
,它们是实现双向绑定的关键
。
从View侧看,ViewModel中的DOM Listeners工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;
从Model侧看,当我们更新Model中的数据时,Data Bindings工具会帮我们更新页面中的DOM元素。
Hello World示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World示例</title>
</head>
<body>
<!-- 1.这是我们的View-->
<div id="app">
</div>
</body>
<!-- 引入JS类库 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
// 2.这是我们的Model
var exampleData = {
message: 'Hello World!'
}
// 3.创建一个 Vue 实例,即ViewModel,它连接 View 与 Model
new Vue({
el: '#app',
// data: exampleData
// 或者
data: {
message: 'Hello World!'
}
})
</script>
</html>
使用Vue的过程就是定义MVVM各个组成部分的过程的过程。
- 定义View
- 定义Model
-
创建一个 Vue 实例,即ViewModel,它连接 View 与 Model
- 声明式渲染
在创建Vue实例时,需要传入一个选项对象,选项对象可以包含数据、挂载元素、方法、模生命周期钩子等等。
示例中,选项对象的
el属性
指向View
,el: '#app'
表示该Vue实例将挂载到<div id="app">...</div>
这个元素;data属性指向Model
。
组件化应用构建
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
生命周期图示
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。 同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
比如created
钩子可以用来在一个实例被创建之后执行代码:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
也有一些其它的钩子,在实例生命周期的不同阶段被调用,如mounted
、updated
和destroyed
。生命周期钩子的this
上下文指向调用它的 Vue 实例
。
不要在选项属性或回调上使用箭头函数,比如created: () => console.log(this.a)
或vm.$watch('a', newValue => this.myMethod())。
因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,
经常导致Uncaught TypeError: Cannot read property of undefined
或Uncaught TypeError: this.myMethod is not a function
之类的错误。
计算属性和侦听器
- 计算属性 对于任何复杂逻辑,你都应当使用计算属性。
<div id="example">
// viewmodel属性值
<p>Original message: ""</p>
// 计算属性值
<p>Computed reversed message: ""</p>
// 方法返回值
<p>Reversed message: ""</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
// computed内声明计算属性
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
},
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
})
计算属性定义的逻辑也可以使用一个方法代替,从而实现相同的效果的,但不同的是计算属性是基于它们的依赖进行缓存的
。
只在相关依赖发生改变时它们才会重新求值。这就意味着只要message
还没有发生改变,多次访问reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
- 侦听器/侦听器 Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。
Class 与 Style 绑定
条件渲染
列表渲染
# 使用is属性解决组件标签违反H5规范问题
# 场景:如标准H5规范:table-->tbody-->tr、现需要组件列表渲染tr,因组件标签名称与tr不同导致H5渲染有bug,可是使用 <tr is="row"></tr> 现实row组件与tr标签绑定
# 使用ref引用操作DOM文档、ref定义到组件,表示可以直接引用组件
<div ref="hello"></div>
this.$refs.hello ==> <div ref="hello"></div>
事件处理
表单输入绑定
组件
# 组件之间数据传递可以使用类型校验
# 无校验
props : ['one','two']
# 校验
props : {
one : {
type :String,
required : true,
default : "默认值",
validator : function(value){
return (value.length > 5 && value.length < 32)
}
},
// 数字或者字符串
two : [number,String]
}
# 非父子组件之间传值(Bus/总线/发布订阅/观察者模式)
插槽
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。
# 具名插槽:针对组件内有有多个插槽时,名称指定调用插槽
过渡 & 动画
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。 包括以下工具:
- 在 CSS 过渡和动画中自动应用 class
- 可以配合使用第三方 CSS 动画库,如
Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如
Velocity.js