几天前you大大发布消息说vue3已经处在cr阶段了,也就是说,新加的功能已经确定,接下来就是查改bug了。

前不久公司人员分享了一些关于vue3相关的知识,刚好我也对vue3很感兴趣并且之前就进行了了解,所以在这里记录一下。

这篇文章主要以理论为主,代码实践请移步:vue3新特性实践——南征北战

首先来说说you大大最为自豪的改进:Composition API

you大大说以前在2点几的版本中我们使用mixin来混入公用方法或者其它内容,这样做混入的对象来源不明,相同功能代码陈列杂乱,修改起来上翻下翻,对开发者造成困扰。

而使用Composition API,来源明确、相同功能的代码块在一起,修改的时候只需要专注一个地方,更易于维护。

以前我们在代码里都是使用this来访问属性,比如this.foo() this.obj this.$watch,这样vue对外暴露的东西太多,引入第三方组件安全问题无法保障,造成困扰。vue3之后我们将不再使用this,api以模块的方式引入,函数的方式使用。

接下来我们一起来看一下Composition API的实现,我们把源码fork下来,发现vue3的代码结构变了好多有木有!它的文件入口呢?它的核心代码呢?
在这里插入图片描述

原来全都在packages里面了,一个代码部分在一个文件夹里面。公共部分在compiler-core、ssr编译相关在compiler-ssr、与dom编译相关的在compiler-dom…

我们先去找ref这个API,顺藤摸瓜,在reactivity文件夹下的ref.ts里。不要被一堆的重载函数迷惑眼睛,我们直接看实现它的函数:

function createRef(rawValue: unknown, shallow = false) {
  if (isRef(rawValue)) {
    return rawValue
  }
  let value = shallow ? rawValue : convert(rawValue)
  const r = {
    __v_isRef: true,
    get value() {
      track(r, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newVal) {
      if (hasChanged(toRaw(newVal), rawValue)) {
        rawValue = newVal
        value = shallow ? newVal : convert(newVal)
        trigger(
          r,
          TriggerOpTypes.SET,
          'value',
          __DEV__ ? { newValue: newVal } : void 0
        )
      }
    }
  }
  return r
}

createRef返回一个__v_isRef属性为true并进行get、set操作的对象,分别调了track trigger方法。
track方法收集依赖,

export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (!shouldTrack || activeEffect === undefined) {
    return
  }
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
    if (__DEV__ && activeEffect.options.onTrack) {
      activeEffect.options.onTrack({
        effect: activeEffect,
        target,
        type,
        key
      })
    }
  }
}

trigger方法根据映射关系执行对应cb。

然后我们来看toRefs,toRefs循环每个对象属性,Proxy会对每个属性读写操作拦截。

export function toRefs<T extends object>(object: T): ToRefs<T> {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret: any = {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K
): Ref<T[K]> {
  return {
    __v_isRef: true,
    get value(): any {
      return object[key]
    },
    set value(newVal) {
      object[key] = newVal
    }
  } as any
}

因为vue3使用Proxy拦截对象,所以不需要像以前那样使用Object.definepropty挨个定义对象了,Object.definepropty去定义的缺点就是每次创建组件实例的时候开销比较大。
在这里插入图片描述

Fragment
以前我们在template中根节点只能有一个元素标签,vue3以后将支持多个同级标签共存。

<tempalate>
    <div>haha</div>
    <div>hehe</div>
</tempalte>

组件递归操作相比之前会减少很多无用代码,这样一来,我们做一些无尽列表就可以优化很多。

内部做的性能优化
  1. Tree shaking 没有用的代码不打包

  2. 内部模块可以直接拿出来单独引用(按需引入)

  3. 之前vdom是不管动态静态节点全部一股脑diff。vue3做了静态分析。

  4. Ssr编译策略改变,能静态化的就静态字符串化(静态代码部分直接字符串拼接)重写了渲染器

  5. 之前使用this访问属性,而暴露在this上的属性是用object.definepropty去定义,所以每次创建组建实例的时候开销比较大。vie3直接用proxy拦截。

  6. 渲染的时候最大化利用node异步状态。精准处理了服务器异步并发问题。
    动静比越大优势越大

在vue3中,我们可能不能像往常一样写ts装饰器,ts 装饰器对vue3代码可能会有影响。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐