Vue-js基础
title: Vue.js基础
date: 2019-09-07 09:42:13
tags: # 这里写的分类会自动汇集到 categories 页面上,分类可以多级
- Vue.js # 一级分类
- Vue.js基础 # 二级分类
介绍
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
vue 和 react 都不是 mvvm 框架,只是借鉴
什么是 MVVM
MVVM 是 Model-View-ViewModel 的缩写。MVVM 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。viewmodel 是组件的实例.
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
Vue.js 的优点:
- 低耦合。视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的”View”上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试。界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写
- 易用灵活高效
引入
在 html 内引入:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
工程化时最好使用vue/cli
构建.
实例和数据绑定
通过构造函数Vue
就可以创建一个Vue
的实例.并启动Vue
应用.
1 | let app = new Vue({ |
el
用于指定页面中的 DOM 元素来挂载 Vue 实例.可以是标签,可以是 css 语法.
通过Vue
实例的data
选项,可以声明应用内可以双向绑定的数据.建议所有会用到的数据都预先在data
内声明,也可以指向一个已有的变量.值可以先设置默认值.
挂载成功后,可以通过app.$el
来访问该元素,Vue
实例本身也代理了data
对象里的所有属性,所以可以这样访问:
访问 Vue 实例的属性: app.$el, app.$data
.
访问 data 元素的属性: app.msg
生命周期钩子
钩子(hook),可以理解为挂载点.在整个 Vue 实例创建过程中,有一些挂载点.
生命周期流程:
所有的生命周期钩子自动绑定
this
上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。这是因为箭头函数绑定了父上下文,因此 this 与你期待的 Vue 实例不同,this.fetchTodos 的行为未定义。
生命周期钩子 | 类型 | 详细 |
---|---|---|
beforeCreate | Function | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 |
created | Function | 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 |
beforeMoute | Function | 在挂载开始之前被调用:相关的render 函数首次被调用。 |
mounted | Function | el 被新创建的vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当mounted 被调用时vm.$el 也在文档内。 |
beforeUpdate | Function | 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 |
updated | Function | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或watcher 取而代之 |
actived | Function | keep-alive 组件激活时调用。 |
deactivated | Function | keep-alive 组件停用时调用。 |
beforeDestroy | Function | 实例销毁之前调用。在这一步,实例仍然完全可用。 |
destroyed | Function | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 |
errorCaptured | (err: Error, vm: Component, info: string) => ?boolean |
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false 以阻止该错误继续向上传播。 |
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.nextTick
替换掉
mounted`:
1 | mounted: function () { |
注意
updated
不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用vm.$nextTick
替换掉updated
:
1 | updated: function () { |
文本插值和表达式
使用双大括号(Mustache 语法)的方法是最基本的文本插值的语法,它会自动将双向绑定的数据实时显示出来.
1 | <span>Message: {{ msg }}</span> |
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
1 | <span v-once>这个将不会改变: {{ msg }}</span> |
v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
指令:
1 | <p>Using mustaches: {{ rawHtml }}</p> |
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
v-bind
v-bind
可缩写成:
.
双大括号语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令:
1 | <div v-bind:id="dynamicId"></div> |
对于布尔特性 (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:
1 | <button v-bind:disabled="isButtonDisabled">Button</button> |
如果isButtonDisabled
的值是null、undefined
或false
,则disabled
特性甚至不会被包含在渲染出来的 <button>
元素中。
js 表达式
Vue .js 只支持单个表达式,不支持语句和流控制。
1 | { |
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。
有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
1 | <!-- 这是语句,不是表达式 --> |
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
计算属性
1 | <div id="example"> |
这里是想要显示变量 message 的翻转字符串,但是更复杂的就需要计算属性.
所有的计算属性都以函数的形式写在 Vue 实例内的computed
选项内,最终返回计算后的结果。
范例:
1 | <div id="example"> |
1 | var vm = new Vue({ |
计算属性缓存
1 | // 在组件中 |
使用 methods 方法也可以起到和计算属性同样效果
区别:
methods: 如果调用方法,只要页面重新渲染.方法就会重新执行,不需要渲染就不需要重新执行
计算属性: 不管渲不渲染,只要计算属性依赖的数据不变,就不会变
何时使用: 取决于是否需要缓存,当遍历大量数组或大量计算,应该使用计算属性.
计算属性缓存 vs 方法 methods
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
1 | computed: { |
相比之下,每当触发重新渲染时,调用方法 methods 将总会再次执行函数。
计算属性 vs 侦听属性 watch
watch:主要用来监听数据变化.可以监听:props,data,computed 内的数据.
属于我的变化影响别人.
计算属性 computed: 依赖 data 中的数据,只有该依赖的数据发生变化才会变化.
属于别人的变化影响我.
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
例子:
1 | <div id="demo">{{ fullName }}</div>; |
计算属性版本:
1 | var vm = new Vue({ |
计算属性的 setter
计算属性默认只有getter
,不过在需要时你也可以提供一个setter
:
1 | // ... |
现在再运行 vm.fullName = ‘John Doe’ 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
侦听器 watch
当需要在数据变化时执行异步或开销较大的操作时,更适合用 watch.
1 | <div id="watch-example"> |
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
v-bind 与 class 和 style 的绑定
应用场景: dom 元素经常绑定一些 class 类名或 style 样式
对象语法
可以给v-bind:class
传一个对象,动态切换 class:
1 | <div v-bind:class="{ active: isActive }"></div> |
也可以传入多个属性来动态切换 class.v-bind:class
指令也可以与普通 class 属性共存.
绑定的数据对象也可以写在data
里.
1 | <div v-bind:class="classObject"></div> |
当 class 的表达式过长或者逻辑复杂,可以绑定一个计算属性.一般当条件多于两个时,就可以使用data
或computed
1 | <div v-bind:class="classObject"></div> |
数组语法
当应用多个 class 时,可以使用数组语法,给v-bind:class
绑定一个数组.数组成员直接对应类名.
1 | <div v-bind:class="[activeClass, errorClass]"></div> |
渲染为
1 | <div class="active text-danger"></div> |
根据条件切换,也可以使用三元表达式:
1 | <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> |
也可以数组语法,对象语法混用:
1 | <div v-bind:class="[{ active: isActive }, errorClass]"></div> |
绑定内联样式
对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象.CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
1 | <div v-bind:style="{color: activeColor, fontSize: fontSize + 'px' }"></div> |
同样的,对象语法常常结合返回对象的计算属性使用。
数组语法
数组语法可以将多个样式对象应用到同一个元素上:
1 | <div v-bind="[baseStyles, overridingStyles]"></div> |
应用多个样式对象时,可以使用数组语法.在实际业务中,style 的数组语法并不常用,因为往往可以写在一个对象里面.而较为常用的应当是计算属性.
使用:style
时,Vue 会自动给特殊的 css 属性名称增加前缀,比如 transform,无需再加前缀属性.
基本指令
v-cloak
解决因为初始化慢,导致页面闪动的情况.这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕.
一般和CSS规则
如 [v-cloak] { display: none }
配合使用(需要写到 css 里).
v-once
只渲染元素和组件一次.之后的重新渲染被当做静态内容跳过,用于优化性能.
写在标签里.
v-pre
跳过这个元素和他子元素的编译过程.可以加快编译.
其他的单独写
v-text, v-html, v-show, v-if, v-else, v-else-if, v-for, v-on, v-bind, v-model, v-slot.
条件渲染
v-if,v-else,v-else-if
用法: 根据表达式值的真假条件渲染元素.在切换时元素及它的数据绑定/组件被销毁并重建.
如果元素是<template>
,将提出他的内容作为条件块.
当条件变化时,该指令触发过渡效果.
当和
v-for
一起使用时,v-for
的优先级更高.
也可以用 v-else 添加一个“else 块”:
1 | <h1 v-if="awesome">Vue is awesome!</h1> |
在<template>
上使用v-if
因为v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template>
元素当做不可见的包裹元素,并在上面使用v-if
。最终的渲染结果将不包含<template>
元素。
1 | <template v-if="ok"> |
v-else-if
,顾名思义,充当 v-if
的“else-if 块”,可以连续使用:
1 | <div v-if="type === 'A'"> |
用key
管理可复用元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染.<input>
不会被替换掉——仅仅是替换了它的 placeholder
。
如果要表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的key
属性即可.
1 | <template v-if="loginType === 'username'"> |
v-show
1 | <h1 v-show="ok">Hello!</h1> |
v-show
只是切换 css 属性display
.v-show
不支持<template>
元素,也不支持v-else
.
v-if 和 v-show 相比
v-if
: 真正的条件渲染,条件为真就渲染,为 false 就移除.
v-show
: 不管初始条件是什么,都会渲染.只是切换 css 的 display 属性.
总结: 如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好
v-for 列表渲染
两种使用场景:
- 遍历多个对象
- 遍历一个对象的多个属性
遍历数组语法: v-for="(item, index) in items"
第一个是数组元素名,第二个是索引.
遍历对象的多个属性语法: v-for="(value, name, index) in object"
第一个是属性值,第二个是属性名,第三个是索引.
在遍历对象时,会按
Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性:
1 | <div v-for="item in items" v-bind:key="item.id"> |
建议尽可能在使用 v-for
时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
不要使用对象或数组之类的非基本类型值作为
v-for
的 key。请用字符串或数值类型的值。
Vue 中的 key
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
这里的就地复用的策略复用的是没有发生改变的元素,其他的还要依次重排。
所以我们需要使用 key 来给每个节点做一个唯一标识,Vue 的 Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点,所以一句话,key 的作用主要是为了高效的更新虚拟 DOM.
v-for 和 v-if
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个v-for
循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:
1 | <li v-for="todo in todos" v-if="!todo.isComplete"> |
上面的代码将只渲染未完成的 todo。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 )上。如:
1 | <ul v-if="todos.length"> |
在组件上使用 v-for
当在组件上使用 v-for
时,key
现在是必须的.
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop
,
不自动将 item
注入到组件里的原因是,这会使得组件与 v-for
的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
1 | //html |
注意这里的 is="todo-item"
属性。这种做法在使用 DOM 模板时是十分必要的,因为在 <ul>
元素内只有<li>
元素会被看作有效内容。这样做实现的效果与 <todo-item>
相同,但是可以避开一些潜在的浏览器解析错误。
1 | //js |
数组更新过滤排序
改变数组的一系列方法:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
两个数组变动 Vue 检测不到:
- 改变数组指定项,
vm.items[indexOfItem] = newValue
- 改变数组长度,
vm.items.length = newLength
解决办法:
改变指定项: Vue.set(app.arr, 1, ‘car’)
改变数组长度: app.arr.splice(1)
显示过滤/排序后的结果
有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际改变或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
计算属性
1 | <li v-for="n in evenNumbers">{{ n }}</li> |
使用 methods
1 | <li v-for="n in even(numbers)">{{ n }}</li> |
v-on
v-on
用来绑定事件监听器.v-on
可以缩写成@
.
在普通元素上,v-on
可以监听原生的 DOM 事件,除了click
外,还有dblclick
、keyup
,mousemove
等。
表达式可以是一个方法名,这些方法都写在 Vue 实例的 methods 属性内,并且是函数的形式,
函数内的this
指向的是当前 Vue 实例本身,因此可以直接使用this.xxx
的形式来访问或修改数据.
如果方法中带有参数,但是没有加括号,默认传原生事件对象 event
动态参数
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a v-bind:[attributeName]="url"> ... </a>
方括号里的属性可以被替换.
可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
当 eventName
的值为 “focus” 时,v-on:[eventName]
将等价于 v-on:focus
。
动态参数表达式有一些语法约束,因为某些字符,例如空格和引号,放在 HTML 特性名里是无效的。同样,在 DOM 中使用模板时你需要回避大写键名。
修饰符
在 Vue 中传入 event 对象用$event
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
.stop
阻止单击事件冒泡 <a v-on:click.stop="doThis"></a>
.prevent
提交事件不再重载页面
.capture
添加事件侦听器时使用时间捕获模式
.self
只当事件在该元素本身(而不是子元素)触发时触发回调
.once
只执行一次的方法
.passive
Vue 还对应 addEventListener 中的 passive 选项提供了 .passive
修饰符。
1 | <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> |
这个 .passive
修饰符尤其能够提升移动端的性能。
不要把
.passive
和.prevent
一起使用,因为.prevent
将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
按键修饰符
1 | <!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> |
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
1 | <input v-on:keyup.page-down="onPageDown"> |
在上述示例中,处理函数只会在 $event.key
等于 PageDown 时被调用。
系统修饰键
.ctrl
.alt
.shift
.meta
.exact
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
1 | <!-- 即使 Alt 或 Shift 被一同按下时也会触发 --> |
v-model
用于在表单类元素上双向绑定
可以用于 input 框,以及 textarea 等
注意:所显示的值只依赖于所绑定的数据,不再关心初始化时的插入的 value
v-model
的实际
v-model
其实是一个语法糖,其实绑定了两层操作:
v-bind
绑定一个 value 值v-on
指令给当前元素绑定 input 事件
1 | <input v-model="total"> |
要使用v-model
,要做到:
- 接收一个 value 属性
- 在有新的 value 时触发 input 事件
文本
1 | <input v-model="message" placeholder="edit me"> |
多行文本
将v-model
绑定到textarea
上
1 | <span>Multiline message is:</span> |
单选按钮
- 单个单选按钮,直接用 v-bind 绑定一个布尔值,用 v-model 是不可以的
- 如果是组合使用,就需要 v-model 来配合 value 使用,绑定选中的单选框的 value 值.
1 | <div id="example-4"> |
复选框
单个复选框,绑定到布尔值:
1 | <input type="checkbox" id="checkbox" v-model="checked"> |
多个复选框,绑定到同一个数组:
1 | <div id='example-3'> |
下拉框
单选下拉:
1 | <div id="example-5"> |
多选下拉:
1 | <div id="example-6"> |
使用v-for
配合
1 | <select v-model="selected"> |
绑定值
- 单选按钮
只需要用 v-bind 给单个单选框绑定一个 value 值,此时,v-model 绑定的就是他的 value 值
- 复选框
- 下拉框
在 select 标签上绑定 value 值对 option 并没有影响
修饰符
修饰符 | 解释 |
---|---|
lazy | v-model 默认是在 input 输入时实时同步输入框的数据,而 lazy 修饰符,可以使其在失去焦点或者敲回车键之后再更新 |
number | 将输入的字符串转化为 number 类型 |
trim | trim 自动过滤输入过程中首尾输入的空格 |