Vue2学习笔记(尚硅谷天禹老师)

server/2024/12/22 17:53:55/

目录

一、入门案例

二、模板语法

三、数据绑定

四、el和data的两种写法

五、MVVM模型

六、Object.defineproperty方法

七、Vue中响应式原理

八、数据代理

九、methods配置项

十、Vue中的事件处理

十一、Vue中的键盘事件

十二、计算属性

十三、监视属性watch

十四、绑定Class样式

十五、绑定style样式

十六、条件渲染

十七、列表渲染

十八、Key的原理以及diff算法

十九、列表过滤、排序

二十、过滤器

二十一、Vue如何监视data配置项

二十二、Vue如何监测数组

二十三、Vue.$set()方法

二十四、收集表单的value数据  

二十五、v-text和v-html指令

二十六、v-cloak、v-once、v-pre指令

二十七、自定义指令directives

二十八、生命周期

一、入门案例

         Vue可以看作是一个数据管理大师,其目的通常是将其管理的数据展示到HTML页面上,或者收集HTML页面上的数据进行储存在自身或者传递给服务器。

        Vue管理的数据大致可以分为:Ⅰ,对象数据:对象里面包含一些基本数据类型和嵌套着一些其他对象。Ⅱ,数组;Ⅲ,函数。我们需要了解Vue是如何管理对象、数组、函数这3种数据类型,包括:如何响应式这些数据、如何代理这些数据等。

        下面的代码是一个入门案例。首先引入vue.js的完整版,引入vue.js之后,在window上就多出了一个Vue构造函数对象,使用new Vue()就可以创建一个Vue实例对象vm,在创建vm的时候,需要把希望给vm管理的数据通过data配置项交给vm,把希望给vm管理的函数通过methods配置项交给vm。

<!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><!-- 准备好一个容器 --><div id="root"><h1>Hello!{{name}}!</h1></div><script>Vue.config.productionTip = false // 阻止vue在启动时生成生产提示new Vue({el:'#root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串data:{ //data用于存储数据,数据共el所指定的容器去使用name:'么么哒'}})</script>
</body>
</html>

        vm实例在得到数据之后,首先会进行一系列的加工,然后把数据放到HTML页面上。所有需要告诉vm把数据放到哪个DOM节点。使用el配置项或者vm.$mount()就可以指定vm管理哪一个DOM节点(通常是div节点)。可以将一个字符串选择器或者一个DOM节点原始js对象交给el和$mount,例如:el:"#root"或者el:document.getElementByID("#root")。

        在得到管理的模板之后,就可以将数据展示到模板当中了。使用模板语法中的插值语法将管理的数据name使用{{name}}展示到模板页面当中。{{}}里面必须是js表达式,这样才能够使用eval()函数进行运行识别,同时,{{}}里面可以直接使用vm管理的数据。

二、模板语法

        1.插值语法:使用双大括号{{}}可以在模板中插入变量或表达式的值。例如:{{name}}。

        2.指令语法:Vue 提供了一些指令,例如v-for、v-bind等。

        他们的共同点就是:{{js表达式}}或者v-bind="js表达式",作用就是将vm管理的数据呈现到HTML模板页面上。js表达式除了原始js表达式之外,还可以写vm自身管理的数据。

        什么是js表达式?答案:执行之后能够得到具体值的语句。试想一下vm需要将数据展示到HTML页面当中,如果你不通过函数return数据、或者表达式计算出数据来,vm拿什么放到页面上面呢?当然undefined不会被vm放入到页面当中。

三、数据绑定

        数据绑定的本质就是:1,vm将数据放到页面,称为单向绑定;2,一些表单节点可以采集数据,vm可以对他们进行收集,也可以把他们放到页面,称为双向绑定。思考:是否需要接触到DOM节点对象本身才能拿到这些数据呢?

        因为存在表单类DOM节点,Vue不得已设计了两个指令来处理数据的绑定,对于表单类节点,例如<input>、<select>、<textarea>等,使用v-model指令进行数据的双向绑定;对于其他节点如<div>使用v-bind进行绑定。

        v-bind可以简写为冒号:。v-model默认收集表单节点的value属性,那么可以使用v-model直接代替value,也就是v-model:value可以简写为v-model

四、el和data的两种写法

        el:一种是使用el配置项;另一种是使用vm.$mount()方法。

        data:一种是直接传入一个对象;另一种是使用函数函数return一个对象。使用函数返回是为了避免多个组件或者多个vm实例共用同一个data数据配置项。原因:函数返回这种形式每次返回的data都是船新版本的对象;而直接传入data由于引用传递的原因导致大家都在操作同一个data配置对象。

五、MVVM模型

        Vue就是典型的MVVM架构,M表示数据模型,也就是对象{}、数组[]等原始js数据组织形式;V就是view视图,也就是HTML页面;VM就是视图模型,也就是vm实例或者vc组件实例,负责数据管理和呈现。

六、Object.defineproperty方法

        Object.defineproperty方法是Vue中实现数据代理和数据劫持的核心方法。但是不幸的是,这个Object.defineproperty只能对对象的属性进行操作(传入的第一个参数是对象),而对象包装的属性有3种:基本数据类型、对象、数组对象。

        Object.defineproperty 的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,Object.defineproperty可以接收三个参数,Object.defineproperty(obj, prop, desc)。obj :  第一个参数就是要在哪个对象身上添加或者修改属性;prop : 第二个参数就是添加或修改的属性名;desc : 配置项,一般是一个对象。

        value:给定的初始值;

        writable:默认为控制属性是否可以被修改,默认false;

        configurable:控制属性是否可以被删除,默认false;

        enumerable: 控制属性是否可以枚举,true的话简单的说就是可以遍历获取该值,默认false。

        注意:当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错滴)。原因:都可以使用getter或setter方法了,难道还能把value固定或者writable变为false不成?

        get 是获取值的时候的方法,获取值的时候会被调用,不设置时默认返回undefined。set 是设置值的时候的方法,设置值的时候会被调用。get或set不是必须成对出现,任写其一就可以。

七、Vue中响应式原理

        Vue响应式的核心就是:数据被修改了、被读取了,Vue能够知道数据被修改或读取了。那只能依靠 Object.defineproperty这个API的set和get方法。而Object.defineproperty是针对对象属性的,拿到一个对象,不管这个对象嵌套了多深,Vue都是进行遍历将每一个对象的属性套上set和get方法,只有这样Vue才知道数据有没有被修改或者读取。

        所以Vue无法直接处理数组对象,因为只有数组的地址变了,Vue才知道他所管理的这个数组被改变了,如果只是修改数组里面的值,Vue就无法进行监测,也就是数组里面的数据变化Vue是不知道的,所以Vue只能劫持重载数组对象的push、shift等方法才能知道数组里面的数据是否被修改。原因就是数组也是一个引用对象,只要地址不变,Vue就判断你没变,即使数组里面的数据已经变化了。什么是数组?内存里面连续的一段储存空间就是数组。

        另外,后面的数据(不是通过data配置对象给的),就只能通过vm.$set()方法把数据变成响应式。而且这种方式存在效率问题,不管是线程执行时间还是内存消耗方面。

八、数据代理

       什么是数据代理?数据代理就是使用A对象对B对象的属性进行操作。在Vue中,对象A就是vm,对象B就是data配置项(vm上的_data对象)、computed上面的属性、methods中的方法。还有其他组件传递过来的props属性等。

        Vue为什么使用vm去代理vm._data?原因就是为了方便在模板语法当中或者vm实例当中可以直接使用vm._data上的数据。本质:Vue中的数据代理本质上就是两次Object.defineproperty封装,第一次是数据劫持,第二次就是数据代理,都是使用的 Object.defineproperty,但是目的不一样。

九、methods配置项

        methods是Vue的options的配置项对象中的一个属性,其本身也是一个对象,但是里面放的是函数。这些函数最终也会被vm进行代理,从而可以通过vm去访问这些方法函数。这些方法一般写成普通函数,以确保函数执行时的this指向vm或者vc。

        而this的作用就是能够从指定对象身上拿到一些数据。

    <script>Vue.config.productionTip = false new Vue({el:'#root', data:{name:'JOJO'},methods:{showInfo1(event){console.log(event)},showInfo2(evnet,num){console.log(event,num)}}})</script>

十、Vue中的事件处理

        浏览器中的事件一般是通过浏览器的交互线程进行识别。浏览器的事件类型大致分为:鼠标事件、键盘事件(keydown、keyup、keypress)、表单事件(input、change、select)、网页状态事件(load、domcontextload、readystatechange等)、焦点/剪贴板事件(focus、blur等)、窗口事件(scroll、resize)、touch事件等。每种事件类型的event对象都是不同的,但是都存在currentTarget 、Target 、type三个属性以及preventDefault() stopPropagation()两个方法。

        此外,事件还分为HTML事件、DOM0级事件、DOM2级事件。Vue中使用的DOM0级事件,也就给节点对应事件的回调从null改成Vue管理的函数或者其他地方的函数。因此,vm实例销毁之后,这些事件仍然存在。

        当一个事件比如click由鼠标发出,浏览器的事件识别线程识别到了鼠标按下,然后生成一个事件对象event,这个event对象取决于事件类型,不同的事件类型的event对象是不一样的。就开始事件流,先从document节点开始捕获到具体节点,然后冒泡执行,冒泡阶段所有被捕获的节点的事件都会被触发。

        在Vue当中,使用v-on:事件名指令或者@事件名称符号给节点绑定事件,例如下面的代码。事件回调一般写在vm中的methods配置项当中。建议使用function关键字写成普通函数,确保函数当中的this为vm或者vc。

<!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"><h2>hello,{{name}}</h2><button v-on:click="showInfo1">点我提示信息1</button><button @click="showInfo2($event,66)">点我提示信息2</button></div><script>Vue.config.productionTip = false new Vue({el:'#root', data:{name:'JOJO'},methods:{showInfo1(event){console.log(event)},showInfo2(evnet,num){console.log(event,num)}}})</script>
</body>
</html>

        事件的参数传递问题:首先@click="demo"和@click="demo($event)"等价。其次可以进行参数传递,例如@click="demo(vm.a,123)"。最后,为了防止$event对象丢失,可以使用@click="demo(vm.a,123,$event,345)"进行占位置,在回调函数的地方的第三个位置接收$event即可。

        事件修饰符:@click.self.once.passive.capture.stop.prevent="demo",一共6个。self表示event的target是当前节点也就是event.currenttaarget==target时触发;once只触发一次;passive表示事件的默认行为(例如交互渲染行为)立即执行,无需等待回调执行完毕;capture表示在捕获阶段执行回调;stop表示阻止事件冒泡;prevent表示阻止事件默认行为。

十一、Vue中的键盘事件

        键盘事件使用很多很频繁。键盘事件包括三种:keyup:所有按键都能触发(例如A、B、C和win键);keydown:同keyup;keypress:按下有值的键时触发,即按下 CtrlAltShiftMeta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个keyup事件。

        一些常见的键盘事件:@keydown.left、keydown.enter、keydown.13、keydown.esc等。

        组合键盘事件:比如CTRL+C,我们CV代码时,小拇指会摁住CTRL不放,然后不断地摁C和V,因此组合键盘事件配合CTRL的keypress和其他键的keyup使用,比如:@ctrl.s,@ctrl.v。

        自定义键码:Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名,例如Vue.config.keyCodes.A = 666.

十二、计算属性

        计算属性是Vue中的一个配置项,用于实时的加工已有的数据。一是要满足实时性、二只能处理已经存在的数据(指数据当前已经在内存里面存在了)。实时性是因为加工得到的数据可能会放置到HTML页面当中,也可能是其他数据链会依赖当前加工的数据,所以需要速度快,不要异步获取数据(否则卡住主线程)。

    <div id="app">姓:<input type="text" v-model:value="firstName"><br/>名:<input type="text" v-model:value="lastName"><br/>全名:<span>{{fullName}}</span></div><script>const vm = new Vue({el:'#app',data:{firstName:'wang',lastName:'y'},computed:{fullName:{get(){return this.firstName+'-'+this.lastName;},set(value){const arr = value.split('-');this.firstName=arr[0];this.lastName=arr[1];}}// 如果没有set函数,可以采用如下的简写形式// fullName(){//     return this.firstName+'-'+this.lastName;// }}});</script>

        计算属性的用法:简写形式时,把函数名当作属性使用。

        计算属性的底层:依靠的是Object.defineProperty()方法提供的gettersetter。也就是使用vm代理这些方法(显示的提供Object.defineProperty()给程序员使用)。另外,能够自动识别所使用数据的变化,但使用的数据发生变化,自动更新属性值。

        计算属性能够使用的数据:vm自身的数据,字面量数据等,最好处理的数据都是响应式数据。其次,计算属性是响应式数据

        计算属性的思考:计算属性在语法层面是一个函数或者一个对象。而且他是一个被Vue管理的响应式函数,当其数据链中的响应式数据变化,计算属性computed就会自动重新计算,说明computed计算属在底层被放置在其所使用数据链路中数据的set()中。

        计算属性的调用时机:1、get函数在初次读取时会执行一次;2、当计算属性所依赖的响应式数据发生改变的时候,get()函数会被再次调用。

        计算属性的优势:与methods实现相比,计算属性采用了缓存机制(脏值检测)在获取计算属性的属性值时,若所依赖的属性没有发生变化,那么就不会执行get()函数而是将缓存的计算属性值返回。与methods每次都会执行计算逻辑相比,效率更高。

        底层原理2:计算属性被Vue中的Watcher类进行封装,当监听的属性发生变化,也就是数据链上的数据发生变化时,自动更新整条数据链。

        计算属性最重要的就是其返回值。

十三、监视属性watch

        监视属性是一个配置项,是一个Vue进行管理的函数,其主要作用就是监视数据有没有发生变化,那么就可以肯定监视的数据必须是响应式数据,也就是被Vue通过set()劫持过的数据(某个数据可以被挂载在多个对象上实现多重响应式,也就是多个监视点)。

        计算属性也可以被监视,因为计算属性也是被响应式的数据。此外,data配置项中的多级嵌套对象的属性也可以进行监视,比如:"school.name"=function(){}。总的来说,只要是vm身上的响应式数据都可以进行监视且成功地运行响应式(就是换一个对象进行监视,多个对象监视同一个数据)。

        其目的是当数据发生变化,就去执行相应的handler函数。然后通过handler函数去执行一些其他逻辑(比如修改vm的数据、给vm添加新的响应式数据等),同时刷新数据链和执行相关的渲染函数。

    <div id="app"><h3>今天天气很{{info}}</h3><button @click="changeWeather()">切换天气</button></div><script>const vm = new Vue({el:'#app',data:{isHot:true,},computed:{info:{get(){return this.isHot?'炎热':'凉爽'}}},methods: {changeWeather(){this.isHot = !this.isHot}},// 监视写法一// watch:{//     // 完整写法//     // isHot:{//     //     // immediate:true,  // 该参数设置为true,可以在初始化的时候让handler调用一下     //     //     // deep:true,        // 深度监视//     //     // 属性变化的回调函数//     //     handler(newValue,oldValue){//     //         console.log('属性isHot被修改了',newValue,oldValue);//     //     },//     // },//     // 简写形式//     // 当我们监视的配置属性只有handler()函数,不需要设置deep和immediate时,可以采用简写形式//     isHot(newValue,oldValue){//         console.log('属性isHot被修改了',newValue,oldValue);//     }// }});// // 正常写法// vm.$watch('isHot',{//              // immediate:true,  // 该参数设置为true,可以在初始化的时候让handler调用一下     //             // deep:true,        // 深度监视//             // 属性变化的回调函数//             handler(newValue,oldValue){//                 console.log('属性isHot被修改了',newValue,oldValue);//             }// });// 简写形式vm.$watch('isHot',function(newValue,oldValue){console.log('属性isHot被修改了',newValue,oldValue);})</script>

        监视属性的两种写法:

        第一种:通过watch配置项,这种分为简写和详细写,详细写就是写成一个对象形式,对象里面写3个配置属性deep、immediate、和handler回调。如果不要求deep和immediate,那么就可以简写为函数形式:ishot(isHot(newValue,oldValue)){}。

        第二种:通过vm.$watch(),如上面的代码,写法和watch配置项一模一样。

        对于deep默认为false,当监视的数据是一个多层嵌套的对象时,可以开启deep为true,这样不管嵌套多深的对象发生数据变化都可以被监视到。 对于immediate,当被监视属性的回调函数注册到vm身上时,立即执行一次。

        回调函数的分类:什么时候写成普通函数?什么时候写成箭头函数?答案就是handler只能写成普通函数,才能确保this指向vm或者vc。

        如果回调函数不需要将一些任务函数放到异步队列当中,写成普通函数,这样this就是vm或者vc,此时的watch和computed效果上等价。如果回调函数handler里面使用了异步任务的回调函数,需要扔到消息队列当中,在将来的某个时刻拿到数据,那么handler里面的回调需要写成箭头函数

        原因就是:vm只是管理的handler函数,当运行handler时,才会生成handler方法体里面的箭头函数,此时这些箭头函数是在vm对象的方法栈生成的,所以handler方法体里面的回调函数的this就是vm或者vc。

        注意:function关键字生成的函数对象在生成的时候就指定this了,也就是handler函数里面的function定义的函数在handler方法栈运行生成且被直接调用 的时候,默认this为window,这就是为什么handler()函数里面的内嵌回调函数不能写成普通函数的原因。

十四、绑定Class样式

        DOM节点的class属性本质上是一个字符串,多个类使用空格进行隔开,空格两边就是类名,所以Vue绑定class样式的本质就是通过已有的数据和条件动态修改class属性的字符串。

        Vue绑定class的前提之一就是,对应的类选择器样式已经存在。

        情况一:添加一个class属性,使用:class="vm身上的字符串类名"就可以,最终Vue整合所有的class之后放入到DOM节点当中。这里的js表达式可以是字面量字符串也可以是vm身上的数据。

<div id="root"><!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/><!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --><div class="basic" :class="classArr">{{name}}</div> <br/><br/><!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="classObj">{{name}}</div> <br/><br/><!-- 绑定style样式--对象写法 --><div class="basic" :style="styleObj">{{name}}</div> <br/><br/><!-- 绑定style样式--数组写法 --><div class="basic" :style="styleArr">{{name}}</div>
</div>

        情况二:绑定多个类样式。这种情况下可以使用数组[]将所有的类名放在一起,然后Vue自动将里面的字符串组合在一起放在DOM节点当中。也就是:class="[vm.a,vm.b,vm.c]",v-bind遇到class时自动合成字符串。

        情况三::class="obj",根据obj对象里面的值来确定哪些类名被整合到最终的字符串当中,这个obj中的key一般是vm身上的属性,value一般是false和true。

<script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'尚硅谷',mood:'normal',classArr:['atguigu1','atguigu2','atguigu3'],classObj:{atguigu1:false,atguigu2:false,},})
</script>

十五、绑定style样式

        style样式和class样式如出一辙,最终都是变成一个字符串。但是style样式的字符串是以key:value的形式表现的。所以style通常绑定一个vm所管理的js对象。例如:style="{fontSize:xx}",其中xx是vm身上的数据。而且Key必须是小驼峰写法,默认是css的样式名称。

        第二种情形就是使用数组,例如style="[styleobj1,styleobj2]",其中styleobj1,styleobj2是vm身上的数据对象。

<script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'尚硅谷',styleObj:{fontSize: '40px',color:'red',},styleObj2:{backgroundColor:'orange'},styleArr:[{fontSize: '40px',color:'blue',},{backgroundColor:'gray'}]},})
</script>

十六、条件渲染

        条件渲染的目的就是从多个DOM节点中选择一个进行展示,这种就是v-if,v-if-else,v-else等指令。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>条件渲染</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button><h2 v-show="true">Hello,{{name}}!</h2><div v-if="n === 1">Angular</div><div v-else-if="n === 2">React</div><div v-else>Vue</div></div></body><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'jojo',n:0}})</script>
</html>

        另一种就是决定某一个DOM节点是否进行展示,这就是v-show指令。这种方式的DOM的display变成None就被隐藏。

        v-if写法:v-if="表达式" v-else-if="表达式" v-else。适用于:切换频率较低的场景。特点:不展示的DOM元素直接被移除。注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断

        v-show写法:v-show="表达式"。适用于:切换频率较高的场景。特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。

十七、列表渲染

        列表渲染的本质是将Vue管理的容器数据通过循环展示到HTML页面上。Vue管理的容器类数据包括:字符串、数组、嵌套对象等。列表渲染循环生成的同一个类型的DOM节点,然后放入到模板当中。此时就需要使用:key这个预占的属性给Vue进行区分唯一的DOM节点。

        一般语法为:(value,id)  in 容器。然后value和id就可以在模板语法当中使用。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>基本列表</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>人员列表(遍历数组)</h2><ul><li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}</li></ul><h2>汽车信息(遍历对象)</h2><ul><li v-for="(value,k) in car" :key="k">{{k}}-{{value}}</li></ul><h2>遍历字符串</h2><ul><li v-for="(char,index) in str" :key="index">{{char}}-{{index}}</li></ul><h2>遍历指定次数</h2><ul><li v-for="(number,index) in 5" :key="index">{{index}}-{{number}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{persons:[{id:'001',name:'张三',age:18},{id:'002',name:'李四',age:19},{id:'003',name:'王五',age:20}],car:{name:'奥迪A8',price:'70万',color:'黑色'},str:'hello'}})</script></body>
</html>

  v-for指令:用于展示列表数据

  1. 语法:<li v-for="(item, index) in xxx" :key="yyy">,其中key可以是index,也可以是遍历对象的唯一标识
  2. 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)

十八、Key的原理以及diff算法

        Vue的vm实例一共管理着3个DOM对象,分别是他的模板对应的真实DOM、真实DOM的备份、以及重新生成的新的DOM,后两个可以看成是虚拟DOM,只要后面两个DOM的DOM树的叶子或者节点不一样,那么就会通过渲染函数重新更新真实DOM。

        使用列表渲染时,会生成新的节点,这些节点不是一开始在模板当中写的,属于后来的DOM节点,所以需要特殊管理。

        列表渲染新生成的DOM节点一般是一棵子DOM树,使用key可以记录这棵子树的根节点,diff差分算法就是根据从这个子树的根节点开始进行对比,判断DOM节点是否发生变化。

        如果发现key不存在,直接创建新的子树。

        如果key存在,则会对子树遍历。但是对于一些真实DOM中存在的内容,比如input框里面的内容,Vue是不知道的,因为Vue只有一开始真实DOM的备份,后面这个真实DOM发生了什么变化Vue是不知道的。这就会导致真实DOM的一部分被替换掉,而另外一部分还是残留的老的真实DOM。

        如果遍历容器时,使用index作为key,则会出现部分真实DOM残留的问题。实际开发中,一般使用独一无二的index作为key。

十九、列表过滤、排序

        列表过滤和排序的本质:对容器里面的数据进行筛选,然后将筛选得到的新容器的数据放到页面上。那么进行筛选,必然会产生新的临时容器。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>列表过滤</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><ul><li v-for="(p,index) of filPersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{keyWord:'',persons:[{id:'001',name:'马冬梅',age:19,sex:'女'},{id:'002',name:'周冬雨',age:20,sex:'女'},{id:'003',name:'周杰伦',age:21,sex:'男'},{id:'004',name:'温兆伦',age:22,sex:'男'}]},computed:{filPersons(){return this.persons.filter((p)=>{return p.name.indexOf(this.keyWord) !== -1})}}})</script></body>
</html>

二十、过滤器

        过滤器本质上就是数据二次加工的一些方法函数。其目的就是在数据真正放入HTML页面之前进行最后一步处理:例如格式化字符串等。

        这些方法和函数必须注册到vm身上才能使用,有两种方法:第一种使用fliters局部过滤器。第二种:使用Vue.fliter这个API注册为全局过滤器,所有vm和vc实例都可以使用。

        Vue.fliter这个API注册过滤器函数的第一个参数是过滤名称,第二个参数是对于的回调,这个回调函数可以接受一个参数,就是管道符|前面的vm身上的数据或者字面量。

        对于局部过滤器,直接把函数名当作过滤器名称。

        过滤器的一般用法:在模板语法当作数据后面使用管道符|过滤器名称

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>过滤器</title><script type="text/javascript" src="../js/vue.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script></head><body><div id="root"><h2>时间</h2><h3>当前时间戳:{{time}}</h3><h3>转换后时间:{{time | timeFormater()}}</h3><h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3><h3>截取年月日:{{time | timeFormater() | mySlice}}</h3></div></body><script type="text/javascript">Vue.config.productionTip = false//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,11)})new Vue({el:'#root',data:{time:1626750147900,},//局部过滤器filters:{timeFormater(value, str="YYYY年MM月DD日 HH:mm:ss"){return dayjs(value).format(str)}}})</script>
</html>

二十一、Vue如何监视data配置项

        新建一个Vue实例对象时,我们传入了一个data配置对象,然后它变成了vm身上的_data对象。那么Vue到底对这个data做了什么手脚呢?原始的data对象又变成了什么样子?

        data对象里面无非状态三种东西:基本数据类型、嵌套对象、数组。首先Vue使用Observe对象不断地遍历data对象,如果他的属性是基本数据类型,那么set/get走一波。如果是数组则进行重写数组的各种方法。如果还是对象则继续重复上面的过程。

        data被Observe进行包装之后,就得到_data对象,然后原始的data又会指向_data对象。然后vm对_data对象进行数据代理。

        这样就会存在一个问题,后添加到vm身上的数据没有被set/get进行包装,不具有响应式特性。那么可以使用Vue.$set()或者vm.$set()将后面的数据变成响应式。变成响应式的过程必然要找到一个可以依赖的对象(object.defineproperty的第一个参数所决定的),那么这个可以依赖的对象不能是vm也不能是_data,更不能是window。他必须是data里面的一个对象,且这个对象必然是响应式的。

二十二、Vue如何监测数组

        一般来说,数组作为的是data对象里面的一个属性,使用Object.defineproperty将数组变成响应式的时候,只要这个数组的堆内存地址不发生改变,就无法监测数组里面的内容是否发生改变。那么Vue是如何监测数组里面内容的改变的呢?

        包装数据永不过时。对于数组对象,我们重写数组的:pop、push、shift、unshift、slice、sort、reverse方法。当vm拿到原生js数组之后,给这个数组穿上马甲,以push为例,调用push时会去找穿了马甲的数组的push方法,这个push一方面往数组里面加东西,另一方面去执行对于的渲染函数,由此实现数组的响应式。

        对于通过data配置项对象传进去的数组如此,但是对于后面添加的新的数组也是如此。

二十三、Vue.$set()方法

        Vue.set和vm.$set都可以把后面来的数据变成响应式数据,这些后来的数据包括:数组、基本数据类型、对象、多层级嵌套对象。

        这个API的原型是Vue.$set(target,key,value)。

        其中target必须是一个对象,这是由Object.defineproperty所决定的且这个target必须是vm身上的一个响应式对象。但不能是vm身上的根响应式对象,实际上可以但是Vue做了if的判断禁止这么做。

        第二个key必须是可哈希的,在js语法当种就只能是数值和字符串。

        第三个value就是key所对应的值。

二十四、收集表单的value数据  

        对于一个form表单,里面存在input、select、textarea等表单元素,对于这些DOM元素,他们是特殊的。对input来说他的value就是输入框里面的值,而且这个值还随着input的type不同而不同,例如number、password、checkbox等,那么他们的value的个数和类型也会不同。

        对于Vue的v-model来说,他主要搜集表单元素的value值,由于表单的value值个数和类型是不确定,所以vue针对不同的表单类型进行了处理,使用不同的数据类型去接收不同个数和类型的表单的value值。总结:每个表单元素都得初始化value属性,对于多个value值,vm使用数组进行接收。

        若:<input type="text"/>,则v-model收集的是value值,用户输入的内容就是value值
        若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value属性
        若:<input type="checkbox"/>:1.没有配置value属性,那么收集的是checked属性(勾选 or 未勾选,是布尔值);2.配置了value属性:v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)v-model的初始值是数组,那么收集的就是value组成的数组
        v-model的三个修饰符:lazy:失去焦点后再收集数据;number:输入字符串转为有效的数字;trim:输入首尾空格过滤。

二十五、v-text和v-html指令

        用于向其所在的DOM节点中插入文本,也就是给DOM.innertext进行赋值。赋值数据来源就是vm所管理的数据或者网络请求得到的数据。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-text指令</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><div>你好,{{name}}</div><div v-text="name"></div><div v-text="str"></div></div></body><script type="text/javascript">Vue.config.productionTip = false new Vue({el:'#root',data:{name:'JOJO',str:'<h3>你好啊!</h3>'}})</script>
</html>

        而v-html指令则是在当前节点append一个新的节点,会把数据当作元素HTML处理。

二十六、v-cloak、v-once、v-pre指令

        由于网络卡顿的原因,当HTML页面已经渲染到页面窗口,但是js脚本还没有运行,页面就会展示还没有经过Vue处理的页面。这个时候可以给vm绑定模板添加v-cloak指令,然后css样式将含有v-cloak属性的元素变为隐藏,当模板被vue接管并mount之后,删掉v-cloak属性,模板就呈现到页面之上了。

        v-once指令:当前DOM只经过vue管理一次,Vue就会放弃对这个节点的管理。

        v-pre指令:对于不包含vue模板语法的dom元素,给他们加上v-pre,那么vue就会在编译模板时忽略这些HTML元素。

v-cloak指令(没有值):

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

v-once指令:

  1. v-once所在节点在初次动态渲染后,就视为静态内容了

  2. 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

v-pre指令:

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

二十七、自定义指令directives

        通过上面对Vue指令的学习,我们可以知道Vue指令不仅能修改DOM元素innerText内容还能修改innerHTML内容,由此可知这些指令铁定拿到了对应的DOM元素。

        另外,例如v-bind指令v-bind:class="arr",指令还能根据""里面的表达式得到具体的数据。那么指令是如何运行这里面的js表达式的?

        最后,指令可以直接触摸到DOM节点,那么指令和生命周期又有什么关系呢?对于v-on绑定事件指令,所有vm对象都可以使用,那么能不能自定义只针对与某个vm自己使用的指令?答案是可以的。

        本质上来说:指令就是将vm身上的数据通过操作DOM节点,把vm身上的数据放到HTML页面的过程。而Vue是存在虚拟DOM的,所以这些指令一直操作的都是虚拟DOM,操作完毕之后再真正地展现到HTML页面当中。

        注册局部指令可以使当前的vm或者vc实例对象使用自定义指令;注册全局指令在Vue构造函数身上,所有的vm和vc实例都可以使用这些指令。

        局部指令注册方法如下:

javascript"> new Vue({															directives:{指令名:配置对象}   }) 		new Vue({															directives:{指令名:回调函数}   }) 	

        全局指令注册方法如下:所有的vm和vc都可以使用。

javascript">Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)

        有两种声明:一种是函数式声明,一种是配置对象声明。函数式声明的调用时机:1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时,也就是函数式声明只会执行bind回调函数和update函数。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>自定义指令</title><script type="text/javascript" src="../js/vue.js"></script></head><!-- 需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。--><body><div id="root"><h2>当前的n值是:<span v-text="n"></span> </h2><h2>放大10倍后的n值是:<span v-big="n"></span> </h2><button @click="n++">点我n+1</button><hr/><input type="text" v-fbind:value="n"></div></body><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{n:1},directives:{//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时big(element,binding){console.log('big',this) //注意此处的this是windowelement.innerText = binding.value * 10},fbind:{//指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}}}})</script>
</html>

        在新的DOM被放到HTML页面上之前,所有指令对虚拟DOM的操作都应该被完成。在操作DOM之前,首先要确定指令与虚拟DOM当中某个节点的联系,也就是bind,所以当指令与模板中某个DOM节点绑定时就会执行bind回调。

        由于Vue是数据驱动的,数据变化模板也会变化,所以当vm管理的数据发生变化时,对应的指令就会执行去修改虚拟DOM然后进行diff然后放到HTML页面。所以只要vm的数据发生改变,该模板上的所有指令都会被重新执行,然后得到最终的虚拟DOM。

        细说element和binding:

       这两个在回调函数当中的对象,element表示该指令所绑定的模板中的DOM节点对象,binding对象是一个上下文信息对象,他记录的有js表达式字符串和指令名称等,然后使用eval函数执行得到value值,这个value值在binding身上,即需要展现到HTML页面上的数据都在binding身上。

        对于有些页面效果或者默认行为,需要DOM节点在HTML页面当中已经呈现了才能进行展示,比如focus得等到那个节点在页面上了之后才能有获取焦点这个效果。所有指令还提供了update回调,他的作用就是当页面已经呈现到HTML页面之后执行的回调,相当于挂载DOM之后再次挂在一次,保证虚拟DOM被挂载到页面,再去执行focus获取焦点。

        细说:bind、inserted、update,首先bind、inserted、一上来就会被执行,也就是第一次生成虚拟DOM和将虚拟DOM放入HTML页面,然后后面就不会执行了,第二次、第三次把虚拟DOM放入HTML页面时,只会执行update回调函数。所以功能上inserted、update是一致的,只不过inserted只执行一次。而且这三个回调也称为钩子hook,但是与生命周期的hook有显著差异。

        一些注意点:1,模板语法当中,指令使用-分割,例如:v-big-number。但是注册指令的时候,多个单词的指令使用可哈希字符串,"big-number",且不加v。2,hook回调函数种的this皆是window而不是vm或者vc,因为数据已经通过binding传递过来了,而且element天然属于window的document,而且还能防止通过this穿透恶意修改vm中的数据。

二十八、生命周期

        生命周期就是vm实例从生成、渲染模板、销毁的整个过程,然后在一些特殊时刻调用对应的钩子函数。这些函数主要用于修改vm或者vc身上的数据,添加或者删除自定义事件等操作。由于这个原因,生命周期当中的钩子函数中的this都是vm或者vc自身,这样才能拿到vm或者vc身上的数据和其他的一些东西。

        下面的图是Vue各个生命周期所需要做的事:

        思考vm的整个流程:

        1、一切都从new Vue开始,短期目标就是成为一个数据管理大师,首先就是初始化所有的生命周期钩子函数(类似多少岁上学、多少岁结婚)这就是beforecreated;然后就是先把data穿上马甲变成自己的,再把methods穿上马甲变成自己的,这就是created,此时空有数据,但是没地方放数据。

        2、等待有缘人,等待一个模板能够被自己接管,或者被vc组件接受。

        3、成功入职。得到模板之后,把自身的数据通过各种指令生成一个虚拟DOM。等待被展示,这就是beforedmounted。

        4、得到展示到HTML页面的机会,将虚拟DOM成功放入HTML中,mounted成功。

        5、牛马一生阶段1。自己的数据不断被修改、不断地增加数据、删减数据,然后被各种指令进行修改,不断地得到新的虚拟DOM,但是还得老板同意之后才能再次放入HTML页面,这就是beforeupdated。

        6、牛马一生阶段2。老板同意修改之后的虚拟DOM上线,updated成功。然后在beforeupdated和updated不断地循环重复。

        7、被老板辞掉了。准备跟老板吵架,打算删库跑路,beforedestoryed。

        8、干不过老板,destoryed完毕。

        总结来说:Vue生命周期钩子是一些函数,他们在特殊的时刻点被自动的调用。由此可知,当vm被创建之处,必须先初始化各种钩子函数(也就是需要生成函数对象)。这些函数的作用就是在特殊时刻点执行一些动作,例如:CRUD数据、添加组件、自定义事件、删除自定义事件等。

        另外,这些生命周钩子一般当作的是Vue的配置项,与data、methods同层次。


http://www.ppmy.cn/server/22967.html

相关文章

在Ubuntu 22.04上部署WendaSNS

一、前提条件 由于WendaSNS不支持PHP8&#xff0c;因此这里再安装php 7.4版本 1. 增加ondrej/php PPA&#xff0c;提供了多个PHP 版本(会因为网络原因&#xff0c;下载较慢) sudo add-apt-repository ppa:ondrej/php 2.更新包列表 sudo apt update 3.安装 PHP 7.4 及相关…

MySQL中连接查询的几种方式

在 MySQL 中&#xff0c;连接查询是使用两个或多个表中的列之间的关联来检索数据的一种方法。MySQL 支持多种类型的连接查询&#xff0c;包括内连接、外连接和交叉连接。下面我将详细介绍每种连接查询的类型&#xff0c;并举例说明&#xff1a; 1. 内连接&#xff08;INNER JO…

三. TensorRT基础入门-TensorRT内部的优化模块

目录 前言0. 简述1.TensorRT的优化策略2. Layer Fusion3. Kernel Auto-Tuning4. Quantization总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第三章—TensorRT 基础入…

大会预告|第五届隆道数智大会重磅嘉宾阵容

5月16日&#xff0c;第五届隆道数智大会——2024数字化采购与供应链发展论坛将在北京举办。本届大会将围绕采购与供应链数字化创新发展趋势&#xff0c;聚焦AI、大数据在企业数字化转型中的应用落地&#xff0c;以深刻的产业洞察、案例型技术和实践探讨&#xff0c;全面展示数智…

CAPS Wizard for Mac:打字输入辅助应用

CAPS Wizard for Mac是一款专为Mac用户设计的打字输入辅助应用&#xff0c;以其简洁、高效的功能&#xff0c;为用户带来了全新的打字体验。 CAPS Wizard for Mac v5.3激活版下载 该软件能够智能预测用户的输入内容&#xff0c;实现快速切换和自动大写锁定&#xff0c;从而大大…

#天空星RTC

一、选择时钟源为LSI 二、频率为32.768kHz 三、配置注意&#xff1a; 1.电源管理时钟 2.RTC备份寄存器&#xff08;每次上电先检测RTC之前是否初始化过&#xff09; 3.时钟源:LSE or LSI 4.写保护 5.编辑模式 6.日期时间获取 四、代码 /** 立创开发板软硬件资料与相关扩…

Java知识点补充

反向代理的作用是什么&#xff1f; 是位于客户端和服务端之间一个代理模型&#xff0c;接受客户端的请求&#xff0c;将其转发到后端服务中&#xff0c;然后将后端响应传回客户端。 负载均衡&#xff1a;减少单个服务器之间的压力&#xff0c;将流量重定向到多个服务器中常见的…

C语言例题30:将一个正整数分解质因数

#include <stdio.h>void main() {int i;int x;printf("请输入一个正整数&#xff1a;");scanf("%d", &x);printf("%d ", x);//方法一&#xff1a;for (i 2; i < x; i) { //除数&#xff0c;从质数2开始while (x % i 0) { //能…