Vue3 新特性、组合式API、新的组件(笔记)

文章目录

      • 1. Vue3带来了什么
        • 1.1 性能的提升
        • 1.2 源码的升级
        • 1.3 支持TypeScript
        • 1.4 新的特性
      • 2. 常用的 Composition(组合式) API
        • 2.1 setup函数
        • 2.2 ref函数
          • 2.2.1 在模板上使用
          • 2.2.2 在组件上使用
          • 2.2.3 函数模板引用
        • 2.3 reactive函数
        • 2.4 计算属性与侦听器
          • 2.4.1 computed函数
          • 2.4.2 watch函数
          • 2.4.3 watchEffect函数
          • 2.4.4 回调触发时机
          • 2.4.5 取消侦听器
        • 2.5 自定义hook
        • 2.6 toRef与toRefs
        • 2.7 Provide与Inject
      • 3. 其他组合式API
        • 3.1 shallowRef与shallowReactive
        • 3.2 readonly与shallowReadonly
        • 3.3 toRaw与markRaw
        • 3.4 customRef
        • 3.5 响应式数据的判断
      • 4. 新的组件
        • 4.1 Fragment
        • 4.2 teleport
        • 4.3 Suspense(试验性)

1. Vue3带来了什么

1.1 性能的提升

  • 打包大小减少41%
  • 初次渲染快55%, 更新渲染快133%
  • 内存减少54%

1.2 源码的升级

  • 使用 Proxy 代替 defineProperty 实现响应式
  • 重写虚拟 DOM 的实现和 Tree-Shaking

1.3 支持TypeScript

  • Vue3 可以更好的支持 TypeScript

1.4 新的特性

  1. Composition API(组合API)

    • setup 配置
    • ref 与 reactive
    • watch 与 watchEffect
    • provide 与 inject
  2. 新的内置组件

    • Fragment
    • Teleport
    • Suspense
  3. 其他改变

    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符

2. 常用的 Composition(组合式) API

2.1 setup函数

  • 在 Vue3 中组件用到的数据、方法等,都要配置在 setup 函数中

  • setup 的两种返回值

    • 如果返回的是一个对象,则对象中的属性和方法在模板中才可以使用
    • 如果返回的是一个渲染函数,则可以自定义渲染内容
  • setup 的参数

    • props:父组件传过来的值
    • context:上下文对象
  • setup 在 beforeCreate 前执行一次,this 是 undefined,所以 Vue2 中的配置项可以访问到 setup 中的数据,而 setup 不能通过 this 访问 Vue2 中的配置项,但是不建议混合使用

    // 当返回值为一个渲染函数时,视图由渲染函数中的内容承包
    import { h } from "vue";
    
    export default {
      setup() {
        return () => h("h1", "哈哈");
      }
    }
    
    // 定义的数据和方法,需要以对象的形式去返回
    export default {
      setup() {
        let num = 123;
        let str = "123";
          
        function fn() {
          console.log("我是一个方法");
        }
        return {
          num,
          str,
          fn,
        };
      }
    }
    
  • 当 setup 为 async 函数时,需要配合 Suspense 和异步组件使用

2.2 ref函数

  • 作用:
    • 定义一个响应式数据
  • 创建响应式数据:
    • 当定义一个基本数据类型的响应式时,使用的是 Object.defineProperty
    • 当定义一个引用数据类型的响应式时,调用了 reactive 函数
  • 获取
    • 在 js 中需要以 .value 的形式去获取
    • 在模板中直接获取
  • 用法:
    import { ref } from "vue";
    
    export default {
      setup() {
        let num = ref(123);
    
        // 触发 fn 函数,改变 num 的值
        function fn() {
          num.value = 234;
        }
        return {
          num,
          fn,
        };
      }
    }
    
2.2.1 在模板上使用
<input ref="input">

<script setup>
import { ref } from 'vue'

// 必须和模板里的 ref 同名,就可以拿到 input DOM 对象
const input = ref()
script>
2.2.2 在组件上使用
<myComponent ref="myComponent">

<script setup>
import { ref } from 'vue'

// 组件实例对象
const myComponent = ref()
script>
2.2.3 函数模板引用
<input :ref="(el) => { el:当前元素 }">

2.3 reactive函数

  • 作用:

    • 定义一个响应式数据
  • 创建响应式数据:

    • 不能定义基本数据类型
    • 当定义一个引用数据类型的响应式时,使用的是 Proxy 和 Reflect
  • 获取:

    • 在 js 中直接获取
    • 在模板中直接获取
  • 用法:

    import { reactive } from "vue";
    
    export default {
      setup() {
        // p 的所有属性都是响应式的
        let p = reactive({
          name: "小红",
          age: 18,
        });
    
        function fn() {
          p.name = "小白";
          p.age = 20;
        }
        return {
          p,
          fn,
        };
      }
    }
    
  • ref 与 reactive 的区别:

    • ref 定义数据后,如果在 js 中获取需要 .value,基本引用数据类型都可以定义
    • reactive 定义数据后,可以直接获取,只能定义引用数据类型
    • 最好使用 ref 定义基本数据类型,使用 reactive 定义引用数据类型

2.4 计算属性与侦听器

2.4.1 computed函数

与 Vue2 中的计算属性使用方法一致

import { computed } from 'vue'

export default {
  setup(){
   	// 简写
    let MyName = computed(() => {
      return;
    });
    
    // 完整写法
    let MyName1 = computed({
      set(value) {},
      get() {
        return;
      },
    });
    return {
      MyName,
      MyName1,
    };
  }
}
2.4.2 watch函数

与 Vue2 中的侦听器一样,但是采用的响应式不同,会分很多种情况

// 基本使用,watch 可以监视 getter/effect 函数、ref、reactive、或这些类型的数组。
watch(观察的数据,数据发生变化的回调,{配置项,可选});
  1. 情况一:监视 ref 定义的响应式数据

    // 当观察 ref 响应式数据时,不需要 .value
    import { ref, watch } from "vue";
    
    let num = ref(123);
    
    watch(num, (newValue, oldValue) => {
      console.log("值发生了改变", newValue, oldValue);
    });
    
  2. 情况二:监视 reactive 定义的响应式数据

    // 引用数据类型无法获取正确的 oldValue,Vue2 也会有这个问题
    // Proxy 默认开启了深度监视,关不掉
    import { reactive, watch } from "vue";
    
    let obj = reactive({
      a: {
        b: {
          c: {
            d: 1,
          },
        },
      },
    });
    
    watch(obj, (newValue, oldValue) => {
        // 最里面的 d 不论怎么改变,watch 都可以检测到
        console.log("值发生了改变", newValue, oldValue);
      }
    );
    
  3. 情况三:监视 getter 函数

    // 这种方式只有当 obj 的地址发生了改变才会进行监视
    // 深度监视需要手动开启
    import { reactive, watch } from "vue";
    
    let obj = reactive({
      a: {
        b: 1,
      },
    });
    
    watch(() => obj, (newValue, oldValue) => {
        console.log("值发生了改变", newValue, oldValue);
      },
      { deep: true }
    );
    
  4. 情况四:几种方式配合使用

    import { ref, watch, reactive } from "vue";
    
    let num = ref(123);
    let str = ref("123");
    let obj = reactive({
      a: {
        b: 1,
      },
    });
    
    watch([num, () => str.value, obj],
      (newValue, oldValue) => {
        // 其中一个值发生了改变,就会被检测到,会返回一个数组
        console.log("值发生了改变", newValue, oldValue);
      },
      { deep: true }
    );
    

总结:

  • 一般使用 ref 定义基本数据类型的响应式,可以直接监视,并不会存在任何问题
  • 使用 reactive 定义引用数据类型的响应式,监视默认会开启深度监视( 无法通过配置去关闭 )。此时 newValue 和 oldValue 的值一样,Vue2 也会有一个问题,可能是因为浏览器引用地址相同,展示数据也会相同
  • 使用 getter 函数,如果返回值为引用数据类型,需要手动开启深度监视
  • 数组可以监视多个数据,当其中一个数据发生改变时,都会被监视到并返回一个数组,并且数组中的每一项,必须为可监视数据
2.4.3 watchEffect函数
// watchEffect 中用到哪个属性,那么就会监视那个属性,并且也是深度监视
// 如果 watchEffect 中使用了网络请求,那么会立即执行一次
import { watchEffect } from "vue"

let num = 10
let str = "10"

watchEffect(() => {
  let newNum = num
  let newStr = str
  console.log("发生了改变")
})
2.4.4 回调触发时机
// 当响应式状态值发生改变时,是先触发了侦听器,后触发的组件更新
// 下面这两种方式,可以获取到最新的 DOM,也就是在组件更新后触发
watch(source, callback, {
  flush: 'post'
})

watchEffect(callback, {
  flush: 'post'
})

// 也可以使用这种方式
import { watchPostEffect } from 'vue'

watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
})
2.4.5 取消侦听器
// 和 Vue2 中全局使用侦听器是一样的
let unwatch = watch(source, callback)

2.5 自定义hook

  • hook 本质是一个函数,把 setup 函数中使用的 Composition API 进行了封装。

  • 类似于 vue2 中的mixin

    // 说白了就是函数的封装、复用
    // hooks/useXxx.js 文件
    export default function (){
      // 一些公共的逻辑
      return xxx
    }
    
    
    // 组件文件
    import useXxx from "@/hooks/useXxx.js"
    
    setup(){
      let xxx = useXxx()
    }
    

2.6 toRef与toRefs

  • toRef 为一个响应式对象的某个属性新建一个 ref,使这两个数据相连接

    // toRef 用法
    const state = reactive({
      foo: 1,
      bar: 2
    })
    
    // 此时 fooRef 和 state.foo 相连接,并且都是响应式的
    const fooRef = toRef(state, 'foo')
    
    fooRef.value++
    console.log(state.foo) // 2
    
    state.foo++
    console.log(fooRef.value) // 3
    
  • toRefs 为一个响应式对象的每一条属性新建一个 ref

    // toRefs 用法
    const state = reactive({
      foo: 1,
      bar: 2
    })
    
    // 此时 stateAsRefs 与 toRefs 相连接
    const stateAsRefs = toRefs(state)
    

2.7 Provide与Inject

  • 作用:实现祖孙组件间通信

  • 用法:

    // 祖先组件
    import { provide } from "vue"
    
    setup(){
      let data = "我是要传入的数据"
      provide('msg', data)
    }
    
    // 后代组件
    import { inject } from "vue"
    
    setup(){
      inject('msg')
    }
    

3. 其他组合式API

3.1 shallowRef与shallowReactive

  • shallowRef:只考虑基本数据类型的响应式,引用数据类型只有当地址发生改变时才会响应
  • shallowReactive:对于引用数据类型,只对数据的第一层进行响应式
  • 用法:和 ref/reactive 函数用法一样

注意:当数据不是响应式时,数据会发生改变,但是视图不会被更新,如果使用其他的方法去更新视图,那么视图也可以拿到 shallowReactive/shallowRef 中最新的数据

3.2 readonly与shallowReadonly

  • readonly:让一个响应式数据变为只读的,所有的数据都为只读
  • shallowReadonly:让一个响应式数据变为只读的,只是第一层的数据只读
  • 用法:调用 readonly/shallowReadonly 函数,返回一个新的只读属性
  • 应用场景:不希望数据被修改时

3.3 toRaw与markRaw

  • toRaw:
    • 作用:将一个由 reactive 定义的响应式对象,变为普通对象
    • 用法:调用 toRaw 函数,返回一个新的普通对象
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 用法:调用 markRaw 函数,为原对象添加一个 __v_skip 为 true 的属性

3.4 customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制

  • 实现一个防抖效果:

    <template>
      <input type="text" v-model="keyword" />
      <h3>{{ keyword }}h3>
    template>
    
    <script>
    import { customRef } from "vue";
    export default {
      name: "Demo",
      setup() {
        //自定义一个 myRef
        function myRef(value, delay) {
          let timer;
          return customRef((track, trigger) => {
            return {
              get() {
                track(); // 允许添加依赖
                return value;
              },
              set(newValue) {
                clearTimeout(timer);
                timer = setTimeout(() => {
                  value = newValue;
                  trigger(); // 触发依赖更新视图
                }, delay);
              },
            };
          });
        }
        let keyword = myRef("hello", 500);
        return {
          keyword,
        };
      },
    };
    script>
    

3.5 响应式数据的判断

  • isRef:检查一个值是否为一个 ref 对象
  • isReactive:检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly:检查一个对象是否是由 readonly 创建的只读代理
  • isProxy:检查一个对象是否是由 reactive 或者 readonly 方法创建的代理,readonly 也是经过 Proxy 进行的代理,只是为只读的
  • 返回值:都为布尔值

4. 新的组件

4.1 Fragment

  • 在 Vue2 中:组件必须有一个根标签
  • 在 Vue3 中:如果组件没有根标签,那么默认会包裹在一个 Fragment 虚拟元素里
  • 好处:减少标签层级,减小内存占用

4.2 teleport

  • 作用:能够将组件的html结构移动到指定位置

  • 用法:

    
    <teleport to="标签名/class类名/id名">
      <div>
        <h1>我是要被移动的内容h1>
      div>
    teleport>
    

4.3 Suspense(试验性)

  • 当组件异步引入时,可以和 Suspense 配合 loading 展示

  • 用法:生产环境请勿使用

    • 异步引入组件

      // 创建一个只有在需要时才会加载的异步组件
      import { defineAsyncComponent } from 'vue'
      const Child = defineAsyncComponent(() => import('./components/Child.vue'))
      
    • 使用 Suspense 包裹组件,并配置好 default 与 fallback

      <template>
        <div class="app">
      	<h3>我是App组件h3>
      	<Suspense>
      	  <template #default>
              
      		<Child/>
      	  template>
      	  <template #fallback>
      		
      		<h3>加载中.....h3>
      	  template>
      	Suspense>
        div>
      template>
      

你可能感兴趣的:(vue,javascript,前端,vue.js)