当前位置: 首页> 文旅> 酒店 > Vue3详解

Vue3详解

时间:2025/7/9 0:49:34来源:https://blog.csdn.net/accumulate_/article/details/140109165 浏览次数:0次

vite和webpack区别

vite

vite使用原生ES模块进行开发,无需在编译时将所有代码转换为JS打包,从而提供了更快的热更新和自动刷新功能;
vite在开发模式下没有打包步骤,而是利用浏览器的ES Module Imports特性实现按需编译,这种模式极大的提高了开发环境的相应速度,在生产模式下,vite使用Rollup进行打包,提供更好的tree-shaking和代码压缩。
由于ES Module需要高版本浏览器支持,所以本地编译选用使用高版本浏览器

webpack

webpack拥有丰富的插件系统和高度可配置性,但由于全量的热更新策略导致编译速度慢;
webpack将所有模块打包为一个或多个文件,初次加载速度较慢
webpack插件生态非常成熟,使得在大型复杂项目中具有很高的灵活性

webpack会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。而vite是直接启动开发服务器,请求哪个模块再对该模块进行实时编译。由于现代浏览器本身就支持ES Module,会自动向依赖的Module发出请求。vite充分利用这一点,将开发环境的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并。由于vite再启动时候不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块时候,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译的时间,项目越复杂、模块越多、vite的优势越明显。
在HMR方面,当改动了一个模块后,仅需让浏览器重新请求该模块即可,不像webapck那样需要把该模块的相关依赖模块全部编译一次,效率更高。
当需要打包到生产环境时,vite使用传统的rollup进行打包,因此vite的主要优势在开发阶段,由于vite使用的是ES Module,因此在代码中不可以使用CommonJS

setup原理

setup函数在组件生命周期中是最早执行的函数,它在** beforeCreate和created生命周期钩子之前调用 。这意味着setup在函数内部,无法访问到组件实例this; setup接收两个参数props 和 context(attrs, slots, emit, expose.暴露公共属性) **;setup返回一个对象,对象中的属性和方法将被暴露给组件的模板和其他组合式API函数;

export function renderComponentVNode(vnode: VNode,parentComponent: ComponentInternalInstance | null = null,slotScopeId?: string,
): SSRBuffer | Promise<SSRBuffer> {const instance = createComponentInstance(vnode, parentComponent, null)// 源码中在渲染组件节点时候调用setupComponentconst res = setupComponent(instance, true /* isSSR */)const hasAsyncSetup = isPromise(res)const prefetches = instance.sp /* LifecycleHooks.SERVER_PREFETCH */if (hasAsyncSetup || prefetches) {...return p.then(() => renderComponentSubTree(instance, slotScopeId))} else {return renderComponentSubTree(instance, slotScopeId)}
}......export function setupComponent(instance: ComponentInternalInstance,isSSR = false,
) {isSSR && setInSSRSetupState(isSSR)const { props, children } = instance.vnodeconst isStateful = isStatefulComponent(instance)initProps(instance, props, isStateful, isSSR)initSlots(instance, children)//  创建setup函数const setupResult = isStateful? setupStatefulComponent(instance, isSSR): undefinedisSSR && setInSSRSetupState(false)return setupResult
}......function setupStatefulComponent(instance: ComponentInternalInstance,isSSR: boolean,
) {const Component = instance.type as ComponentOptionsif (__DEV__) {...}// 0. create render proxy property access cacheinstance.accessCache = Object.create(null)// 1. create public instance / render proxyinstance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)if (__DEV__) {exposePropsOnRenderContext(instance)}// 2. call setup()const { setup } = Componentif (setup) {const setupContext = (instance.setupContext =setup.length > 1 ? createSetupContext(instance) : null)const reset = setCurrentInstance(instance)pauseTracking()const setupResult = callWithErrorHandling(setup,instance,ErrorCodes.SETUP_FUNCTION,[__DEV__ ? shallowReadonly(instance.props) : instance.props,setupContext,],)resetTracking()reset()if (isPromise(setupResult)) {...} else {// instance 将上下文暴露给其他模板使用handleSetupResult(instance, setupResult, isSSR)}} else {// instance 将上下文暴露给其他模板使用finishComponentSetup(instance, isSSR)}
}
  1. 初始化组件
  2. 创建组件实例:每个组件都独立存储相关实例信息
  3. 设置组件实例:频道当前组件实例是否是一个有状态组件,初始化(props,slots),执行setup处理函数
  4. 创建渲染上下文:instance.ctx做一层proxy,对渲染上下文属性的访问和修改,代理到对setupState、ctx、data、props中的数据的访问和修改
  5. 创建setup函数上下文:对setup函数参数context的处理,也就是初始化context里的四个属性
  6. 执行setup函数获取结果:获取setup函数执行结果setupResult
  7. 处理setup函数执行结果
  8. 完成组件实例设置

watchEffect watch区别

  • watchEffect
function watchEffect(/*要运行的副作用函数。用来注册清理回调。清理回调会在该副作用的下一次执行前被调用,可以用来清理无效的副作用*/effect: (onCleanup: OnCleanup) => void,/*可以用来调整副作用的刷新时机或调试副作用的依赖。默认情况下,侦听器在组件渲染之前执行,设置flust: 'post'将会使侦听器延迟到组件渲染之后再执行。在某些特殊情况(列如要使缓存失效),可能有必要在响应式依赖放生改变时立即出发侦察器,可以使用flush: 'sync'当多个属性同时更新时,这将会导致一些性能和数据一致性的问题*/options?: WatchEffectOptions
): StopHandletype OnCleanup = (cleanupFn: () => void) => voidinterface WatchEffectOptions {flush?: 'pre' | 'post' | 'sync' // 默认:'pre'onTrack?: (event: DebuggerEvent) => voidonTrigger?: (event: DebuggerEvent) => void
}type StopHandle = () => voidconst count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1// 副作用清除
watchEffect(async (onCleanup) => {const { response, cancel } = doAsyncWork(id.value)// `cancel` 会在 `id` 更改时调用// 以便取消之前// 未完成的请求onCleanup(cancel)data.value = await response
})//停止侦听器:
const stop = watchEffect(() => {})// 当不再需要此侦听器时:
stop()
  • watch
    侦听一个或多个响应式数据源,并在数据源变化时调用所给的会带哦函数
// 侦听单个来源
function watch<T>(source: WatchSource<T>,callback: WatchCallback<T>,options?: WatchOptions
): StopHandle// 侦听多个来源
function watch<T>(sources: WatchSource<T>[],callback: WatchCallback<T[]>,options?: WatchOptions
): StopHandletype WatchCallback<T> = (value: T,oldValue: T,onCleanup: (cleanupFn: () => void) => void
) => voidtype WatchSource<T> =| Ref<T> // ref| (() => T) // getter| T extends object? T: never // 响应式对象interface WatchOptions extends WatchEffectOptions {immediate?: boolean // 默认:falsedeep?: boolean // 默认:falseflush?: 'pre' | 'post' | 'sync' // 默认:'pre'onTrack?: (event: DebuggerEvent) => voidonTrigger?: (event: DebuggerEvent) => voidonce?: boolean // 默认:false (3.4+)
}

与 watchEffect() 相比,watch() 使我们可以:

懒执行副作用;
更加明确是应该由哪个状态触发侦听器重新执行;
可以访问所侦听状态的前一个值和当前值。

// 监听reactive数据
const state = reactive({ count: 0 })
watch(() => state.count,// 当直接监听一个响应式对象时,监听器会自动启用深度监听(count, prevCount) => {/* ... */}
)// 监听ref数据
const count = ref(0)
watch(count, (count, prevCount) => {/* ... */
})// 当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {/* ... */
})

script setup 基本写法

<template><MyComponent ref="myCompRef"/><button @click="count++">{{ count }}</button><button @click="onClick"></button>
</template><script setup>
import { ref,defineProps,defineEmits,provide,inject,defineExpose,onMounted  } from 'vue'
import MyComponent from './MyComponent.vue'// props
const props = defineProps({foo: String
})
//attrs// slots// emit
const emits = defineEmits(['change', 'delete'])
function onClick(){emits('change', '点击')
}// expose
const myCompRef = ref()
onMounted(() => {myCompRef.value._expose()
})
// 子组件对应的暴露方式
function _expose(){}
defineExpose({_expose
})// 提供响应式的值
const count = ref(0)
provide('count', count)// 注入响应式的值
const count = inject('count')// 注入一个值,若为空则使用提供的默认值
const bar = inject('path', '/default-path')// 注入一个值,若为空则使用提供的函数类型的默认值
const fn = inject('function', () => {})</script>

defineAsyncComponent 异步加载组件

定义一个异步组件,它在运行时是懒加载的。参数可以是一个异步加载函数,或是对加载行为进行更具体定制的一个选项对象。

function defineAsyncComponent(source: AsyncComponentLoader | AsyncComponentOptions
): Componenttype AsyncComponentLoader = () => Promise<Component>interface AsyncComponentOptions {loader: AsyncComponentLoaderloadingComponent?: ComponenterrorComponent?: Componentdelay?: numbertimeout?: numbersuspensible?: booleanonError?: (error: Error,retry: () => void,fail: () => void,attempts: number) => any
}<template><div><AsyncComponent /></div>
</template><script>
import { defineAsyncComponent } from 'vue';
import Loading from "./components/Loading.vue"
import Error from "./components/Error.vue"
export default {components: {AsyncComponent: defineAsyncComponent(() =>loader: async () => {await getDataList()return import('./components/AsyncComponent.vue')},loadingComponent: Loading errorComponent: Error)}
}
</script>

vue3的效率提升主要表现在哪些方面

  • 静态提升
  • 预字符串化
  • 缓存事件处理函数
  • PatchFlag

在这里插入图片描述

上图中会对h1,h2标签做静态提升,在render函数外定义_hoisted_1,_hoisted_1 在render函数中可以重复使用;
当存在连续大量的(测试元素数量>10)静态元素时候,vue3会进行预字符串化,通过_createStaticVNode生成字符串;
当有事件处理时vue3会读取render中_cache缓存,而不需要每次都重新创建一个事件;
vue2中不会区分节点是动态的还是静态的,需要层层比较,而vue3中会在_createElementVNode中对节点进行标记,动态节点标记为1,静态节点标记为-1

 _createElementVNode(type, props, children, patchFlag, dynamicChildren, shapeFlag, hooks, data){/**type:组件的构造函数、选项式组件的 VNode 数据对象或者 HTML 标签字符串。props:一个对象,包含 (可能是 reactive 的) 属性,这些属性会被传递到组件。children:子 VNode 或者静态内容的数组。patchFlag:用于优化渲染的标记位。dynamicChildren:动态子 VNode 的数组,用于动态 children。shapeFlag:标记 VNode 的形状信息,比如是否是元素、文本、Fragment 等。hooks:生命周期钩子对象。data:VNode 数据对象,包含了与平台相关的属性,比如 props、attrs、on。*/
}export const enum PatchFlags {TEXT = 1,// 1 动态的文本节点CLASS = 1 << 1,  // 2 动态的 classSTYLE = 1 << 2,  // 4 动态的 stylePROPS = 1 << 3,  // 8 动态属性,不包括类名和样式FULL_PROPS = 1 << 4,  // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较HYDRATE_EVENTS = 1 << 5,  // 32 表示带有事件监听器的节点STABLE_FRAGMENT = 1 << 6,   // 64 一个不会改变子节点顺序的 FragmentKEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 FragmentUNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 FragmentNEED_PATCH = 1 << 9,   // 512  表示只需要non-props修补的元素 (non-props不知道怎么翻才恰当~)DYNAMIC_SLOTS = 1 << 10,  // 1024 动态的soltDEV_ROOT_FRAGMENT = 1 << 11, //2048 表示仅因为用户在模板的根级别放置注释而创建的片段。 这是一个仅用于开发的标志,因为注释在生产中被剥离。//以下两个是特殊标记HOISTED = -1,  // 表示已提升的静态vnode,更新时调过整个子树BAIL = -2 // 指示差异算法应该退出优化模式
}
关键字:Vue3详解

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: