Vue核心

ppgo8 于 2023-05-30 发布

Vue核心

Vue简介

Vue是什么

一套用于构建用户界面渐进式JavaScript框架。

谁开发

image-20230530171251337

Vue的特点

学习Vue之前掌握的JavaScript的基础知识

ES6语法规范、ES6模块化、包管理器、原型和原型链、数组常用方法、axios、promise


总结

  1. Vue的特点?
    • 组件化模式
    • 声明式编码
    • 虚拟DOM+DIFF技术

Vue官网指南

搭建Vue开发环境

https://v2.cn.vuejs.org/v2/guide/installation.html#%E7%9B%B4%E6%8E%A5%E7%94%A8-lt-script-gt-%E5%BC%95%E5%85%A5

image-20230530175202429


总结

开发环境搭建流程:

  1. 下载开发版本Vue
  2. 安装Vue开发者工具
  3. 设置Vue config,关闭提示

初识Vue

hello案例

live server 运行html文件,会在本地创建一个小服务器,端口是5050.

运行后,会自动发送一个浏览器图标的请求。

image-20230530200922460

将数据存在Vue实例中不写死的好处:当数据发生变化,不需要操作DOM,DOM的内容会自动改变。

把数据交给绿色的框框。


总结

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

容器和实例的对应关系

只能一对一,一对多多对一均不行。

image-20230530202608566

image-20230530202617603

容器有很多数据,那实例数据需要自己就需要写100个吗?

image-20230530202902106

容器``里包裹的是JS表达式

注意区分JS表达式和JS代码(语句):

JS表达式是特殊的JS代码,不能用来赋值,即不能写在赋值运算符右边

判断this指针是否指向实例对象的方法

this instanceof 构造函数

模板语法

Vue容器中的代码被称为【Vue模板】,那么就有对应的Vue模板语法

模板语法分两类:插值语法和指令语法

插值语法

指令语法V-???

举例

<!-- 标签内的内容依然需要符合html语法:url依然需要添加双引号-->
<a v-bind:href="url" >点我前往尚硅谷</a> 
<script>
        new Vue({
            el:'#root',
            data:{
                url:'http://www.atguigu.com'
            }
        })
</script>

数据绑定

单项数据绑定

双向数据绑定


总结

Vue中有2中数据的绑定方式:数据绑定是写在容器中的,实例可能需要提供数据值.

  1. 单项绑定v-bind:数据只能从data流向页面

  2. 双向绑定v-model:数据不仅能从data流向页面,还可以从页面流向data

  3. 备注:

    • 双向绑定一般用在表单元素上

      (如input、select)

      因为有value属性才能捕获到用户的输入,进而影响数据的变化

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

    • 注意区分不同的简写方式:

      v-bind简写方式:

      v-model:value简写方式v-model

el与data语法

vm的属性

el绑定容器

data数据

如何选择

目前使用哪种都可以,以后学习组件时,data必须用函数式,否则会报错。

重要原则

由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再指向Vue实例

MVVM

因此,以后都将Vue实例对象命名为vm


总结

数据代理

Object.defineProperty方法

image-20230531163023820

注意

通过defineProperty添加的属性默认不能遍历(枚举)

如果想遍历该属性,需要设置配置对象:

https://www.bilibili.com/video/BV1Zy4y1K7SH/?p=11&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705 重新看

image-20230531163944678

数据代理

数据代理定义

Vue中数据代理

https://www.bilibili.com/video/BV1Zy4y1K7SH/?p=13&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705

image-20230531175418675

注意: vm._data = data

  1. Vue中的数据代理:

    通过vm对象来代理data对象中属性的操作(读/写)

  2. Vue中数据代理的好处:

    更加方便的操作data中的数据

  3. Vue数据代理基本原理:

    • 通过Object.defineProperty()把data对象中所有属性添加到vm上
    • 为每一个添加到vm上的属性,都指定一个getter/setter
    • 在getter/setter内部去操作(读/写)data中对应的属性

事件处理

事件基本使用

https://www.bilibili.com/video/BV1Zy4y1K7SH/?p=14&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705

代码没写

事件的基本使用:

  1. 使用v-on=xxx@xxx绑定事件,其中xxx是事件名。
  2. 事件的回调需要配置的methods函数中,最终会在vm上。
  3. methods中配置的函数,不要用箭头函数!否则this的指向就不是vm了。
  4. methods中配置的函数,都是被Vue所管理的函数,this的指向都是vm或组件实例对象。
  5. @click=’demo‘@click="demo($event)"效果一致,但后者可以传参。

事件修饰符

​ Vue中的事件修饰符:

  1. prevent:阻止默认事件,如点击a标签跳转

  2. stop:阻止事件冒泡

  3. once:事件只触发一次

  4. capture:使用事件捕获方式,即在事件捕获阶段触发事件,默认是在事件冒泡阶段触发。

  5. self:只有event.target是当前操作的元素时才触发事件

    `e.target`是不会随着冒泡改变的,只会是用户点击操作的那个元素。

    使用:从某种程度来说也能阻止冒泡,但是用的不多。

  6. passive:事件的默认行为立即执行,无需等待事件回调操作。

    滚动事件补充:

    • scroll滚动事件:滚动条滚动,键盘上下按钮和鼠标滚轮滚动都算;

    • wheel滚动事件:鼠标滚轮滚动事件,只要鼠标轮在动,就会触发;键盘上下操作滚动条不触发。

      • 当使用wheel事件时,首先执行回调函数,回调函数结束后,才执行默认事件:滚动条向下滑动
      • 不是所有事件都存在这个问题,scoll就没有这个问题。所以也不会很常用。

    事件默认行为与事件回调之间的执行关系

    默认情况下:事件回调函数执行完,事件默认行为才会发生

    • 适用场景:passive在移动端用的场景比较多。

键盘事件

  1. 键盘事件语法

    • keyup抬起键盘

    • keydown按下键盘

    • 语法

      • @键盘事件=回调函数,不区分按键
      • @键盘事件.指定按键名/vue提供的别名=回调函数,区分按键
      // 当抬起回车键时,执行showInfo
      @keyup.enter="showInfo"
      
  2. Vue中常见的按键别名

    // Vue提供的别名,可以代替键盘原本的名字
    回车:Enter
    删除:delete(捕获"删除"和"退格")
    退出:esc
    空格:space
    换行:tab //特殊:tab必须配合keydown使用;因为tab是切换走,如果keyup抬起来的时候已经离开了元素,检测不到。
    上:up
    下:down
    左:left
    右:rigjt
    
  3. Vue中未提供的别名的按键,可以使用按键原始的Key值来绑定,但注意要转换为短横线命令

    e.key // key获得键盘名
    // 小驼峰的key,转换为短横线命名:
    @keydown.caps-lock=回调函数  //把按键字母小写,用-连接
    
  4. 用法特殊的键:系统修饰键ctrl/alt/shift/meta

    • 配合keydown使用:正常触发事件。
    • 配合keyup使用:单按没用。按下修饰键的同时,再按下其他键,随后释放其他键,事件被触发。

计算属性与监视属性

计算属性computed

函数是否写括号?

  1. 事件回调写函数,函数可以写不写小括号都代表调用函数.
  2. 插值语法写函数
    • 写小括号代表调用函数,结果是返回值;
    • 不写小括号代表函数自身。

只要data中的数据发生改变,就一定会重新解析模板模板各个方法重新调用一遍


总结

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

  2. 原理:底层借助了Object.defineProperty方法提供的getter和setter

  3. 计算属性中get函数什么时候执行?
    • 初次读取时会执行一次;

      如果后面还有且依赖数据没变化,会走缓存,不再重新调用。

    • 当依赖的数据发生改变时会再次调用

    <!-- fullName也在vm身上,直接使用插值语法获得 -->
     	全名:<span></span> <br />
     	全名:<span></span> <br />
        全名:<span></span> <br />
        全名:<span></span> <br />
    <!-- 以上有四个fullName但是只调用了一次fullname -->
    
  4. 优势:

    • methods相比,内部有缓存机制,实现复用,效率更高。

      methods方法没有缓存,写了几个就要调用几次

    • 计算过程可见,调试方便。

  5. 备注:

    • 计算属性最终会挂在在vm上,直接写计算属性名字即可

      原因:底层会自动调用get,把get方法返回值返回给对应的计算属性

    • 如果计算属性要被修改,那必须写set函数去响应修改;

      且set要把接收到新值 反应到 所依赖的数据上!

监视属性watch

基础知识

监视属性watch

  1. 执行时机:当监视属性发生变化时,回调函数自动调用,执行相关操作

  2. 监视属性必须存在才能监视!!!不报错,但是不存在的属性新旧值都是undefined

  3. 可监视的属性:data中属性计算属性都可以

  4. 监视属性的两种写法:

    • new Vue时传入watch配置

      watch: {
          info: {
              // 初始化时让handler就调用一次
              immediate: true, 
              // 当info发生改变时,handler自动调用
              // 第一个参数新值,第二个参数之前的值
              handler(newValue, oldValue) {
                  console.log('info被改变了', newValue, oldValue)
              }
          }
      }
      
    • 通过vm.$watch('属性名',{回调函数})

      ```js vm.$watch(‘isHot’, { // 回调函数的简写形式 handler(newValue, oldValue) { console.log(‘ishot被改变了’, newValue, oldValue) } })

​ 配置项immediate: true,初始化时执行一次。

深度监视

深度监视,配置项deep:ture

备注

代码举例

const vm = new Vue({
    el: "#root",
    data: {
        isHot: true,
        numbers: {
            a: 1,
            b: 2,
        }
    },
    // 监视
    watch: {
        // 监视多级对象结构中某个属性的变化
        'numbers.a': {
            handler() {
                console.log('a改变了');
            },
        },

        // 监视多级结构中所有属性的变化
        numbers: {
            deep: true,
            handler() {
                console.log('numbers里面的有东西改变了');
            }
        },
    }
})

注意:

监视简写

前提:没有配置项immediate: truedeep:ture时,监视属性可以简写。

1.在watch属性中的简写

简写方法:函数名是监视的属性,(){}里面按照原来的写法写

// 监视
watch: {
    // 正常写法
    isHot: {
        handler(newValue, oldValue) {
            console.log('isHot被改变了', newValue, oldValue)
        }
    },
        
    // 简写形式
    // 函数名:监视的是谁
    isHot(newValue, oldValue) {
        console.log('简写形式isHot被改变了', newValue, oldValue)
    }
}

2.在Vue实例化后的简写vm.$watch()

简写方法:第二个参数写成匿名函数,(){}里面内容按原来的方法写

 // 正常写法
vm.$watch('isHot', {
    handler(newValue, oldValue) {
        console.log('ishot被改变了', newValue, oldValue)
    }
})

// 简写
vm.$watch('isHot', function (newValue, oldValue) {
    console.log('实例化方式ishot被改变了', newValue, oldValue)
})

区分计算属性和监视

有些需求计算属性和监视属性都能做,如根据姓和名拼出全名的案例。


总结

Vue绑定class与style

细节:

vue模板中,元素标签内的属性前添加了vue指令,如冒号:,那么等号后面引号里面的内容就是JS表达式。引号的里面要用JS表达式去分析,是字符串还是变量

下面的两种写法就是完全不同的意义:

// []里面的东西被写死了,vue不会帮我们保存
<div class="basic" :class="['atguigu1','atguigu2','atguigu3']"></div> <br />
// []里面的东西是变量,会去data里面寻找变量对应的值
<div class="basic" :class="[atguigu1,atguigu2,atguigu3]"></div> <br />

image-20230607190455418

条件渲染

image-20230607195152681

template结合v-if

列表渲染

遍历列表

image-20230612104406585

key原理

key的原理

虚拟DOM的对比算法:对比的是虚拟DOM,不是换整个虚拟DOM,每个key下的虚拟DOM有哪部分没变,就可以继续复用,针对的单元很小,只要有不变就复用,

VUE的操作流程:初始数据 =>虚拟DOM =>真实DOM

用户接触到的是真实DOM

使用index作为key:如果对数据进行了破坏顺序的操作,如在最前面插入新数据(最后面插入新数据不会影响旧的数据),那么就会出问题。

新的数据会影响之前存在的数据的key,

image-20230612110419761

image-20230612110444731

不写key,那么vue会把遍历的索引值自动作为数据的key。所以也可能出问题。

image-20230612110758885

从后台请求过来的数据 不可能没有数据的唯一标识,不然后端不合格。

列表过滤

使用场景:筛选

p.name.indexOf(“”) //返回0

所有字符串都包含空字符串

监视属性可以直接使用参数获得用户输入的新值和旧值

计算属性需要使用data的数据属性中获得用户输入的新值

监视属性:默认当监视属性的值发生变化的时候执行,可手动设置刚开始就执行一次

计算属性handler调用时机:一开始的时候自动调用一次,当依赖的属性值发生变化的时候再执行

总结:监视属性和计算属性都能做,优先选择计算属性。

列表排序

升序和降序都是自上而下的:从上面到下面

数组方法

filter

sort 返回值,改不改变原数组,必须马上反应过来

https://www.bilibili.com/video/BV1Zy4y1K7SH?p=33&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705

Vue检测数据发生变化

检测对象改变

数据代理:

源代码中vue实例中的data属性,挂载在vm的_data上vm._data=data,将_data中的一级属性挂载在vm上

image-20230612154433296

在进行vm._data=data之前,底层还做一步操作:加工data。加工一下就可以做响应式了,getter和setter。

https://www.bilibili.com/video/BV1Zy4y1K7SH?p=34&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705 重看,没看懂 35-37都没看

检测数组发生改变

收集表单数据

   <form action="">
            <label for='demo'>账号:</label>
            <input type="text" id="demo">
   </form>

label for 作用:点击账号文字,用户也可以获得后面表单控件的焦点

禁止表单的自动提交:(自动提交会刷新页面,表单内容会消失)

1.button默认是submit类型,改成button类型,就不会提交了

<button type="button">提交</button> 

2.阻止form的默认事件,给button添加不要给整个form添加否则会有意想不到的影响

 <button type="submit" @click.prevent >提交</button>

612d03e37ba26bf8b578eefd18f44bd

过滤器

计算属性也能做,methods也能做,所以这个不是强制的。

BootCDN https://www.bootcdn.cn/ 这个网站里面有一些好用的JS库

过滤器就是一个函数

在模板中 传入的数据实参 过滤器函数名
 <!-- 过滤器实现 -->
<!-- 底层:过滤器函数可以额外传入其他配置参数,但是在Vue实例中,第一个形参是过滤器函数左边的值;后面的参数才是传入的其他实参值 -->
<!-- 多个过滤器之间可以串联  -->

<h3 >现在是:</h3>  

在Vue实例中,使用fileters属性,配置下面的函数

filters:{
   // value:传入的参数值
    timeFormater(value){
        // 代码块,返回值会渲染到插值语法中
    }
}

image-20230612194737611

time实参传递的方式,先传给第一个过滤器函数,再将第二个过滤器的返回值传给下一次

谁可以使用Vue中的过滤器,只有这个Vue实例绑定的容器,才可以使用该实例中的过滤器。

全局作用器,配置了可以给任何一个容器使用

Vue.filter

Vue.filter('过滤器函数名',function(value){
    return 返回值  // 返回值会被渲染到模板代码中
})

过滤器可以用在插值语法中和V-bind中,v-model不行

也可以用在下面的方法中

image-20230612195634663


总结

image-20230612195711220

复杂的应该用计算属性或methods

Vue指令

常用内置指令

常用指令如下:

  1. 更新标签中的元素内容

    • v-text 不解析html标签

    • v-html 解析html标签

  2. 条件渲染

    只有符合条件时,标签才会渲染到页面

    • v-ifv-else 动态控制节点是否存在

    • v-show 动态控制节点是否显示

  3. 编译数组/对象

    可以遍历后生成指定个数的标签

    • v-for
  4. 绑定事件监听,一般简写为@

    • v-on
  5. 数据绑定

    • v-bind 单向数据绑定

    • v-model 双向数据绑定

  6. 其他

    • v-cloak 防止闪现,与css配合[v-cloak]{dispaly:none}

v-text

v-text使用拿到的属性值,替换元素标签里的全部内容,不解析html标签,全部当做文字

插值语法也不会解析

    <div id="root">
        <div></div>
        <div v-text="name"></div>
    </div>

image-20230612200446722

v-html

和v-text的区别是会解析html标签,可能会有安全问题

image-20230612203039757

XSS攻击image-20230612203108865

v-cloak指令

这个指令没有值,只有名。

当Vue未开始渲染时,模板代码中的元素会有v-cloak属性,当Vue开始渲染时,v-cloak属性消失。

作用:和CSS结合,让未渲染的模板代码不显示。

不显示含有v-cloak的元素标签
[v-cloak]{
    dispaly:none
}

https://www.bilibili.com/video/BV1Zy4y1K7SH/?p=42&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705

v-once指令

v-once作用:

image-20230612205655740

<!-- 准备好一个容器 -->
<div id="root">
    <h2 v-once>初始化的n值是:</h2>
    <h2>当前的n值是:</h2>
    <button @click="n++">点我n++</button>
</div>

<!-- Vue实例 -->
<script>
    new Vue({
        el: "#root",
        data: {
            n: 1,
        }
    });
</script>

v-pre

image-20230612210148200

v-pre可以给没有使用插值语法、指令语法的节点,可以加快速度。

注意和once区分

Vue中事件 使用@

image-20230612210306408

自定义指令

用自定义指定的简写方式:其实就是指定了bind和update,没有指定inserted

// 写自定义指令
directives: {
    // big函数什么时候被调用?1.指令与元素成功绑定时(一上来就执行)2.指令所在的模板被重新解析时。
    // 第一个参数:真实dom元素
    // 第二个参数:对象,里面有很多属性;元素和指令之间的绑定关系
    // 使用JS语法简写
    big(elemnet, binding) {
        // console.log(elemnet, binding);
        elemnet.innerText = binding.value * 10
    },
    // fbind不能写成函数,需要用最完整的对象写法
    fbind: {
        // 指令与元素成功绑定时(一上来)
        bind() {

        },
        // 指令所在元素被插入页面时
        inserted(element, binding) {
            element.focus()
            element.value = binding.value
        },
        // 指令所在模板被重新解析时
        update(element, binding) {
            element.focus()
            element.value = binding.value
        },
    }
}

注意事项

image-20230612220901089

image-20230612221107737

image-20230612221621383

生命周期

基础知识

<div id="root">
    <h2 :style="{opacity}">欢迎学习Vue</h2>
       // 会被重新解析无数次,change重新调用无数次
</div>

new Vue({
    el: "#root",
    data: {
        opacity: 1,
    },
    methods: {
        change() {
            setInterval(() => {
                this.opacity -= 0.01
                if (this.opacity <= 0) {
                    this.opacity = 1
                }
            }, 16)
        }

    },
});

上面的是垃圾代码,模板会无数次解析,change会无数次调用,开启了无数个计时器

原因:每次data中的数据opacity发生变化都会重新解析模板

重要思想

每次data中的数据发生改变,Vue就会重新解析模板

JS表达式代码 对象简写形式。

如果对象值和属性相同,那么可以只写属性,不写值。

别人访问这个属性名就会返回这个属性的值。

细节,1.定时器中用箭头函数 2.小数的判断条件 小于等于0而不是 等于0因为有可能错过=0这个点

变化的东西交给JS处理。

 <script>
        const vm = new Vue({
            el: "#root",
            data: {
                opacity: 1,
            },
            methods: {
                change() {
                    // 箭头函数没有自己的this,所以这里的this是change函数的this就是Vue
                    setInterval(() => {
                        this.opacity -= 0.01
                        // 写<=0是因为JS处理小数不灵活。可能没等于0,直接就跳到比0小的地方了
                        if (this.$listeners.opacity <= 0) {
                            this.opacity = 1
                        }
                    }, 16)

                }
            }
        });

    </script>

undefined不显示在页面上

Vue模板工作流程:代码解析为虚拟DOM,虚拟DOM转换成真实DOM,把真实DOM放在模板上。

// 什么时候调用?
// Vue完成模板解析 并把真实的DOM元素放入页面后(挂在元素)调用mounted
// 只调用一次,他是一个
mounted() {
    setInterval(() => {
        this.opacity -= 0.01
        if (this.opacity <= 0) {
            this.opacity = 1
        }
    }, 16)
}

总结

生命周期(其实是一个简称)

  1. 又名:生命周期回调函数、生命周期函数、生命周期钩子(程序员交流常用)
  2. 本质函数,Vue在关键时刻帮程序员调用的一些特殊名称的函数
  3. 生命周期函数的名字不可更改,但函数体的内容可以根据程序员需求编写的。
  4. 生命周期函数中的this指向的是vm或组件实例对象。

分析生命周期

红色方框里面的内容是生命周期;蓝色阴影的部分是Vue实例化的环节.

生命周期

挂载流程

截止到mounted就是挂载流程.

挂载流程时不涉及新旧虚拟DOM的比较,因为都是最新的,没有改变;在更细的时候才有虚拟DOM比较.

更新流程

只要data数据改变就会更新

销毁流程

先调用vm.$destroy()方法,才会走销毁流程;调用该方法后,vm就会自我销毁.

生命周期小结