专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
庞门正道  ·  如!何!提!加!薪? ·  3 天前  
润农畜牧报价  ·  2025年2月5日 全国各地区鸡蛋报价! ·  3 天前  
VogueBusiness  ·  VOGUE Runway 热度榜:盘点 ... ·  4 天前  
VogueBusiness  ·  VOGUE Runway 热度榜:盘点 ... ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

社区精选|前端 Vue 必问面试题

SegmentFault思否  · 公众号  ·  · 2024-02-16 13:48

正文

今天小编为大家带来的是社区作者 xiangzhihong 的文章,让我们一起来学习前端 Vue 必问面试题。



1 Vue3.0 为什么要使用 proxy

在 Vue2 中, 0bject.defineProperty 会改变原始数据,而 Proxy 是创建对象的虚拟表示,并提供 set 、get 和 deleteProperty 等处理器,这些处理器可在访问或修改原始对象上的属性时进行拦截,有以下特点∶

  • 不需用使用 Vue.$set 或 Vue.$delete 触发响应式。
  • 全方位的数组变化检测,消除了Vue2 无效的边界情况。
  • 支持 Map,Set,WeakMap 和 WeakSet。

Proxy 实现的响应式原理与 Vue2 的实现原理相同,实现方式大同小异∶

  • get 收集依赖
  • Set、delete 等触发依赖
  • 对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执行依赖相关的收集或触发逻辑。

2 谈谈你对 slot 的理解,以及 slot 的使用场景

在 HTML 中 slot 元素 ,作为 Web Components 技术套件的一部分,是 Web 组件内的一个占位符。该占位符可以在后期使用自己的标记语言填充:

<template id="element-details-template">
<slot name="element-name">Slot templateslot>
template>
<element-details>
<span slot="element-name">1span>
element-details>
<element-details>
<span slot="element-name">2span>
element-details>

template 不会展示到页面中,需要用先获取它的引用,然后才会添加到 DOM 中。

customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document
.getElementById('element-details-template')
.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(template.cloneNode(true));
}
})
Slot 艺名插槽,花名“占坑”,我们可以理解为 solt 在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中 slot 位置)。
通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理。如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情。通过 slot 插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用

3 Vue 渲染大量数据时怎么进行优化。

企业级项目中渲染大量数据的情况比较常见,因此这是前端面试中一个必问的题目。对于这个题目,我们可以从以下几个方面进行考虑:

  • 在大型企业级项目中经常需要渲染大量数据,此时很容易出现卡顿的情况。比如大数据量的表格、树。
  • 处理时要根据情况做不同处理。
  • 可以采取分页的方式获取,避免渲染大量数据。
  • vue-virtual-scroller (opens new window) 等虚拟滚动方案,只渲染视口范围内的数据。
  • 如果不需要更新,可以使用 v-once 方式只渲染一次。
  • 通过 v-memo (opens new window) 可以缓存结果,结合 v-for 使用,避免数据变化时不必要的VNode创建。
  • 可以采用懒加载方式,在用户需要的时候再加载数据,比如 tree 组件子树的懒加载。
还是要看具体需求,首先从设计上避免大数据获取和渲染;实在需要这样做可以采用虚表的方式优化渲染;最后优化更新,如果不需要更新可以 v-once 处理,需要更新可以 v-memo 进一步优化大数据更新性能。其他可以采用的是交互方式优化,无线滚动、懒加载等方案。

4 Scoped 样式穿透是什么

scoped 是 style 标签的一个属性,当在 style 标签中定义了 scoped 时,style 标签中的所有属性就只作用于当前组件的样式,实现组件样式私有化,从而也就不会造成样式全局污染。
项目开发中,多数情况下不能避免引用第三方组件,而第三方组件的样式又不全是我们想要的,就需要在组件中局部修改第三方组件的样式,但同时又不想去除 scoped 属性和避免样式污染。此时只能通过穿透 scoped,写法如下。
<style scoped>
外层 > 第三方组件 {
样式
}

style>

5 谈谈你对 Vue、Angular 以及 React 的理解

首先,我们来看一下 Vue 与 AngularJS 的区别

  • Angular 采用 TypeScript 开发, 而 Vue 可以使用 javascript 也可以使用TypeScript
  • AngularJS 依赖对数据做脏检查,所以 Watcher 越多越慢;Vue.js 使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
  • AngularJS 社区完善, Vue 内置了很多默认的模版和语法,学习成本较小。
接下来,我们看一下 Vue 与 React 的区别

相似点:

  • Virtual DOM。其中最大的一个相似之处就是都使用了 Virtual DOM,也就是能让我们通过操作数据的方式来改变真实的 DOM 状态。因为其实 Virtual DOM 的本质就是一个 JS 对象,它保存了对真实 DOM 的所有描述,是真实 DOM 的一个映射,所以当我们在进行频繁更新元素的时候,改变这个 JS 对象的开销远比直接改变真实 DOM 要小得多。
  • 组件化开发。它们都提倡这种组件化的开发思想,也就是建议将应用分拆成一个个功能明确的模块,再将这些模块整合在一起以满足我们的业务需求。
  • Props。Vue 和 React 中都有 props 的概念,允许父组件向子组件传递数据。
  • 构建工具、Chrome 插件、配套框架。还有就是它们的构建工具以及 Chrome 插件、配套框架都很完善。比如构建工具,React 中可以使用 CRA,Vue 中可以使用对应的脚手架 vue-cli。对于配套框架 Vue 中有 vuex、vue-router,React 中有 react-router、redux。

不同点:

  • 模版编写。最大的不同就是模版的编写,Vue 鼓励你去写近似常规 HTML 的模板,React 推荐你使用 JSX 去书写。
  • 状态管理与对象属性。在 React 中,应用的状态是比较关键的概念,也就是state 对象,它允许你使用 setState 去更新状态。但是在 Vue 中,state 对象并不是必须的,数据是由 data 属性在 Vue 对象中进行管理。
  • 虚拟 DOM 的处理方式不同。Vue 中的虚拟 DOM 控制了颗粒度,组件层面走 watche r通知,而组件内部走 vdom 做 diff,这样,既不会有太多watcher,也不会让 vdom 的规模过大。而 React 走了类似于 CPU 调度的逻辑,把 vdom 这棵树,微观上变成了链表,然后利用浏览器的空闲时间来做 diff。

6 Vue是如何解决跨域问题的

跨域本质是浏览器基于同源策略的一种安全手段同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能。

所谓同源(即指在同一个域)具有以下三个相同点

  • 协议相同(protocol)
  • 主机相同(host)
  • 端口相同(port)
反之非同源请求,也就是协议、端口、主机其中一项不相同的时候,这时候就会产生跨域。

Vue 解决跨域的方法有很多,下面列举了三种:

  • JSONP
  • CORS
  • Proxy
而在 Vue 项目中,我们主要针对 CORS 或 Proxy 这两种方案进行展开:
CORS

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应 CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源,只要后端实现了 CORS,就实现了跨域。

app.use(async (ctx, next)=> {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
})
Proxy
代理(Proxy)也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。 一些网关、路由器等网络设备具备网络代理功能。 一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。

方案一

如果是通过 vue-cli 脚手架工具搭建项目,我们可以通过 webpack 为我们起一个本地服务器作为请求的代理对象。通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果 web 应用和接口服务器不在一起仍会跨域。

在 vue.config.js 文件,新增以下代码:

amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
open: true,// vue项目启动时自动打开浏览器
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'""代替
'^/api': ""
}
}
}
}
}
通过 axios 发送请求中,配置请求的根路径。
axios.defaults.baseURL = '/api'

方案二

通过配置 nginx 实现代理:

server {
listen 80;
# server_name www.josephxia.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

7 Class 与 Style 如何实现动态绑定

Class 可以通过对象语法和数组语法进行动态绑定,比如:
//对象语法
<div v-bind:class="{ active: isActive, 'text-danger': hasError }">div>
data: {
isActive: true,
hasError: false
}
//数组语法
class="[isActive ? activeClass : '', errorClass]">div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}

Style 也可以通过对象语法和数组语法进行动态绑定,比如:

//对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
data: {
activeColor: 'red',
fontSize: 30
}
//数组语法
<div v-bind:style="[styleColor, styleSize]">

data: {
styleColor: {
color: 'red'
},
styleSize:{
fontSize:'23px'
}
}

8 为什么要使用函数式组件,有什么优势

函数组件的特点:

  • 函数式组件需要在声明组件是指定 functional:true。
  • 不需要实例化,所以没有 this,this 通过 render 函数的第二个参数 context 来代替。
  • 没有生命周期钩子函数,不能使用计算属性,watch。
  • 不能通过 $emit 对外暴露事件,调用事件只能通过 context.listeners.click 的方式调用外部传入的事件。
  • 因为函数式组件是没有实例化的,所以在外部通过 ref 去引用组件时,实际引用的是 HTMLElement。
  • 函数式组件的 props 可以不用显示声明,所以没有在 props 里面声明的属性都会被自动隐式。解析为 prop,而普通组件所有未声明的属性都解析到 $attrs 里面,并自动挂载到组件根元素上面(可以通过 inheritAttrs 属性禁止)。

相比普通的类组件,函数组件有如下的一些优势:

  • 由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件;
  • 函数式组件结构比较简单,代码结构更清晰;

Vue.component('functional',{ // 构造函数产生虚拟节点的
functional:true, // 函数式组件 // data={attrs:{}}
render(h){
return h('div','test')
}
})
const vm = new Vue({
el: '#app'
})

9 相比 Vue2.x,Vue 3 有哪些性能方面的提升。

对于这个问题,我们可以从编译阶段、源码体积和响应式系统三个方面进行回答。

在编译阶段,Vue 3 做了如下一些优化:

  • diff 算法优化
  • 静态提升
  • 事件监听缓存
  • SSR 优化
diff 算法优化
vue3 在 diff 算法中相比 vue2 增加了静态标记。关于这个静态标记,其作用是为了会发生变化的地方添加一个 flag 标记,下次发生变化的时候直接找该地方进行比较,性能得到了进一步的提高。

静态提升
Vue3 中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用。
事件监听缓存
默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化。开启了缓存后,没有了静态标记,也就是说下次 diff 算法的时候直接使用。
SSR优化
当静态内容大到一定量级时候,会用 createStaticVNode 方法在客户端去生成一个 static node,这些静态 node,会被直接 innerHtml,就不需要创建对象,然后根据对象渲染。
详细内容参考: https://link.segmentfault.com/?enc=foEWh7S4UgsmFbQ%2FudJ7Ag%3D%3D.iffUI2sb1D1VSXbSt1HiTJBAeqv%2FFBzZx3OLH%2BCRLBnpmdw97LCv0u43kylOMaqcg3YeDU1a6i9E2dm8j%2BzLde%2BOs9JwEpnt1vLUaLStap3shjVUw6vK4VnT6ZT27vls9kmsk%2F0EuPysA9V0KHJh3w%3D%3D

10 vue-router 中如何保护路由

路由保护在应用开发过程中非常重要,几乎每个应用都要做各种路由权限管理,因此相当考察使用者基本功。首先,我们来看一些常见的路由保护实例:
全局守卫:
const router = createRouter({ ... })

router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
路由独享守卫:
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
组件内的守卫:
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
},
}
  • vue-router 中保护路由的方法叫做路由守卫,主要用来通过跳转或取消的方式守卫导航。
  • 路由守卫有三个级别:全局、路由独享、组件级。影响范围由大到小,例如全局的 router.beforeEach(),可以注册一个全局前置守卫,每次路由导航都会经过这个守卫,因此在其内部可以加入控制逻辑决定用户是否可以导航到目标路由;在路由注册的时候可以加入单路由独享的守卫,例如 beforeEnter,守卫只在进入路由时触发,因此只会影响这个路由,控制更精确;我们还可以为路由组件添加守卫配置,例如 beforeRouteEnter,会在渲染该组件的对应路由被验证前调用,控制的范围更精确了。
  • 用户的任何导航行为都会走 navigate 方法,内部有个 guards 队列按顺序执行用户注册的守卫钩子函数,如果没有通过验证逻辑则会取消原有的导航。

11 Vue-router 路由钩子在生命周期的体现

有的时候,需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。为此有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,或者组件级的。

1,全局路由勾子







请到「今天看啥」查看全文