大前端百科全书vue专题之vue3

769 阅读5分钟

大前端百科全书,前端界的百科全书,记录前端各相关领域知识点,方便后续查阅及面试准备

  • 说一下对vue3.0的了解,vue3.0为什么要用代理?
  • 说一下 Vue3 的 Composition API
  • vue Hooks 有哪些?

说一下对vue3.0的了解,vue3.0为什么要用代理?

vue3重写了多种机制

主要是基于:

  • 主流浏览器对新的JavaScript语言特性的普遍支持
  • 当前vue代码库随着时间的推移而暴露出来的设计和体系架构问题

vue3较vue2做了很多优化

  • 1. 重写VDOM机制:通过编译时的标记优化运行时的速度
  • 2. 优化插槽(slot)生成:原来的实现是父组件重渲染时子组件必须同时渲染,而在vue3中子组件提取函数,可以分别渲染。
  • 3. 静态树提升:没有响应式绑定的部分被提取出来作为常量,用到的时候不用再次执行它的渲染函数
  • 4. 静态属性提升:没有响应式绑定的组件属性(props)被提取出来作为常量,用到的时候不用再自行创建。
  • 5. 项目结构优化:内部解耦、更好维护,支持了细粒度的tree-shaking,比如可选的生命周期

Object.defineProperty与Proxy

在vue2中,Object.defineProperty会改变原始数据,而Proxy是创建对象的虚拟表示,并提供set、get和deleteProperty等处理器,这些处理器可在访问或修改原始对象上的属性时进行拦截,有以下特点:

  • 不需要使用 Vue.setVue.set 或 Vue.delete 触发响应式
  • 全方位的数组变化检测,消除了vue2无效的边界情况
  • 支持Map、Set、WeakMap和WeakSet。

Proxy 实现的响应式原理与vue2的实现原理相同,实现方式大同小异:

  • 1、get收集依赖

  • 2、set、delete等触发依赖

  • 3、对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执行依赖相关的收集或触发逻辑

说一下 Vue3 的 Composition API

首先要了解一下 Composition API 设计的好处在哪里? 逻辑组合和复用、类型推导、打包尺寸

在 vue3.0 之前所有的组件都可以看作一个可选项的配置集合,通过 data、computed、methods、watch、以及 created、mounted 等声明周期函数,用这个可选项集合来声明一个组件。

这样写的好处是组织结构清晰,但是在逻辑复用上就不太友好啦,我们都知道的是 js 中最简洁清晰的复用方式就是将逻辑封装到一个函数中,然后函数与函数之间相互调用。

Vue3.0 很好的支持 TS,而 TS 的最重要的一个特性就是类型推导,而函数相对于嵌套的对象来说对类型推导更加友好

另外,以函数形式组织的模块以具名方式导入使用,在tree-sharking的时候支持会更好

vue Hooks 有哪些?

什么是 Hooks

hooks 字面意思就是钩子函数,那么钩子函数的定义是什么呢?

钩子函数:在一个事件触发的时候,在系统级捕获到了它,然后做一些操作。一段用以处理系统消息的程序。钩子就是在某个阶段给你一个做某些处理操作的机会-----类似回调函数

钩子函数:

一个函数/方法,在系统消息触发时被系统调用,例如click等事件调用

不是用户自己触发的,例如发布订阅者模式的方法的实现

钩子函数的名称时确定的,当系统消息触发后,自动会调用

例如Vue的watch()函数,用户只需要编写watch()的函数体里面的函数,当页面元素发生变化的时候,系统就会先调用watch()

例如react的componentWillUpdate函数,用户只需要编写componentWillUpdate的函数体,当组件状态发生改变更新的时候,系统就会调用componentWillComponent

Vue Hooks 就是一些 vue 提供的内置函数,这些函数可以让 Function Component 和 Class Component 一样能够拥有组件状态(state)以及进行副作用(side effect)

为什么使用 Vue Hooks?

首先从 Class-component/Vue-options 开始说起

  • 跨组件代码难以复用
  • 大组件,维护困难,颗粒度不好控制,细粒度划分时,组件嵌套层次太深会影响性能
  • 类组件,this 不可控,逻辑分散,不容易理解
  • mixins 具有副作用,逻辑互相嵌套,数据来源不明,且不能互相消费

当一个模板依赖很多 mixin 的时候,很容易出现数据来源不清或者命名冲突的问题,而且开发 mixins 的时候,逻辑以及逻辑依赖的属性互相分散且 mixins 之间不可互相消费。这些都是开发中令人痛苦的点,因此 vue3.0 中引入 hooks 相关的特性非常明智

常用的 hooks 讲解

1、withHooks

const Foo = withHooks((h) => {
  // state
  const [count, setCount] = useState(0);

  // effect
  useEffect(() => {
    document.title = "count is " + count;
  });

  return h("div", [
    h("span", `count is: ${count}`),
    h(
      "button",
      {
        on: {
          click: () => setCount(count + 1),
        },
      },
      "+"
    ),
  ]);
});

withHooks 是一个高阶函数,传入一个函数,这个函数内部返回一个 vnode,withHooks 方法返回的是一个 vue 的选项对象

Foo = {
  created() {},
  data() {},
  render() {},
};

这个选项对象可以直接调用 Vue.component 方法生成全局组件,或者在 render 方法中生成 vnode

2、useState

useState 理解起来很简单,和 Class Component 的 vuex 中 state 是一样的,都是用来管理组件状态的。因为 Function Component 每次执行的时候都会生成新的函数作用域所以统一组件的不同渲染(render)之间是不能够共用状态的,因此开发者一旦需要在组件中引入状态就需要将原来的 Funtion Component 改为 Class Component,这使得开发者的体验十分不好。useState 就是用来解决这个问题的,它允许 Function Component 将自己的状态持久化到 vue 运行时的某个地方,这样在组件每次渲染的时候都可以从这个地方拿到该状态,而且当该状态被更新的时候,组件也会重渲染

//声明
const [count, setcount] = useState(0)
const [state, setState] = useState({
    status: 'pending',
    data: null,
    error: null
})
const handleTextChange(value) => {
    setText({
        status: 'changed',
        data: value,
        error: null
    })
}
//引用
<div>{count}</div>
< ... onClick= setcount(count + 1) ... >
<div>{state}</div>
onChange=handleTextChange(count)

useState 接收一个 initial 变量作为状态的初始值,返回值是一个数组。

返回数组的第一个元素代表当前 state 的最新值,第二个元素是一个用来更新 state 的函数。这里要注意的是 state 和 setState 这两个变量的命名不是固定的,应该根据你业务的实际情况选择不同的名字,可以是 setA 或 setB,需要注意的是 setState 这个是全量替代

我们在实际开发中,一个组件可能不止一个 state,如果组件有多个 state,则在组件内部多次调用 useState,这些使用类似 Vuex 里面的 state 的使用方式

3、useEffect

useEffect 用于添加组件状态更新后,需要执行的副作用逻辑

useEffect 指定的副作用逻辑,会在组件挂载后执行一次、在每次组件渲染后根据指定的依赖有选择地执行、并在组件卸载时执行清理事件的逻辑

import { withHooks, useState, useEffect } from "vue-hooks";

const Foo = withHooks((h) => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = "count is " + count;
  });
  return h("div", [
    h("span", `count is: ${count}`),
    h("button", { on: { click: () => setCount(count + 1) } }, "+"),
  ]);
});

代码中,通过 useEffect 时,每当 count 的状态值发生变化时,都会重置 document.title。这里没有指定 useEffect 的第二个参数 deps,表示只要组件重新渲染都会执行 useEffect 指定的逻辑,不限制必须是 count 变化时

4、useRef

useRef 是用来在组件不同渲染之间共用的一些数据的,它的作用和我们在 Vue Class Component 里面为$refs.xxx 赋值是一样的。那么它的一些特性就跟 refs 是类似:

  • 组件更新之后,可以获取最新的状态、值
  • 值不需要响应式处理
  • 独立于其他作用域之外,不污染其他作用域
  • useRef 返回的是对象
const [count, setcount] = useState(0);
const num = useRef(count);
const addCount = () => {
  let sum = count++;
  setcount(sum);
  num.current = sum;
  console.log(count, num.current);
};
//得到的结果是
// 0 1
// 1 2
// 2 3
// ...

5、useData

useData 可以理解为 Vue Class Funtion 里面的$data,也可以认为与 useState 类似。不同的是:useState 不提供更新器。只是作为数据变量的声明、修改、调用

// 声明
const data = useData({
  count: 0,
});
// 调用
console.log(data.count);

6、useMounted

useMounted 需要在 mounted 事件中执行的逻辑

useMounted(() => {
  console.log("mounted!");
});

7、useDestroyed

useDestroyed 需要在 destroyed 事件中执行的逻辑

useDestroyed(() => {
  console.log("destroyed!");
});