-
watch
和computed
和methods
區(qū)別是什么落塑?- 翻譯一遍朱庆,說出作用,再找不同
computed
:計算屬性
watch
:監(jiān)聽屬性捺癞,一個對象夷蚊,鍵是需要觀察的表達(dá)式,值是對應(yīng)回調(diào)函數(shù)髓介。值也可以是方法名惕鼓,或者包含選項的對象。
methods
:methods
將被混入到Vue
實例中唐础∠淦纾可以直接通過VM
實例訪問這些方法,或者在指令表達(dá)式中使用一膨。 - 要點:
- 最大區(qū)別是
computed
有緩存:如果computed
屬性依賴的屬性沒有變化呀邢,那么computed
屬性就不會重新計算。watch
在每次監(jiān)聽的值變化時候豹绪,都會執(zhí)行回調(diào)价淌。 -
computed
是計算出一個屬性,而watch
則可能是做別的事情(如上報數(shù)據(jù))
- 最大區(qū)別是
- 翻譯一遍朱庆,說出作用,再找不同
-
Vue
有哪些生命周期鉤子函數(shù)?分別有什么用蝉衣?
注:不要在生命周期函數(shù)中使用箭頭函數(shù)
括尸,因為箭頭函數(shù)沒有this
。使用會導(dǎo)致this作為變量一直向上級詞法作用域查找病毡,直至找到為止濒翻,從而引發(fā)類如Uncaught TypeError: Cannot read property of undefined
或Uncaught TypeError: this.myMethod is not a function
之類的錯誤。-
beforeCreate
:在實例初始化之后啦膜,數(shù)據(jù)觀測(data observer
)和event/watcher
事件配置之前被調(diào)用有送。 -
created
:在實例創(chuàng)建完成后被立即調(diào)用,在這一步實例已經(jīng)完成以下配置:數(shù)據(jù)觀測(data observer
)僧家、屬性和方法的運算娶眷、event/watch
事件回調(diào),然而掛載還沒開始啸臀,$el
屬性目前還不可見届宠。 -
beforeMount
:在掛載開始之前被調(diào)用,相關(guān)的渲染函數(shù)首次被調(diào)用 乘粒。 -
mounted
:el
被新創(chuàng)建的vm.$el
替換豌注,掛載成功。一般在這個鉤子中請求數(shù)據(jù) -
beforeUpdate
:數(shù)據(jù)更新前調(diào)用灯萍。 -
updated
:組件 DOM已經(jīng)更新轧铁,組件更新完畢。 -
beforeDestroy
:整個頁面旦棉,應(yīng)用被銷毀前調(diào)用齿风,此時實例仍然完全可用。 -
destroyed
:Vue
實例銷毀后調(diào)用绑洛,Vue
實例指示的所有東西都會解綁定救斑,所有時間監(jiān)聽器會被移除,所有子實例也會被銷毀真屯。
-
-
Vue
如何實現(xiàn)組件間通信脸候?- 父子組件:
$emit('xxx',data)
$on('xxx',function(){})
- 任意組件,爺孫組件:使用
var eventBus = new Vue()
來通信,eventBus.$on
和eventBus.$emit
是主要API - 任意組件:使用
Vuex
通信
- 父子組件:
-
Vue
數(shù)據(jù)響應(yīng)式怎么做到的绑蔫?- 這篇文章我覺得講的比較明了《詳解Vue響應(yīng)式原理》
-
Vue 2.0
版本 主要通過Object.defineProperty
實現(xiàn)- 使用
Object.defineProperty
把這些屬性全部轉(zhuǎn)為getter/setter
运沦,每個組件實例都對應(yīng)一個watcher
實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù)property
記錄為依賴配深。之后當(dāng)依賴項的setter
觸發(fā)時携添,會通知watcher
,從而使它關(guān)聯(lián)的組件重新渲染篓叶。 -
Vue
不能檢測到對象屬性的添加或刪除烈掠,解決方法是手動調(diào)用Vue.set(object,key,value)
或者this.$set(this.object,key,value)
- 使用
-
Vue 3.0
主要通過Proxy
實現(xiàn)羞秤,區(qū)別就在于Proxy
代理的是整個對象,而Object.defineProperty
代理的是對象的某個屬性向叉,需要遍歷整個對象的屬性。
-
Vue.set
是做什么用的嗦董?
添加或者刪除一個對象屬性時候用 -
Vuex
你怎么用的母谎?-
Vuex
是一個專為Vue.js
應(yīng)用程序開發(fā)的狀態(tài)管理工具 - 核心概念:
2.1State
:聲明定義數(shù)據(jù),唯一的數(shù)據(jù)源京革。 從store 實例中讀取狀態(tài)最簡單的方法就是在計算屬性中返回某個狀態(tài)奇唤。
Vuex
通過store
選項,提供了一種機制將狀態(tài)從根組件“注入”到每一個子組件中(需調(diào)用Vue.use(Vuex)
):
-
const app = new Vue({
el: '#app',
// 把 store 對象提供給 “store” 選項匹摇,這可以把 store 的實例注入所有的子組件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
通過在根實例中注冊 store 選項咬扇,該 store 實例會注入到根組件下的所有子組件中,且子組件能通過 this.$store 訪問到廊勃。
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
2.2 Getters
:可以認(rèn)為是 store
的計算屬性懈贺,getter
的返回值會根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計算坡垫。Getter
接受 state
作為其第一個參數(shù):
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Getter 也可以接受其他 getter 作為第二個參數(shù):
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
注意:getter
在通過屬性訪問時是作為 Vue
的響應(yīng)式系統(tǒng)的一部分緩存其中的,getter
在通過方法訪問時梭灿,每次都會去進行調(diào)用,而不會緩存結(jié)果冰悠。
2.3 Mutation
:更改Vuex
的 store
中的狀態(tài)的唯一方法是提交 mutation
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment//事件類型type (state) {
// 變更狀態(tài)
state.count++
}//回調(diào)函數(shù) (handler)
}
})
store.commit('increment') //提交一個`mutation`來觸發(fā)狀態(tài)的更改
可以向 store.commit
傳入額外的參數(shù)堡妒,即 mutation
的 載荷(payload
):
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
或者
store.commit({
type: 'increment',
amount: 10
})
注意:Mutation
必須是同步函數(shù)!溉卓!因為當(dāng) mutation
觸發(fā)的時候皮迟,回調(diào)函數(shù)還沒有被調(diào)用
2.4 Actions
:Action
類似于 mutation
,不同在于:1.Action
提交的是 mutation
桑寨,而不是直接變更狀態(tài)伏尼。2.Action
可以包含任意異步操作。
Action
函數(shù)接受一個與store
實例具有相同方法和屬性的 context
對象尉尾,因此你可以調(diào)用 context.commit
提交一個 mutation
烦粒,或者通過 context.state
和 context.getters
來獲取state
和 getters
:
...
actions: {
increment (context) {
context.commit('increment')
}
}
實踐中,我們常常用參數(shù)解構(gòu)來簡化代碼,特別是需要commit
很多次的時候:
actions: {
increment ({ commit }) {
commit('increment')
}
}
//異步
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Action
通過 store.dispatch
方法觸發(fā):
store.dispatch('increment')
//or
store.dispatch({
type: 'increment',
payload: payload
})
2.5 Modules
:由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象代赁。當(dāng)應(yīng)用變得非常復(fù)雜時扰她,store
對象就有可能變得相當(dāng)臃腫。
為了解決以上問題芭碍,Vuex
允許我們將 store
分割成模塊(module
)徒役。每個模塊擁有自己的 state、mutation窖壕、action忧勿、getter
杉女、甚至是嵌套子模塊——從上至下進行同樣方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)
對于模塊內(nèi)部的mutation
和 getter
,接收的第一個參數(shù)是模塊的局部狀態(tài)對象鸳吸。
對于模塊內(nèi)部的 action
熏挎,局部狀態(tài)通過 context.state
暴露出來,根節(jié)點狀態(tài)則為 context.rootState
(第三個參數(shù)):
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
默認(rèn)情況下晌砾,模塊內(nèi)部的 action
坎拐、mutation
和 getter
是注冊在全局命名空間的——這樣使得多個模塊能夠?qū)ν?mutation
或 action
作出響應(yīng)。
如果希望你的模塊具有更高的封裝度和復(fù)用性养匈,你可以通過添加 namespaced: true
的方式使其成為帶命名空間的模塊溪猿。當(dāng)模塊被注冊后硬鞍,它的所有 getter
、action
及 mutation
都會自動根據(jù)模塊注冊的路徑調(diào)整命名:
const moduleA = {
namespaced: true,
state: {
count: 3
},
mutations: {
increment (state) {
state.count++
}
}
...
}
store.commit('a/increment') //才能訪問到對應(yīng)的`mutation`
-
VueRouter
你怎么用的?-
Vue Router
是Vue.js
官方的路由管理器瓶摆。 - 核心概念:
-
- 動態(tài)路由匹配:我們經(jīng)常需要把某種模式匹配到的所有路由拴清,全都映射到同個組件事期。例如現(xiàn)在有個
User
組件轧房,對于所有 ID 各不相同的用戶,都要使用這個組件來渲染湿刽。那么聋溜,我們可以在vue-router
的路由路徑中使用“動態(tài)路徑參數(shù)
”(dynamic segment
) 來達(dá)到這個效果:
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 動態(tài)路徑參數(shù) 以冒號開頭
{ path: '/user/:id', component: User }
]
})
現(xiàn)在呢,像 /user/foo 和 /user/bar 都將映射到相同的路由叭爱。當(dāng)匹配到一個路由時撮躁,參數(shù)值會被設(shè)置到 this.$route.params
,可以在每個組件內(nèi)使用买雾。于是把曼,我們可以更新User
的模板,輸出當(dāng)前用戶的 ID:const User = { template: '<div>User {{ $route.params.id }}</div>' }
常規(guī)參數(shù)只會匹配被 / 分隔的 URL 片段中的字符漓穿。如果想匹配任意路徑嗤军,我們可以使用通配符 (*):
{
// 會匹配所有路徑
path: '*'
}
{
// 會匹配以 `/user-` 開頭的任意路徑
path: '/user-*'
}
當(dāng)使用通配符路由時,請確保路由的順序是正確的晃危,也就是說含有通配符的路由應(yīng)該放在最后叙赚。當(dāng)使用一個通配符時,$route.params
內(nèi)會自動添加一個名為 pathMatch
參數(shù)僚饭。它包含了 URL 通過通配符被匹配的部分:// 給出一個路由 { path: '/user-*' } this.$router.push('/user-admin') this.$route.params.pathMatch // 'admin'
- History 模式:
vue-router
默認(rèn)的是hash模式
----使用URL
的hash
來模擬一個完整的URL
震叮,于是當(dāng)URL
改變時,頁面不會重新加載鳍鸵。如果不想要這種帶hash
的UR
L的話苇瓣,可以使用History模式
,這種模式利用的是history.pushState
API來完成URL
的跳轉(zhuǎn)偿乖,因此不會重新加載頁面击罪。不過History模式
需要后臺的支持哲嘲,需要服務(wù)端增加一個覆蓋所有情況的候選資源,當(dāng)URL
匹配不到任何靜態(tài)資源媳禁,則應(yīng)該返回app
依賴的頁面眠副。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
- 路由懶加載:當(dāng)打包構(gòu)建應(yīng)用時,
JavaScript
包會變得非常大竣稽,影響頁面加載囱怕。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時候才加載對應(yīng)組件丧枪,這樣就更加高效了光涂。結(jié)合webpack
的代碼分塊功能庞萍,只需要import('./Foo.vue')//返回Promise
就可以實現(xiàn)懶加載拧烦。
結(jié)合vue的異步組件和webpack,就能定義一個能夠被 Webpack 自動代碼分割的異步組件:
const Foo = () => import('./Foo.vue')
- 重定向和別名:
重定向:重定向通過 routes 配置來完成钝计,從/a重定向到/b:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目標(biāo)也可以是一個命名的路由:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
甚至是一個方法恋博,動態(tài)返回重定向目標(biāo):
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目標(biāo)路由 作為參數(shù)
// return 重定向的 字符串路徑/路徑對象
}}
]
})
別名:/a
的別名是 /b
,意味著私恬,當(dāng)用戶訪問/b
時债沮,URL
會保持為/b
,但是路由匹配則為/a
本鸣,就像用戶訪問 /a
一樣疫衩。
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
代碼為上述的路由配置。 ''別名''的作用是讓你可以自由地將 UI 結(jié)構(gòu)映射到任意的 URL荣德,而不是受限于配置的嵌套路由結(jié)構(gòu)闷煤。
3. 常用 API:
router-link
: 當(dāng)被點擊后,內(nèi)部會立刻把 to
的值傳到router.push()
<router-link to="home">Home</router-link> //字符串
<router-link :to="'home'">Home</router-link> //v-bind的簡略寫法
<router-link :to="{ path: 'home' }">Home</router-link> //對象
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link> //命名的路由
<router-link :to="{ path: 'register', query: { plan: 'private' }}"
>Register</router-link
>//帶查詢參數(shù) /register?plan=private
設(shè)置 replace
屬性的話涮瞻,當(dāng)點擊時鲤拿,會調(diào)用 router.replace()
而不是 router.push()
,于是導(dǎo)航后不會留下 history
記錄署咽。
<router-link :to="{ path: '/abc'}" replace></router-link>
router-view
: 命名視圖近顷,有時候想同時 (同級) 展示多個視圖,而不是嵌套展示宁否,例如創(chuàng)建一個布局窒升,有 sidebar
(側(cè)導(dǎo)航) 和 main
(主內(nèi)容) 兩個視圖,這個時候命名視圖就派上用場了慕匠。如果 router-view 沒有設(shè)置名字异剥,那么默認(rèn)為 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
路由配置:
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
router.push/router.replace/router.go/router.back/router.forward
:動態(tài)的導(dǎo)航到一個新 URL
router.push(location, onComplete?, onAbort?)
router.push(location).then(onComplete).catch(onAbort)
router.replace(location, onComplete?, onAbort?)
router.replace(location).then(onComplete).catch(onAbort)
router.go(n)
router.back()
router.forward()
router.onError
:注冊一個回調(diào)絮重,該回調(diào)會在路由導(dǎo)航過程中出錯時被調(diào)用冤寿。
router.onError(callback)
注意被調(diào)用的錯誤必須是下列情形中的一種:
1.錯誤在一個路由守衛(wèi)函數(shù)中被同步拋出歹苦;
2.錯誤在一個路由守衛(wèi)函數(shù)中通過調(diào)用 next(err)
的方式異步捕獲并處理;
3.渲染一個路由的過程中督怜,需要嘗試解析一個異步組件時發(fā)生錯誤
- 什么是導(dǎo)航守衛(wèi)殴瘦?
- 概念: 導(dǎo)航守衛(wèi):
vue-router
提供的導(dǎo)航守衛(wèi)主要用來通過跳轉(zhuǎn)或取消的方式守衛(wèi)導(dǎo)航。簡單來說号杠,導(dǎo)航守衛(wèi)就是路由跳轉(zhuǎn)期間的鉤子函數(shù)蚪腋,它分為全局的、單個路由獨享的姨蟋、組件內(nèi)部的三種屉凯。 - 導(dǎo)航守衛(wèi)分類:
1.全局的
全局前置守衛(wèi)
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
當(dāng)一個導(dǎo)航觸發(fā)時,全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用眼溶。守衛(wèi)是異步解析執(zhí)行悠砚,此時導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于 等待中。
全局解析守衛(wèi)
在 2.5.0+ 你可以用router.beforeResolve
注冊一個全局守衛(wèi)堂飞。這和 router.beforeEach
類似灌旧,區(qū)別是在導(dǎo)航被確認(rèn)之前,同時在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后绰筛,解析守衛(wèi)就被調(diào)用枢泰。
全局后置鉤子
router.afterEach((to, from) => {
// ...
})
router.afterEach
不會接受 next 函數(shù)也不會改變導(dǎo)航本身。
2.路由獨享的守衛(wèi)
目前只有一個鉤子函數(shù)beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
這些守衛(wèi)與全局前置守衛(wèi)router.beforeEach
的方法參數(shù)是一樣的铝噩。
3.組件內(nèi)部的守衛(wèi)
beforeRouteEnter
beforeRouteUpdate
(2.2 新增) beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應(yīng)路由被 confirm 前調(diào)用
// 不衡蚂!能!獲取組件實例 `this`
// 因為當(dāng)守衛(wèi)執(zhí)行前骏庸,組件實例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當(dāng)前路由改變毛甲,但是該組件被復(fù)用時調(diào)用
// 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id敞恋,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候丽啡,
// 由于會渲染同樣的 Foo 組件,因此組件實例會被復(fù)用硬猫。而這個鉤子就會在這個情況下被調(diào)用补箍。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用
// 可以訪問組件實例 `this`
}
}
beforeRouteEnter
守衛(wèi) 不能 訪問 this
,因為守衛(wèi)在導(dǎo)航確認(rèn)前被調(diào)用啸蜜,因此即將登場的新組件還沒被創(chuàng)建坑雅。不過,你可以通過傳一個回調(diào)給 next
來訪問組件實例衬横。在導(dǎo)航被確認(rèn)的時候執(zhí)行回調(diào)裹粤,并且把組件實例作為回調(diào)方法的參數(shù)。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實例
})
}
注意 beforeRouteEnter
是支持給 next
傳遞回調(diào)的唯一守衛(wèi)蜂林。對于 beforeRouteUpdate
和beforeRouteLeave
來說遥诉,this
已經(jīng)可用了拇泣,所以不支持傳遞回調(diào),因為沒有必要了矮锈。
beforeRouteLeave
通常用來禁止用戶在還未保存修改前突然離開霉翔。該導(dǎo)航可以通過 next(false) 來取消。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('你確定要離開嘛? 你還未保存更改!')
if (answer) {
next()
} else {
next(false)
}
}
導(dǎo)航參數(shù)解析
每個守衛(wèi)方法接收三個參數(shù)(router.afterEach
沒有next
):
to: Route
即將要進入的目標(biāo) 路由對象苞笨。
from: Route
當(dāng)前導(dǎo)航正要離開的路由债朵。
next: Function
一定要調(diào)用該方法來resolve
這個鉤子,如果不寫這個方法瀑凝,路由將不會跳轉(zhuǎn)序芦。執(zhí)行效果依賴next
方法的調(diào)用參數(shù):
-next()
進行管道中的下一個鉤子。如果全部鉤子執(zhí)行完了粤咪,則導(dǎo)航的狀態(tài)就是 confirmed (確認(rèn)的)谚中。
-next(false)
中斷當(dāng)前的導(dǎo)航。如果瀏覽器的URL
改變了 (可能是用戶手動或者瀏覽器后退按鈕)射窒,那么URL
地址會重置到from
路由對應(yīng)的地址藏杖。
-next('/')
或者next({ path: '/' })
跳轉(zhuǎn)到一個不同的地址将塑。當(dāng)前的導(dǎo)航被中斷脉顿,然后進行一個新的導(dǎo)航。 可傳遞的參數(shù)和router-link :to='參數(shù)'
或者router.push('參數(shù)')
中的參數(shù)是一樣的点寥。
-next(error)
(2.4.0+) 如果傳入next
的參數(shù)是一個Error
實例艾疟,則導(dǎo)航會被終止且該錯誤會被傳遞給router.onError()
注冊過的回調(diào)。
- 在beforeRouteEnter
中next(vm => {//通過 vm 訪問組件實例})
完整的導(dǎo)航解析流程
當(dāng)點擊跳轉(zhuǎn)路由時:
1. 導(dǎo)航被觸發(fā)敢辩。
2. 在失活的組件內(nèi)調(diào)用beforeRouteLeave
守衛(wèi)蔽莱。
3. 調(diào)用全局的beforeEach
守衛(wèi)。
4. 在重用的組件內(nèi)調(diào)用beforeRouteUpdate
守衛(wèi) (2.2+)戚长。
5. 在路由配置里調(diào)用beforeEnter
盗冷。
6. 解析異步路由組件。
7. 在被激活的組件內(nèi)調(diào)用beforeRouteEnter
同廉。
8. 調(diào)用全局的beforeResolve
守衛(wèi) (2.5+)仪糖。
9. 導(dǎo)航被確認(rèn)。
10. 調(diào)用全局的afterEach
鉤子迫肖。
11. 觸發(fā)DOM
更新锅劝。
12. 用創(chuàng)建好的實例調(diào)用beforeRouteEnter
守衛(wèi)中傳給next
的回調(diào)函數(shù)。