이 글은 Vue 소스 코드의 파일 구조와 작동 메커니즘을 주로 소개합니다. 이제 필요한 친구들이 참고할 수 있도록 공유합니다.
vue는 지금은 국내 프론트엔드 웹엔드가 전 세계의 3분의 1을 차지하고 있고, 일상적으로 사용하는 기술 스택 중 하나이기도 하고, 게다가 Vue 소스가 많은 이유도 궁금합니다. 최근 커뮤니티에 코드 읽기 기사가 올라왔습니다. 아래에서 빌려왔습니다. 이번 기회에 모든 분들의 기사와 토론에서 약간의 영양분을 끌어내는 동시에 소스 코드를 읽을 때의 몇 가지 생각을 요약하여 일부 기사를 작성합니다. 제 수준에는 한계가 있으니 토론메세지를 남겨주세요~
#🎜🎜 #Target Vue 버전:2.5.17-beta.0
Vue 소스코드 댓글: https://github.com/SHERlocked...
진술: 기사의 소스 코드 구문 그들은 모두 Flow를 사용하며 소스 코드는 필요에 따라 삭제됩니다(혼란을 피하기 위해@_@ ). 풀버전을 보시려면 위의 github 주소를 입력해주세요. 본 글은 연재글 주소는 하단에 있습니다~
0. ul class=" list-paddingleft-2">
2.5.17-beta.0
vue源码注释:https://github.com/SHERlocked...
声明:文章中源码的语法都使用 Flow,并且源码根据需要都有删节(为了不被迷糊 @_@),如果要看完整版的请进入上面的github地址,本文是系列文章,文章地址见底部~
0. 前备知识
Flow
ES6语法
常用的设计模式
柯里化等函数式编程思想
这里推介几篇前备文章:JS 静态类型检查工具 Flow,ECMAScript 6 入门 - 阮一峰,JS中的柯里化,JS 观察者模式,JS 利用高阶函数实现函数缓存(备忘模式)
1. 文件结构
文件结构在vue的CONTRIBUTING.md中有介绍,这边直接翻译过来:
├── scripts ------------------------------- 包含与构建相关的脚本和配置文件 │ ├── alias.js -------------------------- 源码中使用到的模块导入别名 │ ├── config.js ------------------------- 项目的构建配置 ├── build --------------------------------- 构建相关的文件,一般情况下我们不需要动 ├── dist ---------------------------------- 构建后文件的输出目录 ├── examples ------------------------------ 存放一些使用Vue开发的应用案例 ├── flow ---------------------------------- JS静态类型检查工具[Flow](https://flowtype.org/)的类型声明 ├── package.json ├── test ---------------------------------- 测试文件 ├── src ----------------------------------- 源码目录 │ ├── compiler -------------------------- 编译器代码,用来将 template 编译为 render 函数 │ │ ├── parser ------------------------ 存放将模板字符串转换成元素抽象语法树的代码 │ │ ├── codegen ----------------------- 存放从抽象语法树(AST)生成render函数的代码 │ │ ├── optimizer.js ------------------ 分析静态树,优化vdom渲染 │ ├── core ------------------------------ 存放通用的,平台无关的运行时代码 │ │ ├── observer ---------------------- 响应式实现,包含数据观测的核心代码 │ │ ├── vdom -------------------------- 虚拟DOM的 creation 和 patching 的代码 │ │ ├── instance ---------------------- Vue构造函数与原型相关代码 │ │ ├── global-api -------------------- 给Vue构造函数挂载全局方法(静态方法)或属性的代码 │ │ ├── components -------------------- 包含抽象出来的通用组件,目前只有keep-alive │ ├── server ---------------------------- 服务端渲染(server-side rendering)的相关代码 │ ├── platforms ------------------------- 不同平台特有的相关代码 │ │ ├── weex -------------------------- weex平台支持 │ │ ├── web --------------------------- web平台支持 │ │ │ ├── entry-runtime.js ---------------- 运行时构建的入口 │ │ │ ├── entry-runtime-with-compiler.js -- 独立构建版本的入口 │ │ │ ├── entry-compiler.js --------------- vue-template-compiler 包的入口文件 │ │ │ ├── entry-server-renderer.js -------- vue-server-renderer 包的入口文件 │ ├── sfc ------------------------------- 包含单文件组件.vue文件的解析逻辑,用于vue-template-compiler包 │ ├── shared ---------------------------- 整个代码库通用的代码
几个重要的目录:
compiler:编译,用来将template转化为render函数
core:Vue的核心代码,包括响应式实现、虚拟DOM、Vue实例方法的挂载、全局方法、抽象出来的通用组件等
platform:不同平台的入口文件,主要是 web 平台和 weex 平台的,不同平台有其特殊的构建过程,当然我们的重点是 web 平台
server:服务端渲染(SSR)的相关代码,SSR 主要把组件直接渲染为 HTML 并由 Server 端直接提供给 Client 端
sfc:主要是 .vue 文件解析的逻辑
shared:一些通用的工具方法,有一些是为了增加代码可读性而设置的
其中在platform下src/platforms/web/entry-runtime.js
文件作为运行时构建的入口,ESM方式输出 dist/vue.runtime.esm.js,CJS方式输出 dist/vue.runtime.common.js,UMD方式输出 dist/vue.runtime.js,不包含模板 template 到 render 函数的编译器src/platforms/web/entry-runtime-with-compiler.js
文件作为运行时构建的入口,ESM方式输出 dist/vue.esm.js,CJS方式输出 dist/vue.common.js,UMD方式输出 dist/vue.js,包含compiler
2. 入口文件
任何前端项目都可以从 package.json
文件看起,先来看看它的 script.dev
就是我们运行 npm run dev
的时候它的命令行:
"scripts": { "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev" }
这里的 rollup 是一个类似于 webpack 的JS模块打包器,事实上 Vue - v1.0.10
版本之前用的还是 webpack ,其后改成了 rollup ,如果想知道为什么换成 rollup ,可以看看 尤雨溪本人的回答,总的来说就是为了打出来的包体积小一点,初始化速度快一点。
可以看到这里 rollup 去运行 scripts/config.js
文件,并且给了个参数 TARGET:web-full-dev
,那来看看 scripts/config.js
里面是啥
// scripts/config.js const builds = { '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 // 每个包前面的注释-版本/作者/日期.etc }, }
format 编译方式说明:
es:ES Modules,使用ES6的模板语法输出
cjs:CommonJs Module,遵循CommonJs Module规范的文件输出
amd:AMD Module,遵循AMD Module规范的文件输出
umd:支持外链规范的文件输出,此文件可以直接使用script标签
这里的 web-full-dev
就是对应刚刚我们在命令行里传入的命令,那么 rollup 就会按下面的 entry 入口文件开始去打包,还有其他很多命令和其他各种输出方式和格式可以自行查看一下源码。
因此本文主要的关注点在包含 compiler 编译器的 src/platforms/web/entry-runtime-with-compiler.js
文件,在生产和开发环境中我们使用 vue-loader 来进行 template 的编译从而不需要带 compiler 的包,但是为了更好的理解原理和流程还是推介从带 compiler 的入口文件看起。
先看看这个文件,这里导入了个 Vue ,看看它从哪来的
// src/platforms/web/entry-runtime-with-compiler.js import Vue from './runtime/index'
继续看
// src/platforms/web/runtime/index.js import Vue from 'core/index'
keep moving
// src/core/index.js import Vue from './instance/index'
keep moving*2
// src/core/instance/index.js /* 这里就是vue的构造函数了,不用ES6的Class语法是因为mixin模块划分的方便 */ function Vue(options) { this._init(options) // 初始化方法,位于 initMixin 中 } // 下面的mixin往Vue.prototype上各种挂载 initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default Vue
当我们 new Vue( )
Flow
파일 구조
#🎜🎜#파일 구조는 vue의 CONTRIBUTING.md에 소개되어 있으며 여기에서 직접 번역됩니다: #🎜🎜#// src/core/instance/index.js /* 这里就是Vue的构造函数 */ function Vue(options) { this._init(options) // 初始化方法,位于 initMixin 中 } // 下面的mixin往Vue.prototype上各种挂载,这是在加载的时候已经挂载好的 initMixin(Vue) // 给Vue.prototype添加:_init函数,... stateMixin(Vue) // 给Vue.prototype添加:$data属性, $props属性, $set函数, $delete函数, $watch函数,... eventsMixin(Vue) // 给Vue.prototype添加:$on函数, $once函数, $off函数, $emit函数, $watch方法,... lifecycleMixin(Vue) // 给Vue.prototype添加: _update方法, $forceUpdate函数, $destroy函数,... renderMixin(Vue) // 给Vue.prototype添加: $nextTick函数, _render函数,... export default Vue#🎜🎜#a some 중요한 디렉터리: #🎜🎜#
- #🎜🎜#컴파일러:템플릿을 렌더링 함수로 변환하는 데 사용되는 컴파일#🎜🎜 #
- #🎜🎜#코어:반응형 구현, 가상 DOM, Vue 인스턴스 메서드 마운팅, 전역 메서드, 추상화된 공통 구성 요소 등을 포함한 Vue의 핵심 코드 #🎜🎜#
- #🎜🎜#플랫폼: Strong>다양한 플랫폼의 항목 파일은 주로 웹 플랫폼과 weex 플랫폼용입니다. 플랫폼마다 고유한 구성 프로세스가 있습니다. 물론 우리는 웹 플랫폼에 중점을 두고 있습니다 #🎜🎜#
- #🎜 🎜#서버: 서버측 렌더링(SSR) 관련 코드입니다. SSR은 주로 구성 요소를 HTML로 직접 렌더링하여 서버에서 사용합니다. 클라이언트가 클라이언트에 직접 제공됩니다#🎜🎜# #🎜🎜#sfc:주로 .vue 파일 구문 분석 논리#🎜🎜#
- # 🎜🎜#공유:몇 가지 일반적인 도구 메소드 중 일부는 코드 가독성을 높이기 위해 설정되어 있습니다#🎜🎜#
src/platforms/web/entry-runtime.js
파일은 런타임 빌드의 진입점 역할을 하며, CJS 모드는 dist/vue.runtime.common을 출력하고, UMD 모드는 dist/vue.runtime을 출력합니다. Node.js는 컴파일러src/platforms/web/entry-runtime-with-compiler.js
파일의 렌더링 기능에 템플릿 템플릿을 포함하지 않습니다. 런타임 빌드의 입구로서, ESM 모드는 dist/vue.esm.js를 출력하고, CJS 모드는 dist/vue.common.js를 출력하며, UMD 모드는 컴파일러#🎜🎜#2 입구 파일을 포함하여 dist/vue.js를 출력합니다. h2>#🎜🎜#모든 프런트엔드 프로젝트는 package.json
파일에서 시작할 수 있습니다. 먼저 script.dev
를 살펴보겠습니다. code >npm run dev 해당 명령줄: #🎜🎜#// src/core/instance/index.js
Vue.prototype._init = function(options?: Object) {
const vm: Component = this
initLifecycle(vm) // 初始化生命周期 src/core/instance/lifecycle.js
initEvents(vm) // 初始化事件 src/core/instance/events.js
initRender(vm) // 初始化render src/core/instance/render.js
callHook(vm, 'beforeCreate') // 调用beforeCreate钩子
initInjections(vm) // 初始化注入值 before data/props src/core/instance/inject.js
initState(vm) // 挂载 data/props/methods/watcher/computed
initProvide(vm) // 初始化Provide after data/props
callHook(vm, 'created') // 调用created钩子
if (vm.$options.el) { // $options可以认为是我们传给 `new Vue(options)` 的options
vm.$mount(vm.$options.el) // $mount方法
}
}
여기의 롤업은 실제로 webpack과 유사한 JS 모듈 패키저입니다. Vue - v1.0.10
버전은 webpack을 사용했지만 나중에 롤업으로 변경되었습니다. 왜 롤업으로 변경되었는지 알고 싶다면 You Yuxi의 답변을 읽어보세요. 일반적으로 패키지를 더 작게 만들고 초기화 속도를 조금 더 빠르게 만드는 것입니다. .
#🎜🎜#여기서 롤업이 scripts/config.js
파일을 실행하고 TARGET:web-full-dev
매개변수를 제공하는 것을 볼 수 있습니다. scripts/config.js
#🎜🎜#// src/platform/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount // 把原来的$mount保存下来,位于 src/platform/web/runtime/index.js
Vue.prototype.$mount = function(
el?: string | Element, // 挂载的元素
hydrating?: boolean // 服务端渲染相关参数
): Component {
el = el && query(el)
const options = this.$options
if (!options.render) { // 如果没有定义render方法
let template = options.template
// 把获取到的template通过编译的手段转化为render函数
if (template) {
const { render, staticRenderFns } = compileToFunctions(template, {...}, this)
options.render = render
}
}
return mount.call(this, el, hydrating) // 执行原来的$mount
}
#🎜🎜#format 안에 무엇이 있는지 살펴보세요. 컴파일 방법 설명:
es:ES 모듈, ES6 사용 템플릿 구문 출력
cjs:CommonJs 모듈, CommonJs 모듈 사양을 따르는 파일 출력
amd:AMD 모듈, AMD 모듈을 따르는 파일 출력 사양
umd: 외부 링크 사양 파일 출력을 지원합니다. 이 파일은 web-full-dev
에서 스크립트 태그 #🎜🎜##🎜🎜#를 직접 사용할 수 있습니다. 방금 명령줄에 전달한 명령에 따라 롤업은 아래 항목 항목 파일에 따라 패키징을 시작합니다. 그 밖에도 다양한 명령과 다양한 출력 방법 및 형식이 있으며 소스 코드를 직접 확인할 수 있습니다. #🎜🎜##🎜🎜# 따라서 이 기사의 주요 초점은 프로덕션 및 개발 환경 템플릿을 컴파일하기 위해 vue-loader를 사용하며 컴파일러가 있는 패키지는 필요하지 않습니다. 그러나 원리와 프로세스를 더 잘 이해하려면 컴파일러를 사용하여 항목 파일부터 시작하는 것이 좋습니다. #🎜🎜##🎜🎜#먼저 이 파일을 살펴보겠습니다. Vue는 어디에서 왔는지 확인하기 위해 여기로 가져옵니다. new Vue( )
를 사용하면 실제로 이 생성자를 호출할 수 있으며 여기서 시작할 수 있습니다. #🎜🎜#3. 运行机制
Vue - v1.0.10
버전은 webpack을 사용했지만 나중에 롤업으로 변경되었습니다. 왜 롤업으로 변경되었는지 알고 싶다면 You Yuxi의 답변을 읽어보세요. 일반적으로 패키지를 더 작게 만들고 초기화 속도를 조금 더 빠르게 만드는 것입니다. . 这里我用xmind粗略的画了一张运行机制图,基本上后面的分析都在这张图上面的某些部分了
本文 Vue 实例都是用 vm
来表示
上面这个图可以分为多个部分细加阅读,具体的实现我们在后面的文章中详细讨论,这里先贴一部分源码尝尝鲜
3.1 初始化 _init( )
当我们在 main.js 里 new Vue( )
后,Vue 会调用构造函数的 _init( )
方法,这个方法是位于 core/instance/index.js 的 initMixin( )
方法中定义的
// src/core/instance/index.js /* 这里就是Vue的构造函数 */ function Vue(options) { this._init(options) // 初始化方法,位于 initMixin 中 } // 下面的mixin往Vue.prototype上各种挂载,这是在加载的时候已经挂载好的 initMixin(Vue) // 给Vue.prototype添加:_init函数,... stateMixin(Vue) // 给Vue.prototype添加:$data属性, $props属性, $set函数, $delete函数, $watch函数,... eventsMixin(Vue) // 给Vue.prototype添加:$on函数, $once函数, $off函数, $emit函数, $watch方法,... lifecycleMixin(Vue) // 给Vue.prototype添加: _update方法, $forceUpdate函数, $destroy函数,... renderMixin(Vue) // 给Vue.prototype添加: $nextTick函数, _render函数,... export default Vue
我们可以看看 init( )
这个方法到底进行了哪些初始化:
// src/core/instance/index.js Vue.prototype._init = function(options?: Object) { const vm: Component = this initLifecycle(vm) // 初始化生命周期 src/core/instance/lifecycle.js initEvents(vm) // 初始化事件 src/core/instance/events.js initRender(vm) // 初始化render src/core/instance/render.js callHook(vm, 'beforeCreate') // 调用beforeCreate钩子 initInjections(vm) // 初始化注入值 before data/props src/core/instance/inject.js initState(vm) // 挂载 data/props/methods/watcher/computed initProvide(vm) // 初始化Provide after data/props callHook(vm, 'created') // 调用created钩子 if (vm.$options.el) { // $options可以认为是我们传给 `new Vue(options)` 的options vm.$mount(vm.$options.el) // $mount方法 } }
这里 _init()
方法中会对当前 vm
实例进行一系列初始化设置,比较重要的是初始化 State 的方法 initState(vm)
的时候进行 data/props
的响应式化,这就是传说中的通过 Object.defineProperty()
方法对需要响应式化的对象设置 getter/setter
,以此为基础进行依赖搜集(Dependency Collection),达到数据变化驱动视图变化的目的。
最后检测 vm.$options
上面有没有 el
属性,如果有的话使用 vm.$mount
方法挂载 vm
,形成数据层和视图层的联系。这也是如果没有提供 el
选项就需要自己手动 vm.$mount('#app')
的原因。
我们看到 created
钩子是在挂载 $mount
之前调用的,所以我们在 created
钩子触发之前是无法操作 DOM 的,这是因为还没有渲染到 DOM 上。
3.2 挂载 $mount( )
挂载方法 vm.$mount( )
在多个地方有定义,是根据不同打包方式和平台有关的,src/platform/web/entry-runtime-with-compiler.js
、src/platform/web/runtime/index.js
、src/platform/weex/runtime/index.js
,我们的关注点在第一个文件,但在 entry-runtime-with-compiler.js
文件中会首先把 runtime/index.js
中的 $mount
方法保存下来,并在最后用 call 运行:
// src/platform/web/entry-runtime-with-compiler.js const mount = Vue.prototype.$mount // 把原来的$mount保存下来,位于 src/platform/web/runtime/index.js Vue.prototype.$mount = function( el?: string | Element, // 挂载的元素 hydrating?: boolean // 服务端渲染相关参数 ): Component { el = el && query(el) const options = this.$options if (!options.render) { // 如果没有定义render方法 let template = options.template // 把获取到的template通过编译的手段转化为render函数 if (template) { const { render, staticRenderFns } = compileToFunctions(template, {...}, this) options.render = render } } return mount.call(this, el, hydrating) // 执行原来的$mount }
在 Vue 2.0 版本中,所有 Vue 的组件的渲染最终都需要 render 方法,无论我们是用单文件 .vue 方式开发组件,还是写了 el 或者 template 属性,最终都会转换成 render 方法。这里的 compileToFunctions
就是把 template 编译为 render 的方法,后面会介绍。
// src/platform/weex/runtime/index.js Vue.prototype.$mount = function ( el?: string | Element, // 挂载的元素 hydrating?: boolean // 服务端渲染相关参数 ): Component { el = el && inBrowser ? query(el) : undefined // query就是document.querySelector方法 return mountComponent(this, el, hydrating) // 位于core/instance/lifecycle.js }
这里的 el
一开始如果不是DOM元素的话会被 query 方法换成DOM元素再被传给 mountComponent
方法,我们继续看 mountComponent
的定义:
// src/core/instance/lifecycle.js export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { vm.$el = el if (!vm.$options.render) { vm.$options.render = createEmptyVNode } callHook(vm, 'beforeMount') // 调用beforeMount钩子 // 渲染watcher,当数据更改,updateComponent作为Watcher对象的getter函数,用来依赖收集,并渲染视图 let updateComponent updateComponent = () => { vm._update(vm._render(), hydrating) } // 渲染watcher, Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数 // ,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数 new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted) { callHook(vm, 'beforeUpdate') // 调用beforeUpdate钩子 } } }, true /* isRenderWatcher */) // 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例 if (vm.$vnode == null) { vm._isMounted = true // 表示这个实例已经挂载 callHook(vm, 'mounted') // 调用mounted钩子 } return vm }
在 mountComponent
方法里实例化了一个渲染 Watcher
,并且传入了一个 updateComponent
,这个方法:() => { vm._update(vm._render(), hydrating) }
首先使用 _render
方法生成 VNode
,再调用 _update
方法更新DOM。可以看看视图更新部分的介绍
这里调用了几个钩子,他们的时机可以关注一下。
3.3 编译 compile( )
如果在需要转换 render 的场景下,比如我们写的 template ,将会被 compiler 转换为 render 函数,这其中会有几个步骤组成:
入口位于刚刚 src/platform/web/entry-runtime-with-compiler.js 的 compileToFunctions
方法:
// src/platforms/web/compiler/index.js const { compile, compileToFunctions } = createCompiler(baseOptions) export { compile, compileToFunctions }
继续看这里的 createCompiler
方法:
// src/compiler/index.js export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { const ast = parse(template.trim(), options) if (options.optimize !== false) { optimize(ast, options) } const code = generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })
这里可以看到有三个重要的过程 parse
、optimize
、generate
,之后生成了 render 方法代码。
parse
:会用正则等方式解析 template 模板中的指令、class、style等数据,形成抽象语法树 ASToptimize
:优化AST,生成模板AST树,检测不需要进行DOM改变的静态子树,减少 patch 的压力generate
:把 AST 生成 render 方法的代码
3.4 响应式化 observe( )
Vue作为一个MVVM框架,我们知道它的 Model 层和 View 层之间的桥梁 ViewModel 是做到数据驱动的关键,Vue的响应式是通过 Object.defineProperty 来实现,给被响应式化的对象设置 getter/setter
,当 render 函数被渲染的时候会触发读取响应式化对象的 getter
进行依赖收集,而在修改响应式化对象的时候会触发设置 setter
,setter
方法会 notify
它之前收集到的每一个 watcher
来告诉他们自己的值更新了,从而触发 watcher
的 update
去 patch
更新视图。
响应式化的入口位于 src/core/instance/init.js 的 initState
中:
// src/core/instance/state.js export function initState(vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
它非常规律的定义了几个方法来初始化 props
、methods
、data
、computed
、wathcer
,这里只看 initData
方法,来窥一豹
// src/core/instance/state.js function initData(vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} observe(data, true /* asRootData */) // 给data做响应式处理 }
首先判断了下 data 是不是函数,是则取返回值不是则取自身,之后有一个 observe
方法对 data
进行处理,看看这个方法
// src/core/observer/index.js export function observe (value: any, asRootData: ?boolean): Observer | void { let ob: Observer | void ob = new Observer(value) return ob }
这个方法主要用 data
去实例化一个 Observer 对象实例,Observer 是一个 Class,Observer 的构造函数使用 defineReactive
方法给对象的键响应式化,它给对象的属性递归添加 getter/setter
,用于依赖收集和 notify 更新,这个方法大概是这样的
// src/core/observer/index.js function defineReactive (obj, key, val) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { /* 进行依赖收集 */ return val; }, set: function reactiveSetter (newVal) { if (newVal === val) return; notify(); // 触发更新 } }); }
3.5 视图更新 patch( )
当使用 defineReactive
方法将对象响应式化后,当 render 函数被渲染的时候,会读取响应化对象的 getter
从而触发 getter
进行 watcher
依赖的收集,而在修改响应化对象的值的时候,会触发 setter
通知 notify
之前收集的依赖,通知自己已被修改,请按需重新渲染视图。被通知的 watcher
调用 update
方法去更新视图,位于上面介绍过的传递给 new Watcher( )
的 updateComponent
方法中,这个方法会调用 update
方法去 patch
更新视图。
// src/core/instance/lifecycle.js let updateComponent updateComponent = () => { vm._update(vm._render(), hydrating) } // 渲染watcher, Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数 // ,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数 new Watcher(vm, updateComponent, noop, {...}, true /* isRenderWatcher */)
这个 _render
方法生成虚拟 Node, _update
方法中的会将新的 VNode 与旧的 VNode 一起传入 patch
// src/core/instance/lifecycle.js Vue.prototype._update = function(vnode: VNode, hydrating?: boolean) { // 调用此方法去更新视图 const vm: Component = this const prevVnode = vm._vnode vm._vnode = vnode if (!prevVnode) { // 初始化 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */) } else { //更新 vm.$el = vm.__patch__(prevVnode, vnode) } }
_update
调用 __patch__
方法,它主要是对新老 VNode 进行比较 patchVnode
,经过 diff 算法得出它们的差异,最后这些差异的对应 DOM 进行更新。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
위 내용은 Vue 소스 코드의 파일 구조 및 작동 메커니즘의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

JavaScript는 프론트 엔드 및 백엔드 개발에 사용할 수 있습니다. 프론트 엔드는 DOM 작업을 통해 사용자 경험을 향상시키고 백엔드는 Node.js를 통해 서버 작업을 처리합니다. 1. 프론트 엔드 예 : 웹 페이지 텍스트의 내용을 변경하십시오. 2. 백엔드 예제 : node.js 서버를 만듭니다.

Python 또는 JavaScript는 경력 개발, 학습 곡선 및 생태계를 기반으로해야합니다. 1) 경력 개발 : Python은 데이터 과학 및 백엔드 개발에 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 적합합니다. 2) 학습 곡선 : Python 구문은 간결하며 초보자에게 적합합니다. JavaScript Syntax는 유연합니다. 3) 생태계 : Python에는 풍부한 과학 컴퓨팅 라이브러리가 있으며 JavaScript는 강력한 프론트 엔드 프레임 워크를 가지고 있습니다.

JavaScript 프레임 워크의 힘은 개발 단순화, 사용자 경험 및 응용 프로그램 성능을 향상시키는 데 있습니다. 프레임 워크를 선택할 때 : 1. 프로젝트 규모와 복잡성, 2. 팀 경험, 3. 생태계 및 커뮤니티 지원.

서론 나는 당신이 이상하다는 것을 알고 있습니다. JavaScript, C 및 Browser는 정확히 무엇을해야합니까? 그들은 관련이없는 것처럼 보이지만 실제로는 현대 웹 개발에서 매우 중요한 역할을합니다. 오늘 우리는이 세 가지 사이의 밀접한 관계에 대해 논의 할 것입니다. 이 기사를 통해 브라우저에서 JavaScript가 어떻게 실행되는지, 브라우저 엔진의 C 역할 및 웹 페이지의 렌더링 및 상호 작용을 유도하기 위해 함께 작동하는 방법을 알게됩니다. 우리는 모두 JavaScript와 브라우저의 관계를 알고 있습니다. JavaScript는 프론트 엔드 개발의 핵심 언어입니다. 브라우저에서 직접 실행되므로 웹 페이지를 생생하고 흥미롭게 만듭니다. 왜 Javascr

Node.js는 크림 덕분에 효율적인 I/O에서 탁월합니다. 스트림은 메모리 오버로드를 피하고 큰 파일, 네트워크 작업 및 실시간 애플리케이션을위한 메모리 과부하를 피하기 위해 데이터를 점차적으로 처리합니다. 스트림을 TypeScript의 유형 안전과 결합하면 Powe가 생성됩니다

파이썬과 자바 스크립트 간의 성능과 효율성의 차이는 주로 다음과 같이 반영됩니다. 1) 해석 된 언어로서, 파이썬은 느리게 실행되지만 개발 효율이 높고 빠른 프로토 타입 개발에 적합합니다. 2) JavaScript는 브라우저의 단일 스레드로 제한되지만 멀티 스레딩 및 비동기 I/O는 Node.js의 성능을 향상시키는 데 사용될 수 있으며 실제 프로젝트에서는 이점이 있습니다.

JavaScript는 1995 년에 시작하여 Brandon Ike에 의해 만들어졌으며 언어를 C로 실현했습니다. 1.C Language는 JavaScript의 고성능 및 시스템 수준 프로그래밍 기능을 제공합니다. 2. JavaScript의 메모리 관리 및 성능 최적화는 C 언어에 의존합니다. 3. C 언어의 크로스 플랫폼 기능은 자바 스크립트가 다른 운영 체제에서 효율적으로 실행하는 데 도움이됩니다.

JavaScript는 브라우저 및 Node.js 환경에서 실행되며 JavaScript 엔진을 사용하여 코드를 구문 분석하고 실행합니다. 1) 구문 분석 단계에서 초록 구문 트리 (AST)를 생성합니다. 2) 컴파일 단계에서 AST를 바이트 코드 또는 기계 코드로 변환합니다. 3) 실행 단계에서 컴파일 된 코드를 실행하십시오.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

드림위버 CS6
시각적 웹 개발 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.