【Vuejs】625- Vue常見的考點

在你看到這篇文章時刻,說明你多少是一個前端開發這,或者對前端開發有一定的應用和喜好;對於當下的前端來說,瞭解一個前端的流行框架,是多麼一個勢在必行的趨勢,也是找工作中經常會詢問到的,在這裏就列舉一些流行框架vue的常見考點,希望這些考點能給你帶來一定的幫助!

1.頁面中定義一個定時器,在哪個階段清除?

答案:在 beforeDestroy 中銷燬定時器。

① 爲什麼銷燬它:

在頁面 a 中寫了一個定時器,比如每隔一秒鐘打印一次 1,當我點擊按鈕進入頁面 b 的時候,會發現定時器依然在執行,這是非常消耗性能的。

② 解決方案 1:

mounted(){
 this.timer = setInterval(()=>{
    console.log(1)
 },1000)
},
beforeDestroy(){
 clearInterval(this.timer)
}

方案 1 有兩點不好的地方,引用尤大的話來說就是:

它需要在這個組件實例中保存這個 timer,如果可以的話最好只有生命週期鉤子可以訪問到它。這並不算嚴重的問題,但是它可以被視爲雜物。

我們的建立代碼獨立於我們的清理代碼,這使得我們比較難於程序化的清理我們建立的所有東西。

方案 2(推薦):該方法是通過$once 這個事件偵聽器在定義完定時器之後的位置來清除定時器

mounted(){
 const timer = setInterval(()=>{
    console.log(1)
 },1000)
 this.$once('hook:beforeDestroy',()=>{
  clearInterval(timer)
 })
}

官網參考鏈接:https://cn.vuejs.org/v2/guide/components-edge-cases.html

2.父組件如何獲取子組件的數據,子組件如何獲取父組件的數據,父子組件如何傳值?

① 先說,父組件如何主動獲取子組件的數據?

方案 1:$children

$children 用來訪問子組件實例,要知道一個組件的子組件可能是不唯一的,所以它的返回值是數組。

現在,我們定義 Header,HelloWorld 兩個組件

<template>
  <div class="index">
    <Header></Header>
    <HelloWorld :message="message"></HelloWorld>
    <button @click="goPro">跳轉</button>
  </div>
</template>
mounted(){
 console.log(this.$children)
}

打印的是一個數組,可以用 foreach 分別得到所需要的的數據

缺點:

無法確定子組件的順序,也不是響應式的。如果你確切的知道要訪問子組件建議使用$refs。

方案 2 :$refs

<HelloWorld ref="hello" :message="message"></HelloWorld>

調用 helloworld 子組件的時候直接定義一個 ref,這樣就可以通過 this.$refs 獲取所需要的的數據。

this.$refs.hello.屬性
this.$refs.hello.方法

② 子組件如何主動獲取父組件中的數據?

通過 :$parent

用來訪問父組件實例,通常父組件都是唯一確定的,跟children 類似

this.$parent.屬性
this.$parent.方法

父子組件通信除了以上三種,還有 props 和 attrs

③inheritAttrs

這是@2.4 新增的屬性和接口。inheritAttrs 屬性控制子組件 html 屬性上是否顯示父組件的提供的屬性。

如果我們將父組件 Index 中的屬性 desc、keysword、message 三個數據傳遞到子組件 HelloWorld 中的話,如下

父組件 Index 部分

<HelloWorld ref="hello" :desc="desc" :keysword="keysword" :message="message"></HelloWorld>

子組件:HelloWorld,props 中只接受了 message

props: {
    message: String
},

實際情況,我們只需要 message,那其他兩個屬性則會被當做普通的 html 元素插在子組件的根元素上。

如圖這樣做會使組件預期功能變得模糊不清,這個時候,在子組件中寫入,inheritAttrs:false ,這些沒用到的屬性便會被去掉,true 的話,就會顯示。

如果,父組件中沒被需要的屬性,跟子組件本來的屬性衝突的時候,則依據父組件

<HelloWorld ref="hello" type="text" :message="message"></HelloWorld>

子組件:HelloWorld

<template>
  <input type="number">
</template>

這個時候父組件中 type=“text”,而子組件中 type=”number”,而實際中最後顯示的是 type=”text”,這並不是我們想要的,所以只要設置:inheritAttrs:false,type 便會成爲 number上述這些沒被用到的屬性,如何被獲取呢?這就用到了$attrs

③$attrs

作用:可以獲取到沒有使用的註冊屬性,如果需要,我們在這也可以往下繼續傳遞。

就上上述沒有被用到的 desc 和 keysword 就能通過$attrs 獲取到。

通過$attrs 的這個特性可以父組件傳遞到孫組件,免除父組件傳遞到子組件,再從子組件傳遞到孫組件的麻煩

代碼如下 父組件 Index 部分

<div class="index">
  <HelloWorld ref="hello" :desc="desc" :keysword="keysword" :message="message"></HelloWorld>
</div>
 data(){
  return{
   message:'首頁',
   desc:'首頁描述',
   keysword:'我是關鍵詞key'
 }
},

子組件 HelloWorld 部分

<div class="hello">
   <sunzi v-bind="$attrs"></sunzi>
   <button @click="aa">獲取父組件的數據</button>
</div>

孫子組件 sunzi 部分

<template>
  <div class="header">
    {{$attrs}}
    <br>
  </div>
</template>

可以看出通過 v-bind=”$attrs”將數據傳到孫組件中

除了以上,provide / inject 也適用於 隔代組件通信,尤其是獲取祖先組件的數據,非常方便。簡單的說,當組件的引入層次過多,我們的子孫組件想要獲取祖先組件的資源,那麼怎麼辦呢,總不能一直取父級往上吧,而且這樣代碼結構容易混亂。這個就是 provide / inject 要乾的事情。

<template>
  <div>
<childOne></childOne>
  </div>
</template>

<script>
  import childOne from '../components/test/ChildOne'
  export default {
    name: "Parent",
    provide: {
      for: "demo"
    },
    components:{
      childOne
    }
  }

在這裏我們在父組件中 provide for 這個變量,然後直接設置三個組件(childOne、childTwo 、childThird)並且一層層不斷內嵌其中, 而在最深層的 childThird 組件中我們可以通過 inject 獲取 for 這個變量

<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>

3.自定義指令如何定義,它的生命週期是什麼?

通過 Vue.directive() 來定義全局指令

有幾個可用的鉤子(生命週期), 每個鉤子可以選擇一些參數. 鉤子如下:

bind: 一旦指令附加到元素時觸發

inserted: 一旦元素被添加到父元素時觸發

update: 每當元素本身更新(但是子元素還未更新)時觸發

componentUpdate: 每當組件和子組件被更新時觸發

unbind: 一旦指令被移除時觸發。

bind 和 update 也許是這五個裏面最有用的兩個鉤子了

每個鉤子都有 el, binding, 和 vnode 參數可用.

update 和 componentUpdated 鉤子還暴露了 oldVnode, 以區分傳遞的舊值和較新的值.

el 就是所綁定的元素.

binding 是一個保護傳入鉤子的參數的對象. 有很多可用的參數, 包括 name, value, oldValue, expression, arguments, arg 及修飾語.

vnode 有一個更不尋常的用例, 它可用於你需要直接引用到虛擬 DOM 中的節點.

binding 和 vnode 都應該被視爲只讀.

現在,自定義一個指令,添加一些樣式,表示定位的距離

Vue.directive('tack',{
 bind(el,binding){
  el.style.position='fixed';
  el.style.top=binding.value + 'px'
 }
})
<div class="header" v-tack="10" >我是header</div>

假設我們想要區分從頂部或者左側偏移 70px, 我們可以通過傳遞一個參數來做到這一點

Vue.directive('tack', {
 bind(el, binding, vnode) {
  el.style.position = 'fixed';
  const s = (binding.arg === 'left' ? 'left' : 'top');
  el.style[s] = binding.value + 'px';
 }
})

也可以同時傳入不止一個值

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';
 }
})
<div class="header" v-tack="{left:’20’,top:’20’}" >我是header</div>

4、vue 生命週期,各個階段簡單講一下?

breforeCreate():實例創建前,這個階段實例的 data 和 methods 是讀不到的。

created():實例創建後,這個階段已經完成數據觀測,屬性和方法的運算,watch/event 事件回調,mount 掛載階段還沒有開始。$el 屬性目前不可見,數據並沒有在 DOM 元素上進行渲染。

created 完成之後,進行 template 編譯等操作,將 template 編譯爲 render 函數,有了 render 函數後纔會執行 beforeMount()

beforeMount():在掛載開始之前被調用:相關的 render 函數首次被調用

mounted():掛載之後調用,el 選項的 DOM 節點被新創建的 vm.$el 替換,並掛載到實例上去之後調用此生命週期函數,此時實例的數據在 DOM 節點上進行渲染

後續的鉤子函數執行的過程都是需要外部的觸發纔會執行

有數據的變化,會調用 beforeUpdate,然後經過 Virtual Dom,最後 updated 更新完畢,當組件被銷燬的時候,會調用 beforeDestory,以及 destoryed。

5、watch 和 computed 的區別?

computed:

① 有緩存機制;② 不能接受參數;③ 可以依賴其他 computed,甚至是其他組件的 data;④ 不能與 data 中的屬性重複

watch:

① 可接受兩個參數;② 監聽時可觸發一個回調,並做一些事情;③ 監聽的屬性必須是存在的;④ 允許異步

watch 配置:handler、deep(是否深度)、immeditate (是否立即執行)

總結:

當有一些數據需要隨着另外一些數據變化時,建議使用 computed

當有一個通用的響應數據變化的時候,要執行一些業務邏輯或異步操作的時候建議使用 watch

6、請說一下 computed 中的 getter 和 setter

① computed 中可以分成 getter(讀取) 和 setter(設值)

② 一般情況下是沒有 setter 的,computed 預設只有 getter ,也就是隻能讀取,不能改變設值。

一、默認只有 getter 的寫法

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
//其實fullName的完整寫法應該是如下:
fullName: {
 get(){
   return this.firstName + ' ' + this.lastName
 }
}

注意:不是說我們更改了 getter 裏使用的變量,就會觸發 computed 的更新,前提是 computed 裏的值必須要在模板裏使用纔行。如果將{{fullName}}去掉,get()方法是不會觸發的。

二、setter 的寫法,可以設值

<template>
   <div id="demo">
       <p> {{ fullName }} </p>
       <input type="text" v-model="fullName">
       <input type="text" v-model="firstName">
       <input type="text" v-model="lastName">
   </div>
</template>

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 的值,就會觸發 setter,同時也會觸發 getter。

注意:並不是觸發了 setter 也就會觸發 getter,他們兩個是相互獨立的。我們這裏修改了 fullName 會觸發 getter 是因爲 setter 函數裏有改變 firstName 和 lastName 值的代碼,這兩個值改變了,fullName 依賴於這兩個值,所以便會自動改變。

7、導航鉤子有哪幾種,分別如何用,如何將數據傳入下一個點擊的路由頁面?

① 全局導航守衛

前置守衛

router.beforeEach((to, from, next) => {
  // do someting
});

後置鉤子(沒有 next 參數)

router.afterEach((to, from) => {
  // do someting
});

② 路由獨享守衛

cont router = new  VueRouter({
 routes: [
  {
    path: '/file',
    component: File,
    beforeEnter: (to, from ,next) => {
       // do someting
    }
   }
 ]
});

順便看一下路由裏面的參數配置:

③ 組件內的導航鉤子

組件內的導航鉤子主要有這三種:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他們是直接在路由組件內部直接進行定義的

beforeRouteEnter

data(){
 return{
   pro:'產品'
 }
},
beforeRouteEnter:(to,from,next)=>{
  console.log(to)
  next(vm => {
   console.log(vm.pro)
  })
}

注:beforeRouteEnter 不能獲取組件實例 this,因爲當守衛執行前,組件實例被沒有被創建出來,我們可以通過給 next 傳入一個回調來訪問組件實例。在導航被確認時,會執行這個回調,這時就可以訪問組件實例了

僅僅是 beforRouteEnter 支持給 next 傳遞迴調,其他兩個並不支持,因爲剩下兩個鉤子可以正常獲取組件實例 this

如何通過路由將數據傳入下一個跳轉的頁面呢?

答:params 和 query

params

傳參
this.$router.push({
 name:"detail",
 params:{
   name:'xiaoming',
 }
});
接受
this.$route.params.name

query

傳參
this.$router.push({
  path:'/detail',
  query:{
    name:"xiaoming"
  }
 })
接受 //接收參數是this.$route
this.$route.query.id

那 query 和 params 什麼區別呢?

① params 只能用 name 來引入路由,query 既可以用 name 又可以用 path(通常用 path)

② params 類似於 post 方法,參數不會再地址欄中顯示query 類似於 get 請求,頁面跳轉的時候,可以在地址欄看到請求參數那剛纔提到的 this.route 有何區別?

先打印出來看一下 router.push 方法

$route 爲當前 router 跳轉對象,裏面可以獲取 name、path、query、params 等

8、es6 的特有的類型, 常用的操作數組的方法都有哪些?

es6 新增的主要的特性:

① let const 兩者都有塊級作用域

② 箭頭函數

③ 模板字符串

④ 解構賦值

⑤ for of 循環

⑥ import 、export 導入導出

⑦ set 數據結構

⑧ ...展開運算符

⑨ 修飾器 @

⑩ class 類繼承

⑪ async、await

⑫ promise

⑬ Symbol

⑭ Proxy 代理

操作數組常用的方法:

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 雙向綁定原理?

通過 Object.defineProperty()來劫持各個屬性的 setter,getter,在數據變動時發佈消息給訂閱者,觸發相應的監聽回調

10、vue-router 的實現原理,history 和 hash 模式有什麼區別?

vue-router 有兩種模式,hash 模式和 history 模式

hash 模式

url 中帶有#的便是 hash 模式,#後面是 hash 值,它的變化會觸發 hashchange 這個事件。

通過這個事件我們就可以知道 hash 值發生了哪些變化。然後我們便可以監聽 hashchange 來實現更新頁面部分內容的操作:

window.onhashchange = function(event){
  console.log(event.oldURL, event.newURL);
  let hash = location.hash.slice(1);
  document.body.style.color = hash;
}

另外,hash 值的變化,並不會導致瀏覽器向服務器發出請求,瀏覽器不發出請求,也就不會刷新頁面。

history 模式

history api 可以分爲兩大部分,切換和修改

① 切換歷史狀態

包括 back,forward,go 三個方法,對應瀏覽器的前進,後退,跳轉操作

history.go(-2);//後退兩次
history.go(2);//前進兩次
history.back(); //後退
hsitory.forward(); //前進

② 修改歷史狀態

包括了 pushState,replaceState 兩個方法,這兩個方法接收三個參數: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();

通過 pushstate 把頁面的狀態保存在 state 對象中,當頁面的 url 再變回這個 url 時,可以通過 event.state 取到這個 state 對象,從而可以對頁面狀態進行還原,這裏的頁面狀態就是頁面字體顏色,其實滾動條的位置,閱讀進度,組件的開關的這些頁面狀態都可以存儲到 state 的裏面。

history 缺點:

1:hash 模式下,僅 hash 符號之前的內容會被包含在請求中,如http://www.a12c.com,因此對於後端來說,即使沒有做到對路由的全覆蓋,也不會返回 404 錯誤。

2:history 模式下,前端的 URL 必須和實際向後端發起請求的 URL 一致。如http://www.a12c.com/book/a。如果後端缺少對/book/a 的路由處理,將返回 404 錯誤

 

1. JavaScript 重溫系列(22篇全)

2. ECMAScript 重溫系列(10篇全)

3. JavaScript設計模式 重溫系列(9篇全)

4. 正則 / 框架 / 算法等 重溫系列(16篇全)

5. Webpack4 入門(上)|| Webpack4 入門(下)

6. MobX 入門(上) ||  MobX 入門(下)

7. 59篇原創系列彙總

回覆“加羣”與大佬們一起交流學習~

點擊“閱讀原文”查看70+篇原創文章

點這,與大家一起分享本文吧~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章