_little-star_

学习的博客

0%

Vue

Vue

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的源码。应该右键选中从链接另存文件

image-20210320014752045

其中

  • 开发环境用在开发的时候,其中的代码包含了有帮助的命令行警告,方便程序员查看源代码,但相对的文件比较大。

  • 生产环境用在发布产品的时候,其中的代码都是经过压缩的,优化了尺寸和速度,文件也比较小,方便用户下载,但代码的可读性极差。

一句话总结:开发环境面向的是程序员,生产环境面向的是用户。

2、方式二:直接CDN引入

你可以在你的项目中直接CDN(外部)引入:

1
2
3
4
5
6
7
8
9
10
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

<!--如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件-->
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'
</script>

3、方式三:NPM安装

在用 Vue 构建大型应用时推荐使用 NPM 安装[1]。NPM 能很好地和诸如 webpackBrowserify 模块打包器配合使用。同时 Vue 也提供配套工具来开发单文件组件

1
2
# 最新稳定版
$ npm install vue

3、第一个Vuejs程序

image-20210320015424885

1、代码的执行

  1. 阅读JavaScript代码,程序发现创建了一个Vue对象;
  2. 创建Vue对象的时候,传入了一些options:{}
    • {}中包含了el属性:该属性决定了这个Vue对象挂载到哪一个元素上,很明显,我们这里是挂载到了id为app的元素上;
    • {}中包含了data属性:该属性中通常会存储一些数据:
      • 这些数据可以是我们直接定义出来的,比如像上面代码这样
      • 也可能是来自网络,从服务器加载的

2、浏览器执行代码的流程

  1. 执行到10~13行代码显然出对应的HTML;
  2. 执行第16行代码创建Vue实例,并且对原HTML进行解析和修改

3、响应式

Vue代码是可以实现响应式的。在浏览器里进入开发者模式F12中的console。在里面修改代码可以实现浏览器的内容也随着修改而响应着改变。

image-20210320020223661

4、Vue与JavaScript (两种编程范式)

  • 命令式编程(JavaScript )

    命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。

    优点:数据和界面完全分离,不需要js创建页面元素等操作

  • 声明式编程(Vuejs)

    声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。

    优点:当数据发生改变时界面自动发生改变(响应式)

5、Vue的MVVM

1、是什么MVVM

维基百科官方解释

MVVMModel–view–viewmodel)是一种软件架构模式

MVVM有助于将图形用户界面的开发与业务逻辑后端逻辑(数据模型)的开发分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,[1] 这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。[1] 视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。

image-20210402232753389

2、Vue的MVVM

image-20210320022023488

  • 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中定义的方法)

image-20210320022244195

image-20210320022417139

计数器中就有严格的MVVM思想:

  • View依然是我们的DOM
  • Model就是我们我们抽离出来的obj
  • ViewModel就是我们创建的Vue对象实例

01-计数器的MVVM

它们之间如何工作呢?

  1. 首先ViewModel通过Data Binding让obj中的数据实时的在DOM中显示。
  2. 其次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.$nextTick

    • beforeUpdate:

      类型:Function

      详细:

      数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

      该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。

    • updated:

      类型:Function

      详细:

      由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

      当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性watcher 取而代之。

      注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick

    • activated

      类型: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的生命周期

以下图来自官网

Vue 实例生命周期

image-20210320025609038

简化:

image-20210320025717195

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
2
3
4
5
6
var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}

效果:无论点击哪个按钮,日志打印的都是第5个按钮被点击

image-20210320220031829

说明:由于var没有块级作用域,被var定义的i会随着i++的改变而改变。function里面的i受到for循环的i++的影响,被改变成了5,所以输出的都是第5个按钮被点击

3、解决方法:
  1. 用闭包可以解决问题。

    1
    2
    3
    4
    5
    6
    7
    8
    var btns = document.getElementsByTagName('button');
    for (var i=0; i<btns.length; i++) {
    (function (num) { // 0
    btns[i].addEventListener('click', function () {
    console.log('第' + num + '个按钮被点击');
    })
    })(i)
    }

    为什么闭包可以解决问题:函数是一个作用域。

  2. 用ES6的let

    1
    2
    3
    4
    5
    6
    const 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
    2
    const name = 'why';
    name = 'abc';
  • 在使用const定义标识符,必须进行赋值

    1
    const name;
  • 常量的含义是指向的对象不能修改, 但是可以改变对象内部的属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const 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>
效果: ![image-20210320032116662](VUE/12.png) #### 3、v-html 某些情况下,我们从服务器请求到的数据本身就是一个HTML代码。如果我们直接通过"{{}}"来输出,会将HTML代码也一起输出。但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。这个时候,我们就可以使用一个Vue的指令:v-html

v-html:

  • 该指令后面往往会跟上一个string类型
  • 会将string的html解析出来并且进行渲染

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<h2>{{url}}</h2>
<h2 v-html="url"></h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
url: '<a href="http://www.baidu.com">百度一下</a>'
}
})
</script>

效果:

image-202103200324301694、v-text(不常用)

nv-text作用和Mustache比较相似:都是用于将数据显示在界面中

nv-text

  • 通常情况下,接受一个string类型

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<h2>{{message}}, 李银河!</h2>
<h2 v-text="message">, 李银河!</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>

效果:

image-20210320032749380

5、v-pre

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。

比如下面的代码:

  • 第一个h2元素中的内容会被编译解析出来对应的内容
  • 第二个h2元素中会直接显示

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<h2>{{message}}</h2>
<h2 v-pre>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello World'
}
})
</script>

效果:

image-20210320032946332

6、v-cloak

在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签(加载过慢)。

v-cloak

  • 存在期限:在vue解析之前存在,在vue解析之后消失。
  • 该指令后面不需要跟任何表达式

cloak:斗篷(起遮挡作用)

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">
<title>Title</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>

<div id="app" v-cloak>
<h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
// 在vue解析之前, div中有一个属性v-cloak
// 在vue解析之后, div中没有一个属性v-cloak
setTimeout(function () {
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
}, 1000)
</script>

</body>
</html>

效果:

  • 在没加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
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">
<!-- 错误的做法: 这里不可以使用mustache语法-->
<!--<img src="{{imgURL}}" alt="">-->
<!-- 正确的做法: 使用v-bind指令 -->
<img v-bind:src="imgURL" alt="">
<a v-bind:href="aHref">百度一下</a>
<!--<h2>{{}}</h2>-->

<!--语法糖的写法-->
<img :src="imgURL" alt="">
<a :href="aHref">百度一下</a>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
imgURL: 'https://vuejs.org/images/log.png',
aHref: 'https://vuejs.org'
}
})
</script>

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
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.title {
font-size: 50px;
color: red;
}
</style>
</head>
<body>

<div id="app">
<!--<h2 :style="{key(属性名): value(属性值)}">{{message}}</h2>-->

<!--'50px'必须加上单引号, 否则是当做一个变量去解析-->
<h2 :style="{fontSize: '50px'}">{{message}}</h2>

<!--finalSize当成一个变量使用-->
<!--<h2 :style="{fontSize: finalSize}">{{message}}</h2>-->
<h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2>
<h2 :style="getStyles()">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
finalSize: 100,
finalColor: 'red',
},
methods: {
getStyles: function () {
return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
}
}
})
</script>

</body>
</html>
2、绑定方式:数组语法

style后面跟的是一个数组类型

  • 多个值以,分割即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<h2 :style="[baseStyle, baseStyle1]">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
baseStyle: {backgroundColor: 'red'},
baseStyle1: {fontSize: '100px'},
}
})
</script>

4、计算属性(computed)

1、是什么计算属性

在模板中可以直接通过插值语法显示一些data中的数据。但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示:

  • 比如我们有firstName和lastName两个变量,我们需要显示完整的名称:

    • undefined undefined
  • 但是如果多个地方都需要显示完整的名称,我们就需要写多个

    。代码臃肿

我们可以将上面的代码换成计算属性:写在实例Vue的computed选项中的。

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
<div id="app">
<h2>{{firstName + ' ' + lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>

<h2>{{getFullName()}}</h2>

<h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Lebron',
lastName: 'James'
},
// computed: 计算属性()
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>

2、计算属性:

  • 解决代码臃肿

  • 可以进行一些更加复杂的操作

    image-20210320150614370

  • 计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。

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
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
<div id="app">
<!--1.直接拼接: 语法过于繁琐-->
<h2>{{firstName}} {{lastName}}</h2>

<!--2.通过定义methods-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->

<!--3.通过computed-->
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
// angular -> google
// TypeScript(microsoft) -> ts(类型检测)
// flow(facebook) ->
const app = new Vue({
el: '#app',
data: {
firstName: 'Kobe',
lastName: 'Bryant'
},
methods: {
getFullName: function () {
console.log('getFullName');
return this.firstName + ' ' + this.lastName
}
},
computed: {
fullName: function () {
console.log('fullName');
return this.firstName + ' ' + this.lastName
}
}
})

</script>

效果:

  • 当使用computed时:由于有缓存,浏览器只执行了一次。

    image-20210320153300912

  • 当使用methods时:没有缓存,浏览器执行多次,加重了浏览器的负担。

    image-20210320153616146

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的效果:

    image-20210320232959026

    2.3的效果:

    image-20210320233429354

  • 如果需要同时传入某个参数,同时需要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的效果:

    image-20210320234009069

    3.2的效果:

    image-20210320235528249

    3.3的效果:

    image-20210320235715797

3、v-on修饰符

在某些情况下,我们拿到event的目的可能是进行一些事件处理。Vue提供了修饰符来帮助我们方便的处理一些事件:

  • .stop - 调用 event.stopPropagation()。
  • .prevent - 调用 event.preventDefault()。
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件。
  • .once - 只触发一次回调。

image-20210321000423484

更多修饰符参考官网的事件修饰符,以下来自官网。

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
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

<input v-on:keyup.page-down="onPageDown">

6、条件判断

1、v-if、v-else-if、v-else

  • 这三个指令与JavaScript的条件语句if、else、else if类似。

  • Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件。

简单的案例演示:

image-20210321004504280

image-20210321005848254

v-if的原理:

  • v-if后面的条件为false时,对应的元素以及其子元素不会渲染。

  • 也就是根本没有不会有对应的标签出现在DOM中。

2、一个简单的小案例(用户登陆方式切换)

用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。类似如下情景:

image-20210321013857218

代码:

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">
<title>Title</title>
</head>
<body>

<div id="app">
<span v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="用户账号">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱">
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isUser: true
}
})
</script>

</body>
</html>
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
2
3
4
5
6
7
8
<span v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="用户账号" key="username">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱" key="email">
</span>

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中的一张图来简单说明一下:

    image-20210321153710582

  • 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点

    • 我们希望可以在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
    6
    data: {
    letters: ['a', 'b', 'c', 'd']
    }

    this.letters.push('aaa')
    this.letters.push('aaaa', 'bbbb', 'cccc')
  • pop():删除数组中的最后一个元素

    1
    2
    3
    4
    5
    data: {
    letters: ['a', 'b', 'c', 'd']
    }

    this.letters.pop();
  • shift():删除数组中的第一个元素

    1
    2
    3
    4
    5
    data: {
    letters: ['a', 'b', 'c', 'd']
    }

    this.letters.shift();
  • unshift():在数组最前面添加一个或多个元素

    1
    2
    3
    4
    5
    6
    data: {
    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
    13
    data: {
    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
    5
    data: {
    letters: ['a', 'd', 'c', 'b']
    }

    this.letters.sort()
  • reverse():

    1
    2
    3
    4
    5
    data: {
    letters: ['a', 'd', 'c', 'b']
    }

    this.letters.reverse()

注意:通过索引值修改数组中的元素不能做到响应式

1
2
3
4
5
data: {
letters: ['a', 'd', 'c', 'b']
}

this.letters[0] = 'bbbbbb';

此时可以通过splice方法或Vue的set方法的方式来修改以达到响应式的目的

1
2
3
4
5
// splice方法
this.letters.splice(0, 1, 'bbbbbb')

// Vue的set(要修改的对象, 索引值, 修改后的值)
Vue.set(this.letters, 0, 'bbbbbb')

5、作业:(v-for + 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">
<title>homework</title>
</head>
<style>
.active{
color: red;
}
</style>
<body>

<div id = "app">
<ul>
<li v-for="(movie,index) in movies"
:class="{active: currentIndex === index}"
@click="isClick(index)">
{{index}}.{{movie}}
</li>
</ul>
</div>
<script src="../js/vue.js"></script>

<script>

const app = new Vue({
el: '#app',
data: {
movies: ['海贼王','火影忍者','进击的巨人','妖精的尾巴'],
currentIndex: -1
},
methods: {
isClick: function (index) {
this.currentIndex = index
}
}
})

</script>

</body>
</html>

6、高阶函数filter|map|reduce

1、filter:过滤作用

filter函数的参数是一个回调函数,返回值为一个数组:

  • 回调函数的参数为循环遍历的值n
  • 回调函数有一个要求: 必须返回一个boolean值
    • true: 当返回true时, 函数内部会自动将这次回调的n加入到新的数组中
    • false:当返回false时, 函数内部会过滤掉这次的n

使用:

1
2
3
4
5
6
const nums = [10, 20, 111, 222, 444, 40, 50]

// newNums = [10,20,40,50]
let newNums = nums.filter(function (n) {
return n < 100
})
2、map:映射作用

map函数的参数是一个回调函数,返回值为一个数组:

  • 回调函数的参数为循环遍历的值n
  • 可以在回调函数内对数组的值进行操作,map会帮操作完的值映射到一个新的数组

使用:

1
2
3
4
5
// newNums = [10,20,40,50]
// new2Nums = [20,40,80,100]
let new2Nums = newNums.map(function (n) { // 20
return n * 2
})
3、reduce:作用对数组中所有的内容进行汇总

map函数的参数是一个回调函数,和一个初始值

  • 回调函数有两个参数(previousValue,start)
    • previousValue:数组当前值的前一个值
    • start:数组当前值
  • 初始值为数组一开始值(第一个元素,index=0)的前一个值
1
2
3
4
5
// new2Nums = [20,40,80,100]
// total = 240
let total = new2Nums.reduce(function (preValue, n) {
return preValue + n
}, 0)
4、总结

需求:筛选出数组nums里所有小于100的值,然后就值乘以2再相加。

1
2
3
4
5
6
7
8
9
const nums = [10, 20, 111, 222, 444, 40, 50]

let total = nums.filter(function (n) {
return n < 100
}).map(function (n) {
return n * 2
}).reduce(function (prevValue, n) {
return prevValue + n
}, 0)

以上三个高阶函数的回调函数都可以用箭头函数表示。

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
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
}
})
</script>

image-20210321210459660

案例的解析:

当我们在输入框输入内容时,因为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
2
3
4
5
6
7
8
9
10
<input type="text" v-model="message">
<!--等同与-->
<input type="text" :value="message" @input="valueChange">
methods: {
valueChange(event) {
this.message = event.target.value;
}
}
<!--也等同与-->
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">

3、v-mode:radio

当存在多个单选框时,v-mode可用于将单选框的值和与之对应变量进行双向绑定。

其中一个label与一个input组合,label里面的for与input里面的id一一对应,实现用户点击文字就可以选中对应的单选框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">
</label>
<h2>您选择的性别是: {{sex}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sex: '女'
}
})
</script>

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
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
}
})
</script>

效果:

  • 聚焦时:

image-20210321214116893

  • 失焦时:

image-20210321214156460

2、number修饰符

默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。

number修饰符可以让在输入框中输入的内容自动转成数字类型

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<input type="number" v-model.number="age">
<h2>{{age}}-{{typeof age}}</h2>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
age: 18
}
})
</script>

效果:

  • 没加number:

    image-20210321214845631

  • 加上number:

    image-20210321214942404

3、trim修饰符

如果输入的内容首尾有很多空格,通常我们希望将其去除,trim修饰符可以过滤内容左右两边的空格(浏览器会格式化显示时帮忙去掉多余的空格,但在代码里空格依旧存在,trim修饰符可以过滤内容左右两边的空格)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<input type="text" v-model.trim="name">
<h2>您输入的名字:{{name}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: ''
}
})
</script>

效果:

  • 没加trim:

    image-20210321215801147

  • 加上trim

    image-20210321215539094

三、组件化开发

1、什么是组件化

如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

如图:

  • 我们将一个完整的页面分成很多个组件。
  • 每个组件都用于实现页面的一个功能块。
  • 而每一个组件又可以进行细分。

image-20210321224044742

2、Vue组件化思想

组件化是Vue.js中的重要思想。它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。

image-20210321224237074

组件化思想的应用:

  • 有了组件化的思想,我们在之后的开发中就要充分的利用它。
  • 尽可能的将页面拆分成一个个小的、可复用的组件。
  • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

注意:每一个组件都有独属于自己的data、methodscomputedcomponentstemplate等等。其中app可以看成所有组件的根组件(root)。但要注意,app也只能调用自己的儿子组件,不能去跨辈调用孙子组件。

3、注册组件

组件的使用分成三个步骤:

  1. 创建组件构造器
  2. 注册组件
  3. 使用组件:组件只能在注册过的实例里使用,否则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
35
36
37
38
39
<div id="app">
<!--3.使用组件-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>

<div>
<div>
<my-cpn></my-cpn>
</div>
</div>
</div>

<!--以下没有在app实例里使用,Vue没有进行管理不会加载组件-->
<my-cpn></my-cpn>

<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>组件标题</h2>
<p>我是组件中的一个段落内容</p>
</div>`
})

// 2.注册组件
// 参数1:组件的名称,参数2:组件构造器对象的名称
Vue.component('my-cpn', cpnC)

const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>

注册组件步骤解析

  1. Vue.extend():

    1. 调用Vue.extend()创建的是一个组件构造器;
    2. 通常在创建组件构造器时,传入template代表我们自定义组件的模板;
    3. 该模板就是在使用到组件的地方,要显示的HTML代码;
    4. 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础
  2. Vue.component():

    1. 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称;
    2. 所以需要传递两个参数:
      1. 注册组件的标签名
      2. 组件构造器
  3. 组件必须挂载在某个Vue实例下,否则它不会生效:

    1. 我们来看下面我使用了三次

    2. 而第三次其实并没有生效:

      image-20210322023308247

4、全局组件和局部组件

当我们通过调用Vue.component()注册组件时,组件的注册是全局的

这意味着该组件可以在任意Vue示例下使用。

image-20210322023704676

如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

image-20210322023725407

5、父组件和子组件

在前面我们看到了组件树:组件和组件之间存在层级关系。而其中一种非常重要的关系就是父子组件的关系

我们来看通过代码如何组成的这种层级关系:

image-20210322024427807

父子组件错误用法:以子标签的形式在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: `
    <div>
    <h2>我是标题1</h2>
    <p>我是内容, 哈哈哈哈</p>
    </div>
    `
    })

    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: `
    <div>
    <h2>我是标题2</h2>
    <p>我是内容, 呵呵呵</p>
    </div>
    `
    }
    }
    })
    </script>

7、模板的分离写法

以上代码虽然通过语法糖简化了Vue组件的注册过程,但还有一个地方的写法比较麻烦,就是template模块写法。如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。

Vue提供了两种方案来定义HTML模块内容:

  • 使用