• vue2常见面试题


    文章目录

    1、vue 修改数据页面不重新渲染

    vue2是用过Object.defineProperty实现数据响应式, 组件初始化时,对 data 中的 item 进行递归遍历,对 item 的每一个属性进行劫持,添加 set , get 方法。我们后来 新加的属性 ,并没有通过Object.defineProperty设置成响应式数据,修改后不会视图更新

    通过数组索引号修改了数组,界面会不会相应更新?为什么?

    答:不会。vue 监听不到

    vue 为什么没有提供 arr[下标] = val 变成响应式?

    尤大:“因为性能问题,性能代价和获得的用户体验收益不成正比”

    数组/对象的响应式 ,vue 里面是怎么处理的?

    对象:使用了Object.defineProperty中的 get 和 set

    如何监测对象中的数据?
    通过 setter 实现监视,且要在 new Vue 时就传⼊要监测的数据

    1. 对象中后追加的属性,Vue 默认不做响应式处理
    2. 如需给后添加的属性做响应式,请使⽤如下 API:
      Vue.set(target,propertyName/index,value) vm.$set(target,propertyName/index,value)

    数组Vue重写了数组的原型,更准确的表达是拦截了数组的原型

    如何监测数组中的数据?
    通过包裹数组更新元素的⽅法实现,本质就是做了两件事:

    1. 调⽤原⽣对应的⽅法对数组进⾏更新

    2. 重新解析模板,进⽽更新⻚⾯

    3. 在 Vue 修改数组中的某个元素⼀定要⽤如下⽅法:

      1. 使 ⽤ 这 些 API :
        push() pop() shift() unshift() splice() sort() reverse()
      2. Vue.set()vm.$set()
      3. 覆盖整个数组

    为什么对象和数组要分开处理?

    对象的属性通常比较少,对每一个属性都劫持set和get,并不会消耗很多性能

    数组有可能有成千上万个元素,如果每一个元素都劫持set和get,无疑消耗太多性能了

    所以对象通过defineProperty进行正常的劫持set和get

    数组则通过修改数组原型上的部分方法,来实现修改数组触发响应式

    2、生命周期
    Vue 生命周期都有哪些?
    生命周期执行时机
    beforeCreate在组件实例被创建之初、组件的属性⽣效之前被调用
    created在组件实例已创建完毕。此时属性也已绑定,但真实 DOM 还未⽣成,$el 还不可⽤
    beforeMount在组件挂载开始之前被调⽤。相关的 render 函数⾸次被调⽤
    mounted在 el 被新建的 vm.$el 替换并挂载到实例上之后被调用
    beforeUpdate在组件数据更新之前调⽤。发⽣在虚拟 DOM 打补丁之前
    update在组件数据更新之后被调用
    activited在组件被激活时调⽤(使用了 的情况下)
    deactivated在组件被销毁时调⽤(使用了 的情况下)
    beforeDestory在组件销毁前调⽤
    destoryed在组件销毁后调⽤
    父子组件生命周期执行顺序

    加载渲染过程

    	->父beforeCreate
      ->父created
      ->父beforeMount
      ->子beforeCreate
      ->子created
      ->子beforeMount
      ->子mounted
      ->父mounted
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    更新过程

    父beforeUpdate->子beforeUpdate->子updated->父updated
    
    • 1

    销毁过程

    父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
    
    • 1

    keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载

    参考:https://juejin.cn/post/6844903641866829838#heading-11

    参考:https://juejin.cn/post/7114252241166401573

    3、watch 和 computed 的区别

    官方文档:对于任何复杂逻辑,你都应当使用计算属性

    扩展:vue 中 Computed、Methods、Watch 区别

    computed(计算属性)watch(监视属性/侦听器)
    根据你所依赖的数据动态显示新的计算结果
    不用再 data 中声明,否则报错
    data 的数据监听回调,依赖 data 的数据变化
    直接使用 data 声明的数据
    支持缓存不支持缓存
    不支持异步支持异步
    有 get 和 set 方法,当数据变化时,调用 set 方法可以深度监视 deep,加载就调用 immediate
    监听的函数接收两个函数,newVal 和 oldVla
    当需要进行数值计算,并且依赖于其它数据时,用 computed在某个数据变化时做一些事情或需要异步操作时,用 watch
    computed 能做的watch 都能做到
    4、组件通信(组件间传值)
    • props $emit
    • .sync v-model
    • $parent / $children $parent 获取父组件的实例,任意调用父组件的方法,修改父组件的数据
    • ref 父组件获取 子组件 实例,任意调用子组件的方法获取子组件的属性
    • provide / injectprpvide 父组件内部提供数据 inject 嵌套的子组件可以注入数据
    • $attrs / $listeners $attrs(没有被 props 接收的所有自定义属性) $listeners(可以获取所有的父组件传递过来的自定义事件)
    • eventBus 定义一个事件总线 使用$on 绑定 $emit 触发
    • vuex
    • 路由传参

    参考:https://www.wpgdadatong.com/cn/blog/detail?BID=B3650

    参考:https://juejin.cn/post/7110223595359436813

    5、$nextTick

    NextTick 是什么

    在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

    // 修改数据
    this.message = "修改后的值";
    // 此时DOM还没有更新
    console.log(this.$el.textContent); // => '原始的值'
    this.$nextTick(function () {
      // DOM 更新了
      console.log(this.$el.textContent); // => '修改后的值'
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    应用场景:因为 vue 是异步执行 dom 更新的,所以当你希望在更新数据之后,操作新的视图, 那么你的操作逻辑应写在 Vue.nextTick(callback) 的回调中,而这个回调会在dom 更新循环结束之后执行。 否则,因为异步更新 dom 的原因,如果你不是在 Vue.nextTick(callback) 的回调中执行操作新视图, 那么可能会发生意外。例如你在 created()钩子 是不能操作 dom 的,但你可以在此调接口更新数据, 如果你此时希望接口更新完毕数据后,接着调用操作 dom 的逻辑,那么最后将这部分操作 dom 的逻辑, 放置在Vue.nextTick(callback) 的回调函数中。

    6、修饰符

    引用:https://juejin.cn/post/7026867875990208543#heading-22

    事件修饰符

    stop

    • stop修饰符的作用是阻止冒泡

    prevent⭐

    • prevent修饰符的作用是阻止默认事件(例如 a 标签的跳转)

    capture

    • 事件流默认冒泡,使用 capture 进行捕获捕获

    self

    • self修饰符作用是,只有点击事件绑定的本身才会触发事件

    once

    • once修饰符的作用是,事件只执行一次

    native⭐

    • native修饰符是加在自定义组件的事件上,保证事件能执行
    • native是用来是在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的 HTML 标签看待

    passive

    • 当我们在监听元素滚动事件的时候,会一直触发 onscroll 事件,在 pc 端是没啥问题的,但是在移动端,会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给 onscroll 事件整了一个.lazy 修饰符
    v-bind 修饰符

    sync⭐

    • 父组件传值进子组件,子组件想要改变这个值时,可以这么做

    • // 父组件里
      <children :foo.sync="bar"></children>
      // 子组件里
      this.$emit('update:foo', newValue)
      
      • 1
      • 2
      • 3
      • 4

    camel

    .camel 修饰符允许在使用 DOM 模板时将 v-bind property 名称驼峰化,例如 SVG 的 viewBox property:

    <svg :view-box.camel="viewBox"></svg>
    
    • 1
    鼠标修饰符

    left right middle

    • 这三个修饰符是鼠标的左中右按键触发的事件
    表单相关修饰符

    trim⭐

    • trim修饰符的作用类似于 JavaScript 中的trim()方法,作用是把v-model绑定的值的首尾空格给过滤掉。

    lazy

    • lazy修饰符作用是,改变输入框的值时 value 不会改变,当光标离开输入框时,v-model绑定的值 value 才会改变

    number

    • number修饰符的作用是将值转成数字,但是先输入字符串和先输入数字,是两种情况:

    先输入数字的话,只取前面数字部分

    先输入字母的话,number修饰符无效

    系统修饰符

    .ctrl.alt.shift.meta

    按键修饰符

    配合键盘事件使用: .enter .tab .delete .esc .space .up .down left

    7、图片上传 TODO⭐⭐

    FileReader 与 URL.createObjectURL 实现图片、视频上传预览

    event.target.files 就是用户上传的图片信息

    配合 cropperjs 可以实现图片裁剪功能

    // 如果接口要求 Content-Type 是 multipart/form-data
    // 则你必须传递 FormData 对象
    
    • 1
    • 2
    8、v-for 和 v-if / v-if 和 v-show

    为什么 v-if 和 v-for 不能同时使用?

    • v-if 不能和 v-for 一起使用的原因是 v-for 的优先级比 v-if 高,一起使用会造成性能浪费

    • 解决方案有两种,把 v-if 放在 v-for 的外层或者把需要 v-for 的属性先从计算属性中过滤一次

    • v-if 和 v-for 的优先级问题在 vue3 中不需要考虑,vue3 更新了 v-if 和 v-for 的优先级,使 v-if 的优先级高于 v-for

    v-if 和 v-show

    v-show隐藏则是为该元素添加display:nonev-if是将dom元素整个添加或删除

    • v-showfalse变为true的时候不会触发组件的生命周期
    • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed方法

    性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;

    如果需要非常频繁地切换,则使用 v-show 较好

    如果在运行时条件很少改变,则使用 v-if 较好

    9、Vue.use()

    Vue.use是用来安装插件的

    用法:Vue.use(plugin)

    • 如果插件是一个对象,必须提供 install 方法。
    • 如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入
    • Vue.use(plugin)调用之后,插件的 install 方法就会默认接受到一个参数,这个参数就是 Vue

    总结:Vue.use 是官方提供给开发者的一个 api,用来注册、安装类似 Vuex、vue-router、ElementUI 之类的插件的

    10、 跨域

    线上环境的跨域问题
    1、cors >> 后端加响应头 access-control-allow-origin: ‘*’ 允许所有的页面请求
    2、代理(主流方案)
    开发环境 :webpack的devServer进行代理,本地请求转发到接口 vue.config.js > devServer > proxy {}
    线上环境: 服务器端使用 nginx 反向代理,拦截请求转发到线上接口, 解决跨域问题

    什么是跨域

    跨域问题是浏览器的同源策略所导致的

    其中,域名、协议、端口号相同,称之为同源,如果不同,称之为跨源或跨域

    跨域常见的解决方法:

    • 代理,常用
    • CORS,常用
    • JSONP
    跨域解决方法 1-代理

    代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域

    因此,只需要在开发环境使用代理解决跨域即可,这种代理又称之为开发代理

    在实际开发中,只需要对开发服务器稍加配置即可完成

    // vue 的开发服务器代理配置
    // vue.config.js
    module.exports = {
      devServer: {
        // 配置开发服务器
        proxy: {
          // 配置代理
          "/api": {
            // 若请求路径以 /api 开头
            target: "http://dev.taobao.com", // 将其转发到 http://dev.taobao.com
          },
        },
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    跨域解决方法 2-CORS

    参考:阮一峰 CORS: https://www.ruanyifeng.com/blog/2016/04/cors.html

    CORS是基于http1.1的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享

    CORS 需要浏览器和后端同时支持。

    整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

    因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

    浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

    只要同时满足以下两大条件,就属于简单请求。

    凡是不同时满足下面两个条件,就属于非简单请求。

    (1) 请求方法是以下三种方法之一:

    • HEAD
    • GET
    • POST

    (2)HTTP 的头信息不超出以下几种字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain
    跨域解决方法 3-JSONP

    JSONP 的做法是:当需要跨域请求时,不使用 AJAX,转而生成一个 script 元素去请求服务器,由于浏览器并不阻止 script 元素的请求,这样请求可以到达服务器。服务器拿到请求后,响应一段 JS 代码,这段代码实际上是一个函数调用,调用的是客户端预先生成好的函数,并把浏览器需要的数据作为参数传递到函数中,从而间接的把数据传递给客户端

    JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。

    11、cookie

    什么是 cookie

    cookie 是储存在用户本地终端上的数据,是网站为了识别用户跟踪会话而存储在用户本地终端中的文本数据

    怎么操作

    可以使用js-cookie插件

    模块化开发时直接引入import Cookies from 'js-cookie'

    js-cookie.js 常用的 API 和方法

    设置 cookie

    Cookies.set("name", "value", { expires: 7, path: "" }); //7天过期
    Cookies.set("name", { foo: "bar" }); //设置一个json
    
    • 1
    • 2

    读取 cookie

    Cookies.get("name"); //获取cookie
    Cookies.get(); //读取所有的cookie
    
    • 1
    • 2

    删除 cookie

    Cookies.remove("name"); //删除cookie时必须是同一个路径。
    
    • 1
    12、keep-alive 原理
    keep-alive 是什么
    1. vue 自带的组件 >> 主要功能是缓存组件 >> 提升性能
    2. 使用场景:可以少网络请求,如果当前组件数据量比较大,就可以节省网络请求 >> 提升用户体验
    3. 举例:如果详情页面之间进行切换,就可以使用keep-alive进行缓存组件,防止同样的数据重复请求

    keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

    keep-alive可以设置以下props属性:

    • include - 字符串或正则表达式。只有名称匹配的组件会被缓存
    • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存
    • max - 数字。最多可以缓存多少组件实例

    关于keep-alive的基本用法:

    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    
    • 1
    • 2
    • 3

    匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值),匿名组件不能被匹配

    设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activateddeactivated):

    • 首次进入组件时:beforeRouteEnter > beforeCreate > created> mounted > activated > … … > beforeRouteLeave > deactivated
    • 再次进入组件时:beforeRouteEnter >activated > … … > beforeRouteLeave > deactivated
    使用场景

    使用原则:当我们在某些场景下不需要让页面重新加载时我们可以使用keep-alive

    举个栗子:

    当我们从首页–>列表页–>商详页–>再返回,这时候列表页应该是需要keep-alive

    首页–>列表页–>商详页–>返回到列表页(需要缓存)–>返回到首页(需要缓存)–>再次进入列表页(不需要缓存),这时候可以按需来控制页面的keep-alive

    在路由中设置keepAlive属性判断是否需要缓存

    {
      path: 'list',
      name: 'itemList', // 列表页
      component (resolve) {
        require(['@/pages/item/list'], resolve)
     },
     meta: {
      keepAlive: true,
      title: '列表页'
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用

    <div id="app" class='wrapper'>
        <keep-alive>
            <!-- 需要缓存的视图组件 -->
            <router-view v-if="$route.meta.keepAlive"></router-view>
         </keep-alive>
          <!-- 不需要缓存的视图组件 -->
         <router-view v-if="!$route.meta.keepAlive"></router-view>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    缓存后如何获取数据

    解决方案可以有以下两种:

    • beforeRouteEnter
    • actived
    13、ref

    作用:

    • 放到 dom 节点上 >> 获取原生 dom
    • 组件身上 >> 获取组件实例 >> 可以获取组件内部所有的方法和数据
    14、scoped 原理是什么?

    作用:使样式私有化(模块化),不对全局造成污染

    原理:动态的给组件加上一个 hash 值,用属性选择器去匹配

    15、$router 和 $route

    $route:当前的路由信息对象,获取到路由参数、路径

    1. $route.path: 字符串,对应当前路由的路径,总是解析为绝对路径,如/foo/bar
    2. $route.params: 一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。
    3. r o u t e . q u e r y :一个 k e y / v a l u e 对象,表示 U R L 查询参数。例如,对于路径 / f o o ? u s e r = 1 ,则有 route.query: 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 route.query:一个key/value对象,表示URL查询参数。例如,对于路径/foo?user=1,则有route.query.user == 1,如果没有查询参数,则是个空对象。
    4. $route.hash: 当前路由的 hash 值 (不带#) ,如果没有 hash 值,则为空字符串。锚点
    5. $route.fullPath: 完成解析后的 URL,包含查询参数和 hash 的完整路径。
    6. $route.matched: 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
    7. $route.name: 当前路径名字
    8. $route.meta: 路由元信息

    r o u t e r :全局路由 v u e R o u t e r 的实例,挂载到 V u e 原型上 router:全局路由 vueRouter 的实例,挂载到 Vue 原型上 router:全局路由vueRouter的实例,挂载到Vue原型上router 属性,可以获取到全局路由配置信息,跳转方法

    $router.replace({path:‘home’}),//替换路由,没有历史记录

    $router.push(‘/login’) ,跳转到指定路由

    $router.back()

    $router.go()

    16、发布订阅模式和观察者模式
    发布订阅模式

    发布订阅模式中有三个角色,发布者 Publisher ,信息中心 Event Channel ,订阅者 Subscriber

    我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)

    通常是通过 on 事件订阅消息,emit 事件发布消息,remove 事件删除订阅

    class PubSub {
      constructor() {
        // 事件中心
        // 存储格式: warTask: [], routeTask: []
        // 每种事件(任务)下存放其订阅者的回调函数
        this.events = {};
      }
      // 订阅方法
      subscribe(type, cb) {
        if (!this.events[type]) {
        }
        this.events[type].push(cb);
      }
      // 发布方法
      publish(type, ...args) {
        if (this.events[type]) {
          this.events[type].forEach((cb) => cb(...args));
        }
      }
      // 取消订阅方法
      unsubscribe(type, cb) {
        if (this.events[type]) {
          const cbIndex = this.events[type].findIndex((e) => e === cb);
          if (cbIndex !== -1) {
            this.events[type].splice(cbIndex, 1);
          }
        }
        if (this.events[type].length === 0) {
          delete this.events[type];
        }
      }
      unsubscribeAll(type) {
        if (this.events[type]) {
          delete this.events[type];
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    观察者模式

    当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式

    • 观察者(订阅者) – Watcher
      • update():当事件发生时,具体要做的事情
    • 目标(发布者) – Dep
      • subs 数组:存储所有的观察者
      • addSub():添加观察者
      • notify():当事件发生,调用所有观察者的 update() 方法
    • 没有事件中心
    总结
    设计模式观察者模式发布订阅模式
    主体Watcher 观察者、Dep 目标对象Publisher 发布者、Event Channel 信息中心、Subscribe 订阅者
    主体关系
    Dep 中通过 subs 记录 WatcherPublisher 和 Subscribe 不想不知道对方,通过中介联系
    优点角色明确,Watcher 和 Dep 要遵循约定的成员方法松散耦合,灵活度高,通常应用在异步编程中
    缺点紧耦合当事件类型变多时,会增加维护成本
    使用案例双向数据绑定事件总线 EventBus
    17、vue 响应式原理(数据劫持)
    18、eventBus
    19、v-model 与.sync

    相同点:都是语法糖,都可以实现父子组件中的数据的双向通信。

    //  v-model
    <son  v-model="num"/> //父组件使用子组件
    model:{
    	prop:'newValue', // 默认为 value 可以使用prop自定义属性名
    	event:"updateValue", // event 修改事件名 默认为input
    },
    props: { // 子组件接收
    	value: {  // 默认为value
    		type: Number,
    	}
    }
    this.$emit('input', '小红')
    // .sync
    <son :title.sync="doc.title"></son>   // 父组件
    props:{
    	title:{
    	  type:...
    	}
    }
    this.$emit('update:title', newTitle)  // 子组件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    区别点:格式不同: v-model=“num”, :num.sync=“num”

    v-model: @input + value
    :num.sync: @update:num

    另外需要特别注意的是: v-model只能用一次;.sync可以有多个

    20、如何做样式穿透

    背景:修改当前组件嵌套的子组件内部的样式

    问题

    1. 如果不添加 scoped,可能影响全局样式
    2. 如果添加 scoped,嵌套的子组件内部样式不能生效

    如何解决:添加/deep/ / ::v-deep

    scss: 使用::v-deep

    less: 使用 /deep/

    21、你对SPA单页面的理解,它的优缺点分别是什么?

    SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

    单页应用优缺点

    优点:

    • 具有桌面应用的即时性、网站的可移植性和可访问性
    • 用户体验好、快,内容的改变不需要重新加载整个页面,SPA 相对对服务器压力小;
    • 良好的前后端分离,分工更明确

    缺点:

    • SEO(搜索引擎)难度较大
    • 首次渲染速度相对较慢
    • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
    22、怎么理解 Vue 的单向数据流

    ① 单向数据流是指数据从父组件传向子组件,子组件没有权限直接修改该数据;
    ② 子组件需要在 data 或者 computed 中重新定义变量来接收父组件传来的值,以便修改;
    ③ 子组件可以通过 $emit 的方式通知父组件修改值,再重新传回给子组件;

    23、路由懒加载
    为什么要使用路由懒加载

    为给客户更好的客户体验,首屏组件加载速度更快一些,解决白屏问题

    定义

    懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载

    路由 和 组件 的常用两种懒加载方式:

    1、vue异步组件实现路由懒加载 component:resolve=>(['需要加载的路由的地址',resolve])

    2、es提出的import(推荐使用这种方式) const HelloWorld = ()=> import('需要加载的模块地址')

    通过魔法注释webpackChunkName修改名称,打包时就会显示修改后的新名称

    const { name } = await import(/* webpackChunkName:"newName"*/'路径')
    
    • 1
    24、如何理解vue的key属性

    image-20220811205624543

     key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速,以便高效的更新虚拟 DOM**
      							
     1.  对⽐规则:
      							
         1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
             1. 若虚拟DOM中内容没变, 直接使⽤之前的真实DOM
             2. 若虚拟DOM中内容变了, 则⽣成新的真实DOM,随后替换掉⻚⾯中之前的真
                  实DOM
      							
         2. 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到
             到⻚⾯
      							
           2. ⽤index作为key可能会引发的问题:
            1. 若对数据进⾏逆序添加、逆序删除等破坏顺序操作:会产⽣没有必要的真实DOM
                更新 ==> 界⾯效果没问题, 但效率低
            2. 若结构中还包含输⼊类的DOM:会产⽣错误DOM更新 ==> 界⾯有问题
           3. 开发中如何选择key?
            1. 最好使⽤每条数据的唯⼀标识作为key,⽐如id、⼿机号、⾝份证号、学号等唯⼀
                值
            2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅⽤于渲染列表,
                使⽤index作为key是没有问题的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    25、vue-router 有哪几种导航钩子

    参考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

    参考:https://router.vuejs.org/zh/guide/advanced/meta.html

    全局导航钩子

    • router.beforeEach(to,from,next):添加一个导航守卫,在任何导航前执行
    • router.beforeResolve(to,from):添加一个导航守卫,在导航即将解析之前执行。在这个状态下,所有的组件都已经被获取,并且其他导航守卫也已经成功
    • router.afterEach(to,from):添加一个导航钩子,在每次导航后执行

    组件内的钩子

    • beforeRouterEnter(to, from, next):在渲染该组件的对应路由被验证前调用
    • beforeRouterUpdate:在当前路由改变,但是该组件被复用时调用
    • beforeRouterLeave:在导航离开渲染该组件的对应路由时调用
    const UserDetails = {
      template: `...`,
      beforeRouteEnter(to, from,next) {
        // 在渲染该组件的对应路由被验证前调用
        // 不能获取组件实例 `this` !
        // 因为当守卫执行时,组件实例还没被创建!
      },
      beforeRouteUpdate(to, from) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
        // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
      },
      beforeRouteLeave(to, from) {
        // 在导航离开渲染该组件的对应路由时调用
        // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    路由独享的守卫

    • beforeEnter:只在进入路由时触发,不会在 paramsqueryhash 改变时触发
    const routes = [
      {
        path: '/users/:id',
        component: UserDetails,
        beforeEnter: (to, from) => {
          // reject the navigation
          return false
        },
      },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20220809005712651

    26、什么是 MVVM?(深入待总结)

    img

    • Model:模型层(数据层),主要用于保存一些数据
    • View: 视图层,主要用于将后端数据借助各种元素呈现给用户,同时也可提供用户操作的入口
    • ViewModel:视图模型层:该层也是mvvm中的核心层,主要用于作为Model个View两个层的数据连接层,负责两个层之间的数据传递。该层主要包含两大功能点:
      • DOM监听(DOM Listener)视图变化后更新数据
      • 数据绑定(Data bindings)数据变化后更新视图
    27、Vue 是如何实现数据双向绑定的?

    参考:https://juejin.cn/post/6950939562872930341

    Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据

    Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

    1. 需要Observe(观察者)的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
    2. Compile(编译)解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
    3. Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notify()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
    4. MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

    以上四个步骤的流程图表示如下,如果有同学理解不大清晰的,可以查看作者专门介绍数据双向绑定的文章《0 到 1 掌握:Vue 核心之数据双向绑定》,有进行详细的讲解、以及代码 demo 示例

    1158910-20180306234148823-430002059.png

    28、Vue.observable你有了解过吗?说说看

    参考:http://t.csdn.cn/f9CHg

    29、v-model 的原理?

    我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

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

    以 input 表单元素为例:

    <input v-model='something'>
    // 相当于
    <input v-bind:value="something" v-on:input="something = $event.target.value">
    
    • 1
    • 2
    • 3
    30、data是个函数不是个对象

    首先,data是个函数是确保数据不会被污染,在JS中对象是一个引用数据类型,如果是对象形式,多个vue实例会相互影响,为了保证组件的复用性,data要写成函数,函数形式每次使用时,会返回一个新的对象拷贝,这样使用组件时data中的数据就不会被相互影响

    new Vue的实例,比如app.js中,是不会被复用的,所以不存在数据污染的情况

    31、路由三种模式

    hash模式

    history模式

    abstract模式

    32、hash模式和history模式(补充)

    参考:https://juejin.cn/post/6867875626611654663

    前端路由原理

    前端路由的核心,就在于改变视图的同时不会向后端发出请求;而是加载路由对应的组件。vue-router就是将组件映射到路由, 然后渲染出来的。并实现了三种模式:Hash模式、History模式以及Abstract模式。默认Hash模式

    hash模式 : #后面是路由路径,特点是前端访问,#后面的变化不会经过服务器

    history模式:正常的/访问模式,特点是后端访问,任意地址的变化都会访问服务器

    Hash模式
    • 原理
      基于浏览器的hashchange事件,地址变化时,通过window.location.hash 获取地址上的hash值;并通过构造Router类,配置routes对象设置hash值与对应的组件内容。
    • 优点
    1. hash值会出现在URL中, 但是不会被包含在Http请求中, 因此hash值改变不会重新加载页面
    2. hash改变会触发hashchange事件, 能控制浏览器的前进后退
    3. 兼容性好
    • 缺点
    1. 地址栏中携带#,不美观
    2. 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL
    3. hash有体积限制,故只可添加短字符串
    4. 设置的新值必须与原来不一样才会触发hashchange事件,并将记录添加到栈中
    5. 每次URL的改变不属于一次http请求,所以不利于SEO优化
    History模式
    • 原理
      基于HTML5新增的pushState()和replaceState()两个api,以及浏览器的popstate事件,地址变化时,通过window.location.pathname找到对应的组件。并通过构造Router类,配置routes对象设置pathname值与对应的组件内容。
    • 优点
    1. 没有#,更加美观
    2. pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL
    3. pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中
    4. pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中
    5. pushState() 可额外设置 title 属性供后续使用
    6. 浏览器的进后退能触发浏览器的popstate事件,获取window.location.pathname来控制页面的变化
    • 缺点
    1. URL的改变属于http请求,借助history.pushState实现页面的无刷新跳转,因此会重新请求服务器。所以前端的 URL 必须和实际向后端发起请求的 URL 一致。如果用户输入的URL回车或者浏览器刷新或者分享出去某个页面路径,用户点击后,URL与后端配置的页面请求URL不一致,则匹配不到任何静态资源,就会返回404页面。所以需要后台配置支持,覆盖所有情况的候选资源,如果 URL 匹配不到任何静态资源,则应该返回app 依赖的页面或者应用首页。
    2. 兼容性差,特定浏览器支持
    Abstract模式

    工作时没用过,支持所有javascript运行模式。vue-router 自身会对环境做校验,如果发现没有浏览器的 API,路由会自动强制进入 abstract 模式。在移动端原生环境中也是使用 abstract 模式。

    总结

    hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。

    33、说说你对slot的理解?slot使用场景有哪些?

    参考:https://vue3js.cn/interview/vue/slot.html#%E4%B8%80%E3%80%81slot%E6%98%AF%E4%BB%80%E4%B9%88

    什么是slot

    slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定

    slot的作用

    通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理

    如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情

    通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用

    比如布局组件、表格列、下拉选、弹框显示内容等

    slot的分类
    • 默认插槽

      • 子组件用标签来确定渲染的位置,标签里面可以放DOM结构,当父组件使用的时候没有往插槽传入内容,标签内DOM结构就会显示在页面

        父组件在使用的时候,直接在子组件的标签内写入内容即可

    • 具名插槽

      • 子组件用name属性来表示插槽的名字,不传为默认插槽

        父组件中在使用时在默认插槽的基础上加上slot属性,值为子组件插槽name属性值

    • 作用域插槽

      • 子组件在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件v-slot接受的对象上

        父组件中在使用时通过v-slot:(简写:#)获取子组件的信息,在内容中使用

    34、使用过 Vue SSR 吗?说说 SSR?

    服务器端渲染

    35、说说你对vue的mixin的理解,有什么应用场景?
  • 相关阅读:
    Elasticsearch的使用
    创建hive事物表(模拟测试)
    Spring IOC核心功能快速入门
    springboot结合@Validated全局异常捕捉
    ThreadLocal&上传下载文件
    【微服务】SaaS云智慧工地管理平台源码
    虚拟机搭建负载均衡,mysql主从复制和读写分离(三、搭建负载均衡)
    sqli-labs关卡19(基于http头部报错盲注)通关思路
    18、监测数据采集物联网应用开发步骤(12.3)
    【In-Context Learning】Meta-learning via Language Model In-context Tuning
  • 原文地址:https://blog.csdn.net/weixin_46862327/article/details/126819423