我从未见过如此简洁易懂的Vue教程
我从未见过如此简洁易懂的Vue教程
这是一篇长文,按照我自己的逻辑重新整理一下,包含所有Vue的基础知识点。
但是我更建议你先简略的阅读官方的文档,因为本文具有一定的阅读门槛,同时我也竭尽所能把这门槛降到最低,同样你也可以把本文作为快速回忆教程。
发挥100%的专注力,调动体内所有的热情,你将做到很多令人惊叹的事情
什么是MVVM ?
对比以前的mvc
或者mvp
,就是把C
或者p
替换成vm
。
vm
就是上图,请仔细看一些细节,vm
监听DOM
,当数据改变的时候,vm
会去自动更新视图。
面向未来的组件系统
实现了一些未来的w3c
规范(暂不赘述)
- Web 组件规范
- Slot API
根 vue 实例
let viewModel = new Vue({
// 包含数据、模板、挂载元素、方法、生命周期钩子等选项
})
Hello Wrold 例子
<!-- 这是我们的 View -->
<div id="app">
Hello {{ name }}!
</div>
// 这是我们的 Model
var model = {
name: 'Vue.js'
}
// 创建一个 Vue 实例或 "viewModel"
// 它连接 View 与 Model
var viewModel = new Vue({
el: '#app',
data: model
})
组件 Component 构造器
vue.extend()
返回的只是一个构造器,我们需要通过vue.extend()
的返回值和new
关键字创建实例。
当我们注册为组件的时候,内部就已经帮我们创建好了实例。
Tip: 如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。
<div id="app">
<v-footer></v-footer> <!-- 加一个v前缀,跟原生的footer标签区别开来 -->
</div>
let Footer = vue.extend({
template: '<div>我是页脚组件</div>'
})
// 注册为全局组件
Vue.component('v-footer', MyComponent)
// 创建根实例
new Vue({
el: '#app'
})
生命周期
上图的就是
ViewModel
的生命周期,仔细的看其实并不难。目前先了解一下。
在传统软件工程中,特别是QT类桌面端软件,都有软件的生命周期,还有比如Android
的生命周期,React
的生命周期。
目前的前端趋势正在向此方面靠近。
流程大致像这样
created()->beforeCompile()->compiled()->ready()
->attached()->detached()->beforeDestroy()->destroyed()
绑定
简单的理解就是模板字符串功能,放心的在任何你想用的地方去用,假如错了vue
会给你提示。
定界符都是可以修改的
// 模板定界符
Vue.config.delimiters = ['{{', '}}']
// html模板定界符
Vue.config.unsafeDelimiters = ["{{{", "}}}"]
数据的绑定
<span>消息: {{ msg }}</span> <!-- 同步更新js里面的数据 -->
<span>他将永不会改变: {{* msg }}</span> <!-- 第一次插入之后就不更新了 -->
<div>{{{ raw_html }}}</div> <!-- 插入原生的 html -->
<div id="item-{{ id }}"></div> <!-- 放在id中 -->
表达的绑定
不可使用,var
/let
关键字声明变量,也不能使用if
/for
流程控制。
{{ number + 1 }} // 做简单的运算
{{ ok ? 'YES' : 'NO' }} // 三元表达式
{{ message.split('').reverse().join('') }} // 调用该对象上的方法
过滤器
对数据进行相应的处理,message
为第一个参数
、filter
为要执行的函数
。
{{ message | filter }}
{{ message | filterA | filterB }} // filterB(filterA(message))
{{ message | filterA 'arg1' arg2 }}
// arg2是一个表达式(假设是1+2) filterA(message,arg1,3)
指令
当其表达式的值改变时把某些特殊的行为应用到 DOM 上。
<p v-if="ok">Hello!</p> <!-- 根据if里面的值,确定是否编译 -->
<a v-bind:href="url"></a>
<!-- 等于href="{{url}}" 这里 href 是参数,将元素的 href 属性传进去。
告诉vue元素的 href 特性跟表达式 url 的值绑定 -->
<a v-on:click="doSomething">
<!-- v-on表示监听,传入了click参数,表示当click事件发生的时候,执行doSomething函数 -->
<a v-bind:href.literal="/a/b/c"></a>
<!-- .literal 修饰符告诉指令将它的值解析为一个字面字符串而不是一个表达式 -->
v-bind 缩写
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
计算属性
<div id="example">
a={{ a }}, b={{ b }}
</div>
var vm = new Vue({
el: '#example',
data: {
a: 1
},
computed: {
// 一个计算属性的 getter
b: function () {
// `this` 指向 vm 实例
return this.a + 1
}
}
})
给计算属性设置setter
computed: {
fullname: {
get: function() {
return this.firstName + ' ' + this.lastName
},
set: function() {
let names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
$watch
通常更优的做法是使用computed
计算属性
<div id="app">{{ fullname }}</div>
let vm = new Vue({
data: {
firstName: 'C',
lastName: 'O',
fullName: 'CO'
}
})
vm.$watch('firstname', funciton(val){
this.fullname = val + ' ' + this.lastName
})
vm.$watch('lastname', funciton(val){
this.fullname = this.firstName + ' ' + val
})
// 等价于
let vm = new Vue({
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function(){
return this.firstName + ' ' + this.lastName
}
}
})
Class 与 Style 绑定
vue特意增强了他们,支持对象和数组绑定
Class 对象绑定
<div class="static" :class="{ 'class-a': isA , 'class-b': isB}"></div>
data: {
isA: true,
isB: false
}
<div class="static" :class="classObjcet"></div>
data: {
classObject: {
'class-a': true
'class-b': false
}
}
//渲染之后
<div class="static class-a"></div>
Class 数组语法
<div :class="[classA,classB]"></div>
data: {
classA: 'class-a'
classB: 'class-b'
}
// 渲染为
<div class="class-a class-b"></div>
<div :class="[classA, isB? classB : '']"></div>
// 始终添加classA对应的类名,根据isB的值确认是否添加classB对应的值。
// 在1.0.19+之后,为了更明了的写法,支持数组里面嵌套对象语法
<div :class="[classA, {classB: isB, classC: isC}]"></div>
Style 对象语法
CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case),自动添加浏览器的前缀。
<div :style="{color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
<div :style="styleObject"></div>
data = {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
Style 数组语法
<div :style="[styleObjectA,styleObjectB]"></div>
data = {
styleObjectA: {
fontSize: '15px'
}
}
条件渲染
** Tip: v-else 元素必须立即跟在 v-if 或 v-show 元素的后面——否则它不能被识别。**
v-if
<h1 v-if="ok">yes</h1>
<h1 v-else>no</h1>
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-show
不支持 template 元素包裹
<h1 v-show="ok">Hello!</h1>
// 在组件上不能使用 v-else
<custom-component v-show="condition"></custom-component>
<p v-show="!condition">这可能也是一个组件</p>
if 与 show 之间的战争
如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。
列表渲染
<ul id="list">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var vm = new Vue({
el: '#list',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar'}
]
}
})
通过$index
可以访问索引,且在v-for
块内可以访问的到其他属性。
<ul id="list">
<li v-for="item in items">
{{ parentMessage }} - {{ $index }} - {{ item.message }}
</li>
</ul>
var vm = new Vue({
el: '#list',
data: {
parentMessage: 'Parent',
items: [
{message: 'Foo'},
{message: 'Bar'}
]
}
})
为索引设置一个别名,且 1.0.17+ 之后可以使用 for of
<div v-for="(key,value) of items">
</div>
// 使用一层 template 包裹
<template v-for="item in items">
<span>{{ item.id }}</span>
<span>{{ item.content }}</span>
</template>
数组变动检测
以下是vue提供的一些数组上的方法,能触发视图更新。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
替换数组
当我们使用一些不改变数组本身的方法的时候(纯函数),我们可以直接赋值给自身,虽然替换了原始的数组,但是vue不会重新渲染所有,他会去进行对比。
track-by
通过此选项设置参考特征,用特征对比,一样就进行复用
{
items: [
{_uid: '339a99', ... }
{_uid: '2r9c92', ... }
]
}
<div v-for="item in items" track-by="_uid">
<!-- content -->
</div>
// _uid 就说明可以复用
track-by="$index"
这样让数据替换高效,此时DOM节点不再映射数组顺序变化,不能同步临时状态。
v-for
包含 <input>
元素或者子组件,要小心使用
更新问题
vue不能检测下面数组的变化
- 直接用索引设置元素,如 vm.items[0] = {};
- 修改数据的长度,如 vm.items.length = 0。
(1) 解决方法
vm.items.$set(0, {})
(2) 解决方法
vm.items = []
对象v-for
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ $key }} : {{ value }}
</li>
</ul>
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
方法与事件处理
<div id="example">
<button @click="greet">Greet</button>
</div>
let vm = new Vue({
el: '#example',
data: {
name: 'Vue.js'
},
methods: {
greet: function(event) {
alert('hello'+this.name+'!')
console.log(event.target.tagName)
}
}
})
<div id="example">
<button :click="say('hi')">Say Hi</button>
<button :click="say('what')">Say What</button>
</div>
new Vue({
el: '#example',
methods: {
say: function(msg) {
alert(msg)
}
}
})
事件修饰符
在事件处理器中经常需要调用event.preventDefault
或 event.stopPropagation
// 阻止单击事件冒泡
<a @click.stop="do"></a>
// 提交事件不再重载页面
<a @submit.prevent="submit"></a>
// 修饰符可以串联
<a @click.stop.prevent="do"></a>
// 只有修饰符
<form @submit.prevent></form>
按键修饰符
- enter
- tab
- delete
- esc
- space
- up
- down
- left
- right
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
自定义按键别名
// 可以使用 @keyup.f1
Vue.directive('on').keyCodes.f1 = 112