- 教程
【免费下载链接】FAQGURU
:school_satchel: :rocket: :tada: A list of interview questions. This repository is everything you need to prepare for your technical interview.
导读:本文以开源面试题库仓库 FAQGURU 的中文文档 topics/zh/vuejs.md 为骨架,系统梳理 Vue.js 的实例创建、模板绑定、指令、组件化、计算属性、过滤器、路由、Vuex 状态管理、Mixins 与部署构建等核心知识点,并给出大量可运行的代码示例。读完本文,你不仅能完整回答 Vue.js 面试中的高频提问,还能掌握组件通信、数据监听与工程化部署的实战套路。
一、文档背景与阅读前提
FAQGURU 是一个以「技术面试题 + 参考答案 + 代码片段」为组织形式的开源仓库,仓库根目录 readme.md 明确说明其定位是「参考工具」(reference tool),帮助开发者备战技术面试,且很多问题是真实面试中出现的开放式问题。本文所依据的 topics/zh/vuejs.md 是 Vue.js 专题的中文版本,对应的英文原版位于 topics/en/vuejs.md,仓库中文总览见 topics/zh/readme.md。
需要特别说明的是:该文档撰写于 Vue 2.x 时代(文中代码使用vue/2.5.1、提到v-bind表达式在 2.1.0+ 支持过滤器、router.go()在 2.0 中发生变化),因此文中示例均以 Vue 2 语法为准。理解这一前提有助于你区分 Vue 2 与 Vue 3 的 API 差异(例如 Vue 3 中过滤器已被移除,推荐使用计算属性或方法代替)。
二、Vue.js 是什么?如何创建第一个实例?
2.1 Vue.js 的本质
Vue.js是一套用于构建用户界面的渐进式框架(progressive framework)。它学起来很容易:要使用 Vue.js,你只需要给网站添加一点动态功能,无需安装任何东西,只需在项目中引入 Vue.js 库即可。
Vue.js 自带以下特性:
- 模板语法(Templates):声明式地把 DOM 绑定到底层数据
- 响应式(Reactivity):数据变化自动驱动视图更新
- 组件化(Components):把 UI 拆分为可复用的独立单元
- 过渡动画(Transitions):为插入、更新或移除 DOM 元素时提供过渡效果
- 路由(Routing):配合 vue-router 实现单页应用导航
2.2 创建 Vue 实例
每个 Vue 应用都是通过Vue函数创建一个新的 Vue 实例开始的:
var vm = new Vue({ // 选项 });实例选项中可以配置el(挂载点)、data(数据)、computed(计算属性)、methods(方法)、watch(侦听器)、components(局部组件)等。下面是一个最小可运行的双向绑定示例:
<div id="app">{{message}} <input v-model="message" /></div> <script type="text/javascript"> var message = 'Vue.js is rad'; new Vue({ el: '#app', data: { message } }); </script>这里el: '#app'将实例挂载到id="app"的 DOM 节点,data中的message通过插值{{ message }}渲染到页面,并与输入框通过v-model建立双向绑定——这正是「响应式」与「模板语法」两大特性的直接体现。
三、数据绑定:双向绑定、单向绑定与 v-bind/v-model 之辨
3.1 单向绑定与双向绑定的区别
单向数据流(one-way data flow)中,应用的视图(UI)部分不会在数据模型更改时自动更新,我们需要编写自定义代码,在每次数据模型更改时手动更新视图。Vue.js 中使用v-bind进行单向绑定。
双向绑定(two-way data binding)中,应用的视图会在数据模型更改时自动更新。Vue.js 中使用v-model指令实现双向数据绑定。
3.2 v-model 的语法糖本质
v-model用于表单输入的双向绑定。它其实是两个指令的组合:
v-bind:负责把 JS 值传入模板(value 属性)v-on:input:负责在输入事件发生时更新 JS 值
代码如下:
<input v-model="something" />它是下面写法的语法糖:
<input v-bind:value="something" v-on:input="something = $event.target.value" />v-model适用于所有原生 HTML 输入类型(text、textarea、number、radio、checkbox、select)。如果你想把日期数据存储为 ISO 字符串(yyyy-mm-dd)形式,可以把v-model与input type="date"结合使用。
四、指令系统:概念、常用指令与指令类型
4.1 什么是指令?
Vue.js 中的指令概念比 Angular 的简单得多。Vue.js 指令通过新的属性和标签来扩展 HTML,为应用提供扩展性功能。除内置指令外,你还可以编写自定义指令。
4.2 常用内置指令
下面是一些 Vue.js 中常用的指令:
v-showv-ifv-modelv-elsev-on
补充说明:v-if与v-show都控制元素显示,但前者是「条件渲染」(不满足条件时不渲染 DOM),后者是「样式切换」(始终渲染但通过display隐藏);v-on可用@缩写(如@click),v-bind可用:缩写(如:key)。
4.3 指令类型
Vue.js 中有以下几种指令:
- 一般指令(General Directives):如
v-if、v-show、v-for,带有参数与值 - 文字指令(Literal Directives):值被当作字符串处理
- 空指令(Empty Directives):不接收参数与值
- 自定义指令(Custom Directives):通过
Vue.directive()注册,可在钩子函数(如bind、inserted、update)中操作 DOM
4.4 循环:v-for
你可以在模板中这样使用循环:
<b-menu v-for="menu in menu_items" v-bind:key="menu.label"> <label>{{ menu.label }}</label> <b-menu-list v-for="item in menu.items" v-bind:key="item"> <b-menu-item> {{ item.message }}</b-menu-item> </b-menu-list> </b-menu>注意:使用v-for时必须绑定key,这是 Vue 的 diff 算法高效复用与正确跟踪节点身份的关键,建议使用唯一且稳定的值(如menu.label、item.id),避免使用数组索引。
五、组件化开发:组件、props 与应用组织
5.1 组件是什么?
组件(Component)是 Vue.js 最强大的特性之一。组件是自定义元素,扩展了基本的 HTML 元素,进而能对代码进行封装复用。
下面是在一个组件中注册另一个 Vue 组件的方法:
export default { el: '#your-element', components: { 'your-component' } }5.2 组件的 props
每个组件实例都有自己独立的作用域,这意味着你不能(也不应该)直接引用子组件模板中的父级数据。可以通过使用props将数据传递给子组件。props 是你可以在组件上注册的自定义属性,当一个值传递给某个 props 属性时,它就变成了那个组件实例上的一个属性。
Vue.component('blog-post', { // JavaScript 中是 camelCase 命名法 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>', });在模板中使用时建议采用 kebab-case(如post-title),Vue 会自动完成 camelCase 与 kebab-case 之间的映射。
5.3 Vue.js 应用组织的基本逻辑
一个Vue.js 应用由new Vue创建的根 Vue 实例组成,你可以编写复用组件进行嵌套组织,构成树状形式。例如,TODO 应用的组件树可能如下所示:
Root Instance └─ TodoList ├─ TodoItem │ ├─ DeleteTodoButton │ └─ EditTodoButton └─ TodoListFooter ├─ ClearTodosButton └─ TodoListStatistics所有 Vue 组件同样都是 Vue 实例。理解「根实例 → 嵌套组件树」的组织方式,是设计可维护应用的前提。
5.4 组件通信方法全览
Vue.js 应用中常见的组件通信通道有以下几种:
- Props(
props):Vue 中最简单的通信方法,用于父子组件直接通信,主要用于传递与逻辑有关的数据或层级有限的数据集 - Refs 和方法(
ref):当无法使用 prop 来让子组件处理父组件传递的事件时,可以在子组件上设置 ref 并调用它的方法 - 事件(
$emit和$on):用于父子组件直接通信的简单方法,适合逻辑交互 - 事件总线(
this.$root.$emit、this.$root.$on):当从祖先组件传递 props 到深层嵌套的子组件时比较有用,不过父子组件之间就不需要这样做了 - 集中存储(Vuex):Vuex 是 Vue 的状态管理库,它不仅支持事件,还为应用提供了完整的功能
5.5 父组件如何调用子组件的函数?
给子组件设置ref属性,并通过$refs直接调用子组件上的方法:
HTML:
<div id="app"> <child-component ref="childComponent"></child-component> <button @click="click">Click</button> </div>JavaScript:
var ChildComponent = { template: '<div>{{value}}</div>', data: function() { return { value: 0, }; }, methods: { setValue: function(value) { this.value = value; }, }, }; new Vue({ el: '#app', components: { 'child-component': ChildComponent, }, methods: { click: function() { this.$refs.childComponent.setValue(2.0); }, }, });$refs提供了对已注册子组件实例的直接访问,适合「父触发、子响应」的命令式调用场景。
5.6 Vue.js 2.0 中兄弟组件之间如何通信?
Vue.js 2.0 中,可以使用eventHub(事件中心)机制:
const eventHub = new Vue(); // 独立的事件注册中心 // 使用全局 mixin 分发给组件 Vue.mixin({ data: function() { return { eventHub: eventHub, }; }, }); // 你的组件可以触发事件 this.eventHub.$emit('update', data); // 可以监听事件 this.eventHub.$on('update', data => { // 搞事情 });你甚至可以将 Vue 根实例用作「全局」事件中心:
// 组件 1 this.$root.$emit('eventing', data); // 组件 2 mounted() { this.$root.$on('eventing', data => { console.log(data); }); }需要提醒的是:对于简单场景事件总线足够,但事件一多会难以追踪,大型应用仍推荐使用 Vuex 做集中状态管理。
六、计算属性与方法:区别与传参
6.1 方法 vs 计算属性的主要区别
Vue.js 中,计算属性和方法在本质上是完全不一样的,并且在大多数情况下不可互换:
- 计算属性(computed property)可以视为派生值,只要用于计算的基础值更新,它就会自动更新,并且带缓存——只有依赖项变化时才会重新计算
- 方法(method)只是绑定到 Vue 实例的函数,只会在你显式调用它时执行,每次调用都会重新求值
6.2 可以给计算属性传递参数吗?
可以使用方法或计算属性实现带参的派生逻辑。
方法形式:
<span>{{ fullName('Hi') }}</span> methods: { fullName(salut) { return `${salut} ${this.firstName} ${this.lastName}` } }带参数计算属性形式(计算属性返回一个接收参数的函数,即「闭包」技巧):
computed: { fullName() { return salut => `${salut} ${this.firstName} ${this.lastName}` } }第二种写法保持了「依赖数据变化时自动更新」的特性,但需要理解它实际是「返回函数」,因此无法享受常规计算属性的缓存特性。
七、过滤器:格式化输出的利器
7.1 过滤器指什么?
Vue.js 中的过滤器,用于对即将在浏览器上渲染的输出值进行格式化转换。过滤器本质上是一个函数:接收一个参数值并经过相应处理,返回处理过后的值。在标记语言中,它由管道符号(|)表示,后面可以跟一个或多个参数:
<element directive="expression | filterId \[args...\]"></element>7.2 过滤器的使用位置
Vue.js 允许你自定义过滤器,用于常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind 表达式(后者从 2.1.0+ 开始支持)。过滤器应添加在 JavaScript 表达式的尾部,由「管道」符号表示:
<!-- 在双花括号中 --> {{ message | capitalize }} <!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div>7.3 怎么给过滤器传递参数?
定义过滤器时,第一个参数固定是待处理的值,后续参数来自模板中的传参:
filters:{ currency: function(value, arg1){ return arg1+value; }用法:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.1/vue.js"></script> <div id="vue-instance">{{123 | currency('$') }}</div>渲染结果为$123。再次强调:该语法属于 Vue 2.x,Vue 3 已移除过滤器,需要改用计算属性或方法实现同样的格式化。
八、路由:跳转、查询参数与零依赖路由
8.1 Vue.js 中如何跳转到另一个页面?
如果你使用vue-router,可以使用router.go(path)导航到其它特定路由,并可在组件内通过this.$router访问路由属性。router.go()在 Vue.js 2.0 版本中发生了变化,现在推荐使用:
this.$router.push({ name: 'yourroutename' }) // 或直接传入路径 this.$router.push('yourroutename')(提示:在 2.0 中router.go(n)的语义已调整为「浏览器历史前进/后退 n 步」,而router.push()用于真正的页面跳转。)
8.2 如何获取 URL 的查询参数?
可以在组件中访问$route对象,它提供了我们需要的参数:
// 组件内 console.log(this.$route.query.test);对于/search?test=hello这样的 URL,this.$route.query.test即为hello;路径参数(如/user/:id)则可通过this.$route.params获取。
8.3 如何实现一个简单的路由(不使用额外的库)?
如果你只需要简单的路由功能,不涉及其它功能,可以通过动态渲染页面组件来实现:
const NotFound = { template: '<p>Page not found</p>' }; const Home = { template: '<p>home page</p>' }; const About = { template: '<p>about page</p>' }; const routes = { '/': Home, '/about': About, }; new Vue({ el: '#app', data: { currentRoute: window.location.pathname, }, computed: { ViewComponent() { return routes[this.currentRoute] || NotFound; }, }, render(h) { return h(this.ViewComponent); }, });该方案的核心思想是:用一个「路径 → 组件」的映射表,结合计算属性动态选出当前组件并渲染。但对于大多数单页应用,仍建议使用官方支持的 vue-router 库,它提供了嵌套路由、路由守卫、懒加载等完整能力。
九、Vuex:状态管理库与 Angular Service 的对应
9.1 Vuex 是什么?
Vuex 是 Vue.js 应用的状态管理库。它承担应用中所有组件的集中存储作用,它的规则保证了状态只能以可预测的方式进行变更。Vuex 背后的基础理念受到 Flux、Redux 和 Elm 架构的启发。
Vuex 解决了两个核心问题:
- 多个视图可能依赖于同一状态:对于多层嵌套的组件,通过传递 props 会很麻烦,兄弟组件则无法直接共享
- 不同视图的操作可能会改变同一状态:直接操作父/子实例引用,或试图通过事件修改、同步状态的多个副本,这两种模式都很脆弱,会导致代码难以维护
Vuex 通过「集中式 Store + 明确规则(state → getters → mutations → actions)」让状态变更可追踪、可预测。
9.2 Vue.js 中与 Angular Service 对应的方案
Angular Service 在 Vue.js 中有 4 种对应方式:
- 无状态 service:使用 mixins 复用逻辑
- 有状态 service:使用 Vuex
- 从 Vue 代码中导出和导入service(ES Module 方式)
- 使用任意JavaScript 全局对象
按「是否有共享状态」来选择方案,是避免过度设计的关键。
十、Mixins 与全局常量
10.1 为什么需要 mixins?
Mixins 是为 Vue 组件分发复用函数的一种灵活方法。mixin 对象可以包含任何组件选项(data、methods、computed、生命周期钩子等)。当组件使用 mixin 时,mixin 中的所有选项将「混入」到组件自己的选项中。
// 定义 mixin 对象 var myMixin = { methods: { getProducts() { myApi.get('products?id=' + prodId).then(response => (this.product = response.data)); }, }, }; // 定义使用 mixin 的组件 var Component = Vue.extend({ mixins: [myMixin], }); // 初始化时使用 mixin new Vue({ mixins: [myMixin], created: function() { console.log('other code'); }, });注意:当 mixin 与组件自身存在同名选项时,组件自身的选项拥有更高优先级;生命周期钩子则会先执行 mixin 的再执行组件的。
10.2 创建可从整个应用访问的常量的最佳方法
始终可以在 Vue 应用的作用域外定义变量,并在整个应用中使用它。推荐用 ES Module 的方式单独建一个常量文件:
// const.js export default { c1: 'Constant 1', c2: 'Constant 2', };然后在组件中导入:
// component.vue import const from './const'; export default { methods: { method() { return const.c1; } } }这样常量只维护在一处,且不进入 Vue 的响应式系统,避免了不必要的性能开销。
十一、本地存储与数据监听实战
11.1 Vue.js 中怎么使用本地存储?
可以直接调用浏览器原生的 localStorage API:
localStorage.setItem('YourItem', response.data); localStorage.getItem('YourItem'); localStorage.removeItem('YourItem');11.2 如何在用户输入时将新值保存到本地存储?
使用watch方法检测存储数据的变化:
watch: { input: function () { if (isLocalStorage() /* 检测是否支持 localstorage 的函数*/) { localStorage.setItem('storedData', this.input) } } }watch在input数据每次变化时触发,配合能力检测(isLocalStorage())确保在不支持 localStorage 的环境(如隐私模式)下不会报错。
11.3 如何监听数组长度?
使用侦听器监听返回数组长度的计算属性:
var vm = new Vue({ el: 'body', data: { items: [], }, computed: { item_length: function() { return this.battle_logs.length; }, }, watch: { items: { handler: function() { console.log('caught!'); }, deep: true, }, }, });或者直接监听计算出的长度属性:
vm.$watch('item_length', function(newVal, oldVal) { console.log('caught!'); });(提示:监听数组本身时,由于 Vue 2 对items.length这类「数组长度变化」的侦测有限,通常更稳妥的做法是像上面那样监听计算属性返回的 length。)
11.4 如何监听组件 props 的变化?
当 props 改变时,可以通过watchprops 来执行一些代码:
new Vue({ el: '#app', data: { text: 'Hello', }, components: { child: { template: `<p>{{ myprop }}</p>`, props: ['myprop'], watch: { myprop: function(newVal, oldVal) { // 监听 myprop console.log('Prop changed: ', newVal, ' | was: ', oldVal); }, }, }, }, });在 HTML 中:
<script src="https://unpkg.com/vue/dist/vue.js"></script> <div id="app"> <child :myprop="text"></child> <button @click="text = 'Another text'">Change text</button> </div>点击按钮修改父级text后,子组件 watcher 会收到新旧值并打印Prop changed: Another text | was: Hello。
十二、样式与 class 的动态绑定
12.1 Vue.js 中如何绑定 style?
通过v-bind:style绑定一个内联样式对象:
<div id="table"> <div v-for="table in tables"> <div class="table-div" v-bind:style="{top: table.top + 'px', left: table.left + 'px'}"> {{table.name}} </div> </div> </div>这里配合v-for遍历tables,把每项的top、left动态拼接成像素值,实现类似「表格单元格绝对定位」的布局效果。
12.2 Vue.js 中如何切换 class?
可以让 class 的激活状态依赖于某一个布尔值:
<th class="initial" v-on:click="myFilter" v-bind:class="{ active: isActive }"> <span class="wkday">M</span> </th>methods: { myFilter: function(){ this.isActive = !this.isActive; // 一些过滤用户的代码 } }点击<th>时myFilter翻转isActive,{ active: isActive }中的activeclass 随之加上或移除,这是 Vue 对象语法绑定 class 的典型用法(也可使用数组语法[isActive ? 'active' : ''])。
十三、渲染强制更新、布局跳闪与构建部署
13.1 如何强制 Vue.js 重新加载/重新渲染?
Vue 实例提供了强制更新方法:
vm.$forceUpdate();需要注意:正常情况下 Vue 的响应式系统会自动完成更新,$forceUpdate()只是兜底手段——例如在数据上动态新增了未被 Vue 2 转化为响应式的属性时,才需要考虑强制刷新。
13.2 如何避免 Vue.js 中的布局跳闪(FOUC)?
要是你受不了首次渲染的时间(即页面先展示空结构再「跳闪」出内容),唯一的办法就是搞服务器端渲染(SSR)。你的应用加载时便已经有了初始 HTML 结构,浏览器拿到的是渲染好的页面,从而彻底消除客户端首屏空白/跳闪问题。
13.3 如何部署 Vue.js 应用?
如果你已经像这样创建了项目:
vue init webpack myproject现在可以在应用所在的目录下执行以下命令构建生产包:
npm run build然后复制index.html和/dist/ 文件夹到你网站服务器的根目录即可。构建产物是纯静态资源,可托管到任意 Web 服务器、CDN 或对象存储。
13.4 Gulp 和 Vue.js 如何配合使用?
可以使用Vueify——它是browserify的转换工具(transform),负责把.vue单文件组件编译为浏览器可识别的 JS:
gulp.task('browserify', function() { return gulp .src('src/js/main.js') .pipe(plumber()) .pipe( browserify({ debug: !env.p, transform: ['vueify'], }) ) .pipe(gulpif(env.p, uglify())) .pipe(gulp.dest('build/js')); });这段 Gulp 任务展示了完整的编译管线:读取入口main.js→ 出错保护(plumber)→ browserify 打包并启用vueify转换 → 生产环境(env.p)下用uglify压缩 → 输出到build/js。在 webpack 成为主流之前,这是 Vue 2 早期常见的构建方案。
十四、Vue.js 的优缺点与适用边界
14.1 优点
- 方便应用和界面开发:声明式模板 + 响应式系统大幅降低 UI 开发复杂度
- 和 AngularJS 一样支持双向通信:
v-model让表单开发非常顺手 - 支持状态控制:配合 Vuex 可集中管理共享状态
- 自然的框架思想流程:模板、脚本、样式组织直观,学习曲线平缓
原文档还总结了以下具体优势:
- 扩展 HTML:通过划分不同组件来优化 HTML 代码块,与 Angular 有许多相似特性
- 详细的文档:能有效加快开发人员的学习进度,只需了解 HTML 和 JavaScript 基本知识
- 适应性:从其他框架切换到 Vue.js 不需要很长的适应时间,因为其设计与架构与 Angular 和 React 较相似
- 很棒的整合:可用于构建单页面应用或更复杂的 Web 应用,轻量的交互部分可以轻松集成到现有基础架构中,而不会对整个系统产生副作用
- 规模大:可复用模板配合简单结构,能省下大量时间
- 轻量:原文档(约 2018 年的对比数据)称 Vue.js 只有 20KB 左右大小,同时兼备速度与灵活性(注意:该数据仅代表当时版本情况)
14.2 缺点(原文档观点)
- 规模有限:文档认为其生态规模相对有限(写作时的观点)
- 只有一个创建者:由尤雨溪主导(注:如今 Vue 已由核心团队与社区共同维护)
- 开发者社区不大:与 React、Angular 相比市场份额较小,知识分享起步阶段(以上两点均为原文档约 2018 年撰写时的判断)
原文档还列出了另外三条当时语境下的缺点,了解它们有助于你带着批判性视角看待框架选型:
- 缺乏资源:与 React 和 Angular 相比,Vue.js 市场份额仍很小,相关资料分享处于起步阶段
- 过于灵活的风险:整合进大型项目时可能遇到问题,若找不到现成解决方案会比较棘手,不过很快就会有相应方案
- 中国背景:Vue.js 有中国背景,很多内容与说明是中文的,早期可能导致开发阶段的一些不便,不过越来越多的资料已被翻译成英文
结语
本文完整覆盖了 topics/zh/vuejs.md 中的全部 39 个知识点,并将其重组为「实例与响应式 → 绑定与指令 → 组件化 → 计算属性与过滤器 → 路由与状态管理 → 工程化与部署」的递进脉络。作为面试复习,你可以把每个小节当作独立的速查卡;作为实战参考,其中的代码片段均可在 Vue 2.x 项目中直接运行验证。配合仓库中的英文原版 topics/en/vuejs.md 对照阅读,还能顺便积累面试时的英文表述。需要提醒的是,若你的项目已基于 Vue 3,请注意文中过滤器、$refs使用方式等 API 的版本差异,并参考官方迁移文档进行适配。
- 教程
【免费下载链接】FAQGURU
:school_satchel: :rocket: :tada: A list of interview questions. This repository is everything you need to prepare for your technical interview.
相关推荐
FAQGURU 前端面试指南:HTML5 核心概念与高频面试题全解析
FAQGURU 前端面试指南:HTML5 核心概念与高频面试题全解析 HTML5 是 Web 前端面试中出镜率最高的考点之一,本篇文章以 FAQGURU 开源面
教程FAQGURU 面试指南:AngularJS 核心机制与高频面试题全解析
FAQGURU 面试指南:AngularJS 核心机制与高频面试题全解析 本文是 FAQGURU 技术面试题库中 topics/en/angularjs.md
教程FAQGURU 技术面试指南:GraphQL 核心概念、Schema 设计、错误处理与 REST 对比全解析
FAQGURU 技术面试指南:GraphQL 核心概念、Schema 设计、错误处理与 REST 对比全解析 GraphQL 是 Facebook 于 2012
教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考