当前位置:   article > 正文

Vue3总结- Composition API-vue3与vue2差异_vue2和vue3 components

vue2和vue3 components


前言

转载:https://blog.csdn.net/weixin_47521346/article/details/109185232?spm=1001.2014.3001.5501


一、Vue2 与 Vue3 的差异

下面讲述Vue3破坏性变更的地方

1.全局API

全局api已迁移至 createApp()创建的实例下:

2.x全局API3.x实例API(app)
Vue.config.production已经删除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use

1.use

const app = createApp(MyApp)
app.use(VueRouter)

  • 1
  • 2
  • 3

2.component & directive

代码如下(示例):

const app = createApp(MyApp)

app.component('button-counter', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

app.directive('focus', {
  mounted: el => el.focus()
})

app.mount('#app')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.provide & inject

代码如下(示例):

// 在入口文件
app.provide('guide', 'Vue 3 Guide')

// 在子组件中
export default {
  inject: {
    book: {
      from: 'guide'
    }
  },
  template: `<div>{{ book }}</div>`
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

组件中使用 可参考组合式api中https://v3.cn.vuejs.org/guide/composition-api-provide-inject.html#%E4%BF%AE%E6%94%B9%E5%93%8D%E5%BA%94%E5%BC%8F-property
例如:有时我们需要在注入数据的组件内部更新 inject 的数据。在这种情况下,我们建议 provide 一个方法来负责改变响应式 property
代码如下(示例):

<!-- src/components/MyMap.vue -->
<template>
  <MyMarker />
</template>

<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'

export default {
  components: {
    MyMarker
  },
  setup() {
    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })

    const updateLocation = () => {
      location.value = 'South Pole'
    }

    provide('location', location)
    provide('geolocation', geolocation)
    provide('updateLocation', updateLocation)
  }
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'

export default {
  setup() {
    const userLocation = inject('location', 'The Universe')
    const userGeolocation = inject('geolocation')
    const updateUserLocation = inject('updateLocation')

    return {
      userLocation,
      userGeolocation,
      updateUserLocation
    }
  }
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

后续更新 详情请看转载

二、Composition API

reactive

reactive 基本等价于2.x中的Vue.observable(),返回一个响应式对象,就像2.x中定义在data选项里的数据一样,最终都会被转换成响应式对象。基于 ES2015 的 Proxy 实现。

import { reactive } from 'vue'

// state 现在是一个响应式的状态
const state = reactive({
  count: 0,
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ref

接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性.value

const count = ref(0)   // 相当于返回{value:0}
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

ref 适合基础类型值
reactive 适合对象类型的值
把变量全塞对象里直接用reactive,对象解构的时候,数据会丢失响应式特性

const pos =  reactive({
  x: 0,
  y: 0,
})

function updatePosition(e) {
// 解构对象,导致响应式丢失,相当于重新将值赋给了一个变量,之后的更改不会改变原属性的值
  let {x,y} = pos

  x = e.pageX
  y = e.pageY
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

正因为此,官方提供了toRefs与toRef的函数,来将一个响应式对象的基础类型属性值转换为ref对象

const state = reactive({
  foo: 1,
  bar: 2,
})

const fooRef = toRef(state, 'foo') // 转换单个的foo属性为ref对象

fooRef.value++
console.log(state.foo) // 2

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
const state = reactive({
  foo: 1,
  bar: 2,
})

const stateAsRefs = toRefs(state)  // 转换state对象的所有属性为ref对象
/*
stateAsRefs 的类型如下:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

vue3中reacitve函数如何声明一个响应式数组,如以下案例

<template>
  <div>

      <div v-for="item in arr.list" :key="item"> 
          {{item}}
      </div>

        <button @click="change">change</button>
  </div>
</template>

<script>

  import { defineComponent, reactive,ref } from 'vue';

  export default defineComponent({
    setup(props,context) {
      let arr = reactive({
        list:[]
      })

      function change(){
        console.log("change...");
        let newArr = [1,2,3]
        arr.list = newArr
      }
      

      return{
        arr,
        change
      }

    },
  });
</script>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
<template>
  <div>
      名字:{{ name }}
  </div>
  <!-- toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象 -->
</template>
<script lang="tsx" src="./script"></script>
<style lang="less" src="./index.less" scoped></style>
import { computed, defineComponent, reactive, ref, toRef, toRefs } from 'vue'
import { copyValueObject } from '@/utils/util.ts'

export default defineComponent({
  setup(props, { emit }) {
    let testObj = reactive({
      name: '咳咳咳',
      age: 56,
    })
    setTimeout(() => {
      testObj = copyValueObject(testObj, { name: 'jajjaj', age: 50 })
    }, 500)
    return {
      ...toRefs(testObj),
    }
  },
})
utils.js
export const copyValueObject = (object: any, valueObject: any) => {
  for (const key in object) {
    if (
      valueObject[key] ||
      valueObject[key] === 0 ||
      valueObject[key] === false
    ) {
      object[key] = valueObject[key]
    }
  }
  return object
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

watchEffect

预期接收一个含有副作用的函数,仅当该过程中用到的响应式状态发生改变时,会重新执行该函数。

import { reactive, watchEffect } from 'vue'

const state = reactive({
  count: 0,
})

onMounted(()=>{
    // 立即执行一次,之后会在state.count发生改变的时候执行,组件卸载的时候销毁
    watchEffect(() => {
        document.body.innerHTML = `count is ${state.count}`
    })
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

watch和watchEffect比较
watchEffect和computed比较像,都是通过执行副作用函数获取要监听的数据源,而watch是通过第一参数获取要监听的数据源
watch的参数有3个(数据源,副作用函数,配置),watchEffect只有两个(副作用函数,配置)
watch的副作用函数接收的参数比watchEffect多两个(新旧值)
deep和immediate只对watch有用
① watch可以访问新值和旧值,watchEffect不能
② watchEffect有副作用,DOM挂载或者更新之前就会触发****通过 flush:post可以避免副作用,在DOM更新后运行副作用,确保模板引用与DOM保持同步,并引入正确的元素在执行数据请求时,副作用函数往往是一个异步函数

// 同步的方式
const stop = watchEffect(() => {
  /* ... */
})

// 之后
stop()

// 如果是回调里有异步,可以用回调的参数onInvalidate去取消监听
const data = ref(0)
watchEffect((onInvalidate) => { // 立即执行,其后data改变,组件更新后执行
  console.log(data.value)
  
  const timer = setInterval(()=>{
    data.value ++
  },1000)

  // 第一次初始化时候不执行该回调,仅注册回调,data改变时以及停止侦听时,会触发该回调
  onInvalidate(() => {  
    // 取消定时器
    clearInterval(timer)
  })
})
// output: 0 1

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

onInvalidate 触发时机

副作用即将重新执行时(也就是追踪的依赖发生改变时)
侦听器被停止时(如果在 setup() 或 生命周期钩子函数中使用了 watchEffect, 则在卸载组件时)

三、高级响应式系统API

1.customRef

customRef 用于自定义一个 ref,可以显式地控制依赖追踪和触发响应。

<template>
    <input v-model="text" />
</template>
<script>
function useDebouncedRef(value, delay = 200) {
  let timeout   
  return customRef((track, trigger) => {
    return {
      get() {
        track()  // 调用track收集依赖
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger() // 调用trigger,触发响应
        }, delay)
      },
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello'),
    }
  },
}
</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

markRaw

显式标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身。作用有点类似Object.freeze, 去除响应式。

const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

shallowReactive

与reactive类似,唯一的区别就是只创建“浅代理”,嵌套对象不会变成响应式

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2,
  },
})

// 变更 state 的自有属性是响应式的
state.foo++
// ...但不会深层代理
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

shallowReadonly

与readonly类似,唯一的区别就是只限制“浅只读”。嵌套对象仍然可以赋值

const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2,
  },
})

// 变更 state 的自有属性会失败
state.foo++
// ...但是嵌套的对象是可以变更的
isReadonly(state.nested) // false
state.nested.bar++ // 嵌套属性依然可修改

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

shallowRef

与ref类似,唯一的区别只是“浅引用” ,只会追踪它的 .value 更改操作,但是如果赋值的是一个对象,则该对象不是可响应,并且后续的对象的属性更改均不会触发视图响应

const foo = shallowRef({})
foo.value.a = 1  // 这个a也不会响应到视图上去
isReactive(foo.value) // false
// 更改对操作会触发响应
foo.value = []
// 但上面新赋的这个对象并不会变为响应式对象,只是会同步这个值,视图上会同步显示[]
isReactive(foo.value) // false

const bar = shallowRef(0)
bar.value ++ // 1 , 这个是响应式的

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

toRaw

返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。简单来说就是返回代理之前的原始对象。

const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true

  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号