前端笔记(Vue题目)

101 阅读3分钟

1. v-show 和 v-if 的区别

  • v-show 通过 CSS display控制显示和隐藏
  • v-if 组件真正的渲染和销毁,而不是显示和隐藏
  • 频繁切换显示状态用 v-show ,否则用 v-if

2. 为何在v-for中使用key

  • 必需用key,且不能是index和random
  • diff算法中通过tag 和 key 来判断,是否是sameNode
  • 减少渲染次数,提示渲染性能

3. 描述Vue组件生命周期(父子组件)

  • 单组件生命周期图
  • 父子组件生命周期关系(类似洋葱模型)

4. Vue组件如何通讯

  • 父子组件propsthis.$emit
  • 自定义事件 event.$no event.$off event.$emit
  • vuex

5. 描述组件渲染和更新的过程

msedge_jLWPTHaGf8.png

6. 双向数据绑定 v-model 的实现原理

  • input元素的value = this.name
  • 绑定input事件this.name = $event.target.value
  • data 更新触发 re-render

7. 对MVVM的理解

MVVM

8. compuited 有何特点

  • 缓存,data不变不会重新计算

9. 为何组件data必需是一个函数

msedge_GzKGYtiEki.png

  • 根本上.vue会编译为一个class,使用组件相当于对组件进行实例化,如果data不是函数,根据原型链组件会使用同一个原型的data即组件的data会共享。如果data是一个函数,则在实例化当中都会执行函数,data就会在闭包之中。不会向互影响

10. ajax请求应该放在哪个生命周期

  • mounted
  • JS是单线程的,ajax异步获取数据
  • 放在mounted之前没有用,只会让逻辑更加混乱

11. 如何将组件所有props 传递给子组件?(细节知识点,优先级不高)

  • $props
  • <User v-bind= "$props" />

msedge_xyeZSQkFnC.png

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

  • mixin
//index.vue
<template>
    <div>
        <MixinDemo/>
    </div>
</template>
<script>

import MixinDemo from './MixinDemo'

export default {
    components: {
        MixinDemo
    },
}
</script>
// MixinDemo.vue
<template>
    <div>
        <p>{{name}} {{major}} {{city}}</p>
        <button @click="showName">显示姓名</button>
    </div>
</template>

<script>
import myMixin from './mixin'
export default {
    mixins: [myMixin], // 可以添加多个,会自动合并起来
    data() {
        return {
            name: '双越',
            major: 'web 前端'
        }
    },
    methods: {
    },
    mounted() {
        // eslint-disable-next-line
        console.log('component mounted', this.name)
    }
}
</script>
// mixin.js
export default {
    data() {
        return {
            city: '北京'
        }
    },
    methods: {
        showName() {
            // eslint-disable-next-line
            console.log(this.name)
        }
    },
    mounted() {
        // eslint-disable-next-line
        console.log('mixin mounted', this.name)
    }
}

13. 何时加载异步组件?

  • 加载大组件
  • 路由异步加载
     <!-- 异步组件 -->
   <FormDemo v-if="showFormDemo"/> // 定义异步组件
   <button @click="showFormDemo = true">show form demo</button> // 设置按钮,点击事件触发showFormDemo为true

<script>
//import FormDemo from '../BaseUse/FormDemo' //设置同步组件
export default {
    components: {
       FormDemo: () => import('../BaseUse/FormDemo'),//设置异步组件
    },
}
</script>

14. 何时需要使用keep-alive?

  • 缓存组件,不需要重复渲染
  • 如多个静态的tab页的切换
  • 优化性能
// 组件和动态组件结合使用案例
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

15. 何时需要使用beforeDestroy(生命周期函数)

  • 绑定自定义事件event.$off
  • 清除定时器
  • 解绑自定义的DOM事件,如 window scroll 等

16. 什么是作用域插槽

//index.vue
<ScopedSlotDemo :url="website.url"> 
            <template v-slot="slotProps"> //定义一个参数接收传来的值
                {{slotProps.slotData.title}} //使用
            </template>
        </ScopedSlotDemo>
//ScopedSlotDemo.vue
<template>
    <a :href="url">
        <slot :slotData="website"> // 定义一个值用来传给父组件
            {{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
        </slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
        return {
            website: {
                url: 'http://wangEditor.com/',
                title: 'wangEditor',
                subTitle: '轻量级富文本编辑器'
            }
        }
    }
}
</script>

17.Vuex中的action和mutation有何区别

  • action中处理异步,mutation不可以
  • mutation做原子操作(即每次做一个操作)
  • action 可以整合多个mutation

18. Vue-router常用的路由模式

  • hash默认
  • H5history(需要服务端支持)

19. 如何配置Vue-router异步加载

export default new VueRouter({
    router:[
        {
            path:'/',
            component:()=>import(
                /* webpackChunkName:"navigator"*/
                './../components/Navigator' 
            )
        },{
            path:'/feedback',
            component:() => import(
                /*webpackChunkName:"feedback"*/
                './../components/FeedBack'
            )
        }
    ]
})

20. 请用vnode 描述一个DOM结构

<div id="div1" class="container">
    <p>vdom</p>
    <ul style="font-size:20px">
        <li>a</li>
    </ul>
</div>
{
    tag:'div',
    props:{
        id:'div1',
        class: 'container'
    }
    children:[
        {
            tag:'p',
            children:'vdom'
        },{
            tag:'ul',
            props:{
                style:'font-size:20px'
                children:[
                    {
                        tag:'li',
                        children:'a'
                    }
                ]
            }
        }
    ]
}

21. 监听data变化的核心API是什么

  • Object.defineProperty
  • 以及深度监听、监听数组 详细查看原理重点
// 触发更新视图
function updateView() {
    console.log('视图更新')
}

// 重新定义数组原型 用于将数组进行监听
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
    arrProto[methodName] = function () {
        oldArrayProperty[methodName].call(this, ...arguments)
        // Array.prototype.push.call(this, ...arguments)
        updateView() // 触发视图更新
    }
})

// 重新定义属性,监听起来
function defineReactive(target, key, value) {
    // 深度监听
    observer(value)

    // 核心 API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                // 深度监听
                observer(newValue)

                // 设置新值
                // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
                value = newValue

                // 触发更新视图
                updateView()
            }
        }
    })
}

// 监听对象属性
function observer(target) {
    if (typeof target !== 'object' || target === null) {
        // 不是对象或数组
        return target
    }

    // 污染全局的 Array 原型
    // Array.prototype.push = function () {
    //     updateView()
    //     ...
    // }

    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }

    // 重新定义各个属性(for in 也可以遍历数组)
    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}

// 准备数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: '北京' // 需要深度监听
    },
    nums: [10, 20, 30]
}

// 监听数据
observer(data)

// 测试
 data.name = 'lisi'
 data.age = 21
 console.log('age', data.age)
 data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
 delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
 data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组

22. Vue 如何监听数组变化

  • Object.defineProperty不能监听数组变化
  • 重新定义原型,重写push pop 等方法,实现监听
  • Proxy可以原生支持监听数组变化

23. 请描述响应式原理

  • 监听data变化 (即第21题)
  • 组件渲染和更新的流程 (即第5题)

24. diff算法的时间复杂度

  • O(n)
  • O(n^3)的基础上做了一些调整

25. 简述diff算法的过程

  • patch(elem,vnode) 和 patch(vnode,newVnode)
  • patchVnode 和 addVnodes 和 removeVnodes
  • updateChildren(key的重要作用)

26.Vue为何是异步渲染,$nextTick有何用?

  • 异步渲染(以及合并data修改),以提高渲染性能
  • $nextTick在DOM更新完之后,触发回调

27.Vue常见性能优化方式

  • 合理使用 v-showv-if
  • 合理使用 computed
  • v-for 时加key,以及避免和v-if同时使用
  • 自定义事件、DOM事件及时销毁
  • 合理使用异步组件
  • 合理使用keep-alive
  • data层级不要太深
  • 使用vue-loader在开发环境做模板编译(预编译)