VUE3.0 技术分享

84 阅读3分钟

layout: center

VUE2.0回顾


Object.defineProperty

  • Vue 2 响应式并不是真正意义上的代理,而是基于 Object.defineProperty() 实现的。
1.无法检测到对象的新增、删除。
2.无法检测到Array数据的变化。
const obj = {};
Object.defineProperty(obj, 'a', {
  set(val) {
    console.log(`开始设置新值: ${val}`)
  },
  get() { 
    console.log(`开始读取属性`)
    return 1; 
  },
  writable : true
})

obj.a = 2 // 开始设置新值: 2
obj.a // 开始获取属性 
Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // ...
      if (Dep.target) {
        // 收集依赖
        dep.depend()
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // ...
      // 通知视图更新
      dep.notify()
    }
  })

开发效率问题

  • Vue2 对TS支持不友好,Vue 2 是使用 Flow.js 来做类型校验,Flow.js 已经停止维护了。Vue2 需要vue-class-component强化vue原生组件,也需要vue-property-decorator增加更多结合Vue特性的装饰器,写法比较繁琐。

  • Option API 在组织代码较多组件的时候不易维护。

  • 通用逻辑代码不好复用,虽然提供了mixin,还会带来命名冲突的问题。


layout: center


Proxy

new Proxy(obj, {
  get(target, propKey, receiver) { 
    return Reflect.get(target, propKey, receiver)
  },
  set(target, propkey, value, proxy) {
    return Reflect.set(target, propkey, value, proxy)
   },
   ...
})

Proxy 存在一些兼容性问题, 不兼容 IE11 以下的浏览器


TypeScript

  • 全部模块使用 TypeScript 重构,TS可以带来更方便的代码提示,并且让我们的代码能够更健壮。
let name:string = 'vuejs'
name = 1 // 报错
interface Person {
    name: string;
    age: number;
}
let me:Person = {
  name:'vue',
  age:18
}

me.age = '18' // 报错

layout: center

Composition API


Composition API

Ref

import { ref } from 'vue'

let foo = 0
let bar = ref(0)

foo = 1
bar = 1 // ts-error
bar.value = 1 // success

Reactive

import { reactive } from 'vue'

const foo = { prop: 0 }
const bar = reactive({ prop: 0 })

foo.prop = 1
bar.prop = 1

Composition API

setup

import { ref } from 'vue'

setup() {
    const count = ref(0)

    // 返回值会暴露给模板和其他的选项式 API 钩子
    return {
      count
    }
}

setup

import { ref } from 'vue'

setup(props, { attrs, slots, emit, expose }) {
    ...
}


Composition API

toRefs

import { reactive, toRefs } from "vue"
const state = reactive({
  foo: 1,
  bar: 2
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref<number>,
  bar: Ref<number>
}
*/
// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

toRefs


import { reactive } from "vue"
const state = reactive({
    foo: 1,
    bar: 2
  })

  // ...基于状态的操作逻辑

  // 在返回时都转为 ref
return toRefs(state)


Composition API

watch


import { reactive,watch } from 'vue'

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  },
)

watchEffect


import { ref,watchEffect } from 'vue'

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 输出 0

count.value++
// -> 输出 1


Composition API

computed


import { ref, computed } from 'vue'

//创建一个只读的计算属性 ref:
const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

computed


//创建一个可写的计算属性 ref:
import { ref, computed } from 'vue'

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

Composition API

Composition API带来的好处

  • 所有 API 都是 import 引入的,用到的功能都 import 进来,没有用到的打包的时候会被清理掉 ,减小包的大小。
  • 可以把一个功能模块的 methods、data 都放在一起书写,方便维护。
  • 代码方便复用

CompositionAPI VS optionsApi


layout: center

可组合函数 HOOKS


可组合函数 HOOKS

useDOMCreate

import { onUnmounted } from 'vue'

function useDOMCreate(nodeId: string) {
  const node = document.createElement('div')
  node.id = nodeId
  document.body.appendChild(node)
  onUnmounted(() => {
    document.body.removeChild(node)
  })
}

export default useDOMCreate

useClickOutside


import { ref, onMounted, onUnmounted, Ref } from 'vue'

const useClickOutside = (elementRef: Ref<null | HTMLElement>) => {
  const isClickOutside = ref(false)
  const handler = (e: MouseEvent) => {
    if (elementRef.value) {
      if (elementRef.value.contains(e.target as HTMLElement)) {
        isClickOutside.value = false
      } else {
        isClickOutside.value = true
      }
    }
  }
  onMounted(() => {
    document.addEventListener('click', handler)
  })
  onUnmounted(() => {
    document.removeEventListener('click', handler)
  })
  return isClickOutside
}

export default useClickOutside


可组合函数使用


import { defineComponent, ref, watch } from 'vue'
import useClickOutside from './useClickOutside'
export default defineComponent({
  setup() {
    const isClickOutside = useClickOutside(dropdownRef)

    return {}
  }
})


其他新功能

  • Vue3.0生态(Vite、Pinia、VueRouter、SSR)
  • 新的组件,Vue 3 内置了 Fragment、Teleport 和 Suspense 三个新组件
  • 自定义渲染器
  • <script setup> click here

Thank You!

vue.js | windicss | Monaco