加载中...

vue3的学习


vue3的学习

了解响应式的原理

vue2的响应式原理

实现的原理是通过 Object.defineProperty() 对属性的读取、修改进行拦截(数据劫持)

//  定义数据
let person = { name: '张三', age: 18 }

// 基于 person 创建响应式对象 data
const data = {};

Object.keys(person).forEach(key => {
  Object.defineProperty(data, key, {
    configurable:true,
    get() {
      // 此处依赖收集
      console.log('访问了', key);
      return person[key];
    },
    set(v) {
      // 此处执行回调更新
      console.log('访问了', key);
      person[key] = v;
    }
  })
})

读取data.name的时候就会触发get函数,修改触发set函数

存在问题: 新增属性、删除属性, 界面不会更新。 直接通过下标修改数组, 界面不会自动更新,对于数组的一些方法检测不到,只能使用$set来对数据继续操作,通过重写更新数组的一系列方法来实现拦截。

对象:
    this.$set(需要修改的对象,需要修改的值,修改的值)
    this.$delete(需要删除的对象,需要删除的值)
或者引入Vue
    import Vue from 'vue'
    Vue.set(需要修改的对象,需要修改的值,修改的值)
    Vue.delete(需要删除的对象,需要删除的值)
数组:
    this.Arr[0]= 'hello'   Arr(这个是需要修改的数组名) 这种方法修改界面是不会刷新的
解决方法
    this.$set(需要修改的数组,数组的索引,修改的值)
    this.Arr.splice(索引,索引开始删除的数量,替换的值)
    例如:this.Arr.splice(01'hello')

Vue3的响应式

vue2存在的问题在vue3得到了解决

vue3通过Proxy(代理): 拦截对象中任意属性的变化, 内部基于 ES6 Proxy 实现,通过代理对象操作源对象内部数据进行操作,再通过Reflect(反射): 对源对象的属性进行操作。

const initData = { value: 1 };

const p = new Proxy(initData, {
  // 读取p的某个属性时调用
  get(target, propName) {
    return Reflect.get(target, propName)
  },
  // 修改p的某个属性、或给p追加某个属性时调用
  set(target, propName, value) {
    Reflect.set(target, propName, value)
  },
  // 删除p的某个属性时调用
  deleteProperty(target, propName) {
    return Reflect.deleteProperty(target, propName)
  }
})

计算属性与监视

computed函数

写法:

// 引入
import {computed} from 'vue'
  setup() {
     // 定义数据
    const data = reactive({
      a: 'z',
      b: 's'
    })
    // 计算属性——简写
    const newData = computed(() => {
      return <这里书写操作的数据>
    })

    // 计算属性——完整
    const newData = computed({
      get() {
        return <这里书写操作的数据>
      },
      // 基于TS
      set(value: string) {
        // 这里书写获得了新的数据和操作数据的方法
      }
    })
    return {
      data,
      newData
    }
  }

watch函数

监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视 (deep配置失效)。

监视reactive定义的响应式数据中某个属性时:deep配置有效。

//情况一:监视ref定义的响应式数据
watch(<需要监视的数据>,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue) // newValue:新值;oldValue:旧值
},{immediate:true})

//情况二:监视多个ref定义的响应式数据
watch([a,b],(newValue,oldValue)=>{
    console.log('a或b变化了',newValue,oldValue)
})

// 情况三:监视reactive定义的响应式数据
watch(<reactive定义的响应式数据>,(newValue,oldValue)=>{
    console.log('数据变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效

//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>a.b,(newValue,oldValue)=>{
    console.log('a的b变化了',newValue,oldValue)
},{immediate:true,deep:true})

//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>a.b,()=>a.c],(newValue,oldValue)=>{
    console.log('a的属性变化了',newValue,oldValue)
},{immediate:true,deep:true})

//特殊情况
watch(()=>a.b,(newValue,oldValue)=>{
    console.log('a的b变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效

watchEffect函数

watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调

watch既要指明监视的属性,也要指明监视的回调

watchEffect会监视函数内部使用的值

watchEffect(()=>{
    const x1 = a.b
    const x2 = c.d
    console.log('watchEffect配置的回调执行了')
})
这里就会去监视a的b属性,c的d属性

provide 与 inject

主要用于父与自己后代的一种通信方式,有点像vue2的消息订阅发布,全局事件总线

父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这 些数据,如下所示:

父组件:
    setup(){
        ...
        // 定义数据
        let data = reactive({name:'zs',age:'18'})
        provide('data',data)
        ...
    }
后代组件:
    setup(){
        ...
        const data = inject('data')
        return {data}
        ...
    }

新的组件

Fragment

vue2中,文件的template标签下必须要有一个根标签,也就是说必须要有一个div包裹,但在vue3中可以不需要用div包裹,因为在vue3的渲染的时候,会自动给我们添加Fragment标签,使用vue的调试工具可以查看

示例

Teleport

这个组件常用于弹窗,能够将我们的 组件html结构移动到指定位置的技术。

示例:

<teleport to="移动位置">
    <div v-if="isShow" class="mask">
        <div class="dialog">
            <h3>我是一个弹窗</h3>
            <button @click="isShow =false">关闭弹窗</button>
        </div>
    </div>
</teleport>

Suspense

该组件用于优化性能,在网络很慢的时候,不需要等待自己定义的组件完全加载完再去呈现内容,会提前加载静态的组件,可以理解为图片的懒加载,首先我们需要将自己定义的组件实现异步引入:

import { defineAsyncComponent } from 'vue'
const demo = defineAsyncComponent(()=>import('./components/demo.vue'))

接下来我们需要使用 Suspense 包裹组件,并配置好 default fallback,因为Suspense 的底层是使用插槽实现的,将需要展示的写在default 下,将懒加载的组件写在fallback下:

<template>
  <div class="app">
    <Suspense>
      <template v-slot:default>
        <demo />
      </template>
      <template v-slot:fallback>
        <h3>加载中.....</h3>
      </template>
    </Suspense>
  </div>
</template>

全局API的转移

vue3针对vue2的这些API做出了调整:

2.x 全局 API( Vue )		 3.x 实例 API ( app )
Vue.config.xxxx 			app.config.xxxx
Vue.config.productionTip 	移除
Vue.component 				app.component
Vue.directive 				app.directive
Vue.mixin 					app.mixin
Vue.use 					app.use
Vue.prototype 				app.config.globalProperties

同时vue3移除keyCode作为 v-on 的修饰符,同时也不再支持 config.keyCodes

移除 v-on.native 修饰符,需要指定的原生事件只需要说明即可,例如:

父组件中绑定事件:
    <my-component
        v-on:close="addList"
        v-on:click="CheckList"
    />
子组件中声明自定义事件:
    <script>
        export default {
        // 没有在emits注明,vue3会将click作为原生的点击事件
        emits: ['close']
        }
    </script>

移除过滤器(filter),官方建议把那些逻辑写在计算属性上

更多详细内容见vue3官网:<Vue.js (vuejs.org)>


文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !