欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

深入理解vue-class-component源碼閱讀

vue-class-component是vue作者尤大推出的一個支持使用class方式來開發(fā)vue單文件組件的庫。但是,在使用過程中我卻發(fā)現(xiàn)了幾個奇怪的地方。

睢陽網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,睢陽網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為睢陽近1000家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的睢陽做網(wǎng)站的公司定做!

首先,我們看一個簡單的使用例子:

// App.vue
<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  props: {
    propMessage: String
  }
})
export default class App extends Vue {
  // initial data
  msg=123

  // use prop values for initial data
  helloMsg='Hello, '+this.propMessage

  // lifecycle hook
  mounted () {
    this.greet()
  }

  // computed
  get computedMsg () {
    return'computed '+this.msg
  }

  // method
  greet () {
    alert('greeting: '+this.msg)
  }
}
</script>

//main.js
import App from './App.vue'

newVue({
  el: '#app',
  router,
  store,
  components: {
    App
  },
  template: '<App/>'
})

在這個例子中,很容易發(fā)現(xiàn)幾個疑點:

1. App類居然沒有constructor構(gòu)造函數(shù);
2. 導(dǎo)出的類居然沒有被new就直接使用了。
3. msg=123,這是什么語法?

首先,針對前兩個疑問,需要說明一下,class不一定非得有構(gòu)造函數(shù),同樣也不一定非得使用new才能使用。熟悉原理的朋友應(yīng)該知道,class只是一個ES6的語法糖,說白了還是一個Function而已。但是,這兩點無疑是class這個語法糖的重要價值所在,可這里卻偏偏沒用,不由讓人奇怪,甚至?xí)?,既然不?dāng)class用,那為什么不干脆就用Function呢?

而第三點,卻是妥妥點的語法錯誤啊,為此我還特意打開了Chrome控制臺試驗了一下,確實報錯了。實驗結(jié)果如下:

深入理解vue-class-component源碼閱讀

那這到底是怎么回事呢?出于程序員的好奇心,我對vue-class-component的源碼探索了一番。下面就一起來看看,相信看完就可以解答上面的疑惑了。

第一步,在看源碼之前,必須對裝飾器的知識有一定了解。裝飾器種類有好幾種,vue-class-component中主要用了類裝飾器,本文只對類裝飾器做簡單介紹,更多信息請參閱阮老師的文章:ECMAScript 6入門。

類裝飾器,顧名思義,就是用來裝飾一個類的,說的直白點就是用于修改一個類的。它具體有兩種用法。如下:

// 用法一
function Decorator (target) {  
  // 處理target  
  return target
}

@Decorator
class ClassTest () {}

// 用法二
function DecoratorFactory (options) {  
  return function Decorator (target) {    
    //@todo 利用options一起處理target     
    // 然后返回 
    return target  
  }
}

@DecoratorFctory(options)
class ClassTest () {}

在兩個用法中,我們將Decorator稱為裝飾器函數(shù),DecoratorFactory稱為裝飾器工廠。

類裝飾器函數(shù)規(guī)定只能接收類構(gòu)造函數(shù)本身,如果還需要額外的參數(shù)傳入,則需要使用裝飾器工廠函數(shù)。

我們以裝飾器工廠函數(shù)為例,說明其執(zhí)行流程:

1. JS引擎首先會執(zhí)行工廠函數(shù),然后保存其返回的裝飾器函數(shù);
2. 然后解析class,將其轉(zhuǎn)化為一個構(gòu)造函數(shù);
3. 將上述構(gòu)造函數(shù)作為參數(shù)執(zhí)行第一步得到的裝飾器函數(shù)。
4. 如果裝飾器函數(shù)有返回值,則會將類變量(如例子中的ClassTest變量)指向返回值,否則類變量仍然指向構(gòu)造函數(shù),基于JS引用變量的特點,即使仍指向原構(gòu)造函數(shù),這個構(gòu)造函數(shù)也可能在裝飾器中被改造過了。

直接使用裝飾器函數(shù)的情況類似上面,只是少了裝飾器工廠這一步處理過程。

了解了基本知識,我們開始第二步,解析vue-class-component執(zhí)行流程。這里將根據(jù)裝飾器的執(zhí)行流程,分三個部分講解。第一,工廠函數(shù)做了什么;第二,class解析之后是什么樣的;第三,裝飾器函數(shù)又做了什么。

工廠函數(shù)做了什么?

// vue-class-component使用的是TS語法
// Component實際上是既作為工廠函數(shù),又作為裝飾器函數(shù)
function Component (options: ComponentOptions<Vue> | VueClass<Vue>): any {
  if (typeofoptions==='function') {
    // 區(qū)別一下。這里的命名雖然是工廠,其實它才是真正封裝裝飾器邏輯的函數(shù)
    return componentFactory (options)
  }
  return function (Component:VueClass<Vue>){
    return componentFactory(Component,options)
  }
}

從源碼中可以看出,Component函數(shù)只是對參數(shù)進行了判斷,說明它既可以用作工廠函數(shù),也可以用作裝飾器函數(shù)。而實際裝飾器的邏輯則被封裝在componentFactory函數(shù)里,這里對命名需要注意區(qū)分下,此工廠非彼工廠。

Class解析之后是什么樣的

在文章開頭我們就有疑問,在class中不經(jīng)過constructor直接給其屬性賦值是不符合JS語法的,而且我們還在Chrome上試驗過了,確實會報錯。但我們在使用component-class-component時卻又實實在在那么干了,并且也沒什么問題,這是怎么回事呢?
事實上,Chrome等主流瀏覽器對于ES6以及更高級的ES7、ES8的支持是不完整的,很多功能特性都不支持,這也是我們平時為什么都會使用babel來將高級的ES語法轉(zhuǎn)換成ES5的原因。而我們前面提及的這點疑惑正是這個原因,Chrome不支持,不代表babel不支持。

不過,即便如此,我們又產(chǎn)生了一個新的疑惑,這種語法我沒見過,那么經(jīng)過babel轉(zhuǎn)換后的class會是什么樣的呢?畢竟這個轉(zhuǎn)換結(jié)果會作為參數(shù)傳遞 給Component裝飾器來處理,要想了解Component的處理過程,這個參數(shù)需要先了解。
于是,我在Component函數(shù)內(nèi)添加了一條console.log(),得到了打印后的結(jié)果,只是我使用的webpack+babel-loader執(zhí)行的編譯,結(jié)果比較難以閱讀,我簡單翻譯了一下,并和class源碼一起對比如下:

// 轉(zhuǎn)換前
class User {
  name = 'yl'
  age = 10

  get computeMethod () {
    cnsole.log(1)
  }

  method () {
    console.log(2)
  }
}

// 轉(zhuǎn)換后
function User () {
  this.name = 'yl'
  this.age = 10
}

// 計算屬性定義
User.prototype.defineProperty(this, 'computeValue', {
  get () {
    console.log(1)
    return this.name
  }
})

User.prototype.method = function () {
  console.log(2)
}

由此,我們也可以推測出,一個.vue文件導(dǎo)出的類會被解析成什么樣子。

裝飾器函數(shù)又做了什么

此時,我們已經(jīng)知曉了傳遞給裝飾器函數(shù)的參數(shù)是什么樣了。這個參數(shù)應(yīng)該是一個構(gòu)造函數(shù),它的主體會對類實例的屬性進行賦值,它的原型則攜帶著各種屬性和方法。
而我們知道的,如果不使用vue-class-component,那么一個.vue文件應(yīng)該導(dǎo)出如下對象:

export default {
  name: 'test',
  data () {
    return {...}
  },
  computed: {
    com1 () {...},
    com2 () {...}
  },
  methods: {...},
  // 各種hook函數(shù)
}

很顯然,裝飾器函數(shù)必然是將傳入的組件構(gòu)造函數(shù)轉(zhuǎn)換成了一個vue配置對象。那么,具體內(nèi)部是怎么做的呢?我們來看看源碼。(源碼筆者加上了詳細(xì)注釋,但較長,可以直接跳過看后面的總結(jié)。)

// 這個函數(shù)就是封裝了裝飾器邏輯的函數(shù),接受兩個參數(shù):
// 第一個是所裝飾的類的構(gòu)造函數(shù);第二個是開發(fā)者傳入的mixins對象
function componentFactory (
 Component: VueClass<Vue>,
 options: ComponentOptions<Vue> = {}
): VueClass<Vue> {
 // 首先給options.name賦值,確保最終生成的對象具有name屬性。
 options.name = options.name || (Component as any)._componentTag || (Component as any).name
 // 獲取構(gòu)造函數(shù)原型,這個原型上掛在了該類的method
 const proto = Component.prototype
 // 遍歷原型
 Object.getOwnPropertyNames(proto).forEach(function (key) {
  // 如果是constructor,則不處理。
  // 這也是為什么vue單文件組件類不需要constructor的直接原因,因為有也不會做任何處理
  if (key === 'constructor') {
   return
  }

  // 如果原型屬性(方法)名是vue生命周期鉤子名,則直接作為鉤子函數(shù)掛載在options最外層
  if ($internalHooks.indexOf(key) > -1) {
   options[key] = proto[key]
   return
  }
  // 先獲取到原型屬性的descriptor。
  // 在前文已提及,計算屬性其實也是掛載在原型上的,所以需要對descriptor進行判斷
  const descriptor = Object.getOwnPropertyDescriptor(proto, key)!
  if (descriptor.value !== void 0) {
   // 如果屬性值是一個function,則認(rèn)為這是一個方法,掛載在methods下
   if (typeof descriptor.value === 'function') {
    (options.methods || (options.methods = {}))[key] = descriptor.value
   } else {
    // 如果不是,則認(rèn)為是一個普通的data屬性。
    // 但是這是原型上,所以更類似mixins,因此掛在mixins下。
    (options.mixins || (options.mixins = [])).push({
     data (this: Vue) {
      return { [key]: descriptor.value }
     }
    })
   }
  } else if (descriptor.get || descriptor.set) {
   // 如果value是undefined(ps:void 0 === undefined)。
   // 且描述符具有g(shù)et或者set方法,則認(rèn)為是計算屬性。不理解的參考我上面關(guān)于class轉(zhuǎn)換成構(gòu)造函數(shù)的例子
   // 這里可能和普通的計算屬性不太一樣,因為一般計算屬性只是用來獲取值的,但這里卻有setter。
   // 不過如果不使用setter,與非class方式開發(fā)無異,但有這一步處理,在某些場景會有特效。
   (options.computed || (options.computed = {}))[key] = {
    get: descriptor.get,
    set: descriptor.set
   }
  }
 })

 // 收集構(gòu)造函數(shù)實例化對象的屬性作為data,并放入mixins
 (options.mixins || (options.mixins = [])).push({
  data (this: Vue) {
   // 實例化Component構(gòu)造函數(shù),并收集其自身的(非原型上的)屬性導(dǎo)出,內(nèi)部還針對不同vue版本做了兼容。
   // 感興趣的可以自己去瞅瞅源碼,不復(fù)雜,在此不贅述。
   return collectDataFromConstructor(this, Component)
  }
 })

 // 處理屬性裝飾器,vue-class-component只提供了類裝飾器。
 // 像props、components等特殊參數(shù)只能寫在Component(options)的options參數(shù)里。
 // 通過這個接口可以擴展出屬性裝飾器,像vue-property-decorator庫那種的屬性裝飾器
 const decorators = (Component as DecoratedClass).__decorators__
 if (decorators) {
  decorators.forEach(fn => fn(options))
  delete (Component as DecoratedClass).__decorators__
 }

 // 獲取Vue對象
 const superProto = Object.getPrototypeOf(Component.prototype)
 const Super = superProto instanceof Vue
  ? superProto.constructor as VueClass<Vue>
  : Vue
 // 通過vue.extend生成一個vue實例
 const Extended = Super.extend(options)

 // 在前面只處理了Component構(gòu)造函數(shù)原型和其實例化對象的屬性和方法。
 // 對于構(gòu)造函數(shù)本身的靜態(tài)屬性還沒有處理,在此處理,處理過程類似前面,不贅述。
 forwardStaticMembers(Extended, Component, Super)

 // 反射相關(guān)處理,這個是新特性,本人了解也不多,但到此已經(jīng)不影響理解了,所以可以略過。
 // 如有對此了解的,歡迎補充。
 if (reflectionIsSupported) {
  copyReflectionMetadata(Extended, Component)
 }

 // 最終返回這個vue實例對象
 return Extended
}

源碼較長,在此總結(jié)一下。這里主要做了四件事:

第一,將傳入的構(gòu)造函數(shù)原型上的屬性放入data中,將方法根據(jù)是否是生命周期鉤子、是否是計算屬性,來分別放入對應(yīng)的位置。

第二,實例化構(gòu)造函數(shù),將構(gòu)造函數(shù)實例化對象的屬性放入data,實例化對象本身(不算原型上的)是不帶有方法的,即使某個屬性的值是function類型,也應(yīng)該作為data來處理。

第三、對構(gòu)造函數(shù)自身的靜態(tài)屬性和方法處理,處理方式同原型的處理方式。

第四,提供屬性裝飾器的拓展功能,Component只裝飾了類,如果想對類中的屬性做進一步的處理,可以從此入手,比如vue-property-decorator庫提供的那些裝飾器就是依賴這個拓展功能。

說到此,想必大家對前面的疑惑也釋然了,同時對vue-class-component的實現(xiàn)原理也有了一個大體的思路。因本人技術(shù)有限,文中可能存在膚淺、錯誤的地方,如有發(fā)現(xiàn),還請不吝賜教,感謝!

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享題目:深入理解vue-class-component源碼閱讀
分享網(wǎng)址:http://chinadenli.net/article6/jggpog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、手機網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、網(wǎng)站制作網(wǎng)頁設(shè)計公司、品牌網(wǎng)站設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)
亚洲精品国产第一区二区多人| 欧美国产在线观看精品| 欧美黑人巨大一区二区三区| 日本中文在线不卡视频| 欧美成人一区二区三区在线| 国产日产欧美精品大秀| 久久精品中文字幕人妻中文| 国产农村妇女成人精品| 91老熟妇嗷嗷叫太91| 久久亚洲成熟女人毛片| 亚洲综合香蕉在线视频| 中文字幕亚洲人妻在线视频| 精品人妻一区二区三区免费| 激情少妇一区二区三区| 激情中文字幕在线观看| 亚洲一区二区三区熟女少妇| 日本免费熟女一区二区三区| 国产综合香蕉五月婷在线| 加勒比东京热拍拍一区二区| 97人摸人人澡人人人超碰| 成人精品一区二区三区在线| 后入美臀少妇一区二区| 国产91麻豆精品成人区| 人妻内射精品一区二区| 亚洲欧美日韩国产成人| 日韩黄片大全免费在线看| 99国产高清不卡视频| 国内尹人香蕉综合在线| 日韩少妇人妻中文字幕| 激情爱爱一区二区三区| 麻豆亚州无矿码专区视频| 男女午夜视频在线观看免费| 日本加勒比不卡二三四区| 免费午夜福利不卡片在线 视频| 丰满人妻少妇精品一区二区三区| 亚洲最大的中文字幕在线视频| 国产日韩欧美在线播放| 日韩午夜老司机免费视频| 97人妻人人揉人人躁人人| 国产农村妇女成人精品| 麻豆一区二区三区在线免费|