この記事は主に Vue ソースコードのファイル構造と動作メカニズムを紹介します。これは、必要な友人に参照してもらうために共有します。現在、Vue は国内のフロントエンドの 3 分の 1 です。これは私が日常的に使用している主要なテクノロジ スタックの 1 つでもあり、最近コミュニティに Vue のソース コードを読む記事が多数登場しています。この機会に皆さんの記事やディスカッションから学び、ソースコードを読んで考えたことをまとめ、自分の考えをまとめた記事も作成しましたので、ディスカッション用にメッセージを残してください。 ~
ターゲット Vue バージョン: 2.5.17-beta.0
Vue ソース コード コメント: https://github.com/SHERlocked...
ステートメント: ソース記事内のコード構文は Flow を使用しており、ソース コードは必要に応じて省略されています (混乱しないように@_@)。完全版をご覧になりたい場合は、上記の github アドレスを入力してください。この記事は一連の記事です。記事は下部の記事アドレスを参照してください~
0. 予備知識
-
Flow
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,包含compiler2. 入口文件
任何前端项目都可以从
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( )
- ES6 の構文🎜
- 🎜よく使われるデザインパターン🎜
- 🎜思考のカリー化などの関数型プログラミング🎜
1. ファイル構造
🎜 ファイル構造は 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🎜いくつかの重要なディレクトリ:🎜
src/platforms/web/entry-runtime.js
ファイルをダウンロードします。出力 dist/vue.runtime.esm.js、CJS モード出力 dist/vue.runtime.common.js、UMD モード出力 dist/vue.runtime.js には、テンプレートからレンダリング関数までのコンパイラは含まれませんsrc/platforms/web/entry-runtime-with-compiler.js
このファイルは、ESM モードの出力 dist/vue.esm.js、CJS モードの出力 dist/ への入り口として機能します。 vue.common.js、UMD モード出力 dist/vue.js (コンパイラを含む)🎜2. エントリ ファイル
🎜どのフロントエンド プロジェクトもpackage.json
から開始できます。まず、その script.dev
を見てみましょう。つまり、 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方法 } }
ここでのロールアップは JS です。 webpack に似たモジュール パッケージャー。実際、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 }🎜形式 コンパイル方法の説明:
es:ES モジュール、ES6 テンプレート構文出力を使用
cjs:CommonJs モジュール、CommonJs モジュール仕様に準拠したファイル出力
amd:AMD モジュール、AMD モジュール仕様に準拠したファイル出力
umd: 外部リンク仕様のファイル出力をサポートします。このファイルはスクリプト タグを直接使用できます🎜🎜ここでの
web-full-dev
は、コマンド ラインで渡したコマンドに対応します。 rollup 以下のエントリーファイルに従ってパッケージ化を開始します。他にも多数のコマンドや出力方法があり、ソースコードを自分で確認することができます。 🎜🎜したがって、この記事の主な焦点は、コンパイラーを含む src/platforms/web/entry-runtime-with-compiler.js
ファイルです。実稼働環境および開発環境では、vue-loader を使用して、テンプレートのコンパイルにはコンパイラを備えたパッケージは必要ありませんが、原理とプロセスをよりよく理解するために、コンパイラを備えたエントリ ファイルから始めることをお勧めします。 🎜🎜まずこのファイルを見てみましょう。Vue がどこから来たのかを確認するためにここにインポートされています🎜// 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 }🎜引き続き見てください🎜
// 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 }🎜動き続けてください🎜
// src/platforms/web/compiler/index.js const { compile, compileToFunctions } = createCompiler(baseOptions) export { compile, compileToFunctions }🎜動き続けてください*2🎜
// 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 } })🎜
new Vue( )
、このコンストラクターは実際に呼び出されます。ここから読み取りを開始できます。 🎜3. 运行机制
这里我用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 中国語 Web サイトの他の関連記事を参照してください。

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

現実世界におけるJavaScriptのアプリケーションには、サーバー側のプログラミング、モバイルアプリケーション開発、モノのインターネット制御が含まれます。 2。モバイルアプリケーションの開発は、ReactNativeを通じて実行され、クロスプラットフォームの展開をサポートします。 3.ハードウェアの相互作用に適したJohnny-Fiveライブラリを介したIoTデバイス制御に使用されます。

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません
