2022面试官系列


 

Vue篇

39.Vue最⼤的特点(核⼼)

1.组件化:就是可以将页面和页面中可复用的元素都看做成组件,写页面的过程,就是写组件,然后页面是由这些组件“拼接“起来的组件树

2.数据驱动:就是让我们只关注数据层,只要数据变化,页面(即视图层)会自动更新,至于如何操作 dom,完全交由 vue 去完成,咱们只关注数据,数据变了,页面自动同步变化了,很方便

40.Vue和JQuery对⽐

JQuery:玩 dom 操作的神器,强大的选择器,分装了好多好用的 dom 操作方法 和 ajax方法

Vue:主要是数据驱动和组件化,很少操作dom(可以用ref)

41.Vue常⻅指令

v-if:根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定/组件被销毁并重建。

v-show:根据表达式之真假值,切换元素的 display CSS 属性。

v-for:循环指令,基于一个数组或者对象渲染一个列表,vue 2.0 以上必须需配合 key 值使用。

v-bind:动态地绑定一个或多个特性/属性,或一个组件 prop 到表达式。

v-on:用于监听指定元素的 DOM 事件,比如点击事件。绑定事件监听器。

v-model:实现表单输入和应用状态之间的双向绑定

v-pre:跳过这个元素和它子元素的编译过程。用来显示原始Mustache标签。跳过大量没有指令的节点会加快编译。

v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

42.v-if和v-for优先级问题

  1. 如果同时出现,每次渲染都会先执行循环在判断条件,无法避免循环,浪费了性能
  2. 在外层嵌套`,在这一层进行v-if然后在内部进行v-for`循环
  3. 如果条件出现在循环内部,可通过计算属性提前过滤不需要显示的项

43.Vue⾃定义指令实现

1.全局自定义指令:Vue.directive(‘指令名’, { inserted(el) { } })

2.局部自定义指令:directives:{ }

44.Vue修饰符的⽤途

修饰符用途:

  1. 通过自定义属性存储变量,避免暴露数据
  2. 防治污染 HTML 结构

45.Vue常⽤修饰符(v-onv-bindv-model)

v-on 指令常用修饰符<@>

.stop - 调用 event.stopPropagation(),禁止事件冒泡。

.prevent - 调用 event.preventDefault(),阻止事件默认行为。

.capture - 添加事件侦听器时使用 capture 模式。

.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。

.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。

.native - 监听组件根元素的原生事件。

.once - 只触发一次回调。

v-bind 指令常用修饰符<:>

.prop - 被用于绑定 DOM 属性 (property)。(差别在哪里?看46)

.camel - (2.1.0+) 将 kebab-case 特性名转换为 camelCase. (从 2.1.0 开始支持)

.sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。

v-model 指令常用修饰符:

.lazy - 取代 input 监听 change 事件

.number - 输入字符串转为数字

.trim - 输入首尾空格过滤

46.v-bind有⽆.prop的差别

v-bind 默认绑定到 DOM 节点的 attribute 上。使用 .prop修饰后,会绑定到 proterty

使用 property 获取最新的值 attribute 设置的自定义属性会在渲染后的 HTML 标签显示,property 不会

.prop,``

标签结构:``

input.data === undefined
input.attributes.data === this.inputData

.prop,``

标签结构:``

input.data === this.inputData
input.attributes.data === undefined

47.Vue中methods、computed和watch的区别

  1. Methods: 中都是封装好的函数,无论是否有变化只要触发就会执行
  2. Computed: 是vue独有的特性计算属性,可以对data中的依赖项再重新计算, 得到一个新值,应用到视图中,和 methods 本质区别是 computed 是可缓存的, 也就是说 computed 中的依赖项没有变化,则 computed 中的值就不会重新计算, 而 methods 中的函数是没有缓存的。
  3. Watch: 是监听 data 和计算属性中的新旧变化。 computed 和 watch 都是观察页面的数据变化的。数据变化时执行异步操作或开销较大的操作,这个时候使用 watch 是合适的

48.Vue单项数据流

单向数据流主要是 vue 组件间传递数据是单向的,数据总是从父组件传递给子组件,子组件在其内部维护自己的数据,但它没有权限修改父组件传递给它的数据,当尝试这样做的时候vue 将会报错。这样做是为了组件间更好的维护。

在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以vue 不推荐子组件修改父组件的数据

49.Vue路由(前端路由的实现原理)

第一种:利用 H5 的 history API 实现:主要通过 history.pushStatehistory.replaceState 来实现,不同之处在于,pushState 会增加一条新的历史记录,而 replaceState则会替换当前的历史记录[发布项目时,需要配置下 apache]

this.$router.push(location, onComplete?, onAbort?)` 点击 `` 等同于调用 `router.push(...)
this.$router.replace(...)` 点击 `` 等同于调用 `router.replace(...)

第二种:利用 url 的 hash(#)实现:主要利用监听哈希值的变化来触发事件hashchange 事件来做页面局部更新

50.Vue路由守卫(钩⼦)和路由中间件

Vue 路由守卫(钩子):跳转过程中拦截当前路由和要跳转路由的信息,处理页面访问权限

全局:beforeEach (to, from, next)

组件内:beforeRouteEnter(to, from, next)

在同构渲染下,页面的拦截从服务端角度是在进入页面前就需要处理页面访问的,可以使用 路由中间件 处理,它允许定义一个自定义函数运行在一个页面或一组页面渲染前。既处理服务端渲染的路由拦截又处理客户端渲染的拦截

/**
 * 验证是否登录的中间件
 */
​
// 每一个中间件应放置在 middleware 目录,文件名的名称即为中间件名
export default function ({ store, redirect }) {
  // If the user is not authenticated
  if (!store.state.user) {
    return redirect('/login')
  }
}
​
// 使用
export default {
  ...
  // 在路由匹配组件渲染之前会先执行中间件处理
  middleware: ['authenticated']
}

51.Vue过滤器(0,1->男⼥)

跟创建自定义指令类似,也有全局和局部过滤器的形式

全局过滤器:Vue.filter(‘过滤器名’,function(参数 1,参数 2,…) { ... return 要返回的数据格式 })

局部过滤器:在组件内部添加 filters 属性来定义过滤器

fitlers:{ 过滤器名(参数 1,参数 2,,…参数 n) { ... return 要返回的数据格式 } }

52.Vue组件通讯(传值)的四种形式及实现

第一种:父传子:主要通过 props 来实现

父组件通过 import 引入子组件,并注册( components:{} ),在子组件标签上添加要传递的属性,子组件通过 props 接收,接收有两种形式: 一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{ }来接收,对象形式可以设置要传递的数据类型和默认值,而数组只是简单的接收

第二种:子传父:主要通过 $emit 来实现

子组件通过通过绑定事件触发函数,在其中设置this.$emit(‘要派发的自定义事件’,要传递的值),然后父组件中,在这个子组件身上@(v-on)派发的自定义事件,绑定事件触发的methods 中的方法接受的默认值,就是传递过来的参数,或者直接使用 @event 接收参数

第三种:兄弟之间传值有两种方法:

方法一:通过 event bus 实现

创建一个空的 vue 作为事件总线/事件中心 并暴露出去,这个作为公共的 bus,即当作两个组件的桥梁,在两个兄弟组件中分别引入刚才创建的 bus,在组件 A 中通过bus.$emit(’自定义事件名’,要发送的值)发送数据,在组件 B 中通过 bus.$on(‘自定义事件名‘,function(v) { //v 即为要接收的值 })接收数据

方法二:通过 vuex 实现

vuex 是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括 state,actions,mutations,getters 和 modules 5 个要素,主要流程:组件通过 dispatch 到 actions,actions 是异步操作,再 actions中通过 commit 到 mutations,mutations 再通过逻辑操作改变 state,从而同步到组件,更新其数据状态.而 getters 相当于组件的计算属性对,组件中获取到的数据做提前处理的.再说到辅助函数的作用.

第四种:通过 ref 获取子组件

把它作用到普通 HTML 标签上,则获取到的是 DOM 对象

把它作用到组件标签上,则获取到的是组件实例对象

在使用子组件的时候,添加 ref 属性,然后在父组件等渲染完毕后使用 $refs 访问($refs 只会在组件渲染完成之后生效,并且它们不是响应式的)

53.如何解决Vue⾸屏加载慢或⽩屏(性能优化)

  1. 合理使用 v-ifv-showv-for 遍历为 item 添加 key,避免同时使用 v-if
  2. 区分 computed 和 watch 的使用
  3. addEventListner添加的事件在组件销毁时用removeEventListner移除监听
  4. 路由懒加载 const test = () => import(‘@/...’))
  5. 开启 Gzip 压缩 config index.js module.exports = {...:{productionGzip: false}}
  6. 使用 webpack 的 externals 属性把不需要打包的库文件分离出去,减少打包后文件的大小,优化 Source Map
  7. 使用 vue 的服务端渲染(SSR),首屏快,SEO好
  8. 网络加载方面的优化:减少http请求(合并文件、CSS雪碧图、图片路由 懒加载),减少dom操作(dom 缓存),代码封装
  9. 第三方库用 cdn 加载(OSS优化),按需引入

54.聊聊SSR服务端渲染

一份代码,服务端先通过 server-side-rendering 生成 html 以及初始化数据(客户端SPA),客户端拿到代码后,通过对 html 的 dom 进行 path 和事件绑定对 dom 进行客户端激活。接管服务端渲染的内容把它激活为一个动态页面

  1. 客户端发起请求
  2. 服务端渲染首屏内容 + 生成客户端 SPA 相关资源
  3. 服务端将生成的首屏资源发送给客户端
  4. 客户端直接展示服务端渲染好的首屏内容
  5. 首屏中的 SPA 相关资源执行之后会激活客户端 Vue
  6. 之后客户端所有的交互都由客户端 SPA 处理

55.Vue响应式(双向数据绑定)原理(发布订阅模式)

Vue.js 2.x 中响应式系统的核心 defineProperty

初始化时遍历 data 中的所有成员,通过 defineProperty 把对象的属性转换成 getter 和 setter,如果 data 中的属性又是对象的话,需要递归处理每一个子对象的属性。这些都是初始化时进行的,如果你、未使用这些属性也会进行响应式的处理

Vue.js 3.x 中使用 Proxy 对象重写响应式系统

1.Proxy 的性能本身就比 defineProperty 好,且代理对象可以拦截属性的访问、赋值、删除等操作,不需要初始化时遍历所有的属性,如果有多层属性嵌套只有访问某个属性时才会递归处理下一级属性

2.使用 Proxy 对象默认可以监听动态新增的属性,而 Vue.js 2.x 想要动态添加响应式属性需要调用 Vue.set 方法来处理

3.Vue.js 2.x 监听不到属性的删除

4.Vue.js 2.x 对数组的索引和 length 属性也监听不到

56.观察者模式和发布订阅模式

Vue 响应式原理通过数据劫持结合 发布订阅者模式来实现

观察者模式:

由具体目标调度:比如当事件触发,Dep就会调用观察者的方法(订阅者和发布者之间是存在依赖的)

发布订阅模式:

由统一调度中心调用,发布者和订阅者不需要知道对方的存在

在 new Vue的时候:在Observer中通过Object.defineProperty()达到数据劫持,代理所有数据的gettersetter属性,在每次触发setter的时候会通过Dep来通知Watcher,Watcher作为Observer数据监听器与Compiler模板解析器之间的桥梁。当Observer监听到数据发生改变时,通过Updater来通知Compiler更新视图,而Compiler通过Watcher订阅对应数据,绑定更新函数,通过Dep来添加订阅者

57.Vue如何动态添加属性,实现数据相应

当创建好 Vue 实例后,新增一个成员,此时 data 并没有定义该成员,data 中的成员是在创建 Vue 对象的时候 new Observer 来将其设置成响应式数据,当 Vue 实例化完成之后,再添加一个成员,此时仅仅是给 vm 上增加了一个js属性而已,因此并不是响应式的

可以使用 Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性。还可以使用vm.$set实例方法,这也是全局Vue.set方法的别名。

原理:defineReactive(ob.value, key, val) 给新加的属性添加依赖,以后再直接修改这个新的属性的时候就会触发页面渲染。ob.dep.notify() 触发当前的依赖(这里的依赖依然可以理解成渲染函数),所以页面就会进行重新渲染

58.虚拟DOM中dif算法

Vue 2.x 内部使用的 Virtual DOM 就是改造的 Snabbdom

虚拟DOM是如何实现的:虚拟DOM是通过js语法来在内存中维护一个通过数据结构描述出来的一个模拟DOM树,当数据发生改变的时候,会先对虚拟DOM进行模拟修改,然后再通过新的虚拟DOM树与旧的虚拟DOM树来对比,而这个对比就是通过diff算法来进行的.虚拟DOM最大的意义不在于性能的提升(JavaScript对象比DOM对象性能高),而在于抽象了DOM的具体实现(对DOM进行了一层抽象)

  1. 首先对根元素进行对比,如果根元素发生改变就直接对根元素替换
  2. 如果根元素没有发生改变的话,再对下一层元素进行对比,如果对比发现元素发生删除,就执行删除,发现元素被替换就执行替换,发现添加了新的元素就执行添加
  3. 对比的同时,会通过key值来判断元素是否发生改变,判断元素是仅仅位置发生改变还是需要整个替换或删除
  4. 如果不是元素发生改变的话,再对内容进行对比,如果是内容发生改变的话,就直接修改内容
  5. 其实就是进行逐层对比,再通过不同的对比来判断执行不同的操作

虚拟DOM最大的意义不在于性能的提升(JavaScript对象比DOM对象性能高),而在于抽象了DOM的具体实现(对DOM进行了一层抽象)

59.虚拟DOM中key的作⽤和好处

作用:追踪列表中哪些元素被添加、被修改、被移除的辅助标志。可以快速对比两个虚拟DOM对象,找到虚拟DOM对象被修改的元素,然后仅仅替换掉被修改的元素,然后再生成新的真实DOM

好处:可以优化 DOM 的操作,减少Diff算法和渲染所需要的时间,提升性能

Vue Vuex篇

60.异步更新队列nextTick的作⽤是使⽤场景

Vue 更新 DOM 是异步执行的、批量的,nextTick 就是在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

例如:有一个 div,默认用 v-if 将它隐藏,点击一个按钮后,改变 v-if 的值,让它显示出来,同时拿到这个 div 的文本内容。如果 v-if 的值是 false,直接去获取 div 内容是获取不到的,因为此时 div 还没有被创建出来,那么应该在点击按钮后,改变 v-if 的值为 true,div 才会被创建,此时再去获取,

Vue 在观察到数据变化时并不是直接更新 DOM,而是开启一个队列,并缓冲在同一个事件循环中发生的所有数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和 DOM 操作。然后,在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。Vue 会根据当前浏览器环境优先使用原生的 Promise.thenMutationObserver,如果都不支持,就会采用 setImmediate(宏任务队列) setTimeout 代替

61.Vue中keep-alive组件的作⽤(避免重新渲染)及使用场景

主要用于保留组件状态或避免重新渲染。属性:

include:字符串或正则表达式。只有匹配的组件会被缓存。

exclude:字符串或正则表达式。任何匹配的组件都不会被缓存。

包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。当组件在 ` 内被切换,在 2.2.0 及其更高版本中,activateddeactivated`生命周期 将会在 树内的所有嵌套组件中触发

场景:

  • Vue中前进刷新,后退缓存用户浏览数据
  • 列表页面 =>点击进入详情页=> 后退到列表页 要缓存列表原来数据
  • 重新进入列表页面 => 获取最新的数据

62.Vuex如何实现数据持久化

因为 vuex 中的 state 是存储在内存中的,一刷新就没了,例如登录状态,解决方案有:

第一种:利用 H5 的本地存储:localStorage, sessionStorage,不会自动把数据发送给服务器

第二种:利用第三方封装好的插件,例如:vuex-persistedstate

第三种:使用 js-cookie cookieparse(将Cookie字符串解析为一个对象) 插件来做存储

如果是现代化服务端渲染,不能放到本地存储,因为本地存储只有客户端可以获取到,我们希望数据既能被客户端获取又能被服务端获取。因此我们将数据放到cookie,这样前后端都能获取。

客户端加载 js-cookie 包

// process.client 是Nuxt中特殊提供的数据,运行在客户端为 true; 运行在服务端为 false
const Cookie = process.client ? require('js-cookie') : undefined
​
// 为了防止刷新页面数据丢失,需要将数据持久化
Cookie.set('user', data.user)
​
// 服务端渲染期间 nuxtServerInit 是一个特殊的 action 方法 
// commit:用来提交 mutation 的 commit 方法; req:服务端渲染期间的 request 请求对象
nuxtServerInit ({ commit }, { req }){
  const parsed = cookieparser.parse(req.headers.cookie)
  user = JSON.parse(parsed.user) // 转为对象
  commit('setUser', user)
}

第四种:可以把数据传递到后台,存储到数据库中,比较耗费资源

63.Cookie、localStorage、sessionStorage

CookielocalStoragesessionStorage
数据生命周期一般由服务器生成,可设置失效时间。如果在浏览器生成,默认关闭后失效除非被消除,永久保存仅在当前会话下有小,关闭页面失效
大小4KB5MB5MB

cookie 最开始被设计出来并不是来做本地存储的。而是为了弥补HHTP请求在状态管理上的不足。HTTP为无状态协议,客户端向服务端发请求,服务器返回响应,下次发请求让服务端知道客户端是谁

cookie 是网站为了标识用户身份而存储在用户本地终端上的数据(通常加密),cookie始终在同源的http请求中携带(即使不需要)

64.Vue中的http请求如何管理

vue 中的 http 请求如果散落在 vue 各种组件中,不便于后期维护与管理,所以项目中通常将业务需求统一存放在一个目录下管理,这里面放入组件中用到的所有封装好的 http 请求并导出,再其他用到的组件中导入调用

export function xxx() {
  return request({
    url: '/api',
    method: 'GET'
  })
}

65.Axios拦截器(封装请求模块、对公共数据做操作)

axios 拦截器可以让我们在项目中对后端 http 请求和响应自动拦截处理,减少请求和响应的代码量,提升开发效率同时也方便项目后期维护

/**
 * 基于 axios 封装的请求模块
 */
​
const request = axios.create({
  baseURL: 'https://conduit.productionready.io/'
})
​
export default request

66.Vue 跨域相关问题

不同协议、不同域名、不同端口都会造成跨域。由于开发服务器的缘故,我们将应用运行在 loaclhost 的一个端口上面,而最终上线过后,一般又和 API 部署到同源地址下面

这就会产生一个非常见问题:实际生产当中可以直接访问API,但是回到开发环境就会产生跨域请求问题

我最推荐的⽅式就是: CORS 全称为 Cross Origin Resource Sharing(跨域资源共享)。这种⽅案对于前端来说没有什么⼯作量,和正常发送请求写法上没有任何区别,⼯作量基本都在后端这⾥。每⼀次请求,浏览器必须先以 options 请求⽅式发送⼀个预请求(也不是所有请求都会发送 options),通过预检请求从⽽获知服务器端对跨源请求⽀持的 HTTP ⽅法。在确认服务器允许该跨源请求的情况下,再以实际的 HTTP 请求⽅法发送那个真正的请求。推荐的原因是:只要第⼀次配好了,之后不管有多少接⼝和项⽬复⽤就可以了,⼀劳永逸的解决了跨域问题,⽽且不管是开发环境还是正式环境都能⽅便的使⽤

但总有后端觉得麻烦不想这么搞,那纯前端也是有解决⽅案的:

dev 开发模式下可以下使⽤ webpack 的 proxy,但这种⽅法在⽣产环境是不能使⽤的,在⽣产环境中需要使⽤ nginx 进⾏反向代理。不管是 proxy 还是 nginx 的原理都是⼀样的,通过搭建⼀个中转服务器来转发请求规避跨域的问题

如果只针对vue本身可以通过代理的方式可以实现:在 config中的index.js中配置prox

module.exports = {
  ...
  devServer: {
    proxy: {
      '/boss': {
        target: 'http://eduboss.lagou.com',
        // changeOrigin: true 以实际代理请求的主机名请求
        // 设置请求头中的 host 为 target,防⽌后端反向代理服务器⽆法识别
        changeOrigin: true
      }
    }
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值