关于vue.js:百度前端一面必会vue面试题合集

49次阅读

共计 10065 个字符,预计需要花费 26 分钟才能阅读完成。

从 0 到 1 本人构架一个 vue 我的项目,说说有哪些步骤、哪些重要插件、目录构造你会怎么组织

综合实际类题目,考查实战能力。没有什么相对的正确答案,把平时工作的重点有条理的形容一下即可

思路

  • 构建我的项目,创立我的项目根本构造
  • 引入必要的插件:
  • 代码标准:prettiereslint
  • 提交标准:husky,lint-staged`
  • 其余罕用:svg-loadervueusenprogress
  • 常见目录构造

答复范例

  1. 0 创立一个我的项目我大抵会做以下事件:我的项目构建、引入必要插件、代码标准、提交标准、罕用库和组件
  2. 目前 vue3 我的项目我会用 vite 或者 create-vue 创立我的项目
  3. 接下来引入必要插件:路由插件 vue-router、状态治理vuex/piniaui 库我比拟喜爱 element-plu s 和antd-vuehttp 工具我会选axios
  4. 其余比拟罕用的库有vueusenprogress,图标能够应用vite-svg-loader
  5. 上面是代码标准:联合 prettiereslint即可
  6. 最初是提交标准,能够应用huskylint-stagedcommitlint
  7. 目录构造我有如下习惯:.vscode:用来放我的项目中的 vscode 配置
  8. plugins:用来放 vite 插件的 plugin 配置
  9. public:用来放一些诸如 页头 icon 之类的公共文件,会被打包到dist 根目录下
  10. src:用来放我的项目代码文件
  11. api:用来放 http 的一些接口配置
  12. assets:用来放一些 CSS 之类的动态资源
  13. components:用来放我的项目通用组件
  14. layout:用来放我的项目的布局
  15. router:用来放我的项目的路由配置
  16. store:用来放状态治理 Pinia 的配置
  17. utils:用来放我的项目中的工具办法类
  18. views:用来放我的项目的页面文件

如何从实在 DOM 到虚构 DOM

波及到 Vue 中的模板编译原理,次要过程:

  1. 将模板转换成 ast 树, ast 用对象来形容实在的 JS 语法(将实在 DOM 转换成虚构 DOM)
  2. 优化树
  3. ast 树生成代码

Vue 子组件和父组件执行程序

加载渲染过程:

  1. 父组件 beforeCreate
  2. 父组件 created
  3. 父组件 beforeMount
  4. 子组件 beforeCreate
  5. 子组件 created
  6. 子组件 beforeMount
  7. 子组件 mounted
  8. 父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed

vue-router 路由钩子函数是什么 执行程序是什么

路由钩子的执行流程, 钩子函数品种有: 全局守卫、路由守卫、组件守卫

残缺的导航解析流程:

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创立好的组件实例会作为回调函数的参数传入。

用 VNode 来形容一个 DOM 构造

虚构节点就是用一个对象来形容一个实在的 DOM 元素。首先将 template(实在 DOM)先转成 ast ast 树通过 codegen 生成 render 函数, render 函数里的 _c 办法将它转为虚构 dom

理解 nextTick 吗?

异步办法,异步渲染最初一步,与 JS 事件循环分割严密。次要应用了宏工作微工作(setTimeoutpromise那些),定义了一个异步办法,屡次调用 nextTick 会将办法存入队列,通过异步办法清空以后队列。

参考 前端进阶面试题具体解答

delete 和 Vue.delete 删除数组的区别

  • delete 只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变。
  • Vue.delete 间接删除了数组 扭转了数组的键值。

对 SPA 单页面的了解,它的优缺点别离是什么?

SPA(single-page application)仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载实现,SPA 不会因为用户的操作而进行页面的从新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,防止页面的从新加载。

长处:

  • 用户体验好、快,内容的扭转不须要从新加载整个页面,防止了不必要的跳转和反复渲染;
  • 基于下面一点,SPA 绝对对服务器压力小;
  • 前后端职责拆散,架构清晰,前端进行交互逻辑,后端负责数据处理;

毛病:

  • 首次加载耗时多:为实现单页 Web 利用性能及显示成果,须要在加载页面的时候将 JavaScript、CSS 对立加载,局部页面按需加载;
  • 后退后退路由治理:因为单页利用在一个页面中显示所有的内容,所以不能应用浏览器的后退后退性能,所有的页面切换须要本人建设堆栈治理;
  • SEO 难度较大:因为所有的内容都在一个页面中动静替换显示,所以在 SEO 上其有着人造的弱势。

computed 和 watch 有什么区别?

computed:

  1. computed是计算属性, 也就是计算值, 它更多用于计算值的场景
  2. computed具备缓存性,computed 的值在 getter 执行后是会缓存的,只有在它依赖的属性值扭转之后,下一次获取 computed 的值时才会从新调用对应的 getter 来计算
  3. computed实用于计算比拟耗费性能的计算场景

watch:

  1. 更多的是「察看」的作用, 相似于某些数据的监听回调, 用于察看 props $emit 或者本组件的值, 当数据变动时来执行回调进行后续操作
  2. 无缓存性,页面从新渲染时值不变动也会执行

小结:

  1. 当咱们要进行数值计算, 而且依赖于其余数据,那么把这个数据设计为 computed
  2. 如果你须要在某个数据变动时做一些事件,应用 watch 来察看这个数据变动

说说 Vue 的生命周期吧

什么时候被调用?

  • beforeCreate:实例初始化之后,数据观测之前调用
  • created:实例创立万之后调用。实例实现:数据观测、属性和办法的运算、 watch/event 事件回调。无 $el .
  • beforeMount:在挂载之前调用,相干 render 函数首次被调用
  • mounted:了被新创建的 vm.$el 替换,并挂载到实例下来之后调用改钩子。
  • beforeUpdate:数据更新前调用,产生在虚构 DOM 从新渲染和打补丁,在这之后会调用改钩子。
  • updated:因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例依然可用。
  • destroyed:实例销毁之后调用,调用后,Vue 实例批示的所有货色都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期外部能够做什么?

  • created:实例曾经创立实现,因为他是最早触发的,所以能够进行一些数据、资源的申请。
  • mounted:实例曾经挂载实现,能够进行一些 DOM 操作。
  • beforeUpdate:能够在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:能够执行依赖于 DOM 的操作,然而要防止更改状态,可能会导致更新无线循环。
  • destroyed:能够执行一些优化操作,清空计时器,解除绑定事件。

ajax 放在哪个生命周期?:个别放在 mounted 中,保障逻辑统一性,因为生命周期是同步执行的, ajax 是异步执行的。复数服务端渲染 ssr 同一放在 created 中,因为服务端渲染不反对 mounted 办法。 什么时候应用 beforeDestroy?:以后页面应用 $on,须要解绑事件。分明定时器。解除事件绑定, scroll mousemove

说一下 Vue 的生命周期

Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是 Vue 的⽣命周期。

  1. beforeCreate(创立前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到 data、computed、watch、methods 上的办法和数据。
  2. created(创立后):实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到 $el 属性。
  3. beforeMount(挂载前):在挂载开始之前被调用,相干的 render 函数首次被调用。实例已实现以下的配置:编译模板,把 data 外面的数据和模板生成 html。此时还没有挂载 html 到页面上。
  4. mounted(挂载后):在 el 被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的 html 内容替换 el 属性指向的 DOM 对象。实现模板中的 html 渲染到 html 页面中。此过程中进行 ajax 交互。
  5. beforeUpdate(更新前):响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。
  6. updated(更新后):在因为数据更改导致的虚构 DOM 从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM 曾经更新,所以能够执行依赖于 DOM 的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
  7. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例依然齐全可用,this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。

另外还有 keep-alive 独有的生命周期,别离为 activateddeactivated。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。

写过自定义指令吗 原理是什么

指令实质上是装璜器,是 vue 对 HTML 元素的扩大,给 HTML 元素减少自定义性能。vue 编译 DOM 时,会找到指令对象,执行指令的相干办法。

自定义指令有五个生命周期(也叫钩子函数),别离是 bind、inserted、update、componentUpdated、unbind

1. bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。2. inserted:被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变动。通过比拟更新前后的绑定值,能够疏忽不必要的模板更新。4. componentUpdated:被绑定元素所在模板实现一次更新周期时调用。5. unbind:只调用一次,指令与元素解绑时调用。

原理

1. 在生成 ast 语法树时,遇到指令会给以后元素增加 directives 属性

2. 通过 genDirectives 生成指令代码

3. 在 patch 前将指令的钩子提取到 cbs 中, 在 patch 过程中调用对应的钩子

4. 当执行指令对应钩子函数时,调用对应指令定义的办法

Vue 的生命周期办法有哪些 个别在哪一步发申请

beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。在以后阶段 data、methods、computed 以及 watch 上的数据和办法都不能被拜访

created 实例曾经创立实现之后被调用。在这一步,实例已实现以下的配置:数据观测(data observer),属性和办法的运算,watch/event 事件回调。这里没有 $el, 如果非要想与 Dom 进行交互,能够通过 vm.$nextTick 来拜访 Dom

beforeMount 在挂载开始之前被调用:相干的 render 函数首次被调用。

mounted 在挂载实现后产生,在以后阶段,实在的 Dom 挂载结束,数据实现双向绑定,能够拜访到 Dom 节点

beforeUpdate 数据更新时调用,产生在虚构 DOM 从新渲染和打补丁(patch)之前。能够在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程

updated 产生在更新实现之后,以后阶段组件 Dom 已实现更新。要留神的是防止在此期间更改数据,因为这可能会导致有限循环的更新,该钩子在服务器端渲染期间不被调用。

beforeDestroy 实例销毁之前调用。在这一步,实例依然齐全可用。咱们能够在这时进行善后收尾工作,比方革除计时器。

destroyed Vue 实例销毁后调用。调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

activated keep-alive 专属,组件被激活时调用

deactivated keep-alive 专属,组件被销毁时调用

异步申请在哪一步发动?

能够在钩子函数 created、beforeMount、mounted 中进行异步申请,因为在这三个钩子函数中,data 曾经创立,能够将服务端端返回的数据进行赋值。

如果异步申请不须要依赖 Dom 举荐在 created 钩子函数中调用异步申请,因为在 created 钩子函数中调用异步申请有以下长处:

  • 能更快获取到服务端数据,缩小页面 loading 工夫;
  • ssr 不反对 beforeMount、mounted 钩子函数,所以放在 created 中有助于一致性;

Computed 和 Watch 的区别

对于 Computed:

  • 它反对缓存,只有依赖的数据产生了变动,才会从新计算
  • 不反对异步,当 Computed 中有异步操作时,无奈监听数据的变动
  • computed 的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 申明过,或者父组件传递过去的 props 中的数据进行计算的。
  • 如果一个属性是由其余属性计算而来的,这个属性依赖其余的属性,个别会应用 computed
  • 如果 computed 属性的属性值是函数,那么默认应用 get 办法,函数的返回值就是属性的属性值;在 computed 中,属性有一个 get 办法和一个 set 办法,当数据发生变化时,会调用 set 办法。

对于 Watch:

  • 它不反对缓存,数据变动时,它就会触发相应的操作
  • 反对异步监听
  • 监听的函数接管两个参数,第一个参数是最新的值,第二个是变动之前的值
  • 当一个属性发生变化时,就须要执行相应的操作
  • 监听数据必须是 data 中申明的或者父组件传递过去的 props 中的数据,当发生变化时,会触发其余操作,函数有两个的参数:

    • immediate:组件加载立刻触发回调函数
    • deep:深度监听,发现数据外部的变动,在简单数据类型中应用,例如数组中的对象发生变化。须要留神的是,deep 无奈监听到数组和对象外部的变动。

当想要执行异步或者低廉的操作以响应一直的变动时,就须要应用 watch。

总结:

  • computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值。
  • watch 侦听器 : 更多的是 察看 的作用,无缓存性,相似于某些数据的监听回调,每当监听的数据变动时都会执行回调进行后续操作。

使用场景:

  • 当须要进行数值计算, 并且依赖于其它数据时,应该应用 computed,因为能够利用 computed 的缓存个性,防止每次获取值时都要从新计算。
  • 当须要在数据变动时执行异步或开销较大的操作时,应该应用 watch,应用 watch 选项容许执行异步操作 (拜访一个 API),限度执行该操作的频率,并在失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 代替 Object.defineProperty。因为 Proxy 能够间接监听对象和数组的变动,并且有多达 13 种拦挡办法。

相干代码如下

import {mutableHandlers} from "./baseHandlers"; // 代理相干逻辑
import {isObject} from "./util"; // 工具办法

export function reactive(target) {
  // 依据不同参数创立不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {if (!isObject(target)) {return target;}
  const observed = new Proxy(target, baseHandler);
  return observed;
}

const get = createGetter();
const set = createSetter();

function createGetter() {return function get(target, key, receiver) {
    // 对获取的值进行喷射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回以后对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {return function set(target, key, value, receiver) {const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {console.log("属性值被批改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此办法
  set, // 当批改属性时调用此办法
};

形容下 Vue 自定义指令

在 Vue2.0 中,代码复用和形象的次要模式是组件。然而,有的状况下,你依然须要对一般 DOM 元素进行底层操作,这时候就会用到自定义指令。
个别须要对 DOM 元素进行底层操作时应用,尽量只用来操作 DOM 展现,不批改外部的值。当应用自定义指令间接批改 value 值时绑定 v -model 的值也不会同步更新;如必须批改能够在自定义指令中应用 keydown 事件,在 vue 组件中应用 change 事件,回调中批改 vue 数据;

(1)自定义指令根本内容

  • 全局定义:Vue.directive("focus",{})
  • 部分定义:directives:{focus:{}}
  • 钩子函数:指令定义对象提供钩子函数

    o bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。

    o inSerted:被绑定元素插入父节点时调用(仅保障父节点存在,但不肯定已被插入文档中)。

    o update:所在组件的 VNode 更新时调用,然而可能产生在其子 VNode 更新之前调用。指令的值可能产生了扭转,也可能没有。然而能够通过比拟更新前后的值来疏忽不必要的模板更新。

    o ComponentUpdate:指令所在组件的 VNode 及其子 VNode 全副更新后调用。

    o unbind:只调用一次,指令与元素解绑时调用。

  • 钩子函数参数
    o el:绑定元素

    o bing:指令外围对象,形容指令全副信息属性

    o name

    o value

    o oldValue

    o expression

    o arg

    o modifers

    o vnode 虚构节点

    o oldVnode:上一个虚构节点(更新钩子函数中才有用)

(2)应用场景

  • 一般 DOM 元素进行底层操作的时候,能够应用自定义指令
  • 自定义指令是用来操作 DOM 的。只管 Vue 推崇数据驱动视图的理念,但并非所有状况都适宜数据驱动。自定义指令就是一种无效的补充和扩大,不仅可用于定义任何的 DOM 操作,并且是可复用的。

(3)应用案例

高级利用:

  • 鼠标聚焦
  • 下拉菜单
  • 绝对工夫转换
  • 滚动动画

高级利用:

  • 自定义指令实现图片懒加载
  • 自定义指令集成第三方插件

action 与 mutation 的区别

  • mutation 是同步更新, $watch 严格模式下会报错
  • action 是异步操作,能够获取数据后调用 mutation 提交最终数据

为什么 vue 组件中 data 必须是一个函数?

对象为援用类型,当复用组件时,因为数据对象都指向同一个 data 对象,当在一个组件中批改 data 时,其余重用的组件中的 data 会同时被批改;而应用返回对象的函数,因为每次返回的都是一个新对象(Object 的实例),援用地址不同,则不会呈现这个问题。

mixin 和 mixins 区别

mixin 用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的。

Vue.mixin({beforeCreate() {// ... 逻辑        // 这种形式会影响到每个组件的 beforeCreate 钩子函数},
});

尽管文档不倡议在利用中间接应用 mixin,然而如果不滥用的话也是很有帮忙的,比方能够全局混入封装好的 ajax 或者一些工具函数等等。

mixins 应该是最常应用的扩大组件的形式了。如果多个组件中有雷同的业务逻辑,就能够将这些逻辑剥离进去,通过 mixins 混入代码,比方上拉下拉加载数据这种逻辑等等。
另外须要留神的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并。

对前端路由的了解

在前端技术晚期,一个 url 对应一个页面,如果要从 A 页面切换到 B 页面,那么必然随同着页面的刷新。这个体验并不好,不过在最后也是无奈之举——用户只有在刷新页面的状况下,才能够从新去申请数据。

起初,扭转产生了——Ajax 呈现了,它容许人们在不刷新页面的状况下发动申请;与之共生的,还有“不刷新页面即可更新页面内容”这种需要。在这样的背景下,呈现了 SPA(单页面利用)。

SPA 极大地晋升了用户体验,它容许页面在不刷新的状况下更新页面内容,使内容的切换更加晦涩。然而在 SPA 诞生之初,人们并没有思考到“定位”这个问题——在内容切换前后,页面的 URL 都是一样的,这就带来了两个问题:

  • SPA 其实并不知道以后的页面“停顿到了哪一步”。可能在一个站点下通过了重复的“后退”才终于唤出了某一块内容,然而此时只有刷新一下页面,所有就会被清零,必须反复之前的操作、才能够从新对内容进行定位——SPA 并不会“记住”你的操作。
  • 因为有且仅有一个 URL 给页面做映射,这对 SEO 也不够敌对,搜索引擎无奈收集全面的信息

为了解决这个问题,前端路由呈现了。

前端路由能够帮忙咱们在仅有一个页面的状况下,“记住”用户以后走到了哪一步——为 SPA 中的各个视图匹配一个惟一标识。这意味着用户后退、后退触发的新内容,都会映射到不同的 URL 下来。此时即使他刷新页面,因为以后的 URL 能够标识出他所处的地位,因而内容也不会失落。

那么如何实现这个目标呢?首先要解决两个问题:

  • 当用户刷新页面时,浏览器会默认依据以后 URL 对资源进行从新定位(发送申请)。这个动作对 SPA 是不必要的,因为咱们的 SPA 作为单页面,无论如何也只会有一个资源与之对应。此时若走失常的申请 - 刷新流程,反而会使用户的后退后退操作无奈被记录。
  • 单页面利用对服务端来说,就是一个 URL、一套资源,那么如何做到用“不同的 URL”来映射不同的视图内容呢?

从这两个问题来看,服务端曾经齐全救不了这个场景了。所以要靠咱们前端自力更生,不然怎么叫“前端路由”呢?作为前端,能够提供这样的解决思路:

  • 拦挡用户的刷新操作,防止服务端自觉响应、返回不合乎预期的资源内容。把刷新这个动作齐全放到前端逻辑里消化掉。
  • 感知 URL 的变动。这里不是说要革新 URL、凭空制作出 N 个 URL 来。而是说 URL 还是那个 URL,只不过咱们能够给它做一些渺小的解决——这些解决并不会影响 URL 自身的性质,不会影响服务器对它的辨认,只有咱们前端感知的到。一旦咱们感知到了,咱们就依据这些变动、用 JS 去给它生成不同的内容。

正文完
 0