1. 什么是双向绑定? Vue 双向绑定的原理是什么?
双向绑定是一种数据绑定机制,指的是视图和数据之间可以相互同步。即,当模型数据(Model)发生变化时,视图(View)会自动更新;反之,视图变化时,模型数据也会更新。
Vue 双向绑定的原理依赖于 Object.defineProperty
(Vue2)或 Proxy
(Vue3)的响应式原理:
- Vue2:通过
Object.defineProperty
监听对象属性的 getter 和 setter,实现数据劫持,并结合模板编译更新视图。 - Vue3:通过
Proxy
创建响应式代理,监听整个对象的操作(如属性读取、设置、新增、删除),从而更灵活高效地更新视图。
2. 如何使用 Vue 开发多语言项目?
使用 Vue 开发多语言项目时,可以借助 vue-i18n 插件。主要步骤如下:
- 安装依赖:
npm install vue-i18n
- 配置多语言文件,例如
en.json
和zh.json
。 - 初始化
i18n
实例并挂载:import { createApp } from 'vue'; import { createI18n } from 'vue-i18n'; import App from './App.vue';const messages = {en: { hello: 'Hello' },zh: { hello: '你好' }, };const i18n = createI18n({locale: 'en', // 默认语言messages, });const app = createApp(App); app.use(i18n); app.mount('#app');
- 在模板中使用
$t
来实现多语言:<template><p>{{ $t('hello') }}</p> </template>
3. Vue 中什么是递归组件? 请举例说明
递归组件是指组件自身调用自身,常用于处理嵌套结构(如树形菜单、文件夹结构)。
示例:文件夹结构
<template><ul><li v-for="item in items" :key="item.id">{{ item.name }}<folder v-if="item.children" :items="item.children" /></li></ul>
</template><script>
export default {name: 'Folder',props: ['items'],
};
</script>
4. Vue 的 data 中如果有数组,如何检测数组的变化?
在 Vue2 中,数组的响应式存在一些限制。Vue2 修改数组时需要使用以下方法触发视图更新:
push()
,pop()
,shift()
,unshift()
,splice()
,sort()
,reverse()
等。
注意:直接通过索引修改数组或使用 length
属性时,不会自动触发更新:
this.arr[0] = 'newValue'; // 不会触发更新
this.arr.length = 2; // 不会触发更新
解决方法:
- 使用
Vue.set()
:Vue.set(this.arr, 0, 'newValue');
在 Vue3 中,通过 Proxy
的响应式机制,数组的索引和长度变化都能被正确检测。
5. Vue3 中的 Suspense 组件有什么作用? 如何使用它来处理异步组件?
Suspense 是 Vue3 提供的一个内置组件,用于处理异步组件加载,并显示加载状态或备用内容。
使用示例:
<template><Suspense><template #default><AsyncComponent /></template><template #fallback><p>Loading...</p></template></Suspense>
</template><script>
import { defineAsyncComponent } from 'vue';const AsyncComponent = defineAsyncComponent(() =>import('./AsyncComponent.vue')
);export default {components: { AsyncComponent },
};
</script>
作用:
- 显示异步组件加载时的备用内容(
fallback
插槽)。 - 等待异步组件加载完成后再渲染。
6. Vue 3 中的 Vue Composition API 是什么?
Composition API 是 Vue3 提供的一种新的代码组织方式,增强了逻辑复用和代码清晰度。核心方法包括 setup
、ref
、reactive
等。
示例:
<template><div>{{ count }}</div><button @click="increment">Increment</button>
</template><script>
import { ref } from 'vue';export default {setup() {const count = ref(0);const increment = () => {count.value++;};return { count, increment };},
};
</script>
7. Vue 中子组件可以直接修改父组件的数据吗?
不可以。子组件不能直接修改父组件的数据,应该通过 事件传递 或 props + emit 机制实现:
- 父组件通过
props
将数据传递给子组件。 - 子组件通过
$emit
触发事件通知父组件更新数据。
8. Vue 中 style 的 scoped 属性有什么用? 它的实现原理是什么?
scoped
属性用于使样式只作用于当前组件,避免污染全局样式。
实现原理:
- Vue 为 scoped 样式的元素添加一个唯一的属性(如
data-v-xxx
)。 - 编译时,将样式选择器加上该属性,确保样式作用域限定在组件内。
示例:
<template><div class="example">Hello</div>
</template><style scoped>
.example {color: red;
}
</style>
9. Vuex 状态管理存在什么缺点?
- 复杂性增加:项目较小时,引入 Vuex 会显得过于繁重。
- 调试困难:需要配合工具(如 Vue DevTools)调试。
- 模块间耦合性:模块较多时,可能导致数据和逻辑交叉。
10. 你在 Vue 项目中如何发送请求? ajax、fetch、axios 之间有什么区别?
发送请求:常用 axios
,因为其功能强大、支持拦截器、兼容性好。
- ajax:基于
XMLHttpRequest
,过于底层,不推荐。 - fetch:浏览器自带 API,支持 Promise,但不支持拦截器,需手动处理错误和超时。
- axios:基于
Promise
,封装了XMLHttpRequest
,提供了更好的功能和更简洁的 API。
11. Vue 中怎么改变插入模板的分隔符?
通过 delimiters
配置:
const app = new Vue({el: '#app',delimiters: ['${', '}'],
});
12. Vue 中有哪些边界情况需要注意?
- 组件销毁时未清除定时器或事件监听器。
- 动态组件切换导致状态丢失。
- 大量 DOM 操作导致性能问题。
- 深层嵌套的响应式数据可能导致性能问题。
13. 虚拟 DOM 和 DIFF 算法的关系,其中 key 的作用是什么?
虚拟 DOM 是一种用 JavaScript 对 DOM 结构进行抽象的表示,便于高效更新视图。
DIFF 算法 用于比较新旧虚拟 DOM 树,计算最小更新操作。
key
的作用:
- 唯一标识节点,帮助 DIFF 算法精准复用和更新 DOM 节点。
- 避免不必要的 DOM 重建。
14. 什么是事件总线 EventBus? 怎么在 Vue 项目中使用它?
事件总线是一个空的 Vue 实例,用于组件之间的事件通信。
使用方法:
- 创建 EventBus:
const EventBus = new Vue(); export default EventBus;
- 组件中使用:
// 子组件 A 触发事件 EventBus.$emit('eventName', data);// 子组件 B 监听事件 EventBus.$on('eventName', (data) => {console.log(data); });
1. Vue 项目部署到服务器后,报 404 错误的原因是什么?
主要原因是 Vue Router 的 history 模式在部署时没有正确配置服务端。
在 history 模式下,URL 直接指向某个路由路径,而不是实际的文件路径,因此刷新时,服务器会尝试查找对应路径的文件,找不到时返回 404。
解决方法:
- 确保服务端将所有路由都指向
index.html
:- Apache:
<IfModule mod_rewrite.c>RewriteEngine OnRewriteBase /RewriteRule ^index\.html$ - [L]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule . /index.html [L] </IfModule>
- Nginx:
location / {try_files $uri $uri/ /index.html; }
- Apache:
2. 什么是 Vuex? 为什么需要它? 它有哪些优点和适用场景?
Vuex 是 Vue 的状态管理库,用于集中式管理应用中的共享状态。
为什么需要 Vuex:
- 解决多组件共享状态的问题。
- 提供单向数据流,便于调试和维护。
优点:
- 状态集中管理,便于追踪和调试。
- 提供模块化管理,适合大型项目。
- 支持 Vue DevTools,调试方便。
适用场景:
- 大型复杂项目,组件间状态共享频繁。
- 需要对状态进行集中管理和严格约束的应用。
3. 如何在组件中重复使用 Vuex 的 mutation?
可以通过 mapMutations
辅助函数绑定 Vuex 的 mutations:
import { mapMutations } from 'vuex';export default {methods: {...mapMutations(['mutationA', 'mutationB']),customMethod() {this.mutationA('payload');},},
};
4. 如何解决 SPA 单页应用首屏加载速度慢的问题?
-
代码分割:
使用dynamic import
和 Webpack 的lazy-loading
,按需加载组件:const AsyncComponent = () => import('./components/Component.vue');
-
服务端渲染(SSR):
使用 Vue SSR 提供服务端渲染,减少首屏加载时间。 -
骨架屏:
在加载真实内容前显示骨架屏以优化用户体验。 -
资源优化:
- 压缩 JS、CSS 和图片资源。
- 使用 CDN 加速静态资源加载。
- 开启 HTTP/2。
-
缓存:
使用浏览器缓存或 Service Worker 缓存静态资源。
5. Vue Router 的导航守卫有哪些? 它们接受哪些参数?
Vue Router 提供三种导航守卫:
-
全局守卫:
beforeEach
:在每次导航前触发。beforeResolve
:在所有组件内守卫和异步路由完成后触发。afterEach
:导航完成后触发。
-
路由独享守卫:
定义在路由配置中的beforeEnter
。 -
组件内守卫:
beforeRouteEnter
:进入路由前触发。beforeRouteUpdate
:当前路由改变时触发。beforeRouteLeave
:离开当前路由前触发。
参数:
to
:目标路由对象。from
:当前路由对象。next
:用于控制导航流程的回调函数。
6. 请求数据的逻辑应该写在 Vue 组件的 methods 中还是 Vuex 的 actions 中?
- 小型项目:请求逻辑可以写在组件的
methods
中。 - 大型项目:推荐将请求逻辑写在 Vuex 的
actions
中,便于管理和复用。
7. Vuex 和 localStorage 的区别是什么?
- Vuex:在内存中管理状态,适合频繁更新、跨组件共享的数据。
- localStorage:持久化存储,数据存储在浏览器本地,不会随页面刷新丢失。
使用场景:
- Vuex:实时更新数据,如用户会话信息。
- localStorage:持久存储,如用户设置、令牌。
结合使用:可以在 Vuex 的状态中使用 localStorage
持久化部分数据。
8. Vuex 的实现原理是什么?
Vuex 的核心原理是基于 Vue 的响应式机制和发布-订阅模式:
- 响应式:通过 Vue 的
reactive
或data
实现对状态的响应式绑定。 - 发布-订阅:
- 组件订阅 Store 的状态。
- Actions 或 Mutations 修改状态后,触发订阅的组件更新。
9. Element UI 组件库是怎么做表单验证的? 怎么在循环中对每个 input 进行验证?
验证原理:
- 通过
el-form
组件结合async-validator
库,定义验证规则。
示例:
<el-form :model="form" :rules="rules" ref="formRef"><el-form-item label="Name" prop="name"><el-input v-model="form.name" /></el-form-item>
</el-form><script>
export default {data() {return {form: { name: '' },rules: { name: [{ required: true, message: 'Name is required', trigger: 'blur' }] },};},
};
</script>
循环验证:
通过 :prop
动态绑定每个字段:
<el-form :model="form" :rules="rules" ref="formRef"><el-form-item v-for="(item, index) in form.items" :prop="'items.' + index + '.name'" :key="index"><el-input v-model="item.name" /></el-form-item>
</el-form>
10. 什么是 Vue 的高阶组件? 请举例说明
高阶组件(HOC)是复用组件逻辑的一种模式,本质是返回一个增强的组件。
示例:
function withLogger(WrappedComponent) {return {mounted() {console.log(`${WrappedComponent.name} mounted`);},render(h) {return h(WrappedComponent, { props: this.$props });},};
}// 使用 HOC
const EnhancedComponent = withLogger(MyComponent);
11. Vue 中如何实现强制刷新组件?
可以通过修改组件的 key
属性实现:
<template><MyComponent :key="key" /><button @click="refresh">Refresh</button>
</template><script>
export default {data() {return { key: 0 };},methods: {refresh() {this.key++;},},
};
</script>
12. Vue Router 的 history 模式为什么刷新时会出现 404 错误?
原因:
在 history 模式下,路由的 URL 不包含 #
,刷新时,服务器会将 URL 当作文件路径处理,而对应的文件可能并不存在,因此报 404。
解决方法:
在服务器端配置所有路径指向 index.html
(见问题 1)。
1. Vue、React 和 Angular 有什么区别? 各自的优缺点和使用场景是什么?
Vue.js
- 特点:轻量级、渐进式框架,关注视图层,易上手。
- 优点:
- 简单易学,文档完善,适合快速开发。
- 双向数据绑定,开发效率高。
- 生态丰富(Vue Router、Vuex)。
- 缺点:
- 社区主导,相较于 React 的大厂支持,稳定性稍弱。
- 灵活性高,可能导致项目规范不统一。
- 使用场景:
- 中小型项目,关注开发效率。
- 注重用户交互的前端项目。
React
- 特点:以组件为中心,功能强大,采用虚拟 DOM 和单向数据流。
- 优点:
- 灵活性高,适用于各种场景。
- 背靠 Facebook 社区,更新稳定。
- 丰富的第三方库和生态。
- 缺点:
- 需要搭配 Redux/MobX 等工具,学习成本高。
- 灵活性可能带来复杂的代码管理。
- 使用场景:
- 复杂的 Web 应用(如电商、社交平台)。
- 高性能要求的项目。
Angular
- 特点:全功能框架,提供完整的解决方案。
- 优点:
- 企业级应用支持强大,提供完整的开发工具链。
- 双向数据绑定,集成性强。
- 强大的 TypeScript 支持。
- 缺点:
- 学习曲线陡峭。
- 项目体积较大。
- 使用场景:
- 企业级应用,大型项目。
- 需要长期维护的复杂系统。
2. Element UI 是什么? 你如何在 Vue 项目中集成 Element UI?
Element UI 是基于 Vue 的 UI 组件库,提供了一系列高质量的组件(如表单、表格、对话框)以提高开发效率。
集成步骤:
- 安装依赖:
npm install element-ui
- 全局引入:
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);
- 按需引入(推荐):
安装babel-plugin-component
,优化打包大小:
修改 Babel 配置:npm install babel-plugin-component -D
在代码中按需导入:module.exports = {plugins: [['component',{libraryName: 'element-ui',styleLibraryName: 'theme-chalk',},],], };
import { Button, Select } from 'element-ui'; Vue.use(Button); Vue.use(Select);
3. 是否阅读过 Vue Router 的源码? 有哪些优秀的设计?
阅读过 Vue Router 的部分源码,以下是一些优秀设计:
- 动态路由匹配:
通过路径参数、正则匹配解析路由地址。 - 导航守卫机制:
利用 Promise 链式调用依次执行beforeEach
、beforeResolve
、组件内守卫等。 - 路由懒加载:
动态加载组件结合 Webpack 的import()
,优化性能。 - 抽象路由层:
支持多种路由模式(hash
、history
、abstract
),通过适配器模式实现统一接口。 - 路由缓存:
结合 Vue 的keep-alive
提高页面切换性能。
4. Vue 的基本实现原理是什么?
Vue 的核心原理是 数据驱动视图更新 和 响应式机制,主要包括以下部分:
- 响应式系统:
- Vue2 使用
Object.defineProperty
劫持对象属性,创建 getter 和 setter。 - Vue3 使用
Proxy
,拦截对象的所有操作。
- Vue2 使用
- 模板编译:
- 将模板字符串解析为抽象语法树(AST)。
- 转化为渲染函数(
render
)。
- 虚拟 DOM:
- 渲染函数生成虚拟 DOM。
- 比较新旧虚拟 DOM 的差异(Diff 算法),进行最小化更新。
- 组件化:
- 通过递归和组合,实现组件嵌套和复用。
5. 你是否阅读过 Vue 组件库(如 Element UI)的源码? 有哪些巧妙的设计?
Element UI 的设计亮点:
- 动态插槽机制:
利用 Vue 的slot
动态生成不同内容,提升灵活性。 - 全局组件注册:
自动注册所有组件,方便全局使用。 - 指令封装:
使用自定义指令(如v-loading
)实现功能复用。 - 样式隔离:
通过 BEM 规范和 SCSS 实现组件的样式模块化,避免冲突。 - 表单验证:
结合async-validator
库提供灵活的验证机制。
6. 怎么让 Vue 项目支持使用 TypeScript?
- 安装依赖:
vue add typescript
- 配置项目:
- 生成
tsconfig.json
。 - 将
.js
文件改为.ts
。 - 使用 Vue SFC 的
<script lang="ts">
。
- 生成
- 使用类型声明:
- 定义组件
props
和state
类型。 - 使用
interface
或type
定义数据结构。
import { defineComponent } from 'vue';export default defineComponent({props: {name: String,},setup(props) {const message: string = `Hello, ${props.name}`;return { message };}, });
- 定义组件
7. Vue 模板到渲染的过程是什么?
- 模板解析:
- 将模板编译为 AST(抽象语法树)。
- 优化:
- 标记静态节点,避免不必要的更新。
- 生成渲染函数:
- 将 AST 转化为
render
函数。
- 将 AST 转化为
- 渲染:
- 调用
render
函数生成虚拟 DOM。
- 调用
- Diff:
- 比较新旧虚拟 DOM 的差异,更新真实 DOM。
8. 如何使用 Vue 编写一个 Tab 切换组件? 请介绍设计思路
思路:
- 数据驱动:
使用父组件传递标签数据,控制选中状态。 - 插槽支持:
提供默认插槽,让用户自定义内容。 - 事件通信:
子组件通过事件通知父组件切换标签。
示例代码:
<!-- TabContainer.vue -->
<template><div><div class="tab-headers"><buttonv-for="(tab, index) in tabs":key="index"@click="selectTab(index)":class="{ active: index === activeIndex }">{{ tab.label }}</button></div><div class="tab-content"><slot :active-index="activeIndex"></slot></div></div>
</template><script>
export default {props: ['tabs'],data() {return { activeIndex: 0 };},methods: {selectTab(index) {this.activeIndex = index;},},
};
</script><!-- 使用 -->
<template><TabContainer :tabs="tabList"><template v-slot="{ activeIndex }"><div v-for="(tab, index) in tabList" :key="index" v-show="index === activeIndex">{{ tab.content }}</div></template></TabContainer>
</template><script>
import TabContainer from './TabContainer.vue';export default {components: { TabContainer },data() {return {tabList: [{ label: 'Tab 1', content: 'Content 1' },{ label: 'Tab 2', content: 'Content 2' },],};},
};
</script>
1. Vue 如何优化网站首页的加载速度?
- 代码分割和懒加载:
- 使用动态导入按需加载组件:
const Home = () => import('./components/Home.vue');
- 使用动态导入按需加载组件:
- 使用骨架屏:
- 在主内容加载前显示骨架屏,提升用户体验。
- 预渲染或服务端渲染(SSR):
- 使用插件如
vue-meta
或框架如Nuxt.js
进行 SEO 优化和首屏渲染。
- 使用插件如
- 压缩和优化资源:
- 压缩 CSS、JS 文件。
- 使用现代图片格式(如 WebP)。
- 使用 CDN:
- 将静态资源(如 JS 框架、图片)部署到 CDN 上,加快加载速度。
- HTTP/2 和缓存:
- 开启 HTTP/2 以并行加载资源。
- 利用浏览器缓存或 Service Worker 缓存静态文件。
2. 如何修改 Vue 打包后生成文件的路径?
在 vue.config.js
中配置 publicPath
和 outputDir
:
module.exports = {publicPath: './', // 设置资源路径相对路径outputDir: 'dist', // 修改打包输出文件夹
};
3. 说说你了解哪些 Vue 组件设计原则?
- 单一职责:
- 一个组件只负责一项功能,避免过于复杂。
- 高内聚低耦合:
- 组件之间通过
props
和事件通信,避免直接依赖。
- 组件之间通过
- 数据驱动:
- 用数据驱动 UI,而不是直接操作 DOM。
- 插槽设计:
- 使用
slot
提供扩展点,增加组件的灵活性。
- 使用
- 状态管理:
- 只在必要的地方管理状态,避免不必要的状态提升。
- 可复用性:
- 提取通用逻辑,利用混入(Vue2)或组合式 API(Vue3)增强复用性。
- 性能优化:
- 避免不必要的重渲染,合理使用
v-show
、keep-alive
等。
- 避免不必要的重渲染,合理使用
4. 什么是 Vue 的 Object.defineProperty?
Vue2 的响应式系统基于 Object.defineProperty
,通过拦截对象属性的 getter 和 setter 实现响应式。
-
实现方式:
- 拦截属性读取,收集依赖:
get: function() {// 收集依赖 }
- 拦截属性更新,通知依赖:
set: function(newVal) {// 通知依赖更新 }
- 拦截属性读取,收集依赖:
-
局限性:
- 不能检测对象新增/删除属性。
- 对数组的
push
等操作无法直接拦截。
5. Vue 中给 data 的对象添加新属性时会发生什么? 如何解决?
问题:Vue 无法监听新添加的属性,因为 Object.defineProperty
在初始化时定义了响应式属性。
解决方法:
- 使用
Vue.set
:Vue.set(obj, 'newProp', value);
- 使用 Vue3 的响应式 Proxy(无此问题)。
6. Vue 首页白屏可能是什么问题引起的? 如何解决?
可能原因:
- 资源路径错误:
- 打包后的文件未正确加载,
publicPath
配置错误。
- 打包后的文件未正确加载,
- 异步加载失败:
- 异步组件加载时网络延迟或失败。
- JS/CSS 压缩问题:
- 压缩不当导致代码不可运行。
- 浏览器兼容性问题:
- 低版本浏览器不支持 ES6。
解决方法:
- 确保
publicPath
配置正确。 - 使用 Polyfill 增强兼容性。
- 通过骨架屏优化用户体验。
- 使用浏览器开发工具检查资源加载情况。
7. 如何解决 Vue 动态设置 img 的 src 属性不生效的问题?
原因:某些浏览器或图片资源跨域限制。
解决方法:
- 确保路径正确。
- 使用完整 URL。
- 若跨域受限,设置服务端
Access-Control-Allow-Origin
。
8. 虚拟 DOM 真的比真实 DOM 的性能更好吗?
- 场景:
- 虚拟 DOM 优势在于批量更新和跨平台能力。
- 对于小规模静态页面,真实 DOM 性能更优。
- 虚拟 DOM 的优势:
- 减少直接操作 DOM 的成本。
- 通过 Diff 算法最小化真实 DOM 更新。
- 兼容服务端渲染(SSR)。
9. 你使用过哪些 Vue 的 UI 库? 说说它们的优缺点?
- Element UI:
- 优点:功能齐全,设计简洁,社区成熟。
- 缺点:组件体积较大,不适合移动端。
- Ant Design Vue:
- 优点:设计优雅,企业应用广泛。
- 缺点:文档略复杂。
- Vuetify:
- 优点:基于 Material Design,适合移动端。
- 缺点:学习曲线稍高。
- iView:
- 优点:轻量级,易用。
- 缺点:社区支持较少。
10. 如何销毁 Vue 组件中的定时器?
在组件销毁时清除定时器:
export default {data() {return { timer: null };},mounted() {this.timer = setInterval(() => console.log('Tick'), 1000);},beforeDestroy() {clearInterval(this.timer);},
};
11. 如果 Vue 给组件绑定自定义事件无效,如何解决?
原因:
- 事件未正确绑定。
- 组件未通过
$emit
触发事件。
解决方法:
- 检查父组件是否正确监听:
<ChildComponent @customEvent="handleEvent" />
- 检查子组件是否正确触发:
this.$emit('customEvent', payload);
12. 什么是 JSX? Vue 中怎么使用 JSX?
JSX 是 JavaScript 的语法扩展,允许在 JS 中直接写 HTML。
Vue 中使用 JSX:
- 安装依赖:
npm install @vitejs/plugin-vue-jsx
- 配置 Vite:
import vueJsx from '@vitejs/plugin-vue-jsx';export default {plugins: [vueJsx()], };
- 使用 JSX:
export default {render() {return <div>Hello JSX!</div>;}, };
13. Vue 中 slot 的实现原理是什么?
-
编译阶段:
slot
会被解析为子组件的插槽。- 插槽内容存储为
VNode
。
-
运行时渲染:
- 插槽内容作为子组件的
children
传递。 - 使用
renderSlot
渲染插槽内容:renderSlot(slots, 'slotName', props);
- 插槽内容作为子组件的
-
作用域插槽:
- 父组件通过
scopedSlots
提供数据,子组件动态接收。
1. Vue 组件中,如果使用原生
addEventListener
监听事件,是否需要手动销毁? 为什么? - 父组件通过
是的,需要手动销毁。
- 原因:
- Vue 会自动销毁使用
v-on
或 Vue 自身绑定的事件监听,但对于原生addEventListener
的监听,Vue 无法追踪,需手动移除。 - 如果不移除,可能导致内存泄漏,因为事件监听器引用了 DOM 节点,即使节点被销毁,监听器仍会占用资源。
- Vue 会自动销毁使用
解决方法:
在 beforeDestroy
或 unmounted
钩子中移除事件监听:
mounted() {this.listener = () => console.log('Event triggered');window.addEventListener('resize', this.listener);
},
beforeDestroy() {window.removeEventListener('resize', this.listener);
},
2. Vue 中 v-model
是如何实现的?
工作原理:
v-model
是 Vue 提供的语法糖,用于实现双向数据绑定。
- 绑定值:
- 通过
:value
将数据绑定到表单控件的value
属性。
- 通过
- 监听事件:
- 添加默认的
@input
事件监听器,当用户输入时更新数据。
- 添加默认的
等价代码:
<input v-model="message" />
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value" />
自定义组件中的实现:
通过 modelValue
和 update:modelValue
实现:
<template><input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template><script>
export default {props: ['modelValue'],
};
</script>
3. 在 Vue 项目中如何引入第三方前端库? 有哪些方法?
-
通过 npm 安装:
- 使用 npm 或 yarn 安装第三方库。
- 在组件中
import
:npm install lodash
import _ from 'lodash';
-
通过 CDN 引入:
- 在
public/index.html
中直接添加<script>
标签。<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
- 全局可用的库通过
window
对象访问。
- 在
-
全局引入:
- 在
main.js
中导入:import axios from 'axios'; Vue.prototype.$axios = axios; // 全局绑定
- 在
-
插件形式引入:
- 若库支持 Vue 插件模式,使用
Vue.use
注册:import VueToast from 'vue-toast-notification'; Vue.use(VueToast);
- 若库支持 Vue 插件模式,使用
4. 什么是 Vue 的 render
函数? 它有什么好处?
定义:
render
函数是 Vue 提供的底层渲染函数,用于直接创建虚拟 DOM。
语法:
render(createElement) {return createElement('div', { class: 'box' }, 'Hello, Render');
}
好处:
- 动态生成模板:
- 适合需要动态构建模板的场景。
- 增强灵活性:
- 支持复杂逻辑,超越模板语言的限制。
- 用于 JSX 支持:
- 搭配 JSX 提供更清晰的组件语法。
5. 你了解过哪些 Vue 开发规范?
-
文件组织:
- 使用单文件组件(SFC),按功能模块划分文件夹。
- 文件命名使用 PascalCase(组件)或 kebab-case(视图)。
-
组件设计:
- 组件名称使用多单词(避免与 HTML 标签冲突)。
- 遵循单一职责,提取复用组件。
-
代码风格:
- 变量命名使用 camelCase。
- 模板逻辑保持简单,复杂逻辑移至脚本部分。
-
Props 和事件:
props
必须声明类型和默认值。- 事件命名使用 kebab-case。
-
数据管理:
- 使用 Vuex 管理全局状态。
- 本地数据使用
data
定义。
-
性能优化:
- 合理使用
v-show
和v-if
。 - 避免深度递归绑定。
- 合理使用
6. Vue 怎么与原生 App 进行交互? 有哪些方法?
-
通过 WebView 与 App 通信:
- App 调用 Vue 方法:
在 WebView 中注入 JavaScript 方法:window.nativeCall = function(data) {console.log('App called with data:', data); };
- Vue 调用 App 方法:
使用window.location
调用特定协议:window.location.href = 'myapp://openCamera';
- App 调用 Vue 方法:
-
使用 JSBridge:
- 建立 Vue 和原生代码之间的桥梁。
- 示例:
window.JSBridge.callHandler('methodName', params, (response) => {console.log(response); });
-
使用 PostMessage:
- Vue 中向 App 发送消息:
window.postMessage({ type: 'openCamera' });
- App 接收并返回数据。
- Vue 中向 App 发送消息:
-
NativeScript-Vue:
- 使用 NativeScript 框架直接构建原生应用。
7. Vue Router 完整的导航解析过程是怎样的?
-
导航触发:
- 用户点击链接或调用
router.push
。
- 用户点击链接或调用
-
解析路由配置:
- 匹配目标路由及其嵌套结构。
-
触发全局守卫:
- 调用
beforeEach
:router.beforeEach((to, from, next) => {// 全局逻辑next(); });
- 调用
-
解析组件内守卫:
- 调用目标组件的
beforeRouteEnter
、beforeRouteUpdate
。
- 调用目标组件的
-
触发全局后置守卫:
- 调用
afterEach
,但不阻塞导航。
- 调用
-
渲染视图:
- 根据路由匹配结果加载对应组件,更新页面。
简要示例:
router.beforeEach((to, from, next) => {// 验证权限if (to.meta.requiresAuth && !isAuthenticated()) {next('/login');} else {next();}
});