【讀vue 源碼】溯源 import Vue from 'vue' 到底做了什么?

閱讀資源

vue.js源碼托管地址

flow 靜態(tài)檢查工具地址

rollup 源碼構(gòu)建

flow 靜態(tài)檢查工具

flow 是由Facebook出品的javascript靜態(tài)類(lèi)型檢查工具。vue.js 的源碼是采用flow做了靜態(tài)類(lèi)型檢查豁生。因?yàn)閖avascript是動(dòng)態(tài)類(lèi)型的語(yǔ)言楷力。語(yǔ)言靈活的同時(shí)也很容易引發(fā)一些隱蔽的隱患代碼匠襟。在編譯的時(shí)候看上去不會(huì)報(bào)錯(cuò)擦俐,但運(yùn)行的時(shí)候就會(huì)出現(xiàn)奇奇怪怪的bug望薄。而類(lèi)型檢查倒彰,就是在編譯期盡早發(fā)現(xiàn)由類(lèi)型錯(cuò)誤引起的bug素挽,又不影響代碼運(yùn)行(不需要運(yùn)行時(shí)動(dòng)態(tài)檢查類(lèi)型)。一些復(fù)雜的項(xiàng)目使用工具的手段可以增強(qiáng)代碼的可讀性狸驳,增加項(xiàng)目的可維護(hù)性预明。

flow 類(lèi)型檢查分為兩種方式:

  • 類(lèi)型推斷:通過(guò)變量使用的上下文來(lái)推斷出變量類(lèi)型,然后根據(jù)推斷來(lái)檢查類(lèi)型耙箍。
  • 類(lèi)型注釋?zhuān)鹤⑨尯米兞康念?lèi)型撰糠,通過(guò)注釋來(lái)檢查類(lèi)型。
// 類(lèi)型推斷
// @flow
function split(str) {
    return str.split(' ')
}
split(1)      // Error!
split('abc')  // Works!

傳入?yún)?shù) 1 的時(shí)候辩昆,flow 代碼檢查就會(huì)報(bào)錯(cuò)阅酪,因?yàn)?split()方法是字符串原型對(duì)象上的方法,它期待的參數(shù)類(lèi)型是字符串汁针,而傳入的確實(shí)是數(shù)字型术辐,所以就會(huì)報(bào)錯(cuò)。當(dāng)傳入‘a(chǎn)bc’字符串后就可以正常執(zhí)行代碼了施无。

// 類(lèi)型注釋
// @flow
function concat(a: string, b: string) {
  return a + b;
}

concat("A", "B");   // Works!
concat(1, 2);       // Error!

因?yàn)榧舆\(yùn)算符辉词,即可以執(zhí)行數(shù)字的相加,也可以執(zhí)行字符串的拼接猾骡。所以 concat()方法提前注釋好參數(shù)的類(lèi)型瑞躺。接收的參數(shù)a和b都是字符串,所以調(diào)用concat(1, 2);就會(huì)報(bào)錯(cuò)兴想。調(diào)用concat("A", "B")就能正常執(zhí)行幢哨。

需要注意的是:當(dāng)一個(gè)文件中出現(xiàn)注釋@flow 的標(biāo)記,說(shuō)明該文件是需要用flow 進(jìn)行類(lèi)型檢查的嫂便,否則不進(jìn)行 flow 檢查捞镰。更多內(nèi)容請(qǐng)查看flow 靜態(tài)檢查工具官方文檔。

vue.js源碼中的flow應(yīng)用

在 Vue.js 的主目錄下有 .flowconfig 文件, 它是 Flow 的配置文件岸售。如下:

[ignore]  // 忽略的文件
.*/node_modules/.*
.*/test/.*
.*/scripts/.*
.*/examples/.*
.*/benchmarks/.*

[include]

[libs] // 這里 [libs] 配置的是 flow几迄,表示指定的庫(kù)定義都在 flow 文件夾內(nèi)
flow   // 對(duì)應(yīng)的 flow 目錄

[options]
unsafe.enable_getters_and_setters=true
module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1'
module.name_mapper='^core/\(.*\)$' -> '<PROJECT_ROOT>/src/core/\1'
module.name_mapper='^shared/\(.*\)$' -> '<PROJECT_ROOT>/src/shared/\1'
module.name_mapper='^web/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/web/\1'
module.name_mapper='^weex/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/weex/\1'
module.name_mapper='^server/\(.*\)$' -> '<PROJECT_ROOT>/src/server/\1'
module.name_mapper='^entries/\(.*\)$' -> '<PROJECT_ROOT>/src/entries/\1'
module.name_mapper='^sfc/\(.*\)$' -> '<PROJECT_ROOT>/src/sfc/\1'
suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line

vue.js 內(nèi)的flow目錄說(shuō)明

flow
├── compiler.js        # 編譯相關(guān)
├── component.js       # 組件數(shù)據(jù)結(jié)構(gòu)
├── global-api.js      # Global API 結(jié)構(gòu)
├── modules.js         # 第三方庫(kù)定義
├── options.js         # 選項(xiàng)相關(guān)
├── ssr.js             # 服務(wù)端渲染相關(guān)
├── vnode.js           # 虛擬 node 相關(guān)

vue 源碼的目錄結(jié)構(gòu)

Vue.js 的源碼都在 src 目錄下,目錄結(jié)構(gòu)如下:

src
├── compiler    # 包含 Vue.js 所有編譯相關(guān)的代碼冰评。
├── core        # 包含了 Vue.js 的核心代碼映胁,包括內(nèi)置組件、全局 API 封裝甲雅,Vue 實(shí)例化解孙、觀察者、虛擬 DOM抛人、工具函數(shù)等等弛姜。
├── platforms   # Vue.js 是一個(gè)跨平臺(tái)的 MVVM 框架,它可以跑在 web 上妖枚,也可以配合 weex 跑在 native 客戶端上廷臼。
├── server      # 所有服務(wù)端渲染相關(guān)的邏輯都在這個(gè)目錄下。
├── sfc         # vue.js通過(guò) .vue 單文件來(lái)編寫(xiě)組件绝页。這個(gè)目錄下的代碼邏輯會(huì)把 .vue 文件內(nèi)容解析成一個(gè) JavaScript 的對(duì)象荠商。
├── shared      # 定義一些工具方法,這里定義的工具方法都是會(huì)被瀏覽器端的 Vue.js 和服務(wù)端的 Vue.js 所共享的续誉。

從整個(gè)目錄結(jié)構(gòu)來(lái)看莱没,作者把功能模塊拆的非常的清楚,相關(guān)的邏輯都放在同一個(gè)目錄下來(lái)進(jìn)行維護(hù)酷鸦∈味悖可復(fù)用的代碼也單獨(dú)成為一個(gè)文件夾。

vue.js源碼構(gòu)建

Vue.js 源碼是基于 Rollup 構(gòu)建的臼隔,它的構(gòu)建相關(guān)配置都在 scripts 目錄下嘹裂。Rollup 是一個(gè)javascript 的模塊打包工具。相比webpack更為輕量摔握。了解更多請(qǐng)?jiān)L問(wèn)Rollup Github 地址

構(gòu)建腳本

NPM 托管的項(xiàng)目都會(huì)有一個(gè)package.json的文件寄狼,對(duì)這個(gè)項(xiàng)目加以描述。script 字段用來(lái)定義NPM 的執(zhí)行腳本盒发。vue.js 的執(zhí)行構(gòu)建的腳本如下:

"scripts": {
    "build": "node scripts/build.js",
    "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
    "build:weex": "npm run build -- weex",
  },

也就是說(shuō)例嘱,當(dāng)我們執(zhí)行 npm run build的時(shí)候,實(shí)際上就是執(zhí)行 node scripts/build.js這條語(yǔ)句宁舰。也就是說(shuō) scripts/build.js就是構(gòu)建入口的js文件。

構(gòu)建過(guò)程

1. 從構(gòu)建的入口文件開(kāi)始:scripts/build.js

let builds = require('./config').getAllBuilds() //拿到所有的配置

// filter builds via command line arg
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // filter out weex builds by default
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

build(builds)

上面這部分代碼奢浑,首先從配置文件scripts/config.js中讀取配置相關(guān)的數(shù)據(jù)蛮艰,在對(duì)配置進(jìn)行相應(yīng)的過(guò)濾,從而構(gòu)建出不同用途的vue.js雀彼。

2. 查看構(gòu)建的配置文件:scripts/config.js

const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },
  'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.prod.js'),
    format: 'cjs',
    env: 'production',
    banner
  },
  // Runtime+compiler CommonJS build (CommonJS)
  'web-full-cjs-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.dev.js'),
    format: 'cjs',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  'web-full-cjs-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.prod.js'),
    format: 'cjs',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime only ES modules build (for bundlers)
  'web-runtime-esm': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.esm.js'),
    format: 'es',
    banner
  },
  // Runtime+compiler ES modules build (for bundlers)
  'web-full-esm': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.js'),
    format: 'es',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.js'),
    format: 'es',
    transpile: false,
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.min.js'),
    format: 'es',
    transpile: false,
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // runtime-only build (Browser)
  'web-runtime-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.js'),
    format: 'umd',
    env: 'development',
    banner
  },
  // runtime-only production build (Browser)
  'web-runtime-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.min.js'),
    format: 'umd',
    env: 'production',
    banner
  },
  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler production build  (Browser)
  'web-full-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.min.js'),
    format: 'umd',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Web compiler (CommonJS).
  'web-compiler': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
  },
  // Web compiler (UMD for in-browser use).
  'web-compiler-browser': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/browser.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'VueTemplateCompiler',
    plugins: [node(), cjs()]
  },
  // Web server renderer (CommonJS).
  'web-server-renderer-dev': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.dev.js'),
    format: 'cjs',
    env: 'development',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-prod': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.prod.js'),
    format: 'cjs',
    env: 'production',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-basic': {
    entry: resolve('web/entry-server-basic-renderer.js'),
    dest: resolve('packages/vue-server-renderer/basic.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'renderVueComponentToString',
    plugins: [node(), cjs()]
  },
  'web-server-renderer-webpack-server-plugin': {
    entry: resolve('server/webpack-plugin/server.js'),
    dest: resolve('packages/vue-server-renderer/server-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-webpack-client-plugin': {
    entry: resolve('server/webpack-plugin/client.js'),
    dest: resolve('packages/vue-server-renderer/client-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  // Weex runtime factory
  'weex-factory': {
    weex: true,
    entry: resolve('weex/entry-runtime-factory.js'),
    dest: resolve('packages/weex-vue-framework/factory.js'),
    format: 'cjs',
    plugins: [weexFactoryPlugin]
  },
  // Weex runtime framework (CommonJS).
  'weex-framework': {
    weex: true,
    entry: resolve('weex/entry-framework.js'),
    dest: resolve('packages/weex-vue-framework/index.js'),
    format: 'cjs'
  },
  // Weex compiler (CommonJS). Used by Weex's Webpack loader.
  'weex-compiler': {
    weex: true,
    entry: resolve('weex/entry-compiler.js'),
    dest: resolve('packages/weex-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
  }
}

上面這部分代碼是vue.js構(gòu)建的配置壤蚜、服務(wù)端渲染webpack插件即寡、weex的打包配置。對(duì)于單個(gè)配置袜刷,遵循了Rollup 的構(gòu)建規(guī)則聪富。配置說(shuō)明:

  • entry屬性:構(gòu)建入口js文件的地址。
  • dest屬性:構(gòu)建完成后的js文件地址
  • format屬性:構(gòu)建文件的格式著蟹。'cjs'表示構(gòu)建出來(lái)的文件遵循 CommonJS 規(guī)范墩蔓;'es' 表示構(gòu)建出來(lái)的文件遵循 ES Module 規(guī)范; 'umd' 表示構(gòu)建出來(lái)的文件遵循 UMD 規(guī)范萧豆。
  • banner屬性:對(duì)vue.js的一個(gè)簡(jiǎn)單的描述奸披。包含作者信息,版本號(hào)等涮雷。

3. 以一個(gè)配置為例探尋構(gòu)建過(guò)程:web-runtime-cjs

'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },

從配置中可見(jiàn):入口的js文件地址阵面,與完成后的js地址,均調(diào)用了resolve() 方法洪鸭。

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

resolve() 方法將傳入的參數(shù) p 調(diào)用split()方法样刷,通過(guò)'/'分割成數(shù)組,然后取第一個(gè)元素設(shè)置為base,那么上述案例中 base即為 web览爵。但是base 并不是真實(shí)路徑颂斜,而是借助了別名的配置。別名配置的代碼如下:scripts/alias

const path = require('path')

const resolve = p => path.resolve(__dirname, '../', p)
// 到真實(shí)文件的一個(gè)映射關(guān)系
module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}

由上述代碼可知:web 對(duì)應(yīng)的知識(shí)路徑是path.resolve(__dirname, '../', 'src/platforms/web')拾枣。由此找到它的入口文件是src/platforms/web/entry-runtime.js它經(jīng)過(guò) Rollup 的構(gòu)建打包后沃疮,最終會(huì)在 dist/vue.runtime.common.js

Runtime Only VS Runtime + Compiler

通常我們利用 vue-cli 去初始化我們的 Vue.js 項(xiàng)目的時(shí)候會(huì)詢問(wèn)是用 Runtime Only 版本的還是 Runtime + Compiler 版本梅肤。他們的區(qū)別如下:

  • Runtime Only 通常需要借助如 webpack 的 vue-loader 工具把 .vue 文件編譯成 JavaScript司蔬,將template 編譯成render 函數(shù)。因?yàn)槭窃诰幾g階段做的姨蝴,所以它只包含運(yùn)行時(shí)的 Vue.js 代碼俊啼,因此代碼體積也會(huì)更輕量。

  • Runtime + Compiler
    我們?nèi)绻麤](méi)有對(duì)代碼做預(yù)編譯左医,但又使用了 Vue 的 template 屬性并傳入一個(gè)字符串授帕,則需要在客戶端編譯模板,如下所示:

// 需要編譯器的版本
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 這種情況不需要
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

綜上:因?yàn)樵?Vue.js 2.0 中浮梢,最終渲染都是通過(guò) render 函數(shù)跛十,如果寫(xiě) template 屬性,則需要編譯成 render函數(shù)秕硝,那么這個(gè)編譯過(guò)程會(huì)發(fā)生運(yùn)行時(shí)芥映,所以需要帶有編譯器的版本。顯然,這個(gè)編譯過(guò)程對(duì)性能會(huì)有一定損耗奈偏,所以推薦使用 Runtime Only 坞嘀。

vue 的入口

當(dāng)我們開(kāi)發(fā)的時(shí)候import Vue from 'vue'到底做了些什么?順著 Runtime Only 構(gòu)建出來(lái)的vue.js 它的入口是在src/platforms/web/entry-runtime.js代碼如下:

/* @flow */

import Vue from './runtime/index'

export default Vue 

上述代碼 導(dǎo)出一個(gè) Vue,而這個(gè)Vue是從./runtime/index導(dǎo)入的惊来。

vue靜態(tài)的全局配置和原型對(duì)象上的方法

繼續(xù)看./runtime/index文件丽涩。代碼如下:

/* @flow */

import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'

import {
 query,
 mustUseProp,
 isReservedTag,
 isReservedAttr,
 getTagNamespace,
 isUnknownElement
} from 'web/util/index'

import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'

// install platform specific utils
// 靜態(tài)的全局配置
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
// 原型__patch__
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
// 定義了原型上的$mount 方法
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && inBrowser ? query(el) : undefined
 return mountComponent(this, el, hydrating)
}

// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
 setTimeout(() => {
   if (config.devtools) {
     if (devtools) {
       devtools.emit('init', Vue)
     } else if (
       process.env.NODE_ENV !== 'production' &&
       process.env.NODE_ENV !== 'test'
     ) {
       console[console.info ? 'info' : 'log'](
         'Download the Vue Devtools extension for a better development experience:\n' +
         'https://github.com/vuejs/vue-devtools'
       )
     }
   }
   if (process.env.NODE_ENV !== 'production' &&
     process.env.NODE_ENV !== 'test' &&
     config.productionTip !== false &&
     typeof console !== 'undefined'
   ) {
     console[console.info ? 'info' : 'log'](
       `You are running Vue in development mode.\n` +
       `Make sure to turn on production mode when deploying for production.\n` +
       `See more tips at https://vuejs.org/guide/deployment.html`
     )
   }
 }, 0)
}

export default Vue

上述代碼還是從core/index文件中導(dǎo)入一個(gè)Vue,最后將其導(dǎo)出裁蚁。在該文件中定義了Vue的一些靜態(tài)的全局配置矢渊,和原型對(duì)象上的方法。

通過(guò)initGlobalAPI 給 vue 添加靜態(tài)方法

繼續(xù)往下看core/index如何定義 Vue 的厘擂,代碼如下:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue) // 定義了vue 本身的靜態(tài)方法

Object.defineProperty(Vue.prototype, '$isServer', {
 get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
 get () {
   /* istanbul ignore next */
   return this.$vnode && this.$vnode.ssrContext
 }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
 value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue

同樣 它是從./instance/index 文件中導(dǎo)入 Vue ,最后將其導(dǎo)出昆淡。該文件通過(guò)initGlobalAPI方法 給 vue 添加靜態(tài)方法。

通過(guò) Mixin 混入往Vue 的原型上添加方法

繼續(xù)往下刽严,到./instance/index 文件昂灵,代碼如下:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'


// 終于溯源結(jié)束了,Vue就是一個(gè)用 Function 實(shí)現(xiàn)的類(lèi),所以才通過(guò) new Vue 去實(shí)例化它舞萄。
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)
}

// 在vue原型上掛了方法
initMixin(Vue) 
stateMixin(Vue) 
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

到此眨补,終于溯源結(jié)束了,Vue就是一個(gè)用 Function 實(shí)現(xiàn)的類(lèi),所以才通過(guò) new Vue 去實(shí)例化它倒脓。該文件中通過(guò) Mixin 混入的方法撑螺,往Vue 的原型上添加了方法。

結(jié)束

最近一段時(shí)間都會(huì)認(rèn)真的去看vue.js的源碼崎弃「饰睿【讀vue 源碼】會(huì)按照一個(gè)系列去更新。分享自己學(xué)習(xí)的同時(shí)饲做,也希望與更多的同行交流所得线婚,如此而已。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盆均,一起剝皮案震驚了整個(gè)濱河市塞弊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泪姨,老刑警劉巖游沿,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肮砾,居然都是意外死亡诀黍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)唇敞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蔗草,“玉大人咒彤,你說(shuō)我怎么就攤上這事疆柔≈渚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵旷档,是天一觀的道長(zhǎng)模叙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鞋屈,這世上最難降的妖魔是什么范咨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮厂庇,結(jié)果婚禮上渠啊,老公的妹妹穿的比我還像新娘。我一直安慰自己权旷,他們只是感情好替蛉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拄氯,像睡著了一般躲查。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上译柏,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天镣煮,我揣著相機(jī)與錄音,去河邊找鬼鄙麦。 笑死典唇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胯府。 我是一名探鬼主播介衔,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盟劫!你這毒婦竟也來(lái)了夜牡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侣签,失蹤者是張志新(化名)和其女友劉穎塘装,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體影所,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹦肴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猴娩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阴幌。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勺阐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出矛双,到底是詐尸還是另有隱情渊抽,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布议忽,位于F島的核電站懒闷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栈幸。R本人自食惡果不足惜愤估,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望速址。 院中可真熱鬧玩焰,春花似錦、人聲如沸芍锚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闹炉。三九已至蒿赢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渣触,已是汗流浹背羡棵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗅钻,地道東北人皂冰。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像养篓,于是被迫代替她去往敵國(guó)和親秃流。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354