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也能夠的
    },
  ]
})
複製代碼
相關文章
相關標籤/搜索