vue基础复习

2,003 阅读10分钟

vue学习文档

前言

本来只为面向面试总结一下vue文档中本身就可以回答的面试问题,本来只想单纯的记录一些概念,不过后来发现如果概念离开代码会变得索然无味,便将其用法也记录在此,方便日后自己查询。只是为了记录自己的学习而已,相当于自己的学习笔记。所有内容均可在vue官方文档中找到,且90%以上都是文档中的原话!

data中的数据响应?

只有实例被创建时就已经存在于data中的属性才是响应的

计算属性vs方法?

1. 计算属性是基于他们的响应式依赖进行缓存的;
2. 每当执行重新渲染时,调用方法将会重新执行函数;

计算属性vs侦听属性?

1. 计算属性用于处理模板中的一些复杂逻辑计算的
2. 侦听属性是用于监听数据变化后处理一些逻辑
3. 侦听属性可以执行异步操作,可以在得到最终结果之前设置中间状态,限制执行该操作的频率

class三元表达式?

:calss="[isActive ? 'activeClass' : '', 'errorClass']"
:calss="[isActive ? 'activeClass' : '']"

style三元表达式?

:style="'width': isActive ? '80px' : '100px'"

v-if和v-show?

1. v-if是真正的条件渲染,因为他会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建
2. v-if也是惰性的,如果在初始渲染时条件为假,则什么也不做---知道条件第一次为真时才会开始渲染条件块儿
3. v-show不管初始条件是什么,元素始终被渲染,冰企鹅额只是简单地基于css进行切换
4. 一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变则使用v-if较好
5. 渲染的如果是个组件的话,v-if会重新执行其生命周期中的函数,而v-show则不会
6. v-if能使用template,而v-show则不能使用

v-if与v-for一起使用?

vue官方文档不推荐一起使用,v-for具有比v-if更高的优先级,v-if会运行于每一个v-for循环中,每循环一次则v-if一次

key的作用?

vue通常会复用已有的元素而不是从头开始渲染,key用来表示这两个元素是完全独立的,不要复用它们

事件修饰符?

.stop阻止单击事件继续传播
.prevent调教事件不再重载页面
.capture事件捕获
.self事件冒泡
.once点击事件将只会触发一次
.passive滚动的你默认行为可立即触发
.enter按下键盘回车键触发
.tab按下【tab】键触发
.delete按下删除键触发
.esc按下【esc】键触发
.ctrl按下【ctrl】键触发
.alt按下【alt】键触发
.shift按下【shift】键触发
.lazy在"change"时而非"input"时更新
.number自动将用户输入转化为数值类型,需要给input框加一个type="number"
.trim自动过滤用户输入的首尾空白字符
.native在一个组件的根元素上监听原生事件
// 温馨提示:修饰符可以串联

自定义事件修饰符:

Vue.config.keyCodes = { 
    v: 86, f1: 112, // camelCase 不可用 
    mediaPlayPause: 179, // 取而代之的是 kebab-case 且用双引号括起来 
    "media-play-pause": 179, 
    up: [38, 87] 
}

data必须是一个函数?

当一个组件被定义,`data` 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 `data` 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 `data` 函数,每次创建一个新实例后,我们能够调用 `data` 函数,从而返回初始数据的一个全新副本数据对象。

单个根元素?

用来规定单页面文件的入口

单向数据流?

父级的prop的更新会向下流动到子组件中,但反过来则不行

全局注册?

vue.component(name, url); // 简写的,用过的都懂

插槽?

需要插入的子组件中写入- <slot name="left-btn"></slot>
父组件调用子组件时,将插入内容包裹
<componentName>
    <button slot="left-btn">按钮</button>
<componentName>

动态组件?

keep-alive:第一次创建时会被缓存起来。避免反复重新渲染导致的性能问题。
// 温馨提示:要求被切换到的组件都有自己的名字

异步组件?

components: {
  'my-conmponent': () => import('./my-async-component');
}
温馨提示:components有s

生命周期?

beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。(创建前)
created: 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,`$el` property 目前尚不可用。(创建后)
beforeMount:在挂载开始之前被调用:相关的 `render` 函数首次被调用。(挂载前)
mounted:实例被挂载后调用,这时 `el` 被新创建的 `vm.$el` 替换了。如果根实例挂载到了一个文档内的元素上,当 `mounted` 被调用时 `vm.$el` 也在文档内。(挂载后)
beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。(dom更新前)
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。(dom更新后)
activated:被 keep-alive 缓存的组件激活时调用。
deactivated: 被 keep-alive 缓存的组件停用时调用。
beforeDestroy: 实例销毁之前调用。在这一步,实例仍然完全可用。(销毁前)
destroyed:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。(销毁后)
errorCaptured: 当捕获一个来自子孙组件的错误时被调用。

transition?

// 用法一:
<div id="demo"> 
    <button v-on:click="show = !show"> Toggle </button> 
    <transition name="fade"> 
        <p v-if="show">hello</p> 
    </transition> 
</div>
.fade-enter-active, .fade-leave-active { 
    transition: opacity .5s; 
} 
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { 
    opacity: 0; 
}
// 用法二:(引入第三方动画库)
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3"> 
    <button @click="show = !show"> Toggle render </button> 
    <transition name="custom-classes-transition" enter-active-class="animated tada" leave-active-class="animated bounceOutRight" > 
        <p v-if="show">hello</p>
    </transition> 
</div>
// 用法三: (用js操作动画)
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script> 
<div id="example-4">
    <button @click="show = !show"> Toggle </button> 
    <transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false" > 
        <p v-if="show"> Demo </p> 
    </transition> 
</div>
new Vue({ 
    el: '#example-4', 
    data: { show: false }, 
    methods: { 
        beforeEnter: function (el) { 
            el.style.opacity = 0 el.style.transformOrigin = 'left' 
        }, 
        enter: function (el, done) { 
            Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
            Velocity(el, { fontSize: '1em' }, { complete: done }) 
        }, 
        leave: function (el, done) { 
            Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 }) 
            Velocity(el, { rotateZ: '100deg' }, { loop: 2 }) 
            Velocity(el, { rotateZ: '45deg', translateY: '30px', translateX: '30px', opacity: 0 }, { complete: done }) 
        } 
    } 
})
// 还有其他的方式,就不一一列举了

自定义指令?

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})
// 如果想注册局部指令,组件中也接受一个 directives 的选项
directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

插件?

// 开发插件
MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}
// 使用插件
Vue.use(MyPlugin)

过滤器

// 局部过滤器
filters: {
  capitalize (value) {
    // 逻辑
  }
}
// 全局过滤器
Vue.filter('capitalize', function (value) {
  //逻辑
})
// 使用
{{ message | capitalize }} or <div v-bind:id="rawId | formatId"></div>
// 温馨提示v-model中使用第二种

关于测试

1. 单元测试:jest,vue有官方插件;mocha,vue有官方插件
2. 组件测试
3. 端到端的测试
// 这些官方文档上都有,不细说

vuex?

const obj = {
    // 初始化值
    state: {
    },
    // 获取值
    getters:{
    },
    // 设置值
    mutations: {
    },
    // 异步
    actions: {
    },
    // 模块儿
    modules: {
    }
}
// 温馨提示:可将其抽离成单个文件-模块儿化

响应式原理?

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

哪些数据vue无法实现响应式?

1.从上述响应式原理可以看出,如果数据未在data中进行初始化,vue就响应不了,解决办法是在vue data中进行数据初始化,或者使用Vue.observable(obj)(该方法让一个对象成为可响应的)
2. Vue 无法检测 对象属性 的添加或移除。由于 Vue 会在初始化实例时对 对象属性 执行 getter/setter 转化,所以 对象属性 必须在 `data` 对象上存在才能让 Vue 将它转换为响应式的。
3.  当你利用索引直接设置一个数组项时,例如:`vm.items[indexOfItem] = newValue`
4.  当你修改数组的长度时,例如:`vm.items.length = newLength`
// 2、3、4均可用vue.set解决,数组还可通过vue已经包裹后的方法解决,而对象可在data中声明初始值进行解决

vue包裹的的数组方法,它们能触发视图更新?

push-方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
pop-方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
shift-方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
unshift-方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
splice- 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
sort-方法用原地算法对数组的元素进行排序,并返回数组。
reverse-方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。

记一种视图不更新的特殊情况

// 通过 `v-once` 创建低开销的静态组件
<div v-once> <h1>Terms of Service</h1> ... a lot of static content ... </div>
这是创建静态内容的方式,会导致模板无法正确更新。

Vue.nextTick?

在修改数据之后立即使用这个方法,获取更新后的 DOM// 当前组件视图都渲染完成后执行
mounted: function () { 
    this.$nextTick(function () {
    }) 
}
// 视图都重绘完成后执行(子组件 DOM 已经更新后)
updated: function () { 
    this.$nextTick(function () { 
    }) 
}

computed?

computed: {
    // 仅读取
    aDouble: function () {
      return this.a * 2
    },
    // 读取和设置
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }

watch?

 watch: {
    data: {
      handler(newVal, oldVal) { /* ... */ },
      deep: true, // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
      immediate: true // 如果是上一级组件传过来的值的变化需要用这个属性,否则监听不到
    }
}

父子组件传值?

父传子: 父组件通过v-bind(:)传递,子组件通过props进行接收;
子组件:通过调用事件和this.$emit传递,父组件@子组件定义的方法名,通过函数参数的形式接收;

props?

props: { 
    // 检测类型 
    height: Number, 
    // 检测类型 + 其他验证 
    age: { 
        type: Number,  // 值的类型
        default: 0,  // 默认值
        required: true, // 是否必填
        validator: function (value) { 
            return value >= 0 // 传值数据校验
        } 
    } 
}

$parent$children

可以调用父组件或者子组件中的方法,并传值。它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信

provide / inject

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

vm.$attrs

包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

vm.$listeners

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

MVVM设计模式?

MVVM模式,模型(Model)-视图(View-视图模型(ViewModel):为视图层(View)量身定做一套视图模型(ViewModel),并在视图模型(ViewModel)中创建属性和方法,为视图层(View)绑定数据(Model)并实现交互。(此处出自《JavaScript设计模式》)

双向绑定实现原理?

1. 创建虚拟dom树 
2. 一旦被检测的数据改变会通过Object.defineProperty定义数据拦截,截取到数据的变化。 
3. 截取到数据变化从而通过订阅--发布者模式触发观察者,从而改变虚拟dom中的具体数据 
4. 通过更新虚拟dom的元素值,从而改变最后渲染dom的值,完成双向绑定的实现

vue指令?

v-text:更新元素的 文本内容。
v-html:插入html元素,不能插入vue模板html。
v-show: 根据表达式之真假值,切换元素的 `display` CSS 属性。
v-if:条件渲染。
v-elseif-else大家都懂。
v-else-ifif-(else-if)-else大家都懂。
v-for:循环渲染。
v-on:绑定事件监听器。
v-bind: 动态地绑定一个或多个html属性,或一个组件 prop 到表达式。
v-model:在表单控件或者组件上创建双向绑定。
v-once:只渲染元素和组件一次。
v-slot:提供具名插槽或需要接收 prop 的插槽。
v-cloak:这个指令保持在元素上直到关联实例结束编译。

路由模式?

1. history: 
2. hash:默认的路由模式带#号
3. abstract

参考:

vue官方文档- cn.vuejs.org/v2/guide/

后记:

在此总结一句话:面试之前请先过一遍技术类的官方文档。原因是很多问题文档上都有原话,其次就是你会从中学到之前知道但不是特别清楚,知道但已忘记又或者之前根本就没留意的知识点!