IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> VUE面试题-2022 -> 正文阅读

[JavaScript知识库]VUE面试题-2022

一. $nextTick使用以及原理

A: 使用: 它可以在DOM更新完毕之后执行一个回调;
原理: 先来了解下,浏览器的机制宏任务微任务

宏任务(Macrotask):
setTimeout,setInterval ,script(整体代码块) ,MessageChannel
I/O,事件队列
微任务(Microtask) :MutationObserver(浏览器环境),Promise.[ then/catch/finally ], process.nextTick(Node环境)

1.1、$nextTick有什么用?

Vue是异步渲染的框架。
data改变之后,DOM不会立刻渲染。
n e x t T i c k 会在 D O M 渲染之后被触发,以获取最新的 D O M 节点。连续多次的异步渲染, nextTick会在DOM渲染之后被触发,以获取最新的DOM节点。 连续多次的异步渲染, nextTick会在DOM渲染之后被触发,以获取最新的DOM节点。连续多次的异步渲染,nextTick只会执行最后一次渲染后的结果。

1.2、$nextTick的原理

$nextTick: 异步执行传入的回调函数(是通过事件循环中的任务队列的方式)。

  1. data数据更新 => 生成新的虚拟dom => diff算法比较新旧虚拟dom => patch更新变化的虚拟dom到真实的dom上 => 触发更新回调。
  2. diff算法比较新旧虚拟dom, 比较后,在patch更新虚拟到真实dom之前,触发this.$nextTick。
    (vue中更新DOM的方法也是通过nextTick进行调用的)。
  3. 触发this.$nextTick后,虚拟dom更新到真实dom上之前,开始处理里面的回调。
    这个时候会判断下当前环境(浏览器)支持哪种任务:Promise,MutationObserver,setImmediate,setTimeout。
  4. 如果支持promise(走promise): patch更新变化的虚拟dom到真实的dom上,然后.then开始处理里面的回调函数;
    如果走MutationObserver,patch更新变化的虚拟dom到真实的dom上,然后开始处理里面的回调函数;
    如果走setTimeout, patch更新变化的虚拟dom到真实的dom上,然后开始处理里面的回调函数

代码执行步骤:

//data数据更新 => 生成新的虚拟dom =>  diff算法比较新旧虚拟dom => patch更新变化的虚拟dom到真实的dom上 =>  触发更新回调
    const fun = () =>{
            console.log("DOM更新完成了")
        }
        this.$nextTick(fun)

        //判断支持的异步方法
        //1、promise
        new Promise((reslove) => {
            // patch更新变化的虚拟dom到真实的dom上
        }).then(() => {
            fun()
        });
        //2、MutationObserver(用的是它的微任务特性)
        // patch更新变化的虚拟dom到真实的dom上
        new MutationObserver(() => {
            fun()
        });
        //3、setTimeout
        // patch更新变化的虚拟dom到真实的dom上
        setTimeout(() => {
            fun()
        })

1.3、循环调用的话nextTick里面有容错机制吗?

多次调用 nextTick 会将方法存入队列 callbacks 中,通过这个异步方法清空当前队列。

二. VUE3中watch与watchEffect

2.1、 watchEffect
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

watchEffect的一些特点:

不需要手动传入依赖(不用指定监听对象)
无法获取原始值,只能获取更新后的值
立即执行(在onMounted前调用)
一些异步操作放里面更加的合适
watchEffect第一个参数是一个箭头函数(是一个副作用函数),里面用来获取侦听到的新值以及用来进行其它操作(比如清除副作用等)

import { watchEffect } from 'vue'
const count = ref(0)

watchEffect(() =>{
 	console.log(count.value)
 	// -> logs 0
})

setTimeout(() => {
  count.value++
  // -> logs 1
}, 1000)

进入页面,直接打印结果:1

watch监听的一些特点:
监听单一数据,监听多个数据,监听基本数据,监听对象属性;

watch监听基本数据类型:

<template>
    <div>
        <h1 @click = "changeTitle">{{title}}</h1>
    </div>
</template>
<script>
export default {
    data(){
        return {
            title:'标题',
        }
    },
    methods:{
        changeTitle(){
            this.title += 'kk'
        },
    },
    watch:{
        // 监听基本数据类型
        title(oldval, newval){
            console.log('oldval', oldval)
            console.log('newval', newval)
        },
    }
}
</script>

监听对象中的属性:

<template>
         <h1 @click = "changeTitleObj">{{obj.a}}</h1>
</template>
<script>
export default {
    data(){
        return {
            obj:{
                a:'对象中的属性'
            }
        }
    },
    methods:{
        changeTitleObj(){
            this.obj.a += '22'
        }
    },
    watch:{
        // 监听对象--这样子是监听不到的
        obj(newval){
            console.log('对象newval', newval)
        },
        // 深度监听方法1-- 使用handler,deep
        obj:{
            handler(oldval, newval){
                console.log('对象深度oldval', oldval)
            console.log('对象深度newval', newval)
            },
            deep: true,
        },
        // 深度监听方法2-- 直接取用对象的属性
        'obj.a' (val){
            console.log('val', val)
        }
    }
}
</script>

VUE3中watch与watchEffect —— 全网最详细系列

三. Ref reactive区别,底层怎么实现响应式的

reactive与ref对比
从定义数据角度对比:

  • ref用来定义:任意数据类型
  • reactive用来定义:对象(或数组)类型数据

如何选择 ref 和 reactive?

  • 基础类型值(String,Number,Boolean,Symbol) 或单值对象(类似{ count: 1 }这样只有一个属性值的对象) 使用 ref
  • 引用类型值(Object、Array)使用 reactive

从原理角度对比:

ref通过Object.defineProperty()的get和set来实现响应式(数据劫持)。

reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据

从使用角度对比:

  • ref定义的数据:访问或更改数据需要.value
  • reactive定义的数据:操作数据与读取数据均不需要.value。

toRef
针对一个响应式对象(reactive封装)的prop(属性)创建一个ref,且保持响应式

两者保持引用关系

语法:const 属性名= toRef(对象,‘属性名’)

toRefs
toRefs 是一种用于破坏响应式对象并将其所有属性转换为 ref 的实用方法

将响应式对象(reactive封装)转成普通对象

对象的每个属性(Prop)都是对应的ref

两者保持引用关系

语法:const 属性名= toRefs(对象,‘属性名’)

四. vue监听数组

Vue2为什么不能监测数组的变化
首先从表象上来看,Vue2对数组的响应式实现是有些不足的:

  • 无法监测数组的新增
  • 无法监测用索引改变数组的操作
    先来简单分析下为什么会存在上述问题:
    我们知道Vue2是通过Object.defineProperty方法来进行数据监测的。
    从上述结果可以看出,Object.defineProperty是可以对数组进行监测的,但是Vue2为什么没用呢,其实是出于性能的考虑,数据一般会被频繁的改动,每次的改动都需要遍历整个数组,给数组属性重新observe,这样会极大的消耗性能,因此在Vue2中hack了Array上的一些方法。

1、首先还是定义一组数据用于展示,hobbys 为字符串数组,friends 为对象数组

const vm = new Vue({
  el: '#root',
  data() {
    return {
      hobbys: ['抽烟', '喝酒', '烫头'],
      firends: [
        { name: 'al', age: 20 },
        { name: 'hj', age: 22 }
      ]
    }
  },
})

2、展示完了之后,我们在控制台上查看 vm.data 发现 两个数组中的值,并没有像对象中的值一样,绑定一个 get 和 set 方法。
在这里插入图片描述

这也就是意味着,如果我通过 vm._data.hobbys 去修改 hobbys 的第一个值的时候,Vue内的数据是会修改的,但是,因为没有 set 函数,无法监听到数据的改变,所以页面上是不会重新渲染修改后的属性值的。例如下图
在这里插入图片描述
Vue-数组变更方法
Vue 收集了我们平时操作数组的方法,发现 push、pop、shift、unshift,splice,sort、reserve这七个操作数组的方法,都会对原数组产生影响,反之像 map、filter、some、every等数组遍历的方法都只是会产生一个新的数组,并不会对原数组产生影响。

所以 Vue 将这7个方法进行了处理,并规定如果调用了这七个操作数组的方法,那我的 Vue 才会去监听数据的改变,如果是map等方法,那你原数据都没有改变,我也不用监听了。所以,当我想去操作数组中的属性的时候,我们不能直接 通过序号来操作,而是需要调用数组的这七个方法。
那么 Vue 包装之后的这些方法里面又是怎么做的来让Vue实现对数据监听的呢?我们还是以push为例,当调用 vm中数组原型上的 push 方法之后,分别作了如下操作

    1、调用原生的 push方法,改变数组数据

    2、生成新的虚拟DOM,新旧虚拟DOM对比,模板编译、页面重新渲染

总结:
1、Vue 监听数组改变,使用的是 数组的变更方法,包括:push、pop、shift、unshift、splice、sort、reserve,因为这七个方法操作数组之后,会改变原数组

2、这七个方法不是Array.prototype 上的方法,而是经过 Vue 的包装处理之后,挂载到Vue 的 Array.prototype 上的

3、同时也可以使用 set 方法,以此来改变当前数组,且Vue还能监听到,不过基本不这么使用

VUE3检测数组:
Vue3是用Proxy来进行对象、数组的代理,其实只要理解了Proxy就明白Vue3为什么会抛弃Object.defineProperty了:

Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。
由于 Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。
Object.defineProperty对新增属性需要手动进行Observe。
由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使用 Object.defineProperty 进行劫持。
Proxy 具有更多的拦截支持,可以做的更精细化的控制

五. vue. s e t , v u e . set, vue. set,vue.delete

this.$set的使用

就是当data中包含声明且已赋值的对象或者数组(数组包对象)时,我们要向当前的这个对象中添加一个新的属性并且更新,结果发现并不会更新视图,

受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能让它是响应的。

所以在data中注册的对象是响应式的,而后来添加的属性不是响应式的,只是个普通的属性,为了避免上面出现的问题,我们使用this.$set来向响应式对象中添加响应式的新属性

语法:this.$set(target,key,value)

<script>
export default {
 data() {
   return {
     student: {
       name: '张三',
     }
   }
 },
 methods: {
   setMessage() {
     this.$set(this.student, 'age', 15)
     console.log(this.student)
   }
 }
}
</script>

delete与$delete

var a=[1,2,3,4]
var b=[1,2,3,4]
delete a[1]
console.log(a) //134 length4 (变为undefined)
this.$delete(b,1)
console.log(b) //134 length3 (完成删除)
let obj={a:1,b:2}

六. Vue页面优化

在vue中,可以通过路由配个路由占位符来完成单页面应用的实现,其原理时通过对路由占位符的更新来完成单页面应用的实现。
单页面应用的优点在于页面的切换不会导致整个页面的刷新,而是对路由占位符的更新,比起传统的,单页面应用切换页面速度更快、用户体验更好,代码的样式及标准更好控制,程序员的工作量更少。缺点在于单页面的首屏加载速度较慢,SEO不友好。
1. 缩小项目体积:
原理:体积越小,加载越快。
方法:

通过webpack对项目体积进行压缩,开启gzip压缩文件
通过对css3、js文件的合并,如在两不同组件中,拥有相同的样式,可通过全局css文件中设置。在js文件上,将相同的方法封装合并成一个方法,如API请求。
减小图片体积,图标可通过矢量图来代替。

2. 减少加载模块:

原理:单页面应用的首屏加载较慢主要是因为单页面应用在首屏时,无论是否需要都会加载所有的模块,可通过按需加载、路由懒加载来优化。
方法:

按需加载,通过对路由文件的配置,来对相关模块划分区间,如登录界面可以和首页、主页面划分一块,在进入首屏时,只对首屏所在的区块进行加载。通过require.ensure()来将多个相同类的组件打包成一个文件。如示例代码,打包时,将两个组件打包成一个js文件,文件名为good。

{
     path: '/goodList',	//path路径	
     name: 'goodList',	//组件名
     component: r => require.ensure([], () => 	r(require('../components/goodList')), 'good')	//good类型的组件
},
{
     path: '/goodOrder',
     name: 'goodOrder',
     component: r => require.ensure([], () => r(require('../components/goodOrder')), 'good')//good类型的组件
}

动态加载,通过import来实现路由的动态加载,这个时候对于路由的加载是动态的,用到再加载。

{
     path: '/goodList',	//path路径	
     name: 'goodList',	//组件名
     component: () => import('../components/goodList')
},
{
     path: '/goodOrder',
     name: 'goodOrder',
     component: () => import('../components/goodOrder'),
}

3. 代码层面的优化
1 v-for 遍历为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。

2 v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高,这意味着 v-if 将分别重复运行于每个 v-for 循环中,将会影响速度。建议替换成 computed 属性。

3 v-if 和 v-show 区分使用场景
v-if 是真正的条件渲染,也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行样式切换。
v-if 适用于很少改变条件,不需要频繁切换的场景;v-show 则适用于需要非常频繁切换的场景。

4 computed 和 watch 区分使用场景
computed 计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch 更多的是观察的作用,每当监听的数据变化时都会执行相关回调。

当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,watch 允许我们执行异步操作。
5 keep-alive
利用keep-alive包裹,将不活动的组件缓存起来。
在组件切换过程中将状态保留在内存中,防止重复渲染dom,减少加载时间及性能消耗,提高用户体验。

6 图片资源懒加载(使用插件)
对于图片过多的页面,为了加快页面加载速度,可以将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件。
四.Webpack 层面的优化

1 Webpack 对图片进行压缩
对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader 来压缩图片

2 模板预编译
打包时,直接把组件中的模板转换为render函数,这叫做模板预编译。这样一来,运行时就不再需要编译模板了,提高了运行效率。

五. 基础的 Web 技术优化化

1 浏览器缓存
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的。

七. Vue2 vue3怎么父子传参

八. 自定义指令

九. Mixin的使用以及哪些不足?

mixin: 把多个组件共同的配置,抽取出来成一个混入对象;
看代码:
mixinx/mixin.js

let mixin = {
  created() {
    console.log("我是mixin里面的created!")
  },
  methods: {
    hello() {
      console.log("hello from mixin!")
    }
  }
}
 
export default mixin

home.vue文件中使用:

<template>
  <div class="home">
    <span>This is a Home page</span>
  </div>
</template>
 
<script>
import myMixins from "../mixins/mixin.js";   //导入混入(mixin)
export default {
  name: 'Home',
  mixins: [myMixins]     //使用混入(mixin)
}
</script>

真实项目中使用:是因为这个接口好多个页面使用,所以做了mixins处理:
dictData.js:

**在这里插入代码片let dictData = {
    data() {
        return {
            dictStatusList:[],//状态字典列表
        }
    },
    methods: {
        getDictData(dictType) {
            this.$API.system.dict.data.list.post({
                dictType
            }).then(res => {
                if(res.code == "2000") {
                    this[`${dictType}List`] = res.data;
                }
            })
        },
    },
};
export default dictData;

home.vue文件使用:

import dictData from "@/mixins/dictData";
export default {
    mixins:[dictData],
      created() {
        //获取状态字典
        this.getDictData("dictStatus");
        //获取是否字典
        this.getDictData("dictYesNo");
    },
}

十. vue2 vue3的区别

十一. hash和history

十二. 虚拟DOM循环算法

十三. hash和history

十四. 路由守卫

十五. 打包:打包loader plug-in,热加载

Watch监听的原理,
Template一个跟标签

路由守卫

Vue3的vuex
History模式刷新后,404怎么办

  1. 整个路由的原理

  2. 枚举:
    一个对象有10个属性,用枚举的方式,拿出7个;
    readonly , writeable

  3. 页面优化,webpack

  4. 事件循环机制,应用场景

  5. ES6 新特性

  6. ref reactive的原理

  7. Vue2 vue3 区别

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:13:50  更:2022-07-17 16:17:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/11 12:45:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码