国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

vue核心面試題匯總【查缺補(bǔ)漏】

這篇具有很好參考價(jià)值的文章主要介紹了vue核心面試題匯總【查缺補(bǔ)漏】。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

?給大家推薦一個(gè)實(shí)用面試題庫

1、前端面試題庫 (面試必備) ? ? ? ? ? ?推薦:★★★★★

地址:web前端面試題庫文章來源地址http://www.zghlxwxcb.cn/news/detail-726406.html

很喜歡‘萬變不離其宗’這句話,希望在不斷的思考和總結(jié)中找到Vue中的,來解答面試官拋出的各種Vue問題,一起加油~

一、MVVM原理

Vue2官方文檔中沒有找到VueMVVM的直接證據(jù),但文檔有提到:雖然沒有完全遵循MVVM模型,但是 Vue 的設(shè)計(jì)也受到了它的啟發(fā),因此在文檔中經(jīng)常會(huì)使用vm(ViewModel 的縮寫) 這個(gè)變量名表示 Vue 實(shí)例。

為了感受MVVM模型的啟發(fā),我簡單列舉下其概念。

MVVM是Model-View-ViewModel的簡寫,由三部分構(gòu)成:

  • Model: 模型持有所有的數(shù)據(jù)、狀態(tài)和程序邏輯
  • View: 負(fù)責(zé)界面的布局和顯示
  • ViewModel:負(fù)責(zé)模型和界面之間的交互,是Model和View的橋梁

二、SPA單頁面應(yīng)用

單頁Web應(yīng)用(single page web application,SPA),就是只有一張Web頁面的應(yīng)用,是加載單個(gè)HTML頁面并在用戶與應(yīng)用程序交互時(shí)動(dòng)態(tài)更新該頁面的Web應(yīng)用程序。我們開發(fā)的Vue項(xiàng)目大多是借助個(gè)官方的CLI腳手架,快速搭建項(xiàng)目,直接通過new Vue構(gòu)建一個(gè)實(shí)例,并將el:'#app'掛載參數(shù)傳入,最后通過npm run build的方式打包后生成一個(gè)index.html,稱這種只有一個(gè)HTML的頁面為單頁面應(yīng)用。

當(dāng)然,vue也可以像jq一樣引入,作為多頁面應(yīng)用的基礎(chǔ)框架。

三、Vue的特點(diǎn)

  • 清晰的官方文檔和好用的api,比較容易上手。
  • 是一套用于構(gòu)建用戶界面的漸進(jìn)式框架,將注意力集中保持在核心庫,而將其他功能如路由和全局狀態(tài)管理交給相關(guān)的庫。
  • 使用?Virtual DOM
  • 提供了響應(yīng)式?(Reactive) 和組件化?(Composable) 的視圖組件。

四、Vue的構(gòu)建入口

vue使用過程中可以采用以下兩種方式:

  • 在vue腳手架中直接使用,參考文檔:https://cn.vuejs.org/v2/guide/installation.html
  • 或者在html文件的頭部通過靜態(tài)文件的方式引入:?<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

那么問題來了,使用的或者引入的到底是什么?
答:引入的是已經(jīng)打包好的vue.js文件,通過rollup構(gòu)建打包所得。

構(gòu)建入口在哪里?
答:在vue源碼的package.json文件中:

"scripts": {
    // ...
    "build": "node scripts/build.js",
    "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
    "build:weex": "npm run build -- weex",
    // ...
  },

通過執(zhí)行npm run build的時(shí)候,會(huì)進(jìn)行scripts/build.js文件的執(zhí)行,npm run build:ssr和npm run build:weex的時(shí)候,將ssr和weex作為參數(shù)傳入,按照參數(shù)構(gòu)建出不一樣的vue.js打包文件。

所以說,vue中的package.json文件就是構(gòu)建的入口,具體構(gòu)建流程可以參考vue2入口:構(gòu)建入口。

五、對(duì)import Vue from "vue"的理解

在使用腳手架開發(fā)項(xiàng)目時(shí),會(huì)有一行代碼import Vue from "vue",那么這個(gè)Vue指的是什么。
答:一個(gè)構(gòu)造函數(shù)。

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

我們開發(fā)中引入的Vue其實(shí)就是這個(gè)構(gòu)造函數(shù),而且這個(gè)構(gòu)造函數(shù)只能通過new Vue的方式進(jìn)行使用,否則會(huì)在控制臺(tái)打印警告信息。定義完后,還會(huì)通過initMixin(Vue)、stateMixin(Vue)、eventsMixin(Vue)、lifecycleMixin(Vue)renderMixin(Vue)的方式為Vue原型中混入方法。我們通過import Vue from "Vue"引入的本質(zhì)上就是一個(gè)原型上掛在了好多方法的構(gòu)造函數(shù)。

六、對(duì)new Vue的理解

// main.js文件
import Vue from "vue";
var app = new Vue({
  el: '#app',
  data() {
    return {
      msg: 'hello Vue~'
    }
  },
  template: `<div>{{msg}}</div>`,
})

console.log(app);

new Vue就是對(duì)構(gòu)造函數(shù)Vue進(jìn)行實(shí)例化,執(zhí)行結(jié)果如下:

vue核心面試題匯總【查缺補(bǔ)漏】,前端面試題,vue.js,前端,javascript

可以看出實(shí)例化后的實(shí)例中包含了很多屬性,用來對(duì)當(dāng)前app進(jìn)行描述,當(dāng)然復(fù)雜的Vue項(xiàng)目這個(gè)app將會(huì)是一個(gè)樹結(jié)構(gòu),通過$parent$children維護(hù)父子關(guān)系。

new Vue的過程中還會(huì)執(zhí)行this._init方法進(jìn)行初始化處理。

七、編譯

虛擬DOM的生成必須通過render函數(shù)實(shí)現(xiàn),render函數(shù)的產(chǎn)生是在編譯階段完成,核心代碼如下:

export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

主要完成的功能是:

  • 通過const ast = parse(template.trim(), options)template轉(zhuǎn)換成ast
  • 通過optimize(ast, options)對(duì)ast進(jìn)行優(yōu)化
  • 通過const code = generate(ast, options)將優(yōu)化后的ast轉(zhuǎn)換成包含render字符串的code對(duì)象,最終render字符串通過new Function轉(zhuǎn)換為可執(zhí)行的render函數(shù)

模板編譯的真實(shí)入口可以參考vue2從template到render:模板編譯入口
parse可以參考vue2從template到render:AST
optimize可以參考vue2從template到render:optimize
generate可以參考vue2從template到render:code

八、虛擬DOM

先看瀏覽器對(duì)HTML的理解

<div>  
    <h1>My title</h1>  
    Some text content  
    <!-- TODO: Add tagline -->  
</div>

當(dāng)瀏覽器讀到這些代碼時(shí),它會(huì)建立一個(gè)DOM樹來保持追蹤所有內(nèi)容,如同你會(huì)畫一張家譜樹來追蹤家庭成員的發(fā)展一樣。 上述 HTML 對(duì)應(yīng)的 DOM 節(jié)點(diǎn)樹如下圖所示:

vue核心面試題匯總【查缺補(bǔ)漏】,前端面試題,vue.js,前端,javascript

?每個(gè)元素都是一個(gè)節(jié)點(diǎn)。每段文字也是一個(gè)節(jié)點(diǎn)。甚至注釋也都是節(jié)點(diǎn)。一個(gè)節(jié)點(diǎn)就是頁面的一個(gè)部分。就像家譜樹一樣,每個(gè)節(jié)點(diǎn)都可以有孩子節(jié)點(diǎn) (也就是說每個(gè)部分可以包含其它的一些部分)。

再看Vue對(duì)HTML template的理解

Vue 通過建立一個(gè)虛擬 DOM?來追蹤自己要如何改變真實(shí) DOM。因?yàn)樗男畔?huì)告訴 Vue 頁面上需要渲染什么樣的節(jié)點(diǎn),包括及其子節(jié)點(diǎn)的描述信息。我們把這樣的節(jié)點(diǎn)描述為“虛擬節(jié)點(diǎn) (virtual node)”,也常簡寫它為“VNode”?!疤摂M DOM”是我們對(duì)由 Vue 組件樹建立起來的整個(gè) VNode 樹的稱呼。

簡言之,瀏覽器對(duì)HTML的理解是DOM樹,Vue對(duì)HTML的理解是虛擬DOM,最后在patch階段通過DOM操作的api將其渲染成真實(shí)的DOM節(jié)點(diǎn)。

九、模板或者組件渲染

Vue中的編譯會(huì)執(zhí)行到邏輯vm._update(vm._render(), hydrating),其中的vm._render執(zhí)行會(huì)獲取到vNode,vm._update就會(huì)對(duì)vNode進(jìn)行patch的處理,又分為模板渲染和組件渲染。

十、數(shù)據(jù)響應(yīng)式處理

Vue的數(shù)據(jù)響應(yīng)式處理的核心是Object.defineProperty,在遞歸響應(yīng)式處理對(duì)象的過程中,為每一個(gè)屬性定義了一個(gè)發(fā)布者dep,當(dāng)進(jìn)行_render函數(shù)執(zhí)行時(shí)會(huì)訪問到當(dāng)前值,在get中通過dep.depend進(jìn)行當(dāng)前Watcher的收集,當(dāng)數(shù)據(jù)發(fā)生變化時(shí)會(huì)在set中通過dep.notify進(jìn)行Watcher的更新。

數(shù)據(jù)響應(yīng)式處理以及發(fā)布訂閱者模式的關(guān)系請(qǐng)參考vue2從數(shù)據(jù)變化到視圖變化:發(fā)布訂閱模式

十一、this.$set

const app = new Vue({
  el: "#app",
  data() {
    return {
      obj: {
        name: "name-1"
      }
    };
  },
  template: `<div @click="change">{{obj.name}}的年齡是{{obj.age}}</div>`,
  methods: {
    change() {
      this.obj.name = 'name-2';
      this.obj.age = 30;
    }
  }
});

以上例子執(zhí)行的結(jié)果是:
name-1的年齡是
當(dāng)點(diǎn)擊后依然是:
name-2的年齡是
可以看出點(diǎn)擊后,objname屬性變化得到了視圖更新,而age屬性并未進(jìn)行變化。

name屬性響應(yīng)式的過程中鎖定了一個(gè)發(fā)布者dep,在當(dāng)前視圖渲染時(shí)在發(fā)布者depsubs中做了記錄,一旦其發(fā)生改變,就會(huì)觸發(fā)set方法中的dep.notify,繼而執(zhí)行視圖的重新渲染。然而,age屬性并未進(jìn)行響應(yīng)式的處理,當(dāng)其改變時(shí)就不能進(jìn)行視圖渲染。

十二、組件注冊(cè)

組件的使用是先注冊(cè)后使用,又分為:

  • 全局注冊(cè):可以直接在頁面中使用
  • 局部注冊(cè):使用時(shí)需要通過import xxx from xxx的方式引入,并且在當(dāng)前組件的選項(xiàng)components中增加局部組件的名稱。

十三、異步組件

Vue單頁面應(yīng)用中一個(gè)頁面只有一個(gè)<div id="app"></div>承載所有節(jié)點(diǎn),因此復(fù)雜項(xiàng)目可能會(huì)出現(xiàn)首屏加載白屏等問題,Vue異步組件就很好的處理了這問題。

十四、this.$nextTick

因?yàn)橥ㄟ^new實(shí)例化構(gòu)造函數(shù)Vue的時(shí)候會(huì)執(zhí)行初始化方法this._init,其中涉及到的方法大多都是同步執(zhí)行。nextTick在vue中是一個(gè)很重要的方法,在new Vue實(shí)例化的同步過程中將一些需要異步處理的函數(shù)推到異步隊(duì)列中去,可以等new Vue所有的同步任務(wù)執(zhí)行完后,再執(zhí)行異步隊(duì)列中的函數(shù)。

nextTick的實(shí)現(xiàn)可以參考?vue2從數(shù)據(jù)變化到視圖變化:nextTick,

十五、keep-alive內(nèi)置組件

vue中支持組件化,并且也有用于緩存的內(nèi)置組件keep-alive可直接使用,使用場景為路由組件動(dòng)態(tài)組件。

  • activated表示進(jìn)入組件的生命周期,deactivated表示離開組件的生命周期
  • include表示匹配到的才緩存,exclude表示匹配到的都不緩存
  • max表示最多可以緩存多少組件

keep-alive的具體實(shí)現(xiàn)請(qǐng)參考?vue中的keep-alive(源碼分析)

十六、生命周期

vue中的生命周期有哪些?
答案:11個(gè),分別為beforeCreate、created、beforeMountmounted、beforeUpdate、updatedactivated、deactivatedbeforeDestroy、destroyederrorCaptured

具體實(shí)現(xiàn)請(qǐng)參考?vue生命周期

十七、v-show和v-if的區(qū)別

先看v-ifv-show的使用場景:

(1)v-if更多的使用在需要考慮白屏?xí)r間或者切換次數(shù)很少的場景
(2)v-show更多使用在transition控制的動(dòng)畫或者需要非常頻繁地切換的場景

再從底層實(shí)現(xiàn)思路上分析:

(1)v-if條件為false時(shí),會(huì)生成空的占位注釋節(jié)點(diǎn),那么在考慮首頁白屏?xí)r間時(shí),選用v-if比較合適。條件從false變化為true的話會(huì)從空的注釋節(jié)點(diǎn)變成真實(shí)節(jié)點(diǎn),條件再變?yōu)?code>false時(shí)真實(shí)節(jié)點(diǎn)又會(huì)變成注釋節(jié)點(diǎn),如果切換次數(shù)比較多,那么開銷會(huì)比較大,頻繁切換場景不建議使用v-if。
(2)v-show條件為false時(shí),會(huì)生成真實(shí)的節(jié)點(diǎn),只是為當(dāng)前節(jié)點(diǎn)增加了display:none來控制其隱藏,相比v-if生成空的注釋節(jié)點(diǎn)其首次渲染開銷是比較大的,所以不建議用在考慮首屏白屏?xí)r間的場景。如果我們頻繁切換v-show的值,從display:nonedisplay:block之間的切換比起空的注釋節(jié)點(diǎn)和真實(shí)節(jié)點(diǎn)的開銷要小很多,這種場景就建議使用v-show。

可以通過vue中v-if和v-show的區(qū)別(源碼分析)了解v-ifv-show詳細(xì)過程。

十八、v-for中key的作用

v-for進(jìn)行循環(huán)展示過程中,當(dāng)數(shù)據(jù)發(fā)生變化進(jìn)行渲染的過程中,會(huì)進(jìn)行新舊節(jié)點(diǎn)列表的比對(duì)。首先新舊vnode列表首先通過首首、尾尾、首尾尾首的方式進(jìn)行比對(duì),如果key相同則采取原地復(fù)用的策略進(jìn)行節(jié)點(diǎn)的移動(dòng)。

如果首尾兩兩比對(duì)的方式找不到對(duì)應(yīng)關(guān)系,繼續(xù)通過keyvnode的對(duì)應(yīng)關(guān)系進(jìn)行尋找。

如果keyvnode對(duì)應(yīng)關(guān)系中找不到,繼續(xù)通過sameVnode的方式在未比對(duì)的節(jié)點(diǎn)中進(jìn)行尋找。

如果都找不到,則將其按照新vnode進(jìn)行createElm的方式進(jìn)行創(chuàng)建,這種方式是比節(jié)點(diǎn)移動(dòng)的方式計(jì)算量更大。

最后將舊的vnode列表中沒有進(jìn)行匹配的vnode中的vnode.elm在父節(jié)點(diǎn)中移除。

簡單總結(jié)就是,新的vnode列表在舊的vnode列表中去尋找具有相同的key的節(jié)點(diǎn)進(jìn)行原地復(fù)用,如果找不到則通過創(chuàng)建的方式createElm去創(chuàng)建一個(gè),如果舊的vnode列表中沒有進(jìn)行匹配則在父節(jié)點(diǎn)中移除其vnode.elm。這就是原地復(fù)用邏輯的大體實(shí)現(xiàn)。

具體keydiff算法的關(guān)系可以參考vue2從數(shù)據(jù)變化到視圖變化:diff算法圖解

十九、v-for和v-if能同時(shí)使用嗎

答案是:用了也能出來預(yù)期的效果,但是會(huì)有性能浪費(fèi)。

同時(shí)包含v-forv-iftemplate模板在編輯階段會(huì)執(zhí)行v-forv-if優(yōu)先級(jí)更高的編譯流程;在生成vnode的階段,會(huì)包含屬性isCommenttrue的空白占位vnode;在patch階段,會(huì)生成真實(shí)的占位節(jié)點(diǎn)。雖然一個(gè)空的占位節(jié)點(diǎn)無妨,但是如果數(shù)據(jù)量比較大的話,也是一個(gè)性能問題。

當(dāng)然,可以在獲取到數(shù)據(jù)(一般是在beforeCreate或者created階段)時(shí)進(jìn)行過濾處理,也可以通過計(jì)算屬性對(duì)其進(jìn)行處理。

可以通過v-for和v-if可以一起使用嗎?了解v-forv-if的詳細(xì)過程。

二十、vue中的data為什么是函數(shù)

答案是:是不是一定是函數(shù),得看場景。并且,也無需擔(dān)心什么時(shí)候該將data寫為函數(shù)還是對(duì)象,因?yàn)?code>vue內(nèi)部已經(jīng)做了處理,并在控制臺(tái)輸出錯(cuò)誤信息。

場景一new Vue({data: ...})
這種場景主要為項(xiàng)目入口或者多個(gè)html頁面各實(shí)例化一個(gè)Vue時(shí),這里的data即可用對(duì)象的形式,也可用工廠函數(shù)返回對(duì)象的形式。因?yàn)?,這里的data只會(huì)出現(xiàn)一次,不存在重復(fù)引用而引起的數(shù)據(jù)污染問題。

場景二:組件場景中的選項(xiàng)
在生成組件vnode的過程中,組件會(huì)在生成構(gòu)造函數(shù)的過程中執(zhí)行合并策略:

// data合并策略
strats.data = function (
  parentVal,
  childVal,
  vm
) {
  if (!vm) {
    if (childVal && typeof childVal !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'The "data" option should be a function ' +
        'that returns a per-instance value in component ' +
        'definitions.',
        vm
      );

      return parentVal
    }
    return mergeDataOrFn(parentVal, childVal)
  }

  return mergeDataOrFn(parentVal, childVal, vm)
};

如果合并過程中發(fā)現(xiàn)子組件的數(shù)據(jù)不是函數(shù),即typeof childVal !== 'function'成立,進(jìn)而在開發(fā)環(huán)境會(huì)在控制臺(tái)輸出警告并且直接返回parentVal,說明這里壓根就沒有把childVal中的任何data信息合并到options中去。

可以通過vue中的data為什么是函數(shù)?了解詳細(xì)過程。

二十一、this.$watch

使用場景:用來監(jiān)聽數(shù)據(jù)的變化,當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候,可以做一些業(yè)務(wù)邏輯的處理。

配置參數(shù):

  • deep:監(jiān)聽數(shù)據(jù)的深層變化
  • immediate:立即觸發(fā)回調(diào)函數(shù)

實(shí)現(xiàn)思路:?Vue構(gòu)造函數(shù)定義完成以后,在執(zhí)行stateMixin(Vue)時(shí)為Vue.prototype上定義$watch。該方法通過const watcher = new Watcher(vm, expOrFn, cb, options)進(jìn)行Watcher的實(shí)例化,將options中的user屬性設(shè)置為true。并且,$watch邏輯結(jié)束的會(huì)返回函數(shù)function unwatchFn () { watcher.teardown() },用來取消偵聽的函數(shù)。

可以通過watch選項(xiàng)和$watch方法的區(qū)別vue中的watch和$watch監(jiān)聽的事件,執(zhí)行幾次?來了解詳細(xì)過程。

二十二、計(jì)算屬性和偵聽屬性的區(qū)別

相同點(diǎn):?兩者都是Watcher實(shí)例化過程中的產(chǎn)物

計(jì)算屬性:

  • 使用場景:模板內(nèi)的表達(dá)式主要用于簡單運(yùn)算,對(duì)于復(fù)雜的計(jì)算邏輯可以用計(jì)算屬性
  • 計(jì)算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的,當(dāng)依賴的數(shù)據(jù)未發(fā)生變化時(shí),多次調(diào)用無需重復(fù)執(zhí)行函數(shù)
  • 計(jì)算屬性計(jì)算結(jié)果依賴于data中的值
  • 同步操作,不支持異步

偵聽屬性:

  • 使用場景:當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開銷較大的操作時(shí),可以用偵聽屬性
  • 可配置參數(shù):可以通過配置immediatedeep來控制立即執(zhí)行和深度監(jiān)聽的行為
  • 偵聽屬性偵聽的是data中定義的

計(jì)算屬性請(qǐng)參考vue2從數(shù)據(jù)變化到視圖變化:計(jì)算屬性
偵聽屬性請(qǐng)參考vue2從數(shù)據(jù)變化到視圖變化:偵聽器

二十三、v-model

// main.js
new Vue({
  el: "#app",
  data() {
    return {
      msg: ""
    };
  },
  template: `<div>
    <input v-model="msg" placeholder="edit me">
    <p>msg is: {{ msg }}</p>
  </div>`
});

普通input:input中的v-model,最終通過target.addEventListener處理成在節(jié)點(diǎn)上監(jiān)聽input事件function($event){msg=$event.target.value}}的形式,當(dāng)input值變化時(shí)msg也跟著改變。

// main.js
const inputBox = {
  template: `<input @input="$emit('input', $event.target.value)">`,
};

new Vue({
  el: "#app",
  template: `<div>
    <input-box v-model="msg"></input-box>
    <p>{{msg}}</p>
  </div>`,
  components: {
    inputBox
  },
  data() {
    return {
      msg: 'hello world!'
    };
  },
});

組件v-model在組件中則通過給點(diǎn)擊事件綁定原生事件,當(dāng)觸發(fā)到$emit的時(shí)候,再進(jìn)行回調(diào)函數(shù)?unction input($$v) {msg=$$v}的執(zhí)行,進(jìn)而達(dá)到子組件修改父組件中數(shù)據(jù)msg的目的。

二十四、v-slot

v-slot產(chǎn)生的主要目的是,在組件的使用過程中可以讓父組件有修改子組件內(nèi)容的能力,就像在子組件里面放了個(gè)插槽,讓父組件往插槽內(nèi)塞入父組件中的楔子;并且,父組件在子組件中嵌入的楔子也可以訪問子組件中的數(shù)據(jù)。v-slot的產(chǎn)生讓組件的應(yīng)用更加靈活。

1、具名插槽

let baseLayout = {
  template: `<div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>`,
  data() {
    return {
      url: ""
    };
  }
};

new Vue({
  el: "#app",
  template: `<base-layout>
    <template v-slot:header>
      <h1>title-txt</h1>
    </template>
    <p>paragraph-1-txt</p>
    <p>paragraph-2-txt</p>
    <template v-slot:footer>
      <p>foot-txt</p>
    </template>
  </base-layout>`,
  components: {
    baseLayout
  }
});

引入的組件baseLayout中的template被添加了屬性v-slot:headerv-slot:footer,子組件中定義了對(duì)應(yīng)的插槽被添加了屬性name="header"name="footer",未被進(jìn)行插槽標(biāo)識(shí)的內(nèi)容被插入到了匿名的<slot></slot>中。

2、作用域插槽

let currentUser = {
  template: `<span>
    <slot name="user" v-bind:userData="childData">{{childData.firstName}}</slot>
  </span>`,
  data() {
    return {
      childData: {
        firstName: "first",
        lastName: "last"
      }
    };
  }
};

new Vue({
  el: "#app",
  template: `<current-user>
    <template v-slot:user="slotProps">{{slotProps.userData.lastName}}</template>
  </current-user>`,
  components: {
    currentUser
  }
});

當(dāng)前例子中作用域插槽通過v-bind:userData="childData"的方式,將childData作為參數(shù),父組件中通過v-slot:user="slotProps"的方式進(jìn)行接收,為父組件使用子組件中的數(shù)據(jù)提供了可能。

v-slot的底層實(shí)現(xiàn)請(qǐng)參考vue中的v-slot(源碼分析)

二十五、Vue.filters

filters類似于管道流可以將上一個(gè)過濾函數(shù)的結(jié)果作為下一個(gè)過濾函數(shù)的第一個(gè)參數(shù),又可以在其中傳遞參數(shù)讓過濾器更靈活。

// main.js文件
import Vue from "vue";

Vue.filter("filterEmpty", function(val) {
  return val || "";
});

Vue.filter("filterA", function(val) {
  return val + "平時(shí)周末的";
});

Vue.filter("filterB", function(val, info, fn) {
  return val + info + fn;
});

new Vue({
  el: "#app",
  template: `<div>{{msg | filterEmpty | filterA | filterB('愛好是', transformHobby('chess'))}}</div>`,
  data() {
    return {
      msg: "張三"
    };
  },
  methods: {
    transformHobby(type) {
      const map = {
        bike: "騎行",
        chess: "象棋",
        game: "游戲",
        swimming: "游泳"
      };
      return map[type] || "未知";
    }
  }
});

其中我們對(duì)msg通過filterEmptyfilterAfilterB('愛好是', transformHobby('chess'))}進(jìn)行三層過濾。

Vue.filters的底層實(shí)現(xiàn)請(qǐng)查看vue中的filters(源碼分析)

二十六、Vue.use

  • 作用:Vue.use被用來安裝Vue.js插件,例如vue-router、vuexelement-ui。
  • install方法:如果插件是一個(gè)對(duì)象,必須提供?install?方法。如果插件是一個(gè)函數(shù),它會(huì)被作為install方法。install方法調(diào)用時(shí),會(huì)將Vue作為參數(shù)傳入。
  • 調(diào)用時(shí)機(jī):該方法需要在調(diào)用?new Vue()?之前被調(diào)用。
  • 特點(diǎn):當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會(huì)被安裝一次。

二十七、Vue.extend和選項(xiàng)extends

1、Vue.extend

Vue.extend使用基礎(chǔ)Vue構(gòu)造器創(chuàng)建一個(gè)“子類”,參數(shù)是一個(gè)包含組件選項(xiàng)的對(duì)象,實(shí)例化的過程中可以修改其中的選項(xiàng),為實(shí)現(xiàn)功能的繼承提供了思路。

new Vue({
  el: "#app",
  template: `<div><div id="person1"></div><div id="person2"></div></div>`,
  mounted() {
    // 定義子類構(gòu)造函數(shù)
    var Profile = Vue.extend({
      template: '<p @click="showInfo">{{name}} 喜歡 {{fruit}}</p>',
      data: function () {
        return {
          name: '張三',
          fruit: '蘋果'
        }
      },
      methods: {
        showInfo() {
          console.log(`${this.name}喜歡${this.fruit}`)
        }
      }
    })
    // 實(shí)例化1,掛載到`#person1`上
    new Profile().$mount('#person1')
    // 實(shí)例化2,并修改其`data`選項(xiàng),掛載到`#person2`上
    new Profile({
      data: function () {
        return {
          name: '李四',
          fruit: '香蕉'
        }
      },
    }).$mount('#person2')
  },
});

在當(dāng)前例子中,通過Vue.extend構(gòu)建了子類構(gòu)造函數(shù)Profile,可以通過new Profile的方式實(shí)例化無數(shù)個(gè)vm實(shí)例。我們定義初始的template、datamethodsvm進(jìn)行使用,如果有變化,在實(shí)例的過程中傳入新的選項(xiàng)參數(shù)即可,比如例子中實(shí)例化第二個(gè)vm的時(shí)候就對(duì)data進(jìn)行了調(diào)整。

2、選項(xiàng)extends

extends允許聲明擴(kuò)展另一個(gè)組件 (可以是一個(gè)簡單的選項(xiàng)對(duì)象或構(gòu)造函數(shù)),而無需使用?Vue.extend。這主要是為了便于擴(kuò)展單文件組件,以實(shí)現(xiàn)組件繼承的目的。

const common = {
  template: `<div>{{name}}</div>`,
  data() {
    return {
      name: '表單'
    }
  }
}

const create = {
  extends: common,
  data() {
    return {
      name: '新增表單'
    }
  }
}

const edit = {
  extends: common,
  data() {
    return {
      name: '編輯表單'
    }
  }
}

new Vue({
  el: "#app",
  template: `<div>
    <create></create>
    <edit></edit>
  </div>`,
  components: {
    create,
    edit,
  }
});

當(dāng)前極簡demo中定義了公共的表單common,然后又在新增表單組件create和編輯表單組件edit中擴(kuò)展了common。

二十八、Vue.mixin和選項(xiàng)mixins

全局混入和局部混入視情況而定,主要區(qū)別在全局混入是通過Vue.mixin的方式將選項(xiàng)混入到了Vue.options中,在所有獲取子組件構(gòu)建函數(shù)的時(shí)候都將其進(jìn)行了合并,是一種影響全部組件的混入策略。

而局部混入是將選項(xiàng)通過配置mixins選項(xiàng)的方式合并到當(dāng)前的子組件中,只有配置了mixins選項(xiàng)的組件才會(huì)受到混入影響,是一種局部的混入策略。

二十九、Vue.directive和directives

1、使用場景

主要用于對(duì)于DOM的操作,比如:文本框聚焦,節(jié)點(diǎn)位置控制、防抖節(jié)流、權(quán)限管理、復(fù)制操作等功能

2、鉤子函數(shù)

  • bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。
  • inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
  • update:所在組件的 VNode 更新時(shí)調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新。
  • componentUpdated:指令所在組件的 VNode?及其子 VNode?全部更新后調(diào)用。
  • unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用。

3、鉤子函數(shù)參數(shù)

  • el:指令所綁定的元素,可以用來直接操作 DOM。
  • binding:一個(gè)對(duì)象,包含以下 property:
    • name:指令名,不包括?v-?前綴。
    • value:指令的綁定值,例如:v-my-directive="1 + 1"?中,綁定值為?2。
    • oldValue:指令綁定的前一個(gè)值,僅在?update?和?componentUpdated?鉤子中可用。無論值是否改變都可用。
    • expression:字符串形式的指令表達(dá)式。例如?v-my-directive="1 + 1"?中,表達(dá)式為?"1 + 1"
    • arg:傳給指令的參數(shù),可選。例如?v-my-directive:foo?中,參數(shù)為?"foo"
    • modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar?中,修飾符對(duì)象為?{ foo: true, bar: true }。
  • vnode:Vue 編譯生成的虛擬節(jié)點(diǎn)。
  • oldVnode:上一個(gè)虛擬節(jié)點(diǎn),僅在?update?和?componentUpdated?鉤子中可用。

4、動(dòng)態(tài)指令參數(shù)

指令的參數(shù)可以是動(dòng)態(tài)的。例如,在?v-mydirective:[argument]="value"?中,argument?參數(shù)可以根據(jù)組件實(shí)例數(shù)據(jù)進(jìn)行更新!這使得自定義指令可以在應(yīng)用中被靈活使用。

三十、vue中的原生事件

vue中可以通過@或者v-on的方式綁定事件,也可為其添加修飾符。

new Vue({
  el: '#app',
  template: `<div @click='divClick'><a @clickt='aClick' href=''>點(diǎn)擊</a></div>`,
  methods: {
    divClick() {
      console.log('divClick')
    },
    aClick() {
      console.log('aClick')
    },
  }
})

以上例子如果點(diǎn)擊a會(huì)觸發(fā)其默認(rèn)行為,如果href不為空還會(huì)進(jìn)行跳轉(zhuǎn)。除此之外,點(diǎn)擊還會(huì)繼續(xù)觸發(fā)div上綁定的點(diǎn)擊事件。

如果通過@click.stop.prevent='aClick'的方式為a標(biāo)簽的點(diǎn)擊事件添加修飾符stopprevent,那么就不會(huì)觸發(fā)其a的默認(rèn)行為,即使href不為空也不會(huì)進(jìn)行跳轉(zhuǎn),同時(shí),div上的點(diǎn)擊事件也不會(huì)進(jìn)行觸發(fā)。

模板的渲染一般分為編譯生成render函數(shù)、render函數(shù)執(zhí)行生成vNodepatch進(jìn)行渲染。下面按照這步驟進(jìn)行簡單分析。

1、render

通過編譯生成的render函數(shù):

with(this) {
    return _c('div', {
        on: {
            "click": divClick
        }
    }, [_c('a', {
        attrs: {
            "href": "http://www.baidu.com"
        },
        on: {
            "click": function ($event) {
                $event.stopPropagation();
                $event.preventDefault();
                return aClick($event)
            }
        }
    }, [_v("點(diǎn)擊")])])
}

其中divon作為div事件描述。a標(biāo)簽的attrs作為屬性描述,on作為事件描述,在描述中.stop被編譯成了$event.stopPropagation()來阻止事件冒泡,.prevent被編譯成了$event.preventDefault()用來阻止a標(biāo)簽的默認(rèn)行為。

2、vNode

通過執(zhí)行Vue.prototype._renderrender函數(shù)轉(zhuǎn)換成vNode。

3、patch

patch的過程中,當(dāng)完成$el節(jié)點(diǎn)的渲染后會(huì)執(zhí)行invokeCreateHooks(vnode, insertedVnodeQueue)邏輯,其中,針對(duì)attrs會(huì)將其設(shè)置為$el的真實(shí)屬性,當(dāng)前例子中會(huì)為a標(biāo)簽設(shè)置herf屬性。針對(duì)on會(huì)通過target.addEventListener的方式將其處理過的事件綁定到$el上,當(dāng)前例子中會(huì)分別對(duì)diva中的click進(jìn)行處理,再通過addEventListener的方式進(jìn)行綁定。

小結(jié)

vue中的事件,從編譯生成render再通過Vue.prototype._render函數(shù)執(zhí)行render到生成vNode,主要是通過on作為描述。在patch渲染階段,將on描述的事件進(jìn)行處理再通過addEventListener的方式綁定到$el上。

三十一、常用修飾符

1、表單修飾符

(1).lazy

在默認(rèn)情況下,v-model?在每次?input?事件觸發(fā)后將輸入框的值與數(shù)據(jù)進(jìn)行同步 ,可以添加?lazy?修飾符,從而轉(zhuǎn)為在?change?事件之后進(jìn)行同步:

<input v-model.lazy="msg">
(2).number

如果想自動(dòng)將用戶的輸入值轉(zhuǎn)為數(shù)值類型,可以給?v-model?添加?number?修飾符:

<input v-model.number="age" type="number">
(3).trim

如果要自動(dòng)過濾用戶輸入的首尾空白字符,可以給?v-model?添加?trim?修飾符:

<input v-model.trim="msg">

2、事件修飾符

(1).stop

阻止單擊事件繼續(xù)傳播。

<!--這里只會(huì)觸發(fā)a-->
<div @click="divClick"><a v-on:click.stop="aClick">點(diǎn)擊</a></div>
(2).prevent

阻止標(biāo)簽的默認(rèn)行為。

<a  v-on:click.prevent="aClick">點(diǎn)擊</a>
(3).capture

事件先在有.capture修飾符的節(jié)點(diǎn)上觸發(fā),然后在其包裹的內(nèi)部節(jié)點(diǎn)中觸發(fā)。

<!--這里先執(zhí)行divClick事件,然后再執(zhí)行aClick事件-->
<div @click="divClick"><a v-on:click="aClick">點(diǎn)擊</a></div>
(4).self

只當(dāng)在 event.target 是當(dāng)前元素自身時(shí)觸發(fā)處理函數(shù),即事件不是從內(nèi)部元素觸發(fā)的。

<!--在a標(biāo)簽上點(diǎn)擊時(shí)只會(huì)觸發(fā)aClick事件,只有點(diǎn)擊phrase的時(shí)候才會(huì)觸發(fā)divClick事件-->
<div @click.self="divClick">phrase<a v-on:click="aClick">點(diǎn)擊</a></div>
(5).once

不像其它只能對(duì)原生的 DOM 事件起作用的修飾符,.once?修飾符還能被用到自定義的組件事件上,表示當(dāng)前事件只觸發(fā)一次。

<a v-on:click.once="aClick">點(diǎn)擊</a>
(6).passive

.passive?修飾符尤其能夠提升移動(dòng)端的性能

<!-- 滾動(dòng)事件的默認(rèn)行為 (即滾動(dòng)行為) 將會(huì)立即觸發(fā) -->  
<!-- 而不會(huì)等待 `onScroll` 完成 -->  
<!-- 這其中包含 `event.preventDefault()` 的情況 -->  
<div v-on:scroll.passive="onScroll">...</div>

3、其他修飾符

除了表單和事件的修飾符,Vue還提供了很多其他修飾符,在使用的時(shí)候可以查閱文檔。

小結(jié)

Vue中提供了很多好用的功能和api,那么修飾符的出現(xiàn)就為功能和api提供了更為豐富的擴(kuò)展屬性和更大的靈活度。

三十二、vue-router

vue路由是單頁面中視圖切換的方案,有三種mode:

  • hash,#后的僅僅作為參數(shù),不屬于url部分
  • history,路徑作為請(qǐng)求url請(qǐng)求資源鏈接,如果找不到會(huì)出現(xiàn)404錯(cuò)誤
  • abstract,服務(wù)端渲染場景 hash場景下,會(huì)出現(xiàn)url鏈接,再修改其view-router中對(duì)應(yīng)的值。

了解vue-router的底層實(shí)現(xiàn)請(qǐng)參考vue2視圖切換:vue-router

三十三、vuex

vuex是狀態(tài)管理倉庫,一般使用的場景為:多個(gè)視圖依賴于同一狀態(tài),來自不同視圖的行為需要變更同一狀態(tài)。其管理的狀態(tài)是響應(yīng)式的,修改也只能顯式提交mutation的方式修改。vuexstate、getter、mutation、actionmodule五個(gè)核心,并且通過module實(shí)現(xiàn)了vuex樹的管理。

了解vuex的底層實(shí)現(xiàn)請(qǐng)參考vue2狀態(tài)管理:vuex

三十四、eventBus

使用場景:兄弟組件傳參

const eventBus = new Vue();

const A = {
  template: `<div @click="send">component-a</div>`,
  methods: {
    send() {
      eventBus.$emit('sendData', 'data from A')
    }
  },
}

const B = {
  template: `<div>component-b</div>`,
  created() {
    eventBus.$on('sendData', (args) => {
      console.log(args)
    })
  },
}

new Vue({
  el: '#app',
  components: {
    A,
    B,
  },
  template: `<div><A></A><B></B></div>`,
})

在當(dāng)前例子中,A組件和B組件稱為兄弟組件,A組件通過事件總線eventBus中的$emit分發(fā)事件,B組件則通過$on來監(jiān)聽事件。

實(shí)現(xiàn)原理:eventsMixin

export function eventsMixin (Vue: Class<Component>) {
  const hookRE = /^hook:/
  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
    const vm: Component = this
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$on(event[i], fn)
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn)
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      if (hookRE.test(event)) {
        vm._hasHookEvent = true
      }
    }
    return vm
  }

  Vue.prototype.$once = function (event: string, fn: Function): Component {
    const vm: Component = this
    function on () {
      vm.$off(event, on)
      fn.apply(vm, arguments)
    }
    on.fn = fn
    vm.$on(event, on)
    return vm
  }

  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
    const vm: Component = this
    // all
    if (!arguments.length) {
      vm._events = Object.create(null)
      return vm
    }
    // array of events
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$off(event[i], fn)
      }
      return vm
    }
    // specific event
    const cbs = vm._events[event]
    if (!cbs) {
      return vm
    }
    if (!fn) {
      vm._events[event] = null
      return vm
    }
    // specific handler
    let cb
    let i = cbs.length
    while (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1)
        break
      }
    }
    return vm
  }

  Vue.prototype.$emit = function (event: string): Component {
    const vm: Component = this
    if (process.env.NODE_ENV !== 'production') {
      const lowerCaseEvent = event.toLowerCase()
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          `Event "${lowerCaseEvent}" is emitted in component ` +
          `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
          `Note that HTML attributes are case-insensitive and you cannot use ` +
          `v-on to listen to camelCase events when using in-DOM templates. ` +
          `You should probably use "${hyphenate(event)}" instead of "${event}".`
        )
      }
    }
    let cbs = vm._events[event]
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      const info = `event handler for "${event}"`
      for (let i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
      }
    }
    return vm
  }
}

Vue構(gòu)造函數(shù)定義完執(zhí)行的eventsMixin函數(shù)中,在Vue.prototype上分別定義了$on、$emit、$off$once的方法易實(shí)現(xiàn)對(duì)事件的綁定、分發(fā)、取消和只執(zhí)行一次的方法。eventBus就是利用了當(dāng)new Vue實(shí)例化后實(shí)例上的$on、$emit$off$once進(jìn)行數(shù)據(jù)傳遞。

三十五、ref

使用場景:?父組件獲取子組件數(shù)據(jù)或者執(zhí)行子組件方法

const A = {
  template: `<div>{{childData.age}}</div>`,
  data() {
    return {
      childData: {
        name: 'qb',
        age: 30
      },
    }
  },
  methods: {
    increaseAge() {
      this.childData.age++;
    }
  }
}

new Vue({
  el: '#app',
  components: {
    A,
  },
  template: `<A ref='childRef' @click.native='changeChildData'></A>`,
  methods: {
    changeChildData() {
      // 執(zhí)行子組件的方法
      this.$refs.childRef.increaseAge()
      // 獲取子組件的數(shù)據(jù)
      console.log(this.$refs.childRef.childData);
    },
  }
})

在當(dāng)前例子中,通過ref='childRef'的方式在當(dāng)前組件中定義一個(gè)ref,可以通過this.$refs.childRef的方式獲取到子組件A。可以通過this.$refs.childRef.increaseAge()的方式執(zhí)行子組件中age增加的方法,也可以通過this.$refs.childRef.childData的方式獲取到子組件中的數(shù)據(jù)。

三十六、props

使用場景:?父子傳參

const A = {
  template: `<div @click='emitData'>{{childData}}</div>`,
  props: ['childData'],
  methods: {
    emitData() {
      this.$emit('emitChildData', 'data from child')
    }
  },
}

new Vue({
  el: '#app',
  components: {
    A
  },
  template: `<A :childData='parentData' @emitChildData='getChildData'></A>`,
  data() {
    return {
      parentData: 'data from parent'
    }
  },
  methods: {
    getChildData(v) {
      console.log(v);
    }
  }
})

從當(dāng)前例子中可以看出,數(shù)據(jù)父傳子是通過:childData='parentData'的方式,數(shù)據(jù)子傳父是通過this.$emit('emitChildData', 'data from child')的方式,然后,父組件通過@emitChildData='getChildData'的方式進(jìn)行獲取。

1、父組件render函數(shù)

new Vue中傳入的模板template經(jīng)過遍歷生成的render函數(shù)如下:

with(this) {
    return _c('A', {
        attrs: {
            "childData": parentData
        },
        on: {
            "emitChildData": getChildData
        }
    })
}

其中data部分有attrson來描述屬性和方法。

在通過createComponent創(chuàng)建組件vnode的過程中,會(huì)通過const propsData = extractPropsFromVNodeData(data, Ctor, tag)的方式獲取props,通過const listeners = data.on的方式獲取listeners,最后將其作為參數(shù)通過new VNode(options)的方式實(shí)例化組件vnode。

2、子組件渲染

在通過const child = vnode.componentInstance = createComponentInstanceForVnode( vnode, activeInstance )創(chuàng)建組件實(shí)例的過程中,會(huì)執(zhí)行到組件繼承自Vue._init方法,通過initEvents將事件處理后存儲(chǔ)到vm._events中,通過initPropschildData賦值到子組件Avm實(shí)例上,并進(jìn)行響應(yīng)式處理,讓其可以通過vm.childData的方式訪問,并且數(shù)據(jù)發(fā)生變化時(shí)視圖也可以發(fā)生改變。

組件模板編譯后對(duì)應(yīng)的render函數(shù)是:

with(this) {
    return _c('div', {
        on: {
            "click": emitData
        }
    }, [_v(_s(childData))])
}

createElm完成節(jié)點(diǎn)的創(chuàng)建后,在invokeCreateHooks(vnode, insertedVnodeQueue)階段,給DOM原生節(jié)點(diǎn)節(jié)點(diǎn)綁定emitData

3、this.$emit

在點(diǎn)擊執(zhí)行this.$emit時(shí),會(huì)通過var cbs = vm._events[event]取出_events中的事件進(jìn)行執(zhí)行。

至此,父組件中的傳遞的數(shù)據(jù)就在子組件中可以通過this.xxx的方式獲得,也可以通過this.$emit的方式將子組件中的數(shù)據(jù)傳遞給父組件。

prop數(shù)據(jù)發(fā)生改變引起視圖變化的底層邏輯請(qǐng)參考vue2從數(shù)據(jù)變化到視圖變化:props引起視圖變化詳解

三十七、$attrs和$listeners

使用場景:?父子組件非props屬性和非native方法傳遞

// main.js文件
import Vue from "vue";

const B = {
  template: `<div @click="emitData">{{ formParentData }}</div>`,
  data() {
    return {
      formParentData: ''
    }
  },
  inheritAttrs: false,

  created() {
    this.formParentData = this.$attrs;
    console.log(this.$attrs, '--------------a-component-$attrs')
    console.log(this.$listeners, '--------------b-component-$listeners')
  },
  methods: {
    emitData() {
      this.$emit('onFun', 'form B component')
    }
  },
}

const A = {
  template: `<B v-bind='$attrs' v-on='$listeners'></B>`,
  components: {
    B,
  },
  props: ['propData'],
  inheritAttrs: false,
  created() {
    console.log(this.$attrs, '--------------b-component-$attrs')
    console.log(this.$listeners, '--------------b-component-$listeners')
  }
}

new Vue({
  el: '#app',
  components: {
    A,
  },
  template: `<A :attrData='parentData' :propData='parentData' @click.native="nativeFun" @onFun="onFun"></A>`,
  data() {
    return {
      parentData: 'msg'
    }
  },
  methods: {
    nativeFun() {
      console.log('方法A');
    },
    onFun(v) {
      console.log('方法B', v);
    },
  }
})

當(dāng)前例子中,new Vuetemplate模板中有attrData、propData、click.nativeonFun在進(jìn)行傳遞。實(shí)際運(yùn)行后,在A組件中this.$attrs{attrData: 'msg'}this.$listeners{onFun:f(...)}。在A組件中通過v-bind='$attrs'v-on='$listeners'的方式繼續(xù)進(jìn)行屬性和方法的傳遞,在B組件中就可以獲取到A組件中傳入的$attrs$listeners。

當(dāng)前例子中完成了非props屬性和非native方法的傳遞,并且通過v-bind='$attrs'v-on='$listeners'的方式實(shí)現(xiàn)了屬性和方法的跨層級(jí)傳遞。

同時(shí)通過this.$emit的方法觸發(fā)了根節(jié)點(diǎn)中onFun事件。

關(guān)于例子中的inheritAttrs: false,默認(rèn)情況下父作用域的不被認(rèn)作propsattribute綁定將會(huì)“回退”且作為普通的HTML屬性應(yīng)用在子組件的根元素上。當(dāng)撰寫包裹一個(gè)目標(biāo)元素或另一個(gè)組件的組件時(shí),這可能不會(huì)總是符合預(yù)期行為。通過設(shè)置inheritAttrsfalse,這些默認(rèn)行為將會(huì)被去掉。

三十八、$parent和$children

使用場景:?利用父子關(guān)系進(jìn)行數(shù)據(jù)的獲取或者方法的調(diào)用

const A = {
  template: `<div @click="changeParentData">{{childRandom}}</div>`,
  data() {
    return {
      childRandom: Math.random()
    }
  },
  mounted() {
    console.log(this.$parent.parentCount, '--child-created--'); // 獲取父組件中的parentCount
  },
  methods: {
    changeParentData() {
      console.log(this.$parent); // 打印當(dāng)前實(shí)例的$parent
      this.$parent.changeParentData(); // 調(diào)用當(dāng)前父級(jí)中的方法`changeParentData`
    },
    changeChildData() {
      this.childRandom = Math.random();
    }
  }
}
const B = {
  template: `<div>b-component</div>`,
}

new Vue({
  el: '#app',
  components: {
    A,
    B,
  },
  template: `<div><A></A><B></B><p>{{parentCount}}</p><button  @click="changeChildrenData">修改子組件數(shù)據(jù)</button></div>`,
  data() {
    return {
      parentCount: 1
    }
  },
  mounted() {
    console.log(this.$children[0].childRandom, '--parent-created--'); // 獲取第一個(gè)子組件中的childRandom
  },
  methods: {
    changeParentData() {
      this.parentCount++;
    },
    changeChildrenData() {
      console.log(this.$children); // 此時(shí)有兩個(gè)子組件
      this.$children[0].changeChildData(); // 調(diào)起第一個(gè)子組件中的'changeChildData'方法
    }
  }
})

在當(dāng)前例子中,父組件可以通過this.$children獲取所有的子組件,這里有A組件和B組件,可以通過this.$children[0].childRandom的方式獲取子組件A中的數(shù)據(jù),也可以通過this.$children[0].changeChildData()的方式調(diào)起子組件A中的方法。

子組件可以通過this.$parent的方式獲取父組件,可以通過this.$parent.parentCount獲取父組件中的數(shù)據(jù),也可以通過this.$parent.changeParentData()的方式修改父組件中的數(shù)據(jù)。

Vue$parent$children父子關(guān)系的底層構(gòu)建請(qǐng)參考雜談:??????/parent/children的底層邏輯

三十九、inject和provide

使用場景:嵌套組件多層級(jí)傳參

const B = {
  template: `<div>{{parentData1}}{{parentData2}}</div>`,
  inject: ['parentData1', 'parentData2'],
}

const A = {
  template: `<B></B>`,
  components: {
    B,
  },
}

new Vue({
  el: '#app',
  components: {
    A,
  },
  template: `<A></A>`,
  provide: {
    parentData1: {
      name: 'name-2',
      age: 30
    },
    parentData2: {
      name: 'name-2',
      age: 29
    },
  }
})

例子中在new Vue的時(shí)候通過provide提供了兩個(gè)數(shù)據(jù)來源parentData1parentData2,然后跨了一個(gè)A組件在B組件中通過inject注入了這兩個(gè)數(shù)據(jù)。

1、initProvide

在執(zhí)行組件內(nèi)部的this._init初始化方法時(shí),會(huì)執(zhí)行到initProvide邏輯:

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

如果在當(dāng)前vm.$options中存在provide,會(huì)將其執(zhí)行結(jié)果賦值給vm._provided

2、initInjections

function initInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, key, result[key], () => {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)
  }
}
function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      const provideKey = inject[key].from
      let source = vm
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}

如果當(dāng)前組件中有選項(xiàng)inject,會(huì)以while循環(huán)的方式不斷在source = source.$parent中尋找_provided,然后獲取到祖先組件中提供的數(shù)據(jù)源,這是實(shí)現(xiàn)祖先組件向所有子孫后代注入依賴的核心。

四十、Vue項(xiàng)目能做的性能優(yōu)化

1、v-ifv-show

  • 頻繁切換時(shí)使用v-show,利用其緩存特性
  • 首屏渲染時(shí)使用v-if,如果為false則不進(jìn)行渲染

2、v-forkey

  • 列表變化時(shí),循環(huán)時(shí)使用唯一不變的key,借助其本地復(fù)用策略
  • 列表只進(jìn)行一次渲染時(shí),key可以采用循環(huán)的index

3、偵聽器和計(jì)算屬性

  • 偵聽器watch用于數(shù)據(jù)變化時(shí)引起其他行為
  • 多使用compouter計(jì)算屬性顧名思義就是新計(jì)算而來的屬性,如果依賴的數(shù)據(jù)未發(fā)生變化,不會(huì)觸發(fā)重新計(jì)算

4、合理使用生命周期

  • destroyed階段進(jìn)行綁定事件或者定時(shí)器的銷毀
  • 使用動(dòng)態(tài)組件的時(shí)候通過keep-alive包裹進(jìn)行緩存處理,相關(guān)的操作可以在actived階段激活

5、數(shù)據(jù)響應(yīng)式處理

  • 不需要響應(yīng)式處理的數(shù)據(jù)可以通過Object.freeze處理,或者直接通過this.xxx = xxx的方式進(jìn)行定義
  • 需要響應(yīng)式處理的屬性可以通過this.$set的方式處理,而不是JSON.parse(JSON.stringify(XXX))的方式

6、路由加載方式

  • 頁面組件可以采用異步加載的方式

7、插件引入

  • 第三方插件可以采用按需加載的方式,比如element-ui。

8、減少代碼量

  • 采用mixin的方式抽離公共方法
  • 抽離公共組件
  • 定義公共方法至公共js
  • 抽離公共css

9、編譯方式

  • 如果線上需要template的編譯,可以采用完成版vue.esm.js
  • 如果線上無需template的編譯,可采用運(yùn)行時(shí)版本vue.runtime.esm.js,相比完整版體積要小大約30%

10、渲染方式

  • 服務(wù)端渲染,如果是需要SEO的網(wǎng)站可以采用服務(wù)端渲染的方式
  • 前端渲染,一些企業(yè)內(nèi)部使用的后端管理系統(tǒng)可以采用前端渲染的方式

11、字體圖標(biāo)的使用

  • 有些圖片圖標(biāo)盡可能使用字體圖標(biāo)

四十一、Vue項(xiàng)目白屏問題

  • 1、開啟gzip壓縮減小文件體積。
  • 2、webpack設(shè)置productionSourceMap:false,不在線上環(huán)境打包.map文件。
  • 3、路由懶加載
  • 4、異步組件的使用
  • 5、靜態(tài)資源使用cdn鏈接引入
  • 6、采用ssr服務(wù)端渲染方案
  • 7、骨架屏或者loading效果填充空白間隙
  • 8、首次不渲染的隱藏采用v-if
  • 9、注重代碼規(guī)范:抽取公共組件,公共js,公共css樣式,減小代碼體積。刪除無用代碼,減少非必要注釋。防止寫出死循環(huán)等等
  • 10、刪除輔助開發(fā)的console.log
  • 11、非Vue角度思考:非重要文件采用異步加載方式、css樣式采用媒體查詢、采用域名分片技術(shù)、http1升級(jí)成http2、如果是SSR項(xiàng)目考慮服務(wù)端渲染有沒有可優(yōu)化的點(diǎn)、請(qǐng)求頭是否帶了多余信息等思路

內(nèi)容有些多,大體可以歸類為從服務(wù)端拿到資源的速度、資源的體積和渲染是否阻塞的角度去作答。

四十二、從0到1構(gòu)建一個(gè)Vue項(xiàng)目需要注意什么

  • 架子:選用合適的初始化腳手架(vue-cli2.0或者vue-cli3.0)
  • 請(qǐng)求:數(shù)據(jù)axios請(qǐng)求的配置
  • 登錄:登錄注冊(cè)系統(tǒng)
  • 路由:路由管理頁面
  • 數(shù)據(jù):vuex全局?jǐn)?shù)據(jù)管理
  • 權(quán)限:權(quán)限管理系統(tǒng)
  • 埋點(diǎn):埋點(diǎn)系統(tǒng)
  • 插件:第三方插件的選取以及引入方式
  • 錯(cuò)誤:錯(cuò)誤頁面
  • 入口:前端資源直接當(dāng)靜態(tài)資源,或者服務(wù)端模板拉取
  • SEO:如果考慮SEO建議采用SSR方案
  • 組件:基礎(chǔ)組件/業(yè)務(wù)組件
  • 樣式:樣式預(yù)處理起,公共樣式抽取
  • 方法:公共方法抽離

四十三、SSR

1、什么是服務(wù)端渲染(SSR)?

Vue.js 是構(gòu)建客戶端應(yīng)用程序的框架。默認(rèn)情況下,可以在瀏覽器中輸出 Vue 組件,進(jìn)行生成 DOM 和操作 DOM。然而,也可以將同一個(gè)組件渲染為服務(wù)器端的 HTML 字符串,將它們直接發(fā)送到瀏覽器,最后將這些靜態(tài)標(biāo)記"激活"為客戶端上完全可交互的應(yīng)用程序。

2、為什么使用服務(wù)端渲染(SSR)?

與傳統(tǒng) SPA (單頁應(yīng)用程序 (Single-Page Application)) 相比,服務(wù)器端渲染 (SSR) 的優(yōu)勢(shì)主要在于:

  • 更好的 SEO,由于搜索引擎爬蟲抓取工具可以直接查看完全渲染的頁面。
  • 更快的內(nèi)容到達(dá)時(shí)間 (time-to-content),特別是對(duì)于緩慢的網(wǎng)絡(luò)情況或運(yùn)行緩慢的設(shè)備。

3、使用服務(wù)器端渲染 (SSR) 時(shí)需要考慮的問題?

使用服務(wù)器端渲染 (SSR) 時(shí)還需要有一些權(quán)衡之處

  • 開發(fā)條件所限。瀏覽器特定的代碼,只能在某些生命周期鉤子函數(shù) (lifecycle hook) 中使用;一些外部擴(kuò)展庫 (external library) 可能需要特殊處理,才能在服務(wù)器渲染應(yīng)用程序中運(yùn)行。
  • 涉及構(gòu)建設(shè)置和部署的更多要求。與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁面應(yīng)用程序 (SPA) 不同,服務(wù)器渲染應(yīng)用程序,需要處于 Node.js server 運(yùn)行環(huán)境。
  • 更多的服務(wù)器端負(fù)載。在 Node.js 中渲染完整的應(yīng)用程序,顯然會(huì)比僅僅提供靜態(tài)文件的 server 更加大量占用 CPU 資源 (CPU-intensive - CPU 密集),因此如果你預(yù)料在高流量環(huán)境 (high traffic) 下使用,請(qǐng)準(zhǔn)備相應(yīng)的服務(wù)器負(fù)載,并明智地采用緩存策略。

四十四、scoped

Vue項(xiàng)目開發(fā)的項(xiàng)目中如果樣式中未使用scoped,組件間的樣式會(huì)出現(xiàn)覆蓋的問題。

反例:

// app.vue文件
<template>
  <div>
    <h3 class="title">app-txt</h3>
    <child></child>
  </div>
</template>

<script>
import child from "@/components/child";
export default {
  components: { child },
};
</script>

<style>
.title {
  color: red;
}
</style>
// child.vue文件
<template>
  <h3 class="title">child-txt</h3>
</template>

<style>
  .title {
    color: green;
  }
</style>

父組件和子組件的樣式顏色都為green,子組件中的樣式覆蓋了父組件的樣式。

正例:

<template>
  <h3 class="title">child-txt</h3>
</template>

<style scoped>
  .title {
    color: green;
  }
</style>

此時(shí),父組件中顏色為red,子組件中顏色為green。

主要原因:

vue核心面試題匯總【查缺補(bǔ)漏】,前端面試題,vue.js,前端,javascript

vue核心面試題匯總【查缺補(bǔ)漏】,前端面試題,vue.js,前端,javascript

?例子中的DOM節(jié)點(diǎn)和CSS層疊樣式中都被添加了data-v-xxx來表示唯一,所以scoped是給當(dāng)前組件的節(jié)點(diǎn)和樣式唯一標(biāo)識(shí)為data-v-xxx,避免了樣式覆蓋。

?給大家推薦一個(gè)實(shí)用面試題庫

1、前端面試題庫 (面試必備) ? ? ? ? ? ?推薦:★★★★★

地址:web前端面試題庫

到了這里,關(guān)于vue核心面試題匯總【查缺補(bǔ)漏】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • CSS查缺補(bǔ)漏之常用長度單位(px、em、rem、%、vw/vh、vmin/vmax)

    CSS查缺補(bǔ)漏之常用長度單位(px、em、rem、%、vw/vh、vmin/vmax)

    此文內(nèi)容較少,輕輕松松掌握,莫要有壓力~ 正如現(xiàn)實(shí)生活中長度具有mm、dm、cm、m等,在css中,也具備多種長度單位,本文對(duì)常用的幾種單位進(jìn)行詳細(xì)舉例介紹~ px:像素單位 初學(xué)css時(shí),px單位經(jīng)常被使用,此處按下不表~ ?em:表示相對(duì)于 當(dāng)前元素 或 父元素 的font-size的倍數(shù)

    2024年02月09日
    瀏覽(20)
  • Web前端阿里等大廠面試題匯總,Web核心技術(shù)之JSP(過時(shí)技術(shù)),前端音頻框架

    Web前端阿里等大廠面試題匯總,Web核心技術(shù)之JSP(過時(shí)技術(shù)),前端音頻框架

    jstl jstl 1.2 taglibs standard 1.1.2 org.apache.tomcat.maven tomcat7-maven-plugin 2.2 8.1.2 創(chuàng)建包 創(chuàng)建不同的包結(jié)構(gòu),用來存儲(chǔ)不同的類。包結(jié)構(gòu)如下 8.1.3 創(chuàng)建表 – 刪除tb_brand表 drop table if exists tb_brand; – 創(chuàng)建tb_brand表 create table tb_brand ( – id 主鍵 id int primary key auto_increment, – 品牌名稱 brand_n

    2024年04月16日
    瀏覽(28)
  • 〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的「數(shù)組」

    〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的「數(shù)組」

    當(dāng)前子專欄 基礎(chǔ)入門三大核心篇 是免費(fèi)開放階段 。 推薦他人訂閱,可獲取扣除平臺(tái)費(fèi)用后的35%收益,文末名片加V! 說明:該文屬于 大前端全棧架構(gòu)白寶書專欄, 目前階段免費(fèi)開放 , 購買任意白寶書體系化專欄可加入 TFS-CLUB 私域社區(qū)。 福利:除了通過訂閱\\\"白寶書系列專

    2024年02月04日
    瀏覽(23)
  • 〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的DOM簡介

    〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的DOM簡介

    說明:該文屬于 大前端全棧架構(gòu)白寶書專欄, 目前階段免費(fèi) , 如需要項(xiàng)目實(shí)戰(zhàn)或者是體系化資源,文末名片加V! 作者:不渴望力量的哈士奇(哈哥),十余年工作經(jīng)驗(yàn), 從事過全棧研發(fā)、產(chǎn)品經(jīng)理等工作,目前在公司擔(dān)任研發(fā)部門CTO。 榮譽(yù): 2022年度博客之星Top4、2023年度超

    2024年02月04日
    瀏覽(25)
  • 常見前端面試之VUE面試題匯總七

    常見前端面試之VUE面試題匯總七

    ? 20. 對(duì) vue 設(shè)計(jì)原則的理解 1.漸進(jìn)式 JavaScript 框架:與其它大型框架不同的是,Vue 被設(shè)計(jì) 為可以自底向上逐層應(yīng)用。Vue 的核心庫只關(guān)注視圖層,不僅易于上 手,還便于與第三方庫或既有項(xiàng)目整合。另一方面,當(dāng)與現(xiàn)代化的工 具鏈以及各種支持類庫結(jié)合使用時(shí),Vue 也完全能

    2024年02月11日
    瀏覽(28)
  • 常見前端面試之VUE面試題匯總六

    常見前端面試之VUE面試題匯總六

    ?17. MVVM 的優(yōu)缺點(diǎn)? 優(yōu)點(diǎn): 分離視圖(View)和模型(Model),降低代碼耦合,提?視圖或者 邏輯的重?性: ?如視圖(View)可以獨(dú)?于 Model 變化和修改,?個(gè) ViewModel 可以綁定不同的\\\"View\\\"上,當(dāng) View 變化的時(shí)候 Model 不可以不變,當(dāng) Model 變化的時(shí)候 View 也可以不變。你可以把?

    2024年02月11日
    瀏覽(34)
  • 〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的數(shù)組的常用方法 (一)

    〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript 的數(shù)組的常用方法 (一)

    當(dāng)前子專欄 基礎(chǔ)入門三大核心篇 是免費(fèi)開放階段 。 推薦他人訂閱,可獲取扣除平臺(tái)費(fèi)用后的35%收益,文末名片加V! 說明:該文屬于 大前端全棧架構(gòu)白寶書專欄, 目前階段免費(fèi)開放 , 購買任意白寶書體系化專欄可加入 TFS-CLUB 私域社區(qū)。 福利:除了通過訂閱\\\"白寶書系列專

    2024年02月07日
    瀏覽(23)
  • 常見前端面試之VUE面試題匯總十三

    常見前端面試之VUE面試題匯總十三

    ?39. Vue 中 key 的作用 vue 中 key 值的作用可以分為兩種情況來考慮: 第一種情況是 v-if 中使用 key。由于 Vue 會(huì)盡可能高效地渲染元 素,通常會(huì)復(fù)用已有元素而不是從頭開始渲染。因此當(dāng)使用 v-if 來 實(shí)現(xiàn)元素切換的時(shí)候,如果切換前后含有相同類型的元素,那么這個(gè) 元素就會(huì)

    2024年02月10日
    瀏覽(20)
  • 常見前端面試之VUE面試題匯總十一

    常見前端面試之VUE面試題匯總十一

    31. Vuex 有哪幾種屬性? 有五種,分別是 State、 Getter、Mutation 、Action、 Module state = 基本數(shù)據(jù)(數(shù)據(jù)源存放地) getters = 從基本數(shù)據(jù)派生出來的數(shù)據(jù) mutations = 提交更改數(shù)據(jù)的方法,同步 actions = 像一個(gè)裝飾器,包裹 mutations,使之可以異步。 modules = 模塊化 Vuex 32. Vuex 和單純的全局對(duì)

    2024年02月10日
    瀏覽(21)
  • 〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript的流程控制語句「while循環(huán)語句」

    〖大前端 - 基礎(chǔ)入門三大核心之JS篇?〗- JavaScript的流程控制語句「while循環(huán)語句」

    當(dāng)前子專欄 基礎(chǔ)入門三大核心篇 是免費(fèi)開放階段 。 推薦他人訂閱,可獲取扣除平臺(tái)費(fèi)用后的35%收益,文末名片加V! 說明:該文屬于 大前端全棧架構(gòu)白寶書專欄, 目前階段免費(fèi)開放 , 購買任意白寶書體系化專欄可加入 TFS-CLUB 私域社區(qū)。 福利:除了通過訂閱\\\"白寶書系列專

    2024年02月04日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包