VUE带你乘风破浪~

梳理这份 Vue 面试题,初衷是为了帮助自己系统复习,查漏补缺,同时也希望能给正在准备前端面试的小伙伴们一些参考和启发!

1.你对vue的理解到什么程度?

对 Vue 的理解可以总结为以下几个方面,涵盖核心概念、高级特性和实际应用场景

1.1.核心机制理解

  • 响应式系统 :理解基于 Object.defineProperty(Vue 2)和 Proxy(Vue 3)的依赖追踪与触发更新机制,包括依赖收集(Dep/Watcher)和异步批量更新(nextTick)。
  • 虚拟 DOM 与渲染 :了解模板编译为渲染函数、虚拟 DOM 的 diff/patch 过程,以及 key 的作用。
  • 组件化 :掌握组件生命周期、父子通信(props/$emit)、跨组件通信(provide/inject、事件总线、Vuex/Pinia)。

1.2. Composition API 深度掌握

  • 熟练使用 refreactivecomputedwatch 等响应式 API,理解其与 Options API 的差异。
  • 能够合理组织逻辑复用(自定义组合式函数),避免 React Hooks 的闭包陷阱问题。
  • 了解 <script setup> 语法糖和编译器优化(如静态提升)。

1.3. 状态管理方案

  • Vuex:理解模块化、严格模式、插件机制。
  • Pinia:熟悉基于 Composition API 的设计、类型推导优势,以及持久化等插件生态。
  • 能够根据场景选择轻量级(共享状态)或集中式方案。

1.4. 性能优化实践

  • 组件级优化:v-oncev-memo、懒加载(defineAsyncComponent)。
  • 列表渲染:键策略与避免 v-if/v-for 混用。
  • 代码分割:路由懒加载(import() + Webpack/Vite)。
  • 响应式数据精细化控制(如 shallowRef 避免深层响应)。

1.5. 生态工具链

  • 构建工具 :Vite 的快速启动与 HMR 原理,配置 Vue 项目(如 @vitejs/plugin-vue)。
  • 路由:Vue Router 的动态路由、导航守卫、滚动行为控制。
  • 服务端渲染:Nuxt.js 的约定式开发、hydration 过程及 SEO 优化。

1.6. 原理层探究

  • 能解释模板如何编译为渲染函数(如 vue-template-compiler)。
  • 了解响应式系统的局限性(如数组/对象变更检测的注意事项)。
  • 熟悉自定义指令、插件开发等扩展机制。

1.7. 实战经验

  • 复杂表单处理(表单校验、动态表单)。
  • 权限控制(路由守卫、动态菜单)。
  • 与 TypeScript 深度集成(类型化 propsemits、Pinia Store)。

2.vue为什么要求组件模板只能有一个根元素?

Vue 2.x 要求组件模板必须有单个根元素 ,这一限制源于其虚拟 DOM 的 diff 算法设计。不过 Vue 3 通过引入 Fragment(片段) 支持了多根节点模板。以下是具体原因和演进过程:

Vue 2 单根元素的原因

  1. 虚拟 DOM 的 Patch 机制

    Vue 的响应式更新需要比较新旧虚拟 DOM 树(diff 算法),而 diff 算法需要明确的父子关系作为比较锚点。单根元素能确保:

    • 组件始终有一个确定的入口节点用于挂载和比对
    • 父组件通过 $el 属性能直接访问子组件的根 DOM
  2. 模板编译的确定性

    模板会被编译为渲染函数,单根节点使编译结果更可预测:

    javascript 复制代码
    // 编译后的渲染函数需要返回单个 VNode
    render(h) {
      return h('div', {}, [/* 子节点 */]) // 必须单一根节点
    }
  3. 与 HTML 解析兼容

    浏览器解析 HTML 时,若模板有多个并列根元素(如 <div></div><span></span>),实际会被包裹在隐式父级中,可能导致意外行为。

Vue 3 的多根节点支持

Vue 3 通过 Fragment 特性允许模板多根节点:

vue 复制代码
<template>
  <div>Node 1</div>
  <div>Node 2</div> <!-- 合法 -->
</template>

实现原理

  • 编译时自动将多根节点包裹在虚拟的 Fragment 节点中
  • Fragment 不会渲染为实际 DOM,仅作为逻辑容器
  • 父组件通过 $el 会指向多根节点中的第一个 DOM 元素

为什么 Vue 2 不这样设计?

  1. 性能权衡
    Vue 2 的虚拟 DOM 实现更简单高效,单根节点减少边缘情况处理。
  2. 渐进式演进
    Vue 3 重写了虚拟 DOM,引入 Fragment 等新特性以适配更复杂场景。

实际影响

场景 Vue 2 Vue 3
多根节点模板 ❌ 报错 ✅ 支持
$el 指向 根元素 第一个节点
递归组件引用自身 必须通过单根包裹 可直接多根

最佳实践建议

  1. Vue 2 项目 :用 <div><transition> 包裹多节点
  2. Vue 3 项目
    • 多根节点时注意样式作用域(可能需改用 :deep()
    • 需要访问根元素时,使用 ref 替代 $el

3.什么是diff算法?

Diff 算法是虚拟 DOM 的核心,用于对比新旧虚拟 DOM 树的差异,计算出最小的 DOM 操作。

1.为什么需要它?

直接操作DOM非常耗性能(比如 innerHTML 全量替换),而 虚拟 DOM(Virtual DOM) 提供了一种更高效的方式:

  1. 不直接操作真实 DOM(因为 DOM 操作很慢)。
  2. 先生成虚拟 DOM(JavaScript 对象,轻量级)
  3. 对比新旧虚拟 DOM(diff 算法)
  4. 只更新必要的真实 DOM(减少性能开销)
javascript 复制代码
// 直接操作 DOM(性能差)
document.getElementById('app').innerHTML = newHTML;

// 使用虚拟 DOM + Diff(性能优化)
const oldVNode = createVNode(oldTree);
const newVNode = createVNode(newTree);
const patches = diff(oldVNode, newVNode); // 计算差异
patch(realDOM, patches); // 局部更新

2. 主要策略

  • 同级比较:只比较同一层级的节点,不跨层级。
  • 双端比较(头头、尾尾、旧头新尾、旧尾新头)。
  • 依赖 key :用于精准识别相同节点(如 v-for 列表)

3. 核心流程

graph TD A[新旧虚拟DOM树] --> B[树级对比] B --> C{节点类型相同?} C -->|是| D[比较属性] C -->|否| E[替换整个节点] D --> F[子节点列表对比] F --> G[双端比较算法] G --> H[生成DOM操作指令]

双端比较算法

步骤 操作 目的
① 头头对比 旧头 vs 新头 处理头部相同节点
② 尾尾对比 旧尾 vs 新尾 处理尾部相同节点
③ 旧头 vs 新尾 交叉对比 检测列表反转情况
④ 旧尾 vs 新头 交叉对比 检测列表反转情况
key 匹配 建立索引查找 处理中间插入/删除

场景 :对比子节点列表 [A,B,C,D](旧)和 [D,A,B,E](新)

(1)初始化指针
javascript 复制代码
let oldStartIdx = 0, oldEndIdx = 3; // 旧列表头尾指针
let newStartIdx = 0, newEndIdx = 3; // 新列表头尾指针
(2)双端比较步骤
javascript 复制代码
// 步骤1:头头对比 (A vs D)
if (oldChildren[oldStartIdx].key === newChildren[newStartIdx].key) {
  // 不匹配,进入下一步
}

// 步骤2:尾尾对比 (D vs E)
if (oldChildren[oldEndIdx].key === newChildren[newEndIdx].key) {
  // 不匹配,进入下一步
}

// 步骤3:旧头 vs 新尾 (A vs E)
if (oldChildren[oldStartIdx].key === newChildren[newEndIdx].key) {
  // 不匹配,进入下一步
}

// 步骤4:旧尾 vs 新头 (D vs D) -> 匹配!
if (oldChildren[oldEndIdx].key === newChildren[newStartIdx].key) {
  patchVnode(oldChildren[oldEndIdx], newChildren[newStartIdx]);
  // 移动 DOM 节点到新位置
  parent.insertBefore(oldChildren[oldEndIdx].elm, oldChildren[oldStartIdx].elm);
  oldEndIdx--;
  newStartIdx++;
}

// 步骤5:key 映射查找剩余节点
const keyMap = {};
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
  keyMap[oldChildren[i].key] = i;
}

图示过程

ini 复制代码
旧列表: [A, B, C, D]   新列表: [D, A, B, E]
       ↑       ↑            ↑       ↑
     oldStart oldEnd      newStart newEnd

1. D 匹配,移动 D 到最前面
2. 剩余比较 [A,B,C] 和 [A,B,E]
3. 发现 E 是新增,插入

4. Key 的作用

没有 Key 的问题

javascript 复制代码
// 旧列表: [A, B, C]
// 新列表: [B, A, C]
// 没有 key 时,可能误判 B 是修改后的 A

正确使用 Key

html 复制代码
<!-- Vue 模板 -->
<template>
  <div v-for="item in list" :key="item.id">
    {{ item.text }}
  </div>
</template>
javascript 复制代码
// 虚拟 DOM 结构
{
  tag: 'div',
  key: item.id, // 通过 key 精准匹配
  children: item.text
}

5. Vue 3 优化

静态提升(Hoist Static)

javascript 复制代码
// 编译前
<template>
  <div>Hello</div>  <!-- 静态节点 -->
  <div>{{ msg }}</div>
</template>

// 编译后(静态节点被提升)
const _hoisted = createVNode('div', null, 'Hello');
function render() {
  return [_hoisted, createVNode('div', null, ctx.msg)];
}

Block Tree 动态追踪

javascript 复制代码
// 编译时标记动态节点
const block = {
  dynamicChildren: [
    { tag: 'div', props: { id: ctx.id } } // 只追踪动态部分
  ]
}

6. 完整 Diff 示例

javascript 复制代码
function updateChildren(parentElm, oldCh, newCh) {
  let oldStartIdx = 0, newStartIdx = 0;
  let oldEndIdx = oldCh.length - 1, newEndIdx = newCh.length - 1;

  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    // 双端比较逻辑...
  }

  // 处理剩余节点
  if (oldStartIdx > oldEndIdx) {
    // 新增节点
    addVnodes(parentElm, newCh[newStartIdx]);
  } else {
    // 删除旧节点
    removeVnodes(parentElm, oldCh[oldStartIdx]);
  }
}

7.示例对比

场景:列表更新

Vue 2 的处理

  1. 全量比对新旧列表。
  2. 依赖 key 查找可复用节点。
  3. 移动或新增/删除 DOM。

Vue 3 的处理

  1. 通过 Block Tree 直接定位动态节点。
  2. 静态部分跳过比对。
  3. 仅更新变化的 DOM。

8.总结对比表

特性 Vue 2 Vue 3
Diff 策略 双端比较 + 全量比对 双端比较 + 静态标记 + Block Tree (动态追踪)
多根节点 ❌ 不支持 ✅ Fragment 支持
静态节点处理 参与 Diff ✅ 静态提升(不参与 Diff)
动态节点优化 ❌ 无 ✅ Patch Flag 标记动态属性
性能 (较慢)全量比对 更快(精准比对)
Key 必要性 必需 必需(但优化了复用逻辑)

4. watch和created哪个先执行?为什么?

在 Vue 的生命周期中,created 钩子会先于 watch 执行。这是因为:

  1. 初始化阶段

    • Vue 首先初始化数据(dataprops 等)。
    • 然后调用 created 钩子,此时组件实例已创建完成,但 DOM 还未生成。
  2. watch 设置阶段

    • 在数据初始化完成后,Vue 才会设置响应式系统和 watch 监听器。
    • watch 的首次触发是在数据变化后,而 created 是同步执行的钩子。

示例代码

javascript 复制代码
export default {
  data() {
    return { count: 0 };
  },
  created() {
    console.log("created 执行"); // 1. 先执行
    this.count = 1; // 修改数据
  },
  watch: {
    count() {
      console.log("watch 触发"); // 2. 后执行
    },
  },
};

5. mixins和extends有什么区别?

特性 mixins extends
数量 可混入多个 只能继承一个
合并策略 选项合并(相同选项合并为数组) 类似 mixins,但优先级更高
用途 功能复用 扩展基础组件
执行顺序 后定义的 mixin 先执行 在 mixins 之前执行
组件优先级 低于组件自身选项 高于 mixins,但低于组件自身选项

示例

javascript 复制代码
// extends 示例
const BaseComponent = { methods: { log() { console.log("base"); } } };

// mixins 示例
const myMixin = { methods: { log() { console.log("mixin"); } } };

export default {
  extends: BaseComponent,
  mixins: [myMixin],
  methods: {
    log() {
      console.log("component");
      // 调用优先级:component > extends > mixins
    },
  },
};

6. mixins有什么使用场景?

  1. 跨组件共享逻辑
    • 表单验证、数据获取等通用逻辑。
  2. 功能注入
    • 日志记录、埋点统计等全局功能。
  3. UI 行为模式
    • 拖拽、无限滚动等可复用的交互行为。

最佳实践

  • 保持单一职责,避免复杂依赖。
  • 使用命名前缀防止属性冲突(如 $_mixinName)。

7. created与activated有什么区别?

钩子 调用时机 调用次数 适用场景
created 组件实例创建完成后同步调用 1 次 初始化数据、API 调用
activated <keep-alive> 缓存的组件激活时 多次 恢复定时器、刷新数据

示例

javascript 复制代码
export default {
  created() {
    console.log("组件创建"); // 仅执行一次
  },
  activated() {
    console.log("组件激活"); // 每次从缓存恢复时触发
  },
};

8. 如何引入异步组件?

方法 1:工厂函数

javascript 复制代码
components: {
  AsyncComp: () => import("./AsyncComp.vue"),
}

方法 2:高级异步组件(支持 Loading/Error 状态)

javascript 复制代码
const AsyncComp = () => ({
  component: import("./AsyncComp.vue"),
  loading: LoadingComp,
  error: ErrorComp,
  delay: 200,    // 延迟显示 Loading
  timeout: 3000, // 超时时间
});

路由懒加载

javascript 复制代码
const router = new VueRouter({
  routes: [{ path: "/async", component: () => import("./AsyncRoute.vue") }],
});

9. 在vue项目中scss scoped穿透符,无效的解决方案有哪些?

问题

scoped 样式中,>>> 在 Sass/SCSS 中可能失效。

解决方案

  1. 使用 ::v-deep/deep/

    scss 复制代码
    .parent ::v-deep .child { color: red; }
  2. 全局样式(不加 scoped)。

  3. CSS Modules:

    html 复制代码
    <style module>
    .parent { /* ... */ }
    .parent .child { /* ... */ }
    </style>

10. 什么在v-for中的key不推荐使用随机数或者index呢?那要怎么使用才比较好呢?

原因

  • 随机数:每次渲染生成新 key,导致 DOM 无法复用,性能下降。
  • index:列表顺序变化时,key 不稳定性可能导致状态错乱(如输入框内容错位)。

推荐做法

html 复制代码
<div v-for="item in items" :key="item.id">{{ item.text }}</div>

11. vue-loader在webpack编译流程中的哪个阶段?

作用阶段 :Webpack 的 模块转换阶段
功能

  1. 解析 .vue 单文件组件。
  2. 拆解为 template/script/style,分别交给对应 loader 处理。
  3. 生成可执行的 JavaScript 模块。

配置示例

javascript 复制代码
// webpack.config.js
module: {
  rules: [
    {
      test: /\.vue$/,
      loader: "vue-loader",
      options: { /* ... */ },
    },
  ],
},

12. 预渲染和SSR(服务端渲染)有什么区别?

特性 预渲染 (Prerendering) 服务端渲染 (SSR)
渲染时机 构建时生成静态HTML 每次请求时服务器动态渲染
适用场景 静态页面(如官网、博客) 动态内容(如用户仪表盘)
SEO支持 ✅ 完全支持 ✅ 完全支持
实现复杂度 ⚠️ 简单 ⚠️ 复杂
服务器要求 无需Node.js(纯静态托管) 需Node.js服务器
数据实时性 ❌ 需重新构建更新 ✅ 请求时实时获取

如何选择

  • 纯静态内容 → 预渲染(如prerender-spa-plugin
  • 用户相关动态数据 → SSR(如Nuxt.js

13. 你有用过预渲染技术吗?怎么做的?

预渲染技术是一种用于提升网页性能和SEO的技术,它在服务器端生成HTML内容,而不是在客户端动态生成。Vue.js支持多种预渲染技术,比如服务器端渲染(SSR)静态站点生成(SSG)

预渲染技术实现方案

  1. 静态站点生成(SSG)
bash 复制代码
# 使用Vue CLI插件
vue add prerender-spa
javascript 复制代码
// vue.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin')

module.exports = {
  plugins: [
    new PrerenderSPAPlugin({
      staticDir: path.join(__dirname, 'dist'),
      routes: ['/', '/about', '/contact'],
      renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
        renderAfterDocumentEvent: 'render-complete'
      })
    })
  ]
}
  1. 服务端渲染(SSR)
bash 复制代码
# 使用Nuxt.js框架
npx create-nuxt-app my-ssr-project
javascript 复制代码
// nuxt.config.js
export default {
  ssr: true,  // 开启服务端渲染模式
  target: 'server' // 默认配置
}

核心区别

  • SSG:构建时生成静态HTML,适合内容不变的页面
  • SSR:每次请求时动态渲染内容,适合个性化内容

14. 使用vue如何判断页面是否编辑及编辑页面未保存离开时,给出弹窗提示?

可以通过监听页面的beforeunload事件来实现。当用户尝试离开页面时,检查是否有未保存的编辑内容,如果有,则弹出提示。

完整解决方案

javascript 复制代码
export default {
  data() {
    return {
      isEdited: false, // 页面标记是否编辑
      formData: {},
      initialData: {}
    }
  },
  
  created() {
    this.initialData = JSON.parse(JSON.stringify(this.formData))
  },
  
  watch: {
    formData: {
      deep: true,
      handler() {
        this.isEdited = !_.isEqual(this.formData, this.initialData)
      }
    }
  },
  
  beforeRouteLeave(to, from, next) {
    if (this.isEdited) {
      this.$confirm({
        title: '确认离开',
        message: '有未保存的更改,确定要离开吗?',
        confirmButtonText: '离开',
        cancelButtonText: '取消'
      }).then(() => {
        next()  // 确认离开
      }).catch(() => {
        next(false)  // 取消离开
      })
    } else {
      next() // 没有编辑内容直接离开
    }
  },
  
  mounted() {
    window.addEventListener('beforeunload', this.handleWindowClose)
  },
  
  methods: {
    handleWindowClose(e) {
      if (this.isEdited) {
        e.preventDefault()
        e.returnValue = '您有未保存的更改'
      }
    }
  },
  
  beforeDestroy() {
    window.removeEventListener('beforeunload', this.handleWindowClose)
  }
}

15. vue的.sync修饰符可以用表达式吗?为什么?

.sync修饰符不能直接使用表达式。.sync修饰符是Vue 2.x中用于双向绑定子组件的props的语法糖,它基于v-bindv-on实现。

深度解析

  1. 不能使用表达式
html 复制代码
<!-- 错误用法 -->
<component :[dynamicProp].sync="value" />
  1. 根本原因
  • .sync是编译时语法糖,会被转换为固定的update:propName模式
  • 表达式无法在编译阶段静态分析
  • 底层实现依赖明确的prop名称
  1. 替代方案
javascript 复制代码
<component :prop.sync="value"></component>
// 等价于
<component 
  :propName="value"
  @update:propName="value = $event"
/>

16. v-if和v-show哪个优先级更高?

v-if的优先级高于v-show

  • v-if条件渲染 ,它会根据条件的真假来决定是否渲染元素。如果条件为false,元素不会被渲染到DOM中。
  • v-show条件显示 ,它会始终渲染元素,只是通过CSS的display属性来控制元素的显示和隐藏。
  • 当同时使用v-ifv-show时,v-if会先决定是否渲染元素,如果v-if的条件为falsev-show不会生效。

17. v-for和v-if哪个优先级更高?

版本对比分析

Vue版本 优先级 具体表现
2.x v-for 先循环再判断,性能较差
3.x v-if 先判断再循环,推荐用法

最佳实践

html 复制代码
<!-- Vue2兼容写法 -->
<template v-for="item in list">
  <div v-if="item.isActive" :key="item.id">
    {{ item.name }}
  </div>
</template>

<!-- Vue3推荐写法 -->
<div v-for="item in activeItems" :key="item.id">
  {{ item.name }}
</div>

<script>
computed: {
  //  将条件逻辑提前到数据层面,而不是依赖模板中的 v-if
  activeItems() { 
    return this.list.filter(item => item.isActive)
  }
}
</script>

18. 如何批量引入组件?

适用场景

  • Webpack 项目 :使用 require.context
  • Vite 项目 :使用 glob 动态导入。
  • 少量组件:手动批量注册。
  • Vue CLI 项目:使用插件自动导入。

1. 使用 require.context(适用于 Webpack)

components/index.js 中:

javascript 复制代码
const files = require.context('./', false, /\.vue$/);
const components = files.keys().map(key => ({
  name: key.replace(/(\.\/|\.vue)/g, ''),
  component: files(key).default
}));

const install = Vue => {
  components.forEach(item => Vue.component(item.name, item.component));
};

export default { install };

main.js 中:

javascript 复制代码
import Vue from 'vue';
import Components from './components';
Vue.use(Components);

2. 使用 glob 和动态导入(适用于 Vite)

components/index.js 中:

javascript 复制代码
import { defineAsyncComponent } from 'vue';
const components = import.meta.glob('./**/*.vue', { eager: true });

const install = Vue => {
  for (const path in components) {
    const name = path.split('/').pop().replace(/\.vue$/, '');
    Vue.component(name, defineAsyncComponent(() => components[path]));
  }
};

export default { install };

main.js 中:

javascript 复制代码
import Vue from 'vue';
import Components from './components';
Vue.use(Components);

3. 手动批量注册(适用于少量组件)

components/index.js 中:

javascript 复制代码
import Button from './Button.vue';
import Input from './Input.vue';

const install = Vue => {
  Vue.component('Button', Button);
  Vue.component('Input', Input);
};

export default { install };

main.js 中:

javascript 复制代码
import Vue from 'vue';
import Components from './components';
Vue.use(Components);

4. 使用 Vue CLI 插件(如 vue-cli-plugin-auto-import

安装插件:

bash 复制代码
npm install -D vue-cli-plugin-auto-import

vue.config.js 中配置:

javascript 复制代码
module.exports = {
  pluginOptions: {
    'auto-import': {
      components: true
    }
  }
};

19. vue的v-for如何倒序输出?

四种实现方式

  1. 计算属性(推荐)
javascript 复制代码
computed: {
  reversedItems() {
    return [...this.items].reverse()
  }
}
  1. 模板内反转
html 复制代码
<div v-for="item in [...items].reverse()" :key="item.id">
  1. 方法处理
javascript 复制代码
methods: {
  reverseArray(arr) {
    return [...arr].reverse()
  }
}
  1. 后端排序
javascript 复制代码
// API请求时添加排序参数
axios.get('/api/items?sort=desc')

20. 如何在全局使用axios的实例呢?

在 Vue 项目中全局使用 Axios 实例是一种常见的做法,可以方便地在各个组件中调用 HTTP 请求,同时也可以统一配置 Axios 的基础路径、拦截器等。以下是实现全局使用 Axios 实例的步骤:

1. 安装 Axios

如果尚未安装 Axios,可以通过以下命令安装:

复制代码
npm install axios

2. 创建 Axios 实例

在项目中创建一个专门的文件(如 src/axios.jssrc/api/index.js),用于配置和导出 Axios 实例。

javascript 复制代码
// utils/request.js
import axios from 'axios'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
})

// 请求拦截
service.interceptors.request.use(config => {
  config.headers['Authorization'] = getToken()
  return config
})

// 响应拦截
service.interceptors.response.use(
  response => response.data,
  error => {
    if (error.response.status === 401) {
      router.push('/login')
    }
    return Promise.reject(error)
  }
)

export default service

// main.js
import request from '@/utils/request'
app.config.globalProperties.$http = request

// 组件中使用
this.$http.get('/user/info')

21. v-show指令算是重排吗?

浏览器渲染原理分析

关于"重排"(Reflow)

"重排"(Reflow)是指浏览器重新计算 DOM 元素的布局的过程。当 DOM 元素的尺寸、位置或其他布局相关的属性发生变化时,浏览器需要重新计算这些元素的布局,从而触发重排。重排是一个相对耗性能的操作,因为它涉及到重新计算页面的布局。

  1. v-show本质
css 复制代码
/* 显示时 该元素重新进入布局,浏览器同样需要重新计算布局 */
display: block;

/* 隐藏时 该元素从布局中移除,浏览器需要重新计算周围元素的布局*/
display: none;
  1. 重排影响
  • 初次渲染:导致一次完整重排
  • 切换显示时:触发相邻元素重排
  • 性能消耗:比重绘(repaint)更昂贵
  1. 优化建议
  • 频繁切换的元素使用v-show
  • 首次渲染不需要的元素使用v-if
  • 复杂动画考虑使用visibility + opacity

21. axios同时请求多个接口,如果当token过期时,怎么取消后面的请求?

请求生命周期管理:

取消请求解决方案CancelToken

  1. 统一管理所有请求:使用一个数组(或 Map)存储所有进行中的请求及其取消函数
  2. 拦截 401 响应:在响应拦截器中检测 Token 过期(401 状态码)
  3. 批量取消请求:当检测到 Token 过期时,遍历并执行所有存储的取消函数
  4. 清理资源:取消后清空存储的请求队列,避免内存泄漏
javascript 复制代码
// utils/request.js

import axios from 'axios';

// 存储所有进行中的请求及其取消函数
const pendingRequests = new Map();

// 创建 Axios 实例
const api = axios.create({
  baseURL: 'https://api.example.com',
});

// 请求拦截器:添加 Token 和取消机制
api.interceptors.request.use(config => {
  // 获取 Token(根据实际存储位置调整)
  const token = localStorage.getItem('auth_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  // 创建取消令牌
  const source = axios.CancelToken.source();
  config.cancelToken = source.token;

  // 生成请求唯一标识(URL + 方法 + 参数)
  const requestId = `${config.url}-${JSON.stringify(config.params)}-${config.method}`;
  
  // 存储取消函数
  pendingRequests.set(requestId, source.cancel);
  
  // 请求完成后自动移除
  config.meta = config.meta || {};
  config.meta.requestId = requestId;

  return config;
});

// 响应拦截器:处理 Token 过期
api.interceptors.response.use(
  response => {
    // 请求成功完成,从队列移除
    const requestId = response.config.meta?.requestId;
    if (requestId) pendingRequests.delete(requestId);
    return response;
  },
  error => {
    // 请求失败,从队列移除
    const requestId = error.config.meta?.requestId;
    if (requestId) pendingRequests.delete(requestId);

    // 检测 Token 过期
    if (error.response?.status === 401) {
      cancelAllRequests('Token expired, canceling pending requests');
      // 这里可以添加重定向到登录页等逻辑
      window.location.href = '/login';
    }

    // 如果是主动取消的请求,不报错
    if (axios.isCancel(error)) {
      return new Promise(() => {}); // 中断 Promise 链
    }
    
    return Promise.reject(error);
  }
);

// 取消所有进行中的请求
function cancelAllRequests(message) {
  pendingRequests.forEach(cancel => {
    cancel(message);
  });
  pendingRequests.clear(); // 清空队列
  console.warn(message);
}

// 示例:同时发起多个请求
const requests = [
  api.get('/user'),
  api.get('/posts'),
  api.get('/notifications')
];

Promise.allSettled(requests)
  .then(results => {
    results.forEach(result => {
      if (result.status === 'rejected' && !axios.isCancel(result.reason)) {
        // 处理真实错误(非取消导致的错误)
        console.error('Request failed:', result.reason.message);
      }
    });
  });

22.从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织?

创建项目基础

bash 复制代码
npm init vue@latest my-vue-project

或使用Vite:

bash 复制代码
npm create vite@latest my-vue-project --template vue

安装基础依赖

bash 复制代码
cd my-vue-project
npm install

配置TypeScript(可选)

  • 安装TypeScript相关依赖
  • 配置tsconfig.json

配置代码规范和格式化

  • ESLint + Prettier
  • Stylelint(用于CSS规范)

插件推荐

核心插件

  • vue-router:官方路由管理
  • pinia:状态管理(替代Vuex)
  • axios:HTTP请求库
  • vite(或webpack):构建工具

UI组件库(选其一)

  • Element Plus / Ant Design Vue / Vuetify / Naive UI

开发辅助

  • unplugin-auto-import:自动导入API
  • unplugin-vue-components:自动导入组件
  • vite-plugin-svg-icons:SVG图标处理
  • vueuse:常用组合式API工具集

测试工具

  • vitest(或Jest):单元测试
  • cypress:E2E测试

其他实用插件

  • dayjs:日期处理
  • lodash-es:实用工具库
  • nprogress:页面加载进度条
  • js-cookie:Cookie操作

目录结构组织

bash 复制代码
my-vue-project/
├── public/                   # 静态资源(不经过打包处理)
├── src/
│   ├── api/                  # API请求相关
│   │   ├── modules/          # 按模块划分的API
│   │   └── index.ts          # API统一导出
│   ├── assets/               # 静态资源(会经过打包处理)
│   │   ├── images/           # 图片资源
│   │   ├── svg/              # SVG图标
│   │   └── styles/           # 全局样式
│   ├── components/           # 公共组件
│   │   ├── common/           # 全局通用组件
│   │   └── business/         # 业务通用组件
│   ├── composables/          # 组合式函数
│   ├── directives/           # 自定义指令
│   ├── hooks/                # 自定义hooks
│   ├── layouts/              # 布局组件
│   ├── router/               # 路由配置
│   ├── stores/               # Pinia状态管理
│   ├── utils/                # 工具函数
│   ├── views/                # 页面组件
│   ├── App.vue               # 根组件
│   └── main.ts               # 应用入口
├── .env                      # 环境变量
├── .env.development          # 开发环境变量
├── .env.production           # 生产环境变量
├── vite.config.ts            # Vite配置
├── tsconfig.json             # TypeScript配置
├── package.json
└── README.md

详细配置建议

  1. Vite配置优化

    • 配置alias路径别名
    • 配置proxy代理
    • 配置打包优化选项
  2. 路由配置

    • 实现路由懒加载
    • 配置路由守卫
    • 实现权限路由(如果需要)
  3. 状态管理

    • 按模块划分store
    • 配置持久化存储(如需)
  4. 样式方案

    • 选择CSS预处理器(Sass/Less)
    • 配置全局变量和mixin
    • 考虑CSS Modules或CSS-in-JS方案
  5. 环境配置

    • 区分不同环境变量
    • 配置多环境打包脚本
  6. Git工作流

    • 配置.gitignore
    • 添加commitlint和husky(如需)

项目启动后的优化方向

  1. 性能优化

    • 代码分割
    • 按需加载
    • 图片压缩
  2. 安全优化

    • XSS防护
    • CSRF防护
    • API安全策略
  3. SEO优化(如需)

    • SSR或静态生成考虑
    • 元信息管理
  4. 监控与错误追踪

    • 错误边界处理
    • 接入Sentry等监控工具

23. 你知道vue的模板语法用的是哪个web模板引擎的吗?说说你对它的理解

1. 本质

Vue 没有使用第三方模板引擎 (如 Mustache/EJS),而是自研了一套基于 HTML 的编译型模板语法 ,最终编译成虚拟 DOM 渲染函数 ,Vue 模板是专为响应式组件设计的编译型语法,比传统模板引擎更强大,比 JSX 更易读。。

2. 核心特点

  • 声明式绑定{{ data }} 自动响应数据变化。
  • 指令系统v-ifv-for 等控制 DOM,比传统引擎(如 Handlebars 的 {{#if}})更直观。
  • 编译优化:模板会被预编译为 JS 代码,运行时直接操作虚拟 DOM,性能远超传统字符串拼接引擎。

指令系统

  • v-if:条件渲染
  • v-for:列表渲染
  • v-bind:动态属性
  • v-on:事件监听

3. 与传统引擎对比

Vue 模板 Mustache/Handlebars
更新机制 响应式(自动更新) 手动重新渲染
性能 虚拟 DOM 差量更新 全量替换 HTML
扩展能力 组件化 + 自定义指令 依赖 Helper 函数

4. 编译过程

html 复制代码
<div>{{ message }}</div>  

↓ 编译为 ↓

javascript 复制代码
// vue2
function render() { return h('div', message) }  

// vue3
function render(_ctx) {
  return _createVNode("div", null, _toDisplayString(_ctx.message))
}

5. 为什么不用 JSX?

  • 模板更贴合 HTML:对设计师/后端开发者更友好。
  • 静态优化:Vue 模板能标记静态节点,提升性能。

6. 性能优势

  • 比传统字符串模板快5倍
  • 虚拟DOM减少直接DOM操作

24. v-model原理剖析

v-model 是 Vue 中用于实现双向数据绑定 的语法糖,它本质上是一个属性绑定 + 事件监听的组合。

1. 基本实现原理

v-model 在底层会被编译为:

  • 一个 valuemodelValue 的属性绑定(props)
  • 一个对应的事件监听(通常是 inputupdate:modelValue

对于原生表单元素

javascript 复制代码
// 原生表单元素
<input v-model="text">
// 被编译为
<input 
  :value="text"
  @input="text = $event.target.value"
>

2. 在自定义组件上的实现

在 Vue 3 中,自定义组件的 v-model 默认使用 modelValue prop 和 update:modelValue 事件

html 复制代码
<CustomComponent v-model="message" />

↓ 编译为 ↓

<CustomComponent 
  :modelValue="message"
  @update:modelValue="newValue => message = newValue"
/>

Vue3升级 多v-model支持:

html 复制代码
<CustomComponent
  v-model:name="userName"
  v-model:email="userEmail"
/>

↓ 编译为 ↓

<CustomComponent
  :name="userName"
  @update:name="newValue => userName = newValue"
  :email="userEmail"
  @update:email="newValue => userEmail = newValue"
/>

3. 在不同元素上的实现差异

文本输入框(input type="text")

  • 绑定 value 属性
  • 监听 input 事件

复选框(checkbox)

  • 绑定 checked 属性
  • 监听 change 事件
  • 处理数组值的情况

单选按钮(radio)

  • 绑定 checked 属性
  • 监听 change 事件

选择框(select)

  • 绑定 value 属性
  • 监听 change 事件

25. 多语言项目实践

完整解决方案

javascript 复制代码
// i18n.js
import { createI18n } from 'vue-i18n'

const messages = {
  en: {
    greeting: 'Hello {name}',
    date: 'Today is {date}'
  },
  zh: {
    greeting: '你好 {name}',
    date: '今天是 {date}'
  }
}

const i18n = createI18n({
  locale: localStorage.getItem('lang') || 'zh',
  messages,
  pluralizationRules: {
    zh(choice) {
      // 中文复数规则
    }
  }
})

// 组件中使用
const { t } = useI18n()
console.log(t('greeting', { name: 'John' }))

26. 计算属性命名冲突

核心规则

  • ❌ 禁止与data同名
  • 原因:两者都挂载到组件实例
  • 解决方案:
javascript 复制代码
data() {
  return { rawCount: 0 } // data用raw前缀
},
computed: {
  formattedCount() {      // computed用formatted前缀
    return this.rawCount * 2
  }
}

27. data与methods同名问题

重要结论

  1. ❌ 严格禁止同名
  2. 优先级:methods > data
  3. 后果:方法会覆盖数据属性
  4. 解决方案:
javascript 复制代码
data() {
  return { userCount: 0 } // 名词命名
},
methods: {
  countUsers() { }       // 动词命名
}

28.怎么给vue定义全局的方法?

1. 通过 Vue.prototype (Vue 2)

javascript 复制代码
// main.js
Vue.prototype.$myMethod = function() {
  console.log('这是全局方法')
}

// 组件中使用
this.$myMethod()

2. 使用全局混入 (Vue 2/3 都适用)

javascript 复制代码
// main.js
Vue.mixin({
  methods: {
    $globalMethod() {
      console.log('全局混入方法')
    }
  }
})

// 组件中使用
this.$globalMethod()

3. 使用 provide/inject (Vue 2.2+/3 推荐)

javascript 复制代码
// 根组件
export default {
  provide() {
    return {
      app: {
        globalMethod: () => console.log('注入的全局方法')
      }
    }
  }
}

// 子组件中使用
export default {
  inject: ['app'],
  mounted() {
    this.app.globalMethod()
  }
}

4. 使用 Composition API 的 provide (Vue 3)

javascript 复制代码
// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.provide('globalMethod', () => {
  console.log('Composition API 全局方法')
})

// 组件中使用
import { inject } from 'vue'

export default {
  setup() {
    const globalMethod = inject('globalMethod')
    globalMethod()
  }
}

5. 使用插件方式 (最规范的做法)

javascript 复制代码
// plugins/globalMethods.js
export default {
  install(app) {
    app.config.globalProperties.$formatDate = (date) => {
      return new Date(date).toLocaleDateString()
    }
  }
}

// main.js
import globalMethods from './plugins/globalMethods'
app.use(globalMethods)

// 组件中使用
this.$formatDate(new Date())

注意事项

  • 避免过度使用全局方法,优先考虑组件间通信或 Vuex/Pinia 状态管理
  • 全局方法命名建议添加前缀(如 $)以避免命名冲突
  • 对于工具类函数,也可以考虑单独模块导出而不是挂载到 Vue 实例

29. vue2.0不再支持v-html中使用过滤器了怎么办?

为什么 Vue 移除了这个功能?

Vue 团队认为:

  • 过滤器在 v-html 中的使用场景有限
  • 增加了模板编译的复杂性
  • 可能导致安全问题不易被发现
  • 使用方法和计算属性是更明确的替代方案

在 Vue 2.0 中,确实不再支持在 v-html 指令中直接使用过滤器。以下是几种替代方案:

1. 使用方法替代过滤器

javascript 复制代码
// 组件中定义方法
methods: {
  formatHtml(html) {
    // 在这里实现你的过滤逻辑
    return html.replace(/\n/g, '<br>')
  }
}
html 复制代码
<!-- 模板中使用 -->
<div v-html="formatHtml(rawHtml)"></div>

2. 使用计算属性

javascript 复制代码
computed: {
  processedHtml() {
    // 在这里实现过滤逻辑
    return this.someFilter(this.rawHtml)
  },
  someFilter() {
    // 定义过滤器逻辑
    return function(value) {
      return value.toUpperCase() // 示例
    }
  }
}
html 复制代码
<div v-html="processedHtml"></div>

3. 创建自定义指令

javascript 复制代码
// 全局注册指令
Vue.directive('format-html', {
  bind: function(el, binding) {
    // 在这里实现过滤逻辑
    el.innerHTML = binding.value.replace(/\n/g, '<br>')
  }
})
html 复制代码
<!-- 使用指令 -->
<div v-format-html="rawHtml"></div>

4. 使用组件封装

javascript 复制代码
Vue.component('safe-html', {
  props: ['html'],
  methods: {
    filter(html) {
      // 过滤逻辑
      return html
    }
  },
  template: '<div v-html="filter(html)"></div>'
})
html 复制代码
<safe-html :html="rawHtml"></safe-html>

5. 使用第三方库

可以考虑使用专门处理 HTML 的库,如:

  • DOMPurify (安全过滤)
  • marked (Markdown 转换)
javascript 复制代码
import DOMPurify from 'dompurify'

// 在方法中使用
methods: {
  sanitize(html) {
    return DOMPurify.sanitize(html)
  }
}

最佳实践建议

  1. 安全性优先 :使用 v-html 时务必注意 XSS 风险,建议使用 DOMPurify 等库进行净化
  2. 性能考虑:对于频繁更新的内容,使用计算属性比方法更高效
  3. 代码组织:复杂的过滤逻辑可以提取到单独的模块/工具函数中

30.怎么解决vue打包后静态资源图片失效的问题?

常见原因分析

  1. 路径错误:开发和生产环境路径不一致
  2. 文件未正确打包:图片未被正确处理或复制到 dist 目录
  3. 相对路径问题:部署到子目录时路径解析错误
  4. URL 处理方式不当:动态绑定的图片路径未正确处理

解决方案:

1. 正确引用静态资源

javascript 复制代码
//  推荐使用相对路径或 require引入图片
<img :src="imgUrl" >

imgUrl: require('@/assets/images/logo.png')

在 CSS 中引用

css 复制代码
/* 使用相对路径 */
background: url('./assets/images/bg.jpg');

2. 配置 vue.config.js

如果使用了自定义的 Webpack 配置,确保 url-loaderfile-loader 正确处理图片资源

javascript 复制代码
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  chainWebpack: config => {
    config.module
      .rule('images')
      .use('url-loader')
      .loader('url-loader')
      .tap(options => ({
        ...options,
        limit: 4096, // 小于4kb的图片转为base64
        fallback: {
          loader: 'file-loader',
          options: {
            name: 'img/[name].[hash:8].[ext]'
          }
        }
      }))
  }
}

3. 使用环境变量控制路径

javascript 复制代码
// .env.production
VUE_APP_PUBLIC_PATH=/my-app/

// vue.config.js
module.exports = {
  publicPath: process.env.VUE_APP_PUBLIC_PATH || '/'
}

检查与调试技巧

  1. 检查打包后的 dist 目录

    • 确认图片文件是否被正确复制
    • 检查文件命名是否符合预期(是否有 hash 值)
  2. 浏览器开发者工具

    • 查看图片请求的完整 URL
    • 检查 404 错误的具体路径
  3. 查看生成的 HTML/CSS

    • 确认图片路径在生成的代码中是否正确

31.怎么解决vue动态设置img的src不生效的问题?

核心原因 :Vue 动态绑定图片路径时,直接使用字符串路径会导致 webpack 无法处理资源依赖关系,从而在打包后路径失效。

解决方案

1. 使用 require 引入图片(推荐)

javascript 复制代码
// 方法1:直接require
<img :src="require('@/assets/images/' + imgName)" />
const imageUrl = require(`@/assets/${dynamicName}.png`);

// 方法2:在data/computed中处理
data() {
  return {
    dynamicImg: require('@/assets/images/example.jpg')
  }
}

// 3.提前导入所有可能的图片
const imageMap = {
  'cat': require('@/assets/cat.jpg'),
  'dog': require('@/assets/dog.jpg')
}

// 4.使用new URL() 引入图片(适用于 Vite 项目)
const imageUrl = new URL(`../assets/${dynamicName}.png`, import.meta.url).href;

2. 使用 import 引入图片

javascript 复制代码
// 在script部分先import
import imgUrl from '@/assets/images/dynamic-img.jpg'

export default {
  data() {
    return {
      dynamicSrc: imgUrl
    }
  }
}

3. 处理动态路径拼接

javascript 复制代码
// 工具函数
methods: {
  getImgUrl(img) {
    return require(`@/assets/images/${img}`)
  }
}

// 使用
<img :src="getImgUrl('product-' + id + '.jpg')" />

4. 使用 public 目录(不推荐)

javascript 复制代码
// 将图片放在public/img目录
<img :src="'/img/' + imgName" />

// 注意:public目录文件不会被webpack处理

常见问题排查

  1. 路径大小写问题

    确保路径中的大小写与实际文件完全一致

  2. 路径拼接错误

    javascript 复制代码
    // 错误示例(缺少文件后缀)
    require(`@/assets/images/${imgName}`) // 如果imgName不带.jpg会失败
  3. webpack配置问题 确保 vue.config.js 正确配置了文件加载器:

    javascript 复制代码
    module.exports = {
      chainWebpack: config => {
        config.module
          .rule('images')
          .use('url-loader')
          .loader('url-loader')
          .tap(options => ({
            limit: 8192,
            name: 'img/[name].[hash:8].[ext]'
          }))
      }
    }

最佳实践

  1. 小图片:使用 require 动态加载
  2. 大量动态图片:考虑放在 public 目录或使用 CDN
  3. 生产环境:确保路径是相对路径(配置 publicPath: './')

32. 使用vue后怎么针对搜索引擎做SEO优化?

专业解决方案

  1. 服务端渲染(SSR)
  • 使用Nuxt.js框架
  • 配置nuxt.config.js中的target: 'server'
  • 优点:完美解决SPA的SEO问题
  • 缺点:需要Node.js服务器支持
  1. 预渲染(Prerender)
bash 复制代码
npm install prerender-spa-plugin
javascript 复制代码
// vue.config.js
new PrerenderSPAPlugin({
  routes: ['/', '/about'],
  renderer: new PrerenderSPAPlugin.PuppeteerRenderer()
})
  • 适用:静态内容为主的网站
  1. 动态Meta管理
javascript 复制代码
// 使用vue-meta插件
head() {
  return {
    title: this.pageTitle,
    meta: [
      { hid: 'description', name: 'description', content: this.pageDesc }
    ]
  }
}

33. 跟keep-alive有关的生命周期

专业解析

  1. activated
  • 触发时机:被缓存的组件重新激活时
  • 典型应用:
javascript 复制代码
activated() {
  this.fetchData()  // 重新获取数据
  this.startTimer() // 恢复定时器
}
  1. deactivated
  • 触发时机:组件被缓存时
  • 典型应用:
javascript 复制代码
deactivated() {
  clearInterval(this.timer)  // 清除定时器
  this.cancelRequest()      // 取消未完成请求
}

34. 框架选型(Vue/React/AngularJS)

专业对比分析

维度 Vue React AngularJS
学习曲线 平缓 中等 陡峭
数据绑定 双向 单向 双向
模板语法 HTML-based JSX 指令系统
状态管理 Vuex/Pinia Redux/MobX Service
适用场景 快速开发 大型应用 企业级应用

选择Vue的核心理由

  • 渐进式框架,灵活易上手
  • 单文件组件开发体验优秀
  • 中文文档和社区资源丰富
  • 性能优化良好,体积小巧

35. Vue2.0的IE兼容性

专业兼容方案

  1. 支持范围
  • 官方支持:IE9+
  • 实际测试:IE10+更稳定
  1. 必备polyfill
javascript 复制代码
// babel.config.js
module.exports = {
  presets: [
    ['@vue/cli-plugin-babel/preset', {
      useBuiltIns: 'entry',
      corejs: 3
    }]
  ]
}
  1. IE专属问题解决
  • 避免使用ES6+语法
  • CSS变量添加兼容前缀
  • 第三方库检查IE支持性

36. Todo应用开发思路

专业实现方案

  1. 架构设计
graph TD A[TodoApp] --> B[TodoList] A --> C[TodoForm] B --> D[TodoItem]
  1. 状态管理
javascript 复制代码
// store/todos.js
state: () => ({
  todos: [
    {
      id: 1,
      text: 'Learn Vue',
      completed: false,
      createdAt: '2023-01-01'
    }
  ],
  filter: 'all' // all|active|completed
})
  1. 核心功能实现
javascript 复制代码
// 添加待办
addTodo(text) {
  this.todos.push({
    id: Date.now(),
    text,
    completed: false
  })
}

// 过滤显示
filteredTodos() {
  switch(this.filter) {
    case 'active': return this.todos.filter(t => !t.completed)
    case 'completed': return this.todos.filter(t => t.completed)
    default: return this.todos
  }
}

37. Vue风格指南

关键规范要点

  1. 组件命名
  • 始终使用多单词(避免与HTML元素冲突)
  • 基础组件加Base前缀
  1. Prop定义
javascript 复制代码
props: {
  status: {
    type: String,
    required: true,
    validator: v => ['success', 'pending'].includes(v)
  }
}
  1. 模板规范
  • 为v-for设置key
  • 避免v-if和v-for一起使用
  • 组件样式使用scoped
  1. 代码组织
  • 单文件组件顺序:

    html 复制代码
    <template>...</template>
    <script>...</script>
    <style scoped>...</style>

38. Vue版本演进

1.x vs 2.x专业对比

特性 Vue 1.x Vue 2.x
架构 无虚拟DOM 引入虚拟DOM
性能 较慢 快2-4倍
响应式 基于getter/setter 使用defineProperty
指令系统 复杂 简化
组件通信 <math xmlns="http://www.w3.org/1998/Math/MathML"> d i s p a t c h / dispatch/ </math>dispatch/broadcast 事件总线
生命周期 较多钩子 精简优化

39. key的作用原理

专业深度解析

  1. diff算法核心
  • 无key:就地复用节点,可能产生错误状态
  • 有key:精准识别节点,正确更新DOM
  1. 虚拟DOM对比过程
graph LR A[新旧节点对比] --> B{key匹配?} B -->|是| C[复用节点] B -->|否| D[创建新节点]
  1. 生产环境实践
html 复制代码
<!-- 正确用法 -->
<div v-for="item in items" :key="item.id">

<!-- 错误用法 -->
<div v-for="(item, index) in items" :key="index">

40. 重置data的方法

专业实现方案

  1. 基础重置
javascript 复制代码
Object.assign(this.$data, this.$options.data.call(this))
  1. 深度重置
javascript 复制代码
function deepReset() {
  const initial = this.$options.data()
  Object.keys(initial).forEach(key => {
    if (Array.isArray(initial[key])) {
      this[key] = []
    } else if (typeof initial[key] === 'object') {
      this[key] = JSON.parse(JSON.stringify(initial[key]))
    } else {
      this[key] = initial[key]
    }
  })
}
  1. 注意事项
  • 不会触发响应式更新
  • 引用类型需要特殊处理
  • 避免直接修改$data引用

41. 保留HTML注释

专业配置方法

  1. 全局配置
javascript 复制代码
new Vue({
  comments: true,  // 默认false
  template: `
    <!-- 开发调试注释 -->
    <div>主要内容</div>
  `
})
  1. 编译后结果
html 复制代码
<!-- 开发调试注释 -->
<div>主要内容</div>
  1. 生产环境建议
  • 通过构建工具移除注释
  • 减少代码体积

42. Vue.observable

专业应用场景

  1. 小型状态管理
javascript 复制代码
// store.js
export const state = Vue.observable({
  count: 0,
  increment() {
    this.count++
  }
})
  1. 响应式原理
javascript 复制代码
// 近似实现
function observable(obj) {
  Object.keys(obj).forEach(key => {
    defineReactive(obj, key)
  })
  return obj
}
  1. 与Vuex对比 | 特性 | Vue.observable | Vuex | |------------|---------------------|--------------------| | 复杂度 | 简单 | 复杂 | | 功能 | 基础响应式 | 完整状态管理 | | 调试 | 无工具支持 | Devtools集成 |

43. scoped样式原理

专业实现机制

  1. 编译转换过程
html 复制代码
<!-- 源码 -->
<style scoped>
.button { color: red; }
</style>

<!-- 编译后 -->
<style>
.button[data-v-f3f3eg9] { color: red; }
</style>
  1. 深度作用选择器
css 复制代码
/* Vue2语法 */
/deep/ .child-component { ... }

/* Vue3语法 */
::v-deep(.child-component) { ... }
  1. 性能影响
  • 增加CSS特异性
  • 轻微影响选择器匹配速度
  • 组件样式隔离性好

44. Vue3.0改进期待

已实现重要特性

  1. Composition API
javascript 复制代码
setup() {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  return { count, double }
}
  1. 性能优化
  • 打包体积减少41%
  • 渲染速度提升55%
  • 内存占用减少54%
  1. TypeScript支持
  • 更好的类型推断
  • 更完善的类型定义

45. Vue边界情况

专业处理方案

  1. 访问根实例
javascript 复制代码
this.$root
  1. 强制更新
javascript 复制代码
this.$forceUpdate()
  1. 递归组件
javascript 复制代码
name: 'TreeNode',
components: { TreeNode }
  1. 循环引用
javascript 复制代码
components: {
  ComponentA: () => import('./ComponentA')
}
  1. 程序化事件
javascript 复制代码
this.$on('custom-event', handler)
this.$once('custom-event', handler)
this.$off('custom-event', handler)

46. 如何在子组件中访问父组件的实例?

专业解决方案

  1. 使用$parent(不推荐)
javascript 复制代码
// 子组件中
this.$parent.parentMethod()
  1. Props传递父组件引用(推荐)
html 复制代码
<!-- 父组件 -->
<child :parent="this"></child>

<!-- 子组件 -->
<script>
export default {
  props: ['parent'],
  methods: {
    callParent() {
      this.parent.parentMethod()
    }
  }
}
</script>
  1. Provide/Inject(推荐用于深层嵌套)
javascript 复制代码
// 父组件
export default {
  provide() {
    return {
      parent: this
    }
  }
}

// 子组件
export default {
  inject: ['parent'],
  methods: {
    callParent() {
      this.parent.parentMethod()
    }
  }
}

47. watch的属性用箭头函数定义结果会怎么样?

专业分析

  1. 问题现象
javascript 复制代码
watch: {
  value: () => {  // 错误用法
    console.log(this) // undefined
  }
}
  1. 根本原因
  • 箭头函数绑定父级上下文
  • Vue无法将组件实例绑定到this
  1. 正确写法
javascript 复制代码
watch: {
  value: function(newVal, oldVal) {  // 标准函数
    console.log(this) // 组件实例
  }
  // 或
  value(newVal, oldVal) {  // 方法简写
    console.log(this)
  }
}

48. methods的方法用箭头函数定义结果会怎么样?

专业分析

  1. 问题代码
javascript 复制代码
methods: {
  handleClick: () => {  // 错误用法
    console.log(this) // 不是组件实例
  }
}
  1. 影响范围
  • 无法访问this上的数据/方法
  • 事件绑定失效
  • 生命周期钩子调用异常
  1. 解决方案
javascript 复制代码
methods: {
  // 标准函数定义
  handleClick() {
    console.log(this) // 组件实例
  }
}

49. 在vue项目中如何配置favicon?

专业配置方案

  1. 基础配置
html 复制代码
<!-- public/index.html -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
  1. 动态favicon
javascript 复制代码
// 组件中修改
const changeFavicon = (iconUrl) => {
  const link = document.querySelector("link[rel*='icon']") || 
               document.createElement('link')
  link.type = 'image/x-icon'
  link.rel = 'shortcut icon'
  link.href = iconUrl
  document.head.appendChild(link)
}
  1. 多尺寸favicon
html 复制代码
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">

50. babel-polyfill的作用

专业解析

  1. 核心功能
  • 提供ES6+环境的polyfill
  • 实现Promise、Set、Map等新特性
  • 支持Array.includes等新方法
  1. 现代替代方案
javascript 复制代码
// babel.config.js
module.exports = {
  presets: [
    ['@vue/cli-plugin-babel/preset', {
      useBuiltIns: 'usage',  // 按需引入
      corejs: 3             // 指定core-js版本
    }]
  ]
}
  1. 兼容性处理范围
  • IE9+兼容
  • 旧版浏览器API补全
  • 异步语法转换

51. Vue的错误处理机制

专业解决方案

  1. 全局错误处理
javascript 复制代码
Vue.config.errorHandler = (err, vm, info) => {
  console.error(`Error: ${err.toString()}\nInfo: ${info}`)
  // 上报错误到监控系统
}
  1. 组件级错误捕获
javascript 复制代码
errorCaptured(err, vm, info) {
  // 阻止错误继续向上传播
  return false
}
  1. 异步错误处理
javascript 复制代码
window.onerror = function(message, source, lineno, colno, error) {
  // 处理全局未捕获异常
}

52. e.target vs e.currentTarget

专业区别

特性 e.target e.currentTarget
触发元素 实际触发事件的元素 绑定事件的元素
事件委托 指向子元素 始终指向绑定元素
典型应用场景 识别具体点击的子项 保证始终访问事件绑定元素

示例

html 复制代码
<div @click="handleClick">
  <button>按钮</button>
</div>

<script>
methods: {
  handleClick(e) {
    console.log(e.target)      // <button>
    console.log(e.currentTarget) // <div>
  }
}
</script>

53. .vue文件中style和script的必要性

专业分析

  1. style标签
  • 非必须,可以外部引入CSS
  • 推荐使用scoped避免样式污染
  • 支持多种预处理器(Sass/Less)
  1. script标签
  • 非必须,纯展示组件可不写
  • 但实际开发中95%组件需要script
  • 用于处理逻辑、数据、方法等

最小组件示例

html 复制代码
<template>
  <div>Hello World</div>
</template>
<!-- 无script和style -->

54. 强制刷新组件方案

专业方法

  1. 暴力刷新
javascript 复制代码
this.$forceUpdate()
  1. key-changing(推荐):
html 复制代码
<component :key="componentKey" />

<script>
methods: {
  forceRerender() {
    this.componentKey += 1; // 修改key值触发重新渲染
  }
}
</script>
  1. v-if控制
html 复制代码
<component v-if="show" />
<script>
methods: {
  reload() {
    this.show = false
    this.$nextTick(() => this.show = true)
  }
}
</script>

55. 父组件接收子组件多个参数

专业方案

  1. 对象形式传递
javascript 复制代码
// 子组件
this.$emit('submit', { name, age, email })

// 父组件
<child @submit="handleSubmit" />

methods: {
  handleSubmit({ name, age, email }) {
    // 解构参数
  }
}
  1. 参数列表形式
javascript 复制代码
// 子组件
this.$emit('submit', name, age, email)

// 父组件
<child @submit="handleSubmit(arguments)" />

methods: {
  handleSubmit(args) {
    const [name, age, email] = args
  }
}

56. Vue最佳实践总结

专业建议

  1. 组件设计
  • 单一职责原则
  • 合理划分容器组件和展示组件
  • 使用name属性便于调试
  1. 状态管理
  • 避免直接修改props
  • 复杂状态使用Pinia/Vuex
  • 遵循单向数据流
  1. 性能优化
  • 合理使用v-if和v-show
  • 列表渲染务必设置key
  • 组件懒加载
  1. 代码风格
  • 组件名使用PascalCase
  • 方法名使用camelCase
  • 自定义事件使用kebab-case

57. 自定义事件无效解决方案

专业排查

  1. 事件名大小写
javascript 复制代码
// 子组件
this.$emit('myEvent')  // 错误
this.$emit('my-event') // 正确
  1. 原生事件监听
html 复制代码
<child @click.native="handleClick" />
  1. 事件验证
javascript 复制代码
emits: {
  // 验证自定义事件
  'my-event': (payload) => {
    return typeof payload === 'string'
  }
}

58. 属性与方法同名问题

专业分析

  1. 冲突现象
javascript 复制代码
data() {
  return { count: 0 }
},
methods: {
  count() { return 1 }  // 会覆盖data中的count
}
  1. 底层原理
  • methods会覆盖data中的同名属性
  • Vue会在控制台输出警告
  1. 解决方案
  • 严格遵循命名规范
  • data使用名词,methods使用动词

59. _/$开头变量处理

专业解析

  1. 特殊处理规则
  • Vue不会代理_$开头的属性
  • 避免与Vue内部API冲突
  1. 访问方式
javascript 复制代码
data() {
  return { _private: 'secret' }
},
methods: {
  getPrivate() {
    return this.$data._private  // 必须通过$data访问
  }
}

60. v-for遍历对象顺序

专业解答

  1. 默认顺序
  • 遵循Object.keys()顺序
  • 不保证插入顺序(尤其在不同JavaScript引擎中)
  1. 保证顺序的方法
javascript 复制代码
computed: {
  sortedItems() {
    return Object.entries(this.obj)
      .sort((a, b) => a[0].localeCompare(b[0]))
      .reduce((acc, [key, val]) => {
        acc[key] = val
        return acc
      }, {})
  }
}

61. 组件扩展方案

专业方法

  1. 组合式继承
javascript 复制代码
// base-component.js
export default {
  data() {
    return { baseData: 'value' }
  },
  methods: {
    baseMethod() { /*...*/ }
  }
}

// extended-component.vue
import BaseComponent from './base-component'
export default {
  extends: BaseComponent,
  // 添加新功能
}
  1. 高阶组件
javascript 复制代码
function withLogging(WrappedComponent) {
  return {
    mounted() {
      console.log('Component mounted')
    },
    render(h) {
      return h(WrappedComponent, {
        props: this.$props
      })
    }
  }
}
  1. 插槽扩展
html 复制代码
<!-- 基础组件 -->
<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

<!-- 扩展使用 -->
<base-component>
  <template #header>自定义头部</template>
  自定义内容
</base-component>

62. <math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s 和 attrs和 </math>attrs和listeners使用场景

专业解析

  1. $attrs
  • 包含父作用域中不作为props识别的attribute
  • 典型场景:高阶组件封装
html 复制代码
<base-input v-bind="$attrs"></base-input>
  1. $listeners
  • 包含父作用域中的v-on事件监听器
  • 典型场景:事件透传
html 复制代码
<base-button v-on="$listeners"></base-button>
  1. 组合使用
html 复制代码
<advanced-component 
  v-bind="$attrs"
  v-on="$listeners"
></advanced-component>

63. 部署后404问题分析

专业排查方案

  1. 路由模式问题
javascript 复制代码
// history模式需要服务器配置
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})
  1. Nginx配置示例
nginx 复制代码
location / {
  try_files $uri $uri/ /index.html;
}
  1. 静态资源路径问题
javascript 复制代码
// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/project-name/'
    : '/'
}

64. v-once使用场景

专业应用场景

  1. 静态内容优化
html 复制代码
<div v-once>{{ staticContent }}</div>
  1. 性能敏感区域
html 复制代码
<template v-for="item in list">
  <div v-once :key="item.id">{{ item.name }}</div>
</template>
  1. 避免不必要的更新
html 复制代码
<footer v-once>© 2023 Company</footer>

65. .lazy修饰符原理

专业解析

  1. 默认行为对比
html 复制代码
<!-- 默认行为 -->
<input v-model="text"> <!-- 实时更新 -->

<!-- lazy修饰符 -->
<input v-model.lazy="text"> <!-- change事件触发更新 -->
  1. 底层实现
javascript 复制代码
// 编译后代码
h('input', {
  on: {
    change: $event => (text = $event.target.value)
  }
})

66. 单根元素限制原因

专业解释

  1. 虚拟DOM差异算法要求
  • 需要单一根节点进行对比
  • Vue3 Fragment支持多根节点
  1. 代码示例对比
html 复制代码
<!-- Vue2 错误 -->
<template>
  <div>A</div>
  <div>B</div>
</template>

<!-- Vue3 支持 -->
<template>
  <div>A</div>
  <div>B</div>
</template>

67. EventBus重复触发解决方案

专业方案

  1. 手动注销事件
javascript 复制代码
// 组件内
beforeDestroy() {
  EventBus.$off('eventName', this.eventHandler)
}
  1. 使用once监听
javascript 复制代码
EventBus.$once('eventName', callback)
  1. 路由守卫处理
javascript 复制代码
router.beforeEach((to, from, next) => {
  EventBus.$off()
  next()
})

68. 修改打包文件路径

专业配置

  1. 基础配置
javascript 复制代码
// vue.config.js
module.exports = {
  outputDir: 'dist',
  assetsDir: 'static',
  filenameHashing: true
}
  1. CDN路径
javascript 复制代码
module.exports = {
  publicPath: 'https://cdn.example.com/project/'
}

69. Vue与原生App交互

专业方案

  1. JS Bridge通信
javascript 复制代码
// Vue调用原生
window.WebViewJavascriptBridge.callHandler('nativeMethod', data)

// 监听原生调用
window.WebViewJavascriptBridge.registerHandler('jsMethod', callback)
  1. URL Scheme
javascript 复制代码
location.href = 'myapp://method?param=value'
  1. PostMessage
javascript 复制代码
window.postMessage(JSON.stringify(message), '*')

70. Tab切换实现

专业代码

html 复制代码
<template>
  <div class="tabs">
    <div 
      v-for="(tab, index) in tabs"
      :key="index"
      @click="currentTab = index"
      :class="{ active: currentTab === index }"
    >
      {{ tab.title }}
    </div>
  </div>
  <component :is="tabs[currentTab].component" />
</template>

<script>
export default {
  data() {
    return {
      currentTab: 0,
      tabs: [
        { title: 'Tab1', component: 'Tab1Content' },
        { title: 'Tab2', component: 'Tab2Content' }
      ]
    }
  }
}
</script>

71. 递归组件

专业示例

vue 复制代码
<template>
  <div>
    {{ node.name }}
    <tree-node 
      v-for="child in node.children"
      :key="child.id"
      :node="child"
    />
  </div>
</template>

<script>
export default {
  name: 'TreeNode',
  props: ['node']
}
</script>

72. 访问子组件实例

专业方案

  1. ref方式
html 复制代码
<child-component ref="child"></child-component>

<script>
this.$refs.child.method()
</script>
  1. $children(不推荐):
javascript 复制代码
this.$children[0].method()

73. 访问父组件实例

专业方案

  1. $parent(不推荐):
javascript 复制代码
this.$parent.parentMethod()
  1. Provide/Inject(推荐):
javascript 复制代码
// 父组件
provide() {
  return {
    parent: this
  }
}

// 子组件
inject: ['parent']

74. 访问根实例

专业方案

javascript 复制代码
this.$root.rootMethod()

75. Object.defineProperty理解

专业解析

  1. Vue2响应式核心
javascript 复制代码
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get() {
    // 依赖收集
    track()
    return value
  },
  set(newVal) {
    // 触发更新
    trigger()
    value = newVal
  }
})
  1. 局限性
  • 无法检测数组索引变化
  • 无法检测对象属性添加/删除

76. 原生事件销毁

专业建议

  1. 必须手动销毁
javascript 复制代码
mounted() {
  window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
  window.removeEventListener('resize', this.handleResize)
}
  1. 原因
  • Vue无法管理原生事件监听
  • 可能导致内存泄漏

77. 定时器销毁

专业方案

javascript 复制代码
data() {
  return {
    timer: null
  }
},
mounted() {
  this.timer = setInterval(() => {
    // do something
  }, 1000)
},
beforeDestroy() {
  clearInterval(this.timer)
}

78. 组件销毁时机

专业总结

  1. 路由切换
  2. v-if条件为false
  3. 父组件销毁
  4. 调用$destroy()方法
  5. keep-alive排除

79. 大数据渲染优化

专业方案

  1. 虚拟滚动
bash 复制代码
npm install vue-virtual-scroller
  1. 分页加载
javascript 复制代码
// 只渲染可视区域数据
displayData() {
  return this.bigData.slice(
    (this.currentPage - 1) * this.pageSize,
    this.currentPage * this.pageSize
  )
}
  1. 冻结数据
javascript 复制代码
this.bigData = Object.freeze(bigData)

80. this使用注意事项

专业建议

  1. 避免箭头函数
javascript 复制代码
// 错误
methods: {
  handleClick: () => {
    console.log(this) // undefined
  }
}

// 正确
methods: {
  handleClick() {
    console.log(this) // 组件实例
  }
}
  1. 异步回调
javascript 复制代码
// 保存this引用
const vm = this
setTimeout(function() {
  vm.data = 'value'
}, 100)

81. JSX使用理解

专业解析

  1. 配置支持
bash 复制代码
npm install @vue/babel-preset-jsx
  1. 示例对比
javascript 复制代码
// 模板语法
<template>
  <div>{{ message }}</div>
</template>

// JSX语法
render() {
  return <div>{this.message}</div>
}
  1. 优势场景
  • 动态生成复杂DOM结构
  • 更好的TypeScript支持

82. 组件命名规范

专业建议

  1. 文件命名
  • MyComponent.vue (PascalCase)
  • my-component.vue (kebab-case)
  1. 组件注册
javascript 复制代码
components: {
  MyComponent, // 推荐
  'my-component': MyComponent // 兼容
}
  1. 目录结构
arduino 复制代码
components/
|- BaseButton.vue    // 基础组件
|- AppHeader.vue     // 业务组件
|- TheNavbar.vue     // 唯一组件

83. Vue2+TypeScript配置

专业配置

  1. 安装依赖
bash 复制代码
npm install @vue/cli-plugin-typescript --save-dev
  1. tsconfig.json
json 复制代码
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "moduleResolution": "node"
  }
}
  1. shims-vue.d.ts
typescript 复制代码
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

84. <template>标签的作用

专业解析

  1. 模板容器功能
  • 包裹多个元素而不渲染额外DOM节点
  • 支持v-if/v-for等指令分组
html 复制代码
<template v-if="show">
  <div>A</div>
  <div>B</div>
</template>
  1. 特殊场景应用
  • Vue2中解决单根元素限制
  • Vue3支持多根节点后仍可用于逻辑分组
  1. 与普通div的区别 : | 特性 | <template> | <div> | |---------------|---------------------------|---------------------------| | DOM渲染 | 不渲染 | 渲染为实际节点 | | 支持指令 | 是 | 是 | | 样式作用 | 无 | 有 |

85. is特性的应用场景

专业应用

  1. 动态组件
html 复制代码
<component :is="currentComponent"></component>
  1. 解决DOM模板限制
html 复制代码
<table>
  <tr is="vue-row"></tr>
</table>
  1. HTML元素限制规避
html 复制代码
<ul>
  <li is="vue-item"></li>
</ul>
  1. Vue3变化
  • 仍支持但推荐使用v-is指令
html 复制代码
<table>
  <tr v-is="'vue-row'"></tr>
</table>

86. :class:style表示方式

专业分类

  1. :class的三种写法
html 复制代码
<!-- 对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>

<!-- 数组语法 -->
<div :class="[activeClass, errorClass]"></div>

<!-- 三目运算 -->
<div :class="isActive ? 'active' : 'inactive'"></div>
  1. :style的两种写法
html 复制代码
<!-- 对象语法 -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

<!-- 数组语法 -->
<div :style="[baseStyles, overridingStyles]"></div>

87. 函数式组件

专业理解

  1. 核心特征
  • 无状态(无data)
  • 无实例(无this上下文)
  • 更高性能
  1. 实现方式
javascript 复制代码
Vue.component('functional-button', {
  functional: true,
  props: ['text'],
  render(h, context) {
    return h('button', context.data, context.props.text)
  }
})
  1. 适用场景
  • 纯展示型组件
  • 高阶组件(HOC)
  • 性能敏感区域

88. 修改模板分隔符

专业配置

  1. 全局修改
javascript 复制代码
new Vue({
  delimiters: ['${', '}'] // 替换默认的{{ }}
})
  1. 组件级修改
javascript 复制代码
export default {
  delimiters: ['#[', ']'],
  template: `<div>#[message]</div>`
}
  1. 注意事项
  • 避免与Markdown等语法冲突
  • 团队统一规范

89. name选项的作用

专业用途

  1. 递归组件
javascript 复制代码
name: 'TreeNode',
components: { TreeNode }
  1. 调试工具显示
  • Vue DevTools中显示组件名
  • 错误堆栈追踪更清晰
  1. keep-alive缓存
html 复制代码
<keep-alive include="ComponentA">
  <component :is="currentComponent"></component>
</keep-alive>
  1. 动态组件匹配
javascript 复制代码
components: {
  [name]: componentDefinition
}

90. provide/inject机制

专业理解

  1. 跨层级通信
javascript 复制代码
// 祖先组件
provide() {
  return {
    theme: this.theme
  }
}

// 后代组件
inject: ['theme']
  1. 响应式处理
javascript 复制代码
provide() {
  return {
    getTheme: () => this.theme // 函数式保持响应
  }
}
  1. 与props对比: | 特性 | provide/inject | props | |------------|---------------------|----------------------| | 数据流向 | 任意层级向下 | 父子组件之间 | | 响应性 | 需特殊处理 | 自动响应 | | 适用场景 | 组件库/高阶组件 | 常规组件通信 |

91. Vue DevTools使用

专业技巧

  1. 核心功能
  • 组件树查看
  • 状态调试
  • 事件追踪
  • 性能分析
  1. 高级用法
javascript 复制代码
// 自定义检查器
app.config.devtools = true

// 时间旅行调试
import { createStore } from 'vuex'
  1. 生产环境禁用
javascript 复制代码
Vue.config.devtools = process.env.NODE_ENV !== 'production'

92. Slot机制深度解析

专业应用

  1. 基本类型
html 复制代码
<!-- 默认slot -->
<slot></slot>

<!-- 具名slot -->
<slot name="header"></slot>

<!-- 作用域slot -->
<slot :user="user"></slot>
  1. 高级用法
html 复制代码
<!-- 动态slot名 -->
<template v-slot:[dynamicSlotName]>

<!-- 缩写语法 -->
<template #header>
  1. 设计模式应用
  • 布局组件(Layout)
  • 复合组件(Compound)
  • 渲染委托(Render Delegation)

93. 动态组件实践

专业方案

  1. 基础用法
html 复制代码
<component :is="currentTabComponent"></component>
  1. 状态保持
html 复制代码
<keep-alive>
  <component :is="currentComponent"></component>
</keep-alive>
  1. 高级场景
javascript 复制代码
// 异步组件
const AsyncComponent = () => ({
  component: import('./Component.vue'),
  loading: LoadingComponent,
  error: ErrorComponent
})

94. prop验证类型

完整类型列表

  1. 原生构造函数
  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  1. 自定义构造函数
javascript 复制代码
propA: {
  type: Person, // 自定义类
  validator: value => value instanceof Person
}
  1. 多类型支持
javascript 复制代码
propB: [String, Number]

95. prop验证与默认值

专业配置

javascript 复制代码
props: {
  // 完整验证
  propA: {
    type: String,
    required: true,
    default: 'default value',
    validator: value => value.length > 0
  },
  
  // 对象/数组默认值需工厂函数
  propB: {
    type: Object,
    default: () => ({})
  }
}

96. 路由组件缓存与更新

专业方案

  1. 基础缓存
html 复制代码
<keep-alive>
  <router-view></router-view>
</keep-alive>
  1. 条件缓存
html 复制代码
<keep-alive :include="['Home', 'User']">
  <router-view></router-view>
</keep-alive>
  1. 缓存更新策略
javascript 复制代码
// 方案1:监听路由变化
watch: {
  '$route'() {
    this.refreshData()
  }
},

// 方案2:使用activated钩子
activated() {
  this.loadData()
}

97. 组件设计原则

专业准则

  1. 单一职责原则
  • 每个组件只做一件事
  • 控制组件代码行数(建议<500行)
  1. 开放封闭原则
  • 对扩展开放(通过props/slots)
  • 对修改封闭(内部状态私有)
  1. DRY原则
  • 提取可复用逻辑(mixins/composables)
  • 避免重复代码
  1. 无副作用原则
  • 纯函数式渲染
  • 避免直接操作DOM

98. 组件通信设计

专业模式

场景 推荐方案 示例
父子通信 props/events <child @update="handle">
跨层级 provide/inject provide('key', value)
全局状态 Pinia/Vuex store.user
组件实例访问 ref/template refs this.$refs.child
事件广播 Mitt/EventBus emitter.emit('event')

99. 性能优化策略

专业建议

  1. 代码层面
  • v-if和v-for避免共用
  • 合理使用v-once
  • 组件懒加载
  1. 架构层面
  • 路由懒加载
  • 异步组件
  • 服务端渲染
  1. 工具层面
  • 使用生产环境构建
  • 开启Gzip压缩
  • 配置CDN加速

100. TypeScript集成

专业配置

  1. 完整配置链
bash 复制代码
npm install @vue/cli-plugin-typescript vue-tsc --save-dev
  1. tsconfig.json关键项
json 复制代码
{
  "compilerOptions": {
    "strict": true,
    "jsx": "preserve",
    "types": ["webpack-env"]
  }
}
  1. 组件写法
typescript 复制代码
import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    message: {
      type: String as PropType<string>,
      required: true
    }
  },
  setup(props) {
    const count = ref(0)
    return { count }
  }
})

相关推荐
10年前端老司机5 分钟前
React 受控组件和非受控组件区别和使用场景
前端·javascript·react.js
夏晚星6 分钟前
vue实现微信聊天emoji表情
前端·javascript
停止重构8 分钟前
【方案】前端UI布局的绝技,响应式布局,多端适配
前端·网页布局·响应式布局·grid布局·网页适配多端
極光未晚8 分钟前
TypeScript在前端项目中的那些事儿:不止于类型的守护者
前端·javascript·typescript
ze_juejin9 分钟前
Vue3 + Vite + Ant Design Vue + Axios + Pinia 脚手架搭建
前端·vue.js
lichenyang45311 分钟前
React项目(移动app)
前端
用户618482402195112 分钟前
Vue-library-start,一个基于Vite的vue组件库开发模板
前端
美团技术团队24 分钟前
报名 | 美团技术沙龙第86期:多业务场景下,美团如何做性能优化
前端
Rrvive1 小时前
localhost 和 127.0.0.1 的核心区别
前端
蓝倾1 小时前
如何使用Python通过API接口批量抓取小红书笔记评论?
前端·后端·api