【Vue3】尚硅谷(天禹老师主讲)Vue3笔记

本文是主要内容为转载,对其中部分内容进行了代码解析
只为记录学习过程,共享学习

Vue3快速上手

【Vue3】尚硅谷(天禹老师主讲)Vue3笔记_第1张图片

1、Vue3简介

  • 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王)
  • 耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者
  • github上的tags地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.0

2.Vue3带来了什么

1.性能的提升

  • 打包大小减少41%

  • 初次渲染快55%, 更新渲染快133%

  • 内存减少54%

2.源码的升级

  • 使用Proxy代替defineProperty实现响应式

  • 重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript

  • Vue3可以更好的支持TypeScript

4.新的特性

  • Composition API(组合API)

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

    • Fragment
    • Teleport
    • Suspense
  • 其他改变

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

一、创建Vue3.0项目工程

1.使用 vue-cli 创建

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

二 、常用Conposition APL

点击跳转

三、其它 Composition API

1.shallowReactive 与 shallowRef

shallowReactive:

shallowReactive 用于创建一个浅层响应式对象,即只有对象的第一层属性是响应式的,而深层嵌套的对象不会被转换成响应式对象。这可以用于避免深层对象的监听,提高性能。

import { shallowReactive } from 'vue';

const obj = shallowReactive({
  nested: {
    count: 0
  },
  message: 'Hello'
});

// 只有第一层的属性是响应式的
console.log(obj.nested); // 没有响应式
console.log(obj.message); // 有响应式

// 修改第一层的属性,触发响应
obj.message = 'Updated';
// 修改第二层的属性,不触发响应
obj.nested.count++;

shallowRef:

shallowRef 用于创建一个浅层响应式引用,它与ref 相似,但只对引用类型(对象、数组等)进行浅层的响应式处理

import { shallowRef } from 'vue';

const refObj = { count: 0 };
const shallowObj = shallowRef(refObj);

// 修改引用对象的属性,触发响应
refObj.count++;
// 修改引用对象的属性,触发响应
shallowObj.value.count++;

// 修改引用对象,触发响应
shallowObj.value = { count: 1 };

总结一下,shallowReactive 适用于创建一个对象,只使对象的第一层属性响应式,而 shallowRef 适用于创建一个引用,对引用类型进行浅层响应式处理。使用这两者之一取决于你的具体需求和数据结构。

2.readonly 与 shallowReadonly

readonly

readonly 用于创建一个完全的只读对象,该对象及其嵌套的属性都是不可修改的。这意味着无论是对象的属性值还是嵌套对象,都不能被修改。如果你尝试修改 readonly 对象的属性,Vue 会发出警告。

import { readonly } from 'vue';

const obj = readonly({
  prop1: 'value1',
  nested: {
    prop2: 'value2'
  }
});

// 下面的操作会触发警告
obj.prop1 = 'new value';
obj.nested.prop2 = 'new value';

shallowReadonly:

shallowReadonly 用于创建一个浅层只读对象,即只有对象的第一层属性是只读的,而深层嵌套的对象仍然可以被修改。这允许你修改嵌套对象的属性,但不能修改嵌套对象本身。

import { shallowReadonly } from 'vue';

const obj = shallowReadonly({
  prop1: 'value1',
  nested: {
    prop2: 'value2'
  }
});

// 修改第一层的属性,允许
obj.prop1 = 'new value';

// 修改第二层的属性,会触发警告
obj.nested.prop2 = 'new value';

总结:

  • readonly 创建的对象是完全只读的,包括对象的所有嵌套属性。
  • shallowReadonly 创建的对象只有第一层属性是只读的,允许修改嵌套对象的属性,但不允许修改嵌套对象本身。

3.toRaw 与 markRaw

toRaw

  • 作用:将一个由reactive生成的响应式对象转为普通对象。
  • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。例如在自定义函数中处理对象时,可能需要获取对象的原始版本而不是代理后的版本。
import { reactive, toRaw } from 'vue';

const obj = reactive({
  prop1: 'value1',
  prop2: 'value2'
});

const rawObj = toRaw(obj);

console.log(rawObj === obj); // false
console.log(rawObj.prop1); // 'value1'

markRaw

  • 作用:标记一个对象,使其永远不会再成为响应式对象。
  • 应用场景:
    有些值不应被设置为响应式的,例如复杂的第三方类库等。
    当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
import { reactive, markRaw } from 'vue';

const obj = reactive({
  prop1: 'value1',
  prop2: markRaw({
    nestedProp: 'value2'
  })
});

console.log(obj.prop1); // 'value1'

// obj.prop2 是一个不可代理的对象
console.log(obj.prop2.nestedProp); // 'value2'

// 尝试对 obj.prop2 进行响应式操作会引发警告
obj.prop2.nestedProp = 'new value';

总结:

  • toRaw 用于获取一个响应式对象的原始版本。
  • markRaw 用于标记一个对象,使其成为不可代理的对象。

4.customRef

customRef 在 Vue 3 中的应用场景主要是用于创建具有自定义行为的响应式对象。这个功能允许你手动控制对象的响应式,适用于一些特殊的需求。以下是一些 customRef 的应用场景:

  1. 延迟更新: 你可以使用 customRef 来实现一个具有延迟更新的响应式属性。例如,你可能希望在用户输入停止一段时间后再更新状态,而不是每次输入都立即更新。

    import { customRef } from 'vue';
    
    function useDebouncedRef(value, delay = 200) {
      let timeout;
      return customRef((track, trigger) => {
        return {
          get() {
            track();
            return value;
          },
          set(newValue) {
            clearTimeout(timeout);
            timeout = setTimeout(() => {
              value = newValue;
              trigger();
            }, delay);
          }
        };
      });
    }
    
    export default {
      setup() {
        const inputValue = useDebouncedRef('', 500);
        return {
          inputValue
        };
      }
    };
    
  2. 异步更新: 你可以使用 customRef 来实现具有异步更新的响应式属性。例如,当某个异步操作完成后再更新状态。

    import { customRef } from 'vue';
    
    function useAsyncRef(asyncGetter) {
      let value;
      let isPending = true;
    
      return customRef((track, trigger) => {
        return {
          get() {
            track();
            return value;
          },
          async set(newValue) {
            isPending = true;
            value = await asyncGetter(newValue);
            isPending = false;
            trigger();
          }
        };
      });
    }
    
    export default {
      setup() {
        const asyncValue = useAsyncRef(async (inputValue) => {
          // 模拟异步操作
          await new Promise(resolve => setTimeout(resolve, 1000));
          return inputValue.toUpperCase();
        });
        return {
          asyncValue
        };
      }
    };
    
  3. 自定义依赖追踪: 如果你希望在特定条件下追踪依赖,可以使用 customRef 实现自定义依赖追踪的逻辑。

    import { customRef } from 'vue';
    
    function useConditionalRef(initialValue) {
      let value = initialValue;
    
      return customRef((track, trigger) => {
        return {
          get() {
            if (value > 0) {
              track();
            }
            return value;
          },
          set(newValue) {
            value = newValue;
            trigger();
          }
        };
      });
    }
    
    export default {
      setup() {
        const conditionalValue = useConditionalRef(1);
        return {
          conditionalValue
        };
      }
    };
    

这些是一些 customRef 的应用场景示例。总体来说,customRef 提供了更灵活的方式来处理响应式对象,使得你能够根据具体需求实现自定义的响应逻辑。

5.provide 与 inject

作用:实现祖与后代组件间通信

套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

provide

provide 是一个在父组件中使用的函数,用于提供数据给其子组件。它接收两个参数,第一个是键名,用于在子组件中识别数据,第二个是提供给子组件的数据。

// ParentComponent.vue
<script>
import { provide } from 'vue';
export default {
  setup() {
    const sharedData = 'Shared Data';
    provide('sharedKey', sharedData);
    return {
      // other setup logic
    };
  }
};
</script>

inject

inject 是一个在子组件中使用的函数,用于接收来自父组件的提供的数据。它接收一个键名数组或对象,键名对应了在父组件中使用 provide 时设置的键名。

// ChildComponent.vue
<script>
import { inject } from 'vue';
export default {
  setup() {
    const sharedData = inject('sharedKey', 'Default Value');
    console.log(sharedData); // 'Shared Data'
    return {
      // other setup logic
    };
  }
};
</script>

在上面的例子中,inject 接收了一个键名 'sharedKey',它会在父组件中查找是否有与之对应的数据,如果找到,则返回该值,否则返回提供的默认值 'Default Value'

注意事项:

  • provideinject 是成对使用的,子组件通过 inject 获取提供的值。
  • 如果子组件的 inject 中没有找到对应的键名,它会使用提供的默认值。
  • provideinject 不仅限于直接父子关系,也可以用于祖先和后代组件之间的通信。

这一对 API 提供了一种简单而强大的方式,在组件树中共享数据,避免了通过 props 一层层传递数据的繁琐。

6.响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

四、Composition API 的优势

五、Composition API 的优势

1.Fragment

在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级, 减小内存占用

2.Teleport

Teleport 将组件的内容渲染到任意的 DOM 元素上,而不受父组件的限制。
Teleport 在处理弹出层、模态框等情况时非常有用,因为它可以将组件的内容渲染到页面的其他位置,而不仅仅是组件所在的父组件内。

<template>
  <div>
    <button @click="toggleModal">Open Modal</button>
    <!-- 使用 Teleport 渲染到 id 为 "modal-container" 的元素中 -->
    <teleport to="#modal-container">
      <Modal v-if="showModal" @close="closeModal" />
    </teleport>
  </div>
</template>

<script>
import Modal from './Modal.vue';

export default {
  data() {
    return {
      showModal: false
    };
  },
  methods: {
    toggleModal() {
      this.showModal = !this.showModal;
    },
    closeModal() {
      this.showModal = false;
    }
  },
  components: {
    Modal
  }
};
</script>

在上面的例子中, 标签将 Modal 组件的内容渲染到页面中 id 为 “modal-container” 的元素上。这样,无论 Modal 组件在组件树中的位置如何,它的内容都会被移动到指定的 DOM 元素上。

注意:

  • Teleport 的实现涉及两个部分: 组件和 to 属性。to 属性指定了目标容器的选择器。
  • to 属性可以是一个字符串,也可以是一个函数。函数的参数是 teleport 目标元素的引用,返回值是一个 DOM 元素,表示 Teleport 的目标容器。
  • Teleport 可以用于任何需要在 DOM 树中的不同位置之间移动组件内容的情况,而不仅仅是模态框。

3.Suspense

是一个用于处理异步组件加载的特殊组件。它允许你在异步组件加载完成之前展示一些备用内容(例如 loading 状态),以提供更好的用户体验。

<template>
  <div>
    <Suspense>
      <!-- 异步组件的引入 -->
      <AsyncComponent />
      
      <!-- 备用内容,可自定义 loading 状态 -->
      <template #fallback>
        <p>Loading...</p>
      </template>
    </Suspense>
  </div>
</template>

<script>
// 异步组件的定义
const AsyncComponent = () => import('./AsyncComponent.vue');

export default {
  components: {
    AsyncComponent
  }
};
</script>

在上面的例子中, 是一个异步组件,通过使用 import() 动态导入。当这个异步组件加载时, 将显示备用内容,直到异步组件加载完成。