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

目录

一、入门案例

二、模板语法

三、数据绑定

四、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 = false
		
    const 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 = false
		
    const 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 = false

		const 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 = false
			
			new 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 = false

			new 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实例都可以使用这些指令。

        局部指令注册方法如下:

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

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

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 = false

		new Vue({
			el:'#root',
			data:{
				n:1
			},
			directives:{
                //big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
				big(element,binding){
					console.log('big',this) //注意此处的this是window
					element.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同层次。

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

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

相关文章

【echarts】数据起点不从X轴的原点开始【不从0开始】

echarts折线图x轴不从0开始怎么办&#xff1f; 或者说为什么有些图是这样的 有些却是这样的 原因出在这里&#xff1a; boundaryGap: false 默认是true&#xff0c;是指坐标轴两边留白。改为false&#xff1a;不留白即从原点开始。 看一下官方的说明

中小型企业网络实战topo

1、设备命名&#xff0c;务必按照规范进行命名规划&#xff1b; 2、子网划分&#xff0c;申请到了公网地址段&#xff0c;201.1.1.0/24&#xff0c;根据公司的实际情况&#xff0c;合理规划拓扑需要的公网地址&#xff0c; 做到合理规划不浪费&#xff1b; 3、子网划分&a…

嵌入式开发学习--进程、线程

什么是进程 进程和程序的区别 概念 程序&#xff1a;编译好的可执行文件&#xff0c;存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09;&#xff0c;程序是静态的&#xff0c;没有任何执行的概念。 进程&#xff1a;一个独立的可调度的任务&#xff0c;执行一…

做抖音小店如何选品?这几个技巧,精准“锁定”爆品!

哈喽~我是电商月月 做抖音小店最重要的就是选品&#xff0c;这点大家都知道 一个店铺商品选的好&#xff0c;顾客喜欢&#xff0c;质量完好&#xff0c;销量和售后都不用操心&#xff0c;和达人合作时&#xff0c;爆单的机会也就越高 那这种商品是什么样的&#xff0c;新手开…

基于ssm微信小程序的4S店客户管理系统

采用技术 基于ssm微信小程序的4S店客户管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 管理员端 管理员登录 管理员首页 用户管理 门店管理 …

RustGUI学习(iced)之小部件(一):如何使用按钮和文本标签部件

前言 本专栏是学习Rust的GUI库iced的合集&#xff0c;将介绍iced涉及的各个小部件分别介绍&#xff0c;最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个&#xff0c;目前处于发展中&#xff08;即版本可能会改变&#xff09;&#xff0c;本专栏基于版本0.12.1. 概述…

高效一键改写文章,智能伪原创工具轻松搞定

在信息爆炸的时代&#xff0c;想要高效率的一键改写文章却是很多创作者都想了解的方法。然而在人工智能技术发展的今天&#xff0c;智能伪原创工具的出现&#xff0c;也正是成了广大创作者用来一键改写文章的好方法&#xff0c;因为它的优势&#xff0c;可以为大家轻松完成改写…

光伏二次设备主要有哪些

光伏电站二次设备类型比较多&#xff0c;信息显示、数据安全、远动通信、电能质量、微机保护等都有不同设备相互配合完成&#xff0c;根据项目具体需求来选择&#xff0c;简单可以分为以下几种&#xff1a; 一、光伏二次设备保护屏&#xff1a; 1、光伏二次设备预制舱 二次设…

短视频矩阵系统源码====3年技术公司源头开发商交付

短视频矩阵系统#源头技术打磨 哈尔滨爆火带动了一波“北上热潮”&#xff0c;各地文旅坐不住了&#xff0c;兄弟们开“卷”&#xff01;这波互卷浪潮中&#xff0c;河南率先出圈。如今&#xff0c;河南文旅账号粉丝已经突破200w&#xff01; 01 矩阵打法&#xff0c;很难不火…

超越边界:如何ChatGPT 3.5、GPT-4、DALL·E 3和Midjourney共同重塑创意产业

KKAI&#xff08;kkai人工智能&#xff09;是一个整合了多种尖端人工智能技术的多功能助手平台&#xff0c;融合了OpenAI开发的ChatGPT3.5、GPT4.0以及DALLE 3&#xff0c;并包括了独立的图像生成AI—Midjourney。以下是这些技术的详细介绍&#xff1a; **ChatGPT3.5**&#xf…

Lab2: system calls

Using gdb Looking at the backtrace output, which function called syscall? 可以看到是trap.c中usertrap函数调用了syscall函数 What is the value of p->trapframe->a7 and what does that value represent? p->trapframe->a7的值为7&#xff0c;代表了函…

MATLAB 条件语句

MATLAB 条件语句 决策结构要求程序员应指定一个或多个要由程序评估或测试的条件&#xff0c;如果确定条件为真&#xff0c;则应指定要执行的一个或多个语句&#xff0c;如果条件为真&#xff0c;则可以选择要执行的其他语句。条件确定为假。 以下是大多数编程语言中常见的典型…

我们支持批量了!

当我们有很多文件要处理时&#xff0c;一个一个操作不仅费时费力&#xff0c;而且还容易漏掉某个文件。那么有没有更加简单的方法呢&#xff1f;可以试试批量功能&#xff01; 目前以下功能都支持批量操作 音频提取 视频格式转换 字幕生成 视频静音 图片格式转换 图片压缩 视频…

JavaScript —— APIs(四)

一、日期对象 1. 实例化 new 括号里面为空&#xff0c;得到当前时刻的时间 括号里面为指定日期&#xff0c;得到对应日期的时间 注意&#xff1a;若括号里面只有年月日&#xff0c;没有时间&#xff0c;则得到的结果就没有时间&#xff1b;括号里面指定时间是多少&#xff0c;…

身份证二要素核验介绍及使用方法

一、身份证二要素核验简介及重要性 身份证二要素核验是一种重要的身份验证技术&#xff0c;它在现代社会中发挥着至关重要的作用&#xff0c;特别是在涉及个人信息安全和隐私保护的领域。通过身份证二要素核验&#xff0c;我们可以有效地确认个人身份的真实性&#xff0c;从而…

爬虫学习笔记-数美验证

测试网址&#xff1a;智能验证码体验_图片验证码_数美科技数美科技智能验证码在线体验&#xff0c;智能识别风险用户级别&#xff0c;自行切换智能验证码难度及类型&#xff0c;提供滑动、拼图、点选、数字、动态等多种智能验证码服务&#xff0c;精准拦截机器行为。https://ww…

SOLIDWORKS Composer如何使用3D工具实现更真实的动画效果

当我们使用SOLIDWORKS composer创建动画时&#xff0c;往往会涉及到产品的安装与拆解&#xff0c;现实生活中我们在拆卸组装产品的时候&#xff0c;我们往往需要一些工具的协助&#xff0c;比如扳手、螺丝刀等等&#xff0c;那么我们如何在虚拟动画中也将这一过程以逼真的形式展…

新建云仓库

1.GitHub新建云仓库&#xff1a; LICENSE:开源许可证&#xff1b;README.md:仓库说明文件&#xff1b;开源项目&#xff1b;cocoaPodsName.podspec: CocoaPods项目的属性描述文件。 2.Coding新建云仓库&#xff1a; 备注&#xff1a; Coding新建项目&#xff1a;

STM32,复位和时钟控制

外部时钟 HSE 以后需要用到什么就这样直接拿去配就行了

左叶子之和(力扣404)

解题思路:用后序遍历找左孩子&#xff0c;需要注意的是左叶子需要通过其父节点来判断其是不是左叶子 具体代码&#xff1a; class Solution { public: int sumOfLeftLeaves(TreeNode * root) { if(rootNULL)return 0; if(root->leftNULL&&root->rightNULL)ret…