2023 vue面试题部分总结

147 阅读9分钟

vue3 vue2 区别

使用方面:

  1. 选项式API 代替组合式APi
  2. 组合式API 没有this
  3. 生命周期没有create ,setup 等同于create,卸载改成unmount
  4. V-if 优先级高于v-for
  5. 根实例的创建从new app 变成 create App
  6. 一些全局注册,比如mixin ,注册全局组件,use改成了用app 实例调用,而不是vue 类调用
  7. 新增了传送门teleport 组件
  8. template模版可以不包在一个根div 中
  9. 新增watchEffect
  10. shallowReactive 只在第一次加监听
  11. shallowReadonly

原理方面:

  1. object.defineProperty 改成 proxy ,解决数组无法通过下标修改,无法监听到对象属性新增修改删除问题。提高响应书效率

  2. vue3 并没有完全抛弃 defineProperty ,ref 是通过 defineProperty 去给一个空对象,定义的一个value属性来做响应式

    reactive 是通过 proxy包装出来的

  3. 组合式API 的写法,源码改成了函数式编程,方便按需引入

  4. 性能优化,增加了静态节点标记。会标记静态节点,diff对比不对静态节点做对比,从而增加效率

    1. 标记策略:对动态的值加上标记,值标1,动态class 标记2

进阶方面:

  1. vue3 不推荐使用mixin 进行复用逻辑提取,而是推荐使用hook

  2. v-model 应用与组件时,监听的事件和传递的值改变

    1. vue2 中 value 监听 change/input 事件
    2. Vue3 中自定义事件 modelValue 监听update:modelValue
  3. Ts 更好的配合

vue v-show v-if 理解

v-if 直接操作的DOM(v-show隐藏则是为该元素添加css--display:nonedom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除)

v-if 触发生命周期: 由false变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子

true变为false的时候触发组件的beforeDestorydestoryed方法

v-if v-for 为什么不建议一起使用

v-for优先级比v-if

  1. 永远不要把 v-ifv-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
  2. 在 vue 2.x 中,在一个元素上同时使用 v-if 和 v-for 时, v-for 会优先作用。 在 vue 3.x 中, v-if 总是优先于 v-for 生效

注意事项:

  1. 永远不要把 v-ifv-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)

  2. 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环

    <template v-**if**="isShow"> <p v-for="item in items"> </template>

  3. 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项

    computed: {
        items: function() {
          return this.list.filter(function (item) {
            return item.isShow
          })
        }
    }
    

SPA 手屏加载速度慢怎么解决

  1. 什么是首屏加载

    指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容

  2. 首屏加载出现的原因

    • 网络延时问题
    • 资源是否重复发送请求去加载了
    • 资源文件体积是否过大
    • 加载脚本的时候,渲染内容堵塞了
  3. 解决方案

常见的几种SPA首屏优化方式

  • 静态资源本地缓存

  • 减小入口文件积

  • UI框架按需加载

  • 图片资源的压缩

  • 组件重复打包

  • 开启GZip压缩

  • 使用SSR


    减少入口文件体积

    常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加

vue-router配置路由的时候,采用动态加载路由的形式

以函数的形式加载路由,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会加载路由组件

routes:[ path: 'Blogs', name: 'ShowBlogs', component: () => import('./components/ShowBlogs.vue') ]

静态资源本地缓存

后端返回资源问题:

  • 采用HTTP缓存,设置Cache-ControlLast-ModifiedEtag等响应头
  • 采用Service Worker离线缓存

前端合理利用localStorage

UI框架按需加载

在日常使用UI框架,例如element-UI、或者antd,我们经常性直接引用整个UI

import ElementUI from 'element-ui' Vue.use(ElementUI)

但实际上我用到的组件只有按钮,分页,表格,输入与警告 所以我们要按需引用

import { Button, Input, Pagination, Table, TableColumn, MessageBox } from 'element-ui';
Vue.use(Button)
Vue.use(Input)
Vue.use(Pagination)

或者

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from  './router'
import { Button,Row,DatePicker } from 'element-ui';
Vue.config.productionTip = falseVue.use(VueRouter)
Vue.component(Button.name,Button)
Vue.component(Row.name,Row)
Vue.component(DatePicker.name,DatePicker)
​
new Vue({
  render: h => h(App),
  router
}).$mount('#app')
组件重复打包

假设A.js文件是一个常用的库,现在有多个路由使用了A.js文件,这就造成了重复下载

解决方案:在webpackconfig文件中,修改CommonsChunkPlugin的配置

minChunks: 3

minChunks为3表示会把使用3次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件

图片资源的压缩

对页面上使用到的icon,可以使用在线字体图标,或者雪碧图,将众多小图标合并到同一张图上,用以减轻http请求压力。

开启GZip压缩

使用SSR

SSR(Server side ),也就是服务端渲染,组件或页面通过服务器生成html字符串,再发送到浏览器

从头搭建一个服务端渲染是很复杂的,vue应用建议使用Nuxt.js实现服务端渲染

为什么data属性是一个函数不是对象

组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。

采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象

vue组件中添加新属性不刷新

vue history模式下刷新404及上线后的代理问题

vue.config.js设置 publicPathhistoryApiFallback

vue 404 前端配置

但是上线后,问题又来了

怎么办?只需在nginx.conf中添加参数配置即可。

location / {
         try_files $uri $uri/ /index.html$args;
        }

修改完配置文件后记得配置的更新

nginx -s reload

原因分析

产生问题的本质是因为我们的路由是通过JS来执行视图切换的,

当我们进入到子路由时刷新页面,web容器没有相对应的页面此时会出现404

所以我们只需要配置将任意页面都重定向到 index.html,把路由交由前端处理

配置proxy后导致页面报错

很多小伙伴上线后配置了跨域代理,但是发现接口返回的并不是想要的数据,还是大块的html,这时候大部分情况下是因为proxy参数没配好导致的。

  '/api': {
        //这里最好有一个 /
        target: 'https://api.thecatapi.com/v1/', // 线上服务器端接口地址
        ws: false, //如果要代理 websockets,配置这个参数
        // 如果是https接口,需要配置这个参数
        changeOrigin: true, //是否跨域
        historyApiFallback: {
          index: '/index.html', // 与output的publicPath
        },
        pathRewrite: {
          '^/api': '/',
        },
      },
    },
————————————————
版权声明:本文为CSDN博主「Boriska1996」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/RequesToGod/article/details/126284395

使用代理,首先需要有一个标识,告诉程序这个连接要使用代理,不然的话,可能你的html、css、js、矢量图等静态资源都跑去代理,从而导致返回的是一个html,而不是你想要的数据。所以我们要通过一个唯一标识,让接口使用代理,静态资源文件使用本地。 proxy中的 ‘/api’:{······},就是告诉node,我的接口是要以 /api 开头的才使用代理。所有的接口都要写成 /api/xx/xx ,以 /api 开头,最后代理的接口路径路径就是 http://localhost:8080/api/xx/xx

Element-ui 二次封装 怎么全局用,不全局怎么单独引用

全局引入

(1)在入口文件main.js引入所有自定义封装的组件

通过 import 引入全局组件,注意引入的路径一定要正确

import Button from './components/button'
// 注册全局组件
Vue.component('button', Button)
// 第一个参数 全局组件的名字(字符串类型),第二个参数:哪一个组件(引入的组件名)

局部引入

引入时的名字 一般都与组件名保持一致,components的写的就是书写组件的标签

import Button from '@/components/button'
export default {
   components: {
     Button
   },
}

Element-ui 全局引入,部分引入

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

部分引入

// 在Script标签中导入Button组件
import { Button } from 'element-ui'
export default {
components: {
'el-button': Button
}
}

生命周期

数据请求在created和mouted的区别

created是在组件实例一旦创建完成的时候立刻调用,这时候页面dom节点并未生成;mounted是在页面dom节点渲染完毕之后就立刻执行的。触发时机上created是比mounted要更早的,两者的相同点:都能拿到实例对象的属性和方法。 讨论这个问题本质就是触发的时机,放在mounted中的请求有可能导致页面闪动(因为此时页面dom结构已经生成),但如果在页面加载前完成请求,则不会出现此情况。建议对页面内容的改动放在created生命周期当中。

通信方式

  • 父子组件:props/$emit/$parent/ref
  • 兄弟组件:$parent/eventbus/vuex
  • 跨层级关系:eventbus/vuex/provide+inject/$attrs + $listeners/$root

小结

  • 父子关系的组件数据传递选择 props$emit进行传递,也可选择ref
  • 兄弟关系的组件数据传递可选择$bus,其次可以选择$parent进行传递
  • 祖先与后代组件数据传递可选择attrslisteners或者 ProvideInject
  • 复杂关系的组件数据传递可以通过vuex存放共享的变量

缓存

Vue 2 是 路由包着 keep-alives

Vue3 是keep-alive 包着路由

include 缓存哪些路由地址 ; exclude 不缓存哪些

watch 原理

watch 本质上是为每个监听属性 setter 创建了一个 watcher,当被监听的属性更新时,调用传入的回调函数。常见的配置选项有 deepimmediate,对应原理如下

  • deep:深度监听对象,为对象的每一个属性创建一个 watcher,从而确保对象的每一个属性更新时都会触发传入的回调函数。主要原因在于对象属于引用类型,单个属性的更新并不会触发对象 setter,因此引入 deep 能够很好地解决监听对象的问题。同时也会引入判断机制,确保在多个属性更新时回调函数仅触发一次,避免性能浪费。
  • immediate:在初始化时直接调用回调函数,可以通过在 created 阶段手动调用回调函数实现相同的效果

v-model 的原理

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

v-for vi-if

vue2 v-for>v-if

Vue3 v-if>v-for

nextTick 原理

为什么Vue采用异步渲染

核心是nextTick

vue 3

juejin.cn/post/713992…

选项式 组件式区别

逻辑组织上2偏碎片化,3将逻辑关注点都放一个函数里

逻辑复用上,hooks取代mixin 命名冲突,数据来源不明

Tree-shaking 代码压缩

见不到this 使用,减少this 指向不明情况

Vue3 性能上提升

  1. 编译阶段

    1. diff 优化----增加了静态标记,在会发生变化处添加标记,下次发生变化直接找到该地方

    2. 静态提升----不参与更新的元素做静态提升,只会被创建一次,在渲染时直接复用。

      做了静态提示后,被放置render函数外,每次直接取出。该元素被打上静态标记-1。负数代表永远不会diff

    3. 事件监听缓存

      1. 没开启事件监听器缓存,默认情况绑定事件被视为动态绑定,开启缓存后没有了静态标记,diff算法时直接用
    4. ssr优化 ----当静态内容一定量级时会用createStaticVNode方法在客户端生成一个static node ,这些静态node 会被直接 innerHtml,就不需要创建对象,然后根据对象渲染

  2. 源码体积

    1. Tree shanking 。没用到的函数变量模块等,会被摇掉,打包整体体积变小
  3. 响应式系统

    1. proxy Reflect 结合对整个对象进行监听

vue3 treeShanking 特性

清楚多余代码优化打包体积

如何做的?基于es6 import export.主要是es6 的模块的静态编译思想,在编译时就能确定依赖关系,以及输入输出

好处?体积小,执行快,

vue3 新特性有哪些

  1. 性能提升

  2. 支持t s

  3. 新增组建式 setup()函数

  4. 新增组件

    1. fragment
    2. teleport
    3. Suspense 等待异步时渲染一些额外的内容,提高用户体验
  5. Tree shaking 支持摇树优化

  6. Custom Render API :自定义渲染器。实现DOM 的方式进行WebGL编程

vue3 升级了哪些重要功能

新的API:Vue3使用createApp方法来创建应用程序实例,并有新的组件注册和调用方法。

emits属性

生命周期

多个Fragment

移除 .sync

异步组件的写法

const Foo = defineAsyncComponent(() => import('./Foo.vue') )

vue3 生命周期有哪些变化

Setup 取代 beforeCreate Created

Setup onbeforeMounted onMounted onBeforeUpdate onUpdated onBeforeUmount onUnmounted onErrorCaptured onRenderTracked onRenderTriggered onActivated onDeactivated

watch watchEffect 区别

Watch 能访问改变前后的值,watchEffect只能获取改变后的值

Watch不会立即执行,watchEffect会立即执行

watchEffect 有点像 computed

computed必写返回值重结果。watchEffect重过程可无返回值

setup 到底干啥的

Vue3 语法糖,属性,方法无需返回直接使用

引入组件自定注册,无需components手动注册

defineProps 接父组件传的值

useAttrs获取属性,useSlots获取插槽,defineEmits 获取自定义事件

默认不会对外暴露任何属性,如果有需要可使用defineExpose

vue3 通信方式

props

$emit

expose/ref

atrrs

V-model

Provide/inject(原理:原型链)

Vuex/pinia

Mitt

vue2 通信方式

props

$emit/v-on

.sync

v-model

ref

Children/parent

Attrs/listeners

Provide/inject

EventBus

Vuex

$root

Slot

ref 与 reactive 区别

Ref 可接受原始数据类型,引用类型

reactive 函数只能接受引用类型

Ref 底层还是 reactive

EventBus mitt 区别

优点 :

非常小,压缩后仅200 bytes

支持t s

跨框架 react。 JQ 等

使用简单仅有 on,emit,off 等少量实用API

谈谈pinia

优点:

更加轻量级 ,,压缩后提交只有1.6KB

源码全由TS编写完成

移除mutations ,只剩state, action ,getters

有store 概念,但都相互隔离的

无需手动添加每个store ,它的模块自定注册

支持服务端渲染SSR

更加优化的代码分割机制,传送门

setup 中如何获取组件实例

getCurrentInstance() 方法来获取组件实例。方法返回一个对象,对象里包含组件实例以及其他相关信息。

如何理解toRef toRefs

当我们从 setup 函数返回一个响应式对象时,对象的属性将失去响应式。为了防止这种情况,我们可以使用 toRefstoRef 函数。

toRef :

Suspense

Suspense 组件允许你等待一个或多个异步组件,然后显示一些备用内容,直到所有的异步组件都被解析。

链接:juejin.cn/post/726007…

effect、reactive、ref

import { effect, reactive, ref } from 'vue'
​
// 使用 reactive
const state = reactive({ a: 1 })
​
effect(() => {
  console.log(state.a)
})
​
state.a = 2 // 打印 "2"// 使用 ref
const count = ref(0)
​
effect(() => {
  console.log(count.value)
})
​
count.value++ // 打印 "1"

在这个示例中,我们创建了一个响应式对象和一个 ref,然后使用 effect 创建了两个副作用,它们分别打印出对象和 ref 的值。当这些值被改变时,副作用就会被触发。

render 函数

Render 函数的工作原理是通过返回一个虚拟节点(VNode)来告诉 Vue 如何渲染界面。Vue 3 提供了 h 函数用于创建 VNode。

import { h } from 'vue'export default {
  render() {
    return h('div', {}, 'Hello, world!')
  }
}