Vue2基础速通

Vue基础速通

前言

  欢迎来到知识汲取者的个人博客!在这篇文章中,我将为你介绍Vue.js的基础知识,帮助你快速入门并掌握Vue开发的基本技巧。Vue.js是一个流行的JavaScript框架,被广泛用于构建现代化、交互式的Web应用程序。它采用了MVVM(Model-View-ViewModel)架构模式,通过数据驱动视图的方式实现了高效的前端开发。在这篇文章中,我们将从Vue.js的核心概念开始,包括Vue实例、组件、指令和响应式数据等。我会详细解释每个概念的作用和用法,并通过实际的代码示例帮助你理解和运用这些概念。如果你是初学者,相信通过本文的学习,一定可以让你熟练掌握Vue的基础语法,理解Vue中的一些常见概念。
PS:对于文章一些描述不当、存在错误的地方,还请大家指出,笔者不胜感激

相关推荐

  • Vue2官方文档
  • Vue3官方文档

1、Vue概述

  • Vue是什么

    Vue (发音为 /vjuː/,类似 view) 是一款基于MVVM思想用于构建用户界面的渐进式 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。

    Vue官网:

    • Vue2:Vue.js (vuejs.org)
    • Vue3:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

    Vue的亲爹:尤雨溪

    image-20221014213323236

    没想到吧😃,Vue亲爹是如此的年轻帅气,和我们以往对于哪些某一领域的开创者的印象是很不一样的,不像其他的大部分创始人都是大胡子老头,这个Vue的创始人看着好亲切(没错它是一个华人),而且它本科是计算机,硕士读的是美术设计(没想到吧)!难怪Vue是如此的优美,堪称一件精致的艺术品😆,同样的Vue也如同它本人一样,也是如此的年轻

    题外话:他之前是任职于Google,之后又加盟了阿里,现在是全职维护Vue,它的目标是让Vue成为最强的JavaScripted框架

  • 什么是MVVM

    MVVM(Model-View-ViewModel)是一种架构模式,是MVC模式的改进版。MVVM实现了数据的双向绑定1,解决了MVC模式只能实现模型到视图的单向展示。

    • Mode:数据模型,泛指后端进行的各种业务逻辑处理和数据操控
    • View:视图,是用户界面(可以理解为平时浏览的网页),用于展示数据
    • ViewModel:视图模型,是由前端开发人员组织生成和维护的视图数据层,用于Model和View的数据交换。在这一层,前端开发者从后端获取得到Model数据进行转换出来,做二次封装,以生成符合View层使用预期的视图数据模型

    image-20220812082254644

    个人理解:其实MVVM就是在MVC的MV之间添加了一个中间层VM,VM实时监听DOM,一旦网页中的数据发生了改变,VM就会立马反馈给Model,然后Model也发生改变。这让我想起来一个大佬说过的一句话”计算机中任何问题,都可以通过添加一个中间层来进行解决“

  • 什么是渐进式

    渐进式就是可以自底向上逐层地应用,即:既可以简单应用(只需要引入一个简单的核心库),又可以复杂应用(引入各种各样的Vue插件),也就是说Vue的应用是具有层次的

  • Vue的特点

    • 轻量灵活易上手。Vue只注重视图层,核心库只有5Kb,提供 MVVM 数据绑定和一个可组合的组件系统,具有简单、灵活的 API,使开发者更加容易理解,能够更快上手
    • 组件化。Vue采用组件化模式2进行编码,提高代码的复用率,且代码具有更好的可维护性
    • 声明式。Vue采用声明式编码3,让开发人员无需直接操作DOM,提高开发效率
    • 响应性。Vue会自动跟踪 JavaScript 状态变化并在改变发生时响应式地更新 DOM,并且面对数据的动态变化时,Vue使用了虚拟DOM+Diff,尽可能复用DOM结点
  • Vue和其它JS框架的关系:

    • 借鉴Angular的模板数据绑定技术
    • 借鉴React的组件化虚拟DOM技术
  • Vue周边库:

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

  • Vue的发展历程

    image-20221014214659564

    详情请参考:Vue.js的发展历程

2、快速入门

下载并引入Vue.js
关闭警告提示
编写Vue代码
  • Step1:下载并引入Vue.js

    image-20221015101946156

    • vue.js:是开发版。它包含完整的警告和调试模式
    • vue.min.js:是生产版。它删除了警告,是vue.js的压缩版,大小只有37.36KB

    具体下载可以去官网

    image-20221015103722213

  • Step2:关闭提示

    温馨提示:这一步并不是必须的

    1)Google安装Vue插件:解决上面①的警告,同时让Vue的开发更加丝滑

    2)配置productionTip属性:productionTip是Vue的全局属性之一,用于判断是否需要进行警告提示,它在Vue2中是默认取值true,开启提示,所以需要将他设置成false(在Vue3中已经默认取值为false了)

    image-20221015104611184

    3)设置网页Log:

    image-20221015124614200


    image-20221015124744062

    注意:如果favicon.ico图片不在项目的根目录下,则Liver Server无法识别,这时候log无法显示,此时如果任然想要log效果,可以通过<link rel="shortcut icon" href="" type="image/x-icon">标签进行引入

  • Step3:编写Vue代码

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>初识Vue</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><!-- Step0: 创建容器 --><div id="root"><h1>Hello,{{name}}!</h1></div><script>//Step1: 设置Vue全局配置,并创建Vue对象Vue.config.productionTip = false; //关闭Vue的警告提示new Vue({//Step1: 让容器和Vue建立联系。使用el指定当前Vue示例为哪个容器服务,值通常是CSS选择器字符串/*写法一:你找到容器给Vue对象用el: document.getElementById('root')*///写法二:让Vue对象自己去找容器,并使用(推荐使用)el: '#root',//Step2: 进行数据绑定。data中存储的数据提供给建立了联系的容器进行使用(暂时写成对象的形式)data: {name: 'Vue'}});</script>
    </body></html>
    

    image-20221015135900184

    总结:

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

    注意区分js表达式js代码(语句)

    • js表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:

      • a
      • a+b
      • f()
      • x === y ? ‘a’ : ‘b’
    • js代码(语句)

      • if(){}
      • for(){}

      js表达式是js代码的子集,它属于特殊的js代码

3、模板语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上

Vue的模板语法分为插值语法指令语法

  • 插值语法{{JS表达式}}

    作用:用于解析标签体内容

        <div id="root"><h1>hello,{{name}}</h1></div><!-- 引入开发板版Vue.js --><script src="./js/vue.js"></script><script>new Vue({el: '#root',data: {name: 'Vue'}});</script>
    
  • 指令语法v-xxx:

    作用:用于解析标签(解析标签属性、解析标签体内容、绑定事件……)

        <div id="root"><a v-bind:href="url">点击跳转至百度1</a><!-- 简写方式(不是所有的指令都能简写) --><a :href="url">点击跳转至百度2</a></div><!-- 引入开发板版Vue.js --><script src="./js/vue.js"></script><script>new Vue({el: '#root',data: {url: 'http://www.baidu.com'}});</script>
    

    推荐阅读:

    • v-bind详细用法

    • Vue指令大全

总结

Vue模板语法有2大类:
1.插值语法
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,
且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是:v-???,此处我们只是拿v-bind举个例子。

4、数据绑定

示例代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><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="root">单向绑定数据: <input type="text" v-bind:value="value1"> <br> 双向绑定数据: <input type="text" v-model:value="value2"><!--v:model的简写形式:<input type="text" v-model="value2"> --></div><script>new Vue({el: '#root',data: {value1: '单向数据绑定',value2: '双向数据绑定'}});</script>
</body></html>
  • 单向数据绑定:改变data中的数据会影响页面中的数据,反之不会

    v-bind就是单向数据绑定,示例如下:

    image-20221015145855109

    image-20221015145912192

  • 双向数据绑定:改变data中的数据会影响页面中的数据,同时如果改变页面中的数据也会影响data中的数据

    v-model就是双向数据绑定,示例如下:

    image-20221015150057177

    注意v-model只能用于表单类(能进行输入,拥有value值)标签上,比如:input、复选框等,而h1~6、p等标签使用就会直接报错!

总结

Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。

5、el和data的两种写法

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>el和data的两种写法</title><!-- 引入开发板版Vue.js --><script src="./js/vue.js"></script>
</head><body><div id="root"><h1>Hello,{{name}}!</h1></div><script>const vue = new Vue({/*el的第一种写法:el: '#root',*//*data的第一种写法:对象式data: {name: 'Vue'}*///data的第二种写法:函数式/*data: function() {//注意这里一定要使用普通函数进行声明,不能使用箭头函数return {name: 'Vue'}}*///简写:data() {console.log(this); //这个函数是由Vue对象进行调用的return {name: 'Vue'}}});//el的第二种写法:通过Vue的对象内置的mount函数关联容器(这种方式十分灵活)setTimeout(() => {vue.$mount('#root');}, 1000);</script>
</body></html>

总结

data与el的2种写法
1.el有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。
2.data有2种写法
(1).对象式
(2).函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
3.一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。

拓展:MVVM

MVVM模型
1. M:模型(Model) :data中的数据
2. V:视图(View) :模板代码
3. VM:视图模型(ViewModel):Vue实例

观察发现:

  1. data中所有的属性,最后都出现在了vm身上。
  2. 2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。

推荐阅读:箭头函数与普通函数的区别详解

6、数据代理

所谓数据代理(也叫数据劫持),通过一个对象代理对另一个对象属性的操作。指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。比较典型的是 Object.defineProperty() 和 ES2015 中新增的 Proxy 对象。另外还有已经被废弃的 Object.observe(),他被废弃的原因就是由于Proxy的出现

PS:这个思想类似设计模式中的代理模式,而Vue中的双向数据绑定就是用到了数据代理,这和Spring中AOP使用到动态代理是一致的,都是一种代理

示例

问题描述:通过对num操作来改变Person对象中的age属性

当使用Object的defineProperty方法对Person的age属性进行数据代理时,age属性默认将不会被枚举、修改、删除

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>数据代理</title>
</head><body><script>let number = 18;let person = {name: '张三',gender: '男',};Object.defineProperty(person, 'age', {// value: 18,// enumerable:true, //控制属性是否可以枚举,默认值是false// writable: true, //控制属性是否可以被修改,默认值是false// configurable:true //控制属性是否可以被删除,默认值是false//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值get() {console.log('有人读取age属性了')return number},//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值set(value) {console.log('有人修改了age属性,且值是', value)number = value}});console.log(person);//遍历person对象所有的属性名console.log(Object.keys(person));</script>
</body></html>

备注:当我这样写getter和setter时,writableconfigurable属性无效了,因为当我们此时修改age,他会执行number = value,value没有修改成功,但是却修改了number;而获取age时,返回的是number,此时相当于间接改变了age

image-20221015164226817

知识拓展:Vue中的数据代理

在Vue中,data底层是利用了数据代理与View中的数据进行绑定。data中的数据是存储在Vue对象的_data属性中的,但是如果只是这样,就会导致想访问Vue中的某个data的值,就需要Vue对象._data.属性名来获取,这样显得很繁琐,所以Vue直接对_data进行数据代理,直接可以通过Vue对象.属性名来获取data的属性

1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处
更加方便的操作data中的数据,简化对属性的操作
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。

7、事件处理

事件处理需要用到v-on指令,同时还需要搭配methos一起使用

7.1 快速入门

通过点击事件修改h1中的值

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>事件处理1</title><!-- 引入开发版的Vue --><script src="./js/vue.js"></script>
</head>
<div id="root"><h1>{{name}}</h1><button v-on:click="changeName()">点我更换名字</button>
</div><body><script>Vue.config.productionTip = false; //关闭Vue的警告提示const vm = new Vue({el: '#root',data: {name: '张三'},methods: {changeName(e) {console.log(this); //此处的this代表vmconsole.log(e.target); //<button>点我更换名字</button>vm.name = "李四";}}});</script>
</body></html>

430482200207096891

7.2 事件修饰

  • prevent:阻止事件的默认行为,相当于 event.preventDefault() 常用
  • stop :阻止事件冒泡 ,相当于event.stopPropagation()常用
  • once:事件只触发一次 (常用
  • capture:开启事件的捕获模式
  • self:只有event.target是当前操作元素时,才触发事件(可用来阻止事件冒泡)
  • passive:事件的默认行为立即执行,无需等待事件回调再执行
  • native:vue中如果你想在某个组件的根元素上绑定事件,需要使用native修饰符示例

测试前四个事件修饰关键字

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>事件修饰符</title><!-- 引入开发版的Vue --><script src="./js/vue.js"></script>
</head><body><div id="root"><h1>测试prevent</h1><a v-on:click.prevent="jump()" href="http://www.baidu.com">点击跳转至{{name}}</a><!--简写: <a @click.prevent="jump()" href="http://www.baidu.com">点击跳转至{{name}}</a> --><h1>测试stop</h1><div @click="showMsg(1)"><button>div1</button><div @click="showMsg(2)"><button>div2</button><div @click.stop="showMsg(3)"><button>div3</button></div></div></div><h1>测试once</h1><a @click.prevent.once="jump()" href="http://www.baidu.com">点击跳转至{{name}}</a><h1>测试capture</h1><!-- 事件冒泡由内往外,事件捕获由外往内,先事件捕获,再事件冒泡 --><div @click.capture="showMsg(1)"><button>div1</button><div @click="showMsg(2)"><button>div2</button><div @click="showMsg(3)"><button>div3</button></div></div></div><h1>测试self</h1><div @click="showMsg(1)"><button>div1</button><div @click.self="showMsg(2)"><button>div2</button><div @click="showMsg(3)"><button>div3</button></div></div></div></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {name: '百度'},methods: {jump() {alert("将要跳转至" + this.name);},showMsg(number) {console.log(number);}}})</script>
</body></html>

测试一:点击超链接不跳转

测试二:点击div3按钮,再控制台只输出div3;点击div2仍然冒泡

测试三:第一个点击超链接,不跳转;第二次点击,事件不生效,直接跳转到百度

测试四:点击div3,开启了事件捕获的再事件捕获阶段事件就执行了,所以顺序是div1、div3、div2

image-20221015204517661

7.3 键盘事件

  • keyup:用于绑定事件,用于按下并释放某个键时触发绑定事件
  • keydown:用于绑定事件,用于按下某个键时触发绑定事件
  • key:获取按键的名称
  • keyCode :获取按键的编码
  • keyName :获取按键的名称(少部分)

Vue常用按键别名:回车:enter 、删除:delete、退出:esc、空格:space、换行:tab、上\下\左\右:up\down\left\right

需要注意的是:除上述Vue自定义按键,其它的按键,如果是一个单词就直接是按键名,需要首字母大写,比如:Alt、Enter、Shift……

如果是多个单词,测试采用小写字母加-连接,比如:caps-lock(大小写转换键)。同时对于一些特殊的按键是无法绑定事件的,比如:调整音量的按键(F1,F2)、调整亮度的按键(F5,F6)……当使用tab键时,需要搭配keydown使用,因为tab键具有特殊功能,使用tab键会切换焦点,所以@keyup.tab是用于无法触发事件的!系统修饰键:CtryAltShiftMeta(Win)这四个键,搭配@keyup,需要在按下系统修饰键的同时,按下其他非系统修饰键,然后再释放,事件才会被触发;搭配@keydown使用,正常触发事件

//给按键起别名(这里给回车键Enter起别名)
Vue.config.keyCodes.huiche = 13;
//系统修饰符还能接一个其它键
@keyup.ctry.y//只有当按下Ctry+y后释放y才能触发事件

示例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>键盘事件</title><script src="./js/vue.js"></script>
</head><body><div id="root"><input @keyup="showCode" type="text" placeholder="请输入"></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',methods: {showCode(e) {//当按下回车(出车的编码是13),就打印输出input中的内容// if (e.keyCode != 13) return;//可以使用@keyup.enter替代console.log(e.target.value);console.log(e.key, e.keyCode);}}});</script>
</body></html>

备注e.target.value的值不等于e.keyCode,e.target.value只会展示按下字母和数字的按键的,而e.keyCode会展示几乎所有的按键编码(好像只有一些特殊的按键,如:Fn等不会展示编码)

image-20221015222558243

8、计算属性

  • 计算属性是什么?

    计算属性是Vue对插值表达式的一种优化,通过computed关键字将一个属性进行计算,然后再赋值给Vue对象,让Vue对象直接使用{{}}进行引用

  • 为什么要使用计算属性?

    在使用插值表达式进行取值时,有时候我们想要对表达式进行各种各样的操作(使用链式编程),可能会导致{{}}中的值十分复杂,虽然不会报错,但是会严重影响代码的可读性!这就需要使用计算属性来解决这种问题了。

  • 方式一:直接使用插件表达式实现

    备注:这里的例子过于简单,其实直接使用插件表达式也是没毛病的,但如果在实际开发中遇到需要在插件表达式中进行大量函数调用时,建议使用计算属性

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性1</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root">姓: <input type="text" v-model="firstName"> <br>名: <input type="text" v-model="secondName"> <br><!-- slice函数截取firstName前三个字符 -->全名:<span>{{firstName.slice(0,3)}}-{{secondName}}</span></div>
    </body>
    <script>const vm = new Vue({el:'#root',data:{firstName:'张',secondName:'三'}})
    </script>
    </html>
    

    效果展示:

    430482200207096891

  • 方式二:使用methods进行实现

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性2</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root">姓: <input type="text" v-model="firstName"> <br> 名: <input type="text" v-model="secondName"> <br><!-- 注意函数的调用一定不能省略小括号 -->全名:<span>{{fullName()}}</span></div>
    </body>
    <script>const vm = new Vue({el: '#root',data: {firstName: '张',secondName: '三'},methods: {//当firstName或secondName发生改变时,fullName函数就会被调用fullName() {return this.firstName.slice(0, 3) + '-' + this.secondName;}}})
    </script></html>
    
  • 方式三:使用计算属性实现

    备注:计算属性底层也是使用数据代理实现的,需要注意的是计算属性属于Vue对象(可以直接通过Vue调用),但不属于_data

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性3</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root">姓: <input type="text" v-model="firstName"> <br> 名: <input type="text" v-model="secondName"> <br> 全名:<span>{{fullName}}</span></div>
    </body>
    <script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {firstName: '张',secondName: '三'},computed: {fullName: {//get的调用时机:1)初次调用,get被调用 2)所依赖的data数据发生改变时get被调用(不改变就直接使用缓存) get() { // console.log(this);//此处的this是Vuereturn this.firstName + '-' + this.secondName;},//set方法,用于对fullName这个计算属性进行修改 set(value) {console.log("set方法被调用了");const arr = value.split("-");this.firstName = arr[0];this.secondName = arr[1];}}}});
    </script></html>
    

    知识拓展

    • methods实现和计算属性实现的比较使用计算属性实现性能更高,因为计算属性使用了缓存技术,当同一时间内调用同一个计算属性,且这段时间内该计算不发生该改变,不需要执行get方法,而是直接去缓存中取数据,避免了重复计算;而methods就需要重复调用方法

      image-20221015232206979

      image-20221015232535268

    • 计算属性的简写形式:

              computed: {/*fullName: {get() {return this.firstName + '-' + this.secondName;}}*///简写:只针对只读不写的计算属性,也就是只有get函数的计算属性/*fullName: function() {return this.firstName + '-' + this.secondName;}*///进一步简写:fullName() {return this.firstName + '-' + this.secondName;}}
      

总结

计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

9、监视属性

  • 什么是监视属性?

    也称侦听属性,是指使用watch对于属性进行监控,类似于Java中的监听器

  • 监视属性的作用?

    通过检测属性的改变,触发相应的事件

示例

实现应该天气切换效果

使用监视属性:点击跳转

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>天气切换</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
</head><body><div id="root"><h2>天气很{{info}}</h2><button @click="changeWeather()">切换天气</button></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {isHot: true},computed: {info() {return this.isHot ? "热" : "冷";}},methods: {changeWeather() {this.isHot = !this.isHot;}},});</script>
</body></html>

效果展示:

430482200207096891

注意事项

  1. 这里可以直接将方法代码直接写在@click=""中,但是需要注意的是直接写在Vue的指令中,用到的变量或方法只会从Vue对象中寻早,如果Vue中没有找到就会到Vue原型中找;如果是写在方法methods中则会默认在Window对象中寻找,当使用this时才会到Vue对象中寻找,示例:

    image-20221017154440957

    对于第二个按钮,效果和第一个按钮是一致的;但是对于第三个按钮,运行不会报错,但在触发点击事件后就会报错alert方法不存在!原因是由于alert函数是属于Window对象中的,并不是Vue对象中的,在这里使用thiswindow关键字也无效

    最终建议如果是一些简单是语句,且只用到Vue对象或Vue原型中的属性,可以直接在使用第二个按钮的写法,对于复杂的语句最好使用第一个按钮的写法。所有的Vue函数都必须使用普通函数的形式

9.2 快速入门

示例

前面的案例我们学到了函数的两种写法,现在就让我们看看使用监视属性后的效果吧

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>监视属性2</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
</head><body><div id="root"><h2>天气很{{info}}</h2><button @click="changeWeather()">切换天气</button></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {isHot: true},computed: {info() {return this.isHot ? '热' : '冷';}},methods: {changeWeather() {this.isHot = !this.isHot;}},//方式一:/*  watch: {isHot: {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)immediate: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}}} */});//方式二:(方式一中的isHot其实也是需要加引号的,只是简写了)vm.$watch('isHot', {immediate: true,handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}});</script>
</body></html>

效果展示:

image-20221018205348380

9.2 深度监视

  • 什么是深度监视?

    深度监视是指对于多层次结构的属性,其中某一个属性发生改变时,就能得知该属性发生了改变。

    比如:numbers={a:1,b:{c:2,e:3}},numbers中的b属性中的c属性发生了改变时,我们能立刻得知numbers发生了改变的这一过程

  • 深度监视有什么用?

    监控属性的改变,可以做出相应的事件

示例

使用深度监视检测多层次结构中属性的变化

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>监视属性2</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
</head><body><div id="root"><h2>天气很{{info}}</h2><button @click="changeWeather()">切换天气</button><hr><h2>a的值是:{{numbers.a}}</h2><button @click="numbers.a++">点我让a值加1</button><button @click="numbers={a:666,b:888}">点我改变numbers的地址</button></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {isHot: true,numbers: {a: 1,b: 2}},computed: {info() {return this.isHot ? '热' : '冷';}},methods: {changeWeather() {this.isHot = !this.isHot;}},watch: {isHot: {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)immediate: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}},//监视多级结构中某个属性的变化'numbers.a': {handler(newValue, oldValue) {console.log('a未修改前:' + oldValue + ';' + 'a修改后:' + newValue);}},//不开启深度监视,只有当numbers的地址发生改变时,才会调用handler函数/* numbers: {handler() {console.log('numbers的地址发生了改变');}}, *///Vue默认能够检测多层次数据结构的改变,但是watch函数默认不能检测numbers: {//开启watch函数的深度检测,当numbers中的属性或地址发生改变时,调用handler函数deep: true,handler() {console.log('numbers中的属性发生了改变');}}}});</script>
</body></html>

效果展示:

image-20221018212436867

9.3 知识拓展

主要拓展三个点:

  1. 深度监视的简写形式
  2. 使用深度监视实现前面计算属性的案例
  3. 监视属性和计算属性的比较
  • 深度监视的简写形式:

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>监视属性4</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root"><h2>天气很{{info}}</h2><button @click="changeWeather()">切换天气</button></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {isHot: true},computed: {info() {return this.isHot ? '热' : '冷';}},methods: {changeWeather() {this.isHot = !this.isHot;}},watch: {//完整写法:/* isHot: {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)// immediate: true,//开启watch的深度监视// deep: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}} *///简写:(只需要handler时才能使用!)isHot(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}}});//完整写法/*  vm.$watch('isHot', {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)// immediate: true,//开启watch的深度监视// deep: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}}); *///简写:(只需要handler时才能使用!)vm.$watch('isHot', function(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);});</script>
    </body></html>
    

    备注:使用vm配置监听属性和直接在watch中配置监听属性,是等价的。简写的代价是只能调用handler函数,无法配置其它属性

  • 使用深度监视实现前面计算属性的案例:

    也就是实现效果联动

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>监视属性4</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root"><h2>天气很{{info}}</h2><button @click="changeWeather()">切换天气</button></div><script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {isHot: true},computed: {info() {return this.isHot ? '热' : '冷';}},methods: {changeWeather() {this.isHot = !this.isHot;}},watch: {//完整写法:/* isHot: {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)// immediate: true,//开启watch的深度监视// deep: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}} *///简写:(只需要handler时才能使用!)isHot(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}}});//完整写法/*  vm.$watch('isHot', {//程序初识时就执行handler(换句话说,就是当我们加上这个属性,保存后就执行handler函数)// immediate: true,//开启watch的深度监视// deep: true,//当isHot属性发生改变时,handler函数就被调用handler(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);}}); *///简写:(只需要handler时才能使用!)vm.$watch('isHot', function(newValue, oldValue) {console.log('isHot未修改前:' + oldValue + ';' + 'isHot修改后:' + newValue);});</script>
    </body></html>
    

    效果展示:

    430482200207096891

  • 监视属性和计算属性的比较

    从上面那个案例,可以发现使用监视属性watch实现联动效果好像比计算属性computed要复杂的多,这么说是不是如果实现联动效果,是不是可以全部使用计算属性来实现?

    答案是否定的!在一些特殊的地方还是需要使用监视属性来实现,比如延迟动态改变属性值,具体见下面代码👇

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算属性5</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script>
    </head><body><div id="root">姓: <input type="text" v-model="firstName"> <br> 名: <input type="text" v-model="secondName"> <br> 全名:<span>{{fullName}}</span></div>
    </body>
    <script>Vue.config.productionTip = false;const vm = new Vue({el: '#root',data: {firstName: '张',secondName: '三',fullName: '张-三'}/* ,computed: {fullName: function() {//直接无效,因为fullName的Getter没有返回值,这个返回值给了setTimeout函数setTimeout(() => {return this.firstName.slice(0, 3) + '-' + this.secondName;})}} */,watch: {firstName(newValue) {setTimeout(() => {// console.log(this);//指向Vuethis.fullName = newValue.slice(0, 3) + '-' + this.secondName;}, 1000)//这里一定要使用箭头函数,否则this是指向window的(这和methods中的函数是相反的!)/*setTimeout(function() {console.log(this);//指向windowthis.fullName = newValue.slice(0, 3) + '-' + this.secondName;}) */},secondName(newValue) {this.fullName = this.firstName + '-' + newValue;}}});
    </script></html>
    

    知识点:普通函数有自己的this,箭头函数没有自己的this,当使用箭头函数式,函数内部的this会从上一级依次寻找

总结

  1. computed能完成的功能,watch都可以完成
  2. watch能完成的功能,compute不一定能够完成,例如:watch可以进行异步操作(上面的延迟功能)
  3. 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm对象或组件实例对象
  4. 所有不被Vue管理的函数(例如:定时器的回调函数、Ajax的回调函数、Promise的回调函数……),最好写成箭头函数,这样this的指向才能是vm对象或组件的实例化对象

10、动态绑定样式

本小节主要学习:

  • 动态修改class属性
  • 动态修改style属性
  • 动态修改class属性

    实现方式::class=""

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>样式绑定</title><!-- 引入开发版Vue --><script src="./js/vue.js"></script><style>.zero {color: aqua;}.first {width: 100px;height: 100px;background-color: pink;}.second {width: 100px;height: 100px;border: 2px solid greenyellow;border-radius: 10px;}.third {color: red;}</style>
    </head><body><div id="app"><div class="zero" :class="style">你好,Vue</div><button @click="changeStyle">随机切换一种样式</button><button @click="addStyle">添加一种样式</button><button @click="removeStyle">移除一种样式</button><button @click="addAll">添加多个样式</button> {{i}}</div><script>const vm = new Vue({el: '#app',data: {i: 2,arr: ['first', 'second', 'third'],style: []},methods: {changeStyle() {//只有style只有一种样式时,才能进行随机切换if (this.i == this.arr.length - 1) {const index = Math.floor(Math.random() * 3); //index取值范围:0~1this.style.splice(0, this.style.length); //清空数组this.style.push(this.arr[index]);}},addStyle() {if (this.i > -1) {this.style.push(this.arr[this.i]);this.i--;}},removeStyle() {//从最后数组最后一个元素开始删if (this.i < this.arr.length - 1) {this.style.shift();this.i++;} else if (this.style.length != 0) {this.style.splice(0, this.style.length); //清空数组}},addAll() {//注意,不能去掉{{i}},必须是一个动态的数据,使用{{}}符号for (let j = 0; j < this.arr.length; j++) {this.style[j] = this.arr[j];}this.i = -1;}}});</script>
    </body></html>
    

    效果展示:

    430482200207096891
  • 动态修改style属性

    实现方式::style

    注意事项:对于两个名字的style内置属性,需要切换成小驼峰的方式

        <div id="app"><!-- 不使用Vue,硬编码 --><h1 style="font-size: 16px;">张三</h1><!-- 方式一 --><h1 :style="{fontSize: fSize+'px'}">张三</h1><!-- 方式二 --><h1 :style="fontSizeObj">张三</h1></div><script>let vm = new Vue({el: '#app',data: {fSize: 36,fontSizeObj: {// 注意这里的fontSize不能随便命名,必须有对应的CSS属性fontSize: '36px'}}});</script>
    

    效果展示:

    image-20221208192157273

11、条件渲染

  • v-if="表达式":当表达式为false时,不显示,前端无法查看到DOM结构。可以搭配v-else-if="表达式"v-else使用

    注意事项:当我们搭配后面两个指令时,中间一定不要被其它标签打断

        <div v-if="n==1">1</div><div v-else-if="m==2">2</div><!-- 后面无法生效,当n变成3时还会报错 --><div>@</div><div v-else="n==3">3</div>
    

    可以搭配template标签使用,不会影响DOM结构

  • v-show="表达式":当表达式为false时,不显示(等价于display:none),DOM结构仍然存在,前端可以查看到DOM结构

显示、隐藏切换频率高的用v-show,切换频率低的用v-if。因为添加和删除DOM结构需要消耗时间

12、列表渲染

测试使用v-if指令,它可以遍历数组、JSON对象、数字、字符串

  <div id="app"><ul><!-- 遍历数组(这里的in可以用of代替) --><li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li><!-- 遍历数组 --><li v-for="(p,index) in persons" :key="index">{{p}}-{{index}}</li><!-- 遍历JSON对象 --><li v-for="(val,key) in student" :key="key">{{val}}-{{key}}</li><!-- 遍历指定次数 --><li v-for="(number,index) in 3" :key="index">{{number}}-{{index}}</li><!-- 遍历字符串 --><li v-for="(str,index) in strs" :key="index">{{str}}-{{index}}</li></ul></div></body>
<script>new Vue({el: '#app',data: {persons: [{id: '1',name: '张三',age: 18}, {id: '2',name: '李四',age: 19}, {id: '3',name: '王五',age: 20}],student: {name: '张三',age: 18,gender: '男'},strs: 'abc'}});
</script>

效果展示:

image-20221208210015865

13、key的作用和原理

Vue中有虚拟DOM,能够实现DOM的复用,复用的核心算法是diff算法。

首先,使用了Vue的标签,会先将你写的标签转成虚拟DOM,暂存在内存中,然后再转成真实DOM(能够再浏览器中查看到的DOM);当你的代码(也就是你写的标签)发生改变时,会重新转成虚拟DOM,然后从内存中使用diff算法进行虚拟DOM对比,当发现两个虚拟DOM的id一致时,他就会直接使用之前的虚拟DOM,这就避免了DOM的转换所消耗的时间,大大提高代码效率。

显然由于这个原因,所以要保障key值的唯一性,否则Vue就无法进行有效的虚拟DOM对比

示例:

    <div id="app"><button @click.once="add">点击添加一个输入框</button><ul><!-- 遍历数组 --><li v-for="(p,index) in persons" :key="index">{{p.id}}--{{p.name}}--{{index}}<input type="text"></li></ul></div></body>
<script>new Vue({el: '#app',data: {persons: [{id: '1',name: '张三',age: 18}, {id: '2',name: '李四',age: 19}, {id: '3',name: '王五',age: 20}]},methods: {add() {let person = {id: '4',name: '赵六',age: '19'};//将这个JSON对象添加再数组的前面this.persons.unshift(person);}}});
</script>

结果展示:

image-20221208214316210

这是由于key的顺序发生了改变,我再添加一个新的输入框是,是将新的输入框添加再最前面的,而key是index,导致旧的虚拟DOM往后移了一位,产生了错位

image-20221208214530422

总结

面试题:react、vue中的key有什么作用?(key的内部原理)1. 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:2.对比规则:(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。3. 用index作为key可能会引发的问题:1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。4. 开发中如何选择key?:1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

14、过滤

14.1 列表过滤

本小节主要实现:模糊查询、以及降序和升序排序

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>过滤器</title><script src="./js/vue.js"></script>
</head><body><div id="app"><h2>人员列表</h2><input type="text" placeholder="请输入要搜索的名字" v-model="keywords"><button @click="sortType=0">原始排序</button><button @click="sortType=1">升序排序</button><button @click="sortType=2">降序排序</button><h2>使用监视属性实现模糊查询</h2><ul><li v-for="(p,index) in searchPersons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul><hr><h2>使用计算属性实现模式查询</h2><ul><li v-for="(p,index) in computedPersons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script>new Vue({el: '#app',data: {keywords: '',/* 0:原始排序;1:升序;2:降序 */sortType: 0,persons: [{id: 1,name: '马冬梅',age: 23,sex: '女'}, {id: 2,name: '周冬雨',age: 18,sex: '女'}, {id: 3,name: '周杰伦',age: 33,sex: '男'}, {id: 4,name: '周润发',age: 31,sex: '男'}],searchPersons: []},// 使用监视属性实现模糊查询watch: {keywords: {// 初始化时,就调用handler方法(当keyWords为null时,返回0)immediate: true,handler(val) {// 当val非null,且val不存在p.name中,就会返回-1this.searchPersons = this.persons.filter((p) => {return p.name.indexOf(val) != -1});//由于监视属性,必须要keywords发生变化,才会执行handler函数,所以无法实现排序功能/* const arr = this.persons.filter((p) => {return p.name.indexOf(val) != -1})if (this.sortType) {//sortType!=0,进行排序arr.sort((p1, p2) => {return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age})}this.searchPersons = arr */}}},// 使用计算属性实现模式查询computed: {computedPersons() {/* return this.persons.filter((p) => {return p.name.indexOf(this.keywords) != -1}) */const arr = this.persons.filter((p) => {return p.name.indexOf(this.keywords) != -1});if (this.sortType) {//sortType!=0,进行排序arr.sort((p1, p2) => {return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age})}return arr}}});</script>
</body></html>

效果展示:

备注:就实现方式而言,两相比较,使用计算属性更优

430482200207096891

14.2 过滤器

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./js/vue.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.6/dayjs.min.js"></script>
</head>
<div id="app">当前时间戳:<input type="text" size="100" v-model="nowTime"><br/> 计算属性实现时间戳转换:<input type="text" v-model="computedTime" size="100"><br/> methods实现时间戳转换:<input type="text" v-model="methodsTime" @click="getMethodsTime" size="100"><br/> 过滤器实现时间戳转换(无参数):<span>{{nowTime | getFilterTime}}</span><br/> 过滤器实现时间戳转换(有参数):<span>{{nowTime | getFilterTime('YYYY-MM-DD HH:mm:ss')}}</span><br/> 多个过滤器:<span>{{nowTime | getFilterTime('YYYY-MM-DD HH:mm:ss') | secondFilter}}</span><br/> 搭配v-bind使用:<input type="text" :value="msg | secondFilter"><br></div>
<script>Vue.config.productionTip = false;// 全局过滤器Vue.filter('myFilter', function(val) {return dayjs(val).format(str)});const vm = new Vue({el: '#app',data: {nowTime: new Date,methodsTime: '',msg: '搭配v-bind使用过滤器'},computed: {computedTime() {return dayjs(this.nowTime).format('YYYY-MM-DD HH:mm:ss')}},mounted() {this.getMethodsTime();},methods: {getMethodsTime() {this.methodsTime = dayjs(this.nowTime).format()}},filters: {getFilterTime(val, str) {//当str为null时,format会使用dayjs默认的格式return dayjs(val).format(str)},secondFilter(val) {//先执行上面那个过滤器,执行后的结果通过val传入这个过滤器return val.slice(0, 4)}}});
</script><body></body>
</html>

效果展示:

image-20221210213326064

15、数据更新时的底层原理

本小节

  • 给对象新增一个属性失效,Vue无法探测到直接添加的属性的(这是因为Vue底层实现数据监视,是使用数据代理,而数据代理需要属性具有getter和setter,如果直接给一个数据新增一个属性,不会进行数据加工,也就是转到_data中,从而不会有getter和setter,所以也就是会逃避vue的检测),而想要之后添加属性,可以使用以下两个API

    • vue.set(target,attribute,val)

    • vm.$set(target,attribute,val)

注意:target不能是vm,也不能是data,必须是data中的一个属性

<div id="app"><button @click="addSex">给学生添加一个性别属性</button><h3>学生姓名:{{student.name}}</h3><h3>学生年龄:{{student.age}}</h3><h3 v-if="student.sex">学生性别:{{student.sex}}</h3>
</div>
<script>const vm = new Vue({el: '#app',data: {student: {name: '张三',age: 18,teacher: {name: '李四',age: 32}}},methods: {addSex() {//错误示范:// this.student.sex = '男';//方式一:使用Vue的APIVue.set(this.student, 'sex', '男');//方式二:使用vm的API// this.$set(this.student, 'sex', '男');}}});
</script>

效果展示:略……

  • 修改数组的值失效,对于数组而言,只有调用数组自身方法时,Vue才能够监测到(因为数组的修改,无法自动新增getter和setter进行实现,所以Vue就写死规定,只有通过数组自带的七个方法进行数据修改,才能监测到),直接使用赋值号=修改数组,Vue并不能够进行监测。

    数组自身拥有的七个方法:push(向数组的末尾添加一个或多个元素,并返回新的长度)\pop(删除数组最后一个元素,并返回值)\shift(把数组的第一个元素从其中删除,并返回第一个元素的值)\unshift(向数组的开头添加一个或更多元素,并返回新的长度)\splice\(替换数组指定位置的元素)\sort\(排序数组)\reverse(反转数组)

    <div id="app"><button @click="addSex">给学生添加一个性别属性</button><h3>学生姓名:{{student.name}}</h3><h3>学生年龄:{{student.age}}</h3><h3 v-if="student.sex">学生性别:{{student.sex}}</h3><button @click="updateHobby">点击修改学生爱好</button><h3>学生爱好:</h3><ul><li v-for="(h, index) in hobby" :key="index">{{h}}</li></ul></div>
    <script>const vm = new Vue({el: '#app',data: {student: {name: '张三',age: 18,teacher: {name: '李四',age: 32}},hobby: ['抽烟', '喝酒', '烫头']},methods: {updateHobby() {//错误示范,直接报错:Cannot set properties of undefined (setting '0')"//this.student.hobby[0] = "学习";//方式一:this.student.hobby.splice(0, 1, '学习');// 方式二:Vue.set(this.student.hobby, 1, '学习');//方式三:// this.$set(this.student.hobby, 1, '学习');}}});
    </script>
    

效果展示:

image-20221210184436333

16、内置指令和自定义指令

拓展知识:

对于表单数据的收集:

  1. 对于复选框(checkbox),如果不设置vlaue,Vue默认是获取它的checked属性的值
  2. 对于单选框(radio),如果设置vlaue,Vue默认是无法得到单选框的值的,需要设置value
  3. 数据转成JSON字符串,使用API:JSON.stringify()
  4. 表单提交时想执行函数,可以直接再form标签上使用@submit="函数名",如果不想要表单提交,可以加后缀.prevent

  • v-model修饰符:
    • v-model.number:自动将前端输入的字符转成数字,可以搭配HTML的type="number"一起使用
    • v-model.trim:去掉输入框前后的空格,中间的空格无法去除
    • v-model.lazy:失去焦点,才收集数据(默认是实时收集数据的)

16.1 内置指令

内置指令介绍:

			我们学过的指令:v-bind	: 单向绑定解析表达式, 可简写为 :xxxv-model	: 双向数据绑定v-for  	: 遍历数组/对象/字符串v-on   	: 绑定事件监听, 可简写为@v-if 	 	: 条件渲染(动态控制节点是否存存在)v-else 	: 条件渲染(动态控制节点是否存存在)v-show 	: 条件渲染 (动态控制节点是否展示)v-text指令:1.作用:向其所在的节点中渲染文本内容。2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。v-html指令:1.作用:向指定节点中渲染包含html结构的内容。2.与插值语法的区别:(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。(2).v-html可以识别html结构。3.严重注意:v-html有安全性问题!!!!(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!	v-cloak指令(没有值):1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。v-once指令:1.v-once所在节点在初次动态渲染后,就视为静态内容了。2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。v-pre指令:1.跳过其所在节点的编译过程。2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

16.2 自定义指令

Vue中的指令本质就是一个函数,对于自定义指令,我们可以使用directives进行配置

  • 实现方式一:以函数的形式实现

    示例,定义一个v-big指令,和v-text功能类似,但会把绑定的值放大

  • 实现方式二:以对象的形式实现

    示例,定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。

如果想要拿到父元素并修改父元素的样式、让指令所在DOM获取焦点,只能通过方式二来实现。总的来讲:方式二的使用范围大于方式一,但方式一更加简便,且适合大部分常规操作,方式一使用的更加普遍

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./js/vue.js"></script>
</head>
<div id="app"><!-- 原生v-text --><h2>当前n的值是:<span v-text="n"></span></h2><!-- 自定义v-big --><h2>将n放大十倍后的值是:<span v-big="n"></span></h2><button @click="n++">点我n值加1</button><br/><!-- 原生v-bind --><input type="text" :value="n"><br><!-- 自定义v-fbind --><input type="text" v-fbind="n"><br><!-- 自定义的全局指令 --><input type="text" v-big2="n"><br><input type="text" v-f-bind2="n">
</div><body><script>Vue.config.productionTip = false//全局指令Vue.directive('big2', function(element, binding) {element.value = binding.value * 10});Vue.directive('f-bind2', {bind(element, binding) {element.value = binding.value},inserted(element, binding) {element.focus()},update(element, binding) {element.value = binding.value}})const vm = new Vue({el: '#app',data: {n: 1},directives: {//方式一:使用函数。这种方式更加精简,但是对于一些细节无法处理big(element, binding) {console.log(this) //注意,此处的this是window并不是vm//element是指令所在的DOM(即span标签),binding是绑定对象,含有指令所在的DOM的所有属性element.innerText = binding.value * 10//备注:一旦模板发生更新(也就是id=app这个区域的DOM发生更新),big函数就会被执行},//错误演示,使用方式一无法使input标签默认获取焦点(这是由于指令的执行时机决定的)/* fbind(element, binding) {element.innerText = binding.valueelement.focus()} *///方式二:使用对象的形式实现fbind: {//指令与元素成功绑定时执行(一上来就执行)。第一执行bind(element, binding) {element.value = binding.value},//指令所在DOM被展示到页面时执行,第二执行inserted(element, binding) {element.focus()},//模板发生更新时执行,第三执行update(element, binding) {element.value = binding.value}}}})</script>
</body></html>

备注:

  1. 指令定义时,不要加v-,指令使用时,需要加v-
  2. 指令命名时,使用kebab-case方式,不要使用cameCase方式命名,使用kebab-case命名的指令,在定义时记得使用引号包裹

效果展示:

430482200207096891

17、Vue实例化和生命周期

共8个四队钩子方法,对应Vue的8个生命周期

image-20221214143346537

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./js/vue.js"></script>
</head><body><div id="app"><h2 :style="{opacity}">欢迎学习Vue</h2><button @click="stop">停止渐变</button></div><script>const vm = new Vue({el: '#app',data: {opacity: 1},//Vue还未实例化,还未进行数据代理,无法通过vm访问到data和methodsbeforeCreate() {console.log("beforeCreate" + this);// debugger;},//Vue实例化完成,进行了数据代理,可以通过vm访问到data和methods,但还未生成虚拟DOMcreated() {console.log("created" + this);// debugger;},//页面呈现的都是未经Vue编译的DOM,此时虚拟DOM已生成,但未加载到页面上beforeMount() {//注意:此处的DOM操作最终都会无效,可以看前面的图加以理解console.log("beforeMount" + this);document.querySelector('h2').innerHTML = "你好Vue";// debugger;},//Vue完成模板的==初次==解析并把真实的DOM元素放入页面后(即挂载完毕)调用mounted函数mounted() {console.log("mounted" + this)this.change();},//模板更新前(页面和数据没有保持同步,内存中的数据已发生变化,但是页面中的数据没有发生变化)beforeUpdate() {console.log("beforeUpdate" + this);// debugger;},//模板更新就调用updated钩子方法,页面和数据同步updated() {console.log("updated" + this);//https://v2.cn.vuejs.org/v2/api/#vm-destroy// this.$destroy()// debugger;},methods: {change() {this.timer = setInterval(() => {this.opacity -= 0.01if (this.opacity <= 0) this.opacity = 1})},stop() {clearInterval(this.timer);}},beforeDestroy() {console.log("beforeDestroy" + this);// debugger;},destroyed() {console.log("destroyed" + this);// debugger;},});//外部调用定时器实现(定时器直接会被浏览器的JS引擎调用)/* setInterval(() => {vm.opacity -= 0.01if (vm.opacity <= 0) vm.opacity = 1}) */</script>
</body></html>

测试beforeCreate钩子方法的作用:

image-20221214144433824

测试created钩子方法的作用:

image-20221214144546648

测试beforeMount钩子方法的作用:

430482200207096891

各阶段:

image-20221214183804678

备注:mounted:发送ajax请求,启动定时器等异步任务;beforeDestory:做收尾工作,如:清除定时器

参考资料

  • 尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通:B站学习视频
  • 介绍 — Vue.js (vuejs.org):Vue2官方文档
  • 简介 | Vue.js (vuejs.org):Vue3官方文档
  • Vue.js 模板语法 | 菜鸟教程 (runoob.com)
  • Vue.js是什么?它有什么特点?:C语言网Vue教程
  • 编程风格之声明式与命令式的区别
  • 什么是MVVM?
  • vue中的挂载是什么意思?
  • (8条消息) 箭头函数与普通函数的区别详解
  • vue中data是干嘛的? 有几种写法 ? 有什么区别?

在此致谢(^_^ゞ


  1. 数据的双向绑定,指当View的数据发生改变时,Model的数据也会发生改变,反之亦然 ↩︎

  2. 组件化模式,就是将各个不同的部分进行封装,从而降低给部分的耦合度,这样每个部分成为一个组件,具有更高地移植性 ↩︎

  3. 原生的JS是命令式编码,它关注编程的实现步骤,需要一步一步地编写代码向程序下达命令;而声明式编码是告诉程序,你该干嘛,具体实现由程序自己实现 ↩︎

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

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

相关文章

Spring整合tomcat的WebSocket详细逻辑(图解)

主要解决存在的疑问 为什么存在2种spring整合websocket的方式&#xff0c;一种是使用ServerEndpoint注解的方式&#xff0c;一种是使用EnableWebSocket注解的方式&#xff0c;这2种有什么区别和联系&#xff1f;可以共存吗&#xff1f;它们实现的原理是什么&#xff1f;它们的各…

IP应用场景查询API:深入了解网络用户行为的利器

前言 随着数字时代的不断发展&#xff0c;互联网已经成为人们生活的重要组成部分。而随着越来越多的业务和社交活动迁移到在线平台上&#xff0c;了解和理解网络用户行为变得至关重要。为了满足这个需求&#xff0c;IP 应用场景查询 API 崭露头角&#xff0c;成为深入了解网络…

【科普向】Jmeter 如何测试接口保姆式教程

现在对测试人员的要求越来越高&#xff0c;不仅仅要做好功能测试&#xff0c;对接口测试的需求也越来越多&#xff01;所以也越来越多的同学问&#xff0c;怎样才能做好接口测试&#xff1f; 要真正的做好接口测试&#xff0c;并且弄懂如何测试接口&#xff0c;需要从如下几个…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书南京财经大学图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书南京财经大学图书馆

《TCP/IP网络编程》阅读笔记--地址族和数据序列

目录 1--IP地址和端口号 2--地址信息的表示 3--网络字节序与地址变换 4--网络地址的初始化与分配 5--Windows部分代码案例 1--IP地址和端口号 IP 地址分为两类&#xff1a; ① IPv4 表示 4 字节地址族&#xff1b; ② IPv6 表示 16 字节地址族&#xff1b; IPv4 标准的 4 …

C++设计模式_03_模板方法Template Method

文章目录 1. 设计模式分类1.1 GOF-23 模式分类1.2 从封装变化角度对模式分类 2. 重构&#xff08;使用模式的方法&#xff09;2.1 重构获得模式 Refactoring to Patterns2.2 重构关键技法 3. “组件协作”模式4. Template Method 模式4.1 动机&#xff08; Motivation&#xff…

Python实现SSA智能麻雀搜索算法优化LightGBM分类模型(LGBMClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新型的群智能优化算法&#xff0c;在2020年提出&a…

算法与设计分析--实验一

蛮力算法的设计与分析&#xff08;暴力&#xff09; 这次是某不知名学院开学课程的第一次实验&#xff0c;一共5道题&#xff0c;来自力扣 第一题.216组合总和*力扣题目链接 第一道题是经典的树型回溯 class Solution { public:vector<vector<int>> combinatio…

leetcode:58. 最后一个单词的长度

题目&#xff1a; 函数原型&#xff1a; int lengthOfLastWord(char * s) 解析&#xff1a; 求最后一个单词的长度&#xff0c;我们有两种思路 第一种思路&#xff1a; 逆向求&#xff0c;先设置一个字符串下标index&#xff0c;定位到最后一个单词的最后一个字符。再一个设置长…

MySQL——连接查询与子查询

一、连接查询 单表查询&#xff1a;在一张表当中查询数据&#xff0c;叫做单表查询。 连接查询&#xff0c;结合俩&#xff08;多&#xff09;张表&#xff0c;在俩张&#xff08;多&#xff09;表当中查询数据&#xff0c;在一张表当中查询一部分&#xff0c;在另一张表当中…

UE4/UE5 动画控制

工程下载​ ​​​​​​​​​​​​​https://mbd.pub/o/bread/ZJ2cm5pu 蓝图控制sequence播放/倒播动画&#xff1a; 设置开启鼠标指针&#xff0c;开启鼠标事件 在场景中进行过场动画制作 设置控制事件

MySQL的概述、版本、安装过程

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、MySQL的概述 二、MySQL的版本 三、MySQL的下载与安装 前言 本文将来谈谈MySQL的概述&#xff0c;MySQL的版本&#xff0c;以及它…