vue3 技术整理(基础)


0、前言

vue2基础篇链接:https://www.cnblogs.com/xiegongzi/p/15782921.html

vue2组件化开发篇链接:https://www.cnblogs.com/xiegongzi/p/15823605.html

vue2高级篇链接:https://www.cnblogs.com/xiegongzi/p/15875808.html


1、创建vue3项目

官网文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

1.1、使用vue-cli创建

注意:需要保证自己的vue-cli版本在4.5.0以上

查看自己@vue/cli版本
Vue -V

安装或升级@vue/cli
npm install -g @vue/cli

创建Vue3项目
vue create 项目名

使用ui可视化界面创建
ps:会在浏览器中打开一个管理窗口,进行手动创建和配置选项,目前不建议用,学完所有就可以使用了
vue ui

启动Vue3项目
cd 进入到创建的项目中
npm run serve

image

image

image

image


1.2、使用vite创建

官网地址;https://v3.cn.vuejs.org/guide/installation.html#vite

image

vite是另一门技术,是下一代的前端构建工具
ps:是vue官网打造的,久一代的就是webpack 】。vite官网地址 https://vitejs.cn/

vite的优势:

  • 开发环境中,无需打包操作,可快速的冷启动
  • 轻量快速的热重载( HMR ) ps:webpack也可以热部署,只是vite更轻量和快
    • 快体现在热部署的方式不同 ps:官网中有对比图

image

  • 真正的按需编译,无需等待整个应用编译完成再启动【 ps:图中我解读的哪里,是根据路由再找模块 】

整理vite创建vue3的相关指令

创建项目
npm init vite-app 项目名

进入项目目录
cd 项目名

安装依赖
npm install

运行项目
npm run dev

image

image

image

image


2、分析vue-cli创建的vue3项目

使用vscode打开vue-cli创建的项目

2.1、查看整个目录结构

image

image


2.2、分析入口文件main.js

image

其他的东西和vue2中没什么两样


注意点:template中的写法和vue2相比有点变化

image


3、安装vue3的开发者工具

注意:这里安装的测试版(beta)

直接使用google进行安装即可,要是无法进入google应用商店的话,那么:去百度chrome插件网就可以了;或者:下载一个佛跳墙 / 极光
ps:佛跳墙现在的名字貌似改为极光了,下载安装之后,直接链接,然后就可以FQ了,当然还有其他更好的F墙方式


注意:vue2和vue3的开发者工具最好别一起开启,开了vue2就别开vue3,开了vue3就别开vue2,很容器出问题


4、认识基本的Composition API / 组合式API

4.1、认识setup函数

setup是Compostition API / 组合式API的地基,setup是一个函数,且必须有返回值,玩vue3,那么就需要提供一个平台,而这个平台就是setup


4.1.1、对setup函数快速上手

<template>
  <!-- 这里面能够拿到以下的东西,全靠的是setup的return返回回来的结果 -->
  <h1>姓名: {{name}}</h1>
  <h1>性别: {{sex}}</h1>
  <h1>工种: {{job}}</h1>

  <br>
  <br>

  <button @click="helloword">调用一下vue3的setup中定义的方法</button>
</template>

<script>

export default {
  name: 'App',

  // 一、配置setup平台
  setup(){
    // 这里面的配置和vue2中的差不多,什么数据、方法、计算属性、生命周期.......只是写法有区别

    // 配置数据【 ps:直接定义即可 】
    let name = '紫邪情';
    let sex = '女';
    let job = 'Java';

    // 配置方法
    function helloword(){
      // 注意点:alert()里面用的是模板字符串 - 飘字符嘛;${name}就是取上面定义的数据
      alert(`我叫: ${name},性别: ${sex}.工种: ${job}`)
    }

    // setup必须有返回值 return - 就是为了把setup配置的东西交出去嘛,不然别人怎么拿到
    // 但是:return有两种写法

    // 1、对象写法
    return {
      // 返回数据
      name,sex,job,

      // 返回方法
      helloword
    }
  }
}
</script>

image


补充:setup函数返回值的另一种写法:返回渲染函数写法 - 了解即可【 ps:这是为了自定义渲染内容的 】

第一步:引入渲染函数h,指令:import {h} from 'vue'

第二步:使用渲染函数 并 返回

// 2、第二种写法:返回渲染函数 - 了解即可
// 这种写法:会将下面自定义写的渲染内容 放到 前面template中去渲染
// 即:template的渲染依赖于下面自定义内容

// 第一步:需要在本组件中引入渲染函数h
// 第二步:使用渲染函数 并 返回
// return ( (h) => h( 'h1', '这是setup中的返回渲染函数用户') )
// 简写
return () => h('h1','这是setup中的返回渲染函数用户')

image

image


4.1.2、聊聊setup函数的细节问题

setup和vue2的写法一起用 - 最好:坚决别用

image

image

原因:是因为vue3向下兼容嘛,所以:可以加入vue2的写法

但是:说过不建议vue3和vue2混合使用。来演示一下bug

image

image

注意:反过来就可以啊,即:在vue2的配置中可以获取setup函数中配置的东西,演示就跳过了

另外还有一个注意点:如果采用vue2的配置和vue3的setup中配置了相同的东西,那么:优先使用setup中的,如:在vue2的配置中配置了address数据,又在vue3的setup中配置了address数据,那么:优先使用的是setup中的


setup()的另外一个注意点 和 其可以接收的两个参数 - 演示自行玩

setup执行时机:

  • 在beforeCreate之前执行一次,同时在setup(){ }中的this是undefined,即:在setup中不可以用this拿到东西 【 ps:可以试着定义一个beforeCreate和setup,里面都输出一句话,看控制台谁的话在前面即可 】

setup可以接受的两个参数:

  • props:值为对象,包含:组件外部传递过来 且 组件内部声明接收了的属性 【 ps:也就是在组件内部和vue2一样配置了props配置项 - 有三种配置方式,vue2的基础知识,滤过了 】

    • 注意点:若在外部传递了数据,而内部没有配置props配置项进行声明接收,那么:vue2中不会有什么错误,但是:在vue3中就会在控制台抛错
  • context:上下文对象 ps:它里面有三个属性

    • attrs:俗称捡漏王。值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于vue2中的this.$attrs
      • 换言之:就是如果父组件传递了数据,但:子组件中的props配置没有声明要接收,那么:传递的数据就在子组件的attrs属性上
    • slots:看名字就知道,就是收到的插槽内容,相当于vue2中的this.$slots
    • emit:也是看名字就知道的,触发自定义事件的函数嘛,相当于vue2中的this.$emit
      • 但是:注意在vue3中,这个东西根据vue2的正常写法,好使,可是:控制台会抛警告,不想看到警告,那就在使用了这个emit的组件中,配置一个emits配置项即可 - 和props声明接收属性的配置一样,如:emits: ['getField']

4.1.3、vue3的setup函数总结

所谓的setup,就是vue3中的一个全新配置项而已,值是一个函数,它是vue3中Composition API / 组合式API的地基,且这个setup函数必须有一个返回值

组件中所用的:数据、方法、计算属性、生命周期等,均可配置在setup函数中【 ps:注意写法不太一样 】

setup函数的两种返回值写法:

  • 1、返回一个对象,若是这种:那么对象中的属性、方法等,在template模板中均可以直接使用 - 重点
  • 2、返回一个渲染函数,这种方式是为了自定义渲染内容,玩法如下:
    • 1)、引入渲染函数h,指令:import {h} from 'vue'
    • 2)、使用,如指令:return () => h('h1','这是setup中的返回渲染函数用户')

注意点:

  • 1、尽量不要与vue2的配置混合使用
    • 在vue2配置( data、methods、computed... )中可以访问到setup函数中的属性、方法等
      • 但是:在setup函数中不能访问到vue2中配置的内容
      • 如果setup和vue2中的配置有重名的,则:优先使用setup中的配置
    • setup不能是一个async函数,因为返回值不再是retunr的对象,而是promise,这样的话:模板看不到return对象中的属性

setup()的另外一个注意点 和 其可以接收的两个参数

  • setup执行时机:

    • 在beforeCreate之前执行一次,同时在setup(){ }中的this是undefined
  • setup可以接受的两个参数:

    • props:值为对象,包含:组件外部传递过来 且 组件内部声明接收了的属性
      • 注意点:若在外部传递了数据,而内部没有配置props配置项进行声明接收,那么:vue2中不会有什么错误,但是:在vue3中就会在控制台抛错
    • context:上下文对象 【 ps:它里面有三个属性 】
      • attrs:俗称捡漏王。值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于vue2中的this.$attrs
        • 换言之:就是如果父组件传递了数据,但:子组件中的props配置没有声明要接收,那么:传递的数据就在子组件的attrs属性上
      • slots:看名字就知道,就是收到的插槽内容,相当于vue2中的this.$slots
      • emit:也是看名字就知道的,触发自定义事件的函数嘛,相当于vue2中的this.$emit
        • 但是:注意在vue3中,这个东西根据vue2的正常写法,好使,可是:控制台会抛警告,不想看到警告,那就在使用了这个emit的组件中,配置一个emits配置项即可 - 和props声明接收属性的配置一样,如:emits: ['getField']

4.2、ref函数

先做一个实例:修改setup中的数据

<template>
  <h1>vue3的setup函数得到的操作</h1>
  <h2>姓名: {{name}}</h2>
  <h2>性别: {{sex}}</h2>
  <button @click="changeData">修改setup中的数据</button>
</template>

<script>
export default {
  name: 'App',

  // 一、配置setup平台
  setup(){

    let name = '紫邪情';
    let sex = '女';

    function changeData(){
      name = '紫邪晴'
      sex = '男'
      console.log("修改之后的数据: ",name,sex);
    }

    // 1、对象写法
    return {
      // 返回数据
      name,sex,

      // 返回方法
      changeData
    }
  }
}
</script>

image

没实现出来,原因就是:vue不认你的修改,因此:需要借助ref函数来套娃


4.2.1、看看ref函数的真身

<template>
  <h1>vue3的setup函数得到的操作</h1>
  <h2>姓名: {{name}}</h2>
  <h2>性别: {{sex}}</h2>
  <button @click="changeData">修改setup中的数据</button>
</template>

<script>

import {ref} from 'vue'
export default {
  name: 'App',

  // 一、配置setup平台
  setup(){

    // 使用ref函数来进行实现,进行套娃,把数据丢给ref函数进行管理
    let name = ref('紫邪情');
    let sex = ref('女');

    function changeData(){
      // console.log("修改之后的数据: ",name,sex);

      // 看一下ref函数的真身
      console.log(name);
    }

    // 1、对象写法
    return {
      // 返回数据
      name,sex,

      // 返回方法
      changeData
    }
  }
}
</script>

image

既然知道了ref函数的真身,那么:想要实现数据的改变就变得轻松了

image

image


有个注意点

image


4.2.2、使用ref处理对象类型

4.2.2.1、看一下ref函数中套对象的样子是怎样的
<template>
  <h2>工种: {{job.type}}</h2>
  <h2>薪资: {{job.salary}}</h2>
  <button @click="changeData">查看一下ref函数中套对象的样子</button>
</template>

<script>

import {ref} from 'vue'
export default {
  name: 'App',

  // 一、配置setup平台
  setup(){
    // 套对象在ref中
    let job = ref({
      type: 'Java',
      salary: '20k'
    });

    function changeData(){
      // 先看一下ref中套对象的样子是怎样的
      console.log(job.value);
    }

    // 1、对象写法
    return {
      // 返回数据
      job,
      // 返回方法
      changeData
    }
  }
}
</script>

image

既然知道了ref函数中套了对象的样子长什么样的,那么:想要修改ref里面套的对象的属性就很好操作了


4.2.2.2、修改ref函数中对象的属性值

image

image


小小总结一下

ref函数修饰的是基本类型时【 ps:即直接用let name = ref('紫邪情') 】,则:数据代理就是Object.dinfineProperty的setter和getter

ref函数修饰的是对象类型时【 ps:即let job = ref( { } 】,则:数据代理的原理是Proxy对象【 ps:window的ES6的全新配置 。这个对象后续会进行说明 】


4.2.3、对ref函数小小总结一波

作用:定义一个响应式的数据
ps:即,修改数据之后可以把改后的数据渲染到页面中

语法:const xxx = ref(initValue)

  • 创建一个包含响应式数据的引用对应
  • js中操作数据:xxx.value
  • 模板中读取数据:不需要.value,直接:<div>{{xxx}}</div>

注意点:

  • ref()函数中接收的数据可以是:基本类型、也可以是对象类型
    • ref函数修饰的是基本类型时 ps:即直接用let name = ref('紫邪情'),则:数据代理就是Object.dinfineProperty的setter和getter
    • ref函数修饰的是对象类型时 ps:即let job = ref({}),则:数据代理的原理是Proxy对象 ps:这个对象其实是由Object转了一遍,即:Object ——> Proxy,而这个对象window的ES6的全新配置 。这个对象后续会进行说明,对象内部 / 属性实质是借助了vue3的一个新函数 —— reactive()函数

4.3、认识reactive()函数 - 深度监视

这个函数就是专门用来处理数据是对象 / 数组类型的

reactive()函数不能处理基本类型,想要处理基本类型,那么:就使用ref()函数

ref( { } )这里面套对象的类型时,它的原理就是调用了reactive()函数


简单玩一下reactive()函数

1、引入reactive()函数,指令:import {reactive} from 'vue'
2、使用reactive()函数

<template>
  <h1>ref托管的数据</h1>
  <h2>{{name}}</h2>

  <br>
  <br>

  <h1>reactive托管的数据</h1>
  <h2>{{job.type}}</h2>
  <h2>{{job.salary}}</h2>

  <br>
  <br>

  <button @click="changeData">修改ref和reactive托管的数据</button>
</template>

<script>

import {ref,reactive} from 'vue'
export default {
  name: 'App',

  // 一、配置setup平台
  setup(){
    // 配置基本类型数据 - 通过ref实现
    let name = ref('紫邪情');
    // 使用reactive来管理数据
    let job = reactive({
      type: 'Java',
      salary: '20k'
    })
    // 修改基本类型数据
    function changeData(){
      // 修改ref管理的数据类型
      name.value = '紫邪晴';

      // 修改reactive托管的数据 - 相比ref,不再跟value了
      job.type = 'C';
      job.salary = '3毛';
    }

    return {
      // 返回基本类型数据 - ref托管
      name,

      // 返回reactive托管的数据
      job,

      // 返回函数
      changeData,
    }
  }
}
</script>

image


了解reactive的细节问题

前面说:reacitve()函数不能处理基本类型,那测试一下

image

image

reacitve()函数托管数组类型

image

image

想让reactive()函数也能够托管基本类型的数据,怎么办?

  • 把基本类型 使用 对象写法嘛,包装一下呗

image

image

reactive()函数深度监视效果

image

image


4.3.1、对reactive()函数总结一波

作用:定义一个对象类型的响应式数据【 ps:基本类型别用它,用ref函数 】

语法:

  • 引入reactive()函数,指令:import {reactive} from 'vue'
  • 使用:const 代理对象 = reactive(源对象)接收一个对象 / 数组,返回一个代理对象 / proxy对象

reactive定义的响应式数据时“深层次的”

reactive的内部是基于ES6的Proxy实现的,通过代理对象操作源对象内部数据


4.4、vue3中数据监视的原理

4.4.1、Proxy数据监视原理

vue2中数据监视如果是对象类型的,那么是通过Object.defineProperty()的getter和setter来做到数据监视的;如果是数组类型那么就是通过那7个API做到数据监视,但是这种方式有弊端,如下:

  • 新增属性、删除属性,界面不会更新
  • 直接通过下标修改数组,界面不会更新

但是:vue3中就不会出现上面的几种情况


先来看一下Proxy长什么样子

image

image


使用Proxy进行修改数据

<!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>研究Proxy监视数据原理</title>
</head>
<body>
    <script>
        // 定义一个对象
        let person = {
            name: '紫邪情',
            sex: '女'
        }

        // 利用Window.Proxy()来进行修改person

        // 先看一下Proxy的样子
        // console.log( new Proxy(person,{} ) );

        // 使用Proxy进行数据修改
        /*
            people 就是代理对象 它代理的就是person
            new Proxy()就是创建一个代理对象嘛 - 后端的人太熟悉不过了
        */
        const people = new Proxy( person, {
            // 获取对象的属性时调用
            /*
            target 就是源对象 即:person
            propName  就是对象中的属性名  如:name、sex.....
            */
            get(target,propName){
                console.log( "target,propName这两个参数为: ", target,propName);
                console.log(`有人获取person中的${propName}属性`);
                return target[propName];
            },

            // 修改对象中的属性时调用【 ps:修改含增、改、删除是另一个配置 】
            // value就是修改之后的值
            set( target,propName,value ){
                console.log( "target,propName,value这三个参数为: ", target,propName,value);
                console.log( `有人修改person中的${propName}属性` );
                return target[propName] = value;
            },

            // 删除对象中的属性时调用
            deleteProperty(target,propName){
                console.log( "target,propName这两个参数为: ", target,propName);
                return delete target[propName];
            }
        })
    </script>
</body>
</html>

image


4.4.2、Reflect数据监视原理

在vue3中数据监视不止用了window的Proxy对象,还用了window的Reflect对象

Reflect就是反射的意思,这个东西对于玩Java的人来说再熟悉不过了,所以不再过多介绍,在前端中这个是ES6的特性


认识Reflect对象

看看Reflect长什么样

// 先看看Reflect长什么样
console.log(window.Reflect);

image

经过上图的查看之后,其实也就知道Reflect改怎么玩了,调对应的API就可以了
ps:ECMA组织正打算把常用的一些API放到Reflect对象身上,如:目前把Object.defineProperty()就放在Reflect中了 - vue2的数据代理原理的API


使用Reflect实现数据监视

let person = {
    name: '紫邪情',
    sex: '女'
}

// 先看看Reflect长什么样
// console.log(window.Reflect);

// 使用Reflect实现数据监视

// 1、获取对象的属性 - key-value的形式
/*
    key 就是对象名
    value 就是对象的属性名
*/
Reflect.get(person,'name');


// 2、修改对象的属性 
Reflect.set(person,'sex','男');
Reflect.set(person,'age', '18');

// 3、删除对象的属性
Reflect.deleteProperty(person,'sex');

image

注意:使用Reflect做对应的操作之后是有返回值的,如:Reflect.set( person,'age',18 ),返回值是true,所以:就可以利用这个返回值做很多事情,如:进行封装,而Object.defineProperty()并没有返回值

同时:Reflect支持属性名重复,即:若用set()这个API对同一个对象的同一个属性做多次相同的操作,则:不会返回异常,而是返回true / false,因此:才说可以用这个返回值做很多事情;若用Object.defineProperty()来进行相同的操作,则:会直接抛异常,甚至想要后续的代码还能运行,就只能使用try......catch....来对该部分的代码进行包裹了


vue3真正做到数据监视的原理 - 使用Proxy和Reflect对象进行套娃

<!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>vue3实现数据监视的真正原理</title>
</head>
<body>
    <script>
        let person = {
            name: '紫邪情',
            sex: '女'
        }

        const people = new Proxy( person, {
            // 获取对象的属性时调用
            get(target,propName){
                console.log( "target,propName这两个参数为: ", target,propName);
                console.log(`有人获取person中的${propName}属性`);
                // 此处进行了Reflect套娃
                // return target[propName];
                return Reflect.get(target,propName);
            },

            // 修改对象中的属性时调用【 ps:修改含增、改、删除是另一个配置 】
            set( target,propName,value ){
                console.log( "target,propName,value这三个参数为: ", target,propName,value);
                console.log( `有人修改person中的${propName}属性` );
                // 此处进行了Reflect套娃
                // return target[propName] = value;
                return Reflect.set(target,propName,value);
            },

            // 删除对象中的属性时调用
            deleteProperty(target,propName){
                console.log( "target,propName这两个参数为: ", target,propName);
                // 此处进行了Reflect套娃
                // return delete target[propName];
                return Reflect.defineProperty(target,propName);
            }
        })
    </script>
</body>
</html>

4.4.3、vue3中数据监视原理总结

通过Proxy代理对象:拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等

通过Reflect反射对象:对被代理对象的属性进行操作,例子如下:

const people = new Proxy(person, {
    // 拦截读取属性值
    get(target,propName){
        console.log( "target,propName这两个参数为: ", target,propName);
        console.log(`有人获取person中的${propName}属性`);
        // 此处进行了Reflect套娃
        // return target[propName];
        return Reflect.get(target,propName);
    },

    // 拦截修改属性值【 ps:是修改和新增 】
    set( target,propName,value ){
        console.log( "target,propName,value这三个参数为: ", target,propName,value);
        console.log( `有人修改person中的${propName}属性` );
        // 此处进行了Reflect套娃
        // return target[propName] = value;
        return Reflect.set(target,propName,value);
    },

    // 拦截删除属性值
    deleteProperty(target,propName){
        console.log( "target,propName这两个参数为: ", target,propName);
        // 此处进行了Reflect套娃
        // return delete target[propName];
        return Reflect.defineProperty(target,propName);
    }
})

另外:附上Proxy和Reflec对象说明的官网链接

Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect


4.5、reactive() 和 ref()的对比

从定义数据角度对比

  • ref用来定义:基本类型数据
  • reactive用来定义:对象 / 数组类型数据
  • 注:ref也可以用来定义对象 / 数组类型数据,它内部会自动通过reactive转成Proxy代理对象

从原理角度对比:

  • ref通过Object.defineProperty()的get和set来实现的数据劫持 / 数据监视 / 响应式
  • reactive通过使用Proxy代理对象来实现数据劫持,并通过Reflect操作源对象内部的数据

从使用角度对比:

  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取,不需要.value
  • reactive定义的数据:操作数据与读取数据,均不需要.value

4.6、Vue3中的Computed 计算属性函数

其实和uve2中的计算属性没什么两样,只是多了一步引入的问题 以及 排放的问题问题而已

<template>
  姓;<input type="text" v-model="person.firstName">

  <br>

  名: <input type="text" v-model="person.lastName">

  <br>

  <span>全名: {{person.fullName}}</span>
</template>

<script>
import { reactive } from '@vue/reactivity'

// 1、引入computed计算属性函数
import { computed } from '@vue/runtime-core'

export default {
  name: 'App',

  setup(){

    // 数据
    let person = reactive({
      firstName: '紫',
      lastName: '邪情'
    })

    // 2、使用计算属性函数 setup中this无效,所以computed()中使用兰姆达和正常写法都无所谓
    // 简写形式 - 只考虑读的问题
    person.fullName = computed(()=>{
      return person.firstName + "-" + person.lastName;
    })

    // 完整写法 - 考虑读和改的问题
   /*  person.fullName = computed({
      get(){
        return person.firstName + "-" + person.lastName;
      },

      set(value){
        const nameDataArr = value.split('-')
        person.firstName = nameDataArr[0]
        person.lastName = nameDataArr[1]
      }
    }) */

    // 返回数据
    return {
      person,
    }
  }
}
</script>

4.7、vue3中的watch 监视属性函数

和vue2中的watch也差不多


4.7.1、监视ref()定义的数据

这个猫玩意儿和vue2中差不多


简单写法:监视ref托管的单个响应式数据

<template>
  <h1>当前值为: {{num}}</h1>
  <button @click="num ++ ">num++</button>
</template>

<script>

// 1、引入watch函数
import { ref, watch } from '@vue/runtime-core'


export default {
  name: 'App',

  setup(){

    // 准备数据 - 用ref托管
    let num = ref(0)


    // 一、简单写法
    // 2、使用watch函数
    /*
      可以接受三个参数
          第一个:监视的是谁?
          第二个:回调函数 - 新值 和 旧值
          第三个:配置项 - deep深度监视也可以配置
    */
    watch(num , (newValue,oldValue)=>{
      console.log("num的值发生改变了",newValue,oldValue);
    },{immediate:true})


    return {
      num,
    }
  }
}
</script>

image


监视多个属性:监视ref托管的多个响应式数据【 ps:数组写法 】

<template>
  <span>当前名字为: {{name}}</span>
  <br>
  <button @click="name += '!'">改变name</button>
</template>

<script>

// 1、引入watch函数
import { ref, watch } from '@vue/runtime-core'


export default {
  name: 'App',

  setup(){

    // 准备数据 - 用ref托管
    let num = ref(0)

    let name = ref('紫邪情')

    // 监视ref托管的多个响应式数据 - 变化就在这里 监事的是谁?采用数组写法即可
    watch([num,name],(newValue,oldValue)=>{
      console.log("num 和 name的值发生改变了",newValue,oldValue);
    },{immediate:true})
    return {
      num,name
    }
  }
}
</script>

4.7.2、监视reactive()定义的数据

监视reactive托管的一个响应式数据中的全部属性

<template>
  姓名: <input type="text" v-model="person.name"> <br>
  性别: <input type="text" v-model="person.sex"> <br>
  地址: <input type="text" v-model="person.address.detailed.value">

  <br>
  <br>

  <span>姓名: {{person.name}}</span> <br>
  <span>性别: {{person.sex}}</span> <br>
  <span>地址: {{person.address.detailed.value}}</span>
</template>

<script>
import { reactive } from '@vue/reactivity'
import { watch } from '@vue/runtime-core'

export default {
  name: 'App',

  setup(){

    // 准备数据 - 用reactive托管
    let person = reactive({
      name: '紫邪情',
      sex: '女',
      address: {
        detailed: {
          value: '浙江省杭州市'
        }
      }
    })

    // 监视reactive托管的一个响应式数据中的全部属性
    watch(person,(newValue,oldValue)=>{
      console.log("person被修改了", newValue,oldValue);
    })

    return {
      person,
    }
  }
}
</script>

image

上面这种坑就是在监视此种reactive托管的一个响应式数据的全部属性时,并不能获得旧值oldValue,因为:旧值oldValue和新值newValue一样

但是:还有一种坑,就是:此种类型是强制开启了深度监视,即:配置deep:false不顶用

image

image


监视reactive托管的一个响应式数据中的某一个属性

// 类型二、监视reactive托管的一个响应式数据中的某一个属性

/*
  奇葩的地方:
      1、要监视的这个属性需要写成函数式 ()=> person.name
      2、可以争取获取newValue、oldValue
*/
watch(()=> person.name , (newValue,oldValue)=>{
  console.log("person中的name属性被修改了",newValue,oldValue);
})

image


监视reactive托管的一个响应式数据中的某些属性 - 函数式数组写法

// 类型三、监视reactive托管的一个响应式数据中的某些属性

/*
  奇葩的地方:
      1、监视的多个属性需要使用数组套起来
      2、数组中的每一个属性需要写成函数式
*/
watch([()=> person.name , ()=> person.sex] , (newValue,oldValue)=>{
  console.log("person中的name和sex属性被修改了",newValue,oldValue);
})

image


特殊情况:监视reactive托管的一个响应式数据中的某一个属性【 ps:此属性套娃了,又是一个对象 】

// 类型四、监视reactive托管的一个响应式数据中的某个属性,但:此属性又套娃了

/*
  奇葩的地方:
      1、需要开启深度监视 即:deep:true 又生效了
      2、不加 deep:true配置,代码会无效
*/
watch(()=> person.address , (newValue,oldValue)=>{
  console.log("person中的address属性被修改了",newValue,oldValue);
},{deep:true})

image

但是:如果不加deep:true配置呢?

image

image


4.7.2.1、监视reactive托管的一个响应式数据的各种类型总结

注:在vue3中可以同时配置多个watch,而在vue2中配置重复的,那只有前者有效

// 准备数据 - 用reactive托管
let person = reactive({
  name: '紫邪情',
  sex: '女',
  address: {
    detailed: {
      value: '浙江省杭州市'
    }
  }
})

// 类型一、监视reactive托管的一个响应式数据中的全部属性
/*
  此种类型的坑:
      1、无法正确获得oldValue的值【 ps:因newValue和oldValue的值一样 】
      2、简直强制开启了深度监视 【 ps:即deep:false配置无效 】
*/
watch(person,(newValue,oldValue)=>{
  console.log("person被修改了", newValue,oldValue);
},{deep:false})
/*
  如:这里关闭深度监视 理论上:应该监视不到address.detailed.value
      但是:天真
*/


// 类型二、监视reactive托管的一个响应式数据中的某一个属性
/*
  奇葩的地方:
      1、要监视的这个属性需要写成函数式 ()=> person.name
      2、可以争取获取newValue、oldValue
*/
watch(()=> person.name , (newValue,oldValue)=>{
  console.log("person中的name属性被修改了",newValue,oldValue);
})


// 类型三、监视reactive托管的一个响应式数据中的某些属性
/*
  奇葩的地方:
      1、监视的多个属性需要使用数组套起来
      2、数组中的每一个属性需要写成函数式
*/
watch([()=> person.name , ()=> person.sex] , (newValue,oldValue)=>{
  console.log("person中的name和sex属性被修改了",newValue,oldValue);
})

// 类型四、监视reactive托管的一个响应式数据中的某个属性,但:此属性又套娃了
/*
  奇葩的地方:
      1、需要开启深度监视 即:deep:true 又生效了
      2、不加 deep:true配置,代码会无效
*/
watch(()=> person.address , (newValue,oldValue)=>{
  console.log("person中的address属性被修改了",newValue,oldValue);
},{deep:true})

return {
  person,
}

4.8、vue3中的watchEffect 智能监视函数

注:此种监视对应ref托管和reactive托管都可以监视到*

<template>
  姓名: <input type="text" v-model="person.name"> <br>
  性别: <input type="text" v-model="person.sex"> <br>
  地址: <input type="text" v-model="person.address.detailed.value">

  <br>
  <br>

  <span>姓名: {{person.name}}</span> <br>
  <span>性别: {{person.sex}}</span> <br>
  <span>地址: {{person.address.detailed.value}}</span>
</template>

<script>
import { reactive } from '@vue/reactivity'

// 1、引入watchEffect函数
import { watchEffect } from '@vue/runtime-core'

export default {
  name: 'App',

  setup(){

    let person = reactive({
      name: '紫邪情',
      sex: '女',
      address: {
        detailed: {
          value: '浙江省杭州市'
        }
      }
    })

    // 2、使用watchEffect函数对响应式数据进行智能监视
    /* 
      1、不需要指名要监视谁
      2、不需要newValue 和 oldValue【 ps:因为都不知道要监视谁 】
    */
    watchEffect(()=>{
      // 所谓智能:就体现在这里面的函数体中
      //          要监视谁,取决于这个函数体里面用到了谁,那就监视谁

      // 如:要监视person中的name,那就直接写改写的代码即可,此函数会自动判定,从而监视
      const personName = person.name

      // 如:要监视person中的sex,那就用它就可以了
      const personSex = person.sex

      console.log("watchEffect智能监视函数被调用了");

      // 而此函数体中没有用到的,那么:就不会去监视它
    })

    return {
      person,
    }
  }
}
</script>

image

image

image

image


4.9、vue3中的watch函数 和 watchEffect函数的对比

watch函数的套路是:既要指明要监视哪个属性,也有指明监视的回调

watchEffect函数的套路是:不用指明要监视哪个属性,回调中用到了哪个属性,那就监视哪个属性

watchEffect函数和Computed函数有点像:

  • Computed函数注重:计算出来的值 ps:回调函数的返回值。所以必须写返回值
  • watchEffect函数注重:过程 ps:回调函数的函数体。所以不用写返回值

4.10、vue3中的生命周期图

和vue2的生命周期差不多,只是需要注意一些点而已

image

上面这张图官网中有

image


4.10.1、vue3中生命周期的注意项

对比vue2中的生命周期,vue3中改动的地方,如下所示

image

image


2、vue3中生命周期的写法问题

配置项写法 - 和name、setuo()保持平级,写法就是:按照官网中说的哪些名字直接写即可

<script>
    export default {
      name: 'App',

      setup() {},

      // vue3中的生命周期 - 配置项写法     ps:和name、setup保持平级即可
      beforeCreate(){ console.log("------beforeCreate-----"); },
      created(){ console.log("------created-----"); },
      beforeMount(){ console.log("------beforeMount-----"); },
      mounted(){ console.log("------mounted-----"); },
      beforeUpdate(){ console.log("------beforeUpdate-----"); },
      updated(){ console.log("------updated-----"); },
      beforeUnmount(){ console.log("------beforeUnmount-----"); },
      unmounted(){ console.log("------unmounted-----"); },
    }
</script>

另一种写法:组合式API写法 - 万事引入对应函数嘛 - 不过此种方式名字有点区别

  • beforeCreate ====> setup()
  • created ====> setup()
  • beforeMount ====> onBeforeMount
  • mounted ====> onMounted
  • beforeUpdate ====> onBeforeUpdate
  • updated ====> onUpdated
  • beforeUnMount ====> onBeforeUnMount
  • UnMounted ====> onUnMount
<script>
// 1、引入对应的钩子函数
import { onBeforeMount, onMounted } from '@vue/runtime-core'

export default {
  name: 'App',

  setup() {

    // 另一种写法 - 组合式API写法 - 万事引入对应的函数嘛
    /*
      只是注意:setup()就相当于beforeCreate() 和 created()
    */

    // 2、使用对应的钩子函数
    onBeforeMount(()=>{
      console.log("------beforeMount-----");
    })

    onMounted(()=>{
      console.log("------onMounted-----");
    })

    // 其他的都是一样的,就不写了,注意名字即可
  },

</script>

需要注意一个点:配置项写法和组合式API写法同时存在同一个钩子函数时

则:setup()中所用的组合式API写法比配置项写法优先执行


4.12、vue3中的 toRef 和 toRefs 数据拆分函数

这两个东西就是为了解决在模板中渲染时稍微方便点而已,因为在模板中使用插值表达式xxx.xxxx.xxx这种取值并不合理,插值表达式的宗旨就是简单取值嘛,所以通过xxx.xxxx.xxx的方式并不好

当然:toRef和toRefs也不一定能够完全解决插值表达式的问题 ps:主要看自己设计


1、使用toRef()函数交出单个数据

<template>
  <h2>姓名: {{person.name}}</h2>
  <h2>性别: {{person.sex}}</h2>
  <h2>地址: {{person.address.value}}</h2>
  <!-- 上面这种方式并不好,简化 -->
  <br>
  <br>
  <h1>使用toRef和toRefs函数进行简化</h1> <br>
  <!-- 下面就可以直接简写了 -->
  <h2>姓名: {{name}}</h2>
  <h2>性别: {{sex}}</h2>
  <h2>地址: {{address}}</h2>
</template>

<script>
// 1、组合式还是逃不开引入的问题
import { reactive, toRef } from '@vue/reactivity'


export default {
  name: 'App',

  setup() {
    let person = reactive({
      name: '紫邪情',
      sex: '女',
      address: {
        value: '浙江杭州'
      }
    })

    return {
      person,
      // 2、使用toRef()函数
      // 使用toRef函数交出单个数据
      /* 
        第一个参数: 交出的数据是哪个对象中的
        第二个参数: 要交出的是对象中的哪个属性
      */
      name: toRef(person,'name'),
      sex: toRef(person,'sex'),
      // 这里需要注意一下:要交出的对象里面又套娃了,那么:第一个参数需要再进一步
      address: toRef(person.address,'value'),
    }
  },
}
</script>

image


2、使用toRefs()函数

<template>
  <h2>姓名: {{person.name}}</h2>
  <h2>性别: {{person.sex}}</h2>
  <h2>地址: {{person.address.value}}</h2>
  <!-- 上面这种方式并不好,简化 -->
  <br>
  <br>
  <h1>使用toRefs函数进行简化</h1> <br>
  <!-- 下面就可以直接简写了 -->
  <h2>姓名: {{name}}</h2>
  <h2>性别: {{sex}}</h2>
  <!-- 但是:美中不足就是,这里是里面套的娃,所以还得需要xxx.xxx一下 -->
  <h2>地址: {{address.value}}</h2>
</template>

<script>
// 1、组合式还是逃不开引入的问题
import { reactive, toRefs } from '@vue/reactivity'


export default {
  name: 'App',

  setup() {
    let person = reactive({
      name: '紫邪情',
      sex: '女',
      address: {
        value: '浙江杭州'
      }
    })

    return {
      person,
      // 利用toRef()交出数据,需要写多次toRef,所以还是不喜欢,那就用toRefs()函数
      /* 
        直接说要交出哪个对象即可
        注意点:return{}是一个对象,所以:使用toRefs就是对象中套对象,因此注意写法
      */
      ...toRefs(person)
    }
  },
}
</script>

image


现在回过来看一下,为什么通过toRef() 和 toRefs()函数可以做到数据简化

使用toRef()举例,去看一下它长什么样子? - toRefs()函数是一样的原理

console.log(toRef(person,'name'));

image


4.12.1、vue3的toRef() 和 toRefs()函数总结

作用:创建一个RefImpl引用对象,而此对象的value属性是指向你要交出数据的那个对象的某个属性

  • 解读:创建的这个RefImpl引用对象就相当于中间商,这个中间商代表的就是你要交出去的那个数据 ,注意:是代表啊,意思就相当于是此中间商的引用指向的地址 和 原本你要交出去的对象的某个属性指向的是同一个地方,因此:此中间商RefImpl可以修改原数据,即:你把数据交出去之后,被修改了,那么原数据中的数据也会被修改,数据保持同步嘛

语法:const name = toRef(person,'name') 记得先引入对应的函数

应用场景:要将响应式对象中的某个属性单独提供给外部使用时就可以用这两个函数

扩展:toRefstoRef功能一致,但:可以批量创建多个RefImpl引用对象

  • toRefs的语法:toRefs(person)


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM