积累:04-VUE2

1. Vue 生命周期详解

beforeCreate → created → beforeMount → mounted → beforeUpdate → updated → beforeDestroy → destroyed

  • beforeCreate 初始化vue实例
  • created 组件实例已经完全创建 ,此时vm.$el还没有被创建
  • beforeMount 组件挂载之前
  • mounted 组件挂载到实例上去之后
  • beforeUpdate 组件数据发⽣变化,更新之前 。在此阶段可获取到 vm.el 此阶段 vm.el 虽已完成DOM初始化,但并未挂载在 el 选项上
  • updated 组件数据更新之后
  • beforeDestroy 组件实例销毁之前
  • destroyed 组件实例销毁之后
  • activated keep-alive 缓存的组件激活时
  • deactivated keep-alive 缓存的组件停⽤时调⽤
  • errorCaptured 捕获一个来自子孙组件的错误时调用

总结口诀:

"挂前创后,更新前后,销毁前后"------八个阶段掌握生命周期。

数据请求在created和mouted的区别
  • created 是在组件实例⼀旦创建完成的时候⽴刻调⽤,这时候⻚⾯ dom 节点并未⽣ 成; mounted 是在⻚⾯ dom 节点渲染完毕之后就⽴刻执⾏的。触发时机上 created 是⽐ mounte d 要更早的,两者的相同点:都能拿到实例对象的属性和⽅法。
  • 讨论这个问题本质就是触发的时机,放在 mounted 中的请求有可能导致⻚⾯闪动(因为此时⻚ ⾯ dom 结构已经⽣成),但如果在⻚⾯加载前完成请求,则不会出现此情况。建议对⻚⾯内容的改动 放在 created ⽣命周期当中

2. 双向数据绑定

  • 三个重要组合部分

    • 数据层(Model):应⽤的数据及业务逻辑
    • 视图层(View):应⽤的展示效果,各类UI组件
    • 业务逻辑层(ViewModel):框架封装的核⼼,它负责将数据与视图关联起来
  • 职责:

    • 数据变化后更新视图
    • 视图变化后更新数据
  • 两个重要组成部分

    • 监听器(Observer):对所有属性进行监听
    • 解析器(Compiler):对每个元素节点的指令进⾏扫描跟解析,根据指令模板替换数据,以及绑定相应 的更新函数
  • 实现:

    js 复制代码
    new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })
    1. 初始化阶段
    • 执行 new Vue() 实例化,初始化 data,调用 observe() 对数据属性进行劫持。
    • 遍历 data 对象,对每个属性使用 Object.defineProperty() 拦截 getter/setter,实现数据响应。
  1. 模板编译阶段

    • Vue 内部调用 compile() 对挂载元素 el 的 DOM 进行遍历,识别出指令(如 {{message}}v-model)。
    • 创建 Watcher 实例并与 Observer 关联,建立依赖收集关系。
  2. 依赖收集 & 数据绑定

    • 模板中每个绑定的地方,都会生成一个 Watcher,它会被添加到对应数据属性的 Dep(依赖收集器)中。
    • 当数据变化时,Dep 会通知所有相关 Watcher 更新视图。
  3. 双向绑定实现

    • v-model 指令绑定输入框,监听 input 事件,把用户输入同步到 Model。
    • Model 变化后,自动更新视图内容。

2. 常用指令

常用如:

  • v-if/v-else-if/v-else:条件渲染
  • v-show:显示隐藏
  • v-for:循环列表
  • v-model:双向绑定
  • v-bind:属性绑定
  • v-on:事件监听

3. data 为什么是函数?

组件会被复用,每个实例的数据需要独立,函数返回新对象才不会共享内存。


4. v-if 和 v-show 区别

  • v-if 是真删除/添加 DOM,性能开销大,适合偶尔显示
  • v-showdisplay:none 控制隐藏,适合频繁切换

5. v-for 为什么要加 key?

让 Vue 更快更精准对比新旧节点,避免复用错误,提升性能。 Vue 使用 虚拟 DOM(VNode) + diff 算法 来高效更新视图:

  • 当数据变了,Vue 会:

    1. 重新生成一棵新的虚拟 DOM 树;
    2. 拿新旧两棵树进行"对比"(这一步叫 diff);
    3. 找出差异,最小化操作真实 DOM,提高性能!

6. v-if 和 v-for 同时用谁先执行?

v-for 优先,会先循环再判断 v-if

  • 在vue编译阶段,模板会被编译成"渲染函数"。
    • v-for负责生产多个vnode(虚拟DOM)。
    • v-if 负责判断是否渲染这个节点
    • 也就是说要先把节点遍历出来再一个个判断v-if。想优化性能,先过滤数据,不推荐在v-for里加v-if

7. computed 和 watch 区别

特性 computed watch
缓存 ✅有缓存 ❌无缓存
用途 计算派生数据 监听执行逻辑(如异步)
  • computed:根据已有数据推算出新的值
  • watch:监听现有数据做任务

8. nextTick 原理

Vue 异步更新 DOM,nextTick 会在 DOM 更新完之后执行回调,本质是微任务(如 Promise.then())。

  • vue更新DOM是异步的,当修改数据后,DOM不是马上就变,如果立即访问DOM,拿到的还是旧内容,nextTick(fn)就是告诉vue,等DOM更新完了再执行这个函数

扩展补充:js时间循环机制:同步任务->微任务->宏任务

类型 常见 API 举例
宏任务 setTimeoutsetIntervalsetImmediate(Node.js)、UI 渲染、I/O 操作
微任务 Promise.then/catch/finallyqueueMicrotaskMutationObserver

9. 模板编译原理

template → AST 抽象语法树 → render 函数 → 虚拟 DOM → 真正 DOM。


10. 数据响应式原理

Vue2 用 Object.defineProperty() 实现响应式。Vue3 用 Proxy 更强大。 和vue3的区别

特性 Vue 2 Vue 3
模板编译 runtime 编译 更强的 compile-time 优化
AST 结构 基本节点 静态提升、patchFlag
响应式 Object.defineProperty Proxy
Virtual DOM 重度依赖 更轻量,block tree

11. 数组响应式方法

Vue 重写了数组原型方法,如 pushsplice,确保这些方法可以触发视图更新。 以下操作不能自动触发视图更新

  1. 通过索引修改数组
less 复制代码
js
复制编辑
list.value[1] = 999   // ❌ 不会触发视图更新(Vue2 里不行,Vue3 可以)
  • Vue 2 的限制:数组不能通过索引添加/修改时触发响应式更新

  • Vue 3 用 Proxy 修复了这个问题

  • 问题根本:

    • vue2使用的是OBject。defineProperty()来实现响应式
    js 复制代码
    Object.defineProperty(obj,'key',{
        get(){},
        set(){}
    })
    // 它只能劫持对象已有的属性,对于 新增属性 或者 数组索引赋值 无能为力

    但是它只能劫持对象已有的属性 ,对 新增属性数组索引赋值 无能为力,比如:

    ini 复制代码
    js
    复制编辑
    const arr = [1, 2, 3]
    arr[1] = 100    // 改变了数组第二项,但 Vue 2 无法拦截这个动作
    arr[3] = 999    // 新增索引,Vue 2 不知道
    arr.length = 1  // 改变长度,Vue 2 也不知道

Vue 2 是通过重写数组原型方法 来实现响应式(比如重写 push),但这只能拦截函数,不能拦截 arr[i] = xxx 这种直接操作

  • Vue3 使用了ES6的 Proxy "

    js 复制代码
    const p = new Proxy(target,{
        get(target,key,receiver){...}
        set(target, key, value, receiver) { ... },
        deleteProperty(target, key) { ... }
    })
    
    // 1. 胁持了所有属性的读取/修改
    // 2. 能劫持数组索引修改
    // 3. 能劫持length改变
    // 4. 能劫持**in操作符**和**delete**操作
  • Proxy 是怎么解决Vue2无法响应的问题的?

js 复制代码
const arr = reactive([1,2,3])
arr[1] = 100;
arr[3] = 999;
arr.length =1;

//  vue3内部会对数组进行以下处理
const prosy = new Proxy(arr,{
    get(target,key){
        // 依赖收集,(触发track)
        return Reflect.get(target,key)
    }),
     set(target, key, value) {
        // 派发更新(触发 trigger)
        const result = Reflect.set(target, key, value)
        // 如果 key 是数组索引或 length,触发视图更新
        return result
      }
})

12. Vue2 实例挂载流程

new Vue() 调用 _init → 合并选项 → 初始化生命周期、事件、数据 → $mount() → 编译模板 → 渲染 DOM。

  • 一句话:初始化数据(响应式)-> 编译模板(template)-> 渲染成虚拟DOM(vNode) -> 生成真是DOM挂载

🧩 完整流程图解(带源码点位)

以如下代码为例:

css 复制代码
js
复制编辑
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  },
  template: '<div>{{ message }}</div>'
})

🔍 详细执行步骤

① 构造函数初始化 new Vue(options)

源码入口:

scss 复制代码
js
复制编辑
// src/core/instance/init.js
Vue.prototype._init = function (options) {
  ...
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')   // 生命周期钩子1
  initInjections(vm)
  initState(vm)                  // 核心:数据响应式处理
  callHook(vm, 'created')        // 生命周期钩子2
  ...
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)    // 执行挂载流程
  }
}

② 数据响应式系统初始化(initState

处理了:

  • props
  • methods
  • data(使用 defineReactive
  • computed
  • watch

使这些属性具备响应式能力。


③ 编译模板(vm.$mount

ini 复制代码
js
复制编辑
// src/platforms/web/runtime/index.js
const mount = Vue.prototype.$mount = function (el, hydrating) {
  el = query(el) // 获取 DOM 元素

  const options = this.$options
  if (!options.render) {
    let template = options.template
    if (!template && el) {
      template = el.outerHTML
    }
    const { render } = compileToFunctions(template)
    options.render = render
  }

  return mountComponent(this, el)
}

如果你没有手写 render 函数,Vue 会将 template 编译为 render 函数。


④ 创建虚拟 DOM 并挂载(mountComponent

scss 复制代码
js
复制编辑
// src/core/instance/lifecycle.js
function mountComponent(vm, el) {
  vm.$el = el

  callHook(vm, 'beforeMount')  // 生命周期钩子3

  // 核心渲染逻辑
  const updateComponent = () => {
    vm._update(vm._render())
  }

  // 用 watcher 包裹渲染函数,数据变了会重新调用
  new Watcher(vm, updateComponent, noop, {
    before() {
      if (vm._isMounted) callHook(vm, 'beforeUpdate')
    }
  }, true)

  vm._isMounted = true
  callHook(vm, 'mounted')  // 生命周期钩子4
}

⏱ 生命周期钩子触发时机:

生命周期钩子 执行时机
beforeCreate 实例初始化前,数据还没初始化
created 数据初始化完成,还没挂载
beforeMount 模板已编译,挂载前
mounted 挂载完成,DOM 可访问
beforeUpdate 响应式数据更新前
updated DOM 更新后
beforeDestroy 实例销毁前
destroyed 实例销毁后

13. 插槽原理

插槽通过 _t() 渲染函数处理,默认插槽 fallback 生效;作用域插槽用 $scopedSlots 处理。

  • 插槽是 Vue 2 编译阶段把父组件中子组件标签的内容 ,作为VNode 传入子组件 ,在子组件渲染时用 <slot> 标签占位,将这些 VNode 插入到对应位置

14. 组件和插件的区别

对比项 组件 插件
编写方式 .vue 或 template 模板 提供 install 方法
注册方式 components / Vue.component Vue.use()
作用 UI 视图 增强全局功能,如指令、混入、API

15. mixin 和 extends 区别

  • mixins: 多来源合并,像拌饭
  • extends: 单来源继承,像复制模板
在vue2中,两者都是用来复用组件逻辑的机制,但是他们在使用、原理、优先级方面有所不同

✅ 一、使用方法

1. mixins

javascript 复制代码
js
复制编辑
// 定义一个 mixin
const myMixin = {
  data() {
    return {
      message: 'Hello from mixin'
    }
  },
  created() {
    console.log('mixin created')
  }
}

// 使用
export default {
  mixins: [myMixin],
  created() {
    console.log('component created')
  }
}

2. extends

javascript 复制代码
js
复制编辑
const baseComponent = {
  data() {
    return {
      baseMsg: 'from base'
    }
  },
  created() {
    console.log('extends created')
  }
}

export default {
  extends: baseComponent,
  created() {
    console.log('component created')
  }
}

二、原理解析(合并策略)


16. Vue 如何实现双向绑定?

核心:数据响应式 + 模板绑定

  • 绑定 input 值
  • Object.defineProperty 拦截赋值
  • DOM 自动更新

17. 动态添加响应式属性方法

  • Vue.set(obj, key, val)
  • 或用 this.someObject = {...this.someObject, newProp: val}

18. 虚拟 DOM 是什么?

VNode 是用 JS 对真实 DOM 的描述,更新时用 diff 算法找出最小变更,避免频繁重排重绘。


19. 为什么使用虚拟 DOM?

因为操作真实 DOM 很慢,而 VNode 是 JS 对象,比较快。最后只操作需要变化的部分 DOM。


20. Vue diff 算法流程

核心逻辑:同层比较、递归处理、key辅助。使用 key 能显著减少 DOM 操作。


21. Vue 常用修饰符

  • .stop 阻止冒泡
  • .prevent 阻止默认
  • .capture, .once, .self, .native(只用于组件)

22. 事件绑定原理

  • Vue 将事件绑定成函数缓存到 vm._events
  • 最终转成 addEventListener

23. Vue 自定义指令

通过 bind, inserted, update 等钩子,可以实现拖拽、自动聚焦等效果。


24. keep-alive 的作用

缓存组件实例,防止重复渲染,用在 tab 页、分页等场景;配合 include/exclude 更精准控制。


25. 父子组件通信方式

  • 父 → 子:props
  • 子 → 父:$emit
  • 兄弟通信:EventBus 或 Vuex
  • 深层通信:provide/inject

26. Vue-router 两种模式区别

  • hash:带 #,兼容好
  • history:无 #,需后端配合

27. 导航守卫用法

  • 全局守卫:beforeEach
  • 路由独享守卫:beforeEnter
  • 组件内守卫:beforeRouteEnter

28. SSR 是什么,有做过吗?

服务端提前把 HTML 渲染好 → 浏览器直接看完整页面,提升首屏速度 & SEO。但复杂度更高。


29. Vue3 新特性(与 Vue2 对比)

特性 Vue3 提升点
性能 更快,更小
Composition API 更强的逻辑复用
Fragment 多根节点支持
Teleport 跨层级渲染,比如弹窗
Proxy 响应式 更完善,支持动态属性添加

vue2的vuex, 详细说明

  • 为什么要使用?
    • 多个组件数据共享状态
    • 组件嵌套复杂,props + emit 传递混乱
    • 想统一管理数据变更、支持调试工具、状态持久化等
  • 五个核心概念
    • state : 统一存储的数据源
    • getter : 派生状态,类似计算属性
    • mutations : 同步修改state的唯一方法
    • actions : 异步操作,提交mutations
    • mudules : 拆分子模块,组织大型状态树
1. 示例代码
js 复制代码
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new  Vuex.store({
    state:{
        count:0 
    },
    getter :{
        doubleCount(state){
            retrun state.count * 2
        }
    },
    mutations :{ // 同步改变state的唯一方法
        increment(state,payload){
            state.count += payload
        }
    },
    actions:{
         asyncIncrement({ commit }) {
              setTimeout(() => {
                commit('increment', 1)
              }, 1000)
            }
    }
})
2. 在项目中挂载
javascript 复制代码
js
复制编辑
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

🧬 在组件中使用

1. 获取 state

xml 复制代码
vue
复制编辑
<template>
  <div>{{ count }}</div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['count'])
  }
}
</script>

2. 调用 mutation(同步修改)

kotlin 复制代码
js
复制编辑
this.$store.commit('increment', 1)

3. 调用 action(可异步)

kotlin 复制代码
js
复制编辑
this.$store.dispatch('asyncIncrement')

4. 使用 getters

css 复制代码
js
复制编辑
computed: {
  ...mapGetters(['doubleCount'])
}

🧱 模块化结构(modules)

javascript 复制代码
js
复制编辑
// store/modules/user.js
export default {
  namespaced: true,
  state: {
    name: 'Jack'
  },
  mutations: {
    setName(state, name) {
      state.name = name
    }
  }
}
javascript 复制代码
js
复制编辑
// store/index.js
import Vuex from 'vuex'
import user from './modules/user'

export default new Vuex.Store({
  modules: {
    user
  }
})

访问:

kotlin 复制代码
js
复制编辑
this.$store.commit('user/setName', 'Tom')

🔍 Vuex 原理(简化说明)

  1. state 被 Vue 实例包裹为响应式(用 Vue.observable()new Vue({ data })
  2. 组件使用 mapState 其实就是访问这个响应式对象的属性
  3. 所有 commit 都是通过 mutations 中注册的函数来操作 state
  4. dispatch 是一个封装,会调用 actions,再通过 commit 修改数据
  5. 所有状态变更都可以被 Vue DevTools 捕捉和记录

状态流动图(简化版)

bash 复制代码
组件 ------ dispatch ------→ action ------ commit ------→ mutation ------→ state

组件 ←------ computed(mapState/mapGetters) ←------ state/getters
相关推荐
YGY Webgis糕手之路27 分钟前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
90后的晨仔1 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
Ares-Wang1 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
90后的晨仔1 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
德育处主任1 小时前
p5.js 正方形square的基础用法
前端·数据可视化·canvas
烛阴1 小时前
Mix - Bilinear Interpolation
前端·webgl
90后的晨仔1 小时前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js
德育处主任1 小时前
p5.js 矩形rect绘制教程
前端·数据可视化·canvas
前端工作日常2 小时前
我学习到的babel插件移除Flow 类型注解效果
前端·babel·前端工程化
前端工作日常2 小时前
我学习到的 Babel 配置
前端·babel·前端工程化