手牵手系列-面试题2020_持续更新

24 篇文章 0 订阅
10 篇文章 2 订阅

vue面试题 

v-if和v-for一起使用的弊端以及解决办法

v-for的优先级比v-if高,导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素
来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。

解决办法:

1.在v-for的外层包裹一个标签来使用v-if

2.将需要的判断在computed里处理,然后在放到v-for里

vue 中的性能优化

1、Vue 应用运行时性能优化措施

(1)引入生产环境的 Vue 文件

(2)使用单文件组件预编译模板

(3)提取组件的 CSS 到单独到文件

(4)利用Object.freeze()提升性能

(5)扁平化 Store 数据结构

(6)合理使用持久化 Store 数据

(7)组件懒加载

2、Vue 应用加载性能优化措施

(1)服务端渲染 / 预渲染

(2)组件懒加载

Vue 的双向数据绑定的原理

VUE 实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了
对象获取属性值(get)和设置属性值(set)的操作来实现的。

Vue3.0 将用原生 Proxy 替换 Object.defineProperty

为什么要替换 Object.defineProperty?(Proxy 相比于 defineProperty 的优势)

在 Vue 中,Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标
给数组设置值,不能实时响应。

Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。
Vue 2.x里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么
需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。

而要取代它的Proxy有以下两个优点;

可以劫持整个对象,并返回一个新对象
有13种劫持操作
既然Proxy能解决以上两个问题,而且Proxy作为es6的新属性在vue2.x之前就有了,为什么vue2.x
不使用Proxy呢?一个很重要的原因就是:

Proxy是es6提供的新特性,兼容性不好,最主要的是这个属性无法用polyfill来兼容

什么是 Proxy?

1.含义:

Proxy 是 ES6 中新增的一个特性,翻译过来意思是"代理",用在这里表示由它来“代理”某些操作。
 Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,
因此提供了一种机制,可以对外界的访问进行过滤和改写。

使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前
记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。 从而可以让对象只需
关注于核心逻辑,达到关注点分离,降低对象复杂度等目的。

2.基本用法:

let p = new Proxy(target, handler);

参数:

target 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler 是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的
行为的函数。
p 是代理后的对象。当外界每次对 p 进行操作时,就会执行 handler 对象上的一些方法。
Proxy共有13种劫持操作,handler代理的一些常用的方法有如下几个:
get:读取
set:修改
has:判断对象是否有该属性
construct:构造函数

对于 Vue 是一套渐进式框架的理解

每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。

1、使用 vue,你可以在原有大系统的上面,把一两个组件改用它实现,当 jQuery 用;

2、也可以整个用它全家桶开发,当 Angular 用;

3、还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用 OO(Object–Oriented )面向对象和设计模式的那套理念。 也可以函数式,都可以。

它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。

你不必一开始就用 Vue 所有的全家桶,根据场景,官方提供了方便的框架供你使用。

场景联想 场景 1: 维护一个老项目管理后台,日常就是提交各种表单了,这时候你可以把 vue 当成一个 js 库来使用,就用来收集 form 表单,和表单验证。

场景 2: 得到 boss 认可, 后面整个页面的 dom 用 Vue 来管理,抽组件,列表用 v-for 来循环,用数据驱动 DOM 的变化

场景 3: 越来越受大家信赖,领导又找你了,让你去做一个移动端 webapp,直接上了 vue 全家桶!

场景 1-3 从最初的只因多看你一眼而用了前端 js 库,一直到最后的大型项目解决方案。

vue.js 的两个核心是什么?

数据驱动和组件化思想

v-on 可以监听多个方法吗?

肯定可以的。

解析:

<input
  type="text"
  :value="name"
  @input="onInput"
  @focus="onFocus"
  @blur="onBlur"
/>

vue 中 key 值的作用

需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点 所以一句话,key 的作用主要是为了高效的更新虚拟 DOM

$nextTick 的使用 

1、什么是 Vue.nextTick()?

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

所以就衍生出了这个获取更新后的 DOM 的 Vue 方法。所以放在 Vue.nextTick()回调函数中的执行的应该是会对 DOM 进行操作的 js 代码;

理解:nextTick(),是将回调函数延迟在下一次 dom 更新数据后调用,简单的理解是:当数据更新了,在 dom 中渲染后,自动执行该函数,

<template>
  <div class="hello">
    <div>
      <button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      testMsg:"原始值",
    }
  },
  methods:{
    testClick:function(){
      let that=this;
      that.testMsg="修改后的值";
      console.log(that.$refs.aa.innerText);   //that.$refs.aa获取指定DOM,输出:原始值
    }
  }
}
</script>
使用 this.$nextTick()

methods:{
    testClick:function(){
      let that=this;
      that.testMsg="修改后的值";
      that.$nextTick(function(){
        console.log(that.$refs.aa.innerText);  //输出:修改后的值
      });
    }
  }
注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM,

2、什么时候需要用的 Vue.nextTick()??

1、Vue 生命周期的 created()钩子函数进行的 DOM 操作一定要放在 Vue.nextTick()的回调函数中,原因是在 created()钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行 DOM 操作无异于徒劳,所以此处一定要将 DOM 操作的 js 代码放进 Vue.nextTick()的回调函数中。与之对应的就是 mounted 钩子函数,因为该钩子函数执行时所有的 DOM 挂载已完成。

created(){
    let that=this;
    that.$nextTick(function(){  //不使用this.$nextTick()方法会报错
        that.$refs.aa.innerHTML="created中更改了按钮内容";  //写入到DOM元素
    });
}
2、当项目中你想在改变 DOM 元素的数据后基于新的 dom 做点什么,对新 DOM 一系列的 js 操作都需要放进 Vue.nextTick()的回调函数中;通俗的理解是:更改数据后当你想立即使用 js 操作新的视图的时候需要使用它

<template>
  <div class="hello">
    <h3 id="h">{{testMsg}}</h3>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      testMsg:"原始值",
    }
  },
  methods:{
    changeTxt:function(){
      let that=this;
      that.testMsg="修改后的文本值";  //vue数据改变,改变dom结构
      let domTxt=document.getElementById('h').innerText;  //后续js对dom的操作
      console.log(domTxt);  //输出可以看到vue数据修改后DOM并没有立即更新,后续的dom都不是最新的
      if(domTxt==="原始值"){
        console.log("文本data被修改后dom内容没立即更新");
      }else {
        console.log("文本data被修改后dom内容被马上更新了");
      }
    },

  }
}
</script>
正确的用法是:vue 改变 dom 元素结构后使用 vue.$nextTick()方法来实现 dom 数据更新后延迟执行后续代码

    changeTxt:function(){
      let that=this;
      that.testMsg="修改后的文本值";  //修改dom结构

      that.$nextTick(function(){  //使用vue.$nextTick()方法可以dom数据更新后延迟执行
        let domTxt=document.getElementById('h').innerText;
        console.log(domTxt);  //输出可以看到vue数据修改后并没有DOM没有立即更新,
        if(domTxt==="原始值"){
          console.log("文本data被修改后dom内容没立即更新");
        }else {
          console.log("文本data被修改后dom内容被马上更新了");
        }
      });
    }
3、在使用某个第三方插件时 ,希望在 vue 生成的某些 dom 动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法。

Vue.nextTick(callback) 使用原理:

原因是,Vue 是异步执行 dom 更新的,一旦观察到数据变化,Vue 就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个 watcher 被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和 DOm 操作。而在下一个事件循环时,Vue 会清空队列,并进行必要的 DOM 更新。 当你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的 DOM 更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。

vue 中子组件调用父组件的方法

第一种方法是直接在子组件中通过 this.$parent.event 来调用父组件的方法
第二种方法是在子组件里用$emit 向父组件触发一个事件,父组件监听这个事件就行了
第三种是父组件把方法传入子组件中,在子组件里直接调用这个方法

vue 中父组件调用子组件的方法

使用$refs

解析:

父组件

<template>
  <div>
    <button @click="clickParent">点击</button>
    <child ref="mychild"></child>
  </div>
</template>

<script>
  import Child from "./child";
  export default {
    name: "parent",
    components: {
      child: Child
    },
    methods: {
      clickParent() {
        this.$refs.mychild.parentHandleclick("嘿嘿嘿"); // 划重点!!!!
      }
    }
  };
</script>
子组件

<template>
  <div>
    child
  </div>
</template>

<script>
  export default {
    name: "child",
    props: "someprops",
    methods: {
      parentHandleclick(e) {
        console.log(e);
      }
    }
  };
</script>

vue 中 keep-alive 组件的作用

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

解析:

用法也很简单:

<keep-alive>
  <component>
    <!-- 该组件将被缓存! -->
  </component>
</keep-alive>
props _ include - 字符串或正则表达,只有匹配的组件会被缓存 _ exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

// 组件 a
export default {
  name: "a",
  data() {
    return {};
  }
};
<keep-alive include="a">
  <component>
    <!-- name 为 a 的组件将被缓存! -->
  </component> </keep-alive
>可以保留它的状态或避免重新渲染
<keep-alive exclude="a">
  <component>
    <!-- 除了 name 为 a 的组件都将被缓存! -->
  </component> </keep-alive
>可以保留它的状态或避免重新渲染
但实际项目中,需要配合 vue-router 共同使用.

router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:

<keep-alive>
  <router-view>
    <!-- 所有路径匹配到的视图组件都会被缓存! -->
  </router-view>
</keep-alive>
如果只想 router-view 里面某个组件被缓存,怎么办?

增加 router.meta 属性

// routes 配置
export default [
  {
    path: "/",
    name: "home",
    component: Home,
    meta: {
      keepAlive: true // 需要被缓存
    }
  },
  {
    path: "/:id",
    name: "edit",
    component: Edit,
    meta: {
      keepAlive: false // 不需要被缓存
    }
  }
];
<keep-alive>
    <router-view v-if="$route.meta.keepAlive">
        <!-- 这里是会被缓存的视图组件,比如 Home! -->
    </router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive">
    <!-- 这里是不被缓存的视图组件,比如 Edit! -->
</router-view>

vue 更新数组时触发视图更新的方法

Vue.set 可以设置对象或数组的值,通过 key 或数组索引,可以触发视图更新

数组修改

Vue.set(array, indexOfItem, newValue)
this.array.$set(indexOfItem, newValue)
对象修改

Vue.set(obj, keyOfItem, newValue)
this.obj.$set(keyOfItem, newValue)
2.Vue.delete 删除对象或数组中元素,通过 key 或数组索引,可以触发视图更新

数组修改

Vue.delete(array, indexOfItem)
this.array.$delete(indexOfItem)
对象修改

Vue.delete(obj, keyOfItem)
this.obj.$delete(keyOfItem)
3.数组对象直接修改属性,可以触发视图更新

this.array[0].show = true;
this.array.forEach(function(item){
    item.show = true;
});
4.splice 方法修改数组,可以触发视图更新

this.array.splice(indexOfItem, 1, newElement)
5.数组整体修改,可以触发视图更新

var tempArray = this.array;
tempArray[0].show = true;
this.array = tempArray;
6.用 Object.assign 或 lodash.assign 可以为对象添加响应式属性,可以触发视图更新

//Object.assign的单层的覆盖前面的属性,不会递归的合并属性
this.obj = Object.assign({},this.obj,{a:1, b:2})

//assign与Object.assign一样
this.obj = _.assign({},this.obj,{a:1, b:2})

//merge会递归的合并属性
this.obj = _.merge({},this.obj,{a:1, b:2})
7.Vue 提供了如下的数组的变异方法,可以触发视图更新

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

JS面试题

let newList = [1, 2, 3].push(4); // 4 .push()方法返回数组的长度,而不是数组的本身。
let name = "Lydia";

function getName() {
  console.log(name);
  let name = "Sarah";
}
getName();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值