vue3学习笔记(对比vue2学习)

张开发
2026/5/20 11:45:32 15 分钟阅读
vue3学习笔记(对比vue2学习)
setupsetup 是 Vue3.x 新增的一个选项 他是组件内使用 Composition API的入口。setup是一个函数组件中用到的数据、方法都要配置在setUp中它会在beforeCreate之前执行一次若返回一个对象那么对象中的属性、方法在模版中都可以直接使用返回一个渲染函数、可以自定义渲染内容import { ref, reactive } from vue export default { name: Home, setup(props, context) { const title ref(标题) const data reactive({ value: 哈哈哈 }) return { title, data } } }props: 值为对象包含组件外部传递过来且组件内部声明接收了的属性context: 上下文对象attrs: 值为对象包含组件外部传递过来且没有在props配置中声明的属性等同于this.$attrsslots: 收到的插槽内容相当于this.$slotsemit: 分发自定义事件的函数相当于this.$emitv2配置datamessagecomputed。。中可以访问到setup中的属性和方法但反之不行如果不同名setup优先reactive、ref、和toRefs在 vue2.x 中 定义数据都是在data中 但是 Vue3.x 可以使用reactive和ref来进行数据定义。 那么ref和reactive他们有什么区别呢使用ref可以创建一个包含响应式数据的引用对象(简称ref对象)可以是基本类型、也可以是对象const xxx ref(value) //使用 xxx.value // 在模版中 div{{xxx}}/div使用reactive定义一个对象类型的响应式数据但是不能代理基本数据类型const xxx reactive({ yy: }) xxx.yytoRef创建refconst state reactive({ foo: 1, bar: 2 }) const fooRef toRef(state, foo) // 传递props export default { setup(props) { useSomeFeature(toRef(props, foo)) } }toRefs响应式转换将响应式对象转换成普通对象shallowReactive响应式外层转换 适用于一个对象结构比较深但变化时只是外层属性变化const state shallowReactive({ foo: 1, bar: { foo1: 2 } })shallowRef基本数据响应式不进行对象的响应式处理。适用于一个对象数据后续功能不会修改该对象中的属性而是生成新的对象readonly让一个数据变成只读的应用不希望数据被修改时const readOnlyData readonly({ xxx: hello // 只读 aa: { bb: 2 // 只读 } })shallowReadOnly响应式变只读(浅只读)const shallowData shallowReadOnly({ foo: 1, //只读 nested: { bar: 2 // 非只读 } })toRaw将响应式转换成非响应式customRef依赖更新控制import { customRef } from vue export default { name: Home, setup() { const fn function() { return customRef((track, trigger)) { return { get() { track() return value }, set(newValue) { trigger() } } } } } }单Refimport { ref } from vue setup() { const node ref(null) onMounted(() { console.log(node.value) }) }循环中的ref需要自行绑定setup() { const itemsRef [] const setItemRef el { if (el) { itemsRef.push(el) } } }computedimport { computed } from vue; setup() { let fullName computed(() { return person.firsName - person.lastName }) let fullName computed(() { get() { return person.firsName - person.lastName }, set(value) { const nameArr value.split(-); person.firstName nameArr[0] person.lastName nameArr[1] } }) return fullName }watchimport { watch } from vue watch(sum, (newValue, oldValue) { }, { immediate: true, deep: false })watchEffect不需要那个指明监视哪个属性监视的回调中用到哪个属性就监视哪个不需要写返回值watchEffet(() { const x1 sum.value const x2 person.age console.log(watchEffect) })生命周期自定义 Hooks我们可以将一些公共逻辑封装成一个 hook, 我们约定这些「自定义 Hook」以 use 作为前缀和普通的函数加以区分。import { ref、 Ref、computed } from vue export dafault function useCount(initValue 1) { const count ref(initValue); const multiple computed(() count.value * 2) const increase (delta) { count.value 1; } const decrease (delta) { count.value - 1; } return { count, increase, decrease, multiple } } template p倍数: {{ multiple }}/p div button clickincrease()加一/button button clickdecrease()减一/button /div /template script import useCount from ./useCount; setup() { const { count, increase, decrease, multiple } useCount(10) return { count, increase, decrease, multiple } } /script简单对比 vue2.x 与 vue3.x 响应式Object.defineProperty只能劫持对象的属性 而 Proxy 是直接代理对象由于Object.defineProperty只能劫持对象属性需要遍历对象的每一个属性如果属性值也是对象就需要递归进行深度遍历。但是 Proxy 直接代理对象 不需要遍历操作Object.defineProperty对新增属性需要手动进行Observe因为Object.defineProperty劫持的是对象的属性所以新增属性时需要重新遍历对象 对其新增属性再次使用Object.defineProperty进行劫持。也就是 Vue2.x 中给数组和对象新增属性时需要使用$set才能保证新增的属性也是响应式的, $set内部也是通过调用Object.defineProperty去处理的。TeleportTeleport 就像是哆啦 A 梦中的「任意门」任意门的作用就是可以将人瞬间传送到另一个地方。有了这个认识我们再来看一下为什么需要用到 Teleport 的特性呢看一个小例子 在子组件Header中使用到Dialog组件我们实际开发中经常会在类似的情形下使用到 Dialog 此时Dialog就被渲染到一层层子组件内部处理嵌套组件的定位、z-index和样式都变得困难。 Dialog从用户感知的层面应该是一个独立的组件从 dom 结构应该完全剥离 Vue 顶层组件挂载的 DOM同时还可以使用到 Vue 组件内的状态data或者props的值。简单来说就是,即希望继续在组件内部使用Dialog, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中。 此时就需要 Teleport 上场我们可以用包裹Dialog, 此时就建立了一个传送门可以将Dialog渲染的内容传送到任何指定的地方。body div idapp/div div iddialog/div /body template telpport to#dialog/telpport ... /templateSuspenseSuspense 只是一个带插槽的组件只是它的插槽指定了default 和 fallback 两种状态。vue2写法div v-if!loading{{data}}/div div v-else{{loading... }}/divvue3写法Suspense template #default async-component/async-component /template template #fallback divloading.../div /template /Suspense片段Fragmentvue2中一个组件只能定义一个根节点template div/div /template在vue3中template div/div div/div /template更好的 Tree-Shaking受影响的 APIVue.nextTickVue.observable用 Vue.reactive 替换Vue.versionVue.compile仅限完整版本时可用Vue.set仅在 2.x 兼容版本中可用Vue.delete与上同内置工具变更slot 具名插槽语法vue2 slot nametitle/slot template slottitle h1插槽/h1 /template vue3 template #contentscoped div v-foritem in scoped.data/div /template自定义指令vue2 Vue.directive(focus, { inserted: function(el) { el.focus() } }) vue3 const { createApp } from vue const app createApp({}) app.directive(focus, { mounted(el) { el.focus() } })v-model 升级异步组件Vue3中使用defineAsyncComponent 定义异步组件template AsyncPage/ /template sciprt import { defineAsyncComponent } from vue; components: { AsyncPage: defineAsyncComponent(() import (./Page.vue)) AsyncPageOptions: defineAsyncComponent({ loader: () import (./Page.vue), dealu: 200, timeout: 3000, errorComponent: () import(./Error.vue), loadinComponent: () import(./Loading.vue) }) } /scriptVue3设计思想以及响应式源码Vue2缺点对TS支持不友好(所有属性都在this上难以推倒数据类型)全局API都在Vue对象的原型上无法实现TreeShaking对跨平台渲染支持不友好Vue3设计Vue3模块耦合度低可以独立使用Vue3框架更小更易扩展基于monorepo管理项目实现模块拆分vue3源码paclagesreactivity: 响应式系统runtime-core: 与平台web用的最多的是无关的运行时核心runtime-dom: 针对浏览器的运行时包括DOM API属性。事件处理runtime-test: 测试文件server-renderer 服务端渲染compiler-core: 与平台无关的编译器核心compiler-dom: 针对浏览器的编译模块compiler-ssr: 服务端渲染scriptsVue3在开发环境自己使用的开发环境使用esbuild打包生成环境使用rollup打包Vue3中Reactivity模块export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap, ) } function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandlerany, collectionHandlers: ProxyHandlerany, proxyMap: WeakMapTarget, any, ) { if (!isObject(target)) { if ( __DEV__ ) { warn( value cannot be made ${isReadonly ? readonly : reactive}: ${String( target, )} , ) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object if ( target[ReactiveFlags.RAW] !(isReadonly target[ReactiveFlags.IS_REACTIVE]) ) { return target } // only specific value types can be observed. const targetType getTargetType(target) if (targetType TargetType.INVALID) { return target } // target already has corresponding Proxy const existingProxy proxyMap.get(target) if (existingProxy) { return existingProxy } const proxy new Proxy( target, targetType TargetType.COLLECTION ? collectionHandlers : baseHandlers, ) proxyMap.set(target, proxy) return proxy }

更多文章