Vue
一、邂逅Vuejs
1、遇见Vuejs
1、认识Vuejs
Vue (读Vue (读音 /vjuː/,类似于 view)音 /vjuː/,类似于 view)
Vue是一个渐进式的框架,渐进式的框架:
- 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。
- 如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。比如Core+Vue-router+Vuex,也可以满足你各种各样的需求。
与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
2、Vue的特点和Web开发中常见的高级功能
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
2、安装Vuejs
1、方式一:下载和引入
在官网上直接下载vue.js文件引入到项目(本地)中,其中有开发环境与生产环境。
注意:
在下载时不能直接点击,直接点击的话你将看到vue.js的源码。应该右键选中从链接另存文件
。
其中
开发环境用在开发的时候,其中的代码包含了有帮助的命令行警告,方便程序员查看源代码,但相对的文件比较大。
生产环境用在发布产品的时候,其中的代码都是经过压缩的,优化了尺寸和速度,文件也比较小,方便用户下载,但代码的可读性极差。
一句话总结:开发环境面向的是程序员,生产环境面向的是用户。
2、方式二:直接CDN引入
你可以在你的项目中直接CDN(外部)引入:
1 | <!-- 开发环境版本,包含了有帮助的命令行警告 --> |
3、方式三:NPM安装
在用 Vue 构建大型应用时推荐使用 NPM 安装[1]。NPM 能很好地和诸如 webpack 或 Browserify 模块打包器配合使用。同时 Vue 也提供配套工具来开发单文件组件。
1 | 最新稳定版 |
3、第一个Vuejs程序
1、代码的执行
- 阅读JavaScript代码,程序发现创建了一个Vue对象;
- 创建Vue对象的时候,传入了一些options:{}
- {}中包含了el属性:该属性决定了这个Vue对象挂载到哪一个元素上,很明显,我们这里是挂载到了id为app的元素上;
- {}中包含了data属性:该属性中通常会存储一些数据:
- 这些数据可以是我们直接定义出来的,比如像上面代码这样
- 也可能是来自网络,从服务器加载的
2、浏览器执行代码的流程
- 执行到10~13行代码显然出对应的HTML;
- 执行第16行代码创建Vue实例,并且对原HTML进行解析和修改
3、响应式
Vue代码是可以实现响应式的。在浏览器里进入开发者模式F12
中的console
。在里面修改代码可以实现浏览器的内容也随着修改而响应着改变。
4、Vue与JavaScript (两种编程范式)
命令式编程(JavaScript )
命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。
优点:数据和界面完全分离,不需要js创建页面元素等操作
声明式编程(Vuejs)
声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。
优点:当数据发生改变时界面自动发生改变(响应式)
5、Vue的MVVM
1、是什么MVVM
MVVM(Model–view–viewmodel)是一种软件架构模式。
MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑(数据模型)的开发分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,[1] 这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。[1] 视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。
2、Vue的MVVM
- View层:
- 视图层
- 在前端开发中,通常就是DOM层
- 主要的作用是给用户展示各种信息
- Model层:
- 数据层
- 数据可能是我们固定的死数据,但更多的是来自我们服务器,从网络上请求下来的数据
- VueModel层:
- 视图模型层
- 视图模型层是View和Model沟通的桥梁
- 一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中
- 另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data
3、计数器的MVVM示例
计数器:点击 +
计数器+1,点击 -
计数器 -1
在Vue对象中
- 新属性:methods。该属性用于在Vue对象中定义方法。
- 新的指令:@click, 该指令用于监听某个元素的点击事件,并且需要指定当发生点击时,执行的方法(方法通常是methods中定义的方法)
计数器中就有严格的MVVM思想:
- View依然是我们的DOM
- Model就是我们我们抽离出来的obj
- ViewModel就是我们创建的Vue对象实例
它们之间如何工作呢?
- 首先ViewModel通过Data Binding让obj中的数据实时的在DOM中显示。
- 其次ViewModel通过DOM Listener来监听DOM事件,并且通过methods中的操作,来改变obj中的数据。
有了Vue帮助我们完成VueModel层的任务,在后续的开发,我们就可以专注于数据的处理,以及DOM的编写工作了。
6、创建Vue实例传入的options
在创建Vue实例的时候,传入了一个对象options。那么,这个options中可以包含哪些选项呢?详细解析
el:
传入类型:string | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM,挂载要管理的元素
限制:只在用
new
创建实例时生效data:
类型:Object | Function (组件当中data必须是一个函数)
作用:Vue实例对应的数据对象
限制:组件的定义只接受
function
methods:
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
components:
类型:
Object
详细:包含 Vue 实例可用组件的哈希表。
computed:
类型:
{ [key: string]: Function | { get: Function, set: Function } }
详细:
计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
注意如果你为一个计算属性使用了箭头函数,则
this
不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。
生命周期函数:
所有的生命周期钩子
hook
自动绑定this
上下文到实例中,因此你可以访问数据,对 property 和方法进行运算。(粗体表示常用)beforeCreate:
类型:
Function
详细:
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created:
类型:
Function
详细:
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,
$el
property 目前尚不可用。beforeMount:
类型:
Function
详细:
在挂载开始之前被调用:相关的
render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
mounted:
类型:
Function
详细:
实例被挂载后调用,这时
el
被新创建的vm.$el
替换了。如果根实例挂载到了一个文档内的元素上,当mounted
被调用时vm.$el
也在文档内。注意
mounted
不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在mounted
内部使用 vm.$nextTickbeforeUpdate:
类型:
Function
详细:
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated:
类型:
Function
详细:
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
注意
updated
不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在updated
里使用 vm.$nextTickactivated:
类型:
Function
详细:
被 keep-alive 缓存的组件激活时调用。
该钩子在服务器端渲染期间不被调用。
deactivated:
类型:
Function
详细:
被 keep-alive 缓存的组件停用时调用。
该钩子在服务器端渲染期间不被调用。
beforeDestroy:
类型:
Function
详细:
实例销毁之前调用。在这一步,实例仍然完全可用。
该钩子在服务器端渲染期间不被调用。
destroyed:
类型:
Function
详细:
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
该钩子在服务器端渲染期间不被调用。
errorCaptured:
2.5.0+ 新增(具体查看)
类型:
(err: Error, vm: Component, info: string) => ?boolean
详细:
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false
以阻止该错误继续向上传播。
7、Vue的生命周期
以下图来自官网
简化:
8、ES6补充
1、let/var
事实上var的设计可以看成JavaScript语言设计上的错误. 但是这种错误多半不能修复和移除, 以为需要向后兼容。于是,大概十年前,Brendan Eich就决定修复这个问题, 于是他添加了一个新的关键字: let。
我们可以将let看成更完美的var
1、块级作用域
- JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关
- 针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。
我们可以通过ES6与ES5的不同来显示块级作用域的作用:
ES5中的var是没有块级作用域的(if/for),var只有在function中才有块级作用域。
ES5之前因为if和for都没有块级作用域的概念,所以在很多时候,我们都必须借助于function的作用域来解决应用外面变量的问题。
ES6中的let是由块级作用的(if/for)
2、没有块级作用域引起的问题
for的块级:
1 | var btns = document.getElementsByTagName('button'); |
效果:无论点击哪个按钮,日志打印的都是第5个按钮被点击
说明:由于var没有块级作用域,被var定义的i
会随着i++
的改变而改变。function里面的i
受到for循环的i++
的影响,被改变成了5
,所以输出的都是第5个按钮被点击
3、解决方法:
用闭包可以解决问题。
1
2
3
4
5
6
7
8var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
(function (num) { // 0
btns[i].addEventListener('click', function () {
console.log('第' + num + '个按钮被点击');
})
})(i)
}为什么闭包可以解决问题:函数是一个作用域。
用ES6的let
1
2
3
4
5
6const btns = document.getElementsByTagName('button')
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
2、const
- 在很多语言中已经存在, 比如C/C++中, 主要的作用是将某个变量修饰为常量。
- 在JavaScript中也是如此, 使用const修饰的标识符为常量, 不可以再次赋值。
什么时候使用:
当我们修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性。
建议:
在ES6开发中,优先使用const, 只有需要改变某一个标识符的时候才使用let。
使用const时要注意的点(以下代码为错误展示):
一旦给const修饰的标识符被赋值之后, 不能修改
1
2const name = 'why';
name = 'abc';在使用const定义标识符,必须进行赋值
1
const name;
常量的含义是指向的对象不能修改, 但是可以改变对象内部的属性
1
2
3
4
5
6
7
8
9
10
11
12
13const obj = {
name: 'why',
age: 18,
height: 1.88
}
// const修饰的标识符被赋值之后, 不能修改
// obj = {}
// 但是可以改变对象内部的属性
obj.name = 'kobe';
obj.age = 40;
obj.height = 1.87;
3、对象增强写法
ES6中,对对象字面量进行了很多增强。
属性初始化简写和方法的简写:
属性初始化
1
2
3
4
5
6
7
8
9
10
11
12
13// ES5的写法
const obj = {
name: name,
age: age,
height: height
}
// ES6的写法
const obj = {
name,
age,
height,
}方法的简写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// ES5的写法
const obj = {
run: function () {
},
eat: function () {
}
}
// ES6的写法
const obj = {
run() {
},
eat() {
}
}
二、Vue基础语法
1、语法糖
指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
语法糖对程序员来说是友好的,但对机器本身却不怎么好。语法糖越甜,编译成的二进制也就越麻烦,出错的时候也会带来更多的麻烦。程序员要做的不是尽力避免错误,而是聚焦在快速发现并改正错误。真正以快速方式轻易解决错误,“快速的失败”远胜过“预防错误”。
Vue中常用的语法糖:
v-bind
::
v-on
:@
v-once
:.once
2、插值语法
1、mustache语法
Mustache(胡子/胡须)是一款「logic-less(轻逻辑)」的前端模板引擎,它原本是基于 javascript 实现的,但是因为轻量易用,所以经过拓展目前支持更多的平台,如 java,.NET,PHP,C++ 等。Mustache 主要用于在表现和数据相分离的前端技术架构中,根据数据生成特定的动态内容,这些内容在网页中指的是HTML结构,而在小程序中则是WXML结构。在前后端分离的技术架构下面,前端模板引擎是一种可以被考虑的技术选型,随着重型框架(AngularJS、ReactJS、Vue)的流行,前端的模板技术已经成为了某种形式上的标配,Mustache 的价值在于其稳定和经典
主页:https://github.com/janl/mustache.js/
文档:https://mustache.github.io/mustache.5.html
项目主页:http://mustache.github.io/
Handlebars:基于 Mustache 的模板引擎:http://handlebarsjs.com/
对于Vue简单来说:"{{}}"(双大括号)不仅仅可以直接写变量,也可以写简单的表达式
更多的Mustache功能参考:https://www.jianshu.com/p/7f1cecdc27e1
我们可以像下面这样来使用,并且数据是响应式的:
![image-20210320030738172](VUE/11.png)
#### 2、v-once
在某些情况下,我们可能不希望界面随意的跟随改变,这个时候,我们就可以使用一个Vue的指令:v-once
v-once:
- 该指令后面不需要跟任何表达式(比如v-for后面是由跟表达式的)
- p该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="app">
<h2>{{message}}</h2>
<h2 v-once>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
v-html:
- 该指令后面往往会跟上一个string类型
- 会将string的html解析出来并且进行渲染
代码:
1 | <div id="app"> |
效果:
4、v-text(不常用)
nv-text作用和Mustache比较相似:都是用于将数据显示在界面中
nv-text
- 通常情况下,接受一个string类型
代码:
1 | <div id="app"> |
效果:
5、v-pre
v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。
比如下面的代码:
- 第一个h2元素中的内容会被编译解析出来对应的内容
- 第二个h2元素中会直接显示
代码:
1 | <div id="app"> |
效果:
6、v-cloak
在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签(加载过慢)。
v-cloak
- 存在期限:在vue解析之前存在,在vue解析之后消失。
- 该指令后面不需要跟任何表达式
cloak:斗篷(起遮挡作用)
1 |
|
效果:
- 在没加v-cloak之前,浏览器先显示,过1s后显示“你好啊”
- 在加了v-cloak之后,浏览器先显示空白,过1s后显示“你好啊”
3、绑定属性(v-bind)
1、v-bind基础
前面的插值指令主要作用是将值插入到我们模板的内容当中。但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。
- 比如动态绑定a元素中网站的链接href
- 比如动态绑定img元素的src属性
- 动态绑定一些类、样式
v-bind指令:
- 作用:绑定一个或多个属性值,或者向另一个组件传递props值
- 缩写:
:
- 预期:any (with argument) | Object (without argument)
- 参数:attrOrProp (optional)
通过Vue实例中的data绑定元素的src和href:
1 | <div id="app"> |
2、v-bind绑定class
很多时候,我们希望动态的来切换class:
- 当数据为某个状态时,字体显示红色。
- 当数据另一个状态时,字体显示黑色。
1、绑定方式:对象语法
对象语法的含义是:class后面跟的是一个对象。
语法:v-bind:class=’{类名: boolean,类名: boolean}’
eg:v-bind:class=”{类名1: true, 类名2: boolean}
对象语法有下面这些用法:
直接通过{}绑定一个类:
1
<h2 :class="{'active': isActive}">Hello World</h2>
通过判断,传入多个值:
1
<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>
和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个class
1
<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
如果过于复杂,可以放在一个methods或者
computed
中注:classes是一个
计算属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<h2 class="title" :class="classes">Hello World</h2>
<script>
const app = new Vue({
el: '#app',
data: {
isActive: true,
isLine: true
},
computed: {
classes: function () {
return {active: this.isActive, line: this.isLine}
}
}
})
2、绑定方式:数组语法
数组语法的含义是:class后面跟的是一个数组。
数组语法有下面这些用法:
直接通过{}绑定一个类:
1
<h2 :class="['active','line']">Hello World</h2>
和普通的类同时存在,并不冲突
注:会有title/active/line三个class
1
<h2 class="title" :class=“[‘active’, 'line']">Hello World</h2>
如果过于复杂,可以放在一个methods或者computed中
注:classes是一个
计算属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div id="app">
<h2 class="title" :class="getClasses()">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
active: 'aaaaaa',
line: 'bbbbbbb'
},
methods: {
getClasses: function () {
return [this.active, this.line]
}
}
})
</script>
3、v-bind绑定style
我们可以利用v-bind:style来绑定一些CSS内联样式。
在写CSS属性名的时候,比如font-size
- 我们可以使用驼峰式 (camelCase) fontSize
- 或短横线分隔 (kebab-case,记得用单引号括起来) ‘font-size’
1、绑定方式:对象语法
style后面跟的是一个对象类型
- 对象的key是
CSS属性名称
- 对象的value是具体赋的值,值可以来自于data中的属性
- 如果过于复杂,可以放在一个methods或者
computed
中
1 |
|
2、绑定方式:数组语法
style后面跟的是一个数组类型
- 多个值以
,
分割即可
1 | <div id="app"> |
4、计算属性(computed)
1、是什么计算属性
在模板中可以直接通过插值语法显示一些data中的数据。但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示:
比如我们有firstName和lastName两个变量,我们需要显示完整的名称:
- undefined undefined
但是如果多个地方都需要显示完整的名称,我们就需要写多个
。代码臃肿
我们可以将上面的代码换成计算属性:写在实例Vue的computed选项中的。
1 | <div id="app"> |
2、计算属性:
解决代码臃肿
可以进行一些更加复杂的操作
计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。
3、计算属性的setter和getter
每个计算属性都包含一个getter和一个setter
在上面的例子中,我们只是使用getter来读取。
在某些情况下,你也可以提供一个setter方法(不常用)。
由于一般我们不希望有人能任意修改我们的计算属性的值,所以一般省略setter方法。而计算属性的getter就能简写成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 简写前(如果有setter方法):
computed: {
fullName: {
get() {
console.log('---调用了fullName的get');
return this.firstName + ' ' + this.lastName
}
},
set(newValue) {
console.log('---调用了fullName的get');
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
// 简写后:
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
},
4、methods与computed
methods和computed看起来都可以实现我们的功能,那么为什么还要多一个计算属性这个东西呢?
原因:计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。
1 | <div id="app"> |
效果:
当使用computed时:由于有缓存,浏览器只执行了一次。
当使用methods时:没有缓存,浏览器执行多次,加重了浏览器的负担。
5、事件监听(v-on)
在前端开发中,我们需要经常和用于交互。
这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
在Vue中如何监听事件呢?使用v-on指令
v-on:
- 作用:绑定事件监听器
- 缩写(语法糖):@
- 预期:Function | Inline Statement | Object
- 参数:event
1、v-on基础
一般v-on后面加上
:
,然后加上动作
如点击(click)、拖拽、键盘事件(keyup/keydown)等等。若v-on监听的事件简单,可以在v-on后面直接实现
1
<button v-on:click="counter++">+</button>
若v-on监听的事件复杂,就需要将事件的实现抽取成一个方法
1
2
3
4
5
6
7<button v-on:click="increment">+</button>
methods: {
increment() {
this.counter++
}
}
2、v-on参数
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
如果该方法不需要额外参数,那么方法后的()可以不添加。
但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去。
1
2
3
4
5
6
7
8
9
10
11<!--1.事件调用的方法没有参数-->
<!--1.1函数后添加()-->
<button @click="btn1Click()">按钮1</button>
<!--1.1函数后不添加()-->
<button @click="btn1Click">按钮1</button>
methods: {
btn1Click() {
console.log("btn1Click");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--2.在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法-->
<!--2.1函数后没添加()-->
<button @click="btn2Click">按钮2</button>
<!--2.2函数需要参数,()里传入参数-->
<!--<button @click="btn2Click(123)">按钮2</button>-->
<!--2.3如果函数需要参数,但是没有传入, 那么函数的形参为undefined-->
<!--<button @click="btn2Click()">按钮2</button>-->
methods: {
btn2Click(event) {
console.log('--------', event);
}
}2.1的效果:
2.3的效果:
如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
1
2
3
4
5
6
7
8
9
10
11
12
13<!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
<!--3.1在调用函数时, 如何手动的获取到浏览器参数的event对象: $event-->
<button @click="btn3Click('abc', $event)">按钮3</button>
<!--3.2在调用函数时,若event没有加$,那么浏览器将默认将event当成一个变量,若event在app实例里没有定义的话,浏览器会找不到该变量而报错并且返回undefined-->
<button @click="btn3Click('abc', event)">按钮3</button>
<!--3.3在调用函数时,若函数没传入参数,那么浏览器将默认将浏览器参数的event放入第一个参数中,又因为第二个参数没有传值,浏览器会将其变为undefined-->
<button @click="btn3Click">按钮3</button>
methods: {
btn3Click(abc, event) {
console.log('++++++++', abc, event);
}
}3.1的效果:
3.2的效果:
3.3的效果:
3、v-on修饰符
在某些情况下,我们拿到event的目的可能是进行一些事件处理。Vue提供了修饰符来帮助我们方便的处理一些事件:
- .stop - 调用 event.stopPropagation()。
- .prevent - 调用 event.preventDefault()。
- .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
- .native - 监听组件根元素的原生事件。
- .once - 只触发一次回调。
更多修饰符参考官网的事件修饰符,以下来自官网。
1 | <!-- 阻止单击事件继续传播 --> |
6、条件判断
1、v-if、v-else-if、v-else
这三个指令与JavaScript的条件语句if、else、else if类似。
Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件。
简单的案例演示:
v-if的原理:
v-if后面的条件为false时,对应的元素以及其子元素不会渲染。
也就是根本没有不会有对应的标签出现在DOM中。
2、一个简单的小案例(用户登陆方式切换)
用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。类似如下情景:
代码:
1 |
|
1、问题
以上案例会有一个小问题:如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
为什么呢?按道理讲,我们在一个input输入的内容(value),在切换到另外一个input元素后应该消失,因为在另一个input元素中,我们并没有输入内容。
2、问题解答
- 这是因为Vue在进行渲染时,不会直接渲染在浏览器上面,Vue会在其之间构建一个虚拟NOM,Vue会先渲染在虚拟DOM上面,然后在渲染在浏览器上。而出于性能考虑,当出现两个只存在一个(if -else)的时候,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
- 在上面的案例中,Vue内部会发现原来(if)的input元素不再使用,直接作为else中的input来使用了。此时并不会重新构建一个input,并且改变的只有与之前input不同的内容(如for、id、placeholder等等),所以文本里面的内容不会改变。
3、解决方案
如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
并且需要我们保证key的值不同。(若key的值相同的话还是会继承文本内容)
1 | <span v-if="isUser"> |
3、v-show
v-show的用法和v-if非常相似,也用于决定一个元素是否渲染
v-if和v-show对比:
- v-if: 当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中
- v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none
v-if和v-show都可以决定一个元素是否渲染,开发中如何选择呢:
- 当需要在显示与隐藏之间切换很频繁时,使用v-show
- 当只有一次切换时,通过使用v-if
7、循环遍历(v-for)
1、v-for遍历数组
当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成
v-for的语法类似于JavaScript中的for循环。
格式如下:item in items的形式。
如果在遍历的过程中不需要使用索引值
1
2
3<ul>
<li v-for="item in names">{{item}}</li>
</ul>如果在遍历的过程中,我们需要拿到元素在数组中的索引值
1
2
3
4
5
6<ul>
<li v-for="(item, index) in names">
// 使遍历从1开始
{{index+1}}.{{item}}
</li>
</ul>
2、v-for遍历对象
当有一对象需要我们对其里面的数据进行渲染时,我们就可以使用v-for来完成
在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<ul>
<li v-for="item in info">{{item}}</li>
</ul>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
})
</script>效果:
- why
- 18
- 1.88
获取key和value 格式: (value, key)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<ul>
<li v-for="(value, key) in info">{{value}}-{{key}}</li>
</ul>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
})
</script>效果:
- why-name
- 18-age
- 1.88-height
获取key和value和index 格式: (value, key, index)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<ul>
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index + 1}}</li>
</ul>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
})
</script>效果:
- why-name-1
- 18-age-2
- 1.88-height-3
3、v-for的组件的key属性
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
为什么需要这个key属性呢?
这个其实和Vue的虚拟DOM的Diff算法有关系
我们借用React’s diff algorithm中的一张图来简单说明一下:
当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
- 我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的
- 即把C更新成F,D更新成C,E更新成D,最后再插入E
这样做会使程序的执行效率变低,所以可以用
key
这个属性来给每个节点做一个唯一标识- Diff算法就可以正确的识别此节点
- 找到正确的位置区插入新的节点
key的作用主要是为了高效的更新虚拟DOM
4、检测数组更新(响应式)
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新:
push():在数组末尾添加一个或多个元素
1
2
3
4
5
6data: {
letters: ['a', 'b', 'c', 'd']
}
this.letters.push('aaa')
this.letters.push('aaaa', 'bbbb', 'cccc')pop():删除数组中的最后一个元素
1
2
3
4
5data: {
letters: ['a', 'b', 'c', 'd']
}
this.letters.pop();shift():删除数组中的第一个元素
1
2
3
4
5data: {
letters: ['a', 'b', 'c', 'd']
}
this.letters.shift();unshift():在数组最前面添加一个或多个元素
1
2
3
4
5
6data: {
letters: ['a', 'b', 'c', 'd']
}
this.letters.unshift()
this.letters.unshift('aaa', 'bbb', 'ccc')splice(start,index,…items):删除元素/插入元素/替换元素
1
2
3
4
5
6
7
8
9
10
11
12
13data: {
letters: ['a', 'd', 'c', 'b']
}
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
this.letters.splice(1, 3)
this.letters.splice(1)
// 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
// 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
this.letters.splice(1, 0, 'x', 'y', 'z')
sort():对数组进行排序。(参数可以添加排序的规则的方法)
1
2
3
4
5data: {
letters: ['a', 'd', 'c', 'b']
}
this.letters.sort()reverse():
1
2
3
4
5data: {
letters: ['a', 'd', 'c', 'b']
}
this.letters.reverse()
注意:通过索引值
修改数组中的元素不能做到响应式
1 | data: { |
此时可以通过splice方法或Vue的set方法的方式来修改以达到响应式的目的
1 | // splice方法 |
5、作业:(v-for + v-bind + v-on + 当前索引方法的应用)
需求:有一电影列表,点击哪一部影片,哪一部影片就表现为红色。
代码:
1 |
|
6、高阶函数filter|map|reduce
1、filter:过滤作用
filter函数的参数是一个回调函数,返回值为一个数组:
- 回调函数的参数为循环遍历的值n
- 回调函数有一个要求: 必须返回一个boolean值
- true: 当返回true时, 函数内部会自动将这次回调的n加入到新的数组中
- false:当返回false时, 函数内部会过滤掉这次的n
使用:
1 | const nums = [10, 20, 111, 222, 444, 40, 50] |
2、map:映射作用
map函数的参数是一个回调函数,返回值为一个数组:
- 回调函数的参数为循环遍历的值n
- 可以在回调函数内对数组的值进行操作,map会帮操作完的值映射到一个新的数组
使用:
1 | // newNums = [10,20,40,50] |
3、reduce:作用对数组中所有的内容进行汇总
map函数的参数是一个回调函数,和一个初始值
- 回调函数有两个参数(previousValue,start)
- previousValue:数组当前值的前一个值
- start:数组当前值
- 初始值为数组一开始值(第一个元素,index=0)的前一个值
1 | // new2Nums = [20,40,80,100] |
4、总结
需求:筛选出数组nums里所有小于100的值,然后就值乘以2再相加。
1 | const nums = [10, 20, 111, 222, 444, 40, 50] |
以上三个高阶函数的回调函数都可以用箭头函数表示。
1 | let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n) |
8、表单绑定(v-mode)
1、v-mode基础
表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
Vue中使用v-model指令来实现表单元素和数据的双向绑定。
案例:
1 | <div id="app"> |
案例的解析:
当我们在输入框输入内容时,因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。所以,通过v-model实现了双向的绑定。
当然,我们也可以将v-model用于textarea元素。
2、v-mode的原理
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件
1 | <input type="text" v-model="message"> |
3、v-mode:radio
当存在多个单选框时,v-mode可用于将单选框的值和与之对应变量进行双向绑定。
其中一个label与一个input组合,label里面的for与input里面的id一一对应,实现用户点击文字就可以选中对应的单选框。
1 | <div id="app"> |
4、v-mode:checkbox
checkbox复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
v-model即为布尔值
此时input的value并不影响v-model的值
常用于让用户点击
同意协议
后才能点击下一步
的业务场景1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<div id="app">
<label for="licence">-->
<input type="checkbox" id="licence" v-model="isAgree">同意协议
</label>
<h2>您选择的是: {{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isAgree: false,
}
})
</script>
多个复选框
当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组
当选中某一个时,就会将input的value添加到数组中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<div id="app">
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<h2>您的爱好是: {{hobbies}}</h2>
<!--label中的:for与input的:id对应-->
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
hobbies: [],
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}
})
</script>
5、v-mode:select
select也分单选和多选两种情况:
单选:只能选中一个值:
v-model绑定的是一个值
当我们选中option中的一个时,会将它对应的value赋值到mySelect中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<div id="app">
<select name="abc" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>您选择的水果是: {{fruit}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 默认香蕉
fruit: '香蕉'
}
})
</script>
多选:可以选中多个值(属性加上multiple):
v-model绑定的是一个数组
当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div id="app">
<select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>您选择的水果是: {{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
fruits: []
}
})
</script>
6、值绑定
动态的给value赋值而已。我们前面的value中的值,都是在定义input的时候直接给定的(写死),但是真实开发中,这些input的值可能是从网络获取或定义在data中的。所以我们可以通过v-bind:value动态的给value绑定值(其实就是v-bind在input中的应用)
7、修饰符
1、lazy修饰符
默认情况下,v-model默认是在input事件中同步输入框的数据的。也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
lazy修饰符可以让数据在失去焦点或者回车时才会更新。
1 | <div id="app"> |
效果:
- 聚焦时:
- 失焦时:
2、number修饰符
默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
number修饰符可以让在输入框中输入的内容自动转成数字类型
1 | <div id="app"> |
效果:
没加number:
加上number:
3、trim修饰符
如果输入的内容首尾有很多空格,通常我们希望将其去除,trim修饰符可以过滤内容左右两边的空格(浏览器会格式化显示时帮忙去掉多余的空格,但在代码里空格依旧存在,trim修饰符可以过滤内容左右两边的空格)。
1 | <div id="app"> |
效果:
没加trim:
加上trim
三、组件化开发
1、什么是组件化
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
如图:
- 我们将一个完整的页面分成很多个组件。
- 每个组件都用于实现页面的一个功能块。
- 而每一个组件又可以进行细分。
2、Vue组件化思想
组件化是Vue.js中的重要思想。它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。
组件化思想的应用:
- 有了组件化的思想,我们在之后的开发中就要充分的利用它。
- 尽可能的将页面拆分成一个个小的、可复用的组件。
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强。
注意:每一个组件都有独属于自己的data、methods
、computed
、components
、template
等等。其中app
可以看成所有组件的根组件(root)。但要注意,app也只能调用自己的儿子
组件,不能去跨辈调用孙子
组件。
3、注册组件
组件的使用分成三个步骤:
- 创建组件构造器
- 注册组件
- 使用组件:组件只能在注册过的实例里使用,否则Vue因没有进行管理不会加载组件。
注意:字符串的表达除了有''
(单引号)、""
(双引号)以外,还有``
(尖引号)。尖引号可以实现字符串的跨行。
1 | <div id="app"> |
注册组件步骤解析
Vue.extend():
- 调用Vue.extend()创建的是一个组件构造器;
- 通常在创建组件构造器时,传入template代表我们自定义组件的模板;
- 该模板就是在使用到组件的地方,要显示的HTML代码;
- 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础
Vue.component():
- 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称;
- 所以需要传递两个参数:
- 注册组件的标签名
- 组件构造器
组件必须挂载在某个Vue实例下,否则它不会生效:
我们来看下面我使用了三次
而第三次其实并没有生效:
4、全局组件和局部组件
当我们通过调用Vue.component()注册组件时,组件的注册是全局的。
这意味着该组件可以在任意Vue示例下使用。
如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
5、父组件和子组件
在前面我们看到了组件树:组件和组件之间存在层级关系。而其中一种非常重要的关系就是父子组件的关系
我们来看通过代码如何组成的这种层级关系:
父子组件错误用法:以子标签的形式在Vue实例中使用
- 因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
- 该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)
是只能在父组件中被识别的。 - 类似这种用法,
是会被浏览器忽略的。
6、注册组件语法糖
在上面注册组件的方式,可能会有些繁琐。Vue为了简化这个过程,提供了注册的语法糖。
主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
语法糖注册全局组件和局部组件:
全局组件的语法糖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<div id="app">
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1', {
template: `
`
})
const app = new Vue({
el: '#app'
}
})
</script>局部组件的语法糖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 注册局部组件的语法糖
const app = new Vue({
el: '#app',
components: {
'cpn2': {
template: `
`
}
}
})
</script>
7、模板的分离写法
以上代码虽然通过语法糖简化了Vue组件的注册过程,但还有一个地方的写法比较麻烦,就是template模块写法。如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。
Vue提供了两种方案来定义HTML模块内容:
使用