vue面試整理

對MVVM的理解?

MVVM是Model-View-ViewModel的縮寫,Model表明數據模型負責業務邏輯和數據封裝,View表明UI組件負責界面和顯示,ViewModel監聽模型數據的改變和控制視圖行爲,處理用戶交互,簡單來講就是經過雙向數據綁定把View層和Model層鏈接起來。在MVVM架構下,View和Model沒有直接聯繫,而是經過ViewModel進行交互,咱們只關注業務邏輯,不須要手動操做DOM,不須要關注View和Model的同步工做。javascript

vue等單頁面應用及優缺點

vue核心是一個響應的數據綁定系統,mvvm,數據驅動,組件化,輕量,簡潔,高效,快速,模塊友好。html

缺點:不支持低版本瀏覽器,最低到IE9,不利於SEO的優化,首頁加載時間較長,不可使用瀏覽器的導航按鈕須要自行實現前進後退。vue

什麼是RESTful API,而後怎麼使用?

RESTful是一個api的標準,無狀態請求。請求的路由地址是固定的。 restful:給用戶一個url,根據method不一樣在後端作不一樣處理:好比post 建立數據,get 獲取數據,put和patch修改數據,delete刪除數據java

vue-cli中src目錄下每一個文件的用途?

1.vue-cli名字改成@vue/cli,因此全局安裝了舊版的要經過npm install vue-cli -g卸載。vue-router

安裝新版vue-clivuex

npm install -g @vue/clivue-cli

2.建立一個項目 vue create hello-world 3.assets文件夾是放靜態資源;components是放組件;router是定義路由相關的配置;view視圖;app.vue是一個應用主組件;main.js是入口文件npm

v-model的原理

<input v-model="msg" />
複製代碼

至關於編程

<input v-bind:value="msg" v-on:input="msg=$event.target.value" />
複製代碼

v-if和v-show的區別

v-show只是在display: none和display: block之間切換,只須要切換CSS,DOM仍是一直保留着,v-show在初始渲染時有更高的開銷,可是切換開銷很小,更適合頻繁切換的場景後端

v-if涉及到vue底層的編譯,當屬性初始爲false時組件不會被渲染,直到條件爲true,而且切換條件時會觸發銷燬/掛載組件,切換時開銷更高,更適合不常常切換的場景

route和router的區別

route是路由信息對象,包括path,params,hash,query,fullPath,matched,name等路由信息參數。

router是路由實例對象,包括了路由的跳轉方法,鉤子函數。

怎麼定義vue-router的動態路由?怎麼獲取傳過來的值

在router目錄下的index.js文件,對path屬性加上:id,

使用router對象的params.id獲取

active-class是哪一個組件的屬性?嵌套路由怎麼定義

active-class是vue-router模塊中router-link組件的屬性

使用children定義嵌套路由

對keep-alive的瞭解

keep-alive是一個內置組件,可以使被包含的組件保留狀態或避免從新渲染,有include(包含的組件緩存)和exclude(排除的組件不緩存)兩個屬性。

vue經常使用修飾符

.prevent: 提交時間再也不重載頁面

.stop:阻止單擊事件冒泡

.self:當事件發生在該元素自己而不是子元素的時候觸發

.capture:事件偵聽,事件發生的時候會調用

組件中data何時能夠適用對象

組件複用時全部組件實例都會共享data,若是data是對象就會形成一個組件修改data之後會影響到其餘全部組件,因此須要將data寫成函數,每次用到就調用一次函數得到新的數據

當咱們使用new Vue()的方式的時候,不管咱們將data設置爲對象仍是函數都是能夠的,由於new Vue()的方式是生成一個根組件,該組件不會複用,也就不存在共享data的狀況

vue-cli如何新增自定義指令?

1.建立局部指令

directives:{
   // 指令名稱
   dir1: {
       inserted(el){
           // 第一個參數是當前使用指令的DOM
           el.style.width = '200px';
           el.style.height = '200px';
           el.style.background = '#000'
       }
   }
}
複製代碼

2.全局指令

Vue.directive('dir2', {
    inserted(el){
        console.log(el)
    }
})
複製代碼

3.指令的使用

<div v-dir1></div>
<div v-dir2></div>
複製代碼

vue如何自定義一個過濾器?

<input type="text" v-model="msg" />
{{msg | capitalize}}

data(){
    return{
        msg: ''
    }
},
filters: {
    capitalize: function(value){
        if(!value) return "";
        value = value.toString();
        return value.charAt(0).toUpperCase()+value.slice(1)
    }
}
複製代碼

computed和watch區別

computed是計算屬性,依賴其餘屬性計算值,而且computed的值有緩存,只有當計算值變化纔會返回內容

watch監聽到值的變化就會執行回調,在回調中能夠進行一些邏輯操做。

通常來講須要依賴別的屬性來動態得到值的時候可使用computed,對於監聽到值的變化須要作一些複雜業務邏輯的狀況可使用watch

另外computed和watch還支持對象的寫法

data: {
    firstName: 'Chen',
    lastName: 'Miao',
    fullName: 'Chen Miao'
},
watch: {
    firstName: function(val){
        this.fullName = val+ ' '+this.lastName
    },
    lastName: function(val){
        this.fullName = this.firstName+ ' '+val
    }
},
computed: {
    anoFullName: function(){
        return this.firstName+' '+this.lastName
    }
}
複製代碼

extend能作什麼?

做用是擴展組件生成一個構造器,一般與$mount一塊兒使用。

// 建立組件構造器
let Component = Vue.extend({
    template: '<div>test</div>'
})
// 掛載到#app上
new Component().$mount('#app')

// 擴展已有組件
let SuperComponent = Vue.extend(Component)
new SuperComponent({
    created(){
        console.log(1)
    }
})
new SuperComponent().$mount('#app')
複製代碼

mixin和mixins區別

mixin用於全局混入,會影響到每一個組件實例,一般插件都是這樣作初始化的。

Vue.mixin({
    beforeCreate(){
        // 會影響到每一個組件的beforeCreate鉤子函數
    }
})
複製代碼

mixins最經常使用的擴展組件的方式。若是多個組件有相同的業務邏輯,就可將這些邏輯剝離出來,經過mixins混入代碼。須要注意:mixins混入的鉤子函數會先於組件內的鉤子函數執行,而且在遇到同名選項的時候也會有選擇性的進行合併。

如何使用vue.nextTick()?

nextTick可使咱們在下次DOM更新循環結束以後執行延遲迴調,用於得到更新後的DOM

data:function(){
    return {
        message: '沒有更新'
    }
},
methods: {
    updateMessage: function(){
        this.message='更新完成'
        console.log(this.$el.textContent) // '沒有更新'
        this.$nextTick(function(){
          console.log(this.$el.textContent)// '更新完成'  
        })
    }
}
複製代碼

transition 過渡的實現原理

<transition name="fade1">
    <router-view></router-view>
</transition>
複製代碼

類名介紹:

  • v-enter:定義進入過渡的開始狀態
  • v-enter-active:定義進入過渡生效時的狀態
  • v-enter:定義進入過渡的結束狀態
  • v-leave:定義離開過渡的開始狀態
  • v-leave-active:定義離開過渡生效時的狀態
  • v-leave-to:定義離開過渡的結束狀態

簡單說一下組件通訊

父子通訊

1.props和emit

父組件經過props傳遞數據給子組件,子組件經過emit發送事件傳遞給父組件。

// 父組件
<div>
    <child :data="child" @send="getFromChild"></child>
</div>

data(){
    return{
        toChild: '大兒子',
        fromChild: ''
    }
},
methods: {
    getFromChild(val){
        this.fromChild=val
    }
}
// 子組件
<div @click="toParent">{{data}}</div>

props:[data],
methods: {
    toParent(){
        this.$emit('send', '給父親')
    }
}
複製代碼

2.v-model

v-model實際上是props,emit的語法糖,v-model默認會解析成名爲value的prop和名爲input的事件。

// 父組件
<children v-model="msg"></children>
<p>{{msg}}</p>

data(){
    return{
        msg:'model'
    }
}
// 子組件
<input :value="value" @input="toInput" />

props: ['value'],
methods: {
    toInput(e){
        this.$emit('input', e.target.value)
    }
}
複製代碼

3.在父組件使用$children訪問子組件,在子組件中使用$parent訪問父組件

// 父組件
<child />

data(){
    return {
        msg: '父組件數據'
    }
},
methods: {
    test(){
        console.log('我是父組件的方法,被執行')
    }
},
mounted(){
    console.log(this.$children[0].child_msg); // 執行子組件方法
}
// 子組件
<div>{{$parent.msg}}</div>

data(){
    return{
        child_msg: '子組件數據'
    }
},
mounted(){
    // 子組件執行父組件方法
    this.$parent.test(); 
}
複製代碼

$listeners$attrs

$attrs--繼承全部父組件屬性(除了prop傳遞的屬性)

inheritAttrs--默認值true,繼承全部父組件屬性(除props),爲true會將attrs中的屬性當作html的data屬性渲染到dom根節點上

$listeners--屬性,包含了做用在這個組件上全部監聽器,v-on="$listeners"將全部事件監聽器指向這個組件的某個特定子元素

// 父組件
<children :child1="child1" :child2="child2" @test1="onTest1"
@test2="onTest2"></children>

data(){
    return {
        child1: 'childOne',
        child2: 'childTwo'
    }
},
methods: {
    onTest1(){
        console.log('test1 running')
    },
    onTest2(){
        console.log('test2 running')
    }
}

// 子組件
<p>{{child1}}</p>
<child v-bind="$attrs" v-on="$listeners"></child>

props: ['child1'],
mounted(){
    this.$emit('test1')
}

// 孫組件
<p>{{child2}</p>
<p>{{$attrs}}</p>

props: ['child2'],
mounted(){
    this.$emit('test2')
}
複製代碼

.sync方式

在vue1.x中是對prop進行雙向綁定,在vue2只容許單向數據流,也是一個語法糖

// 父組件
<child :count.sync="num" />

data(){
    return {
        num: 0
    }
}
// 子組件
<div @click="handleAdd">add</div>

data(){
    return {
        counter: this.count
    }
},
props: ["count"],
methods: {
    handleAdd(){
        this.$emit('update:count', ++this.counter)
    }
}
複製代碼

兄弟組件通訊

能夠經過查找父組件中的子組件實現, this.$parent.$children$children中能夠經過組件name查詢到須要的組件實例,而後進行通訊

跨多層次組件通訊

可使用provide/inject,雖然文檔中不推薦直接使用在業務中。

假設有父組件A,而後有一個跨多層次的子組件B

// 父組件A
export default{
    provide: {
        data: 1
    }
}
// 子組件B
export default{
    inject: ['data'],
    mounted(){
        // 不管跨幾層都能獲取父組件的data屬性
        console.log(this.data); // 1
    }
}
複製代碼

任意組件

能夠用Vuex或Event Bus解決

eventBus的使用

1.新建一個bus.js文件

import Vue from 'vue';
export default new Vue();
複製代碼

2.使用它

<div @click="addCart">添加</div>
import Bus from 'bus.js';
export default{
    methods: {
        addCart(event){
            Bus.$emit('getTarget', event.target)
        }
    }
}
// 另外一組件
export default{
    created(){
        Bus.$on('getTarget', target =>{
            console.log(target)
        })
    }
}
複製代碼

vue-router路由

單頁面應用SPA的核心之一是:更新視圖而不從新請求頁面

普通路由

router.push('home')

router.push({path: 'home')

命名路由

const router=new VueRouter({
    routes: [{
        path: '/user',
        name: 'user',
        component: User
    }]
})
複製代碼
<router-link :to="{name: 'user'}"></router-link>
複製代碼
router.push({
    name: 'user'
})
複製代碼

動態路由匹配

<div>{{$route.params.id}}</div>
複製代碼
const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        component: User
    }]
})
複製代碼

router.push({name:'user',params: {id: 123})

嵌套路由

<h2>{{$route.params.id}}</h2>
<router-view></router-view>
複製代碼
const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        children: [{
            // 當/user/:id
            path: '',
            component: UserHome
        },{
            // 當/user/:id/profile
            path: 'profile',
            component: UserProfile
        }]
    }]
})
複製代碼

參數的路由

router.push({path:'register',query:{plan:'private'})

編程式導航

router.push()

參數:

1.字符串

router.push('home')

2.對象

router.push({path: 'home'})

3.命名的路由

router.push({ name: 'user', params: { userId: 123 } })

4.帶查詢參數,變成/register?plan=private

router.push({ path: 'register', query: { plan: 'private' } })

router.replace()

不會向history添加新紀錄,替換當前的history記錄

點擊<router-link :to="..." replace>等同於調用router.replace(...)

router.go(n)

在歷史記錄中向前或向後退多少步

// 前進一步,等同history.forward()
router.go(1)
// 後退一步
router.go(-1)
// 前進3步記錄
router.go(3)
複製代碼

命名視圖

<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
複製代碼
const router = new VueRouter({
    routes: [{
        path: '/',
        components: {
            default: Foo,
            a: Bar,
            b: Baz
        }
    }]
})
複製代碼

vue路由的鉤子函數

導航鉤子主要用來攔截導航,讓它完成跳轉或取消。

router.beforEach((to,from,next) => {})

鉤子是異步執行解析的,每一個鉤子方法接收三個參數:

to: Route即將進入的目標路由對象

from: Route當前導航正要離開的路由

next: Function,調用該方法來resolve這個鉤子,執行效果看參數

  • next():進行下一個鉤子

  • next(false):中斷當前的導航

  • next('/')或next({path: '/'}):跳轉到另外一地址

請詳細說下你對vue生命週期的理解

生命週期共分爲8個階段建立前/後,載入前/後,更新前/後,銷燬前/後

建立前/後:在beforeCreated階段,vue實例的掛載元素el和數據對象data都爲undefined,還未初始化。created階段,vue實例的數據對象data有了,el尚未。

載入先後:在beforeMount階段,vue實例的el和data都初始化了,但仍是掛載以前爲虛擬的dom節點,data.message還未替換。在mounted階段,vue實例掛載完成,data.message成功渲染

更新前/後:當data變化時,會觸發beforeUpdated和updated方法

銷燬前/後:beforeDestroy在實例銷燬前調用,實例仍然徹底可用。destroy在實例銷燬以後調用,調用後全部事件監聽器會被移除,全部子實例也會被銷燬。

生命週期的做用?

生命週期中有多個事件鉤子,讓咱們在控制整個Vue實例的過程當中更容易造成好的邏輯。

vuex是什麼?怎麼使用?哪一種功能場景使用它?

1.vuex是vue生態系統中的狀態管理,用來管理vue中的全部組件狀態。

2.使用

import Vue from vue;
import Vuex from vuex;
Vue.use(Vuex);
const store = new Vuex.store({
    state: {
        count: 0
    },
    getters: {
        addTen: state => {
            return state.count+10;
        }
    },
    mutations: {
        increment(state){
            state.count++
        }
    }
})
複製代碼
store.commit('increment');
console.log(this.$store.state.count);
複製代碼

vuex中有

①state狀態,還有mapState映射狀態

computed: mapState({
    count: state => state.count
})
複製代碼

getter至關於store的計算屬性,主要用來過濾一些數據

getters: {
    addTen: state => {
        return state.count+10;
    }
}
store.getters.addTen
複製代碼

mapGetters是將store中的getter映射到局部計算屬性中

computed: {
    ...mapGetters([
        'addTen'
    ])
}
複製代碼

③Mutation 改變vuex中store狀態惟一方法就是提交mutation,可傳入額外參數,是一個同步函數。 在組件中提交Mutation

import {mapMutations} from 'vuex'
export default{
    methods: {
        ...mapMutations([
            'increment'
        ]),
        ...mapMutaions({
            add: 'increment'
        })
    }
}
複製代碼

④Action 相似mutation,可是是異步的,view層經過store.dispath分發action

actions:{
    increment(context){
        context.commit('increment')
    }
}
複製代碼

⑤module 當應用比較複雜,能夠將store分割成模塊

3.經常使用的場景有:單頁應用中,組件之間的狀態,音樂播放、登陸狀態、加入購物車等等

談談你對vue的雙向數據綁定原理的理解

vue.js是採用數據劫持結合發佈者-訂閱者模式的方式,經過 Object.definePorperty() 來劫持各個屬性的setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。

MVVM做爲數據綁定的入口,整合Observer,Compile和Watcher三者,經過Observer來監聽本身的model數據變化,經過Compile來解析編譯模板指定(解析{{}}),最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到數據變化->視圖更新;視圖交互變化input->數據model變動的雙向綁定效果

實現簡單的雙向綁定

<input type="text" id="inp" />
<div id="show"></div>
<script type="text/javascript">
var inp = document.getElementById('inp');
var show = document.getElementById('show');
var obj = {};
function watch(obj, key, callback){
    var val = obj[key];
    Object.defineProperty(obj, key, {
        get: function(){
            return val;
        },
        set: function(newVal){
            callback(newVal, this)
        }
    })
}
watch(obj, "input", function(val){
    show.innerHTML = val
})
inp.addEventListener('keyup', function(e){
    obj.input = e.target.value
})
</script>
複製代碼

另外一種方式實現vue的響應式原理

Proxy在目標對象以前架設一層「攔截」,外界對該對象的訪問都必須先經過這層攔截,所以提供一種機制,能夠對外界的訪問進行過濾和改寫。

<input type="text" id="txt" />
<div id="show"></div>
<script type="text/javascript">
  var inp = document.getElementById('txt');
  var show = document.getElementById('show')
  var obj = {}
  var objKey = 'text'; // 將鍵保存起來
  // Object.defineProperty
  Object.defineProperty(obj, objKey, {
    get: function(){
      return obj[objKey];
    },
    set: function(newVal){
      show.innerHTML = newVal
    }
  })
  inp.addEventListener('keyup', function(e){
    obj[objKey] = e.target.value
  })
  
  // proxy的實現
  const newObj = new Proxy(obj, {
    get: function(target, key, receiver){
      return Reflect.get(target, key, receiver);
    },
    set: function(target, key, value,receiver){
      if(key === objKey){
        show.innerHTML = value
      }
    }
  })
  inp.addEventListener('keyup',function(e){
    newObj[objKey] = e.target.value;
  })
複製代碼

Object.defineProperty的缺點:

1.不能檢測到增長或刪除的屬性

2.數組方面的變更,如根據索引改變元素,以及直接改變數組長度時的變化,不能被檢測到。

聊聊你對Vue.js的template編譯的理解

先轉化成AST樹,再獲得render函數返回VNode(Vue的虛擬DOM節點)

詳細步驟:首先經過compile編譯器把template編譯出AST語法樹,而後AST會通過generate(將AST語法樹轉化成render function字符串的過程)獲得render函數,render的返回值是VNode,VNode是Vue的虛擬DOM節點,裏面有標籤名,子節點,文本等

相關文章
相關標籤/搜索