尝试 Vue.js 最简单的方法是使用 Hello World 例子。你可以在浏览器新标签页中打开它,跟着例子学习一些基础用法。或者你也可以创建一个 .html 文件,然后通过如下方式引入 Vue:
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
1 声明式渲染-引入Hello案例
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
下面一个demo讲解:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
<h1>Hello - {{ message }}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let app = new Vue({
el: '#root', // el用于指定当前Vue实例为哪个容器服务。
data: {
message: 'zhaoshuai@inspur.com'
}
})
</script>
</body>
</html>
我们已经成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.message 的值,你将看到上例相应地更新。(如上)
注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) 然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。
除了文本插值,我们还可以像这样来绑定元素 attribute:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app-2">
<span v-bind:title="bindMessage">
<h1>鼠标悬停几秒钟查看此处动态绑定的提示信息!</h1>
</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
let app2 = new Vue({
el: '#app-2',
data: {
bindMessage: '页面加载于 ' + new Date().toLocaleString()
}
})
</script>
</body>
</html>
这里我们遇到了一点新东西。你看到的 v-bind attribute 被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute。可能你已经猜到了,它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 title attribute 和 Vue 实例的 message property 保持一致”。
property是DOM中的属性,是JavaScript里的对象;
attribute是HTML标签上的特性,它的值只能够是字符串;
如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = ‘新消息’,就会再一次看到这个绑定了 title attribute 的 HTML 已经进行了更新。
注意:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为【Vue模板】;
4.Vue实例和容器是一一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
2 模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
2.1 插值
文本
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<span>Message: {{ msg }}</span>
Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。
2.2 指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:
<a v-bind:href="url">...</a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-3">
<a v-bind:href="url">百度一下</a>
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
let app3 = new Vue({
el: "#app-3",
data: {
url: 'https://www.baidu.com/'
}
})
</script>
</body>
</html>
<div id="app-3">
<a :href="url">百度一下</a>
</div>
2.3 Vue模板语法有两大类:
1.插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是JS表达式,且可以直接读取到data中所有属性。
起始标签和结束标签中间夹的内容就是标签体
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件......)。
举例:v-bind:href="xxx" 或写为 :href="xxx" ,xxx同样为JS表达式。
Vue中有很多指令,且形式都是 v-??? ,此处的v-bind只是例子
3 数据绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
<!-- 单向绑定(v-bind):数据只能从data流向页面-->
单项数据绑定: <input type="text" v-bind:value="name">
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
new Vue({
el: '#app-1',
data: {
name: 'zhaoshuai-lc@inpsur.com'
}
})
</script>
</body>
</html>
(1)双向绑定一般都应用在表单类元素上(都有value值)(如input、select等)。
(2)v-model:value 可以简写为 v-model,因为 v-model默认收集的就是value的值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
<!-- 单向绑定(v-bind):数据只能从data流向页面-->
单项数据绑定: <input type="text" v-bind:value="name"><br/>
<!-- 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data-->
双向数据绑定:<input type="text" v-model:value="name"><br/>
<!-- 备注:
(1)双向绑定一般都应用在表单类元素上(都有value值)(如input、select等)。
(2)v-model:value 可以简写为 v-model,因为 v-model默认收集的就是value的值。-->
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
new Vue({
el: '#app-1',
data: {
name: 'zhaoshuai-lc@inpsur.com'
}
})
</script>
</body>
</html>
v-model:value 可以简写为 v-model,因为 v-model默认收集的就是value的值
<div>简写</div>
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
4 el && data
data与el的两种写法
1.el有2种写法
(1)new Vue时候配置el属性。
(2)先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
2.data有2种写法
(1)对象式
(2)函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
3.一个重要原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
1 el 两种写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
<div>你好,{{ name }}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
let vm = new Vue({
// el: '#app-1', // 第一种
data: {
name: 'zhaoshuai-lc@inspur.com'
}
})
console.info(vm)
// 2秒之后再指定值
setTimeout(() => {
vm.$mount('#app-1') // 第二种
}, 2000)
</script>
</body>
</html>
2 data 的两种写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
<div>你好,{{ name }}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
let vm = new Vue({
el: '#app-1',
// 对象的第一种写法 对象式
/* data: {
name: 'zhaoshuai-lc@inspur.com'
}*/
// 对象的第二种写法 函数式
// 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了,而是window。
// data: function () { 替换为
data() {
console.info('@@@', this) // 此处的this是Vue实例对象
return {
name: 'zhaoshuai-lc@inspur.com'
}
}
})
</script>
</body>
</html>
5 MVVM
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。
原型上有的东西,插值中都可以随便用,如“$options”、“$emit”、“_c”等。也就是说,并不是data里面有的,DOM才可以直接用,而是data中的东西,最终在实例里面(VM中),所以都能在模板(V)中直接使用。
MVVM模型:
1.M:模型(Model):对应data中的数据
2.V:视图(View):模板
3.VM:视图模型(ViewModel):Vue实例对象
观察发现:
1.data中所有的属性,最后都出现在VM身上。
2.VM身上的所有属性即Vue原型上所有属性,在Vue模板中都可以直接使用。
6 Object.defineProperty
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
let person = {
name: 'zhaoshuai-lc',
sex: 'male'
}
Object.defineProperty(person, 'age', {
value: 18
})
console.log(person)
for (let personKey in person) {
console.log(person[personKey])
}
</script>
</body>
</html>
age 是不可以被枚举的
Object.defineProperty(person, 'age', {
value: 18,
enumerable:true, //控制属性是否可以枚举,默认值为false
})
可以被枚举,但是不可以被修改
Object.defineProperty(person, 'age', {
value: 18,
enumerable:true, //控制属性是否可以枚举,默认值为false
writable:true, //控制属性是否可以被修改,默认值为false
configurable:true //控制属性是否可以被删除,默认值为false
})
get() set()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
let number = 19
let person = {
name: 'zhaoshuai-lc',
sex: 'male'
}
Object.defineProperty(person, 'age', {
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get() {
console.log('有人读取age属性了')
return number
},
// 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
console.info("person", person)
console.info("number", number)
</script>
</body>
</html>
7 理解数据代理
数据代理:通过一个对象代理对另一个对象中属性的操作 (读/写)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
let obj1 = {
x: 100
}
let obj2 = {
y: 200
}
Object.defineProperty(obj2, 'x', {
get() {
return obj1.x
},
set(value) {
obj1.x = value
}
})
</script>
</body>
</html>
8 Vue中的数据代理
将鼠标放到实例中的数据上,可以看到也提示了“Invoke property getter”。也就是说当有人访问name的时候,getter就在工作。
vm._data === data 为ture。也就是说_data完全来自于data
创建了实例对象vm——Vue收集data数据——拿到vm后,Vue往vm上添加name、address(通过getter)
也就是说,Vue通过数据代理,把_data中的数据放到vm上。目的就是为了编码更方便。
_data中的东西不是数据代理,而是做了一个数据劫持。相当于是把data里面的东西做了修改/升级,以便更好地完成响应式操作。
1.Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:更加方便操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
9 事件处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app-1">
<button v-on:click="showInfo">{{ name }}</button>
</div>
<script type="text/javascript">
new Vue({
el: '#app-1',
data() {
return {
name: '点我提示信息'
}
},
methods: {
showInfo() {
alert('Hello-zhaoshuai-lc@inspur.com')
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root">
<div>{{ name }}</div>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2(66,$event)">点我提示信息2(传参)</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#root',
data: {
name: 'zhaoshuai-lc'
},
methods: {
showInfo1(event) {
console.log(event.target.innerText)
console.log(this) //此处的this指的是vm
alert('hello-zhaoshuai-lc@inspur.com')
},
showInfo2(number, event) {
console.log(event.target.innerText)
alert('HELLO-zhaoshuai-lc@inspur.com')
}
}
})
</script>
</body>
</html>
事件的基本使用:
1.使用 v-on:xxx 或者 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了(指向window);
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或者组件实例对象;
5. @click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参。
10 时间修饰符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
<style>
* {
margin: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="event">
<!-- 阻止默认事件(常用) -->
<a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vmEvent = new Vue({
el: '#event',
data: {},
methods: {
showInfo() {
alert("hello-zhaoshuai-lc@inspur.com")
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
<style>
* {
margin: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="event">
<!-- 阻止默认事件(常用) -->
<a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
<div class="demo1" @click="showInfo_2">
<button @click.stop="showInfo_1">点我提示信息</button>
</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vmEvent = new Vue({
el: '#event',
data: {},
methods: {
showInfo_1() {
alert("hello-zhaoshuai-lc@inspur.com")
},
showInfo_2() {
alert("hello-2-zhaoshuai-lc@inspur.com")
}
}
})
</script>
</body>
</html>
<!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
<div class="demo1" @click="showInfo_2">
<button @click.stop="showInfo_1">点我提示信息</button>
</div>
这是往外冒泡的点击事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
<style>
* {
margin: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="event">
<!-- 阻止默认事件(常用) -->
<a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
<div class="demo1" @click="showInfo_2">
<button @click.stop="showInfo_1">点我提示信息</button>
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo_3">点我提示信息</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vmEvent = new Vue({
el: '#event',
data: {},
methods: {
showInfo_1() {
alert("hello-zhaoshuai-lc@inspur.com")
},
showInfo_2() {
alert("hello-2-zhaoshuai-lc@inspur.com")
},
showInfo_3() {
alert("hello-3-zhaoshuai-lc@inspur.com")
}
}
})
</script>
</body>
</html>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo_3">点我提示信息</button>
事件只会触发一次。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
<style>
* {
margin: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="event">
<!-- 使用事件的捕获模式 -->
<div class="box1" @click="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vmEvent = new Vue({
el: '#event',
data: {},
methods: {
showMsg(msg) {
console.log(msg)
}
}
})
</script>
</body>
</html>
冒泡的形式,从里到外调用(如上)
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
捕获的形式,从外到里调用(如上)
<!-- 只有event.target是当前操作的元素时才触发事件 -->
<div class="demo1" @click.self="showInfo_2">
<button @click="showInfo_1">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<!-- 滚轮事件-->
<!-- <ul @wheel.passive="demo" class="list">-->
<!-- 拖动滚动条事件-->
<ul @scroll.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
11 键盘事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="key-event">
<input type="text" placeholder="按下回车提示信息" @keyup.enter="showInfo">
</div>
<script type="text/javascript">
new Vue({
el: '#key-event',
data: {},
methods: {
showInfo(e) {
console.log(e.target.value)
}
}
})
</script>
</body>
</html>
1.Vue中常用的案件别名:
回车 => enter
删除 => delete(捕获“删除”和“退格”键)
推出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
打印其他键盘按键的别名以及code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="key-event">
<input type="text" placeholder="按下回车提示信息" @keyup="showInfo">
</div>
<script type="text/javascript">
new Vue({
el: '#key-event',
data: {},
methods: {
showInfo(e) {
console.log(e.key, e.keyCode)
}
}
})
</script>
</body>
</html>
特别注意两个单词的 如 CapsLock:
<input type="text" placeholder="按下回车提示信息" @keyup.caps-lock="showInfo">
系统修饰键(用法特殊):ctrl、alt、shift、meta
配合keyup使用:按下修饰键的同时,再按下其它键,随后释放其它键,事件才被触发。
配合keydown使用:正常触发
修饰符可以连续写,如@click.prevent.stop,先阻止默认事件,后阻止冒泡
系统修饰键可以连续写,如@keyup.ctrl.y,也就是按下Ctrl+y事件
12 姓名案例引出计算属性
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-methods">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="secondName"><br/>
全名:<span>{{firstName.slice(0,3)}}-{{secondName}} </span>
</div>
<script type="text/javascript">
new Vue({
el: '#name-methods',
data() {
return {
firstName: 'zhao',
secondName: 'shuai-lc'
}
},
methods: {}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-methods">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="secondName"><br/>
全名:<span>{{ fullName() }}</span>
</div>
<script type="text/javascript">
new Vue({
el: '#name-methods',
data() {
return {
firstName: 'zhao',
secondName: 'shuai-lc@inspur.com'
}
},
methods: {
fullName() {
return this.firstName + '-' + this.secondName
}
}
})
</script>
</body>
</html>
13 计算属性
计算属性:
1.定义:要用的属性不存在,要通过已有的属性计算得来。
2.原理:底层借助了Object.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
1)初次读取时会执行一次。
2)当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1)计算属性最终会出现在vm上,直接读取使用即可。
2)如果计算属性要被修改,那必须直接写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-calculate">
姓:<input type="text" v-model="firstName"> <br/>
名:<input type="text" v-model="lastName"> <br/>
全名:<span>{{ fullName }}</span> <br/>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#name-calculate',
data: {
firstName: 'zhao',
lastName: 'shuai-lc@inspur.com'
},
methods: {},
computed: {
fullName: {
get() {
return this.firstName + '-' + this.lastName
},
set(value) {
const list = value.split('-');
this.firstName = list[0]
this.lastName = list[1]
}
}
}
})
</script>
</body>
</html>
get有什么作用?当有人读取了fullName时,get就会被调用,且返回值就作为fullName的值
get什么时候被调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
1)vm._data里面时没有计算属性的。
2)计算属性中get函数的this,Vue已经维护好了,并把getter中的this指向调成了vm。
3)书写多次fullName但是getter只用了一次,说明Vue有缓存。如果用上集methods方法,则书写几次方法就调用几次,显然有弊端。
14 计算属性简写
确定了只读,不可以修改,才能使用简写方式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span> <br/><br/>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
// 简写
fullName: function () {
return this.firstName + '-' + this.lastName
}
}
})
</script>
</body>
</html>
14 天气案例引入监视属性
在Vue开发者工具中,如果在页面中没用到该值,即使数据已经发生改变,Vue也不会更新数据。
可以在@click中直接写要执行的简单语句
如果需要在@click中直接写window方法,如写alert,需要在data中写window,然后@click=‘window.alert( )’。
<button @click="judgeHot = !judgeHot">切换天气</button>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h1>今天天气很 - {{ info }}</h1>
<!-- <button @click="changeWeather()">切换天气</button>-->
<button @click="judgeHot = !judgeHot">切换天气</button>
</div>
<script type="text/javascript">
new Vue({
el: '#weather',
data: {
judgeHot: true
},
computed: {
info() {
return this.judgeHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.judgeHot = !this.judgeHot
}
}
})
</script>
</body>
</html>
15 监视属性
监视属性watch:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!
3.监视的两种写法:
1)new Vue时传入watch配置
2)通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h1>今天天气很 - {{ info }}</h1>
<button @click="changeWeather()">切换天气</button>
</div>
<script type="text/javascript">
new Vue({
el: '#weather',
data: {
judgeHot: true,
},
computed: {
info() {
return this.judgeHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.judgeHot = !this.judgeHot
}
},
watch: {
judgeHot: {
// immediate:true, //初始化时让hander()调用一下, 默认为false
immediate: false,
// handler什么时候调用?当judgeHot发生改变时
handler(newValue, oldValue) {
console.log('judgeHot 被修改了 ...', newValue, oldValue)
}
},
info: {
handler(newValue, oldValue) {
console.log('info 被修改了 ...', newValue, oldValue)
}
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h1>今天天气很 - {{ info }}</h1>
<button @click="changeWeather()">切换天气</button>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#weather',
data: {
judgeHot: true,
},
computed: {
info() {
return this.judgeHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.judgeHot = !this.judgeHot
}
}
})
vm.$watch('judgeHot', {
handler(newValue, oldValue) {
console.log('judgeHot 被修改了 ...', newValue, oldValue)
}
})
</script>
</body>
</html>
16 深度监视
深度监视:
1.Vue中的watch默认不监测对象内部值得改变(一层)。
2.配置deep:true可以监测对象内部值改变(多层)。
备注:
1.Vue自身可以监测对象内部值得改变,但Vue提供得watch默认不可以!
2.使用watch时根据数据得具体结构,决定是否采用深度监视。
默认不开启是为了提升效率
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="autoIncrement()">点我让a+1</button>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#weather',
data: {
numbers: {
a: 1,
b: 2
}
},
computed: {},
methods: {
autoIncrement() {
return this.numbers.a++
}
},
watch: {
// 监视多级结构中某个属性的变化
'numbers.a': {
handler() {
console.log('a has changed ...')
}
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="autoIncrementA()">点我让a+1</button>
<h3>b的值是:{{ numbers.b }}</h3>
<button @click="autoIncrementB()">点我让b+1</button>
<br/><br/>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#weather',
data: {
numbers: {
a: 1,
b: 2
}
},
computed: {},
methods: {
autoIncrementA() {
return this.numbers.a++
},
autoIncrementB() {
return this.numbers.b++
}
},
watch: {
// 监视多级结构中所有属性的变化
numbers: {
deep: true,
handler() {
console.log('numbers has changed ...')
}
}
}
})
</script>
</body>
</html>
17 监视的简写形式
简写的前提是,如果不需要immediate、deep等的配置项,即配置项中只有handler的时候才可以简写。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root111">
<h2>今天天气很{{ info }}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root111',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch: {
/*isHot:{
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}*/
isHot(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
}
})
vm.$watch('isHot', {
handler(newValue, oldValue) {
console.log('isHot 被修改了 ...', newValue, oldValue)
}
})
//vm.$watch简写
vm.$watch('isHot', function (newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
})
</script>
</body>
</html>
18 watch对比computed
两者都能实现的时候,选用computed比较简单,需要异步计算等比较复杂实现的时候用watch。
computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="watch-name">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#watch-name',
data: {
firstName: 'zhao',
lastName: 'shuai-lc@inspur.com',
fullName: 'zhao#shuai-lc@inspur.com'
},
watch: {
firstName(newValue, oldValue) {
this.fullName = newValue + '#' + this.lastName
},
lastName(newValue, oldValue) {
this.fullName = this.firstName + '#' + newValue
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="watch-name">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#watch-name',
data: {
firstName: 'zhao',
lastName: 'shuai-lc@inspur.com',
fullName: 'zhao#shuai-lc@inspur.com'
},
watch: {
firstName(val) {
setTimeout(() => {
console.log(this) // vue
this.fullName = val + '-' + this.lastName
}, 1000)
setTimeout(function () {
console.log(this) // window
this.fullName = val + '-' + this.lastName
}, 1000)
},
lastName(val) {
this.fullName = this.firstName + '-' + val
}
}
})
</script>
</body>
</html>
19 条件渲染
使用template可以使其里面的内容在html的结构中不变。
条件渲染:
1.v-if
写法:
(1)v-if=“表达式”
(2)v-else-if=“表达式”
(3)v-else
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”。
2.v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
3.备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="conditional-rendering">
<!-- 使用v-show做条件渲染 切换频率高-->
<!-- <h1 v-show="judge">hello {{ name }}</h1>-->
<!-- 使用v-if做条件渲染 切换频率低-->
<!-- <h1 v-if="judge">hello {{ name }}</h1>-->
<h2>当前的n值是:{{ n }}</h2>
<button @click="n++">点我n+1</button>
<!-- v-if和v-else-if -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>other</div>
<!-- v-if与template结合 -->
<template v-if="n === 1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#conditional-rendering',
data: {
name: 'zhaoshuai-lc@inspur.com',
judge: false,
n: 1
}
})
</script>
</body>
</html>
20 列表渲染
v-for指令:
1.用于展示列表数据
2.语法:v-for=“(item,index) in xxx” :key=“yyy”
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<ul>
<li v-for="item in personList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
personList: [
{id: 1001, name: 'zhaoshuai-la', age: 101},
{id: 1002, name: 'zhaoshuai-lb', age: 102},
{id: 1003, name: 'zhaoshuai-lc', age: 103}
]
}
})
</script>
</body>
</html>
<ul>
<li v-for="(a, b) in personList">
{{ a }} - {{ b }}
</li>
</ul>
上面的key可以做一个替换:
<ul>
<li v-for="(item, index) in personList" :key="index">
{{ item.name }} - {{ item.age }}
</li>
</ul>
in 也可以替换为 of:
<ul>
<li v-for="(item, index) of personList" :key="index">
{{ item.name }} - {{ item.age }}
</li>
</ul>
除了数组还可以遍历对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<ul>
<li v-for="(item, index) of personList" :key="index">
{{ item.name }} - {{ item.age }}
</li>
</ul>
<ul>
<li v-for="(key, value) of car" :key="key">
{{ key }} - {{ value }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
personList: [
{id: 1001, name: 'zhaoshuai-la', age: 101},
{id: 1002, name: 'zhaoshuai-lb', age: 102},
{id: 1003, name: 'zhaoshuai-lc', age: 103}
],
car: {
name: '奥迪A8',
price: '70万',
color: '黑色'
}
}
})
</script>
</body>
</html>
测试遍历字符串:
<ul>
<li v-for="(char, index) of str" :key="index">
{{ index }} - {{ char }}
</li>
</ul>
str: 'hello'
21 key作用与原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<button @click.once="add">添加一个 zhaoshuai-ld</button>
<ul>
<li v-for="(item, index) of personList" :key="index">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
personList: [
{id: 1001, name: 'zhaoshuai-la', age: 101},
{id: 1002, name: 'zhaoshuai-lb', age: 102},
{id: 1003, name: 'zhaoshuai-lc', age: 103}
]
},
methods: {
add() {
let person = {id: 1004, name: 'zhaoshuai-ld', age: 104}
this.personList.unshift(person)
}
}
})
</script>
</body>
</html>
看似没有问题,其实问题很大,如下:
遍历列表时key的作用(index作为key)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<button @click.once="add">添加一个 zhaoshuai-ld</button>
<ul>
<li v-for="(item, index) of personList" :key="index">
{{ item.name }} - {{ item.age }}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
personList: [
{id: 1001, name: 'zhaoshuai-la', age: 101},
{id: 1002, name: 'zhaoshuai-lb', age: 102},
{id: 1003, name: 'zhaoshuai-lc', age: 103}
]
},
methods: {
add() {
let person = {id: 1004, name: 'zhaoshuai-ld', age: 104}
this.personList.unshift(person)
}
}
})
</script>
</body>
</html>
遍历列表时key的作用(id作为key)
<ul>
<li v-for="(item, index) of personList" :key="item.id">
{{ item.name }} - {{ item.age }}
<input type="text">
</li>
</ul>
面试题:react、Vue中的key有什么作用?(key的内部原理)
1.虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
若虚拟DOM中内容没变,直接使用之前的真实DOM
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key:
创建新的真实DOM,随后渲染到页面
3.用index作为key可能会引发的问题:
(1)若对数据进行逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
4.开发中如何选择key?
(1)最好使用每条数据的唯一标识作为key,比如说id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表中用于展示,使用index作为key是没有问题的。
22 列表过滤
用watch实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<input type="text" placeholder="请输入名字" v-model="keyWorld">
<ul>
<li v-for="(item, index) of filterPersonList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
keyWorld: '',
personList: [
{id: '001', name: '马冬梅', age: 19, sex: '女'},
{id: '002', name: '周冬雨', age: 20, sex: '女'},
{id: '003', name: '周杰伦', age: 21, sex: '男'},
{id: '004', name: '温兆伦', age: 22, sex: '男'}
],
filterPersonList: []
},
watch: {
keyWorld: {
immediate: true,
handler(newValue, oldValue) {
this.filterPersonList = this.personList.filter((item) => {
return item.name.indexOf(newValue) !== -1
})
}
}
}
})
</script>
</body>
</html>
用computed实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<input type="text" placeholder="请输入名字" v-model="keyWorld">
<ul>
<li v-for="(item, index) of filterPersonList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
keyWorld: '',
personList: [
{id: '001', name: '马冬梅', age: 19, sex: '女'},
{id: '002', name: '周冬雨', age: 20, sex: '女'},
{id: '003', name: '周杰伦', age: 21, sex: '男'},
{id: '004', name: '温兆伦', age: 22, sex: '男'}
]
},
computed: {
filterPersonList() {
return this.personList.filter((item) => {
return item.name.indexOf(this.keyWorld) !== -1
})
}
}
})
</script>
</body>
</html>
23 列表排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<input type="text" placeholder="请输入名字" v-model="keyWorld">
<button @click="sortType = 2">升序</button>
<button @click="sortType = 1">降序</button>
<button @click="sortType = 0">原序</button>
<ul>
<li v-for="(item, index) of filterPersonList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#v-for',
data: {
keyWorld: '',
sortType: 0, // 0-原顺序 1-降序 2-升序
personList: [
{id: '001', name: '马冬梅', age: 19, sex: '女'},
{id: '002', name: '周冬雨', age: 24, sex: '女'},
{id: '003', name: '周杰伦', age: 55, sex: '男'},
{id: '004', name: '温兆伦', age: 12, sex: '男'}
]
},
computed: {
filterPersonList() {
let filterList = this.personList.filter((item) => {
return item.name.indexOf(this.keyWorld) !== -1
})
// 判断是否需要排序
if (this.sortType) {
filterList.sort((a, b) => {
return this.sortType === 1 ? b.age - a.age : a.age - b.age
})
}
return filterList
}
}
})
</script>
</body>
</html>
24 更新时的一个问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<button @click="updateMei">更新马冬梅的信息</button>
<ul>
<li v-for="(item, index) of personList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#v-for',
data: {
personList: [
{id: '001', name: '马冬梅', age: 19, sex: '女'},
{id: '002', name: '周冬雨', age: 24, sex: '女'},
{id: '003', name: '周杰伦', age: 55, sex: '男'},
{id: '004', name: '温兆伦', age: 12, sex: '男'}
]
},
methods: {
updateMei() {
/*this.personList[0].name = '马保国'
this.personList[0].age = 50
this.personList[0].sex = '男'*/
// ok 如上是起作用的
this.personList[0] = {id: '001', name: '马保国', age: 50, sex: '男'}
}
}
})
</script>
</body>
</html>
25 Vue监测数据的原理_对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-school">
学校名称:{{ name }} <br/>
学校地址:{{ address }}
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#v-school',
data: {
name: '北京大学',
address: '北京'
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
let data = {
name: '尚硅谷',
address: '北京',
}
// 创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
// 准备一个vm实例对象
let vm = {}
vm._data = data = obs
function Observer(obj) {
// 汇总对象中所有的属性
const keys = Object.keys(obj)
// 遍历
keys.forEach((k) => {
Object.defineProperty(this, k, {
get() {
return obj[k]
},
set(val) {
console.log('${k}被改了,我要去解析模板,生成虚拟DOM...我要开始忙了')
obj[k] = val
}
})
})
}
</script>
</body>
</html>
本次书写的代码为简化版,Vue更完善的点有:
如果要修改_data中的name,完整写法为 vm._data.name = ‘atguigu’ ,还可以直接简写为 vm.name = ‘atguigu’,因为Vue做了数据代理。
当对象中还有对象时,Vue也能做到有为其服务的getter和setter。Vue里面写了递归,有多少层就能写到多少层,直到那个东西不再是对象为止,对象在数组中也是如此。
Vue监测数据的原理,就是靠setter。
只要修改数据,Vue就会重新解析模板,生成虚拟DOM。
26 Vue.set()方法
(1)假设数组a中不存在对象b,Vue中访问a.b不会报错,只是不显示(Vue中默认undefined不显示),Vue中访问b会报错。
(2)Vue.set 只能给data里的某个对象追加属性,不能直接给data追加属性。
(3)向响应式对象中添加一个property,并确保这个新property同样是响应式的,且触发视图更新。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-set">
<h1>学校信息</h1>
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<hr/>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性,默认值是男</button>
<h2>姓名:{{ student.name }}</h2>
<h2>性别:{{ student.sex }}</h2>
<h2>年龄:真实{{ student.age.rAge }}, 对外{{ student.age.sAge }}</h2>
<h2>朋友们:</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{ f.name }}--{{ f.age }}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#v-set',
data: {
name: '北京大学',
address: '北京',
student: {
name: 'tom',
age: {
rAge: 40,
sAge: 29
},
friends: [
{name: 'jerry', age: 35},
{name: 'tony', age: 36}
]
}
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
this.$set(this.student, 'sex', '男')
}
},
})
</script>
</body>
</html>
27 Vue监测数据的原理_数组
原生Javascript数组使用的方法,例如push,就是从Array原型中找到的。可用 arr.push === Array.prototype.push 验证。
而Vue中的push却不等于 Array.prototype.push ,因为Vue中的push是经过包装的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="ddd">
<h1>学生信息</h1>
<h2>爱好:</h2>
<ul>
<li v-for="(item, index) in student.hobby" :key="index">
{{ item }}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#ddd',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{name: 'jerry', age: 35},
{name: 'tony', age: 36}
]
}
},
methods: {
},
})
</script>
</body>
</html>
针对于上面的数组,有了解决方案:如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
<button @click="updateMei">更新马冬梅的信息</button>
<ul>
<li v-for="(item, index) of personList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#v-for',
data: {
personList: [
{id: '001', name: '马冬梅', age: 19, sex: '女'},
{id: '002', name: '周冬雨', age: 24, sex: '女'},
{id: '003', name: '周杰伦', age: 55, sex: '男'},
{id: '004', name: '温兆伦', age: 12, sex: '男'}
]
},
methods: {
updateMei() {
/*this.personList[0].name = '马保国'
this.personList[0].age = 50
this.personList[0].sex = '男'*/
// ok 如上是起作用的
// this.personList[0] = {id: '001', name: '马保国', age: 50, sex: '男'} 不起作用
this.personList.splice(0,1, {id: '001', name: '马保国', age: 50, sex: '男'})
}
}
})
</script>
</body>
</html>
28 总结Vue监视数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="ddd">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button>
<br/>
<button @click="addSex">添加一个性别属性,默认值:男</button>
<br/>
<button @click="student.sex = '未知' ">修改性别</button>
<br/>
<button @click="addFriend">在列表首位添加一个朋友</button>
<br/>
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button>
<br/>
<button @click="addHobby">添加一个爱好</button>
<br/>
<button @click="updateHobby">修改第一个爱好为:开车</button>
<br/>
<button @click="removeSmoke">过滤掉爱好中的抽烟</button>
<br/>
<h2>姓名:{{ student.name }}</h2>
<h2 v-if="student.sex">性别:{{ student.sex }}</h2>
<h2>年龄:{{ student.age }}</h2>
<h2>爱好:</h2>
<ul>
<li v-for="(item, index) in student.hobby" :key="index">
{{ item }}
</li>
</ul>
<h2>朋友们:</h2>
<ul>
<li v-for="(item, index) in student.friends" :key="index">
{{ item.name }}--{{ item.age }}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#ddd',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{name: 'jerry', age: 35},
{name: 'tony', age: 36}
]
}
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
this.$set(this.student, 'sex', '男')
},
addFriend() {
this.student.friends.unshift({name: 'jack', age: 70})
},
updateFirstFriendName() {
this.student.friends[0].name = '张三'
this.student.friends[0].age = 5
},
addHobby() {
this.student.hobby.push('学习')
},
updateHobby() {
// this.student.hobby.splice(0,1,'开车')
// Vue.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby, 0, '开车')
},
removeSmoke() {
this.student.hobby = this.student.hobby.filter((h) => {
return h !== '抽烟'
})
}
},
})
</script>
</body>
</html>
Vue监视数据的原理:
1.Vue会监视data中所有层次的数据。
2.如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1)对象中后追加的属性,Vue默认不做响应式处理。
(2)如需给后添加的属性做响应式,请使用如下API:
vm.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3.如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新。
(2)重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm的根数据对象添加属性!