什么是 mvvm遍膜?
MVVM 是 Model-View-ViewModel 的縮寫(xiě)氯哮。mvvm 是一種設(shè)計(jì)思想。Model 層代表數(shù)據(jù)模型宪睹,也可以在 Model 中定義數(shù)據(jù)修改和操作的業(yè)務(wù)邏輯扫腺;View 代表 UI 組件岗照,它負(fù)責(zé)將數(shù)據(jù)模型轉(zhuǎn)化成 UI 展現(xiàn)出來(lái),ViewModel 是一個(gè)同步 View 和 Model 的對(duì)象。在 MVVM 架構(gòu)下攒至,View 和 Model 之間并沒(méi)有直接的聯(lián)系厚者,而是通過(guò) ViewModel 進(jìn)行交互,Model 和 ViewModel 之間的交互是雙向的迫吐, 因此 View 數(shù)據(jù)的變化會(huì)同步到 Model 中库菲,而 Model 數(shù)據(jù)的變化也會(huì)立即反應(yīng)到 View 上。ViewModel 通過(guò)雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來(lái)渠抹,而 View 和 Model 之間的同步工作完全是自動(dòng)的蝙昙,無(wú)需人為干涉,因此開(kāi)發(fā)者只需關(guān)注業(yè)務(wù)邏輯梧却,不需要手動(dòng)操作 DOM, 不需要關(guān)注數(shù)據(jù)狀態(tài)的同步問(wèn)題奇颠,復(fù)雜的數(shù)據(jù)狀態(tài)維護(hù)完全由 MVVM 來(lái)統(tǒng)一管理。
mvvm 和 mvc 區(qū)別放航?
mvc 和 mvvm 其實(shí)區(qū)別并不大烈拒。都是一種設(shè)計(jì)思想。主要就是 mvc 中 Controller 演變成 mvvm 中的 viewModel广鳍。mvvm 主要解決了 mvc 中大量的 DOM 操作使頁(yè)面渲染性能降低荆几,加載速度變慢,影響用戶體驗(yàn)赊时。和當(dāng) Model 頻繁發(fā)生變化吨铸,開(kāi)發(fā)者需要主動(dòng)更新到 View 。
vue 的優(yōu)點(diǎn)是什么祖秒?
- 低耦合诞吱。視圖(View)可以獨(dú)立于 Model 變化和修改,一個(gè) ViewModel 可以綁定到不同的"View"上竭缝,當(dāng) View 變化的時(shí)候 Model 可以不變房维,當(dāng) Model 變化的時(shí)候 View 也可以不變。
- 可重用性抬纸。你可以把一些視圖邏輯放在一個(gè) ViewModel 里面咙俩,讓很多 view 重用這段視圖邏輯。
- 獨(dú)立開(kāi)發(fā)湿故。開(kāi)發(fā)人員可以專注于業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā)(ViewModel)阿趁,設(shè)計(jì)人員可以專注于頁(yè)面設(shè)計(jì),使用 Expression Blend 可以很容易設(shè)計(jì)界面并生成 xml 代碼坛猪。
- 可測(cè)試歌焦。界面素來(lái)是比較難于測(cè)試的,而現(xiàn)在測(cè)試可以針對(duì) ViewModel 來(lái)寫(xiě)砚哆。
vue生命周期的理解?
答:總共分為 8 個(gè)階段創(chuàng)建前/后,載入前/后躁锁,更新前/后纷铣,銷毀前/后。
- 創(chuàng)建前/后: 在 beforeCreate 階段战转,vue 實(shí)例的掛載元素 el 還沒(méi)有搜立。
- 載入前/后:在 beforeMount 階段,vue 實(shí)例的$el 和 data 都初始化了槐秧,但還是掛載之前為虛擬的 dom 節(jié)點(diǎn)啄踊,data.message 還未替換。在 mounted 階段刁标,vue 實(shí)例掛載完成颠通,data.message 成功渲染。
- 更新前/后:當(dāng) data 變化時(shí)膀懈,會(huì)觸發(fā) beforeUpdate 和 updated 方法顿锰。
- 銷毀前/后:在執(zhí)行 destroy 方法后,對(duì) data 的改變不會(huì)再觸發(fā)周期函數(shù)启搂,說(shuō)明此時(shí) vue 實(shí)例已經(jīng)解除了事件監(jiān)聽(tīng)以及和 dom 的綁定硼控,但是 dom 結(jié)構(gòu)依然存在。
為什么vue中data必須是一個(gè)函數(shù)胳赌?
對(duì)象為引用類型牢撼,當(dāng)重用組件時(shí),由于數(shù)據(jù)對(duì)象都指向同一個(gè)data對(duì)象疑苫,當(dāng)在一個(gè)組件中修改data時(shí)熏版,其他重用的組件中的data會(huì)同時(shí)被修改;而使用返回對(duì)象的函數(shù)缀匕,由于每次返回的都是一個(gè)新對(duì)象(Object的實(shí)例)纳决,引用地址不同,則不會(huì)出現(xiàn)這個(gè)問(wèn)題乡小。
組件之間的傳值阔加?
1.父組件與子組件傳值
//父組件通過(guò)標(biāo)簽上面定義傳值
<template>
<Main :obj="data"></Main>
</template>
<script>
//引入子組件
import Main form "./main"
exprot default{
name:"parent",
data(){
return {
data:"我要向子組件傳遞數(shù)據(jù)"
}
},
//初始化組件
components:{
Main
}
}
</script>
//子組件通過(guò)props方法接受數(shù)據(jù)
<template>
<div>{{obj}}</div>
</template>
<script>
exprot default{
name:"son",
//接受父組件傳值
props:["obj"]
}
</script>
2.子組件向父組件傳遞數(shù)據(jù)
//子組件通過(guò)$emit方法傳遞參數(shù)
<template>
<div v-on:click="events"></div>
</template>
<script>
//引入子組件
import Main form "./main"
exprot default{
methods:{
events:function(params){
console.log(params)
}
}
}
</script>
//
<template>
<div v-on:click="emitEvent">{{data}}</div>
</template>
<script>
exprot default{
name:"son",
//接受父組件傳值
props:["data"],
methods: {
emitEvent() {
this.$emit('event', params) // 派發(fā)函數(shù),并傳遞值满钟,params是你自己想傳的值
}
}
}
</script>
active-class 是哪個(gè)組件的屬性胜榔?
vue-router 模塊的 router-link 組件。
嵌套路由怎么定義湃番?
在實(shí)際項(xiàng)目中我們會(huì)碰到多層嵌套的組件組合而成夭织,但是我們?nèi)绾螌?shí)現(xiàn)嵌套路由呢?因此我們需要在 VueRouter 的參數(shù)中使用 children 配置吠撮,這樣就可以很好的實(shí)現(xiàn)路由嵌套尊惰。index.html,只有一個(gè)路由出口
<div id="app">
<!-- router-view 路由出口, 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
</div>
main.js,路由的重定向弄屡,就會(huì)在頁(yè)面一加載的時(shí)候题禀,就會(huì)將 home 組件顯示出來(lái),因?yàn)橹囟ㄏ蛑赶蛄?home 組件膀捷,redirect 的指向與 path 的必須一致迈嘹。children 里面是子路由,當(dāng)然子路由里面還可以繼續(xù)嵌套子路由全庸。
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//引入兩個(gè)組件
import home from "./home.vue"
import game from "./game.vue"
//定義路由
const routes = [
{ path: "/", redirect: "/home" },//重定向,指向了home組件
{
path: "/home", component: home,
children: [
{ path: "/home/game", component: game }
]
}
]
//創(chuàng)建路由實(shí)例
const router = new VueRouter({routes})
new Vue({
el: '#app',
data: {
},
methods: {
},
router
})
home.vue秀仲,點(diǎn)擊顯示就會(huì)將子路由顯示在出來(lái),子路由的出口必須在父路由里面壶笼,否則子路由無(wú)法顯示神僵。
路由之間跳轉(zhuǎn)?
- 聲明式(標(biāo)簽跳轉(zhuǎn))
- 編程式( js 跳轉(zhuǎn)) router.push('index')
懶加載(按需加載路由)(嘲柘考)
webpack 中提供了 require.ensure()來(lái)實(shí)現(xiàn)按需加載挑豌。以前引入路由是通過(guò) import 這樣的方式引入,改為 const 定義的方式進(jìn)行引入墩崩。
- 不進(jìn)行頁(yè)面按需加載引入方式:
import home from '../../common/home.vue'
- 進(jìn)行頁(yè)面按需加載的引入方式:
const home = r => require.ensure( [], () => r (require('../../common/home.vue')))
vuex 是什么氓英?怎么使用?哪種功能場(chǎng)景使用它鹦筹?
vue 框架中狀態(tài)管理铝阐。在 main.js 引入 store,注入铐拐。新建了一個(gè)目錄 store徘键,….. export 。場(chǎng)景有:?jiǎn)雾?yè)應(yīng)用中遍蟋,組件之間的狀態(tài)吹害。音樂(lè)播放、登錄狀態(tài)虚青、加入購(gòu)物車(chē)
// 新建 store.js
import vue from 'vue'
import vuex form 'vuex'
vue.use(vuex)
export default new vuex.store({
//...code
})
//main.js
import store from './store'
...
vue-router 有哪幾種導(dǎo)航鉤子?
三種
- 全局導(dǎo)航鉤子
router.beforeEach(to, from, next),
router.beforeResolve(to, from, next),
router.afterEach(to, from ,next) - 組件內(nèi)鉤子
beforeRouteEnter,
beforeRouteUpdate,
beforeRouteLeave - 單獨(dú)路由獨(dú)享組件
beforeEnter
自定義指令(v-check, v-focus) 的方法有哪些? 它有哪些鉤子函數(shù)? 還有哪些鉤子函數(shù)參數(shù)
- 全局定義指令:在 vue 對(duì)象的 directive 方法里面有兩個(gè)參數(shù), 一個(gè)是指令名稱, 另一個(gè)是函數(shù)它呀。
- 組件內(nèi)定義指令:directives鉤子函數(shù): bind(綁定事件出發(fā))、inserted(節(jié)點(diǎn)插入時(shí)候觸發(fā))棒厘、update(組件內(nèi)相關(guān)更新)
- 鉤子函數(shù)參數(shù): el纵穿、binding
說(shuō)出至少 4 種 vue 當(dāng)中的指令和它的用法
v-if(判斷是否隱藏)、v-for(把數(shù)據(jù)遍歷出來(lái))奢人、v-bind(綁定屬性)谓媒、v-model(實(shí)現(xiàn)雙向綁定)
vue 的雙向綁定的原理是什么(常考)
vue.js 是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式何乎,通過(guò) Object.defineProperty()來(lái)劫持各個(gè)屬性的 setter句惯,getter土辩,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽(tīng)回調(diào)宗弯。
具體步驟:
第一步:需要 observe 的數(shù)據(jù)對(duì)象進(jìn)行遞歸遍歷脯燃,包括子屬性對(duì)象的屬性,都加上 setter 和 getter 這樣的話蒙保,給這個(gè)對(duì)象的某個(gè)值賦值,就會(huì)觸發(fā) setter欲主,那么就能監(jiān)聽(tīng)到了數(shù)據(jù)變化
第二步:compile 解析模板指令邓厕,將模板中的變量替換成數(shù)據(jù),然后初始化渲染頁(yè)面視圖扁瓢,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù)详恼,添加監(jiān)聽(tīng)數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動(dòng)引几,收到通知昧互,更新視圖
第三步:Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁,主要做的事情是:
在自身實(shí)例化時(shí)往屬性訂閱器(dep)里面添加自己自身必須有一個(gè) update()方法待屬性變動(dòng) dep.notice()通知時(shí)伟桅,能調(diào)用自身的 update() 方法敞掘,并觸發(fā) Compile 中綁定的回調(diào),則功成身退楣铁。
第四步:MVVM 作為數(shù)據(jù)綁定的入口玖雁,整合 Observer、Compile 和 Watcher 三者盖腕,通過(guò) Observer 來(lái)監(jiān)聽(tīng)自己的 model 數(shù)據(jù)變化赫冬,通過(guò) Compile 來(lái)解析編譯模板指令,最終利用 Watcher 搭起 Observer 和 Compile 之間的通信橋梁溃列,達(dá)到數(shù)據(jù)變化 -> 視圖更新劲厌;視圖交互變化(input) -> 數(shù)據(jù) model 變更的雙向綁定效果。
vuex 有哪幾種屬性
有 5 種听隐,分別是 state补鼻、getter、mutation遵绰、action辽幌、module
vuex 的 store 特性是什么
vuex 就是一個(gè)倉(cāng)庫(kù),倉(cāng)庫(kù)里放了很多對(duì)象椿访。其中 state 就是數(shù)據(jù)源存放地乌企,對(duì)應(yīng)于一般 vue 對(duì)象里面的 datastate 里面存放的數(shù)據(jù)是響應(yīng)式的,vue 組件從 store 讀取數(shù)據(jù)成玫,若是 store 中的數(shù)據(jù)發(fā)生改變加酵,依賴這相數(shù)據(jù)的組件也會(huì)發(fā)生更新它通過(guò) mapState 把全局的 state 和 getters 映射到當(dāng)前組件的 computed 計(jì)算屬性
vuex 的 getter 特性是什么
getter 可以對(duì) state 進(jìn)行計(jì)算操作拳喻,它就是 store 的計(jì)算屬性雖然在組件內(nèi)也可以做計(jì)算屬性,但是 getters 可以在多給件之間復(fù)用如果一個(gè)狀態(tài)只在一個(gè)組件內(nèi)使用猪腕,是可以不用 getters
vuex 的 mutation 特性是什么
action 類似于 muation, 不同在于:action 提交的是 mutation,而不是直接變更狀態(tài)action 可以包含任意異步操作
vue 中 ajax 請(qǐng)求代碼應(yīng)該寫(xiě)在組件的 methods 中還是 vuex 的 action 中
如果請(qǐng)求來(lái)的數(shù)據(jù)不是要被其他組件公用冗澈,僅僅在請(qǐng)求的組件內(nèi)使用,就不需要放入 vuex 的 state 里如果被其他地方復(fù)用陋葡,請(qǐng)將請(qǐng)求放入 action 里亚亲,方便復(fù)用,并包裝成 promise 返回
不用 vuex 會(huì)帶來(lái)什么問(wèn)題
- 可維護(hù)性會(huì)下降腐缤,你要修改數(shù)據(jù)捌归,你得維護(hù) 3 個(gè)地方
- 可讀性下降,因?yàn)橐粋€(gè)組件里的數(shù)據(jù)岭粤,你根本就看不出來(lái)是從哪里來(lái)的
- 增加耦合惜索,大量的上傳派發(fā),會(huì)讓耦合性大大的增加剃浇,本來(lái) Vue 用 Component 就是為了減少耦合巾兆,現(xiàn)在這么用,和組件化的初衷相背
vuex 原理
vuex 僅僅是作為 vue 的一個(gè)插件而存在虎囚,不像 Redux,MobX 等庫(kù)可以應(yīng)用于所有框架角塑,vuex 只能使用在 vue 上,很大的程度是因?yàn)槠涓叨纫蕾囉?vue 的 computed 依賴檢測(cè)系統(tǒng)以及其插件系統(tǒng)溜宽,vuex 整體思想誕生于 flux,可其的實(shí)現(xiàn)方式完完全全的使用了 vue 自身的響應(yīng)式設(shè)計(jì)吉拳,依賴監(jiān)聽(tīng)、依賴收集都屬于 vue 對(duì)對(duì)象 Property set get 方法的代理劫持适揉。最后一句話結(jié)束 vuex 工作原理留攒,vuex 中的 store 本質(zhì)就是沒(méi)有 template 的隱藏著的 vue 組件;
使用 Vuex 只需執(zhí)行 Vue.use(Vuex)嫉嘀,并在 Vue 的配置中傳入一個(gè) store 對(duì)象的示例炼邀,store 是如何實(shí)現(xiàn)注入的?美團(tuán)
Vue.use(Vuex) 方法執(zhí)行的是 install 方法剪侮,它實(shí)現(xiàn)了 Vue 實(shí)例對(duì)象的 init 方法封裝和注入拭宁,使傳入的 store 對(duì)象被設(shè)置到 Vue 上下文環(huán)境的store中。因此在VueComponent任意地方都能夠通過(guò)this.store 訪問(wèn)到該 store瓣俯。
state 內(nèi)部支持模塊配置和模塊嵌套杰标,如何實(shí)現(xiàn)的?美團(tuán)
在 store 構(gòu)造方法中有 makeLocalContext 方法彩匕,所有 module 都會(huì)有一個(gè) local context腔剂,根據(jù)配置時(shí)的 path 進(jìn)行匹配。所以執(zhí)行如 dispatch('submitOrder', payload)這類 action 時(shí)驼仪,默認(rèn)的拿到都是 module 的 local state掸犬,如果要訪問(wèn)最外層或者是其他 module 的 state袜漩,只能從 rootState 按照 path 路徑逐步進(jìn)行訪問(wèn)。
在執(zhí)行 dispatch 觸發(fā) action(commit 同理)的時(shí)候湾碎,只需傳入(type, payload)宙攻,action 執(zhí)行函數(shù)中第一個(gè)參數(shù) store 從哪里獲取的?美團(tuán)
store 初始化時(shí)介褥,所有配置的 action 和 mutation 以及 getters 均被封裝過(guò)座掘。在執(zhí)行如 dispatch('submitOrder', payload)的時(shí)候,actions 中 type 為 submitOrder 的所有處理方法都是被封裝后的呻顽,其第一個(gè)參數(shù)為當(dāng)前的 store 對(duì)象雹顺,所以能夠獲取到 { dispatch, commit, state, rootState } 等數(shù)據(jù)。
Vuex 如何區(qū)分 state 是外部直接修改廊遍,還是通過(guò) mutation 方法修改的?美團(tuán)
Vuex 中修改 state 的唯一渠道就是執(zhí)行 commit('xx', payload) 方法贩挣,其底層通過(guò)執(zhí)行 this._withCommit(fn) 設(shè)置_committing 標(biāo)志變量為 true喉前,然后才能修改 state,修改完畢還需要還原_committing 變量王财。外部修改雖然能夠直接修改 state卵迂,但是并沒(méi)有修改_committing 標(biāo)志位,所以只要 watch 一下 state绒净,state change 時(shí)判斷是否_committing 值為 true见咒,即可判斷修改的合法性。
調(diào)試時(shí)的"時(shí)空穿梭"功能是如何實(shí)現(xiàn)的挂疆?美團(tuán)
devtoolPlugin 中提供了此功能改览。因?yàn)?dev 模式下所有的 state change 都會(huì)被記錄下來(lái),'時(shí)空穿梭' 功能其實(shí)就是將當(dāng)前的 state 替換為記錄中某個(gè)時(shí)刻的 state 狀態(tài)缤言,利用 store.replaceState(targetState) 方法將執(zhí)行 this._vm.state = state 實(shí)現(xiàn)宝当。