VUE 面试总结

总结一些vue中必知必会的知识点,让咱们在面试以前可以成竹在胸一点~javascript

1、Vue组件通讯

一、父组件向子组件通讯

方法一:propscss

<template>
    <child :msg="message"></child>
</template>
复制代码
<template>
    <div>{{msg}}</div>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    }
}
</script>
复制代码

方法二:使用$childrenhtml

使用$children能够在父组件中访问子组件。好比调用子组件的方法,并传入值等。vue

二、子组件向父组件通讯

方法一:使用vue事件java

父组件向子组件传递事件方法,子组件经过$emit触发事件,回调给父组件。node

<template>
    <child @msgFunc="func"></child>
</template>

<script>

import child from './child.vue';

export default {
    components: {
        child
    },
    methods: {
        func (msg) {
            console.log(msg); // ssss
        }
    }
}
</script>
复制代码
<template>
    <button @click="handleClick">点我</button>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    },
    methods () {
        handleClick () {
            //........
            this.$emit('msgFunc', 'ssss');
        }
    }
}
</script>
复制代码

方法二:使用$parent面试

使用$parent能够访问父组件的实例,固然也就能够访问父组件的属性和方法了。express

三、非父子组件、兄弟组件之间的数据传递

非父子组件通讯,Vue官方推荐使用一个Vue实例做为中央事件总线数组

意思就是将一个公共状态保存在一个这些组件共用的一个父组件上,这样就可使用子组件通讯父组件的方式来间接的完成通讯了。浏览器

2、new Vue()以后都发生了什么?

vue入口源码以下:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
// initMixin给Vue.prototype添加:
// _init函数,
stateMixin(Vue)
/* stateMixin给Vue.prototype添加: $data属性, $props属性, $set函数, $delete函数, $watch函数, ... */
eventsMixin(Vue)
/* eventsMixin给Vue.prototype添加: $on函数, $once函数, $off函数, $emit函数, $watch方法, ... */
lifecycleMixin(Vue)
/* lifecycleMixin给Vue.prototype添加: _update方法:私有方法,用于更新dom,其中调用_patch产生跟新后的dom, $forceUpdate函数, $destroy函数, ... */
renderMixin(Vue)
/* renderMixin给Vue.prototype添加: $nextTick函数, _render函数, ... */
export default Vue
复制代码

vue源码加载的时候,也就是在new Vue()以前作了一些初始化工做。

new Vue的时候会执行一个_init方法。代码以下:

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
复制代码

从上面的代码咱们看见_init很清淅的干了几件事, 合并相关配置, 初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等

3、组件data为何必须是函数?

由于组件可能被多处使用,但它们的data是私有的,因此每一个组件都要return一个新的data对象,若是共享data,修改其中一个会影响其余组件。

4、vue 为何采用Virtual DOM?

  • 建立真实DOM的代价高:真实的 DOM 节点 node 实现的属性不少,而 vnode 仅仅实现一些必要的属性,相比起来,建立一个 vnode 的成本比较低。

  • 触发屡次浏览器重绘及回流:使用 vnode ,至关于加了一个缓冲,让一次数据变更所带来的全部 node 变化,先在 vnode 中进行修改,而后 diff 以后对全部产生差别的节点集中一次对 DOM tree 进行修改,以减小浏览器的重绘及回流。

  • 虚拟dom因为本质是一个js对象,所以天生具有跨平台的能力,能够实如今不一样平台的准确显示。

  • Virtual DOM 在性能上的收益并非最主要的,更重要的是它使得 Vue 具有了现代框架应有的高级特性。

5、Vue建立的一个大体过程

首先实例化,而后数据绑定,组件挂在、内容替换。

结合上例子分析Vue的生命周期:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>vue生命周期学习</title>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
  <div id="app">
    <h1>{{message}}</h1>
  </div>
</body>
<script> var vm = new Vue({ el: '#app', data: { message: 'Vue的生命周期' }, beforeCreate: function() { console.group('------beforeCreate建立前状态------'); console.log("%c%s", "color:red" , "el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //undefined  console.log("%c%s", "color:red","message: " + this.message) }, created: function() { console.group('------created建立完毕状态------'); console.log("%c%s", "color:red","el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //已被初始化  console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeMount: function() { console.group('------beforeMount挂载前状态------'); console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化  console.log("%c%s", "color:red","message: " + this.message); //已被初始化  }, mounted: function() { console.group('------mounted 挂载结束状态------'); console.log("%c%s", "color:red","el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化  }, beforeUpdate: function () { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, updated: function () { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, beforeDestroy: function () { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, destroyed: function () { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message) } }) </script>
</html>
复制代码

[1] 在beforeCreate和created钩子函数之间的生命周期

在这个阶段开始初始化事件、而且对数据进行了检测(劫持),这时候尚未el。

[2] created钩子函数和beforeMount间的生命周期

  • 首先会判断对象是否有el选项。若是有的话就继续向下编译,若是没有el选项,则中止编译,也就意味着中止了生命周期,直到在该vue实例上调用vm.$mount(el)。此时注释掉代码中:el: '#app',

若是咱们在后面继续调用vm.$mount(el),能够发现代码继续向下执行了

  • 而后,咱们往下看,template参数选项的有无对生命周期的影响。

(1).若是vue实例对象中有template参数选项,则将其做为模板编译成render函数。 (2).若是没有template选项,则将外部HTML做为模板编译。 (3).能够看到template中的模板优先级要高于outer HTML的优先级。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>vue生命周期学习</title>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
  <div id="app">
    <!--html中修改的-->
    <h1>{{message + '这是在outer HTML中的'}}</h1>
  </div>
</body>
<script> var vm = new Vue({ el: '#app', template: "<h1>{{message +'这是在template中的'}}</h1>", //在vue配置项中修改的 data: { message: 'Vue的生命周期' } </script>
</html>
复制代码

那么将vue对象中template的选项注释掉后打印以下信息:

这下就能够想一想什么el的判断要在template以前了~是由于vue须要经过el找到对应的outer template。

在vue对象中还有一个render函数,它是以createElement做为参数,而后作渲染操做,并且咱们能够直接嵌入JSX.

new Vue({
    el: '#app',
    render: function(createElement) {
        return createElement('h1', 'this is createElement')
    }
})
复制代码

因此综合排名优先级: render函数选项 > template选项 > outer HTML.

总之在这个阶段就是判断是否有el,或者template。而后进行处理

[3] beforeMount和mounted 钩子函数间的生命周期

这段时间就是替换$el,将{{message}}转为真正的内容。

[4] mounted

在mounted以前h1中仍是经过{{message}}进行占位的,由于此时挂在到页面上的仍是JavaScript中的虚拟DOM形式。在mounted以后能够看到h1中的内容发生了变化。

[5] beforeUpdate钩子函数和updated钩子函数间的生命周期

当vue发现data中的数据发生了改变,会触发对应组件的从新渲染,前后调用beforeUpdate和updated钩子函数。

[6] beforeDestroy和destroyed钩子函数间的生命周期

beforeDestroy钩子函数在实例销毁以前调用。在这一步,实例仍然彻底可用。 destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的全部东西都会解绑定,全部的事件监听器会被移除,全部的子实例也会被销毁。

6、自定义指令

在代码的实例中

指令钩子函数会被传入如下参数:

el:指令所绑定的元素,能够用来直接操做 DOM 。
binding:一个对象,包含如下属性:

    - name:指令名,不包括 v- 前缀。
    - value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
    - oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。不管值是否改变均可用。
    - expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
    - arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
    - modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
    - vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
    - oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
复制代码

7、Vue.js 插件开发详解

Vue.js 插件开发详解

8、组件style的scoped

问题:在组件中用js动态建立的dom,添加样式不生效。
复制代码
<template>
     <div class="test"></div>
</template>
<script>
    let a=document.querySelector('.test');
    let newDom=document.createElement("div"); // 建立dom
    newDom.setAttribute("class","testAdd" ); // 添加样式
    a.appendChild(newDom); // 插入dom
</script>
<style scoped>
.test{
   background:blue;
    height:100px;
    width:100px;
}
.testAdd{
    background:red;
    height:100px;
    width:100px;
}
</style>
复制代码
  • 结果
// test生效   testAdd 不生效
<div data-v-1b971ada class="test"><div class="testAdd"></div></div>
.test[data-v-1b971ada]{ // 注意data-v-1b971ada
    background:blue;
    height:100px;
    width:100px;
}
复制代码

缘由

<style> 标签有 scoped 属性时,它的 CSS 只做用于当前组件中的元素。 它会为组件中全部的标签和class样式添加一个scoped标识,就像上面结果中的data-v-1b971ada。 因此缘由就很清楚了:由于动态添加的dom没有scoped添加的标识,没有跟testAdd的样式匹配起来,致使样式失效。

  • 解决办法:

去掉scoped便可。

9、Vue 数组/对象更新 视图不更新

data() { // data数据
        return {
          arr: [1,2,3],
          obj:{
              a: 1,
              b: 2
          }
        };
      },
   // 数据更新 数组视图不更新
    this.arr[0] = 'OBKoro1';
    this.arr.length = 1;
    console.log(arr);// ['OBKoro1'];
    // 数据更新 对象视图不更新
    this.obj.c = 'OBKoro1';
    delete this.obj.a;
    console.log(obj);  // {b:2,c:'OBKoro1'}
复制代码

因为js的限制,Vue 不能检测以上数组的变更,以及对象的添加/删除,不少人会由于像上面这样操做,出现视图没有更新的问题。

解决方式:

  • 一、this.$set(你要改变的数组/对象,你要改变的位置/key,你要改为什么value)
this.$set(this.arr, 0, "OBKoro1"); // 改变数组
this.$set(this.obj, "c", "OBKoro1"); // 改变对象
复制代码
  • 二、数组原生方法触发视图更新:

Vue能够监测到数组变化的,数组原生方法:

splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
复制代码

意思是使用这些方法不用咱们再进行额外的操做,视图自动进行更新。

10、Vue 过滤器的做用

过滤器,一般用于后台管理系统,或者一些约定类型,过滤。Vue过滤器用法是很简单,可是不少朋友可能都没有用过。

<!-- 在双花括号中 -->
{{ message | filterTest }}
<!-- 在 `v-bind` 中 -->
<div :id="message | filterTest"></div>
复制代码
export default {    
     data() {
        return {
         message:1   
        }
     },
    filters: {  
        filterTest(value) {
            // value在这里是message的值
            if(value===1){
                return '最后输出这个值';
            }
        }
    }
}
复制代码

用法就是上面讲的这样,能够本身在组件中试一试就知道了,很简单很好用的。

11、列表渲染相关

一、v-for循环绑定model:

input在v-for中能够像以下这么进行绑定,我敢打赌不少人不知道。

// 数据 
data() {
      return{
        // 这里设置 key / value
        obj: {
          ob: "OB",
          koro1: "Koro1"
        },
        // model 存储
        model: {
          ob: "默认ob",
          koro1: "默认koro1"
        }   
      }
  },
// html模板
<div v-for="(value,key) in obj">
   <input type="text" v-model="model[key]"> </div>
// input就跟数据绑定在一块儿了,那两个默认数据也会在input中显示
复制代码

二、v-if尽可能不要与v-for在同一节点使用:

v-for 的优先级比 v-if 更高,若是它们处于同一节点的话,那么每个循环都会运行一遍v-if。

若是你想根据循环中的每一项的数据来判断是否渲染,那么你这样作是对的:

<li v-for="todo in todos" v-if="todo.type===1">
  {{ todo }}
</li>
复制代码

若是你想要根据某些条件跳过循环,而又跟将要渲染的每一项数据没有关系的话,你能够将v-if放在v-for的父节点:

// 数组是否有数据 跟每一个元素没有关系
<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>
复制代码

如上,正确使用v-for与v-if优先级的关系,能够为你节省大量的性能。

12、深度watch和watch当即触发回调

watch不少人都在用,可是这watch中的这两个选项deep、immediate,或许不是不少人都知道,我猜。

  • deep

在选项参数中指定 deep: true,能够监听对象中属性的变化。

  • immediate

在选项参数中指定 immediate: true, 将当即以表达式的当前值触发回调,也就是当即触发一次。

watch: {
    obj: {
      handler(val, oldVal) {
        console.log('属性发生变化触发这个回调',val, oldVal);
      },
      deep: true // 监听这个对象中的每个属性变化
    },
    step: { // 属性
      //watch
      handler(val, oldVal) {
        console.log("默认当即触发一次", val, oldVal);
      },
      immediate: true // 默认当即触发一次
    },
  },
复制代码

Vue.js中 watch 的高级用法

十3、这些状况下不要使用箭头函数:

  • 不该该使用箭头函数来定义一个生命周期方法
  • 不该该使用箭头函数来定义 method 函数
  • 不该该使用箭头函数来定义计算属性函数
  • 不该该对 data 属性使用箭头函数
  • 不该该使用箭头函数来定义 watcher 函数

箭头函数绑定了父级做用域的上下文,this 将不会按照指望指向 Vue 实例。 也就是说,你不能使用this来访问你组件中的data数据以及method方法了。 this将会指向undefined。

十4、路由懒加载写法

// 我所采用的方法,我的感受比较简洁一些,少了一步引入赋值。
const router = new VueRouter({
  routes: [
    path: '/app',
    component: () => import('./app'),  // 引入组件
  ]
})
// Vue路由文档的写法:
const app = () => import('./app.vue') // 引入组件
const router = new VueRouter({
  routes: [
    { path: '/app', component: app }
  ]
})
复制代码

若是咱们的路由比较多的话,是否是要在路由上方引入十几行组件?

第一种跟第二种方法相比就是把引入赋值的一步,直接写在component上面,本质上是同样的。两种方式均可以的,只是第一种是懒加载的方式。

十5、项目启动路由和404

export default new Router({
  routes: [
    {
      path: '/', // 项目启动页
      redirect:'/login'  // 重定向到下方声明的路由 
    },
    {
      path: '*', // 404 页面 
      component: () => import('./notFind') // 或者使用component也能够的
    },
  ]
})
复制代码
相关文章
相关标签/搜索