Vue源码学习 - 准备工作
Vue源码学习 - 准备工作
- 准备工作
- 认识Flow
- 为什么用 Flow
- Flow 的工作方式
- 类型推断
- 类型注释
- 数组
- 类和对象
- null
- Flow 在 Vue.js 源码中的应用
- flow实践
- 总结
- Vue.js 源码目录设计
- compiler
- core
- platform
- server
- sfc
- shared
- 总结
- Vue.js 源码构建
- 构建脚本
- 构建过程
- Runtime Only VS Runtime + Compiler
- Runtime Only
- Runtime + Compiler
- 总结
- 从入口开始
- Vue的入口
- Vue 的定义
- initGlobalAPI
- 总结
学习内容和文章内容来自 黄轶老师
黄轶老师的慕课网视频教程地址:
《Vue.js2.0 源码揭秘》、
黄轶老师拉钩教育教程地址:
《Vue.js 3.0 核心源码解析》
这里分析的源码是Runtime + Compiler 的 Vue.js
调试代码在:node_modules\vue\dist\vue.esm.js 里添加
vue版本:Vue.js 2.5.17-beta
你越是认真生活,你的生活就会越美好
——弗兰克·劳埃德·莱特
《人生果实》经典语录
点击回到 Vue源码学习完整目录
准备工作
认识Flow
Flow 是 facebook 出品的 JavaScript 静态类型检查工具。Vue.js 的源码利用了 Flow 做了静态类型检查,所以了解 Flow 有助于我们阅读源码。
为什么用 Flow
JavaScript 是动态类型语言
,它的灵活性有目共睹,但是过于灵活的副作用是很容易就写出非常隐蔽的隐患代码,在编译期甚至看上去都不会报错,但在运行阶段就可能出现各种奇怪的 bug。
类型检查
是当前动态类型语言的发展趋势,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写 JavaScript 具有和编写 Java 等强类型语言相近的体验。
项目越复杂就越需要通过工具的手段来保证项目的维护性和增强代码的可读性。
Vue.js 在做 2.0 重构的时候,在 ES2015 的基础上,除了 ESLint 保证代码风格之外,也引入了 Flow 做静态类型检查。之所以选择 Flow,主要是因为 Babel 和 ESLint 都有对应的 Flow 插件以支持语法,可以完全沿用现有的构建配置,非常小成本的改动就可以拥有静态类型检查的能力。
Flow 的工作方式
通常类型检查分成 2 种方式:
类型推断
:通过变量的使用上下文来推断出变量类型,然后根据这些推断来检查类型。类型注释
:事先注释好我们期待的类型,Flow 会基于这些注释来判断。
类型推断
它不需要任何代码修改即可进行类型检查
,最小化开发者的工作量。它不会强制你改变开发习惯,因为它会自动推断出变量的类型。这就是所谓的类型推断,Flow 最重要的特性之一。
通过一个简单例子说明一下:
/*@flow*/function split(str) {return str.split(' ')
}split(11)
Flow 检查上述代码后会报错,因为函数 split 期待的参数是字符串,而我们输入了数字。
类型注释
如上所述,类型推断是 Flow 最有用的特性之一
,不需要编写类型注释就能获取有用的反馈。但在某些特定的场景下,添加类型注释可以提供更好更明确的检查依据。
考虑如下代码:
/*@flow*/function add(x, y){return x + y
}add('Hello', 11)
Flow 检查上述代码时检查不出任何错误,因为从语法层面考虑, + 既可以用在字符串上,也可以用在数字上,我们并没有明确指出 add() 的参数必须为数字。
在这种情况下,我们可以借助类型注释来指明期望的类型。类型注释是以冒号 : 开头
,可以在函数参数
,返回值
,变量声明
中使用。
如果我们在上段代码中添加类型注释,就会变成如下:
/*@flow*/function add(x:number, y:number):number {return x + y
}add('Hello', 11)
现在 Flow 就能检查出错误,因为函数参数的期待类型为数字
,而我们提供了字符串。
上面的例子是针对函数的类型注释。接下来我们来看看 Flow 能支持的一些常见的类型注释。
数组
/*@flow*/var arr: Array<number> = [1, 2, 3]arr.push('hello')
数组类型注释的格式是Array<T>
,T
表示数组中每项的数据类型
。
在上述代码中,arr 是每项均为数字的数组。如果我们给这个数组添加了一个字符串,Flow 能检查出错误。
类和对象
/*@flow*/class Bar {x: string; // x 是字符串y: string | number; // y 可以是字符串或者数字z: boolean;constructor(x: string, y: string | number) {this.x = xthis.y = ythis.z = false}
}var bar: Bar = new Bar('hello', 4)var obj: {a: string,b: number,c: Array<string>,d: Bar
} = {a: 'hello',b: 11,c: ['hello', 'world'],d: new Bar('hello', 3)
}
类的类型注释格式
如上,可以对类自身的属性
做类型检查,也可以对构造函数的参数
做类型检查。这里需要注意的是,属性 y 的类型中间用 | 做间隔
,表示 y 的类型即可以是字符串也可以是数字。
对象的注释类型类似于类,需要指定对象属性的类型。
null
若想任意类型 T
可以为 null 或者 undefined,只需类似如下写成 ?T
的格式即可。
/*@flow*/var foo: ?string = null
此时,foo 可以为字符串
,也可以为 null
。
目前我们只列举了 Flow 的一些常见的类型注释。如果想了解所有类型注释,请移步 Flow 的官方文档
Flow 在 Vue.js 源码中的应用
有时候我们想引用第三方库,或者自定义一些类型,但 Flow 并不认识,因此检查的时候会报错。为了解决这类问题,Flow 提出了一个 libdef 的概念,可以用来识别这些第三方库或者是自定义类型,而 Vue.js 也利用了这一特性。
在 Vue.js 的主目录下有 .flowconfig 文件
, 它是mermaid flowchat 的配置文件
,感兴趣的同学可以看官方文档。这其中的 [libs] 部分用来描述包含指定库定义的目录,默认是名为 flow-typed 的目录。
这里 [libs] 配置的是 flow,表示指定的库定义都在 flow 文件夹内。我们打开这个目录,会发现文件如下:
flow
├── compiler.js # 编译相关
├── component.js # 组件数据结构
├── global-api.js # Global API 结构
├── modules.js # 第三方库定义
├── options.js # 选项相关
├── ssr.js # 服务端渲染相关
├── vnode.js # 虚拟 node 相关
可以看到,Vue.js 有很多自定义类型的定义,在阅读源码的时候,如果遇到某个类型并想了解它完整的数据结构的时候,可以回来翻阅这些数据结构的定义。
flow实践
安装flow
npm install -g flow-bin
安装好了后,在对应目录下执行flow init
flow init
执行完之后会生成.flowconfig文件
// test.js/*@flow*/
// 上面一行 说明这个文件需要flow去检查 否则会跳过该文件
function split(str) {return str.split(" ");
}split(11);
命令行执行flow时检查
flow
总结
通过对 Flow 的认识,有助于我们阅读 Vue 的源码,并且这种静态类型检查的方式非常有利于大型项目源码的开发和维护。类似 Flow 的工具还有如 TypeScript
,感兴趣的同学也可以自行去了解一下。
点击回到 Vue源码学习完整目录
Vue.js 源码目录设计
Vue.js 的源码都在 src 目录下,其目录结构如下。
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 不同平台的支持
├── server # 服务端渲染
├── sfc # .vue 文件解析
├── shared # 共享代码
compiler
compiler 目录包含 Vue.js 所有编译相关的代码。它包括把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能。
编译的工作可以在构建时做(借助 webpack、vue-loader 等辅助插件);也可以在运行时做,使用包含构建功能的 Vue.js。显然,编译是一项耗性能的工作,所以更推荐前者——离线编译。
core
core 目录包含了 Vue.js 的核心代码,包括内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等等。
这里的代码可谓是 Vue.js 的灵魂
platform
Vue.js 是一个跨平台的 MVVM 框架,它可以跑在 web 上,也可以配合 weex 跑在 native 客户端上。platform 是 Vue.js 的入口,2 个目录代表 2 个主要入口,分别打包成运行在 web 上和 weex 上的 Vue.js。
这边博客重点分析 web 入口打包后的 Vue.js
server
Vue.js 2.0 支持了服务端渲染,所有服务端渲染相关的逻辑都在这个目录下。注意:这部分代码是跑在服务端的 Node.js,不要和跑在浏览器端的 Vue.js 混为一谈。
服务端渲染主要的工作是把组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。
sfc
通常我们开发 Vue.js 都会借助 webpack 构建, 然后通过 .vue 单文件来编写组件。
这个目录下的代码逻辑会把 .vue 文件内容解析成一个 JavaScript 的对象。
shared
Vue.js 会定义一些工具方法,这里定义的工具方法都是会被浏览器端的 Vue.js 和服务端的 Vue.js 所共享的。
总结
从 Vue.js 的目录设计可以看到,作者把功能模块拆分的非常清楚,相关的逻辑放在一个独立的目录下维护,并且把复用的代码也抽成一个独立目录。
这样的目录设计让代码的阅读性和可维护性都变强,非常值得学习和推敲。
点击回到 Vue源码学习完整目录
Vue.js 源码构建
Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下。
构建脚本
通常一个基于 NPM 托管的项目
都会有一个package.json
文件,它是对项目的描述文件,它的内容实际上是一个标准的 JSON 对象。
我们通常会配置script
字段作为 NPM 的执行脚本
,Vue.js 源码构建的脚本如下:
{"script": {"build": "node scripts/build.js","build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer","build:weex": "npm run build -- weex"}
}
这里总共有 3 条命令,作用都是构建 Vue.js,后面 2 条是在第一条命令的基础上,添加一些环境参数。
当在命令行运行 npm run build
的时候,实际上就会执行node scripts/build.js
,接下来我们来看看它实际是怎么构建的。
构建过程
我们对于构建过程分析是基于源码的,先打开构建的入口 JS 文件,在 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 defaultbuilds = builds.filter(b => {return b.output.file.indexOf('weex') === -1})
}build(builds)
这段代码逻辑非常简单,先从配置文件读取配置,再通过命令行参数对构建配置做过滤,这样就可以构建出不同用途的 Vue.js 了。接下来我们看一下配置文件,在 scripts/config.js 中:
const builds = {// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify'web-runtime-cjs': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.common.js'),format: 'cjs',banner},// Runtime+compiler CommonJS build (CommonJS)'web-full-cjs': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.common.js'),format: 'cjs',alias: { he: './entity-decoder' },banner},// Runtime only (ES Modules). Used by bundlers that support ES Modules,// e.g. Rollup & Webpack 2'web-runtime-esm': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.esm.js'),format: 'es',banner},// Runtime+compiler CommonJS build (ES Modules)'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-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},// ...
}
这里列举了一些 Vue.js 构建的配置,关于还有一些服务端渲染 webpack 插件以及 weex 的打包配置就不列举了。
对于单个配置,它是遵循 Rollup 的构建规则的。其中
entry
属性表示构建的入口 JS 文件地址
,
dest
属性表示构建后的 JS 文件地址
。
format
属性表示构建的格式,
cjs
表示构建出来的文件遵循 CommonJS 规范,
es
表示构建出来的文件遵循 ES Module 规范。
umd
表示构建出来的文件遵循 UMD 规范
以web-runtime-cjs
配置为例,它的 entry
是 resolve('web/entry-runtime.js')
,先来看一下resolve
函数的定义。
源码目录:scripts/config.js
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 函数实现非常简单,它先把 resolve 函数传入的参数 p 通过 / 做了分割成数组,然后取数组第一个元素设置为 base。在我们这个例子中,参数 p 是 web/entry-runtime.js
,那么 base 则为 web。base 并不是实际的路径,它的真实路径借助了别名的配置,我们来看一下别名配置的代码,在 scripts/alias 中:
const path = require('path')module.exports = {vue: path.resolve(__dirname, '../src/platforms/web/entry-runtime-with-compiler'),compiler: path.resolve(__dirname, '../src/compiler'),core: path.resolve(__dirname, '../src/core'),shared: path.resolve(__dirname, '../src/shared'),web: path.resolve(__dirname, '../src/platforms/web'),weex: path.resolve(__dirname, '../src/platforms/weex'),server: path.resolve(__dirname, '../src/server'),entries: path.resolve(__dirname, '../src/entries'),sfc: path.resolve(__dirname, '../src/sfc')
}
很显然,这里 web 对应的真实的路径是path.resolve(__dirname, '../src/platforms/web')
,这个路径就找到了 Vue.js 源码的 web 目录。然后 resolve 函数通过 path.resolve(aliases[base], p.slice(base.length + 1)) 找到了最终路径,它就是Vue.js 源码 web 目录下的 entry-runtime.js
。因此,web-runtime-cjs
配置对应的入口文件
就找到了。
它经过 Rollup
的构建打包后,最终会在 dist 目录
下生成 vue.runtime.common.js
Runtime Only VS Runtime + Compiler
通常我们利用 vue-cli 去初始化我们的 Vue.js 项目的时候会询问我们用 Runtime Only 版本的还是 Runtime + Compiler 版本。下面我们来对比这两个版本
Runtime Only
我们在使用 Runtime Only 版本的 Vue.js 的时候,通常需要借助如 webpack 的 vue-loader 工具把 .vue 文件编译成 JavaScript,因为是在编译阶段做的,所以它只包含运行时的 Vue.js 代码,因此代码体积也会更轻量。
Runtime + Compiler
我们如果没有对代码做预编译,但又使用了 Vue 的 template 属性并传入一个字符串,则需要在客户端编译模板,如下所示:
// 需要编译器的版本
new Vue({template: '<div>{{ hi }}</div>'
})// 这种情况不需要
new Vue({render (h) {return h('div', this.hi)}
})
因为在 Vue.js 2.0 中,最终渲染
都是通过render 函数
,如果写 template 属性,则需要编译成 render 函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本。
很显然,这个编译过程对性能会有一定损耗,所以通常我们更推荐使用 Runtime-Only 的 Vue.js。
总结
通过这一节的分析,我们可以了解到 Vue.js 的构建打包过程,也知道了不同作用和功能的 Vue.js 它们对应的入口以及最终编译生成的 JS 文件。尽管在实际开发过程中我们会用 Runtime Only 版本开发比较多,但为了分析 Vue 的编译过程,我们这门课重点分析的源码是 Runtime + Compiler 的 Vue.js。
点击回到 Vue源码学习完整目录
从入口开始
我们之前提到过 Vue.js 构建过程,在 web 应用下
,我们来分析 Runtime + Compiler
构建出来的 Vue.js,它的入口是 src/platforms/web/entry-runtime-with-compiler.js
:
src/platforms/web/entry-runtime-with-compiler.js:
/* @flow */import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'const idToTemplate = cached(id => {const el = query(id)return el && el.innerHTML
})const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean
): Component {el = el && query(el)/* istanbul ignore if */if (el === document.body || el === document.documentElement) {process.env.NODE_ENV !== 'production' && warn(`Do not mount Vue to <html> or <body> - mount to normal elements instead.`)return this}const options = this.$options// resolve template/el and convert to render functionif (!options.render) {let template = options.templateif (template) {if (typeof template === 'string') {if (template.charAt(0) === '#') {template = idToTemplate(template)/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && !template) {warn(`Template element not found or is empty: ${options.template}`,this)}}} else if (template.nodeType) {template = template.innerHTML} else {if (process.env.NODE_ENV !== 'production') {warn('invalid template option:' + template, this)}return this}} else if (el) {template = getOuterHTML(el)}if (template) {/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {mark('compile')}const { render, staticRenderFns } = compileToFunctions(template, {outputSourceRange: process.env.NODE_ENV !== 'production',shouldDecodeNewlines,shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this)options.render = renderoptions.staticRenderFns = staticRenderFns/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {mark('compile end')measure(`vue ${this._name} compile`, 'compile', 'compile end')}}}return mount.call(this, el, hydrating)
}/*** Get outerHTML of elements, taking care* of SVG elements in IE as well.*/
function getOuterHTML (el: Element): string {if (el.outerHTML) {return el.outerHTML} else {const container = document.createElement('div')container.appendChild(el.cloneNode(true))return container.innerHTML}
}Vue.compile = compileToFunctionsexport default Vue
那么,当我们的代码执行import Vue from 'vue'
的时候,就是从这个入口执行代码来初始化 Vue
, 那么 Vue 到底是什么,它是怎么初始化的,我们来一探究竟。
Vue的入口
在这个入口 JS 的上方我们可以找到 Vue 的来源:import Vue from './runtime/index'
,我们先来看一下这块儿的实现,它定义在 src/platforms/web/runtime/index.js
中
src/platforms/web/runtime/index.js
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, isChrome } 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
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
Vue.prototype.__patch__ = inBrowser ? patch : noop// public mount method
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean
): Component {el = el && inBrowser ? query(el) : undefinedreturn mountComponent(this, el, hydrating)
}// ...export default Vue
这里关键的代码是 import Vue from 'core/index'
,之后的逻辑都是对 Vue 这个对象做一些扩展,可以先不用看,我们来看一下真正初始化 Vue 的地方,在 src/core/index.js 中:
src/core/index.js
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)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
这里有 2 处关键的代码,import Vue from './instance/index' 和 initGlobalAPI(Vue),初始化全局 Vue API
(我们稍后介绍),
我们先来看第一部分,在 src/core/instance/index.js
中:
Vue 的定义
src/core/instance/index.js
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'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)
}initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)export default Vue
在这里,我们终于看到了Vue 的庐山真面目
,它实际上就是一个用 Function 实现的类
,我们只能通过 new Vue 去实例化
它。
有些同学看到这不禁想问,为何 Vue 不用 ES6 的 Class 去实现呢?我们往后看这里有很多 xxxMixin 的函数调用
,并把 Vue 当参数传入
,它们的功能都是给 Vue 的 prototype 上扩展一些方法
(这里具体的细节会在之后的文章介绍,这里不展开),Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。这么做的好处是非常方便代码的维护和管理,这种编程技巧也非常值得我们去学习。
initGlobalAPI
Vue.js 在整个初始化过程中,除了给它的原型 prototype
上扩展方法,还会给 Vue 这个对象本身扩展全局的静态方法
,它的定义在src/core/global-api/index.js
中:
src/core/global-api/index.js
export function initGlobalAPI (Vue: GlobalAPI) {// configconst configDef = {}configDef.get = () => configif (process.env.NODE_ENV !== 'production') {configDef.set = () => {warn('Do not replace the Vue.config object, set individual fields instead.')}}Object.defineProperty(Vue, 'config', configDef)// exposed util methods.// NOTE: these are not considered part of the public API - avoid relying on// them unless you are aware of the risk.Vue.util = {warn,extend,mergeOptions,defineReactive}Vue.set = setVue.delete = delVue.nextTick = nextTickVue.options = Object.create(null)ASSET_TYPES.forEach(type => {Vue.options[type + 's'] = Object.create(null)})// this is used to identify the "base" constructor to extend all plain-object// components with in Weex's multi-instance scenarios.Vue.options._base = Vueextend(Vue.options.components, builtInComponents)initUse(Vue)initMixin(Vue)initExtend(Vue)initAssetRegisters(Vue)
}
这里就是在 Vue 上扩展的一些全局方法的定义,Vue 官网中关于全局 API 都可以在这里找到,这里不会介绍细节
,会在之后的章节我们具体介绍到某个 API 的时候会详细介绍。有一点要注意的是,Vue.util 暴露的方法最好不要依赖,因为它可能经常会发生变化,是不稳定的。
总结
那么至此,Vue 的初始化过程基本介绍完毕。这一节的目的是让同学们对 Vue 是什么有一个直观的认识,
它本质上就是一个用 Function 实现的 Class
,
然后它的原型 prototype 以及它本身都扩展了一系列的方法和属性
,
那么 Vue 能做什么,它是怎么做的,我们会在后面的章节一层层帮大家揭开 Vue 的神秘面纱。
点击回到 Vue源码学习完整目录
谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起变得更强
Vue源码学习 - 准备工作相关推荐
- VUE源码学习第一篇--前言
一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...
- vue源码学习--vue源码学习入门
本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...
- Vue源码学习之Computed与Watcher原理
前言 computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...
- Vue源码学习 - 组件化(三) 合并配置
Vue源码学习 - 组件化(三) 合并配置 合并配置 外部调用场景 组件场景 总结 学习内容和文章内容来自 黄轶老师 黄轶老师的慕课网视频教程地址:<Vue.js2.0 源码揭秘>. 黄轶 ...
- Vue源码学习 - 组件化一 createComponent
Vue源码学习 - 组件化一 createComponent 组件化 createComponent 构造子类构造函数 安装组件钩子函数 实例化 VNode 总结 学习内容和文章内容来自 黄轶老师 黄 ...
- Vue源码学习之initInjections和initProvide
Vue源码学习之initInjections和initProvide 在进行源码阅读之前先让我们了解一个概念:provide/inject,这个是Vue在2.2.0版本新增的一个属性,按照Vue官网的 ...
- vue实例没有挂载到html上,vue 源码学习 - 实例挂载
前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...
- Vue源码学习(一):源码的入口在哪里
Vue源码解读系列 文章目录 Vue源码解读系列 前言 一.源码下载 二.目录解读 三.找到打包入口文件 四.如何进行代码调试 总结 前言 如何设计API和如何使用元编程思想(元编程,简单说是指框 ...
- Vue源码学习: 关于对Array的数据侦听
摘要 我们都知道Vue的响应式是通过Object.defineProperty来进行数据劫持.但是那是针对Object类型可以实现, 如果是数组呢? 通过set/get方式是不行的. 但是Vue作者使 ...
最新文章
- vue路由history模式,nginx配置
- 使用datatable 将测试数据与业务分离
- [转载]Eclipse SVN插件比较 Subclipse vs Subversive
- LCA 在线倍增法 求最近公共祖先
- 【读书笔记】基础博弈知识小结
- 在Windows XP系统登录Active Directory域
- Flex里监听mouseDownOutside事件解决弹出窗口点击空白关闭功能
- 【模拟】Biotech
- 数据库原理及应用实验四 简单查询
- STM32库函数模板创建
- thinkphp5项目--个人博客(五)
- 电阻单位符号Ω怎么读?
- 全球及中国再生纸包装行业研究及十四五规划分析报告
- 伟大的micropython smartconfig 配网它来了!!!
- 并查集(Union-Find) (图文详解)
- CreateJS入门 -- 注释详细到爆炸(My Style)
- Export metadata for 'Type' is missing and no defau
- java conversion_java.util.UnknownFormatConversionException: Conversion = ''';
- How to reduce bias and variance ?
- 数值计算高斯求积分的解法