vue2源碼解析之全局API實(shí)例詳解
前言
實(shí)例方法是掛載在vue的原型上,而全局方法是掛載在vue上;全局方法有12個(gè),分別為extend、nextTick、set、delete、directive、filter、component、use、mixin、version和observable;
Vue.extend()
基本使用
/*
作用:
使用基礎(chǔ)vue構(gòu)造器,創(chuàng)建一個(gè)子類,參數(shù)是一個(gè)包含組件選項(xiàng)的對(duì)象
`data` 選項(xiàng)是特例,需要注意 - 在 `Vue.extend()` 中它必須是函數(shù)
*/
Vue.extend(options)
// eg:
<div id="mount-point"></div>
// 創(chuàng)建構(gòu)造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 創(chuàng)建 Profile 實(shí)例,并掛載到一個(gè)元素上。
new Profile().$mount('#mount-point')
// 結(jié)果:
<p>Walter White aka Heisenberg</p>整體源碼
// 源碼位置 src/core/global-api/extend.js
Vue.extend = function (extendOptions: Object): Function {
// 初始化參數(shù)
extendOptions = extendOptions || {}
// 存儲(chǔ)父類
const Super = this
// 存儲(chǔ)父類的唯一標(biāo)識(shí)
const SuperId = Super.cid
// 獲取到參數(shù)中的緩存數(shù)據(jù)
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
// 如果已經(jīng)緩存過就會(huì)緩存中獲取返回
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// 獲取到name
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
// 創(chuàng)建一個(gè)子類
const Sub = function VueComponent (options) {
this._init(options)
}
// 原型繼承
Sub.prototype = Object.create(Super.prototype)
// 修改構(gòu)造器
Sub.prototype.constructor = Sub
// 子類的唯一標(biāo)識(shí)
Sub.cid = cid++
// 合并父類和傳遞進(jìn)來的參數(shù)
Sub.options = mergeOptions(
Super.options,
extendOptions
)
// 子類中存儲(chǔ)父類
Sub['super'] = Super
// 如果子類中有props 進(jìn)行初始化
if (Sub.options.props) {
initProps(Sub)
}
// 如果子類中有計(jì)算屬性,進(jìn)行初始化
if (Sub.options.computed) {
initComputed(Sub)
}
// 把父類的extend,mixin,use放在子類上
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// 把指定的屬性全部從父類上拷貝到子類上
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
if (name) {
Sub.options.components[name] = Sub
}
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
// 緩存子類
cachedCtors[SuperId] = Sub
return Sub
}
}
// 把子類中的props存儲(chǔ)到原型上
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
// 把子類中的計(jì)算屬性存儲(chǔ)到原型上
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}extend方法內(nèi)部其實(shí)就是創(chuàng)建一個(gè)子類,通過原型繼承的方式繼承父類,然后把父類的屬性和一些方法存儲(chǔ)到子類上,并且把子類進(jìn)行緩存,再一開始就先從緩存中獲取子類,如果沒有就進(jìn)行屬性和方法的繼承,最后返回子類;
Vue.nextTick,Vue.set,Vue.delete
以上三種都在實(shí)例方法中解析過
Vue.directive、Vue.filter、Vue.component
基本使用
注冊(cè)或獲取全局指令
// 注冊(cè)
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
// 注冊(cè) (指令函數(shù))
Vue.directive('my-directive', function () {
// 這里將會(huì)被 `bind` 和 `update` 調(diào)用
})
// getter,返回已注冊(cè)的指令
var myDirective = Vue.directive('my-directive')注冊(cè)或獲取過濾器
// 注冊(cè)
Vue.filter('my-filter',function(){})
// 獲取
Vue.filter('my-filter')注冊(cè)或獲取組件
// 注冊(cè) 傳入一個(gè)擴(kuò)展過的構(gòu)造器
Vue.component('my-component', Vue.extend({}))
// 注冊(cè) 傳入一個(gè)選項(xiàng)對(duì)象
Vue.component('my-component', {})
// 獲取
Vue.component('my-component')源碼分析
// 源碼位置 src/core/global-api/
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
// 不存在definition 表示獲取,直接從options下的相應(yīng)的typs+'s'進(jìn)行獲取
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
// 如果是組件 并且definition為一個(gè){}對(duì)象,那么就通過extend繼承vue
if (type === 'component' && isPlainObject(definition)) {
// 沒有name就使用id
definition.name = definition.name || id
// 使definition繼承vue
definition = this.options._base.extend(definition)
}
// 如果是指令并且definition是一個(gè)函數(shù),那么就把這個(gè)函數(shù)作為bind和update
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
// 設(shè)置值 保存到對(duì)應(yīng)的key下
this.options[type + 's'][id] = definition
return definition
}
}
})
}指令,過濾器,組件把它們放在一個(gè)數(shù)組中,遍歷這個(gè)數(shù)組,分別進(jìn)行處理
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
){}
})如果沒有傳遞第二個(gè)參數(shù),表示是獲取,直接進(jìn)行獲取返回
if (!definition) {
return this.options[type + 's'][id]
}否則就是傳遞了第二個(gè)參數(shù),表示是注冊(cè),首先判斷是否是組件,是組件并且第二個(gè)參數(shù)是對(duì)象,那么就繼承Vue;
// 如果是組件
if (type === 'component' && isPlainObject(definition)) {
// 沒有name就使用id
definition.name = definition.name || id
// 使definition繼承vue
definition = this.options._base.extend(definition)
}如果是指令,并且第二個(gè)參數(shù)是函數(shù),那么直接放在一個(gè)對(duì)象中
// 如果是指令
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}最后給相應(yīng)的對(duì)象下的自定義指令或過濾器或組件進(jìn)行賦值并返回
// 設(shè)置值 this.options[type + 's'][id] = definition return definition
通過以上三種方法就可以自定義組件,過濾器和指令,然后就可以在模板中使用它們,最終的處理還是在模板編譯節(jié)點(diǎn)進(jìn)行的;
Vue.use
基本使用
/*
作用:
安裝 Vue.js 插件。如果插件是一個(gè)對(duì)象,必須提供 `install` 方法。
如果插件是一個(gè)函數(shù),它會(huì)被作為 install 方法。install 方法調(diào)用時(shí),會(huì)將 Vue 作為參數(shù)傳入。
該方法需要在調(diào)用 `new Vue()` 之前被調(diào)用。
當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會(huì)被安裝一次
plugin:- `{Object | Function} plugin`
*/
Vue.use( plugin )源碼預(yù)覽
// 源碼位置 src/core/global-api/use.js
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 初始_installedPlugins變量
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 如果已經(jīng)存在直接返回
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// 獲取剩余的參數(shù)轉(zhuǎn)成數(shù)組
const args = toArray(arguments, 1)
// 把this添加進(jìn)去
args.unshift(this)
// 如果install是一個(gè)函數(shù) 直接執(zhí)行
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') { // 如果插件是一個(gè)函數(shù),直接執(zhí)行
plugin.apply(null, args)
}
// 添加到數(shù)組中
installedPlugins.push(plugin)
return this
}
}首先獲取到用來緩存的屬性,如果不存在初始化為一個(gè)數(shù)組,再判斷傳遞進(jìn)來的插件是否已經(jīng)存在,存在表示重復(fù)操作,直接返回;(實(shí)現(xiàn):當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會(huì)被安裝一次)
// 初始_installedPlugins變量
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 如果已經(jīng)存在直接返回
if (installedPlugins.indexOf(plugin) > -1) {
return this
}接著獲取到剩余的參數(shù),并且把當(dāng)前實(shí)例也作為參數(shù)(實(shí)現(xiàn):install 方法調(diào)用時(shí),會(huì)將 Vue 作為參數(shù)傳入。)
// 獲取剩余的參數(shù)轉(zhuǎn)成數(shù)組 const args = toArray(arguments, 1) // 把this添加進(jìn)去 args.unshift(this)
判斷plugin.install是否是一個(gè)函數(shù),如果是函數(shù)就通過plugin執(zhí)行(實(shí)現(xiàn):如果插件是一個(gè)對(duì)象,必須提供 install 方法)
// 如果install是一個(gè)函數(shù) 直接執(zhí)行
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
}否則plugin是一個(gè)函數(shù),那么直接執(zhí)行(實(shí)現(xiàn):如果插件是一個(gè)函數(shù),它會(huì)被作為 install 方法)
else if (typeof plugin === 'function') { // 如果插件是一個(gè)函數(shù),直接執(zhí)行
plugin.apply(null, args)
}最后添加到緩存中并返回
// 添加到數(shù)組中 installedPlugins.push(plugin) return this
Vue.mixin
基本使用
/* 作用: 全局注冊(cè)一個(gè)混入,影響注冊(cè)之后所有創(chuàng)建的每個(gè)vue實(shí)例,插件作者可以使用混入, 向組件注入自定義的行為。**不推薦在應(yīng)用代碼中使用**。 */ Vue.mixin()
源碼預(yù)覽
// 源碼位置 src/core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}混入很簡(jiǎn)單,就是把傳遞進(jìn)來的數(shù)據(jù)和實(shí)例上已有的數(shù)據(jù)進(jìn)行合并替換,最后重新賦值給實(shí)例上的options;
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
// 設(shè)置props,把帶有-的屬性名改成駝峰,存到一個(gè)新的對(duì)象中進(jìn)行返回
normalizeProps(child, vm)
// 設(shè)置注入的屬性 把inject中的數(shù)據(jù)都轉(zhuǎn)成 {from:val}形式的對(duì)象返回
normalizeInject(child, vm)
// 設(shè)置指令 返回指令的值為{ bind: def, update: def }的形式
normalizeDirectives(child)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
// 遞歸處理extends和mixins
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
// 創(chuàng)建一個(gè)新的對(duì)象
const options = {}
let key
for (key in parent) { // 遍歷父級(jí)進(jìn)行設(shè)置屬性
mergeField(key)
}
for (key in child) { // 遍歷子級(jí)進(jìn)行設(shè)置屬性
if (!hasOwn(parent, key)) { // 父級(jí)不存在此屬性就進(jìn)行設(shè)置
mergeField(key)
}
}
// 通過策略模式給不同的屬性設(shè)置對(duì)應(yīng)的執(zhí)行函數(shù),
function mergeField (key) {
// 獲取到不同屬性的函數(shù)
const strat = strats[key] || defaultStrat
// 執(zhí)行對(duì)應(yīng)的函數(shù)重新設(shè)置屬性
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}把子級(jí)的屬性進(jìn)行單獨(dú)處理,處理完成之后和父級(jí)的屬性進(jìn)行合并,合并之后進(jìn)行返回;
Vue.compile
基本使用
/* 作用: 將一個(gè)模板字符串編譯成 render 函數(shù)。**只在完整版時(shí)可用**。 template: 字符串模板 */ Vue.compile(template)
源碼預(yù)覽
Vue.compile = compileToFunctions
compileToFunctions函數(shù)在模板編譯節(jié)點(diǎn)已經(jīng)解析完
Vue.observable
基本用法
/*
作用:
讓一個(gè)對(duì)象可響應(yīng),Vue內(nèi)部會(huì)用它來處理data函數(shù)返回的對(duì)象
返回的對(duì)象可以直接用于[渲染函數(shù)]和[計(jì)算屬性]內(nèi),并且會(huì)在發(fā)生變更時(shí)觸發(fā)相應(yīng)的更新。
也可以作為最小化的跨組件狀態(tài)存儲(chǔ)器,用于簡(jiǎn)單的場(chǎng)景:
*/
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}源碼預(yù)覽
// 源碼位置 src/core/global-api/
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}總結(jié)
extend: 內(nèi)部實(shí)現(xiàn)一個(gè)子類,通過原型繼承的方式繼承自當(dāng)前調(diào)用者(父類),并且把父類中的屬性進(jìn)行合并,存儲(chǔ)到自己的options中,父類的方法添加到自己構(gòu)造器上;把子類進(jìn)行緩存;一進(jìn)入到函數(shù)內(nèi)部首先從緩存中獲取,沒有才進(jìn)行創(chuàng)建子類;
directive,filter,component: 這三種方式通過一個(gè)函數(shù)實(shí)現(xiàn),首先判斷有沒有傳遞第二個(gè)參數(shù),如果沒傳遞表示是獲取,直接獲取返回;如果有傳遞表示注冊(cè),如果是指令并且第二個(gè)參數(shù)是函數(shù),那么把函數(shù)作為bind和update的值;如果是組件并且第二個(gè)參數(shù)是函數(shù),那么通過extend方法進(jìn)行繼承;
use: 如果緩存中有此插件就直接返回,否則就判斷傳遞進(jìn)來的參數(shù)是對(duì)象還是函數(shù),如果是對(duì)象那么就執(zhí)行對(duì)象上的install方法,如果是函數(shù)直接執(zhí)行;并且把當(dāng)前實(shí)例作為參數(shù)傳遞進(jìn)去;最后把插件進(jìn)行緩存;
mixin: 首先把props,inject和指令的屬性進(jìn)行統(tǒng)一格式化操作;然后把當(dāng)前實(shí)例的options和傳遞進(jìn)來的參數(shù)進(jìn)行合并,相同屬性傳遞進(jìn)來的參數(shù)會(huì)覆蓋options中的屬性;
observable: 通過調(diào)用observe給當(dāng)前傳遞進(jìn)來的對(duì)象添加響應(yīng)式,并且返回該對(duì)象;
到此這篇關(guān)于vue2源碼解析之全局API實(shí)例詳解的文章就介紹到這了,更多相關(guān)vue2全局API內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vant中的picker選擇器自定義選項(xiàng)內(nèi)容
這篇文章主要介紹了vant中的picker選擇器自定義選項(xiàng)內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
vue學(xué)習(xí)筆記之給組件綁定原生事件操作示例
這篇文章主要介紹了vue學(xué)習(xí)筆記之給組件綁定原生事件操作,結(jié)合實(shí)例形式詳細(xì)分析了vue.js組件綁定原生事件相關(guān)原理、實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02
vue3中實(shí)現(xiàn)拖拽排序代碼示例(vue-draggable-next的使用)
在Vue3中使用拖拽功能時(shí)應(yīng)選用vue-draggable-next插件,傳統(tǒng)的draggable插件不兼容Vue3,可能導(dǎo)致TypeError錯(cuò)誤,安裝后,需在項(xiàng)目中引入并使用,具體步驟包括安裝插件、引入使用、查看效果和相關(guān)說明,需要的朋友可以參考下2024-09-09
vue.js內(nèi)部自定義指令與全局自定義指令的實(shí)現(xiàn)詳解(利用directive)
這篇文章主要給大家介紹了關(guān)于vue.js內(nèi)部自定義指令與全局自定義指令的實(shí)現(xiàn)方法,vue.js中實(shí)現(xiàn)自定義指令的主要是利用directive,directive這個(gè)單詞是我們寫自定義指令的關(guān)鍵字,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-07-07
vue Tooltip提示動(dòng)態(tài)換行問題
這篇文章主要介紹了vue Tooltip提示動(dòng)態(tài)換行問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09

