快速掌握-vue3

news2024/11/16 7:29:07

是什么

vue2 的升级版, 使用 ts 重构了代码, 带来了 Composition API RFC。 类似于 react hook 的写法。

  1. ts 重构,代码可读性更强
  2. vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
  3. 实现了 TreeShaking (当 Javascript 项目达到一定体积时,将代码分成模块会更易于管理。但是,当这样做时,我们最终可能会导入实际上未使用的代码。Tree Shaking 是一种通过消除最终文件中未使用的代码来优化体积的方法。)
  4. 支持 hook 写法 CompositionAPI。受 ReactHook 启发
  5. 支持 jsx
  6. Vue 3 的 Template 支持多个根标签,Vue 2 不支持
  7. 对虚拟 DOM 进行了重写、对模板的编译进行了优化操作
  8. 在 Vue2.x 中具名插槽和作用域插槽分别使用 slot 和 slot-scope 来实现, 在 Vue3.0 中将 slot 和 slot-scope 进行了合并 v-slot
  9. 在 Vue 3 中对自定义指令的 API 进行了更加语义化的修改,名称和组件生命周期名称相同
  10. v-model 变更:在自定义组件上使用 v-model 时,同一组件可以同时设置多个 v-model, 开发者可以自定义 v-model 修饰符

学到什么

  1. vue3 与 vue2 的核心区别
  2. Tree-Shaking
  3. 函数 setup()
  4. 函数 ref()
  5. 函数 isRef()
  6. 函数 toRefs()
  7. 函数 reactive()
  8. 函数 computed()、watch()
  9. LifeCycle Hooks(新的生命周期)
  10. Template refs
  11. vue3 的全局配置
  12. vue3 组件模板结构
  13. 实现 自定义 Hook
  14. 组件 teleport 任意门
  15. 组件 异步组件

vue3 与 vue2 的核心区别

  1. vue3.x 将使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
  2. Object.defineProperty 只能劫持对象的属性, 而 Proxy 是直接代理对象,由于 Object.defineProperty 只能劫持对象属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。但是 Proxy 直接代理对象,不需要遍历操作
  3. Object.defineProperty 对新增属性需要手动进行 Observe

Tree-Shaking

当 Javascript 项目达到一定体积时,将代码分成模块会更易于管理。但是,当这样做时,我们最终可能会导入实际上未使用的代码。Tree Shaking 是一种通过消除最终文件中未使用的代码来优化体积的方法。

为什么:因为 Vue 实例是作为单个对象导出的,打包器无法分辨出代码中使用了对象的哪些属性。所以,我们需要单独引用

抽离了 一部分 vue2 中的公用函数,需要单独引用。以前的全局 API 现在只能通过具名导入,这一更改会对以下 API 有影响:

  • Vue.nextTick
  • Vue.observable(用 Vue.reactive 替换)
  • Vue.version
  • Vue.compile(仅限完整版本时可用)
  • Vue.set(仅在 2.x 兼容版本中可用)
  • Vue.delete(与上同)

setup 函数

组件提供的新属性,为了使用 vue3 CompositionAPI 新特性而启用的函数,它有自己独立的生命周期

vue3 取消了 beforeCreate 、created 两个钩子函数,统一用 setup 代替

  1. props 用来接收 props 数据
  2. context 上下文对象
  3. return 返回模板中需要使用的函数
setup(props, context) {
    context.attrs
    context.slots
    context.emit

    return {

    }
  }

ref() 函数

组件提供的新特性函数

创建一个响应式的数据对象,这个对象是响应式的,只返回一个 { value: ""} 值

import { defineComponent, ref } from "vue";
export default defineComponent({
  setup() {
    const name = ref < string > "hello";
    // 在js 中获取ref 中定义的值, 需要通过value属性
    console.log(name.value);
    return {
      name,
    };
  },
});

isRef() 函数

组件提供的新特性函数

isRef() 用来判断某个值是否为 ref() 创建出来的对象

import { defineComponent, isRef, ref } from "vue";
export default defineComponent({
  setup(props, context) {
    const name: string = "vue";
    const age = ref<number>(18);
    console.log(isRef(age)); // true
    console.log(isRef(name)); // false
    return {
      age,
      name,
    };
  },
});

toRefs() 函数

组件提供的新特性函数

toRefs() 函数可以将响应式对象,转换为普通的对象。只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据

import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
  setup(props, context) {
    let state = reactive({
      name: "hello",
    });
    const age = ref(18);
    return {
      ...toRefs(state),
      age,
    };
  },
});

reactive() 函数

组件提供的新特性函数

reactive() 函数接收一个普通对象,返回一个响应式的数据对象, 想要使用创建的响应式数据也很简单,创建出来之后,在 setup 中 return 出去,直接在 template 中调用即可

import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
  setup(props, context) {
    let state = reactive({
      name: "hello",
    });

    return state;
  },
});

computed()、watch() 函数

组件提供的新特性函数

computed() 函数 ,用来计算属性,返回的值是一个 ref 对象。

import { computed, defineComponent, ref } from "vue";
export default defineComponent({
  setup(props, context) {
    const age = ref<number>(18);

    const computedAge = computed({
      get: () => age.value + 1,
      set: (value) => age.value + value,
    });
    // 为计算属性赋值的操作,会触发 set 函数, 触发 set 函数后,age 的值会被更新
    age.value = 100;
    return {
      age,
      computedAge,
    };
  },
});

watch() 函数,用来监听属性, 当数据源变化的时候才会被执行。

import { computed, defineComponent, reactive, toRefs, watch } from "vue";
interface Person {
  name: string;
  age: number;
}
export default defineComponent({
  setup(props, context) {
    const state = reactive<Person>({ name: "vue", age: 10 });

    watch(
      [() => state.age, () => state.name],
      ([newName, newAge], [oldName, oldAge]) => {
        console.log(newName);
        console.log(newAge);

        console.log(oldName);
        console.log(oldAge);
      }
    );
    // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调
    state.age = 100;
    state.name = "vue3";
    return {
      ...toRefs(state),
    };
  },
});

LifeCycle Hooks 生命周期

组件提供的新特性函数

生命周期组件中新的写法

import { set } from "lodash";
import {
  defineComponent,
  onBeforeMount,
  onBeforeUnmount,
  onBeforeUpdate,
  onErrorCaptured,
  onMounted,
  onUnmounted,
  onUpdated,
} from "vue";
export default defineComponent({
  setup(props, context) {
    onBeforeMount(() => {
      console.log("beformounted!");
    });
    onMounted(() => {
      console.log("mounted!");
    });

    onBeforeUpdate(() => {
      console.log("beforupdated!");
    });
    onUpdated(() => {
      console.log("updated!");
    });

    onBeforeUnmount(() => {
      console.log("beforunmounted!");
    });
    onUnmounted(() => {
      console.log("unmounted!");
    });

    onErrorCaptured(() => {
      console.log("errorCaptured!");
    });

    return {};
  },
});

模板 Template refs

组件提供的新特性函数

通过 refs 来回去真实 dom 元素,onMounted 中可以得到 ref 的 RefImpl 的对象, 通过.value 获取真实 dom

<template>
  <div class="mine" ref="elmRefs">
    <span>hello</span>
  </div>
</template>

<script lang="ts">
import { set } from "lodash";
import { defineComponent, onMounted, ref } from "vue";
export default defineComponent({
  setup(props, context) {
    // 获取真实dom
    const elmRefs = ref<null | HTMLElement>(null);
    onMounted(() => {
      console.log(elmRefs.value); // 得到一个 RefImpl 的对象, 通过 .value 访问到数据
    });

    return {
      elmRefs,
    };
  },
});
</script>

vue 的全局配置

Vue3 可以在组件用通过 getCurrentInstance() 来获取全局 globalProperties 中配置的信息

const app = Vue.createApp({});
app.config.globalProperties.$http = "axios";

setup( ) {
  const { ctx } = getCurrentInstance();
  ctx.$http
}

vue3 组件模板结构

<template>
  <div class="mine" ref="elmRefs">
    <span>{{ name }}</span>
    <br />
    <span>{{ count }}</span>
    <div>
      <button @click="handleClick">测试按钮</button>
    </div>

    <ul>
      <li v-for="item in list" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  getCurrentInstance,
  onMounted,
  PropType,
  reactive,
  ref,
  toRefs,
} from "vue";

interface IState {
  count: 0;
  name: string;
  list: Array<object>;
}

export default defineComponent({
  name: "demo",
  // 父组件传子组件参数
  props: {
    name: {
      type: String as PropType<null | "">,
      default: "vue3.x",
    },
    list: {
      type: Array as PropType<object[]>,
      default: () => [],
    },
  },
  components: {
    /// TODO 组件注册
  },
  emits: ["emits-name"], // 为了提示作用
  setup(props, context) {
    console.log(props.name);
    console.log(props.list);

    const state = reactive<IState>({
      name: "vue 3.0 组件",
      count: 0,
      list: [
        {
          name: "vue",
          id: 1,
        },
        {
          name: "vuex",
          id: 2,
        },
      ],
    });

    const a = computed(() => state.name);

    onMounted(() => {});

    function handleClick() {
      state.count++;
      // 调用父组件的方法
      context.emit("emits-name", state.count);
    }

    return {
      ...toRefs(state),
      handleClick,
    };
  },
});
</script>

实现 自定义 Hook

功能性组件可以封装成 hook, 以 use 作为前缀,和普通的函数区分

import { ref, Ref, computed } from "vue";

type CountResultProps = {
  count: Ref<number>;
  multiple: Ref<number>;
  increase: (delta?: number) => void;
  decrease: (delta?: number) => void;
};

export default function useCount(initValue = 1): CountResultProps {
  const count = ref(initValue);

  const increase = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value += delta;
    } else {
      count.value += 1;
    }
  };
  const multiple = computed(() => count.value * 2);

  const decrease = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value -= delta;
    } else {
      count.value -= 1;
    }
  };

  return {
    count,
    multiple,
    increase,
    decrease,
  };
}

使用 hook

<template>
  <p>count: {{ count }}</p>
  <p>倍数: {{ multiple }}</p>
  <div>
    <button @click="increase()">加1</button>
    <button @click="decrease()">减一</button>
  </div>
</template>

<script lang="ts">
import useCount from "../hooks/useCount";
 setup() {
    const { count, multiple, increase, decrease } = useCount(10);
        return {
            count,
            multiple,
            increase,
            decrease,
        };
    },
</script>

组件 teleport 任意门

目的: 即希望继续在组件内部使用 Dialog, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中

场景: 弹框

我们可以用<Teleport>包裹 Dialog, 此时就建立了一个传送门,可以将 Dialog 渲染的内容传送到任何指定的地方。使用 teleport 组件,通过 to 属性,指定该组件渲染的位置与 <div id="app"></div> 同级,也就是在 body 下,但是 Dialog 的状态 dialogVisible 又是完全由内部 Vue 组件控制.

<body>
  <div id="app"></div>
  <div id="dialog"></div>
</body>
// Dialog.vue
<template>
  <teleport to="#dialog">
    <div class="dialog">
      <div class="dialog_wrapper">
        <div class="dialog_header" v-if="title">
          <slot name="header">
            <span>{{ title }}</span>
          </slot>
        </div>
      </div>
      <div class="dialog_content">
        <slot></slot>
      </div>
      <div class="dialog_footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </teleport>
</template>

// Footer.vue 子组件

<div class="footer">
    ...
    <Dialog v-if="dialogVisible"></Dialog>
</div>

组件 异步组件

Vue3 中 使用 defineAsyncComponent 定义异步组件,配置选项 component 替换为 loader ,Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise。

<template>
  <!-- 异步组件的使用 -->
  <AsyncPage />
</template>

<script>
  import { defineAsyncComponent } from "vue";

  export default {
    components: {
      // 无配置项异步组件
      AsyncPage: defineAsyncComponent(() => import("./NextPage.vue")),

      // 有配置项异步组件
      AsyncPageWithOptions: defineAsyncComponent({
        loader: () => import(".NextPage.vue"),
        delay: 200,
        timeout: 3000,
        errorComponent: () => import("./ErrorComponent.vue"),
        loadingComponent: () => import("./LoadingComponent.vue"),
      }),
    },
  };
</script>

参考

  1. vue3 官网 https://v3.vuejs.org/
  2. 构建 vite https://github.com/vitejs/vite
  3. vue3 源码 https://github.com/vuejs/vue-next
  4. vue-cli https://cli.vuejs.org/
  5. vue-router https://github.com/vuejs/vue-router-next
最后编辑于:2024-09-24 21:09:17


喜欢的朋友记得点赞、收藏、关注哦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自闭症寄宿学校:为孩子发掘多重才能

在教育的广阔天地里&#xff0c;每一片土壤都孕育着不同的生命&#xff0c;每一颗种子都蕴含着无限的可能。对于自闭症儿童而言&#xff0c;他们的世界或许更加独特与复杂&#xff0c;但同样充满了未被发掘的潜能与才华。在广州&#xff0c;星贝育园自闭症儿童寄宿制学校正以满…

计算机毕业设计 Java酷听音乐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

师生健康信息管理:SpringBoot技术突破

第4章 系统设计 4.1 系统体系结构 师生健康信息管理系统的结构图4-1所示&#xff1a; 图4-1 系统结构 登录系统结构图&#xff0c;如图4-2所示&#xff1a; 图4-2 登录结构图 师生健康信息管理系统结构图&#xff0c;如图4-3所示。 图4-3 师生健康信息管理系统结构图 4.2…

【Linux】用虚拟机配置Ubuntu环境

目录 1.虚拟机安装Ubuntu系统 2.Ubuntu系统的网络配置 3.特别声明 首先我们先要下载VMware软件&#xff0c;大家自己去下啊&#xff01; 1.虚拟机安装Ubuntu系统 我们进去之后点击创建新的虚拟机&#xff0c;然后选择自定义 接着点下一步 再点下一步 进入这个界面之后&…

element-ui 通过按钮式触发日期选择器

element ui 写在前面1. 自定义的日期时间组件CustomDatePicker.vue2. 页面效果总结写在最后 写在前面 需求&#xff1a;elementui中日期时间选择器&#xff0c;目前只能通过点击input输入框触发日期选择器&#xff0c;我希望能通过其他方式触发日期选择器同时把input输入框去掉…

Spring的IOC和DI入门案例分析和实现

前言 IOC和DI是spring的核心之一&#xff0c;那我们为什么要使用spring技术呢&#xff1f;spring技术的优点在哪里&#xff1f; spring的特点&#xff1a; 简化开发&#xff0c;降低企业级开发的复杂性框架整合&#xff0c;高效整合其他技术&#xff0c;提高企业级应用的开发与…

【Python报错已解决】TypeError: ‘NoneType‘ object is not callable

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

【常读常悟】《大数据之路-阿里巴巴大数据实践》一书读书摘要

【常读常悟】《大数据之路-阿里巴巴大数据实践》一书读书摘要 1、背景2、目录结构3、数据加工链路4、章节摘要4.1 第2章 日志采集4.1.1 日志采集方案4.1.2 采集指标 4.2 第3章 数据同步4.2.1 数据的特点4.2.2 数据同步的三种方式4.2.3 数据同步的最佳实践 4.3 第4章 离线数据开…

LabVIEW自动生成NI-DAQmx代码

在现代数据采集和控制系统中&#xff0c;LabVIEW被广泛应用于各种工业和科研领域。其中&#xff0c;NI-DAQmx是一个强大的驱动程序&#xff0c;可以帮助用户高效地管理和配置数据采集任务。本文将介绍如何在LabVIEW中通过DAQ Assistant Express VI和任务常量自动生成NI-DAQmx代…

VBA字典与数组第十九讲:VBA中动态数组的定义及创建

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

【论文笔记】Visual Instruction Tuning

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Instruction Tunin…

Linux线程(二)线程ID及创建线程详解

1.线程ID 就像每个进程都有一个进程 ID 一样&#xff0c;每个线程也有其对应的标识&#xff0c;称为线程 ID。进程 ID 在整个系统中是唯一的&#xff0c;但线程 ID 不同&#xff0c;线程 ID 只有在它所属的进程上下文中才有意义。 进程 ID 使用 pid_t 数据类型来表示&#xf…

【Linux进程间通信】Linux匿名管道详解:构建进程间通信的隐形桥梁

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux进程间通信 &#x1f4d2;1. 进程间通信介绍&#x1f4da;2. 什么是管道&#x1f4dc;3…

22.1 k8s不同role级别的服务发现

本节重点介绍 : 服务发现的应用3种采集的k8s服务发现role 容器基础资源指标 role :nodek8s服务组件指标 role :endpoint部署在pod中业务埋点指标 role :pod 服务发现的应用 所有组件将自身指标暴露在各自的服务端口上&#xff0c;prometheus通过pull过来拉取指标但是promet…

期权卖方如何选择铁矿石行权价?期权策略盈亏分析计算方式详解

截止9月30日收盘&#xff0c;铁矿石2411合约收盘价825元/吨。日线级别处于上涨趋势中 假设以825元为最新价&#xff0c;假设后市铁矿石期货价格会下跌&#xff0c;期权卖方应该如何选择行权&#xff1f; 卖出行权价800的看涨期权&#xff0c;期权报价37.9&#xff0c;一手权利…

【环境配置】科研小白Windows下安装Git

2024年小白使用Win10安装Git 2.46.2教程&#xff1a; 1 下载安装包 访问下载地址 Git - Downloading Package (git-scm.com) 下载之后打开文件 2 安装过程 点击Next 2.1 选择安装路径 2.2 选择勾选必要组件 2.3 一路Next 这一步直接Next即可 继续点击Next 继续点击Ne…

Linux学习之路 -- 线程 -- 线程池

前面介绍了条件变量的生产消费模型&#xff0c;下面介绍一下条件变量的另一个用法&#xff0c;那就是线程池。线程池的用法其实就是先创建一批线程&#xff0c;然后让这些线程从任务队列中取数据。具体就是生产消费者模型&#xff0c;(我的代码中生产线程只有一个并且生产的任务…

自动微分-梯度!

前言背景知识&#xff1a; 梯度下降(Gradient descent,GD) 正文&#xff1a; 自动微分为机器学习、深度学习神经网络的核心知识之一&#xff0c;若想更深一步使用神经网络进行具体问题研究&#xff0c;那么自动微分不得不了解。 “工欲善其事&#xff0c;必先利其器”&…

数据结构 ——— 单链表oj题:合并两个升序链表

目录 题目要求 手搓两个简易链表 代码实现 题目要求 将两个升序链表合并为一个新的升序链表并返回&#xff0c;新链表是通过拼接给定的两个链表的所有节点组成的 手搓两个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct …