文章目录一、为什么 Vue 组件的 data 必须是函数二、Vue2中的过滤器了解吗过滤器的应用场景有哪些v1. 过滤器的工作原理2. 过滤器的应用场景3. 过滤器的进阶特性级联调用串联接收参数4. 为什么 Vue 3 废弃了过滤器三、Vue.mixin混入深度解析1. 核心应用场景2. 核心原理选项合并策略3. 代码示例① 定义混入 (logMixin.js)② 组件引用4. 为什么 Vue 3 弱化了 Mixin四、Vue 面试题解析深挖 Vue.observable1. 什么是 Vue.observable2. 核心应用场景轻量级状态管理A. 创建共享 Store (store.js)3. Vue.observable vs. Vue 3 Reactive4. 面试避坑指南 (关键点)A. 底层局限性B. 引用一致性 (In-place Mutation)C. 组件内的配合使用5. 状态管理方案对比6. 总结语五、Vue 2 源码解析new Vue() 过程中发生了什么1. 初始化入口_init 方法2. 核心初始化阶段A. 合并配置 (Merge Options)B. 初始化核心子系统C. 触发生命周期beforeCreateD. 状态初始化 (State Init)E. 触发生命周期created3. 挂载阶段$mount总结初始化全流程表一、为什么 Vue 组件的 data 必须是函数数据隔离组件是可复用的。如果data是对象所有实例将共享同一个内存地址的数据。闭包原理通过函数返回对象利用闭包特性每次实例化时都会调用函数生成独立的数据副本。避免副作用防止一个组件实例修改数据后影响到其他完全不相关的实例。constMyComponent{data(){return{count:0}}}// 每次实例化时Vue 都会执行 data()constinstanceAMyComponent.data();constinstanceBMyComponent.data();instanceA.count;console.log(instanceB.count);// 依然是 0互不干扰Vue 底层机制Vue 在初始化组件时如果发现data不是函数会在控制台抛出警告并停止后续的响应式处理。二、Vue2中的过滤器了解吗过滤器的应用场景有哪些在 Vue 2 中过滤器 (Filters) 是一个非常实用的功能主要用于在不改变原始数据的情况下对文本进行 格式化处理。不过需要提前说明的是过滤器在 Vue 3 中已被正式废弃。Vue 官方建议使用计算属性Computed或全局方法来替代它。v1. 过滤器的工作原理过滤器本质上是一个纯函数。它接收一个值作为输入处理后返回一个新的值。它通常用在两个地方双花括号插值 和 v-bind 表达式。局部过滤器filters:{capitalize:function(value){if(!value)returnvaluevalue.toString()returnvalue.charAt(0).toUpperCase()value.slice(1)}}全局过滤器Vue.filter(currency,function(value){return$value.toFixed(2)})2. 过滤器的应用场景过滤器最适合处理那些简单的、纯文本的格式转换且这种转换逻辑在多个地方都会复用。货币格式化这是最经典的场景。将数字 1234.5 转换为 $ 1,234.50。用法{{ price | currency }}时间戳格式化将后端返回的 Unix 时间戳如 1712793600格式化为可读的日期如 2024-04-11。用法{{ timestamp | formatDate(‘YYYY-MM-DD’) }}3. 过滤器的进阶特性级联调用串联过滤器可以像传送带一样一个接一个地处理数据{{ message | filterA | filterB }}这里 message 的结果会传给 filterAfilterA 的处理结果再传给 filterB。接收参数过滤器可以接收额外的参数{{ message | filterA(‘arg1’, arg2) }}注意第一个参数始终是管道符| 前面的表达式的值。4. 为什么 Vue 3 废弃了过滤器Vue 3 追求更简洁的架构和更好的 TypeScript 支持废弃过滤器的主要原因包括功能重叠过滤器能做到的计算属性(Computed) 和 普通方法 (Methods) 都能做到且后者更符合 JavaScript 的编程直觉。强制性上下文缺失过滤器内部拿不到 this实例上下文这限制了它访问组件内部数据的能力。Vue 官方对过滤器的定位是 “纯粹的文本转换工具”。如果过滤器能访问 this开发者就可能在过滤器里调用 this.doSomething() 修改组件状态或者根据组件内复杂的动态状态来返回结果。这会打破过滤器的“纯粹性”让模板的渲染逻辑变得难以预测。TS 类型支持差过滤器的参数类型推导非常麻烦。三、Vue.mixin混入深度解析Vue.mixin是 Vue 2 中一种非常灵活的分发复用组件逻辑的方式。它允许你定义一套通用的选项如data、methods、生命周期钩子等然后将其“混入”到一个或多个组件中。1. 核心应用场景当多个组件之间存在重复的逻辑代码时mixin是 Vue 2 中的首选工具① 通用的工具方法如多个页面都需要实现“点击复制”、“图片预览”或“格式化金额”的功能。② 公共的生命周期逻辑如埋点统计页面打开上报、监听全局滚动事件、或在destroyed钩子中清理定时器。③ 表单校验逻辑抽离重复的表单处理逻辑如validate、resetForm等。④ 权限控制在created钩子中统一判断用户权限实现跳转或提示拦截。2. 核心原理选项合并策略Vue.mixin的本质是“合并Merge”。当调用混入时Vue 会将混入对象与组件自身的选项按以下规则合并具体的合并规则选项类型合并策略冲突处理数据对象 (data)内部进行递归合并。键名冲突时以组件自身数据为准组件优先。生命周期钩子全部保留并合并为一个数组。混入对象钩子先执行组件自身钩子后执行。对象选项 (methods等)合并为同一个对象。键名冲突时以组件自身为准。全局混入Vue.mixin({ ... })影响之后创建的所有实例包括第三方组件需慎用。3. 代码示例① 定义混入 (logMixin.js)exportconstlogMixin{data(){return{name:mixin}},created(){console.log(Mixin: 组件已创建);},methods:{hello(){console.log(来自 Mixin 的问候);}}}② 组件引用import{logMixin}from./logMixinexportdefault{mixins:[logMixin],// 局部混入created(){console.log(Component: 自身钩子);}}/** * 控制台输出顺序 * 1. Mixin: 组件已创建 * 2. Component: 自身钩子 */4. 为什么 Vue 3 弱化了 Mixin虽然 Mixin 解决了代码复用问题但其设计缺陷在大型项目中非常明显因此 Vue 3 推荐使用 Composition API (组合式 API) 彻底取代它。Mixin 的三大致命缺点命名冲突多个混入或组件自身存在同名变量/方法时会发生覆盖导致难以察觉的 Bug。来源不明在模板中调用 this.doSomething() 时无法直观判断该方法来自哪个 Mixin增加了维护成本。隐式依赖多个 Mixin 之间可能存在逻辑依赖导致代码耦合严重难以独立重构。四、Vue 面试题解析深挖 Vue.observable在 Vue 2.6 版本中官方暴露了Vue.observable这个 API。它是响应式系统的核心能力外溢允许开发者在不创建组件实例的情况下构建响应式状态。1. 什么是 Vue.observableVue.observable(object)用于将一个普通的 JavaScript 对象转换成响应式对象。底层原理它直接调用了 Vue 内部的observe方法。在 Vue 2 中这意味着它使用Object.defineProperty递归地将对象的属性转化为getter/setter从而实现依赖收集与触发更新。返回值返回的对象已经是“响应式”的了。当这个对象的属性在组件模板或计算属性中被使用时Vue 会自动追踪它。2. 核心应用场景轻量级状态管理在没有Vue.observable之前跨组件共享状态通常依赖Vuex太重或Event Bus逻辑乱。Vue.observable提供了一种极其简洁的跨组件通信方式。A. 创建共享 Store (store.js)importVuefromvue;// 1. 创建响应式状态exportconststoreVue.observable({count:0,user:{name:Gemini}});// 2. 定义修改状态的方法类似 Mutationsexportconstmutations{addCount(){store.count;},setUserName(name){store.user.namename;}};templatedivp当前计数{{sharedCount}}/pbutton clickincrement增加/button/div/templatescriptimport{store,mutations}from./store.js;exportdefault{computed:{// 像访问本地 data 一样访问共享状态sharedCount(){returnstore.count;}},methods:{increment(){mutations.addCount();}}};/script3. Vue.observable vs. Vue 3 ReactiveVue.observable不仅仅是一个 API更是理解 Vue 3 响应式设计的关键传承关系它实际上是 Vue 3 中reactiveAPI 的前身。Vue 3 将这种“对象响应式化”的能力进一步标准化成为了组合式 APIComposition API的核心。设计思想的转变Vue 2 早期响应式系统高度耦合在new Vue()实例中。Vue.observable证明了“状态”可以独立于“UI 渲染”存在。这种解耦的思想直接启发了 Vue 3 的功能组织方式。4. 面试避坑指南 (关键点)在面试中若能提到以下细节可以显著体现你对 Vue 源码的理解深度A. 底层局限性尽管它使对象响应式化但由于 Vue 2 依然基于Object.defineProperty无法监测直接给对象新增属性、删除属性或者通过索引修改数组元素如arr[0] 1仍然无法触发视图更新。解决方法在这些场景下依然需要配合Vue.set()或splice()等变异方法使用。B. 引用一致性 (In-place Mutation)这是一个非常微妙的点Vue.observable会直接修改传入的原对象并返回该对象。验证代码constobj{count:0};constobsVue.observable(obj);console.log(objobs);// true这意味着原对象已经被“注入”了 getter 和 setter。C. 组件内的配合使用在组件中使用外部的observable对象时必须通过computed返回该对象的属性。原因只有在计算属性或渲染函数中访问属性Vue 的Watcher才能正确收集依赖。直接在methods中读取而不经过计算属性可能会导致视图不更新。5. 状态管理方案对比维度Vue.observableVuex复杂度极轻量无额外配置。较重需定义 State, Mutation, Action。调试支持弱。不支持 Vue Devtools 状态快照。强。支持时间旅行Time Travel和状态追踪。严谨性弱。任何地方都能修改状态难以维护。强。严格要求通过 Mutation 修改流程可预测。适用场景逻辑简单的全局配置、跨组件的小型 Store。大型单页应用SPA、业务逻辑复杂的金融/电商项目。6. 总结语Vue.observable是处理中小型项目或组件库内部状态共享的神器。它规避了 Vuex 繁琐的样板代码同时比 Event Bus 更加直观和易于数据追踪。五、Vue 2 源码解析new Vue() 过程中发生了什么执行new Vue()是整个框架生命的起点。这个过程本质上是将你传入的配置对象options转化为一个具备响应式能力、可挂载、可观察的实体的初始化过程。1. 初始化入口_init方法当你调用new Vue(options)时实际上执行的是 Vue 构造函数内部定义的_init方法。// Vue 源码简略实现functionVue(options){if(process.env.NODE_ENV!production!(thisinstanceofVue)){warn(Vue is a constructor and should be called with the new keyword)}this._init(options)}2. 核心初始化阶段在_init内部Vue 按照严格的顺序执行了一系列初始化操作A. 合并配置 (Merge Options)Vue 会将用户传入的options与全局的Vue.options如全局组件、指令、mixin进行合并生成最终的vm.$options。B. 初始化核心子系统initLifecycle: 初始化组件父子关系。设置$parent、$root、$children以及一些状态标识如_isMounted。initEvents: 初始化事件系统。处理父组件在当前组件上注册的事件v-on。C. 触发生命周期beforeCreate注意此时数据监测Data Observation和状态State还未开始初始化。因此在该钩子中无法访问data、methods等。D. 状态初始化 (State Init)这是 Vue 2 最核心的部分按顺序初始化以下内容initInjections: 初始化inject。initState:initProps: 遍历 props通过defineReactive设为响应式。initMethods: 将方法挂载到实例上并绑定this。initData:核心步骤。调用observe(data)递归地使用Object.defineProperty将数据属性转化为 getter/setter。initComputed: 创建计算属性的专用Watcher。initWatch: 遍历 watch 配置创建用户定义的Watcher。initProvide: 初始化provide。E. 触发生命周期created此时数据响应式已经完成。你可以访问data、修改属性但 DOM 还没有生成$el属性目前不可见。3. 挂载阶段$mount如果配置中提供了el选项Vue 会自动调用$mount进入挂载流程编译模板 (Compile)将template字符串编译成render函数。如果是运行时版本Runtime-only此步骤已在构建时由vue-loader完成。触发生命周期beforeMount模板已编译完成虚拟 DOM 已创建但尚未替换页面上的真实 DOM。创建渲染 Watcher (Mount)Vue 会实例化一个渲染 Watcher它会持续监听数据变化并执行updateComponent。_render: 执行渲染函数生成虚拟 DOMVNode。_update: 执行Patch 算法Diff比对新旧 VNode并将差异应用到真实 DOM 上。触发生命周期mounted真实 DOM 已经插入页面可以通过this.$el访问真实的节点。总结初始化全流程表阶段关键操作对应的生命周期1. 初始化准备合并 Options、初始化生命周期/事件-2. 注入前此时实例已创建但数据未挂载beforeCreate3. 状态注入核心初始化 Props/Data/Computed (响应式处理)-4. 状态就绪数据已可访问异步请求通常在此发起created5. 编译挂载模板编译、寻找挂载点-6. 渲染前VNode 已经生成beforeMount7. DOM 生成执行 Render Patch生成真实 DOM 树-8. 挂载完成DOM 节点已进入页面可进行 DOM 操作mounted