欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue源碼之關(guān)于vm.$delete()/Vue.use()內(nèi)部原理詳解

 更新時間:2019年05月01日 11:53:19   作者:小諾哥  
這篇文章主要介紹了Vue源碼之關(guān)于vm.$delete()/Vue.use()內(nèi)部原理詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

vm.$delete()

vm.$delete用法見官網(wǎng)。

為什么需要Vue.delete()?

在ES6之前, JS沒有提供方法來偵測到一個屬性被刪除了, 因此如果我們通過delete刪除一個屬性, Vue是偵測不到的, 因此不會觸發(fā)數(shù)據(jù)響應(yīng)式。

見下面的demo。

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Vue Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
 </head>
 <body>
  <div id="app">
   名字: {{ user.name }} 年紀(jì): {{ user.age }}
   <button @click="addUserAgeField">刪除一個年紀(jì)字段</button>
  </div>
  <script>
   const app = new Vue({
    el: "#app",
    data: {
     user: {
      name: "test",
      age: 10
     }
    },
    mounted() {},
    methods: {
     addUserAgeField() {
      // delete this.user.age; // 這樣是不起作用, 不會觸發(fā)數(shù)據(jù)響應(yīng)式更新
      this.$delete(this.user, 'age') // 應(yīng)該使用
     }
    }
   });
  </script>
 </body>
</html>

源碼分析內(nèi)部實現(xiàn)

源碼位置vue/src/core/instance/state.js的stateMixin方法

export function stateMixin (Vue: Class<Component>) {
  ...
  
  Vue.prototype.$set = set
  Vue.prototype.$delete = del
  
  ...

}

然后查看del函數(shù)位置, vue/src/core/observer/index.js。

/**
 * Delete a property and trigger change if necessary.
 * target: 將被刪除屬性的目標(biāo)對象, 可以是對象/數(shù)組
 * key: 刪除屬性
 */
export function del (target: Array<any> | Object, key: any) {
 // 非生產(chǎn)環(huán)境下, 不允許刪除一個原始數(shù)據(jù)類型, 或者undefined, null
 if (process.env.NODE_ENV !== 'production' &&
  (isUndef(target) || isPrimitive(target))
 ) {
  warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
 }
 // 如果target是數(shù)組, 并且key是一個合法索引,通過數(shù)組的splcie方法刪除值, 并且還能觸發(fā)數(shù)據(jù)的響應(yīng)(數(shù)組攔截器截取到變化到元素, 通知依賴更新數(shù)據(jù))
 if (Array.isArray(target) && isValidArrayIndex(key)) {
  target.splice(key, 1)
  return
 }
 // 獲取ob
 const ob = (target: any).__ob__
 // target._isVue: 不允許刪除Vue實例對象上的屬性
 // (ob && ob.vmCount): 不允許刪除根數(shù)據(jù)對象的屬性,觸發(fā)不了響應(yīng)
 if (target._isVue || (ob && ob.vmCount)) {
  process.env.NODE_ENV !== 'production' && warn(
   'Avoid deleting properties on a Vue instance or its root $data ' +
   '- just set it to null.'
  )
  return
 }
 // 如果屬性壓根不在對象上, 什么都不做處理
 if (!hasOwn(target, key)) {
  return
 }
 // 走到這一步說明, target是對象, 并且key在target上, 直接使用delete刪除
 delete target[key]
 // 如果ob不存在, 說明target本身不是響應(yīng)式數(shù)據(jù),
 if (!ob) {
  return
 }
 // 存在ob, 通過ob里面存儲的Dep實例的notify方法通知依賴更新
 ob.dep.notify()
}

工具函數(shù)

// 判斷是否v是未定義
export function isUndef (v: any): boolean %checks {
 return v === undefined || v === null
}

// 判斷v是否是原始數(shù)據(jù)類型(基本數(shù)據(jù)類型)
export function isPrimitive (value: any): boolean %checks {
 return (
  typeof value === 'string' ||
  typeof value === 'number' ||
  // $flow-disable-line
  typeof value === 'symbol' ||
  typeof value === 'boolean'
 )
}

// 判斷對象上是否有屬性
const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj: Object | Array<*>, key: string): boolean {
 return hasOwnProperty.call(obj, key)
}

關(guān)于__ob__屬性, 在很多源碼地方我們都會看到類似這樣獲取ob(Observer實例)

const ob = (target: any).__ob__

牢記只要數(shù)據(jù)被observe過就會打上這個私有屬性, 是在Observer類的構(gòu)造器里面發(fā)生的

export class Observer {
  constructor (value: any) {
  this.value = value
  // 依賴是存在Observe上的dep屬性, 再次通知依賴更新時候我們一般使用__ob__.dep.notify()
  this.dep = new Dep()
  this.vmCount = 0
  // 定義__ob__
  def(value, '__ob__', this)
  if (Array.isArray(value)) {
   if (hasProto) {
    protoAugment(value, arrayMethods)
   } else {
    copyAugment(value, arrayMethods, arrayKeys)
   }
   this.observeArray(value)
  } else {
   this.walk(value)
  }
  }
  ...

}

Vue.use()

大家都知道這個方法是用來安裝插件的, 是全局api。

具體使用見官網(wǎng)。

通過Vue.use()源碼+Vuex部分源碼分析插件的安裝過程

Vue.use()什么時候被綁在Vue原型上

源碼位置: vue/src/core/index.js

Vue

initGlobalAPI()

源碼位置: vue/src/core/global-api/index.js

export function initGlobalAPI (Vue: GlobalAPI) {
  ...
  // 初始化use()
  initUse(Vue)
  ...

}

initUse()

源碼位置: vue/src/core/global-api/use.js

export function initUse (Vue: GlobalAPI) {
 // 這里的Vue是構(gòu)造器函數(shù).
 // 通過以下源碼:
 // vue-dev/src/core/global-api/index.js initGlobalAPI()中
 // vue-dev/src/core/index.js 這里執(zhí)行了initGlobalAPI() => 初始化一些全局api
 // Vue.use(): 安裝Vue.js的插件
 // 如果插件是一個對象,必須提供 install 方法
 // 如果插件是一個函數(shù),它會被作為 install 方法
 // install 方法調(diào)用時,會將 Vue 作為參數(shù)傳入
 Vue.use = function (plugin: Function | Object) {
  // installedPlugins存儲install后的插件
  const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
  if (installedPlugins.indexOf(plugin) > -1) {
   // 同一個插件只會安裝一次
   return this
  }
  // additional parameters
  // 除了插件外的其他參數(shù) Vue.use(MyPlugin, { someOption: true })
  const args = toArray(arguments, 1)
  // 往args存儲Vue構(gòu)造器, 供插件的install方法使用
  args.unshift(this)
  // 分情況執(zhí)行插件的install方法, 把this(Vue), 參數(shù)拋回給install方法
  // 所以我們常說, install這個方法的第一個參數(shù)是 Vue 構(gòu)造器,第二個參數(shù)是一個可選的選項對象:
  if (typeof plugin.install === 'function') {
   // plugin是一個對象
   plugin.install.apply(plugin, args)
  } else if (typeof plugin === 'function') {
   // plugin是一個函數(shù)
   plugin.apply(null, args)
  }
  // install之后會存儲該插件避免重復(fù)安裝
  installedPlugins.push(plugin)
  return this
 }
}

Vuex源碼

我們都知道開發(fā)一個Vue.js 的插件應(yīng)該暴露一個 install 方法。這個方法的第一個參數(shù)是 Vue 構(gòu)造器,第二個參數(shù)是一個可選的選項對象:

那么我們首先就是看Vuex的install方法是怎么實現(xiàn)的

源碼位置: vuex-dev/src/store.js

let Vue // bind on install

// install: 裝載vuex到vue, Vue.use(Vuex)也是執(zhí)行install方法
// 關(guān)于Vue.use()源碼. vue-dev/src/core/global-api/use.js
export function install (_Vue) {
 if (Vue && _Vue === Vue) {
  if (process.env.NODE_ENV !== 'production') {
   console.error(
    '[vuex] already installed. Vue.use(Vuex) should be called only once.'
   )
  }
  return
 }
 // 首次安裝插件, 會把局部的Vue緩存到全局的window.Vue. 主要為了避免重復(fù)調(diào)用Vue.use()
 Vue = _Vue
 applyMixin(Vue)
}

applyMixin()

源碼位置: vuex/src/mixin.js

export default function (Vue) {
 const version = Number(Vue.version.split('.')[0])

 if (version >= 2) {
  // 如果是2.x.x以上版本,注入一個全局mixin, 執(zhí)行vueInit方法
  Vue.mixin({ beforeCreate: vuexInit })
 } else {
  // override init and inject vuex init procedure
  // for 1.x backwards compatibility.
  // 重寫Vue原型上的_init方法, 注入vueinit方法 _init方法見 vue-dev/src/core/instance/init.js
  const _init = Vue.prototype._init // 作為緩存變量
  Vue.prototype._init = function (options = {}) {
   options.init = options.init
    ? [vuexInit].concat(options.init)
    : vuexInit
   // 重新執(zhí)行_init
   _init.call(this, options)
  }
 }

 /**
  * Vuex init hook, injected into each instances init hooks list.
  */
 // 注入store到Vue構(gòu)造器
 function vuexInit () {
  // 這里的this. 指的是Vue構(gòu)造器
  /**
   * new Vue({
   *  ...,
   *  store,
   *  route
   * })
   */
  // options: 就是new Vue(options)
  // 源碼見 vue-dev/src/core/instance/init.js initMixin方法
  const options = this.$options
  // store injection
  // store是我們使用new Vuex.Store(options)的實例
  // 注入store到Vue構(gòu)造函數(shù)上的$store屬性上, 所以我們在Vue組件里面使用this.$store來使用
  if (options.store) {
   // options.store為真說明是根節(jié)點root
   this.$store = typeof options.store === 'function'
    ? options.store()
    : options.store
  } else if (options.parent && options.parent.$store) {
   // 子組件直接從父組件中獲取$store,這樣就保證了所有組件都公用了全局的同一份store
   this.$store = options.parent.$store
  }
 }
}

至于install方法Vuex是如果執(zhí)行的?

export class Store {
 constructor (options = {}) {
  // 瀏覽器環(huán)境下安裝vuex
  if (!Vue && typeof window !== 'undefined' && window.Vue) {
   install(window.Vue)
  }
  ...
 }
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue 項目打包通過命令修改 vue-router 模式 修改 API 接口前綴

    vue 項目打包通過命令修改 vue-router 模式 修改 API 接口前綴

    這篇文章主要介紹了vue 項目打包通過命令修改 vue-router 模式 修改 API 接口前綴的相關(guān)知識,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-06-06
  • Vue中filter使用及根據(jù)id刪除數(shù)組元素方式

    Vue中filter使用及根據(jù)id刪除數(shù)組元素方式

    這篇文章主要介紹了Vue中filter使用及根據(jù)id刪除數(shù)組元素方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 解決vue初始化項目時,一直卡在Project description上的問題

    解決vue初始化項目時,一直卡在Project description上的問題

    今天小編就為大家分享一篇解決vue初始化項目時,一直卡在Project description上的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-10-10
  • Vue2使用cube-ui?實現(xiàn)搜索過濾、高亮功能

    Vue2使用cube-ui?實現(xiàn)搜索過濾、高亮功能

    cube-ui?是基于?Vue.js?實現(xiàn)的精致移動端組件庫,由于很長一段時間沒有學(xué)習(xí)cube-ui?的功能實現(xiàn)示例代碼了,今天通過本文給大家介紹下Vue2使用cube-ui?實現(xiàn)搜索過濾、高亮功能,感興趣的朋友跟隨小編一起看看吧
    2023-01-01
  • 代號為Naruto的Vue?2.7正式發(fā)布功能詳解

    代號為Naruto的Vue?2.7正式發(fā)布功能詳解

    這篇文章主要為大家介紹了代號為Naruto的Vue?2.7正式發(fā)布功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)的示例詳解

    Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)的示例詳解

    在Vue2.x中,編寫組件的方式是使用Options API,它的特點是在對應(yīng)的屬性中編寫對應(yīng)的功能模塊,這篇文章主要介紹了Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)詳解,需要的朋友可以參考下
    2022-10-10
  • Vue結(jié)合Openlayers使用Overlay添加Popup彈窗實現(xiàn)

    Vue結(jié)合Openlayers使用Overlay添加Popup彈窗實現(xiàn)

    本文主要介紹了Vue結(jié)合Openlayers使用Overlay添加Popup彈窗實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 詳解vue 實例方法和數(shù)據(jù)

    詳解vue 實例方法和數(shù)據(jù)

    這篇文章主要介紹了vue 實例方法和數(shù)據(jù)的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-10-10
  • vue項目中如何調(diào)用多個不同的ip接口

    vue項目中如何調(diào)用多個不同的ip接口

    這篇文章主要介紹了vue項目中如何調(diào)用多個不同的ip接口,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 詳解基于Vue的支持?jǐn)?shù)據(jù)雙向綁定的select組件

    詳解基于Vue的支持?jǐn)?shù)據(jù)雙向綁定的select組件

    這篇文章主要介紹了詳解基于Vue的支持?jǐn)?shù)據(jù)雙向綁定的select組件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09

最新評論