面试问题总结目录

一、 vue 面试题

1.1 keep-alive理解

1.2 父子组件传值

  • 子组件通过props接收父组件穿过来的数据
  • 子组件通过$emit 触发父组件中的方法

1.3 $nextTick的理解和作用

  • nextTick 本质上是一个延时器。vue用三种方案实现nextTick。promise、setTimeOut、HTML5中的API MutationObserver

  • 有时dom还没渲染结束,确想对dom进行一些操作时,可能实际结果与预期不符,可以将对dom的操作或者数据变化后的一些操作放在 $ nextTick函数中进行逻辑的处理。例如:查看切换后的结果,就可以在切换函数中使用$nextTick函数,一般是需要对dom进行操作

  • 像对canvs一类的标签,无法监测到数据变化内容变化的标签,就需要在nextTick中进行重绘。因为对于虚拟dom来说,没有发生变化。

     methods: {
     // 函数执行顺序  点击事件函数(数据data发生变化)——》beforeUpdate函数——》updated函数——》nextTick函数
     // 更新的本质是数据发生了变化
        changeMsg(){
          this.form.username = 'lxf'
          const that = this
          this.$nextTick(function() {
            //  仅在整个视图都被重新渲染之后才会运行的代码
            console.log(that.$refs['userName'].innerHTML + 'changeMsg')
          })
        }
      }
    

1.4 虚拟dom和真实dom

  • 本质上 Virtual Dom 是一个 JavaScript 对象,通过采用对象的格式来表示真实的dom结构。可以有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能
  • 在数据发生变化前,虚拟dom都会缓存一份,在数据发生变化时,新生成的虚拟DOM与缓存的虚拟DOM进行比较(采用diff算法比较),精准更新发生变化的节点,而没有发生变化的直接通过原先的数据进行渲染。
  • 可以减少对dom的操作,提高程序性能;减少页面重绘和回流;跨平台,虚拟DOM本质上是javascript的一个对象,不依赖真实的平台环境。
  • 虚拟DOM形成过程 template模板——》被编译器 compiler编译成渲染函数 ——》在挂载前(beforeMount)调用render函数并返回虚拟DOM对象,在beforeMount后、mounted前通过patch函数转为真实DOM。相关知识ast语法树

1.5 怎样提高vue的性能

二、 vue 生命周期面试题

2.1 vue生命周期理解

2.2 第一次加载页面会触发的钩子函数

beforeCreate created beforeMount mounted

2.3 常用钩子函数适用的场景

  • created :此时数据和方法已经配置完毕,可以进行方法调用和数据使用。
  • mounted : 此阶段dom元素已经挂载结束,可以获取页面中的元素进行操作。如果有子组件建议和vm.$nextTick搭配使用。
  • deforeDestroy :实例销毁前可以进行一些善后处理,如:清除计时器、清除非指令绑定的事件等等…’

2.4 vue 父子组件的渲染顺序

父组件beforeCreated ——》 父组件created ——》父组件beforeMount ——》第一个子组件beforeCreate ——》第一个子组件created ——》第一个子组件beforeMount ——》第N个子组件beforeCreate ——》第N个子组件created ——》第N个子组件beforeMount ——》第一个子组件mounted ——》第N个子组件mounted ——》父组件mounted

2.5 vue父子组件销毁的顺序

父组件beforeDestroy -——》第一个子组件beforeDestroy ——》第一个子组件destroyed -——》第N个子组件beforeDestroy ——》第N个子组件destroyed ——》父组件destroyed

三、 vuex面试题

3.1 vuex是什么

答:vuex是Vue框架的状态管理。需要创建store.js文件,在main.js中引入并 注入。下面是main.js 文件面试问题总结目录_第1张图片

3.2 vuex属性及使用方法

四、 vue-router 面试题

4.1 vue-router是什么

    vue-router是Vue管理路由的一个插件,即记录组件和路径的映射关系。

4.2 vue-router 有哪些常用的组件

  • router-link :是一个路由导航的一个组件,和a标签的功能一样。当路由被激活时有一个激活的css类名(router-link-active),可以做导航高亮显示
  • router-view :是一个function组件,将路由匹配到的视图组件渲染在router-view标签的位置。

4.3 vue-router的两种路由模式以及区别

  • 两种模式 hash和history。默认是hash
  • 共同点:url改变无需重新加载页面
  • 区别
    1. hash路径前带#,history不带#> http://localhost/components/tabs (history)
      http://localhost/#/components/noticeBar (hash值为 #/components/noticeBar)
    2. hash模式下的http请求不会包含hash值
    3. hash支持低版本浏览器,history不是那么友好
    4. history模式部署需要后端配置,当用户刷新页面时会向真的向服务器请求资源,并携带有url,后端不做处理的话会报404。需要后端配置一下apache或是nginx的url重定向,重定向到入口文件(index.html)。
    5. 路由的哈希模式是利用了window.onhashchange事件,当url中的哈希值(#后面的值)发生变化,就会自动调用hashchange的监听事件,在hashchange的监听事件内可以得到改变后的url,这样能够找到对应页面进行加载
       window.addEventListener('hashchange', () => {
      	  // 把改变后的url地址栏的url赋值给data的响应式数据current,调用router-view去加载对应的页面
      	  this.data.current = window.location.hash.substr(1)
      	})
      
    6. history利用的是HTML5 新增的 pushState() 和 replaceState() 方法,在原有的back、forward、go 的基础上,这两个方法提供了对历史记录进行修改的功能。pushState方法、replaceState方法,只能导致history对象发生变化,从而改变当前地址栏的 URL,但浏览器不会向后端发送请求,也不会触发popstate事件的执行
    7. 传统的路由指的是:当用户访问一个url时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发;
      而前端路由是不涉及服务器的,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换

4.4 路由跳转方式

4.5 导航守卫分类

4.5.1 全局守卫导航
4.5.1.1 全局前置守卫 beforeEach

作用:路由每次跳转前都会执行此函数。一般用于跳转前进行判断拦截,例如:是否登录

	const router = new VueRouter({
	  mode: 'history',
	  base: process.env.BASE_URL,
	  scrollBehavior: () => ({ y: 0 }),
	  routes: []
	})
	router.beforeEach((to, from, next) => {
	// to:即将要进入的目标 路由对象
	// form:当前导航正要离开的路由
	// next :执行下一步操作
	  // 判断是否登录
	  if (getToken()) {
	    /* has token*/
	    if (to.path === '/login') {
	      next({ path: '/' })
	      NProgress.done()
	    } else {
	      next()
	    }
	  } else {
	    // 没有token
	    if (whiteList.indexOf(to.path) !== -1) {
	      // 在免登录白名单,直接进入
	      next()
	    } else {
	      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
	      NProgress.done()
	    }
	  }
	})
4.5.1.2 全局后置守卫 afterEach

作用:路由每次跳转结束都会执行此函数。

router.afterEach((to, from) => {// ...})
4.5.2 组件守卫导航
  • beforeRouteEnter 导航确认之前调用,新的组件(页面)还没被创建。可以针对不同页面进行特殊处理。
  • beforeRouteUpdate 动态路由发生变化时调用
  • beforeRouteLeave 用户离开前调用
     beforeRouteEnter(to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
        console.log('beforeRouteEnter')
        next(vm => {
          // 通过 `vm` 访问组件实例
        })
      },
      beforeRouteUpdate(to, from, next) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
        // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
        next()
      },
      beforeRouteLeave(to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
        console.log('beforeRouteLeave')
        next()
      },
    
4.5.3 路由独享守卫
  • 在创建路由时写对应的路由守卫

    const router = new VueRouter({
    		  routes: [
    		    {
    		      path: '/foo',
    		      component: Foo,
    		      beforeEnter: (to, from, next) => {
    		        // ...
    		      }
    		    }
    		  ]
    		})
    

五、 javascript

5.1 深拷贝

  • 为什么需要深拷贝
    希望在改变新数组(对象)时,不改变原数组(对象)。
  • 造成需要深拷贝的原因
    数据类型有两种:基本数据类型和引用数据类型。所有的变量都存在栈内存中,引用数据类型的变量存在栈内存,实际数据存在堆内存。引用数据类型在栈内存中存储的只是一个内存地址。赋值时也是把这个地址赋值给另一个变量。
  • 深拷贝的思路
    1. 判断数据源的数据类型,是数组还是对象,定义出返回的变量finalResult
    2. 循环数据源(for in 循环,数组对象都可循环)
    3. 判断每一项的数据类型
    4. 如果是数组或者对象,就再次调用函数,将当前数据当参数传入函数
    5. 如果是引用类型的其他数据,如Date、Error、RegExp、Set等,通过使用constructor属性创建对应的数据类型( new 数据.constructor(数据))并进行赋值
    6. 剩余的直接进行赋值
    7. 最后将开始定义的变量finalResult 返回
  • 实现深拷贝的方案
    • 使用JSON.stringify和JSON.parse,若有undefined、function,拷贝后丢失,Date、Error、RegExp等引用类型的数据会与初始数据不一样。
    let obj = {
        username: "cat",
        age: 10,
        color: 'gary',
        sex: undefined
    }
    // 将对象转换为json字符串形式
    let tempObj = JSON.stringify(obj);
    // 将转换而来的字符串转换为原生js对象
    let newObj = JSON.parse(tempObj );
    console.log(newObj) // {username: "cat",age: 10, color: 'gary',}
    newObj.age  = 5;
    console.log(obj.age);  // 10
    console.log(newObj.age); // 5
    
    • 使用插件lodash 相关代码

    • 递归

    • 使用Jquery的extend函数
      $.extend( [deep ], target, object1 [, objectN ] )
      deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
      target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
      object1 objectN可选。 Object类型 第一个以及第N个被合并的对象

      let obj = {
         username: "cat",
          age: 10,
          color: 'gary',
          sex: undefined,
          arr: [1,2,3,4]
       }
      	
      let newObj = $.extend(true,{},obj);
      newObj.arr[0]  = 5;
      console.log(obj.arr[0]);  // 1
      console.log(newObj.arr[0]); // 5
      
    • 使用concat函数 只能是数组的拷贝,不能拷贝对象

5.2 排序函数

  • 冒泡排序
  • 快速排序
  • 选择排序
  • 插入排序
  • 归并排序
  • sort函数排序

5.3 去重函数

  • Set
  • reduce函数
  • Map
  • for循环 + 定义数组变量 + Array.includes(判断是否存在的任意函数)
  • filter + Map
  • filter + Array.includes

5.4 常用的es6方法

  • 箭头函数 ()=>{}
  • 扩展运算符 …
  • 解构赋值 {total, rows} = res.data
  • Map
  • Set函数
  • cosnt
  • let
  • 模板字符串

5.5 闭包

5.5.1 闭包定义
  1. 闭包:可以在外部访问函数内部的变量,一般是返回一个函数,函数内部引用了变量
  2. 内存泄漏:指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory
  3. 内存溢出:指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光
5.5.2 闭包的特点
  1. 持久性存储:当内存中的值失去引用时便会被垃圾回收机制处理释放内存,但因为闭包导致某块内存永远存在。
    let fn = null;
    function f1() {
        let a = 666;
        function f2(){
            console.log(a);
        }
        fn = f2;
    }
    f1()
    fn()//-------666
    
    正常情况f1() 执行结束后,其生命周期就结束了,外部无法访问到变量a,但是因为f2的引用赋值给了fn,所以f2被保留了下来,又因为f2中的a依赖f1,所以f1也被保留了下来。因此在f1执行结束后还可以访问到变量a
  2. 导致内存泄漏:闭包会将数据持久性存储,每次外部函数触发,都会开辟一块新的内存,函数调用的那块内存会被长久占用而不能被垃圾回收机制释放,就会导致内存泄漏。
    function init() {
         var a = 0;
         return function (){
            console.log(++a);
         }
     }
     let resA = init();
     let resB = init();
     resA()//----1
     resA()//----2
     resB()//----1
     resB()//----2
    
    每次调用都是重新开始,重新开辟的空间,之前的也不会被释放
5.5.3 闭包中是否可以使用await

5.6 函数的this指向问题

5.7 冒泡和捕获**

5.7.1 定义
  • 事件冒泡:事件由子元素传递给父元素的过程就做事件冒泡。
  • 事件捕获:事件由父元素传递给子元素的过程叫做事件捕获。
5.7.2 区别
  1. 冒泡和捕获就是事件的执行顺序不一样。
  2. 冒泡是先子元素,在层层向外层触发(false)
  3. 捕获是先外层触发,再层层向内层触发,直到触发的元素(true)
5.7.3 阻止事件冒泡的方式
  • event.stoppropagation() 阻止事件冒泡
  • addEventListener(()=>{}, false)
  • vue 修饰符 .stop
5.7.4 阻止默认行为
  • event.preventDefault()
  • vue 修饰符 .prevent
5.7.5 button的默认行为
  • button的 type = submit 默认提交表单
  • button的type = reset 默认行为表单重置
  • button的type = button 就会执行do nothing
  • 未定义button type的,IE浏览器默认的是button类型,其他浏览器默认的是submit类型的

5.8 v-if和v-for

5.8.1 v-if和v-show 的区别
  • 共同点:都可以控制元素的显示与隐藏
  • 不同点
    • v-if 但条件为false时不渲染元素,只当条件为true时才显示
    • v-show 是条件不管是否为true都渲染,当为true时再显示
5.8.2 vue2 和vue3 关于vi-if和v-for的区别
  • vue2 中v-for优先级高于v-if, 造成性能上的浪费,
  • vue3 v-if比v-for的优先级高,导致v-if 使用不了v-for中的变量
5.8.3 如何解决v-for和v-if 不作用于同一个元素上
  • 使用computed 过滤出需要展示的数据
  • 使用template 将for 循环包裹
  • 使用v-show

六、 计算机网络

6.1 常见的浏览器内核

  • IE浏览器内核:Trident内核,也是俗称的IE内核;
  • Chrome浏览器内核:统称为Chromium内核或Chrome内核,以前是Webkit内核,现在是Blink内核
  • Firefox浏览器内核:Gecko内核,俗称Firefox内核;
  • Safari浏览器内核:Webkit内核
  • Opera浏览器内核:最初是自己的Presto内核,后来是Webkit,现在是Blink内核
  • 360浏览器、猎豹浏览器内核:IE+Chrome双内核
  • 搜狗、遨游、QQ浏览器内核:Trident(兼容模式)+Webkit(高速模式);
  • 百度浏览器、世界之窗内核:IE内核;
  • 2345浏览器内核:以前是IE内核,现在也是IE+Chrome双内核;

6.2 浏览器引擎(内核)

  • 浏览器内核原来是指渲染引擎js引擎,随着js引擎越来越独立,现在内核更倾向于渲染引擎。
  • 浏览器内核负责HTML解析、布局、渲染等相关工作,JavaScript引擎负责解析、执行JavaScript代码。
  • 浏览器地址栏输入 chrome://version 进行查看js引擎和浏览器内核(部分可以看见)

6.3 输入ip到页面显示的过程

  • 用户输入域名(IP地址),过DNS解析找到对应的ip地址
  • 根据ip地址向服务器发送请求
  • 建立TCP连接(三次握手)
  • 连接建立完成后,发送http请求
  • 服务器根据请求作出http响应
  • 浏览器得到响应的内容,进行解析渲染并展示
  • 断开连接(四次挥手)

6.4 浏览器渲染的过程

  • 解析html构建DOM树(DOM Tree)
  • 解析css构建CSS规则树(CSSOM Tree)
  • 将DOM树和CSS规则树结合在一起构建成渲染树(Render tree)
  • 根据生成的渲染树,进行回流(reflow),得到节点的几何信息(大小、位置等)
  • 重绘(repaint):根据回流得到的几何信息,得到节点的绝对像素
  • display:将像素发送给GPU,最后通过调用操作系统Native GUI的API绘制,展示在页面上
  • 遇到 ``` 2. 使用style的cssText属性 ```javascript const oDiv = this.$refs.userName // += 是在原来的元素上添加cssText的样式,原来的样式也保留 // ;padding 前添加一个分号是为了兼容IE,IE不能实现样式累加 oDiv.style.cssText += ';padding:5px; border:1px solid #000; margin:5px;'; ```
    7.3.2 批量操作DOM

    思路:核心是使元素脱离标准流,操作结束再放回标准流中。(不在标准流中的元素修改不会触发回流)

    7.3.2.1 隐藏元素

    原因:在隐藏ul和显示ul的时候,触发了两次回流,给ul添加每个li的时候没有触发回流

    let oUl = document.getElementById("test")
    oUl.style.display = 'none';
    for(var i=0;i<data.length;i++){
        var oLi = document.createElement("li");
        oLi.innerText = data[i].name;
        oUl.appendChild(oLi);
    }
    oUl.style.display = 'block';
    
    7.3.2.2 使用文档碎片

    思路:创建文档碎片,将所有li先放在文档碎片中,等都放进去以后,再将文档碎片放在ul中

    let oUl = document.getElementById("test")
    // 创建文档碎片
    let fragment = document.createDocumentFragment();
    for(var i=0;i<data.length;i++){
        var oLi = document.createElement("li");
        oLi.innerText = data[i].name;
        fragment.appendChild(oLi);
    }
    oUl.appendChild(fragment);
    
    7.3.2.3 拷贝节点

    思路:将ul拷贝一份,将所有li放在拷贝中,等都放进去以后,使用拷贝替换掉ul

    let oUl = document.getElementById("test")
    // 拷贝节点
    let newUL = oUl.cloneNode(true);
    for(var i=0;i<data.length;i++){
        var oLi = document.createElement("li");
        oLi.innerText = data[i].name;
        newUL.appendChild(oLi);
    }
    oUl.parentElement.replaceChild(newUl, oUl);
    
    7.3.2.4 其他
    1. 避免设置多层的内联样式。样式层级过多会影响重绘重排效率。
    2. 避免使用table布局。很小的变动会引起table重排。可用ul li span等标签生成table表格
    3. 对于复杂的动画,尽量将元素设置为绝对定位。操作元素的定位属性可以只使这一个元素回流,如果不是,容易引起其父元素和子元素的回流。

你可能感兴趣的:(面试,职场和发展)