vue 学习
CDN
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
一. Vue 实例
1.1 创建一个 Vue 实例
每一个Vue应用都是通过Vue函数创建一个新的 Vue 实例开始的
var vm = new Vue({
//选项
})
vm ViewModel 之缩写
一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。举个例子,一个 todo 应用的组件树可以是这样的:
根实例
└─ TodoList
├─ TodoItem
│ ├─ DeleteTodoButton
│ └─ EditTodoButton
└─ TodoListFooter
├─ ClearTodosButton
└─ TodoListStatistics
1.2 数据与方法
当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。
当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染
==值得注意的是 只有当实例被创建时就已经存在于 data 中 的属性才是响应式的==
比如
vm.b = 'hi'
那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,==但是一开始它为空或不存在,那么你仅需要设置一些初始值---先占位==比如:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
这里唯一的例外是使用 Object.freeze(),==这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。==
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>
Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
1.3 生命周期钩子
每个 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 实例。
生命周期图示:
graph TD
A[new_Vue/新建Vue实例] --> B[初始化-事件&生命周期]
B-->|beforeCreate|C[初始化-注入&校验]
C -->|created|D{是否指定-el-选项?}
D -->|否|E[当调用vm.$mount<el>函数时使用]
D-->|是|F{是否指定-template-选项?}
E-->F
F-->|是|G[将template编译到render函数中<即渲染>]
F-->|否|H[将el外部的HTML作为template编译]
G-->|beforeMount|I[创建vm.$el 并用其替换<el>]
H-->|beforeMount|I
I-->|mounted|J((挂载完毕))
J-->|当data被修改时<beforeUpdate>|N[虚拟DMO重新渲染并应用更新]
N-->|当data被修改时<updateed>|J
J-->|当调用vm.$destroy|K[解除绑定,销毁子组件以及时间监听器]
K-->L((销毁完毕))
L-->M[destroyed]
二.模板语法
2.1 插入值
2.1.1 文本
Mustache 语法(双大括号)
<span>Message : {{ msg }} </span>
Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上msg属性发生了改变,插值处的内容都会更新。
若果不想被多次修改,可以使用 指令 ==v-once==
<span v-once>这个将不会改变: {{ msg }}</span>
2.1.2 原始HTML
双大括号传入的数据会被解释成为普通文本,而非HTML代码,为了输出纯正的 HTML ,需要使用 v-html指令.
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
当传参数 (<span style='color:red'>This should be red. ) 给{{ rawHtml }}后,渲染为:
Using mustaches: <span style='color:red'>This should be red. ## Mustache 插入值 接受输出为文本
Using v-html directive: This should be red. ## v-html 接受的是html代码,并编译渲染
这个 span 的内容将会被替换成为属性值 rawHtml,直接作为 HTML——会忽略解析属性值中的数据绑定。
==注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。==
2.1.3 Attribute(特性)
Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>
==v-bind== 是将<div> 标签中的某些属性与Vue实例中的数据相关联,比如:
1. <div id="head" ></div> #id标签==固定为head==,后续在css中或者js中调用
2.<div v-bind:id="hhhhead" ></div>#id标签 为动态变化,
<script>
var vm = new Vue({
el:"#app", #上一级的容器,app可以理解为一个组件或一个子程序
data:{
hhhhead:heading # 一旦渲染成功,2.中的id 将被替换为heading,网页元素中可见<div id="heading" ></div>
},
})
</script>
对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:
<button v-bind:disabled="isButtonDisabled">Button</button>
如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 元素中。
2.1.4 使用JavaScript表达式
迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,==对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。==
<div id="app">
{{ number + 1 }} --->11
{{ ok ?'YES':'NO' }} #三元表达式 ok 为 true 输出 'YES'否则输出'NO';
{{ message.split('').reverse().jion('') }} #复函计算
</div>
<div v-bind:id="'list-' + id"> </div>
<script>
var vm = new Vue({
el:"#app", #容器,app可以理解为一个组件或一个子程序
data:{
number:10, #页面上会计算 10+1
ok:true,
}
})
</script>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能==包含单个表达式==,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。++==你不应该在模板表达式中试图访问用户定义的全局变量。==++
2.2 指令
指令(Directives)是带有 v- 前缀的特殊attribute.指令attribute的值预期是单个JavaScript表达式(v-for除外).
指令的职责是: 当表达式的值发生改变的时候,将其产生的连带影响,响应式地作用于DOM.
<p v-if='seen'>现在你看到我了<p>
这里 v-if 指令将根据表达式seen的 true or false 来插入/删除元素<p>.
2.2.1 参数
一些指令能够接受一个'参数',在指令之后以冒号表示,eg: 告知 v-bind 指令可以用于响应式地更新 HTML attrubute:
<a v-bing:href='url'>...</a> #在这里== href 是参数==,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
-
==v-bind== 是将<div> 标签中的某些属性与Vue实例中的数据相关联,比如:
1. <div id="head" ></div> #id标签==固定为head==,后续在css中或者js中调用 2.<div v-bind:id="hhhhead" ></div>#id标签 为动态变化, <script> var vm = new Vue({ el:"#app", #上一级的容器,app可以理解为一个组件或一个子程序 data:{ hhhhead:heading # 一旦渲染成功,2.中的id 将被替换为heading,网页元素中可见<div id="heading" ></div> }, }) </script> -
==v-on== 监听DMO事件:
<a v-on:click='doSomeing'>...</a> #在这里参数是监听的事件名
2.2.2 动态参数
==2.6.0==
Vue从2.6.0开始,可以用方括号 [] 括起来的 JavaScript 表达式作为一个指令的参数
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName 会被作为一个 JavaScript 表达式进行==动态求值==,求得的值将会作为最终的参数来使用。
例如,如果你的 Vue 实例有一个 ==data 属性 attributeName,其值为 "href",那么这个绑定将等价于 v-bind:hre==
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
#当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus。
对动态参数值的约束(没懂)
对动态参数的值的约束 动态参数预期会求出一个字符串,异常情况下值为 null。这个特殊的 null 值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
对动态参数表达式的约束(没懂)
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。例如:
<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>
变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>
修饰符
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit">...</form>
例子
<div @click="click1">
<div @click="click2">
click me //一旦单击了""click me"按钮之后,先执行本层div 然后在执行父级div.
//若不想让父级div执行.则在子级div添加修饰符 .stop
//@click.stop="click2"
</div>
</div>
2.3 缩写
v-前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的==单页面应用程序 (SPA - single page application)== 时,v- 前缀也变得没那么重要了。因此,Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:
==v-bind==简写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
==v-on== 简写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
它们看起来可能与普通的 HTML 略有不同,但 : 与 @ 对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,==它们不会出现在最终渲染的标记中==。缩写语法是==完全可选的==
三 计算属性和侦听器
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
如上 在模板中的简单计算.如果放入过多就会使模板的维护变得更难.
SO,为了减少工作量,对于任何复杂逻辑,请使用==计算属性==
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: { //------>>>>>计算属性
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
3.1.2计算属性和方法
上面的代码中computer中getter我们也可以通过调用方法 methods 来达到相同的效果
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个++方法++而不是一个++计算属性++。两种方式的最终结果确实是完全相同的。然而
==不同的是计算属性是基于它们的响应式依赖进行++缓存++的==。==只在相关响应式依赖发生改变时它们才会重新求值==。 反而言之,方法methods是没有缓存的,每次都要运算一遍函数(对于大的函数,很耗资源)
这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
相比之下,每当出发重新渲染时,调用方法将总会再次执行函数.
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
3.2.2计算属性 vs 侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:
<div id="demo">{{ fullName }}</div>
<script>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
//监听firstName,如果发生变化,计算新的fullName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
//监听lastName若发生变化,计算fullName
}
}
})
</script>
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
<script>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
//监听所有特性.因为只要特性一遍 计算属性就会重新计算
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
说明:
computed 计算属性,只要计算元素发生变化,computed 中的函数就会变化,从而达到监听的效果.
3.1.3 计算属性的 setter
计算属性默认只有getter(当访问就是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]
}
}
}
// ...
现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
3.2 侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch选项提供了一个更通用的方法,==来响应数据的变化==。==当需要在数据变化时执行异步或开销较大的操作时==,这个方式是最有用的。
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
除了 watch 选项之外,您还可以使用命令式的 ==vm.$watch API。==
四.Class 与 Style 绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
4.1 绑定HTML Class
4.1.1 对象语法
我们可以传给 v-bind:class 一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActive 的 truthiness。(真值 不是true担保函(非null,0,空,false..),)
你可以在对象中传入更多属性来==动态切换多个 class==。此外,==v-bind:class 指令也可以与普通的 class 属性共存==。当有如下模板:
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
和如下 data:
data: {
isActive: true,
hasError: false
}
结果渲染为:
<div class="static active"></div>
当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 "static active text-danger"。
==绑定的数据对象不必内联定义在模板里:==
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的结果和上面一样。
我们也可以在这里绑定一个返回对象的计算属性。这是一个==常用且强大的模式:==
<div v-bind:class="classObject"></div>
//组件中
data: {
isActive: true, #可以在外面更改状态,然后更改class状态./0322.xb
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error, #通过计算属性判断active的值
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
4.1.2 数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
渲染为:
<div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加 errorClass,但是只有在 isActive 是 truthy[1] 时才添加 activeClass。
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
4.1.3 用在组件上
当在一个自定义组件上使用 class 属性时,这些 class 将被添加到该组件的根元素上面。==这个元素上已经存在的 class 不会被覆盖。==
例如,如果你声明了这个组件:
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
然后在使用它的时候添加一些 class:
<my-component class="baz boo"></my-component>
HTML 将被渲染为:
<p class="foo bar baz boo">Hi</p>
对于带数据绑定 class 也同样适用:
<my-component v-bind:class="{ active: isActive }"></my-component>
当 isActive 为 truthy[1] 时,HTML 将被渲染成为:
<p class="foo bar active">Hi</p>
4.2 绑定内联样式
4.2.1 对象语法
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'
}
}
同样的,对象语法常常结合返回对象的计算属性使用。
4.2.2 数组语法
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
4.2.3 自动添加前缀
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,==Vue.js 会自动侦测并添加相应的前缀。==
4.2.4 多重值
为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这样写只会渲染数组中==最后一个==被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染==display: flex==.
五.条件渲染
5.1 v-if
==v-if指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。==
<h1 v-if="awesome">Vue is awesome!</h1>
也可以用 v-else 添加一个“else 块”:
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>