logo头像
Snippet 博客主题

Vue新手入门指南

1. 为什么要学习流行框架

  • 企业使用框架是为了提高开发效率:在企业中,时间就是效率,效率就是金钱;提高开发效率的发展历程:原生JS -> JQuery之类的类库 -> 前后端模板引擎(ejs/JSP/Freemaker/Thymeleaf/Velocity) -> Angular.js / Vue.js/Rect.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)

  • 简化开发:在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;

  • 增强自己就业时候的竞争力,人无我有,人有我优,我们平时不忙的时候,闲着都在干嘛?玩手机、睡懒觉、追剧?一个60岁的老人都能做的事情,那我们20多岁的人还可以干点什么呢?

    2. 框架和库的区别

  • 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。

    • node 中的 express;Java中的Spring Boot,Python中的django,都是完整的Web解决方案。
  • 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。

      1. 从Jquery 切换到 Zepto
      1. 从 EJS 切换到 art-template,EJS没有自定义过滤器.

3. 开发模式的演变

  • MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用。JAVAEE、.NET三层架构都是基于MVC模式设计出来的

    MVC流程图
    Spring WebMvc执行流程图

  • MVP 模式将 Controller 改名为 Presenter,同时将单向通信改为双向通信。Android开发领域常见模式;

    MVP

  • MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel.

    MVVM模式图

MVVM NodeJS开发模式

4. 流行框架对比

4.1 Angular

由Google公司开发维护,前几年很流行,现在不流行,主要是框架太重,学习曲线高.

4.2 React

FaceBook开源软件,拥有虚拟DOM\提供响应式和组件化的视图组件.运行性能较高.国内互联网巨头(阿里、美团)公司较为常用,可以通过React Native使你只使用JavaScript也能编写原生移动应用。

4.3 Vue

华人尤雨溪个人开发,吸取了Angular和React的优点,大大缩小了库的体积,提升了运行时性能。同样,也使用了虚拟DOM和数据双向绑定,学习曲线很低,目前来看流行度特别高。

5. Vue.js基础学习

5.1 Vue.js是什么

  • Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)
  • Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架!
  • Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
  • 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;

5.2 插值表达式和v-cloak

5.2.1 插值表达式

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 1. 导入Vue的包 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 将来 new 的Vue实例,会控制这个 元素中的所有内容 -->
<!-- Vue 实例所控制的这个元素区域,就是我们的 V -->
<div id="app">
<p>{{ msg }}</p>
</div>

<script>
// 2. 创建一个Vue的实例
// 当我们导入包之后,在浏览器的内存中,就多了一个 Vue 构造函数
// 注意:我们 new 出来的这个 vm 对象,就是我们 MVVM中的 VM调度者
var vm = new Vue({
el: '#app', // 表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
// 这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
data: { // data 属性中,存放的是 el 中要用到的数据
msg: '大家好,我是李彤' // 通过 Vue 提供的指令,很方便的就能把数据渲染到页面上,程序员不再手动操作DOM元素了【前端的Vue之类的框架,不提倡我们去手动操作DOM元素了】
}
})
</script>
</body>
</html>

5.2.2 v-cloak和v-text指令

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 使用 v-cloak 能够解决 插值表达式闪烁的问题 -->
<p v-cloak>++++++++ {{ msg }} ----------</p>
<h4 v-text="msg">==================</h4>
<!-- 默认 v-text 是没有闪烁问题的 -->
<!-- v-text会覆盖元素中原本的内容,但是 插值表达式 只会替换自己的这个占位符,不会把 整个元素的内容清空 -->
<div>{{msg2}}</div>
<div v-text="msg2"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '123',
msg2: '<h1>我是李彤,前后端通吃的程序员</h1>',
}
})
</script>
</body>
</html>

5.3 Vue指令之v-html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<div v-text="msg2"></div>
<div v-html="msg2">1212112</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg2: '<h1>我是李彤,前后端通吃的程序员</h1>'
}
})
</script>
</body>
</html>

5.4 Vue指令之v-bind和v-on

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- v-bind: 是 Vue中,提供的用于绑定属性的指令 -->
<input type="button" value="按钮1" v-bind:title="mytitle + '123'">
<!-- v-bind Vue提供的属性绑定机制 缩写是 :要绑定的属性 -->
<!-- v-on Vue提供的事件绑定机制 缩写是 @要绑定的事件 -->
<!-- v-bind 中,可以写合法的JS表达式 -->
<!-- Vue 中提供了 v-on: 事件绑定机制 , v-on:click和@click不能直接写js表达式-->
<input type="button" value="按钮2" :title="mytitle + '123'" v-on:click="show()">
<input type="button" value="按钮3" @click="show">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
mytitle: 'Vue学习'
},
methods: { // 这个 methods属性中定义了当前Vue实例所有可用的方法
show: function () {
alert('Hello')
}
}
})

/*
// 原生JS事件点击操作
document.getElementById('btn').onclick = function(){
alert('Hello')
}
*/
</script>
</body>
</html>

5.5 Vue指令之v-pre和v-once

5.5.1 v-pre指令

  • 显示原始信息跳过编译过程
  • 跳过这个元素和它的子元素的编译过程。
  • 一些静态的内容不需要编译加这个指令可以加快渲染
1
2
3
4
5
6
7
8
9
10
11
12
<span v-pre>{{ this will not be compiled }}</span>    
<!-- 显示的是{{ this will not be compiled }} -->
<span v-pre>{{msg}}</span> 
<!--   即使data里面定义了msg这里仍然是显示的{{msg}}-->
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>

5.5.3 v-once指令

执行一次性的插值【当数据改变时,插值处的内容不会继续更新】

1
2
3
4
5
6
7
8
9
10
<!-- 即使data里面定义了msg 后期我们修改了 仍然显示的是第一次data里面存储的数据即 Hello Vue.js  -->
<span v-once>{{ msg}}</span>
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>

5.6 跑马灯效果练习

  1. HTML结构:
1
2
3
4
5
6
<!-- 1. 创建一个要控制的区域 -->
<div id="app">
<input type="button" value="浪起来" @click="lang">
<input type="button" value="低调" @click="stop">
<h4>{{ msg }}</h4>
</div>
  1. 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
32
33
34
/*
分析:
1. 给 【浪起来】 按钮,绑定一个点击事件 可写v-on,也可以写@事件名称
2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到 msg 字符串,然后 调用 字符串的 substring 来进行 字符串的截取操作,把 第一个字符截取出来,放到最后一个位置即可;
3. 为了实现点击下按钮,自动截取的功能,需要把 2 步骤中的代码,放到一个定时器中去。
注意:
在VM实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名 或this.方法名 来进行访问,这里的this,就表示 我们 new 出来的 VM 实例对象
*/
var vm = new Vue({
el: '#app',
data: {
msg: '我是李彤,浪起来~~!',
intervalId: null // 在data上定义 定时器Id
},
methods: {
lang() {
// console.log(this.msg)
// 获取到头的第一个字符
if (this.intervalId) return;

this.intervalId = setInterval(() => {
// 获取到第一个字符串符和剩下的字符串后两者拼接,最终赋值到this.msg里
this.msg= this.msg.substring(1) + this.msg.substring(0,1);
}, 400)

// 注意: VM实例,会监听自己身上data中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从 data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
},
stop() { // 停止定时器
clearInterval(this.intervalId)
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
this.intervalId = null;
}
}
})

##

6. Vue指令之事件修饰符

  • .stop 阻止冒泡
  • .prevent 阻止默认事件
  • .capture 添加事件侦听器时使用事件捕获模式
  • .self 只当事件在该元素本身(比如不是子元素)触发时触发回调
  • .once 事件只触发一次
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.inner {
height: 150px;
background-color: darkcyan;
}

.outer {
padding: 40px;
background-color: pink;
}
</style>
</head>

<body>
<div id="app">

<!-- 使用 .stop 阻止冒泡 -->
<div class="inner" @click="div1Handler">
<input type="button" value="戳他1" @click.stop="btnHandler">
</div>

<!-- 使用 .prevent 阻止默认行为 -->
<a href="http://www.baidu.com" @click.prevent="linkClick">有问题,先去百度</a>

<!-- 使用 .capture 实现捕获触发事件的机制 -->
<div class="inner" @click.capture="div1Handler">
<input type="button" value="戳他2" @click="btnHandler">
</div>

<!-- 使用 .self 实现只有点击当前元素时候,才会触发事件处理函数 -->
<div class="inner" @click.self="div1Handler">
<input type="button" value="戳他3" @click="btnHandler">
</div>

<!-- 使用 .once 只触发一次事件处理函数 -->
<a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题,先去百度</a>


<!-- 演示: .stop 和 .self 的区别 -->
<div class="outer" @click="div2Handler">
<div class="inner" @click="div1Handler">
<input type="button" value="戳他4" @click.stop="btnHandler">
</div>
</div>

<!-- .self 只会阻止自己身上冒泡行为的触发,并不会真正阻止 冒泡的行为 -->
<div class="outer" @click="div2Handler">
<div class="inner" @click.self="div1Handler">
<input type="button" value="戳他5" @click="btnHandler">
</div>
</div>

</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
div1Handler() {
console.log('这是触发了 inner div 的点击事件')
},
btnHandler() {
console.log('这是触发了 btn 按钮 的点击事件')
},
linkClick() {
console.log('触发了连接的点击事件')
},
div2Handler() {
console.log('这是触发了 outer div 的点击事件')
}
}
});
</script>
</body>
</html>

7. Vue指令之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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h4>{{ msg }}</h4>

<!-- v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定 -->
<!-- <input type="text" v-bind:value="msg" style="width:100%;"> -->

<!-- 使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定 -->
<!-- 注意: v-model 只能运用在 表单元素中 -->
<!-- input(radio, text, address, email....) select checkbox textarea -->
<input type="text" style="width:100%;" v-model="msg">
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '我是好男人,爱敲代码,爱学习,爱思考,简直是完美,没瑕疵!'
}
});
</script>
</body>
</html>

7.1 简易计算器案例

  1. HTML 代码结构
1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<input type="text" v-model="n1">
<select v-model="opt">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" v-model="n2">
<input type="button" value="=" @click="calc">
<input type="text" v-model="result">
</div>
  1. 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
32
33
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
n1: 0,
n2: 0,
result: 0,
opt: '+'
},
methods: {
calc() { // 计算器算数的方法
// 逻辑:
/* switch (this.opt) {
case '+':
this.result = parseInt(this.n1) + parseInt(this.n2)
break;
case '-':
this.result = parseInt(this.n1) - parseInt(this.n2)
break;
case '*':
this.result = parseInt(this.n1) * parseInt(this.n2)
break;
case '/':
this.result = parseInt(this.n1) / parseInt(this.n2)
break;
} */

// 注意:这是投机取巧的方式,正式开发中,尽量少用
var codeStr = 'parseInt(this.n1) ' + this.opt + ' parseInt(this.n2)'
this.result = eval(codeStr)
}
}
});

8. 在Vue中使用样式

8.1 使用class样式

  1. 数组

    1
    <h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
  2. 数组中使用三元表达式

    1
    <h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
  3. 数组中嵌套对象

    1
    <h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
  4. 直接使用对象

    1
    <h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>

8.2 使用内联样式

  1. 直接在元素上通过 :style 的形式,书写样式对象

    1
    <h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
  2. 将样式对象,定义到 data 中,并直接引用到 :style

    • 在data上定义样式:

      1
      2
      3
      data: {
      h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
      }
    • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

      1
      <h1 :style="h1StyleObj">这是一个善良的H1</h1>
  3. :style 中通过数组,引用多个 data 上的样式对象

    • 在data上定义样式:

      1
      2
      3
      4
      data: {
      h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
      h1StyleObj2: { fontStyle: 'italic' }
      }
    • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

      1
      <h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>

8.3 样式综合练习

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.red {
color: red;
}

.thin {
font-weight: 200;
}

.italic {
font-style: italic;
}

.active {
letter-spacing: 0.5em;
}
</style>
</head>

<body>
<div id="app">
<h1 class="red thin">这是一个很大很大的H1,大到你无法想象!!!</h1>
<!-- 第一种使用方式,直接传递一个数组,注意: 这里的 class 需要使用 v-bind 做数据绑定 -->
<h1 :class="['thin', 'italic']">这是一个很大很大的H1,大到你无法想象!!!</h1>
<!-- 在数组中使用三元表达式 -->
<h1 :class="['thin', 'italic', flag?'active':'']">这是一个很大很大的H1,大到你无法想象!!!</h1>
<!-- 在数组中使用 对象来代替三元表达式,提高代码的可读性 -->
<h1 :class="['thin', 'italic', {'active':flag} ]">这是一个很大很大的H1,大到你无法想象!!!</h1>
<!-- 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名,由于 对象的属性可带引号,也可不带引号,所以 这里我没写引号; 属性的值 是一个标识符 -->
<h1 :class="classObj">这是一个很大很大的H1,大到你无法想象!!!</h1>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true,
classObj: { red: true, thin: true, italic: false, active: false }
},
methods: {}
});
</script>
</body>
</html>
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
<!-- 对象就是无序键值对的集合 -->
<h1 :style="styleObj1">哈哈哈,我笑了</h1>

<h1 :style="[ styleObj1, styleObj2 ]">呜呜呜,我哭了</h1>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
styleObj1: { color: 'red', 'font-weight': 200 },
styleObj2: { 'font-style': 'italic' }
},
methods: {}
});
</script>
</body>
</html>

9. Vue指令之v-for和key属性

  1. 迭代数组
1
2
3
<ul>
<li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年龄:{{item.age}}</li>
</ul>
  1. 迭代对象中的属性
1
2
<!-- 循环遍历对象身上的属性 -->
<div v-for="(val, key, i) in userInfo">{{val}} --- {{key}} --- {{i}}</div>
  1. 迭代数字
1
2
 <!-- 注意:如果使用 v-for 迭代数字的话,前面的 count 值从 1 开始 -->
<p v-for="i in 10">这是第 {{i}} 个P标签</p>

2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。

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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
<div>
<label>Id:
<input type="text" v-model="id">
</label>

<label>Name:
<input type="text" v-model="name">
</label>

<input type="button" value="添加" @click="add">
</div>

<!-- 注意: v-for 循环的时候,key 属性只能使用 number获取string -->
<!-- 注意: key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值 -->
<!-- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果 v-for 有问题,必须 在使用 v-for 的同时,指定 唯一的 字符串/数字 类型 :key 值 -->
<p v-for="item in list" :key="item.id">
<input type="checkbox">{{item.id}} --- {{item.name}}
</p>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [
{ id: 1, name: '李斯' },
{ id: 2, name: '嬴政' },
{ id: 3, name: '赵高' },
{ id: 4, name: '韩非' },
{ id: 5, name: '荀子' }
]
},
methods: {
add() { // 添加方法
this.list.unshift({ id: this.id, name: this.name })
}
}
});
</script>
</body>
</html>

10. Vue指令之v-if和v-show

一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
<!-- <input type="button" value="toggle" @click="toggle"> -->
<input type="button" value="toggle" @click="flag=!flag">

<!-- v-if 的特点:每次都会重新删除或创建元素 -->
<!-- v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式 -->
<!-- v-if 有较高的切换性能消耗 -->
<!-- v-show 有较高的初始渲染消耗 -->
<!-- 如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show -->
<!-- 如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if -->
<h3 v-if="flag">这是用v-if控制的元素</h3>
<h3 v-show="flag">这是用v-show控制的元素</h3>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {
/*
toggle() {
this.flag = !this.flag
}
*/
}
});
</script>
</body>
</html>

10.1 根据条件筛选数据

  1. 1.x 版本中的filterBy指令,在2.x中已经被废除:

filterBy - 指令

1
2
3
4
5
6
7
8
<tr v-for="item in list | filterBy searchName in 'name'">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.ctime}}</td>
<td>
<a href="#" @click.prevent="del(item.id)">删除</a>
</td>
</tr>
  1. 在2.x版本中手动实现筛选的方式
  • 筛选框绑定到 VM 实例中的 searchName 属性:
1
2
<hr> 输入筛选名称:
<input type="text" v-model="searchName">
  • 在使用 v-for 指令循环每一行数据的时候,不再直接 item in list,而是 in 一个 过滤的methods 方法,同时,把过滤条件searchName传递进去:
1
2
3
4
5
6
7
8
9
10
11

<tbody>
<tr v-for="item in search(searchName)">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.ctime}}</td>
<td>
<a href="#" @click.prevent="del(item.id)">删除</a>
</td>
</tr>
</tbody>
  • search 过滤方法中,使用数组的 filter方法进行过滤:
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
add() { // 添加的方法
// console.log('ok')
// 分析:
// 1. 获取到 id 和 name ,直接从 data 上面获取
// 2. 组织出一个对象
// 3. 把这个对象,调用 数组的 相关方法,添加到 当前 data 上的 list 中
// 4. 注意:在Vue中,已经实现了数据的双向绑定,每当我们修改了 data 中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上;
// 5. 当我们意识到上面的第四步的时候,就证明大家已经入门Vue了,我们更多的是在进行 VM中 Model 数据的操作,同时,在操作Model数据的时候,指定的业务逻辑操作;

var car = { id: this.id, name: this.name, ctime: new Date() }
this.list.push(car)
this.id = this.name = ''
},
search(name) {
/*
var newList = []
this.list.forEach(item => {
if (item.name.indexOf(keywords) != -1) {
newList.push(item)
}
})
return newList
*/
// 注意: forEach some filter findIndex 这些都属于数组的新方法,
// 都会对数组中的每一项,进行遍历,执行相关的操作;
return this.list.filter(item => {
// if(item.name.indexOf(keywords) != -1)
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
// 如果包含,则返回 true ,否则返回 false
// contain
if (item.name.includes(keywords)) {
return item;
}
})
},
del(id) {
/*
需求:
根据Id删除数据
分析:
1. 如何根据Id,找到要删除这一项的索引
2. 如果找到索引了,直接调用 数组的 splice 方法
*/

/* this.list.some((item, i) => {
if (item.id == id) {
this.list.splice(i, 1)
// 在 数组的 some 方法中,如果 return true,就会立即终止这个数组的后续循环
return true;
}
}) */

var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
// console.log(index)
this.list.splice(index, 1)
}

11. 过滤器

概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

11.1 私有过滤器

  1. HTML元素:
1
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
  1. 私有 filters 定义方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用
dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错
var dt = new Date(input);
// 获取年月日
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');

// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
// 否则,就返回 年-月-日 时:分:秒
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
// 获取时分秒
var hh = dt.getHours().toString().padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
}
}

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;

11.2 全局过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义一个全局过滤器
Vue.filter('dataFormat', function (input, pattern = '') {
var dt = new Date(input);
// 获取年月日
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
// 否则,就返回 年-月-日 时:分:秒
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
// 获取时分秒,padStart是ES6的新方法,不够补几位数补0
var hh = dt.getHours().toString().,padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
});

注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
<!-- 多个过滤器可以用|起来 -->
<p>{{ msg | msgFormat('疯狂+1', '123') | test }}</p>
</div>

<script>
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
Vue.filter('msgFormat', function (msg, arg, arg2) {
// 字符串的 replace 方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
return msg.replace(/单纯/g, arg + arg2)
})

Vue.filter('test', function (msg) {
return msg + '========'
})

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
},
methods: {}
});
</script>
</body>
</html>

12. 键盘修饰符以及自定义键盘修饰符

12.1 1.x中自定义键盘修饰符【了解即可】

1
Vue.directive('on').keyCodes.f2 = 113;

12.2 2.x中自定义键盘修饰符

  1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名:
1
Vue.config.keyCodes.f2 = 113;
  1. 使用自定义的按键修饰符:
1
<input type="text" v-model="name" @keyup.f2="add">

13. 自定义指令

  1. 自定义全局和局部的 自定义指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 自定义全局指令 v-focus,为绑定的元素自动获取焦点:
Vue.directive('focus', {
inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
el.focus();
}
});

// 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:
directives: {
color: { // 为元素设置指定的字体颜色
bind(el, binding) {
el.style.color = binding.value;
}
},
'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
el.style.fontWeight = binding2.value;
}
}
  1. 自定义指令的使用方式:
1
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">

Vue 1.x 中 自定义元素指令【已废弃,了解即可】

1
2
3
4
5
Vue.elementDirective('red-color', {
bind: function () {
this.el.style.color = 'red';
}
});

使用方式:

1
<red-color>1232</red-color>

14. Vue调试工具vue-devtools的安装步骤和使用

Vue.js devtools - 翻墙安装方式 - 推荐

安装后,我们浏览器f12按下后会多个vue选项,在这里我们可以实时查看Vue中data数据变化。

如果找不到vue选项,将vue-devtools扩展程序详情打开,将允许访问本地文件网站的选项打开即可。

15. HTTP请求类库

我们在Java的世界里,Http请求客户端类库真的很多,比如JDK原生的HTTPURLConnection、Apache HttpClient、Square OkHttp、unirest,除此Java之外,Python也有urllib、requests、aiohttp,JavaScript也有Jquery ajax、axios、fetch。下面让我来依次学习。

在学习之前,我们有个前提条件:我们后端Web项目必须跑起来,并且支持跨域,在这里,我是以Spring Boot Web框架开发Java项目,除此之外,读者们可以根据不同的技术栈来开发Web项目,比如Python的Django或者Flask框架、Node.js的express、egg框架。

15.1 XMLHttpRequests 原生Ajax

原生的写法比较麻烦,要考虑兼容性,现在项目开发几乎很少用这种方式进行ajax请求了。请点击这里查看详情。

15.2 JQuery Ajax

虽然说可以异步请求,但是为了不引入Jquery库,防止DOM操作,我们从此刻开始彻底抛弃Jquery Ajax。想要学习JQuery AJax可以点击这里进行学习。

15.3 axios

15.3.1 axios特性

首先打开axios主页,我们可以查看到axios特性:

  • 从浏览器端发送 XMLHttpRequests Ajax请求
  • 可以在node.js发送http请求
  • 支持 Promise API
  • 可以拦截请求和响应
  • 能转化请求和响应数据
  • 取消请求
  • 自动转化json数据
  • 客户端支持以防止XSRF,保证请求安全

15.3.2 请求示例

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
var url = 'http://localhost:9999/api/v2/sayHello'
// 1.发送GET请求
// 1.1 GET无参请求
axios.get(url).then((rs) => {
// rs.data是获取到服务器端的数据
console.log(rs.data) // {msg: "Hello! 无名! Welcome Study JavaScript!"}
}).catch(() => {
console.log('Http Request Error, url is ' + url)
})
// 1.2 GET有参请求
// 1.2.1 方式一: 通过params形式传递参数
axios.get(url, {
params: {
name: 'vue'
}
}).then((rs) => {
console.log(rs.data) // {"msg":"Hello! vue! Welcome Study JavaScript!"}
}).catch(() => {
console.log('Http Request Error, url is ' + url)
})
// 1.2.2 方式二: 请求地址追加参数
url += '?name=litong'
axios.get(url).then((rs) => {
// rs.data是获取到服务器端的数据
console.log(rs.data) // {msg: "Hello! litong! Welcome Study JavaScript!"}
}).catch(() => {
console.log('Http Request Error, url is ' + url)
})

// 2. POST表单请求
var url = 'http://localhost:9999/api/v2/doFormPost'
// 2.1 POST表单请求方式一: 通过URLSearchParams传递参数
var params = new URLSearchParams();
params.append('name', 'litong');
axios.post(url, params).then(function(ret){
console.log(ret.data);
}).catch(() => {
console.log('Http Request Error, url is ' + url);
})
// 2.2 POST表单请求方式二: 处理后进行表单请求,传递的参数默认是json数据
axios({
method: 'post',
url: url,
data: {
name: 'litong',
age : 25
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
transformRequest: [function (data) {
// Do whatever you want to transform the data
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return (ret.lastIndexOf('&') == ret.length-1) ? ret.substring(0, ret.length-1) : ret
}],
});
// 2.3 POST表单请求方式三: 利用qs模块处理data进行form表单提交
axios({
url: url,
method: 'post',
data: {
name: 'litong',
age : 25
},
transformRequest: [function (data) {
data = Qs.stringify(data);
return data;
}],
headers:{'Content-Type':'application/x-www-form-urlencoded'}
})

// 2.4 POST JSON请求,默认就是application/json请求:
var url = 'http://localhost:9999/api/v2/doJsonPost'
axios.post(url, {
name: 'react.js',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response.data); // {name: "react.js", lastName: "Flintstone", msg: "我是JSON提交"}
})
.catch(function (error) {
console.log('Http Request Error, url is ' + url);
});
// 3. DELETE请求和GET请求类似,此处不做演示
// 4. PUT请求和POST请求类似,此处不做演示

15.3.3 axios全局配置

1
2
3
4
5
6
7
8
// 配置公共的请求头 
axios.defaults.baseURL = 'http://localhost:9999/api/v2';
// 配置 超时时间
axios.defaults.timeout = 2500;
// 配置公共的请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

15.3.4 axios拦截器

  • 请求拦截器
    • 请求拦截器的作用是在请求发送前进行一些操作
      • 例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易
  • 响应拦截器
    • 响应拦截器的作用是在接收到响应后进行一些操作
      • 例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 请求拦截器 
axios.interceptors.request.use(function(config) {
console.log(config.url)
// 1.1 任何请求都会经过这一步 在发送请求之前做些什么
config.headers.mytoken = 'nihao';
// 1.2 这里一定要return 否则配置不成功
return config;
}, function(err){
// 1.3 对请求错误做点什么
console.log(err)
})
// 2. 响应拦截器
axios.interceptors.response.use(function(res) {
// 2.1 在接收响应做些什么
var data = res.data;
return data;
}, function(err){
// 2.2 对响应错误做点什么
console.log(err)
})

15.3 fetch

github简介:

fetch()函数是一种基于Promise的机制,用于在浏览器中以编程方式发出Web请求。这个项目是一个polyfill,它实现了标准Fetch规范的子集,足以使fetch成为传统Web应用程序中XMLHttpRequest的大多数用法的可行替代品。

  • Fetch API是新的ajax解决方案 Fetch会返回Promise
  • fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象
  • 支持fetch(url, options).then()的Promise语法,
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
var url = 'http://localhost:9999/api/v2/sayHello'
// 1.发送GET请求
// 1.1 GET无参请求
/*
Fetch API 基本用法
fetch(url).then()
第一个参数请求的路Fetch会返回Promise 所以我们可以使用then 拿到请求成功的结果
*/
fetch(url).then(function(data){
// text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
return data.json(); // {"msg":"Hello! 无名! Welcome Study JavaScript!"}
}).then(function(data){
// 在这个then里面我们能拿到最终的数据
console.log(data)
});

// 1.2 GET有参请求: 参数追加
url += '?name=李彤'
fetch(url, {
method: 'get' // 默认是GET请求
}).then(function (data) {
return data.json();
}).then(function (data) {
console.log(data) // {msg: "Hello! 李彤! Welcome Study JavaScript!"}
});
// 2. POST表单请求
var url = 'http://localhost:9999/api/v2/doFormPost';
fetch(url, {
method: 'post',
body: 'name=vue&age=25',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function(data) {
return data.json();
}).then(function(data) {
console.log(data) // {"msg":"我表单提交","name":"vue"}
});

// 3 POST JSON请求
var url = 'http://localhost:9999/api/v2/doJsonPost';
fetch(url, {
method: 'post',
body: JSON.stringify({
name: '张三',
pwd: '456'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data); // {"name":"张三","pwd":"456","msg":"我是JSON提交"}
});
// 4. DELETE请求和GET请求类似,此处不做演示
// 5. PUT请求和POST请求类似,此处不做演示

15.4 vue-resource

vue-resource是vue 1.0官方推荐的http请求库,本来还想学习一下,但是随手翻到的资料写到:Vue 要实现异步加载需要使用到 vue-resource 库,Vue.js 2.0版本推荐使用 axios 来完成 ajax 请求,突然一下没有学习的欲望。如果读者想学习,请移步这里进行学习。

16. 参考文档

  1. vue.js 1.x 文档
  2. vue.js 2.x 文档
  3. String.prototype.padStart(maxLength, fillString)
  4. js 里面的键盘事件对应的键码
  5. Vue.js双向绑定的实现原理
  6. axios主页
  7. fetch文档
  8. 流行框架对比
  9. MVC、MVP和MVVM模式演变
支付宝打赏 微信打赏

请作者喝杯咖啡吧