深入理解Vue官方文檔梳理之全局API
Vue.extend
配置項(xiàng)data必須為function,否則配置無效。data的合并規(guī)則(可以看《Vue官方文檔梳理-全局配置》)源碼如下:
傳入非function類型的data(上圖中data配置為{a:1}),在合并options時(shí),如果data不是function類型,開發(fā)版會(huì)發(fā)出警告,然后直接返回了parentVal,這意味著extend傳入的data選項(xiàng)被無視了。
我們知道實(shí)例化Vue的時(shí)候,data可以是對(duì)象,這里的合并規(guī)則不是通用的嗎?注意上面有個(gè)if(!vm)的判斷,實(shí)例化的時(shí)候vm是有值的,因此不同于Vue.extend,其實(shí)下面的注釋也做了說明(in a Vue.extend merge, both should be function),這也是官方文檔為何說data是個(gè)特例。
另外官方文檔所說的“子類”,是因?yàn)閂ue.extend返回的是一個(gè)“繼承”Vue的函數(shù),源碼結(jié)構(gòu)如下:
Vue.extend = function (extendOptions) { //*** var Super = this; var SuperId = Super.cid; //*** var Sub = function VueComponent(options) { this._init(options); }; Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; //*** return Sub };
Vue.nextTick
既然使用vue,當(dāng)然要沿著數(shù)據(jù)驅(qū)動(dòng)的方式思考,所謂數(shù)據(jù)驅(qū)動(dòng),就是不要直接去操作dom,dom的所有操作完全可以利用vue的各種指令來完成,指令將數(shù)據(jù)和dom進(jìn)行了“綁定”,操作數(shù)據(jù)不僅能實(shí)現(xiàn)dom的更新,而且更方便。
如果瀏覽器支持Promise,或者用了Promise庫(kù)(但是對(duì)外暴露的必須叫Promise,因?yàn)樵创a中的判斷為typeof Promise !== 'undefined'),nextTick返回的就是Promise對(duì)象。
Vue.nextTick().then(() => { // do sth })
Vue執(zhí)行nextTick的回調(diào)采用call的方式cb.call(ctx);ctx就是當(dāng)前Vue實(shí)例,因此在回調(diào)中可以直接使用this調(diào)用實(shí)例的配置。
nextTick可以簡(jiǎn)單理解為將回調(diào)放到末尾執(zhí)行,源碼中如果當(dāng)前不支持Promise和MutationObserver,那么會(huì)采用setTimeout的方式來執(zhí)行回調(diào),這不就是我們常用的延后執(zhí)行代碼的方式。
if (typeof Promise !== 'undefined' && isNative(Promise)) { } else if (typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { } else { // fallback to setTimeout /* istanbul ignore next */ timerFunc = function () { setTimeout(nextTickHandler, 0); }; }
舉個(gè)例子來實(shí)際看下:
<div id="app"> <div ref="dom">{{a}}</div> </div> new Vue({ el: '#app', data: { a: 1 }, mounted: function name(params) { console.log('start'); this.$nextTick(function () { console.log('beforeChange', this.$refs.dom.textContent) }) this.a = 2; console.log('change'); this.$nextTick(function () { console.log('afterChange', this.$refs.dom.textContent) }) console.log('end'); } }) // 控制臺(tái)依次打印 // start // change // end // beforeChange 1 // afterChange 2
你估計(jì)會(huì)有些納悶,既然都是最后才執(zhí)行,那為什么beforeChange輸出的是1而不是2,這是因?yàn)閠his.a=2背后觸發(fā)dom更新也是采用nextTick的方式,上面的代碼實(shí)際執(zhí)行的順序是:beforeChange>更新dom>afterChange。
Vue.set
Vue.set( target, key, value ),target不能是 Vue 實(shí)例,或者 Vue 實(shí)例的根數(shù)據(jù)對(duì)象,因?yàn)樵创a中做了如下判斷:
var ob = (target).__ob__; if (target._isVue || (ob && ob.vmCount)) { "development" !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ); return val }
target._isVue阻止了給Vue實(shí)例添加屬性,ob && ob.vmCount阻止了給Vue實(shí)例的根數(shù)據(jù)對(duì)象添加屬性。
Vue.delete
如果Vue能檢測(cè)到delete操作,那么就不會(huì)出現(xiàn)這個(gè)api。如果一定要用delete來刪除$data的屬性,那就用Vue.delete,否則不會(huì)觸發(fā)dom的更新。
同Vue.set,Vue.delete( target, key )的target不能是一個(gè) Vue 示例或 Vue 示例的根數(shù)據(jù)對(duì)象。源碼中的阻止方式和Vue.set相同。
在2.2.0+ 版本中target若為數(shù)組,key則是數(shù)組下標(biāo)。因?yàn)閂ue.delete刪除數(shù)組實(shí)際是用splice來刪除,delete雖然能用于刪除數(shù)組,但位置還在,不能算真正的刪除。
var a = [1, 2, 3]; delete a[0]; console.log(a); // [undefined, 2, 3]
Vue.use
Vue.use 源碼比較簡(jiǎn)單,可以全部貼出來。
Vue.use = function (plugin) { var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); if (installedPlugins.indexOf(plugin) > -1) { return this } // additional parameters var args = toArray(arguments, 1); args.unshift(this); if (typeof plugin.install === 'function') { plugin.install.apply(plugin, args); } else if (typeof plugin === 'function') { plugin.apply(null, args); } installedPlugins.push(plugin); return this };
安裝的插件放到了 installedPlugins ,安裝插件前通過installedPlugins.indexOf(plugin)來判斷插件是否被安裝過,進(jìn)而阻止注冊(cè)相同插件多次。
插件類型為 object,必須指定 install 屬性來安裝插件(typeof plugin.install === 'function'),另外插件執(zhí)行采用plugin.install.apply(plugin, args);,因此 this 訪問 object 的其他屬性。此處的 args 是由 Vue(args.unshift(this);) 和 Vue.use 傳入的除了 plugin 的其他參數(shù)(toArray(arguments, 1),1 表示從 arguments[1] 開始截?。?。
Vue.use({ a: 1, install: function (Vue) { console.log(this.a) // 1 console.log(arguments) // [function Vue(options),"a", "b", "c"] } }, 'a', 'b', 'c')
插件類型為 function,安裝調(diào)用plugin.apply(null, args);,因此在嚴(yán)格模式下插件運(yùn)行時(shí)上下文 this 為 null,非嚴(yán)格模式為 Window。
'use strict' Vue.use(function plugin() { console.log(this) // null console.log(arguments) // [function Vue(options),"a", "b", "c"] }, 'a', 'b', 'c')
Vue.compile
和眾多 JS 模板引擎的原理一樣,預(yù)先會(huì)把模板轉(zhuǎn)化成一個(gè) render 函數(shù),Vue.compile 就是來完成這個(gè)工作的,目標(biāo)是將模板(template 或 el)轉(zhuǎn)化成 render 函數(shù)。
Vue.compile 返回了{(lán)render:Function,staticRenderFns:Array},render 可直接應(yīng)用于 Vue 的配置項(xiàng) render,而 staticRenderFns 是怎么來的,而且按照官網(wǎng)的例子,Vue 還有個(gè)隱藏的配置項(xiàng) staticRenderFns,先來個(gè)例子看看。
var compiled = Vue.compile( '<div>' + '<header><h1>no data binding</h1></header>' + '<section>{{prop}}</section>' + '</div>' ) console.log(compiled.render.toString()) console.log(compiled.staticRenderFns.toString()) // render function anonymous() { with(this) { return _c('div', [_m(0), _c('section', [_v(_s(prop))])]) } } // staticRenderFns function anonymous() { with(this) { return _c('header', [_c('h1', [_v("no data binding")])]) } }
原來沒有和數(shù)據(jù)綁定的 dom 會(huì)放到 staticRenderFns 中,然后在 render 中以_m(0)來調(diào)用。但是并不盡然,比如上述模板去掉<h1>,staticRenderFns 長(zhǎng)度為 0,header 直接放到了 render 函數(shù)中。
function anonymous() { with(this) { return _c('div', [_c('header', [_v("no data binding")]), _c('section', [_v(_s(prop))])]) } }
Vue.compile 對(duì)應(yīng)的源碼比較復(fù)雜,上述渲染 <header> 沒有放到 staticRenderFns 對(duì)應(yīng)源碼的核心判斷如下:
// For a node to qualify as a static root, it should have children that // are not just static text. Otherwise the cost of hoisting out will // outweigh the benefits and it's better off to just always render it fresh. if (node.static && node.children.length && !( node.children.length === 1 && node.children[0].type === 3 )) { node.staticRoot = true; return } else { node.staticRoot = false; }
<header> 不符判斷條件 !(node.children.length === 1 && node.children[0].type === 3), <header> 有一個(gè)子節(jié)點(diǎn) TextNode(nodeType=3)。 注釋也說明了一個(gè) node 符合靜態(tài)根節(jié)點(diǎn)的條件。
另外官網(wǎng)說明了此方法只在獨(dú)立構(gòu)建時(shí)有效,什么是獨(dú)立構(gòu)建?這個(gè)官網(wǎng)做了詳細(xì)的介紹,不再贅述。對(duì)應(yīng)官網(wǎng)地址:對(duì)不同構(gòu)建版本的解釋。
仔細(xì)觀察編譯后的 render 方法,和我們自己寫的 render 方法有很大區(qū)別。但是仍然可以直接配置到 render 配置選項(xiàng)上。那么里面的那些 _c()、_m() 、_v()、_s() 能調(diào)用?隨便看一個(gè) Vue 的實(shí)例的 __proto__ 就會(huì)發(fā)現(xiàn):
// internal render helpers. // these are exposed on the instance prototype to reduce generated render // code size. Vue.prototype._o = markOnce; Vue.prototype._n = toNumber; Vue.prototype._s = toString; Vue.prototype._l = renderList; Vue.prototype._t = renderSlot; Vue.prototype._q = looseEqual; Vue.prototype._i = looseIndexOf; Vue.prototype._m = renderStatic; Vue.prototype._f = resolveFilter; Vue.prototype._k = checkKeyCodes; Vue.prototype._b = bindObjectProps; Vue.prototype._v = createTextVNode; Vue.prototype._e = createEmptyVNode; Vue.prototype._u = resolveScopedSlots; Vue.prototype._g = bindObjectListeners;
正如注釋所說,這些方法是為了減少生成的 render 函數(shù)的體積。
全局 API 還剩 directive、filter、component、mixin,這幾個(gè)比較類似,而且都對(duì)應(yīng)著配置項(xiàng),會(huì)在「選項(xiàng)」中再詳細(xì)介紹。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Vue項(xiàng)目中引入外部文件的方法(css、js、less)
- vue.js引入外部CSS樣式和外部JS文件的方法
- vue項(xiàng)目中在外部js文件中直接調(diào)用vue實(shí)例的方法比如說this
- vue.js父組件使用外部對(duì)象的方法示例
- webpack+vue-cli項(xiàng)目中引入外部非模塊格式j(luò)s的方法
- vue項(xiàng)目中api接口管理總結(jié)
- 詳解vue項(xiàng)目中調(diào)用百度地圖API使用方法
- vue實(shí)現(xiàn)的請(qǐng)求服務(wù)器端API接口示例
- 詳解Vue2 SSR 緩存 Api 數(shù)據(jù)
- Vue 2.x教程之基礎(chǔ)API
- vue 使用外部JS與調(diào)用原生API操作示例
相關(guān)文章
vue計(jì)算屬性時(shí)v-for處理數(shù)組時(shí)遇到的一個(gè)bug問題
這篇文章主要介紹了在做vue計(jì)算屬性,v-for處理數(shù)組時(shí)遇到的一個(gè)bug 問題,需要的朋友可以參考下2018-01-01詳解vue數(shù)組遍歷方法forEach和map的原理解析和實(shí)際應(yīng)用
這篇文章主要介紹了詳解vue數(shù)組遍歷方法forEach和map的原理解析和實(shí)際應(yīng)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11vite創(chuàng)建vue3項(xiàng)目頁(yè)面引用public下js文件失敗解決辦法
Vue3相較于之前的版本有了不少變化,如引用全局Js文件,這篇文章主要給大家介紹了關(guān)于vite創(chuàng)建vue3項(xiàng)目頁(yè)面引用public下js文件失敗的解決辦法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11vue柱狀進(jìn)度條圖像的完美實(shí)現(xiàn)方案
本文是對(duì)bar進(jìn)度條實(shí)現(xiàn)的2種方案進(jìn)行分享,第一種是很簡(jiǎn)單,純css的實(shí)現(xiàn),第二種是echart的實(shí)現(xiàn)。對(duì)vue柱狀進(jìn)度條圖像的實(shí)現(xiàn)方案,感興趣的朋友跟隨小編一起看看吧2019-08-08