该文章只是我对vue基础知识的一点总结,详细vue知识请看Vue官方文档。
一、什么是vue.js
是一个轻量级MVVM框架,数据驱动+组件化的前端开发。
-
数据驱动:数据响应原理——数据(model)改变驱动视图(view)自动更新。
-
组件化:扩展html元素,封装可重用代码。
组件设计原则
-
页面上每个独立的可视/可交互区域视为一个组件
-
每个组件对于一个工程目录,组件所需要的各种资源在这个目录下就近维护
-
页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面
-
二、MVVM
响应式,双向数据绑定,即MVVM。是指数据层(Model)-视图层(View)-数据视图(ViewModel)的响应式框架。它包括:
1.修改View层,Model对应数据发生变化。
2.Model数据变化,不需要查找DOM,直接更新View。
mvvm.png MVVM框架.png在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
三、vue基础
1、创建第一个vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue入门</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root"><h1>{{msg}}</h1></div>
<script>
new Vue({
el:"#root",
data:{
msg:"hello world"
}
})
</script>
</body>
</html>
运行结果:
-
挂载点,模板,实例之间的关系
-
一个 Vue 应用由一个通过
new Vue
创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。 -
<div id="root"></div>
是vue实例的挂载点。vue只会去处理挂载点下面的内容。 -
挂载点内部的内容即为模板内容,如示例中的
{{msg}}
。也可以将模板内容放在vue实例中(template):
-
new Vue({
el:"#root",
template:'<h1>{{msg}}</h1>',
data:{
msg:"hello world"
}
})
2、vue的生命周期
下面部分参考详解vue生命周期,可详细阅读该文章
生命周期函数就是vue实例在某一个时间点会自动执行的函数。
PS:其中created和mounted比较重要,一个是data数据和事件的初始化,一个是html模板,挂载渲染到页面完毕。
生命周期图示:下图展示了实例的生命周期。
lifecycle.png<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue实例生命周期函数</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
beforeCreate: function() {
console.group('------beforeCreate创建前状态------');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('------created创建完毕状态------');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeMount: function() {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------mounted 挂载结束状态------');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
updated: function () {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
destroyed: function () {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
</body>
</html>
控制台:
初始化实例: 更新实例: 销毁实例:3、模板语法
- 插值
区别:v-text会转译,v-html不会转译
<div id="app">
<!-- 插值表达式 -->
<div>{{name}}</div> <!-- <h1>hello</h1> -->
<!-- v-text 与 {{}} 作用相同 -->
<div v-text="name"></div> <!-- <h1>hello</h1> -->
<div v-html="name"></div> <!-- hello -->
</div>
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。注意:每个绑定都只能包含单个表达式。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
-
指令
-
事件绑定:
v-on
<body>
<div id="root">
<!-- 事件绑定 v-on: 简写为 @ -->
<div v-on:click="handleClick"><h1>{{content}}</h1></div>
</div>
<script>
new Vue({
el:"#root",
data:{
content:"hello"
},
methods:{
handleClick:function(){
this.content = "world"
}
}
})
</script>
</body>
- 属性绑定:
v-bind
<body>
<div id="root">
<!-- 属性绑定 v-bind: 简写为 : -->
<div :title="title">hello world</div>
</div>
<script>
new Vue({
el:"#root",
data:{
title:"this is hello world"
}
})
</script>
</body>
- 双向数据绑定:
v-model
<body>
<div id="root">
<!-- 双向数据绑定 v-model -->
<input v-model="content"/>
<div>{{content}}</div>
</div>
<script>
new Vue({
el:"#root",
data:{
content:"this is content"
}
})
</script>
</body>
-
v-if
,v-else
指令
<body>
<div id="root">
<!-- v-if 条件渲染指令,存在与否,它根据表达式的真假来删除和插入元素
当show=false时,直接从dom中移除 -->
<div v-if="show">hello world</div>
<!-- v-if的值为false时显示v-else内容,v-if 与 v-else必须紧贴
另外 还有 v-else-if -->
<div v-else>bye world</div>
<button @click="handleClick">toggle</button>
</div>
<script>
new Vue({
el: "#root",
data: {
show: true
},
methods:{
handleClick:function(){
this.show = !this.show
}
}
})
</script>
</body>
- v-show指令
<body>
<div id="root">
<!-- v-show 条件渲染指令,显示与否
当show=false时,div中的display属性变为none,不会dom中移除。
推荐使用v-show -->
<div v-show="show">hello world</div>
<button @click="handleClick">toggle</button>
</div>
<script>
new Vue({
el: "#root",
data: {
show: true
},
methods:{
handleClick:function(){
this.show = !this.show
}
}
})
</script>
</body>
- v-for指令
<body>
<div id="root">
<ul>
<!-- v-for 循环显示 :key 提升每一项渲染效率,不能相同
一般与后端数据库相连时该项为数据id -->
<li v-for="(item,index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
list: [1,2,3]
}
})
</script>
</body>
- 缩写
- v-bind
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
- v-on
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
4、计算属性、方法与监听器
-
计算属性
对于任何复杂逻辑,你都应当使用计算属性。
<body>
<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>
</body>
结果:
Original message: "Hello"
Computed reversed message: "olleH"
-
计算属性缓存 vs 方法
我们可以通过在表达式中调用方法来达到同样的效果:
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
- 计算属性vs侦听属性
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
侦听属性有缓存,但是代码是命令式且重复的。
将它与计算属性的版本进行比较:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
-
计算属性的setter
计算属性默认只有 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
也会相应地被更新。
-
监听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<body>
<div id="root">
姓:<input v-model="firstName" />
名:<input v-model="lastName" />
<div>{{fullName}}</div>
<div>{{count}}</div>
</div>
<script>
new Vue({
el:"#root",
data:{
firstName: '',
lastName: '',
count: 0
},
// 计算属性,内置缓存,其中内置属性没改变就不会再次计算
computed:{
fullName: function() {
return this.firstName + ' ' + this.lastName
}
},
// 侦听器,监听变化次数
watch: {
fullName: function() {
this.count++
}
}
})
</script>
</body>
5、class与style绑定
下面通过一个点击改变颜色例子来说明样式绑定。
- class的对象绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>class的对象绑定</title>
<script src="./vue.js"></script>
<style>
.activited {
color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 方法一:class的对象绑定 -->
<div @click="handleDivClick"
:class="{activited:isActivited}">
Hello world
</div>
</div>
<script>
var vm = new Vue ({
el: "#app",
data: {
isActivited: false
},
methods: {
handleDivClick: function() {
this.isActivited = ! this.isActivited
}
}
})
</script>
</body>
</html>
- class的数组绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>class的数组绑定</title>
<script src="./vue.js"></script>
<style>
.activited {
color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 方法二:class的数组绑定 -->
<div @click="handleDivClick1"
:class="[activited]">
Hello world!
</div>
</div>
<script>
var vm = new Vue ({
el: "#app",
data: {
activited:""
},
methods: {
handleDivClick1: function() {
this.activited = this.activited === "activited" ? "" : "activited"
}
}
})
</script>
</body>
</html>
- style的对象绑定
<body>
<div id="app">
<!-- 方法三:style的对象绑定 -->
<div @click="handleDivClick2"
:style="styleObj">
Hello world!!
</div>
</div>
<script>
var vm = new Vue ({
el: "#app",
data: {
styleObj: {
color: ""
}
},
methods: {
handleDivClick2: function() {
this.styleObj.color = this.styleObj.color === "" ? "red" : "";
}
}
})
</script>
</body>
- style的数组绑定
<body>
<div id="app">
<!-- 方法四:style的数组绑定(与方法三相似) -->
<div @click="handleDivClick3"
:style=[styleObj]>
Hello world!!!
</div>
</div>
<script>
var vm = new Vue ({
el: "#app",
data: {
styleObj: {
color: ""
}
},
methods: {
handleDivClick3: function() {
this.styleObj.color = this.styleObj.color === "" ? "red" : "";
}
}
})
</script>
</body>
6、条件渲染
v-if
,v-else
,v-show
基础知识详见上文指令部分。
-
在<template>元素上使用
v-if
条件渲染分组当我们需要切换多个元素时,可以把一个
<template>
元素当做不可见的包裹元素,并在上面使用v-if
。最终的渲染结果将不包含
<template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
-
v-else-if
充当
v-if
的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
类似于 v-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
-
用
key
管理可复用的元素Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
但有时我们并不需要这样的功能,如当我们在使用账号登录时,可以选择用户名登录和邮箱登录,而这两者的信息可能是不一样的,这时我们可以增加key使切换时输入的内容清空。如下面的例子:
<body>
<!-- 通过增加key能使v-if 与 v-else 切换时的内容清空 -->
<div id="app">
<div v-if="show">
用户名:<input key="username" />
</div>
<div v-else>
邮箱名:<input key="email"/>
</div>
<button @click="toggle">切换</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show: true,
},
methods: {
toggle: function() {
this.show = !this.show
}
}
})
</script>
</body>
7、列表渲染
- 用
v-for
把一个数组对应为一组元素
<body>
<div id="app">
<!-- v-for 循环显示 :key 提升每一项渲染效率,不能相同 一般与后端数据库相连时该项为数据id -->
<!-- 可以用 of 替代 in 作为分隔符,因为它是最接近 JavaScript 迭代器的语法 -->
<div v-for="(item, index) in list"
:key="item.id">
{{index}}----{{item.text}}----{{item.id}}
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
list: [{
id:"010120201",
text:"hello"
},{
id:"010120202",
text:"hello"
},{
id:"010120203",
text:"hello"
}]
}
})
</script>
</body>
输出结果:
- 一个对象的
v-for
<body>
<div id="app">
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})
</script>
</body>
输出结果:
当我们要在此基础上再加一个数据,在控制台中我们要重新定义该对象才能使页面改变。
vm.object={
firstName: 'John',
lastName: 'Doe',
age: 30,
address: 'hangzhou'
}
除此之外,我们还可以通过set方法向对象注入数据,同时页面更新。
方法一:Vue.set(vm.object,"address","hangzhou")
方法二:vm.$set(vm.object,"address","hangzhou")
-
变异方法
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
-
push()
-
pop()
-
shift()
-
unshift()
-
splice()
-
sort()
-
reverse()
-
-
非变异方法
filter()
,concat()
和slice()
。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
-
注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
1.当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
2.当你修改数组的长度时,例如:
vm.items.length = newLength
-
为了解决第一类问题,以下两种方式都可以实现和
vm.items[indexOfItem] = newValue
相同的效果,同时也将触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set
实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
- 为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
网友评论