国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

VueJS源碼學(xué)習(xí)——項目結(jié)構(gòu)&目錄

ad6623 / 469人閱讀

摘要:所以整個的核心,就是如何實現(xiàn)這三樣?xùn)|西以上摘自囧克斯博客的一篇文章從版本開始這個時候的項目結(jié)構(gòu)如下源碼在里面,為打包編譯的代碼,為打包后代碼放置的位置,為測試代碼目錄。節(jié)點類型摘自資源另一位作者關(guān)于源碼解析

本項目的源碼學(xué)習(xí)筆記是基于 Vue 1.0.9 版本的也就是最早的 tag 版本,之所以選擇這個版本,是因為這個是最原始沒有太多功能拓展的版本,有利于更好的看到 Vue 最開始的骨架和脈絡(luò)以及作者的最初思路。而且能和后續(xù)的 1.x.x 版本做對比,發(fā)現(xiàn)了作者為了修復(fù) bug 而做出的很多有趣的改進甚至回退,如 vue nextTick 的版本迭代經(jīng)歷了更新、回退和再次更新

原文地址
項目地址

囧克斯的一篇源碼解析文章

Vue.js 是一個典型的 MVVM 框架,整個程序從最上層分為

1 全局設(shè)計:包括全局接口、默認選項

2 vm 實例設(shè)計: 包括接口設(shè)計(vm 原型)、實例初始化過程設(shè)計(vm構(gòu)造函數(shù))

構(gòu)造函數(shù)核心的工作內(nèi)容:

整個實例初始化過程,關(guān)鍵在于將 數(shù)據(jù)(Model) 和 視圖(view)建立起關(guān)聯(lián)關(guān)系:

通過 observer 對 data 進行了監(jiān)聽,并且提供訂閱某個數(shù)據(jù)項的變化的能力

把 template 解析成一段 document fragment,然后解析其中的 directive,得到每一個 directive 所依賴的數(shù)據(jù)項及其更新方法。比如 v-text="message" 被解析之后 (這里僅作示意,實際程序邏輯會更嚴謹而復(fù)雜):

所依賴的數(shù)據(jù)項 this.$data.message,以及

相應(yīng)的視圖更新方法 node.textContent = this.$data.message

通過 watcher 把上述兩部分結(jié)合起來,即把 directive 中的數(shù)據(jù)依賴訂閱在對應(yīng)數(shù)據(jù)的 observer 上,這樣當(dāng)數(shù)據(jù)變化的時候,就會觸發(fā) observer,進而觸發(fā)相關(guān)依賴對應(yīng)的視圖更新方法,最后達到模板原本的關(guān)聯(lián)效果。

所以整個 vm 的核心,就是如何實現(xiàn) observer, directive (parser), watcher 這三樣?xùn)|西

以上摘自囧克斯博客的一篇文章

從 v1.0.9 版本開始

這個時候的項目結(jié)構(gòu)如下:

源碼在 src 里面,build 為打包編譯的代碼,dist 為打包后代碼放置的位置, test 為測試代碼目錄。

從 package.json 里可以了解到項目用到的依賴包以及項目的開發(fā)和運行方式,其中編譯代碼是:

    "build": "node build/build.js",

于是我們到對應(yīng)的這個文件里:

var fs = require("fs")
var zlib = require("zlib")
var rollup = require("rollup")
var uglify = require("uglify-js")
var babel = require("rollup-plugin-babel")
var replace = require("rollup-plugin-replace")
var version = process.env.VERSION || require("../package.json").version

var banner =
  "/*!
" +
  " * Vue.js v" + version + "
" +
  " * (c) " + new Date().getFullYear() + " Evan You
" +
  " * Released under the MIT License.
" +
  " */"

// CommonJS build.
// this is used as the "main" field in package.json
// and used by bundlers like Webpack and Browserify.
rollup.rollup({
  entry: "src/index.js",
  plugins: [
    babel({
      loose: "all"
    })
  ]
})
...

可以知道這個時候用的是 rollup 來進行打包編譯的, 入口文件是 __src/index.js__,index.js 的代碼很簡潔:

import Vue from "./instance/vue"
import directives from "./directives/public/index"
import elementDirectives from "./directives/element/index"
import filters from "./filters/index"
import { inBrowser } from "./util/index"

Vue.version = "1.0.8"

/**
 * Vue and every constructor that extends Vue has an
 * associated options object, which can be accessed during
 * compilation steps as `this.constructor.options`.
 *
 * These can be seen as the default options of every
 * Vue instance.
 */

Vue.options = {
  directives,
  elementDirectives,
  filters,
  transitions: {},
  components: {},
  partials: {},
  replace: true
}

export default Vue

// devtools global hook
/* istanbul ignore if */
if (process.env.NODE_ENV !== "production") {
  if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
    window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit("init", Vue)
  }
}

從這里可以知道實例 vue 的實現(xiàn)在 src/instance/vue 中, 還涉及了 directives 應(yīng)該是用于指令解析的方法和 filter 過濾器,這個在 2.0 已經(jīng)不存在但在 1.0 使用比較頻繁的功能, 同時 inBrowser 應(yīng)該是用來判斷是否是瀏覽器環(huán)境,說明 src/util 是一個工具類的目錄,這里一個個驗證

工具類方法 inBrowser

首先看 inBrowser__, 發(fā)現(xiàn) __util/index.js 也只是一個工具函數(shù)入口文件:

export * from "./lang"
export * from "./env"
export * from "./dom"
export * from "./options"
export * from "./component"
export * from "./debug"
export { defineReactive } from "../observer/index"

從字面可以知道涉及到的工具類有 語言、環(huán)境?、dom操作、options?、組件化、開發(fā)類、實時定義? 這些類型的工具, 而 inBrowser 應(yīng)該屬于 env 或者 dom,在 util/env 中找到了其實現(xiàn):

...
// Browser environment sniffing
export const inBrowser =
  typeof window !== "undefined" &&
  Object.prototype.toString.call(window) !== "[object Object]"
...

這里利用瀏覽器的全局對象 window 做區(qū)分,因為在 nodejs 環(huán)境下是沒有 window 這個全局對象的,所以判斷 typeof window 是否不為 "undefined" 且不是由用戶自己創(chuàng)建的一個普通對象,如果是的話, Object.prototype.toString.call(window) // === [object Object]

而在瀏覽器環(huán)境下,則是這樣的情況:

typeof window
// "object"
Object.prototype.toString.call(window)
// "[object Window]"
Vue 實例構(gòu)造函數(shù)實現(xiàn)

再來看 __src/instance/vue__, 應(yīng)該是實現(xiàn)了vue的實例初始化函數(shù),從代碼可以知道是一個實例的構(gòu)造函數(shù),也是頂層實現(xiàn),底層代碼位于子目錄的 api 和 internal,分別實現(xiàn)了公用的方法和私有的方法變量等

import initMixin from "./internal/init"
import stateMixin from "./internal/state"
import eventsMixin from "./internal/events"
import lifecycleMixin from "./internal/lifecycle"
import miscMixin from "./internal/misc"

import globalAPI from "./api/global"
import dataAPI from "./api/data"
import domAPI from "./api/dom"
import eventsAPI from "./api/events"
import lifecycleAPI from "./api/lifecycle"

/**
 * The exposed Vue constructor.
 *
 * API conventions:
 * - public API methods/properties are prefixed with `$`
 * - internal methods/properties are prefixed with `_`
 * - non-prefixed properties are assumed to be proxied user
 *   data.
 *
 * @constructor
 * @param {Object} [options]
 * @public
 */

function Vue (options) {
  this._init(options)
}

// install internals
initMixin(Vue)
...

// install APIs
globalAPI(Vue)
...

export default Vue

目錄如下:

從注釋可以知道,尤大用前綴 $ 標(biāo)記公用方法和變量,用 _標(biāo)記私有的方法和變量,沒有前綴的變量可能用來代理用戶數(shù)據(jù)

從引入的文件可以知道私有方法和變量分別有 lifecycleMixin 生命周期、eventsMixin 事件機制、stateMixin 狀態(tài)、miscMixin 過濾器, 以及實例的共有方法API: 全局 globalAPI 、數(shù)據(jù)綁定 dataAPI、DOM操作 domAPI、事件操作 eventsAPI、生命周期 lifecycleAPI

通過 initMixin(Vue) 向 Vue 的 prototype 添加原型方法:

export default function (Vue) {
    Vue.prototype.方法 = function(options) {
        ...
    }
}

具體如何實現(xiàn)都在 apiinternal 這兩個文件夾里面,所以 src/instance 是 vue 實例構(gòu)造函數(shù)的實現(xiàn)

directives、 filter 和 elementDirectives
// src/index.js
import Vue from "./instance/vue"
import directives from "./directives/public/index"
import elementDirectives from "./directives/element/index"
import filters from "./filters/index"
import { inBrowser } from "./util/index"

Vue.options = {
  directives,
  elementDirectives,
  filters,
  transitions: {},
  components: {},
  partials: {},
  replace: true
}

export default Vue

// devtools global hook
/* istanbul ignore if */
if (process.env.NODE_ENV !== "production") {
  if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
    window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit("init", Vue)
  }
}

index.js 里剩下這三個都是作為 Vue.options 里的變量存在的,前面知道了 Vue 的構(gòu)造函數(shù)實現(xiàn),知道了利用 工具類 inBrowser 來判斷是否處于瀏覽器,在判斷window是否存在 __VUE_DEVTOOLS_GLOBAL_HOOK__ 這個變量, 如果存在,那么代表瀏覽器安裝了 vue 的調(diào)試插件,那么還會調(diào)用這個變量的方法 init 告訴插件已經(jīng)初始化好了 vue 對象。

從1.0 官網(wǎng)文檔 custom-directive 中可以知道 directive 是讓開發(fā)者開發(fā)自己的指令,具體例子如下

而 element-directive 和 directive 類似,只是形式上是作為一個元素存在,無法傳輸給元素數(shù)據(jù),但是可以操作元素的屬性

這是一個強大的功能,讓開發(fā)者決定數(shù)據(jù)改變時以怎樣的形式渲染到視圖里,強大的功能代碼量也不少,光 directives 里就20幾個文件

src/directives/public/index 這個入口文件可以知道 custom directive 含有的方法和屬性:

// text & html
import text from "./text"
import html from "./html"
// logic control
import vFor from "./for"
import vIf from "./if"
import show from "./show"
// two-way binding
import model from "./model/index"
// event handling
import on from "./on"
// attributes
import bind from "./bind"
// ref & el
import el from "./el"
import ref from "./ref"
// cloak
import cloak from "./cloak"

// must export plain object
export default {
  text,
  html,
  "for": vFor,
  "if": vIf,
  show,
  model,
  on,
  bind,
  el,
  ref,
  cloak
}

可以看到 directive 包含了 文本操作、邏輯操作(循環(huán)、條件)、雙向綁定(這個是比較有趣且重要的額部分)、事件綁定、數(shù)據(jù)綁定、dom綁定還有一個cloak用于未渲染完成的樣式情況

two-way binding 即 vue 中的 v-model 屬性,是對表單輸入類型的元素如 textarea、select 以及不同 type 的 input 元素做雙向綁定,其余類型的元素則不支持這種綁定

// src/directives/public/index.js
import { warn, resolveAsset } from "../../../util/index"
import text from "./text"
import radio from "./radio"
import select from "./select"
import checkbox from "./checkbox"

const handlers = {
  text,
  radio,
  select,
  checkbox
}

export default {

  priority: 800,
  twoWay: true,
  handlers: handlers,
  params: ["lazy", "number", "debounce"],

  /**
   * Possible elements:
   *