八股只问vue,代码题考js🫣「Momenta一面」

5,680 阅读8分钟

这是我目前见过给面试效率最高的公司,下午投的boss,没一会打电话问我五点钟能不能面试,后面选了七点钟。一面是个姐姐,声音很好听,人也很nice,辛苦她加班面我这个菜鸡了😂。下面是题目和笔者整理的答案,如有疏漏,烦请指正:

八股文

不知道宽高的块级元素如何垂直水平居中?

1.使用 flex 布局

.parent {
    display: flex;
    justify-content: center;
    align-items: center;
}

2.使用 transform 属性

.parent {
    position: relative;
}

.child {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

computed和watch的区别?

Vue中的computedwatch都是用于观察和响应Vue实例上的数据变化的。它们在使用场景和工作方式上有一些不同:

  1. computed(计算属性)

    • computed是计算属性,主要用于当一个属性依赖于一个或多个其他属性时。当依赖的数据变化时,计算属性会重新计算。
    • computed支持缓存。只有当其依赖的属性值发生变化时,计算属性才会重新计算。如果依赖的数据没有变化,那么多次访问计算属性会直接返回之前的计算结果,而不需要重新进行计算。
    • 当多个属性影响一个属性时,或者一个数据需要经过复杂的计算时,建议使用computed
  2. watch(侦听器)

    • watch主要用于观察Vue实例上某个特定数据的变化,从而执行特定的操作。
    • watch不支持缓存,数据每次变化都会执行对应的函数。
    • 当一个值发生变化后,会引起一系列操作(例如,改变其他属性值),或者在数据变化时需要执行异步或开销较大的操作时,适合使用watch

vue首屏优化?

编码阶段:

  1. 尽量减少 data 中的数据,因为 data 中的数据会增加 getter 和 setter 的数量,从而增加了对应的 watcher 的数量。
  2. 避免在同一个元素上同时使用 v-if 和 v-for,因为 v-for 的优先级比 v-if 高,同时使用它们会造成性能浪费。
  3. 如果需要给每个元素绑定事件,请使用事件代理而不是在每个元素上使用事件监听器。
  4. 对于需要频繁切换显示和隐藏的元素,请使用 v-show 替代 v-if。
  5. 使用唯一的 key 值来确保组件的唯一性。
  6. 使用路由懒加载和异步组件来减少首次加载的代码量。
  7. 使用防抖和节流来优化用户输入等频繁触发的操作。
  8. 按需导入第三方模块以减小打包体积。
  9. 对于长列表,只加载可视区域内的内容,同样的还有图片懒加载。

SEO 优化:

  1. 预渲染:通过预渲染技术生成静态 HTML 页面,以提高搜索引擎爬取效率。
  2. 服务端渲染 (SSR):将 Vue 应用程序在服务器端渲染成 HTML,并将其发送到客户端。这样可以加快首屏加载速度并提供更好的 SEO。

打包优化:

  1. 压缩代码:使用压缩工具(如 UglifyJS)来减小代码体积。
  2. Tree Shaking/Scope Hoisting:通过消除未使用的代码来减小打包体积。
  3. 使用 CDN 加载第三方模块:将第三方库放到 CDN 上以提高加载速度。
  4. 多线程打包:使用多线程构建工具(如 happypack)来加快打包速度。
  5. 抽离公共文件:将公共代码抽离成单独的文件以提高缓存效果。
  6. 优化 Source Map:在开发环境中使用合适的 Source Map 类型以提高调试效率。

用户体验:

  1. 骨架屏:在页面加载过程中显示骨架屏以提高用户体验。
  2. PWA(Progressive Web App):将 Web 应用程序转变为 PWA 以提供离线访问和更好的用户体验。

vuex如何实现持久化?

  1. 手动持久化:

    • 将 state 中的数据存储到 localStorage 中,以便在页面刷新后仍然可以访问。
    • 通过监听 window 对象的 beforeunload 事件,将 state 中的数据存储到 localStorage 中。
  2. 使用插件:

    • 使用 vuex-persistedstate 插件来实现持久化。
    • vuex-persistedstate 插件会将 store 中的数据映射到本地环境中,从而实现持久化。
    • 它支持多种存储引擎,如 localStoragesessionStoragecookie 等。
  3. 原理:

    • Vuex 的状态是响应式的,当状态发生变化时,所有依赖于它的组件都会重新渲染。
    • Vuex 的状态是保存在内存中的,因此在页面刷新后,状态会被重置。
    • 为了解决这个问题,我们需要将状态保存到本地存储中。这样,在页面刷新后,我们可以从本地存储中恢复状态。

自定义组件中的v-model?

v-model 是 Vue 中的一个指令,用于在自定义组件中实现双向数据绑定。它是 Vue 的语法糖,本质上是通过自定义标签的属性传递和接收数据。

// 父组件
<template>
  <div>
    <custom-component v-model="data"></custom-component>
    <p>父组件中的数据:{{ data }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: ''
    }
  },
  components: {
    CustomComponent
  }
}
</script>
// 子组件
<template>
  <div>
    <input :value="value" @input="$emit('update:value', $event.target.value)">
  </div>
</template>

<script>
export default {
  model: {
    prop: 'value',
    event: 'update:value'
  },
  props: {
    value: String
  }
}
</script>

vue获取event对象的方式?

方法一: 默认获取

在方法中不填写参数时,默认第一个参数就是 event 对象。

<template>
  <div>
    <button @click="onClick">点击</button>
  </div>
</template>

<script>
export default {
  methods: {
    onClick(e) {
      console.log(e);
    }
  }
}
</script>

方法二: 特殊变量 $event

事件处理 — Vue.js (vuejs.org)

<template>
  <div>
    <button @click="onClick('hello', $event)">点击</button>
  </div>
</template>

<script>
export default {
  methods: {
    onClick(str, e) {
      console.log(str, e);
    }
  }
}
</script>

vue父子组件的通信方式?ref的弊端?如何使依赖注入传递的数据保持响应式?

  • props / $emit
  • provide / inject
  • $attrs / $listeners
  • ref / $ref
  • $parent / $children
  • vuex / pinia

ref的缺点主要是破坏了单向数据流,但它拿到的数据是响应式的;$parent / $children不仅破坏了单向数据流,而且还不是响应式的。

使依赖注入传递的数据保持响应式的方法:

  1. 直接传入整个父组件的实例:vue2的this就是个响应式数据。如果我们直接在父元素中提供依赖this,那么后代组件就能获得响应式。
  2. 函数/对象式的方式传入:比方说 provide 传递的是父组件的 name 这个变量,那么可以 parentName: () => this.name,或者 parentName: {name: this.name}.
  3. Vue-observable,使用API来创建一个响应式数据然后传递,类似的Vue3可以通过ref/reactive创建的数据传递下去就是响应式的。

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中的修饰符有哪些?有没有使用过sync修饰符?

这篇文章总结的很详细: Vue常用修饰符大全 - 掘金 (juejin.cn)

vue实现登录的逻辑?

  1. 登录表单校验
  2. 人类行为验证(简历上项目用的开源的 SliderCaptcha
  3. 调用 pinia 的登录 action 进行登录,所有用户登录行为将被放入到 pinia 中。
  4. 第一次登录时,前端调用后端的登录接口,发送用户名和密码,密码通过 md5 加密。
  5. 前端拿到 token 后,将其存储到 localStorage 和 pinia 中,并跳转路由页面。
  6. 此后前端每次跳转路由时,就判断 localStorage 中有无 token,没有则跳转到登录页面,有则跳转到对应的路由页面。
  7. 前端每次调用后端接口时,都要在请求头中携带 token
  8. 后端判断请求头中有无 token:
    • header 中存在 token:验证 token 的合法性,成功则返回前端请求的数据;失败(如 token 过期)就返回 401 状态码(401 Unauthorized
    • header 中不存在 token:同样返回 401 状态码
  9. 如果有请求返回了 401 状态码,则清除 token 信息并跳转到登陆页面。

vue的菜单权限和按钮权限?

推荐一篇整理的很好的文章:

面试官:Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?

代码题

js实现版本号排序

// 示例
var versions = ['2.1.0.1', '0.402.1', '10.2.1', '5.1.2', '1.0.4.5']
输出: ['10.2.1', '5.1.2', '2.1.0.1', '1.0.4.5', '0.402.1']
const versions = ['2.1.0.1', '0.402.1', '10.2.1', '5.1.2', '1.0.4.5'];

versions.sort((a, b) => {
  const partsA = a.split('.').map(Number);
  const partsB = b.split('.').map(Number);

  for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
    const partA = partsA[i] || 0;
    const partB = partsB[i] || 0;

    if (partA !== partB) {
      return partB - partA;
    }
  }

  return 0;
});

使用js补全代码

function add(){

}

add(1)(1)(1)() //3

add(1)(2)(3)(4) //10

add(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)() //10

这道题我不确定是不是题目中 add(1)(2)(3)(4) 这个地方最后少打了个括号?当时没意识到跟面试官提出疑惑,想了半天没想明白。

function add(num) {
  let sum = num;

  function addNext(nextNum) {
    if (nextNum === undefined) {
      return sum;
    } else {
      sum += nextNum;
      return addNext;
    }
  }

  return addNext;
}

console.log(add(1)(1)(1)()); // 输出 3
console.log(add(1)(2)(3)(4)); // 输出 10
console.log(add(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)()); // 输出 10