框架知识 - Vue

2020-04-22  本文已影响0人  木头就是我呀

6 道面试题

  1. v-show & v-if 的区别
v-show 元素在树上  只是display去控制是否显示
v-if 元素不上树

使用哪个  具体看该宿主元素是否频繁切换显示
  频繁切换:如选项卡等,使用v-show
  只显示一遍:使用v-if
  1. 为啥 v-for 中要用key
因为vue使用的是虚拟dom,即vdom,且在做视图更新时是异步的,会将多次更新融合在一起,
并比较新的vdom和已存在的vdom之间的差别,即diff算法。
在diff算法中,key作为判断两个vnode是否一致的两个条件之一(sel&&key),
当sel&key有一个不一样,diff算法中就认为两个节点不是一样的,就会增加新的或更新掉旧的。
在v-for生成的元素集合中,sel肯定是一样的,所以key就作为了唯一关键判断各自的方式。
  - 如果不加key,(或者使用index - unshift()),在diff算法中的updateChildren中的sameChild方法就会认为两个
    节点是相同的,就会深度比较,深度比较的时候就会出现一些值的错误(污染),比如checkbox的选中,在
     var elm = vnode.elm = oldVnode.elm;这一步,新的vnode就会被重新赋值,产生不必要的错误。
  - 如果使用key,当unshift一个data时,每个元素都是独立的,因为key不一致,所以在updateChildren中的sameChild方法
    中就会认为是false,此时,新添加的元素对应的vnode就在oldVnodes中找不到,就会preInsert,而不会和
    原有的vnode在深度比较中污染。

v-for中不适用key或者key使用不当  造成的问题:
  1. checkbox列表,选中第一项,再unshift()一个元素,此时选中的还是第一项。有问题。
  1. 描述Vue组件生命周期(有无父子组件的情况)

Vue生命周期详解
当有父子组件同时存在的时候:

 打开页面
 父:beforeCreate  created  beforeMount
 子:beforeCreate  created  beforeMount  mounted
 父:mounted
 销毁组件
 父:beforeDestroy 
 子:beforeDestroy  destroyed
 父:destroyed
image.png
  1. Vue组件如何通讯
值得学习:eventBus进行mixin模式的封装  https://juejin.im/post/5bea35acf265da614e2b9f4b
1. 父子组件  
    父组件:声明xxx函数到子组件上   子组件通过$emit('xxx')进行触发
2. 平行组件(层级较深的组件)
    使用自定义事件,通过公用一个new Vue() 进行使用$emit('xxx')  $on('xxx')  $('xxx')【一定记得用完销毁】
3. 毫不相干的组件
    可以使用vuex进行状态管理
4. 当前组件传值给后面的子孙组件
    可以使用provide/reject
    在父组件
    provide:{
      xxx:111
    }
    在子组件就可以愉快的使用了
    reject : [ 'xxx' ]
  1. 描述组件渲染和更新的过程
- “组件渲染和更新的过程”  简单描述一下
      初次渲染
      1. initState -> 双向数据绑定,监听getter,setter
      2. $mount阶段,将template编译成render函数
      3. 执行render函数,读取到data中的数据,读到了,就触发了getter,读不到,就不触发(watcher)。然后生成vnode
      4. 进行patch(elem,vnode)
      
      更新
      1. 修改数据,触发属性setter
      2. 拿到收集到dep中的watcher,派发更新
      3. 触发render watcher的render回调(重新执行render函数)
      4. 生成新的vnode
      5. patch(vnode,newVNode)
  1. 双向数据绑定v-model的实现原理
1. input元素的value = this.name
2. 绑定oninput事件 this.name = $event.target.value
3. data更新就会触发re-render

一些概念

0. 怎么理解的组件化
  - 很久之前(前后端不分离,asp、jsp、php等模板时期)就有了组件化<%@includefile="xxx" %>
  - node.js中也有组件化<%- include('xxx',{data:{}}); %>
  目前vue、react与传统组件化最大的区别:数据驱动视图
  解放对dom的操作,更关注数据、业务。

----  vue 的十分总要的三个重点:

 ~ 响应式   ~ 模板编译   ~ 虚拟dom

1.如何理解mvvm模型
  会画出来mvvm的模型图 
image.png
3. vue的响应式
  实现原理?
  Object.defineProperty(data,()=>{})
  实现代码
<script>
    // 更新视图
    function updateView() {
        console.log('视图更新了');
    }

    // 处理数组
    let newArray = Object.create(Array.prototype)
    let willMethods = ['push']
    willMethods.forEach(m=>{
        newArray[m] = function () {
            updateView()
            return Array.prototype[m].apply(this,arguments)
        }
    })

    // 真正监听
    function observerReal(target, key, value) {
        observer(value)
        Object.defineProperty(target, key, {
            get() {
                return value
            },
            set(newVal) {
                value = newVal
                updateView()
            }
        })
    }

    // 判断数据类型以及是否需要监听
    function observer(obj) {
        if (obj == null || typeof obj !== 'object') {
            return
        }

        if(obj instanceof Array){
            obj.__proto__ = newArray
        }

        for (let key in obj) {
            observerReal(obj, key, obj[key])
        }
    }

    // 待监听的数据
    let data = {
        name: 'whh',
        age: 18,
        friends: [1, 2, 3],
        info: {
            city: 'bj'
        }
    }

    // 进行监听
    observer(data)
</script>

Object.defineProperty()的一些缺点?
  1. 在监听数据的时候,是一次性递归深度监听全部的,计算量大
  2. 无法监听‘新增属性’&‘删除属性’

4. 虚拟DOM && diff算法
  - vdom是实现vue和React的重要基石
  - diff算法是vdom中最最核心、最关键的部分
  - render函数,类似vdom,便于理解

  为什么出现虚拟dom?有什么历史原因?
  - dom操作十分耗时、耗费性能
  - 将dom的一些计算,交给js去做,将最小的改动交给dom去更新
  
  翻译下面的html标签到vdom
    <div id="div1" class="container">
        <p>vdom</p>
        <ul style="font-size: 20px">
            <li>a</li>
        </ul>
    </div>
    <script>

    // 将上述的html标签转义为vdom
    let vdom = {
        tag:'div',
        props:{
            id:'div1',
            className:'container'
        },
        children:[
            {
                tag:'p',
                textNode:'vdom',
            },
            {
                tag: 'ul',
                props: {
                    style:'font-size:20px'
                },
                children:[
                    {
                        tag:'li',
                        textNode: 'a'
                    }
                ]
            }
        ]
    }
    </script>
 
5. 学习vdom的库是什么?
  snabbdom 库 (vue是参考的该库实现的vdom和diff)
  https://github.com/snabbdom/snabbdom

简单实践snabbdom

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>snabbdom</title>
</head>
<body>
<div id="container">

</div>
<button id="changeBtn">change button</button>

<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>

<script>
    const snabbdom = window.snabbdom
    // 定义patch
    const patch = snabbdom.init({
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners,
    })

    // 定义h
    const h = snabbdom.h
    let container = document.getElementById('container')

    // 生成vnode
    const vnode = h('ul#list',{},
        [
            h('li.item',{},'Item 1'),
            h('li.item',{},'Item 2')
        ]
    )

    patch(container,vnode)

    let changeBtn = document.getElementById('changeBtn')
    changeBtn.addEventListener('click',function () {
        const newVNode = h('ul#list',{},
            [
                h('li.item',{},'Item 1.1'),
                h('li.item',{},'Item 2')
            ]
        )
        patch(vnode,newVNode)
    })
</script>
</body>
</html>
6. diff算法
  找出两个vdom之间的最小区别,进行更新

7. 树diff的时间复杂度
  On^3  在左边第一个节点开始,每一个节点,就遍历右边一遍,此时就是On^2,
  并且在遍历时找到差异后还要计算最小转换方式,就是On^3。1000个节点,就要遍历1亿次,算法几乎不可用。

  Vue和React的解决方案比较粗暴,即遍历左边的时候,同位置去右边拿值,相同就不变,
  不同就替换或删掉,一次循环就结束,即时间复杂度为On。
  - 只比较同一层级,不跨级比较
  - tag不相同,直接删掉重建,不再深度比较
  - tag和key,两者都相同,则认为是相同的节点,不再深度比较。
snabbdom的patch函数的实现逻辑(diff算法的主要逻辑)
8. vue的模板
    - 模板是什么  简单描述一下

      1. 模板不是html,其有指令、插值、JS表达式、能实现判断、循环
      2. html是标签语言,图灵不完备;js可以实现循环、判断,是图灵完备的语言。
      3. 因此,模板肯定是转换成js代码,才可以实现各种功能

    - “组件渲染和更新的过程”  简单描述一下
      初次渲染
      1. initState -> 双向数据绑定,监听getter,setter
      2. $mount阶段,将template编译成render函数
      3. 执行render函数,读取到data中的数据,读到了,就触发了getter,读不到,就不触发(watcher)。然后生成vnode
      4. 进行patch(elem,vnode)
      
      更新
      1. 修改数据,触发属性setter
      2. 拿到收集到dep中的watcher,派发更新
      3. 触发render watcher的render回调(重新执行render函数)
      4. 生成新的vnode
      5. patch(vnode,newVNode)

vue组件渲染和更新的过程
    - vue是使用什么工具将模板编译到render函数的?

        vue-template-complier 工具

    - with语法是什么意思
    
    // 使用with,能改变{}内自由变量的查找方式
    // 将{}内自由变量,当做obj的属性去查找,找不到就报错
    let obj = {a:100,b:200}
    with (obj) {
        console.log(a); // 100
        console.log(b); // 200
        console.log(c); // 报引用错误 ReferenceError: c is not defined
    }

    - vue 使用vue-template-compiler 将template编译成render函数

        let template = `<div>{{name}}</div>`
        compile.compile(template).render

    - vue源码中的一些简写 -c  -s  -v

        -c createElement,类似于snabbdom中的h函数,返回的也是vnode
        -s toString() 转换成字符串
        -v createTextVNode 创建一个文本节点

    - vue渲染的时候是同步渲染还是异步渲染呢?
      
      是异步渲染。就比如:$nextTick();
      目的:汇总data的修改,一次性更新视图;减少DOM的操作次数,提升性能。

    - 前端路由的原理(通用)
      路由模式: hash  history
    
     -  hash的特点,即为什么hash可以作为前端路由的实现模式?
      
      1. hash的变化会触发网页的跳转,即浏览器的前进与后退(最根本)
      2. hash的变化不会刷新页面,这也是SPA项目必备的特点
      3. hash是不会提交到server端,即使强制刷新
    
      - hash路由是基于哪个事件监听实现的?

      window.onhashchange = function(event){}

      - history 简介
      1. history是一个规范的路由,跳转的时候也不刷新页面
      
      - history 的实现原理
      
      history.pushState && history.replaceState

      - history模式是基于哪个事件监听实现的?
       
      window.onpopstate

      - history为什么需要后端支持?
    
      在某个路由页面比如:www.xxx.com/user/user-detail 页面的时候,
      此时强制刷新,浏览器会向后端请求www.xxx.com/user/user-detail对应的资源,肯定没有,所以会出404错误。
      解决方案: 在后端判断一下,当前项目,无论请求什么页面,都返回index.html(原始页)页面即可

Vue的面试真题演练

1. 对mvvm的理解

   看图

2. computed的特点

  - 缓存,data不变就不会重新计算

3. 为何组件的data必须是一个函数

  - vue文件编译后  是生成一个class,在哪里用到,就new一个出来,如果data不是函数的话,那么new出来的各个对象就
    会公用一个data(在堆里找的是同一个data对象),造成污染。  
    当data是个函数的时候,在new出新组件后,由于function的缘故,会不同组件之间data的作用域,就不会形成污染。

4. ajax请求应该放在哪个生命周期?
  
  mounted
  因为js是单线程的,ajax异步获取数据。
  就算放到created中,created后边要执行mount,边要发起请求,其实没必要,会让流程分支,所以建议放在mounted中。

5. 怎样将组件所有的props传递给子组件?
  
  <User v-bind="$props"/>

6. 如何自己实现v-model?
  
  // 子组件
  <template>
      <div>
          <input type="text" :value="name" @input="$emit('change',$event.target.value)">
      </div>
  </template>

  <script>
      export default {
          name: "index",
          model:{
              prop:'name',
              event:'change'
          },
          props:{
              name:{
                  type:String,
                  default:()=>''
              }
          }
      }
  </script>

  // 父组件
  <VModel v-model="name"></VModel>

7. 多个组件有相同的逻辑,如何抽离?
  
  1.mixin  

8. 何时使用异步组件?

  1. 加载大组件,比如编辑器..
  2. 路由异步加载

9. 何时使用keep-alive
  
  1. 缓存组件,不需要重复渲染
  2. 如多个静态的tab页的切换
  3. 优化性能

10. 何时使用beforeDestory
  
  1. 解绑自定义事件  event.$off
  2. 清除定时器
  2. 解绑自己定义的dom事件,比如window.scroll等,vue自带的会自己解除

11. 什么是作用域插槽
  
  1. 在组件内,将一些不确定的东西交给父节点去补充,并且把渲染时的作用域内的值交出去
  // 父组件  <template v-slot='scope'>scope.xxx</template>
  // 子组件  <slot :scope='scope'>  </slot>

12. vuex中的action和mutation有什么区别
  
  1. action中处理异步,mutation中不可以
  2. mutation中做原子操作
  3. action可以整合多个mutation

13. 请描述响应式的原理

  1. 监听data变化
  2. 组件渲染和更新的流程

14. vue常见性能优化
  
  1. 合理使用v-show  v-if
  2. 合理使用computed
  3. v-for中加key,以及避免和v-if一起使用(v-for优先级高,先把所有代码块都for出来,再看v-if,把不要的代码块去掉,很不优雅),
    解决方案:v-for的对象可以是computed计算后的列表,这样最好;如果实在不行,就用v-show替代v-if,可以共存。
  4. 自定义事件,dom事件及时销毁。
  5. 合理使用异步组件
  6. 合理使用keep-alive
  7. data的层级不要太深,响应式监听的时候递归时间就会长。
  8. webpack层级的优化 ... 待补充
  9. 前端通用性能优化,图片懒加载等等...

# 前端通用性能优化
原则: 空间换时间
    1. 多使用内存,缓存或者其他方法
    2. 减少CPU的计算量,减少网络加载耗时
从何入手:
    让加载更快
        减少资源体积 - 压缩代码
        减少访问次数
            合并代码(合并js等资源)
            SSR服务端渲染(把资源在后端一次组装完成,减少客户端的请求次数)
            缓存(静态资源加hash后缀,根据文件计算hash。文件不变,则会自动触发http缓存机制,返回304)
        使用更快的网络 - CDN
    让渲染更快
        CSS放在head,JS放在body最下面
        尽早开始执行JS,在DOMContentLoaded触发
        懒加载(图片懒加载,上滑加载更多...)
        对DOM查询进行缓存
        避免频繁的DOM操作,使用DocumentFragement片段
        节流、防抖
上一篇下一篇

猜你喜欢

热点阅读