2020年,vue面試遇到的問(wèn)題(上)
原文作者:東起
原文鏈接:https://zhuanlan.zhihu.com/p/103763164
0.前言
原文有 36 到 vue 常用面試題,考慮到太多一次也看不完,所以分為 上、中、下三篇,如果你能讀完這三篇文章,相信你在面試中 vue 的問(wèn)題你不會(huì)怕了。
1.頁(yè)面中定義一個(gè)定時(shí)器,在哪個(gè)階段清除?
答案:在 beforeDestroy 中銷毀定時(shí)器。
① 為什么銷毀它:
在頁(yè)面 a 中寫了一個(gè)定時(shí)器,比如每隔一秒鐘打印一次 1,當(dāng)我點(diǎn)擊按鈕進(jìn)入頁(yè)面 b 的時(shí)候,會(huì)發(fā)現(xiàn)定時(shí)器依然在執(zhí)行,這是非常消耗性能的。
② 解決方案 1:
mounted(){
this.timer = setInterval(()=>{
console.log(1)
},1000)
},
beforeDestroy(){
clearInterval(this.timer)
}
方案 1 有兩點(diǎn)不好的地方,引用尤大的話來(lái)說(shuō)就是:
它需要在這個(gè)組件實(shí)例中保存這個(gè) timer,如果可以的話最好只有生命周期鉤子可以訪問(wèn)到它。這并不算嚴(yán)重的問(wèn)題,但是它可以被視為雜物。
我們的建立代碼獨(dú)立于我們的清理代碼,這使得我們比較難于程序化的清理我們建立的所有東西。
方案 2(推薦):該方法是通過(guò)$once 這個(gè)事件偵聽器在定義完定時(shí)器之后的位置來(lái)清除定時(shí)器
mounted(){
const timer = setInterval(()=>{
console.log(1)
},1000)
this.$once('hook:beforeDestroy',()=>{
clearInterval(timer)
})
}
官網(wǎng)參考鏈接:https://cn.vuejs.org/v2/guide/components-edge-cases.html

2.父組件如何獲取子組件的數(shù)據(jù),子組件如何獲取父組件的數(shù)據(jù),父子組件如何傳值?
① 先說(shuō),父組件如何主動(dòng)獲取子組件的數(shù)據(jù)?
方案 1:$children
$children 用來(lái)訪問(wèn)子組件實(shí)例,要知道一個(gè)組件的子組件可能是不唯一的,所以它的返回值是數(shù)組。
現(xiàn)在,我們定義 Header,HelloWorld 兩個(gè)組件
<div class="index">
<Header>Header>
<HelloWorld :message="message">HelloWorld>
<button @click="goPro">跳轉(zhuǎn)button>
div>
template>
mounted(){
console.log(this.$children)
}

打印的是一個(gè)數(shù)組,可以用 foreach 分別得到所需要的的數(shù)據(jù)
缺點(diǎn):
無(wú)法確定子組件的順序,也不是響應(yīng)式的。如果你確切的知道要訪問(wèn)子組件建議使用$refs。
方案 2 :$refs
"hello" :message="message">HelloWorld>
調(diào)用 helloworld 子組件的時(shí)候直接定義一個(gè) ref,這樣就可以通過(guò) this.$refs 獲取所需要的的數(shù)據(jù)。
this.$refs.hello.屬性
this.$refs.hello.方法
② 子組件如何主動(dòng)獲取父組件中的數(shù)據(jù)?
通過(guò) :$parent
用來(lái)訪問(wèn)父組件實(shí)例,通常父組件都是唯一確定的,跟children 類似
this.$parent.屬性
this.$parent.方法
父子組件通信除了以上三種,還有 props 和?attrs
③inheritAttrs
這是@2.4 新增的屬性和接口。inheritAttrs 屬性控制子組件 html 屬性上是否顯示父組件的提供的屬性。
如果我們將父組件 Index 中的屬性 desc、keysword、message 三個(gè)數(shù)據(jù)傳遞到子組件 HelloWorld 中的話,如下
父組件 Index 部分
"hello" :desc="desc" :keysword="keysword" :message="message">HelloWorld>
子組件:HelloWorld,props 中只接受了 message
props: {
message: String
},
實(shí)際情況,我們只需要 message,那其他兩個(gè)屬性則會(huì)被當(dāng)做普通的 html 元素插在子組件的根元素上。
如圖
這樣做會(huì)使組件預(yù)期功能變得模糊不清,這個(gè)時(shí)候,在子組件中寫入,inheritAttrs:false ,這些沒用到的屬性便會(huì)被去掉,true 的話,就會(huì)顯示。
如果,父組件中沒被需要的屬性,跟子組件本來(lái)的屬性沖突的時(shí)候,則依據(jù)父組件
"hello" type="text" :message="message">HelloWorld>
子組件:HelloWorld
<input type="number">
template>
這個(gè)時(shí)候父組件中 type=“text”,而子組件中 type=”number”,而實(shí)際中最后顯示的是 type=”text”,這并不是我們想要的,所以只要設(shè)置:inheritAttrs:false,type 便會(huì)成為 number
上述這些沒被用到的屬性,如何被獲取呢?這就用到了$attrs
③$attrs
作用:可以獲取到?jīng)]有使用的注冊(cè)屬性,如果需要,我們?cè)谶@也可以往下繼續(xù)傳遞。
就上上述沒有被用到的 desc 和 keysword 就能通過(guò)$attrs 獲取到。
通過(guò)$attrs 的這個(gè)特性可以父組件傳遞到孫組件,免除父組件傳遞到子組件,再?gòu)淖咏M件傳遞到孫組件的麻煩
代碼如下 父組件 Index 部分
class="index">
<HelloWorld ref="hello" :desc="desc" :keysword="keysword" :message="message">HelloWorld>
div>
data(){
return{
message:'首頁(yè)',
desc:'首頁(yè)描述',
keysword:'我是關(guān)鍵詞key'
}
},
子組件 HelloWorld 部分
class="hello">
<sunzi v-bind="$attrs">sunzi>
<button @click="aa">獲取父組件的數(shù)據(jù)button>
div>
孫子組件 sunzi 部分
<div class="header">
{{$attrs}}
<br>
div>
template>
可以看出通過(guò) v-bind=”$attrs”將數(shù)據(jù)傳到孫組件中
除了以上,provide / inject 也適用于 隔代組件通信,尤其是獲取祖先組件的數(shù)據(jù),非常方便。
簡(jiǎn)單的說(shuō),當(dāng)組件的引入層次過(guò)多,我們的子孫組件想要獲取祖先組件的資源,那么怎么辦呢,總不能一直取父級(jí)往上吧,而且這樣代碼結(jié)構(gòu)容易混亂。這個(gè)就是 provide / inject 要干的事情。
<div>
<childOne>childOne>
div>
template>
<script>
import childOne from '../components/test/ChildOne'
export default {
name: "Parent",
provide: {
for: "demo"
},
components:{
childOne
}
}
在這里我們?cè)诟附M件中 provide for 這個(gè)變量,然后直接設(shè)置三個(gè)組件(childOne、childTwo 、childThird)并且一層層不斷內(nèi)嵌其中, 而在最深層的 childThird 組件中我們可以通過(guò) inject 獲取 for 這個(gè)變量
<div>
{{demo}}
div>
template>
<script>
export default {
name: "",
inject: ['for'],
data() {
return {
demo: this.for
}
}
}
script>
3.自定義指令如何定義,它的生命周期是什么?
通過(guò) Vue.directive() 來(lái)定義全局指令
有幾個(gè)可用的鉤子(生命周期), 每個(gè)鉤子可以選擇一些參數(shù). 鉤子如下:
bind: 一旦指令附加到元素時(shí)觸發(fā)
inserted: 一旦元素被添加到父元素時(shí)觸發(fā)
update: 每當(dāng)元素本身更新(但是子元素還未更新)時(shí)觸發(fā)
componentUpdate: 每當(dāng)組件和子組件被更新時(shí)觸發(fā)
unbind: 一旦指令被移除時(shí)觸發(fā)。
bind 和 update 也許是這五個(gè)里面最有用的兩個(gè)鉤子了
每個(gè)鉤子都有 el, binding, 和 vnode 參數(shù)可用.
update 和 componentUpdated 鉤子還暴露了 oldVnode, 以區(qū)分傳遞的舊值和較新的值.
el 就是所綁定的元素.
binding 是一個(gè)保護(hù)傳入鉤子的參數(shù)的對(duì)象. 有很多可用的參數(shù), 包括 name, value, oldValue, expression, arguments, arg 及修飾語(yǔ).
vnode 有一個(gè)更不尋常的用例, 它可用于你需要直接引用到虛擬 DOM 中的節(jié)點(diǎn).
binding 和 vnode 都應(yīng)該被視為只讀.
現(xiàn)在,自定義一個(gè)指令,添加一些樣式,表示定位的距離
Vue.directive('tack',{
bind(el,binding){
el.style.position='fixed';
el.style.top=binding.value + 'px'
}
})
class="header" v-tack="10" >我是header</div>
假設(shè)我們想要區(qū)分從頂部或者左側(cè)偏移 70px, 我們可以通過(guò)傳遞一個(gè)參數(shù)來(lái)做到這一點(diǎn)
Vue.directive('tack', {
bind(el, binding, vnode) {
el.style.position = 'fixed';
const s = (binding.arg === 'left' ? 'left' : 'top');
el.style[s] = binding.value + 'px';
}
})
也可以同時(shí)傳入不止一個(gè)值
Vue.directive('tack', {
bind(el, binding, vnode) {
el.style.position = 'fixed';
el.style.top = binding.value.top + 'px';
el.style.left = binding.value.left + 'px';
}
})
class="header" v-tack="{left:’20’,top:’20’}" >我是header</div>
4、vue 生命周期,各個(gè)階段簡(jiǎn)單講一下?
breforeCreate():實(shí)例創(chuàng)建前,這個(gè)階段實(shí)例的 data 和 methods 是讀不到的。
created():實(shí)例創(chuàng)建后,這個(gè)階段已經(jīng)完成數(shù)據(jù)觀測(cè),屬性和方法的運(yùn)算,watch/event 事件回調(diào),mount 掛載階段還沒有開始。$el 屬性目前不可見,數(shù)據(jù)并沒有在 DOM 元素上進(jìn)行渲染。
created 完成之后,進(jìn)行 template 編譯等操作,將 template 編譯為 render 函數(shù),有了 render 函數(shù)后才會(huì)執(zhí)行 beforeMount()
beforeMount():在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用
mounted():掛載之后調(diào)用,el 選項(xiàng)的 DOM 節(jié)點(diǎn)被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用此生命周期函數(shù),此時(shí)實(shí)例的數(shù)據(jù)在 DOM 節(jié)點(diǎn)上進(jìn)行渲染
后續(xù)的鉤子函數(shù)執(zhí)行的過(guò)程都是需要外部的觸發(fā)才會(huì)執(zhí)行
有數(shù)據(jù)的變化,會(huì)調(diào)用 beforeUpdate,然后經(jīng)過(guò) Virtual Dom,最后 updated 更新完畢,當(dāng)組件被銷毀的時(shí)候,會(huì)調(diào)用 beforeDestory,以及 destoryed。
5、watch 和 computed 的區(qū)別?
computed:
① 有緩存機(jī)制;② 不能接受參數(shù);③ 可以依賴其他 computed,甚至是其他組件的 data;④ 不能與 data 中的屬性重復(fù)
watch:
① 可接受兩個(gè)參數(shù);② 監(jiān)聽時(shí)可觸發(fā)一個(gè)回調(diào),并做一些事情;③ 監(jiān)聽的屬性必須是存在的;④ 允許異步
watch 配置:handler、deep(是否深度)、immeditate (是否立即執(zhí)行)
總結(jié):
當(dāng)有一些數(shù)據(jù)需要隨著另外一些數(shù)據(jù)變化時(shí),建議使用 computed
當(dāng)有一個(gè)通用的響應(yīng)數(shù)據(jù)變化的時(shí)候,要執(zhí)行一些業(yè)務(wù)邏輯或異步操作的時(shí)候建議使用 watch
6、請(qǐng)說(shuō)一下 computed 中的 getter 和 setter
① computed 中可以分成 getter(讀取) 和 setter(設(shè)值)
② 一般情況下是沒有 setter 的,computed 預(yù)設(shè)只有 getter ,也就是只能讀取,不能改變?cè)O(shè)值。
一、默認(rèn)只有 getter 的寫法
"demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
//其實(shí)fullName的完整寫法應(yīng)該是如下:
fullName: {
get(){
return this.firstName + ' ' + this.lastName
}
}
注意:不是說(shuō)我們更改了 getter 里使用的變量,就會(huì)觸發(fā) computed 的更新,前提是 computed 里的值必須要在模板里使用才行。如果將{{fullName}}去掉,get()方法是不會(huì)觸發(fā)的。
二、setter 的寫法,可以設(shè)值
{{ fullName }}
var vm = new Vue({
el: '#demo',
data: {
firstName: 'zhang',
lastName: 'san'
},
computed: {
fullName: {
//getter 方法
get(){
console.log('computed getter...')
return this.firstName + ' ' + this.lastName
},
//setter 方法
set(newValue){
console.log('computed setter...')
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
return this.firstName + ' ' + this.lastName
}
}
}
})
在這里,我們修改 fullName 的值,就會(huì)觸發(fā) setter,同時(shí)也會(huì)觸發(fā) getter。
注意:并不是觸發(fā)了 setter 也就會(huì)觸發(fā) getter,他們兩個(gè)是相互獨(dú)立的。我們這里修改了 fullName 會(huì)觸發(fā) getter 是因?yàn)?setter 函數(shù)里有改變 firstName 和 lastName 值的代碼,這兩個(gè)值改變了,fullName 依賴于這兩個(gè)值,所以便會(huì)自動(dòng)改變。
7、導(dǎo)航鉤子有哪幾種,分別如何用,如何將數(shù)據(jù)傳入下一個(gè)點(diǎn)擊的路由頁(yè)面?
① 全局導(dǎo)航守衛(wèi)
前置守衛(wèi)
router.beforeEach((to, from, next) => {
// do someting
});
后置鉤子(沒有 next 參數(shù))
router.afterEach((to, from) => {
// do someting
});
② 路由獨(dú)享守衛(wèi)
cont router = new VueRouter({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
順便看一下路由里面的參數(shù)配置:
③ 組件內(nèi)的導(dǎo)航鉤子
組件內(nèi)的導(dǎo)航鉤子主要有這三種:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他們是直接在路由組件內(nèi)部直接進(jìn)行定義的
beforeRouteEnter
data(){
return{
pro:'產(chǎn)品'
}
},
beforeRouteEnter:(to,from,next)=>{
console.log(to)
next(vm => {
console.log(vm.pro)
})
}
注:beforeRouteEnter 不能獲取組件實(shí)例 this,因?yàn)楫?dāng)守衛(wèi)執(zhí)行前,組件實(shí)例被沒有被創(chuàng)建出來(lái),我們可以通過(guò)給 next 傳入一個(gè)回調(diào)來(lái)訪問(wèn)組件實(shí)例。在導(dǎo)航被確認(rèn)時(shí),會(huì)執(zhí)行這個(gè)回調(diào),這時(shí)就可以訪問(wèn)組件實(shí)例了
僅僅是 beforRouteEnter 支持給 next 傳遞回調(diào),其他兩個(gè)并不支持,因?yàn)槭O聝蓚€(gè)鉤子可以正常獲取組件實(shí)例 this
如何通過(guò)路由將數(shù)據(jù)傳入下一個(gè)跳轉(zhuǎn)的頁(yè)面呢?
答:params 和 query
params
傳參
this.$router.push({
name:"detail",
params:{
name:'xiaoming',
}
});
接受
this.$route.params.name
query
傳參
this.$router.push({
path:'/detail',
query:{
name:"xiaoming"
}
})
接受 //接收參數(shù)是this.$route
this.$route.query.id
那 query 和 params 什么區(qū)別呢?
① params 只能用 name 來(lái)引入路由,query 既可以用 name 又可以用 path(通常用 path)
② params 類似于 post 方法,參數(shù)不會(huì)再地址欄中顯示
query 類似于 get 請(qǐng)求,頁(yè)面跳轉(zhuǎn)的時(shí)候,可以在地址欄看到請(qǐng)求參數(shù)
那剛才提到的 this.和route 有何區(qū)別?
先打印出來(lái)看一下
?router.push 方法
$route 為當(dāng)前 router 跳轉(zhuǎn)對(duì)象,里面可以獲取 name、path、query、params 等
8、es6 的特有的類型, 常用的操作數(shù)組的方法都有哪些?
es6 新增的主要的特性:
① let const 兩者都有塊級(jí)作用域
② 箭頭函數(shù)
③ 模板字符串
④ 解構(gòu)賦值
⑤ for of 循環(huán)
⑥ import 、export 導(dǎo)入導(dǎo)出
⑦ set 數(shù)據(jù)結(jié)構(gòu)
⑧ ...展開運(yùn)算符
⑨ 修飾器 @
⑩ class 類繼承
? async、await
? promise
? Symbol
? Proxy 代理
操作數(shù)組常用的方法:
es5:concat 、join 、push、pop、shift、unshift、slice、splice、substring 和 substr 、sort、 reverse、indexOf 和 lastIndexOf 、every、some、filter、map、forEach、reduce
es6:find、findIndex、fill、copyWithin、Array.from、Array.of、entries、values、key、includes
9、vue 雙向綁定原理?
通過(guò) Object.defineProperty()來(lái)劫持各個(gè)屬性的 setter,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)
10、vue-router 的實(shí)現(xiàn)原理,history 和 hash 模式有什么區(qū)別?
vue-router 有兩種模式,hash 模式和 history 模式
hash 模式
url 中帶有#的便是 hash 模式,#后面是 hash 值,它的變化會(huì)觸發(fā) hashchange 這個(gè)事件。
通過(guò)這個(gè)事件我們就可以知道 hash 值發(fā)生了哪些變化。然后我們便可以監(jiān)聽 hashchange 來(lái)實(shí)現(xiàn)更新頁(yè)面部分內(nèi)容的操作:
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1);
document.body.style.color = hash;
}
另外,hash 值的變化,并不會(huì)導(dǎo)致瀏覽器向服務(wù)器發(fā)出請(qǐng)求,瀏覽器不發(fā)出請(qǐng)求,也就不會(huì)刷新頁(yè)面。
history 模式
history api 可以分為兩大部分,切換和修改
① 切換歷史狀態(tài)
包括 back,forward,go 三個(gè)方法,對(duì)應(yīng)瀏覽器的前進(jìn),后退,跳轉(zhuǎn)操作
history.go(-2);//后退兩次
history.go(2);//前進(jìn)兩次
history.back(); //后退
hsitory.forward(); //前進(jìn)
② 修改歷史狀態(tài)
包括了 pushState,replaceState 兩個(gè)方法,這兩個(gè)方法接收三個(gè)參數(shù):stateObj,title,url
history.pushState({color:'red'}, 'red', 'red'})
window.onpopstate = function(event){
console.log(event.state)
if(event.state && event.state.color === 'red'){
document.body.style.color = 'red';
}
}
history.back();
history.forward();
通過(guò) pushstate 把頁(yè)面的狀態(tài)保存在 state 對(duì)象中,當(dāng)頁(yè)面的 url 再變回這個(gè) url 時(shí),可以通過(guò) event.state 取到這個(gè) state 對(duì)象,從而可以對(duì)頁(yè)面狀態(tài)進(jìn)行還原,這里的頁(yè)面狀態(tài)就是頁(yè)面字體顏色,其實(shí)滾動(dòng)條的位置,閱讀進(jìn)度,組件的開關(guān)的這些頁(yè)面狀態(tài)都可以存儲(chǔ)到 state 的里面。
history 缺點(diǎn):
1:hash 模式下,僅 hash 符號(hào)之前的內(nèi)容會(huì)被包含在請(qǐng)求中,如http://www.a12c.com,因此對(duì)于后端來(lái)說(shuō),即使沒有做到對(duì)路由的全覆蓋,也不會(huì)返回 404 錯(cuò)誤。
2:history 模式下,前端的 URL 必須和實(shí)際向后端發(fā)起請(qǐng)求的 URL 一致。如http://www.a12c.com/book/a。如果后端缺少對(duì)/book/a 的路由處理,將返回 404 錯(cuò)誤
推薦閱讀
我的公眾號(hào)能帶來(lái)什么價(jià)值?(文末有送書規(guī)則,一定要看)
每個(gè)前端工程師都應(yīng)該了解的圖片知識(shí)(長(zhǎng)文建議收藏)
為什么現(xiàn)在面試總是面試造火箭?
瀏覽
1
評(píng)論圖片表情
天天抽天天操
|
伊人伊网
|
欧美性生交大片免费看A片免费
|
色婷婷亚洲精品天天综合
|
欧美性开放网站
|
