您现在的位置是:首页 >其他 >【vue3】需要了解哪些【未完结】网站首页其他

【vue3】需要了解哪些【未完结】

米奇妙妙wuu 2024-06-22 18:01:03
简介【vue3】需要了解哪些【未完结】

一.vue3入门

1. vue3带来了什么

  1. 更快的渲染速度:Vue3使用了Proxy代理对象,可以更快地跟踪数据变化,从而提高渲染速度。

  2. 更小的体积:Vue3的体积比Vue2更小,同时也支持按需加载,减少了页面加载时间。

  3. 更好的TypeScript支持:Vue3对TypeScript的支持更加完善,可以更好地进行类型检查和代码提示。

  4. 更好的组件封装:Vue3引入了Composition API,可以更好地封装组件逻辑,使得组件更加可复用和易维护。

  5. 更好的响应式系统:Vue3的响应式系统进行了重构,可以更好地处理嵌套对象和数组的变化,同时也提供了更多的API来处理响应式数据。

    总之,Vue3带来了更好的性能、更小的体积、更好的TypeScript支持、更好的组件封装和更好的响应式系统,使得开发者可以更加高效地开发Vue应用。

2. vue3工程的创建

1. 使用vue-cli创建

vue-cli官网

2. 使用vite创建

vite官网

与webpack的区别

  1. 底层原理:vite通过go语言实现,性能好,打包速度快很多,比webpack快10-100倍
  2. vite采用预构建,会把所有的模块做预编译,打包构建时只更新有修改的模块,webpack项目越大打包速度越慢
  3. vite有缓存,工具模块和包依赖等采用强缓存,直接缓存到浏览器,不会再重新加载。对可能发生变化的模块做协商缓存,只编译有变化的部分

二、常用Composition API

vue3中的组合API都是函数,使用时需要先引入(setup不需要引入)

1. setup(函数和语法糖)

  1. 是vue3的一个新配置项,值是一个函数
  2. 执行时间:在beforeCreate之前,即组件创建之前
  3. 这就意味着在setup函数中 this 还不是组件实例,this 此时是 undefined
  4. 在模版中需要使用的数据和函数,需要在 setup 返回
  5. setup的参数:
    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。
      • slots: 收到的插槽内容, 相当于 this.$slots。
      • emit: 分发自定义事件的函数, 相当于 this.$emit。
  6. 组件中所用到的:数据(vue2中的data)、方法(vue2中的methods)、生命周期等,均需要写在setup中,且需要被返回后才可以在模板中使用
  7. etup有两种返回值
    1. 返回一个对象,则对象中的属性、方法,在模板中可以直接使用
    2. 返回一个渲染函数,则可以自定义渲染的内容
// import {h} from 'vue'
export default {
name: 'App',
setup() {
  // 数据(相当于vue2中的data)
  let name = 'dudu'
  // 方法(相当于vue2中的methods)
  const sayHi = () => {
    console.log(`hi,${name}`)
  }
  // 返回一个对象
  return {
    name,
    sayHi
  }
  // 返回一个渲染函数
  // return () => {
  //   return h('要返回的元素','要返回的内容')
  // }
},
}

  1. 尽量不要vue2、vue3混用
  • vue3可以向下兼容vue2的data、methods,vue2的data、methods可以通过this获取setup中数据和方法
  • setup里不可以读取data、methods中数据、方法
  • 如果vue2、vue3有重名,setup优先
  1. setup不能是一个async函数,如果使用了async返回的就不是一个对象,而是promise,模板中也就不能使用。但是可以和Suspense、异步组件搭配使用async
  2. setup语法糖可以直接在script标签中写,不需要返回值
<script setup>
// 按需引入
import { ref, reactive, onMounted } from "vue";
import { ElMessage } from "element-plus";
import axios from "axios";
// 定义响应式数据
const showButton = ref(false);
const value1 = ref("");
const inputValue = ref("");
// 定义函数
const handleCommand = (command) => {
  ElMessage(`click on item ${command}`);
};
const handleFocus = () => {
  showButton.value = true;
};
</script>

2. ref 和reactive

  • ref
    • 作用:定义基本类型的响应式数据
    • JS操作数据: xxx.value
    • 模板中读取数据不需要 .value
    • 接收的数据(initValue)可以是:基本类型、也可以是对象类型
    • 基本类型的数据:响应式是通过Object.defineProperty()的get与set完成
    • 对象类型的数据:通过vue3的新函数——reactive函数实现(proxy)
  • reactive作用:定义对象类型的响应式数据(基本类型用ref)
    • 接收一个对象或数组,返回一个代理对象(proxy对象)
    • reactive定义的响应式数据是深层次的
    • 内部基于ES6的proxy实现,通过代理对象对源对象内部数据进行操作
  • 区别
    • 定义
      ref :定义基本数据类型
      reactive:定义对象或数组类型的数据
    • 原理
      ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)
      reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部数据
    • 使用:
      ref定义的数据:操作数据需要 .value ,读取数据时模板中不需要 .value
      reactive定义的数据:操作和读取都不需要 .value
      一般使用 reactive 较多,可以将基本类型的数据封装在对象里,然后再使用reactive

3. vue3的响应式原理

1. Vue2 的响应式原理

  • 实现原理
    • 对象类型:通过Object.defineProperty()对属性读取、修改进行拦截(数据劫持)
    • 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹)
  • 存在的问题
    • 新增属性、删除属性,界面不会更新
    • 直接通过下表修改数组,界面不会自动更新

2.vue3的的响应式原理

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
  • 通过Reflect(反射): 对源对象的属性进行操作

4.computed

  • 与Vue2中computed配置功能一致
  • 使用vuex中的state时,需要用到响应式
  • 给computed传入函数,返回值就是计算属性的值
    给computed传入对象,get获取计算属性的值,set监听计算属性改变。这种写法适用于需要读写计算属性的情况
setup(){
      ...
  	//计算属性——简写
      let fullName = computed(()=>{
          return person.firstName + '-' + person.lastName
      })
      //计算属性——完整
      let fullName = computed({
      	// 读
          get(){
              return person.firstName + '-' + person.lastName
          },
          // 写
          set(value){
              const nameArr = value.split('-')
              person.firstName = nameArr[0]
              person.lastName = nameArr[1]
          }
      })

5.watch

  • 监视单个数据
// 监视(简单写法)
    /**
     * 第一个参数:要监视的内容
     * 第二个参数:监视的值:newValue、oldValue
     */
  watch(sum,(newVal,oldVal) => {
    console.log('sum值发生了变化')
  })

  • 监视多个数据(可以写多个watch,也可以写成数组形式)
watch([xxx,xxxxx],(newVal,oldVal) => {
      console.log('监视多个内容!',newVal,oldVal)
    })

  • 参数问题
    • 第一个参数:要监视的内容
    • 第二个参数:监视的值:newValue、oldValue
    • watch的第三个参数为相关配置
      immediate:是否立即监听
      deep:深度监听
watch([xxx,xxxxxx],(newVal,oldVal) => {
      console.log('监视多个内容!',newVal,oldVal)
    },{
      immediate:true,
      deep:true
    })

注意有坑!!!

  1. 坑1: 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
/* 监视reactive定义的响应式数据
  	若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
  	若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
  */
  watch(person,(newValue,oldValue)=>{
  	console.log('person变化了',newValue,oldValue)
  },{immediate:true,deep:false}) //此处的deep配置不再奏效

  1. 坑2: 监视reactive定义的响应式数据中某个或某些属性时:deep配置有效
    监视某个或某些属性要写成函数形式,返回需要监视的属性
//监视reactive定义的响应式数据中的某些属性
  watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
  	console.log('person的job变化了',newValue,oldValue)
  },{immediate:true,deep:true}

  • watch监视ref时的value问题
    基本类型数据:监视的是数据对应的实例对象,加上了.value 表示监视ref的值
    对象或数组:需要加上 .value 或者开启深度监视

6. watchEffect函数

  • watchEffect函数中直接写回调函数,不需要指定监视的内容
  • 回调函数内部使用的数据就是被监视的数据

watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性

7.watch 和 computed 的区别

  • 缓存
    computed支持缓存,相依赖的数据发生改变才会重新计算;
    watch不支持缓存,只要监听的数据变化就会触发相应操作
  • 异步
    computed不支持异步,当computed内有异步操作时是无法监听数据变化的;
    watch支持异步操作
  • 数据
    computed属性的属性值是一函数,函数返回值为属性的属性值,computed中每个属性都可以设置set与get方法。
    watch监听的数据必须是data中声明过或父组件传递过来的props中的数据,当数据变化时,触发监听器

8.watch和watchEffect的区别

watchwatchEffect 都是 Vue 3 中的响应式 API,它们的主要区别在于:

  1. watch 是一个有返回值的函数,需要手动停止监听,而 watchEffect 是一个自动清理的函数,不需要手动停止监听。
  2. watch 可以监听多个数据源,而 watchEffect 只能监听一个数据源
  3. watch 可以通过第三个参数 options 来配置监听行为,比如是否立即执行回调函数、是否深度监听等,而 watchEffect 没有这些配置选项。
  4. watch具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行(设置immediate: true时可以变为非惰性,页面首次加载就会执行)。watchEffect立即执行,没有惰性,页面的首次加载就会执行

9. watchEffect与computed

  • computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。

  • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

  • computed若是值没有被使用时不会调用,但是watchEffect始终会调用一次

10. vue的生命周期

vue2 的生命周期

包含创建 、运行、销毁三个阶段

  • beforeCreate():实例在内存中被创建出来,还没有初始化好data和methods属性
  • create():实例已经在内存中创建,已经初始化好data和method,此时还没有开始编译模版。
  • beforeMount():已经完成了模版的编译,还没有挂载到页面中。
  • mounted():将编译好的模版挂在到页面指定的容器中显示。
  • beforeUpdate():状态更新之前执行函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为还没有开始重新渲染DOM节点。
  • updated():此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新渲染好了!
  • beforeDestroy():实例被销毁之前。
  • destroyed():实例销毁后调用,Vue实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也都会被销毁。组件已经被完全销毁,此时组件中所有的data、methods以及过滤器,指令等,都已经不可用了。

vue3的生命周期

  • setup():开始创建组件之前,在beforeCreated和created之前执行,创建的是data和method
  • onBeforeMount():组件挂载到节点上之前执行的函数;
  • onMounted():组件挂载完成后执行的函数;
  • onBeforeUpdate():组件更新之前执行的函数;
  • onUpdate():组件更新完成之后执行的函数;
  • onBeforeUnmount():组件卸载之前执行的函数;
  • onUnmounted():组件卸载完成后执行的函数;
  • onActivated():被包含在< keep-alive >中的组件,会多出两个生命周期钩子函数,被激活时执行;
  • onDeactivated():比如从A组件,切换到B组件,A组件消失时执行;
  • onErrorCaptured():当捕获一个来自子孙组件的异常时激活钩子函数。

vue2和vue3的生命周期对比

vue2的生命周期ue3的生命周期
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount(() => {})
mountedonMounted(() => {})
beforeMountonBeforeUpdate(() => {})
updatedonUpdated(() => {})
beforeDestroyonBeforeUnmount(() => {})
destroyedonUnmounted(() => {})

11. toRef 和 toRefs 函数

toReftoRefs 是 Vue 3 中的两个响应式函数,它们的作用是将一个普通的 JavaScript 对象或者数组转换成响应式对象或数组。

  • toRef 函数将一个普通的 JavaScript 对象的属性转换成一个响应式对象的属性,返回一个 Ref 对象。
  • toRefs 函数将一个普通的 JavaScript 对象的所有属性转换成响应式对象的属性,返回一个包含所有 Ref 对象的对象。
  • toRefs 函数返回的是一个包含所有 Ref 对象的对象,而不是一个响应式对象。如果需要将这个对象传递给子组件,需要使用 toRef 函数将其转换成响应式对象。

toRef 函数将一个普通的 JavaScript 对象的属性转换成一个响应式对象的属性,返回一个 Ref 对象。Ref 对象是一个包装器,可以通过 .value 属性获取或设置值。当原始对象的属性值发生变化时,Ref 对象的值也会相应地更新。

import { reactive, toRef } from 'vue'

const state = reactive({
  count: 0
})

const countRef = toRef(state, 'count')

console.log(countRef.value) // 0

state.count++

console.log(countRef.value) // 1

toRefs 函数将一个普通的 JavaScript 对象的所有属性转换成响应式对象的属性,返回一个包含所有 Ref 对象的对象。这个函数的主要作用是将一个响应式对象解构成普通的 JavaScript 对象,方便在模板中使用。

import { reactive, toRefs } from 'vue'

const state = reactive({
  count: 0,
  message: 'Hello, world!'
})

const refs = toRefs(state)

console.log(refs.count.value) // 0
console.log(refs.message.value) // 'Hello, world!'

需要注意的是,toRefs 函数返回的是一个包含所有 Ref 对象的对象,而不是一个响应式对象。如果需要将这个对象传递给子组件,需要使用 toRef 函数将其转换成响应式对象。

import { reactive, toRefs, toRef } from 'vue'

const state = reactive({
  count: 0,
  message: 'Hello, world!'
})

const refs = toRefs(state)

export default {
  setup() {
    return {
      count: toRef(refs, 'count'),
      message: toRef(refs, 'message')
    }
  }
}

12. shallowReactive 与 shallowRef

shallowReactiveshallowRef 是 Vue 3 中的两个响应式 API,它们都可以用来创建响应式数据,但是它们的使用场景和行为有所不同。

shallowReactive 用于创建一个浅层响应式对象,它只会对对象的第一层属性进行响应式处理,而不会对嵌套的对象进行处理。这意味着,如果你修改了嵌套对象的属性,那么这个修改不会触发响应式更新。例如:

import { shallowReactive } from 'vue'

const state = shallowReactive({
  name: '张三',
  age: 18,
  address: {
    province: '广东',
    city: '深圳'
  }
})

// 修改嵌套对象的属性,不会触发响应式更新
state.address.province = '北京'

// 修改第一层属性,会触发响应式更新
state.name = '李四'

shallowRef 用于创建一个浅层响应式引用,它可以将任何类型的数据转换为响应式数据。与 shallowReactive 不同的是,shallowRef 不会对对象进行浅层处理,而是直接将整个对象转换为响应式数据。这意味着,如果你修改了嵌套对象的属性,那么这个修改会触发响应式更新。例如:

import { shallowRef } from 'vue'

const state = shallowRef({
  name: '张三',
  age: 18,
  address: {
    province: '广东',
    city: '深圳'
  }
})

// 修改嵌套对象的属性,会触发响应式更新
state.value.address.province = '北京'

// 修改第一层属性,会触发响应式更新
state.value.name = '李四'

总的来说,shallowReactiveshallowRef 都是用于创建响应式数据的 API,但是它们的使用场景和行为有所不同。如果你需要对嵌套对象进行响应式处理,那么应该使用 shallowRef;如果你只需要对第一层属性进行响应式处理,那么可以使用 shallowReactive

13. readonly 与 shallowReadonly

readonlyshallowReadonly 都是 Vue3 中提供的响应式 API,用于创建只读的响应式对象。它们的区别在于:

  1. readonly 可以递归地将一个对象转换为只读的响应式对象,而 shallowReadonly 只会将对象的第一层属性转换为只读的响应式对象,不会递归转换嵌套的对象。

  2. readonly 返回的对象是完全只读的,无法修改对象的属性值,也无法添加或删除属性。而 shallowReadonly 返回的对象只是第一层属性只读,如果对象的属性是一个对象,那么这个对象的属性仍然可以修改。

readonly 的使用方法如下:

import { reactive, readonly } from 'vue'

const state = reactive({
  count: 0,
  obj: {
    name: '张三',
    age: 18
  }
})

const readonlyState = readonly(state)

console.log(readonlyState.count) // 0
console.log(readonlyState.obj.name) // 张三

// 尝试修改只读对象的属性值
readonlyState.count = 1 // 报错,无法修改只读对象的属性值
readonlyState.obj.name = '李四' // 成功,只读对象的属性值可以修改

从上面的例子可以看出,readonly 可以将一个响应式对象转换为只读的响应式对象,可以通过访问只读对象的属性值来获取数据,但是无法修改只读对象的属性值。

shallowReadonly 的使用方法如下:

import { reactive, shallowReadonly } from 'vue'

const state = reactive({
  count: 0,
  obj: {
    name: '张三',
    age: 18
  }
})

const readonlyState = shallowReadonly(state)

console.log(readonlyState.count) // 0
console.log(readonlyState.obj.name) // 张三

// 尝试修改只读对象的属性值
readonlyState.count = 1 // 报错,无法修改只读对象的属性值
readonlyState.obj.name = '李四' // 成功,只读对象的属性值可以修改

// 尝试修改只读对象的嵌套对象的属性值
readonlyState.obj.age = 20 // 成功,只读对象的嵌套对象的属性值可以修改

从上面的例子可以看出,shallowReadonly 可以将一个响应式对象的第一层属性转换为只读的响应式对象,可以通过访问只读对象的属性值来获取数据,但是无法修改只读对象的第一层属性值,而嵌套对象的属性值可以修改。

14. toRaw 与 markRaw

toRawmarkRaw 是 Vue.js 3.x 中的两个 API,用于处理响应式数据。

  • toRaw 方法用于获取一个响应式对象的原始数据,即非响应式的数据
  • markRaw 方法用于标记一个对象为“非响应式的”,即使这个对象被包含在响应式对象中,也不会被转换为响应式数据
  • markRaw 方法只能标记对象本身为非响应式的,而不能标记对象的属性为非响应式的。如果需要标记对象的属性为非响应式的,可以使用 markRaw 方法嵌套对象。

toRaw使用:

在下面的例子中,toRaw 方法将响应式对象 state 转换为了原始数据 rawState,并将其输出到控制台。

import { reactive, toRaw } from 'vue'

const state = reactive({
  count: 0
})

const rawState = toRaw(state)

console.log(rawState) // { count: 0 }

markRaw使用:
在下面的例子中,markRaw 方法将对象 { name: 'John', age: 30 } 标记为非响应式的,并将其作为 state 对象的一个属性。即使 state 对象是响应式的,data 属性也不会被转换为响应式数据。因此,当我们修改 data 属性的值时,不会触发响应式更新。

import { reactive, markRaw } from 'vue'

const state = reactive({
  count: 0,
  data: markRaw({
    name: 'John',
    age: 30
  })
})

console.log(state.data.name) // 'John'

state.data.name = 'Mike'

console.log(state.data.name) // 'Mike'

在下面的例子中,我们使用 markRaw 方法将对象 { name: 'John', age: 30 } 标记为非响应式的,并将其作为 state 对象的一个属性 data.info。这样,即使 state 对象是响应式的,data.info 属性也不会被转换为响应式数据。因此,当我们修改 data.info 属性的值时,不会触发响应式更新。

import { reactive, markRaw } from 'vue'

const state = reactive({
  count: 0,
  data: {
    info: markRaw({
      name: 'John',
      age: 30
    })
  }
})

console.log(state.data.info.name) // 'John'

state.data.info.name = 'Mike'

console.log(state.data.info.name) // 'Mike'

15.customRef 自定义ref使用

在 Vue 3 中,customRef 是一个新的 API,它允许我们创建一个自定义的 ref。

使用 customRef,我们可以自定义 ref 的读取和写入行为,从而实现更加灵活的数据绑定。

下面是一个使用 customRef 的示例:

import { customRef } from 'vue';

function useCustomRef(initialValue) {
  let value = initialValue;

  return customRef((track, trigger) => ({
    get() {
      track();
      return value;
    },
    set(newValue) {
      value = newValue;
      trigger();
    }
  }));
}

export default {
  setup() {
    const customRef = useCustomRef('initial value');

    return {
      customRef
    };
  }
};

在上面的示例中,我们定义了一个 useCustomRef 函数,它接受一个初始值,并返回一个自定义的 ref。

customRef 的工厂函数中,我们定义了 getset 方法,它们分别对应 ref 的读取和写入操作。在 get 方法中,我们使用 track 函数来追踪依赖,以便在数据变化时更新组件。在 set 方法中,我们使用 trigger 函数来触发更新。

最后,在组件中使用 useCustomRef 函数创建一个自定义的 ref,并将其返回。

使用自定义的 ref,我们可以实现更加灵活的数据绑定,例如在 set 方法中添加一些额外的逻辑,或者在 get 方法中返回一个经过计算的值。

16.provide 与 inject

在 Vue 3 中,provideinject 仍然可以用来实现父组件向子组件传递数据,但是与 Vue 2 中的用法略有不同。

provideinject 都是在组件实例上定义的属性,provide 定义在父组件中,inject 定义在子组件中。provide 可以是一个对象或者一个函数,inject 可以是一个数组或者一个对象。

下面是一个使用 provideinject 实现父组件向子组件传递数据的例子:

<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
import { provide, inject } from 'vue'

const MyProvideComponent = {
  setup() {
    const data = 'Hello, World!'
    provide('myData', data)
  }
}

const ChildComponent = {
  setup() {
    const data = inject('myData')
    return { data }
  },
  template: '<div>{{ data }}</div>'
}

export default {
  components: {
    ChildComponent
  },
  extends: MyProvideComponent
}
</script>

在上面的例子中,我们在父组件中定义了一个 provide,将数据 Hello, World!myData 的键名提供给子组件使用。在子组件中,我们使用 inject 获取父组件提供的数据,并将其绑定到模板中。

需要注意的是,provideinject 并不是响应式的,如果需要在子组件中响应式地使用父组件提供的数据,可以使用 reactiveref 等响应式 API 进行包装。

17.响应式数据的判断

  • isRef : 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。