vue源碼(一) this.data是怎麼來的?

来源:https://www.cnblogs.com/wangzirui98/archive/2020/03/09/12451648.html
-Advertisement-
Play Games

0.獲取源碼 https://github.com/vuejs/vue 從github地址,直接download下來就行了。在新建項目的時候也可以node_modelus里的vue搭配著看。 1.數據的掛載 首先先引入vue,然後新建他的實例。 首先我們得知道我們引入 的是個什麼東西。所以我們找到源 ...


0.獲取源碼

https://github.com/vuejs/vue

從github地址,直接download下來就行了。在新建項目的時候也可以node_modelus里的vue搭配著看。

1.數據的掛載

首先先引入vue,然後新建他的實例。

import Vue from 'vue'
var app = new Vue({
  el:'#app',
  data:{
    return {
        message:"hello world!"  
    }
    }
})

首先我們得知道我們引入 的是個什麼東西。所以我們找到源碼./src/core/instance/index.js里,找到了vue的廬山真面目了,其實vue就是一個類。

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)
}

首先process.env.NODE_ENV是判斷你啟動時候的參數的,如果不符合的話,就發出警告,否則執行_init方法。值得一提的是一般屬性名前面加_預設代表是私有屬性,不對外展示。當然如果你列印vue實例的話還是能看見,因為只是_是私有屬性人們約定俗成的,沒有js語言層面的私有。

那麼這個_init是哪來的呢?往下看:

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

可以看到下麵有一大串Mixin,我們挑第一個initMixin,然後去查看他的定義。vscode可以直接右鍵,然後選擇轉到定義 或者直接command加滑鼠左鍵點擊函數名稱就可以跳過去看到定義這個方法的地方。

export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

   //..

    // a flag to avoid this being observed
    vm._isVue = true
    
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    
   //..
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    // ..

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

​ init就在最開頭

 Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

   //..

    // a flag to avoid this being observed
    vm._isVue = true
    
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
  

​ init具體包括啥呢,首先將this上下文傳給vm這個對象,然後設置_uid然後再機型一系列的初始化的工作。然後再合併options,最後掛載到vm上。
​ 可能有人會好奇,在形參部分,Vue: Class<Component>是什麼意思,因為JavaScript是一個動態類型語言,也就是說,聲明變數的時候不會指派他是任何一種類型的語言,像java就是典型的靜態類型語言。例如:boolean result = true就是聲明result是一個布爾類型,而相對的,JavaScript中可以聲明var result =true。這樣雖然方便很多,但是因為靜態類型在編譯過程中就會查出錯誤並提示開發者改正錯誤,但是像Js這樣的動態語言在編譯的時候既是存在錯誤也不會提出,只有在真正運行時才會出錯。所以就會有不必要的麻煩,那麼如何對Js進行靜態類型檢查呢?就是插件唄。vue用了flow的插件,讓js有了靜態類型檢查,:後面代表了限定vue這個形參的屬性。具體就不展開了,可以去看flow的文檔。

Flow:https://flow.org/

​ 接下來接著說正文,const vm: Component = this可以看到把當前的執行前後文給了vm。然後之後就是一些陸陸續續的掛載,值得註意的就是vm.$options就是填寫在vue實例里的參數,例如el,mounted,data都被保存在$options里。

但是平常使用的時候我們沒有用到this.$options.data1里,反而是直接用this.data1來調用,這其實vue也在其中進行了操作。

我們會發現在上面的代碼段里有一行initState(vm),我們找到initState的定義。

export function initState (vm: Component) {
    // ..
  const opts = vm.$options
  if (opts.data) {
    initData(vm)
  } 
  // ..
}

然後我們可以接著轉到initData這個方法的定義

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

把上面的代碼拆分來看

 let data = vm.$options.data
 data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}

上面代碼先通過$options獲取到data,然後判斷data是不是通過返回對象的方式建立的,如果是,那麼則執行getData方法。getData的方法主要操作就是 data.call(vm, vm) 這步通過給data調用了vm這個上下文環境,然後直接返回這個包括data的vm對象。

那麼現在vm上已經有data了是嗎?確實,但是這個data是vm._data也就是說如果你想訪問message這個屬性你現在只能通過vue._data.message這樣來訪問。所以我們接著往下看。

  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }

這一大段上面聚焦的是prop data methods 們如果相同之後就會提出相應的警示。為什麼要他們不一樣呢,因為他們都是通過this.XX來調用的,如果重名,vue分不清他們是誰。如果都沒問題了,我們就把_datas上的值直接賦給vm,然後轉到最後一步proxy(vm, _data, key) ,然後我們轉移到proxy這個方法中:

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

就是通過sharedPropertyDefinition.getsharedPropertyDefinition.set的設置的get和set方法,然後在通過Object.defineProperty來定義訪問target.key的時候調用sharedPropertyDefinition的set和get。

也就是相當於,我要求vm.message,就會觸發sharedPropertyDefinition的get,然後返回vm._data.message

至此數據就可以通過vm.message的方式訪問了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在SQL Server中,如何快速刪除大表中的數據呢? 回答這個問題前,我們必須弄清楚上下文環境和以及結合實際、具體的需求,不同場景有不同的應對方法。 1: 整張表的數據全部刪除 如果是整張表的數據全部清空、刪除,這種場景倒是非常簡單,TRUNCATE TABLE肯定是最快的。 反而用DELETE處... ...
  • Over()分析函數 說明:聚合函數(如sum()、max()等)可以計算基於組的某種聚合值,但是聚合函數對於某個組只能返回一行記錄。若想對於某組返回多行記錄,則需要使用分析函數。 rank()/dense_rank over(partition by ... order by ...) 說明:ov ...
  • 開心一刻 樓主:來,我們先排練一遍 小伙伴們:好 嘿、哈、嚯 樓主:非常好,就是這個節奏,我們開始吧 樓主:啊、啊、啊,疼 ! 你們是不是故意的 ? 回表與覆蓋索引 正式講 ICP 之前了,我們先將相關的概念捋一捋,知道的就當回顧,不知道的就當瞭解了,這有助於對 ICP 的理解 建個示例表 tbl_ ...
  • 新聞 1. "Pixel 4a真機照曝光:谷歌首次採用挖孔設計 小巧的塑料材質單手新機" 1. "Android 11 Developer Preview首次更新發佈" 1. "[圖]Android 10獲新功能:分享菜單可固定常用應用程式" 1. "[視頻]新Feature Drop彙總:Pixe ...
  • 通過jQuery實現用戶註冊身份驗證,當每個文本框失去焦點時進行該文本框內容校驗,並將校驗信息在文本框右側顯示出結果。 ...
  • 常用的偽類和css選擇器: :required :optional 必填元素和選填元素 :in-range :out-of-range 在範圍之內和不在範圍之內 :valid :invalid 符合要求的和不符合要求的 :read-only :read-write 只讀和可讀可寫 ( :read-w ...
  • HTML5約束驗證API: willValidate 表示如果元素的約束沒有被符合則值為 false validity validationMessage 用於描述與元素相關約束的失敗信息。 checkValidity() 表示如果元素沒有滿足它的任意約束,返回false,其他情況返回 true s ...
  • 概述 Proxy 與 Reflect是 ES6為了操作對象引入的 API 。Proxy可以對目標對象的讀取、函數調用等操作進行攔截,然後進行操作處理。它不直接操作對象,而是像代理模式,通過對象的代理對象進行操作,在進行這些操作時,可以添加一些需要的額外操作。 Proxy的存在就相當於 在對象的讀取、 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...