Эта статья надеется помочь людям, которые хотят съесть торт, но чувствуют, что торт слишком большой, и не знают, где его съесть.
1. Как начать первый шаг
- исходный проект
clone
Спустившись вниз, следуйтеCONTRIBUTINGсерединаDevelopment Setup
в последовательности, выполняемой один за другим
$ npm install
# watch and auto re-build dist/vue.js
$ npm run dev
- Научитесь смотреть на файл package.json так, как если бы вы использовали MVVM для просмотра его рендеринга.
теперь, когда$ npm run dev
команда может быть перекомпилированаvue.js
файл, то мы начинаем сscripts
серединаdev
Начать просмотр.
"dev":"rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
Если вам не ясно здесь
rollup
Что это делаеткликните сюдаКороче говоря, это модульный инструмент упаковки. Конкретное введение здесь пропущено, потому что мы смотрим на vue. Если он слишком нервный, в основном игнорируйте главное, что я хочу сделать на этот раз. Jump Jump Jump не обязательно прыгает куда, поэтому я читаю исходный код. В это время мы должны помнить, какова наша цель на этот раз.
Обратите внимание на два ключевых слова в приведенной выше команде.scripts/config.js
иweb-full-dev
, Посмотримscript/config.js
этот файл.
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
Вспоминая приведенную выше команду, мы передалиTARGET
даweb-full-dev
, то занесите его в метод, вы в итоге увидите вот такойobject
'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
},
Хотя мы не знаем, что он здесь делает, давайте аннотируем его семантикой. Теперь, когда у нас есть входной файл, мы продолжаем открывать файлweb/entry-runtime-with-compiler.js
. ОК, после открытия этого файла я наконец вижу одно из наших целевых ключевых слов.
import Vue from './runtime/index'
Jianghu правил, продолжайте прыгать в этот файл, и тогда вы увидите:
import Vue from 'core/index'
Вы снова увидели знакомые ключевые слова в первой строке кода?Vue
import Vue from './instance/index'
Открытымinstance/index
Закончив наш первый шаг, мы нашли файл из package.json в фреймворк и нашлиVue
место определения. Давайте еще раз рассмотрим процесс:
2. Научитесь использовать демо
Помните, при просмотре исходного кода, чтобы он не сбился с пути, мы должны смотреть на порядок, в котором код выполняется.
-
Структура проекта имеет
examples
Каталог, давайте также создадим в нем нашу собственную демонстрацию, просто скопируйте каталог и назовите его demo, а затем наш код будет протестирован и просмотрен в этом демо.Содержимое index.html выглядит следующим образом:
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="../../dist/vue.js"></script> </head> <body> <div id="demo"> <template> <span>{{text}}</span> </template> </div> <script src="app.js"></script> </body> </html>
Содержимое файла app.js выглядит следующим образом:
var demo = new Vue({ el: '#demo', data() { return { text: 'hello world!' } } })
Представьте vue.js
В html демо выше мы ввели dist/vue.js, тогда под окном будетVue
Object, на данный момент измените код app.js следующим образом:
console.dir(Vue);
Если вы еще не знаете здесь
console.dir
, и знать толькоconsole.log
, то попробуйте сами и запомните отличия.
Из консоли мы видим, чтоVue
Объекты и прототипы имеют ряд свойств, поэтому, откуда берутся эти свойства и что они делают, мы подробно рассмотрим позже.
3. Откуда он взялся
Помните, когда мы нашли конец в главе 1Vue
Файл конструктора? Если не помните, вернитесь и посмотрите, в этой главе мы пройдемся по ним в обратном порядке.Vue
крепление атрибута.
instance(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
Далее начнем следить за порядком выполнения кода, посмотрим, что делают эти функции.
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
-
initMixin(src/core/instance/init.js)
Vue.prototype._init = function (options?: Object) {}
во входящем
Vue
Прототип объекта смонтирован_init
метод. -
stateMixin(src/core/instance/state.js)
// Object.defineProperty(Vue.prototype, '$data', dataDef) // 这里$data只提供了get方法,set方法再非生产环境时会给予警告 Vue.prototype.$data = undefined; // Object.defineProperty(Vue.prototype, '$props', propsDef) // 这里$props只提供了get方法,set方法再非生产环境时会给予警告 Vue.prototype.$props = undefined; Vue.prototype.$set = set Vue.prototype.$delete = del Vue.prototype.$watch = function() {}
Если вы еще не знаете здесь
Object.defineProperty
Что он делает? Мое предложение для вас, что вы можете хорошенько взглянуть на прототип объекта. Это значительно повысит эффективность последующего просмотра кода. В противном случае вы только потратите свое время. -
eventsMixin(src/core/instance/events.js)
Vue.prototype.$on = function() {} Vue.prototype.$once = function() {} Vue.prototype.$off = function() {} Vue.prototype.$emit = function() {}
-
lifecycleMixin(src/core/instance/lifecycle.js)
Vue.prototype._update = function() {} Vue.prototype.$forceUpdate = function () {} Vue.prototype.$destroy = function () {}
-
renderMixin(src/core/instance/render.js)
// installRenderHelpers Vue.prototype._o = markOnce Vue.prototype._n = toNumber Vue.prototype._s = toString Vue.prototype._l = renderList Vue.prototype._t = renderSlot Vue.prototype._q = looseEqual Vue.prototype._i = looseIndexOf Vue.prototype._m = renderStatic Vue.prototype._f = resolveFilter Vue.prototype._k = checkKeyCodes Vue.prototype._b = bindObjectProps Vue.prototype._v = createTextVNode Vue.prototype._e = createEmptyVNode Vue.prototype._u = resolveScopedSlots Vue.prototype._g = bindObjectListeners // Vue.prototype.$nextTick = function() {} Vue.prototype._render = function() {}
После выполнения вышеуказанных 5 методов,instance
средняя параVue
После волны безумного выхода прототипа,Vue
Прототип стал:
Если вы думаете, что это конец? Ответ, конечно, нет. Вернемся к core/index.js, следуя схемам, организованным в главе 1.
Core(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'
// 初始化全局API
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
В порядке выполнения кода посмотримinitGlobalAPI(Vue)
Содержание метода:
// Object.defineProperty(Vue, 'config', configDef)
Vue.config = { devtools: true, …}
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive,
}
Vue.set = set
Vue.delete = delete
Vue.nextTick = nextTick
Vue.options = {
components: {},
directives: {},
filters: {},
_base: Vue,
}
// extend(Vue.options.components, builtInComponents)
Vue.options.components.KeepAlive = { name: 'keep-alive' …}
// initUse
Vue.use = function() {}
// initMixin
Vue.mixin = function() {}
// initExtend
Vue.cid = 0
Vue.extend = function() {}
// initAssetRegisters
Vue.component = function() {}
Vue.directive = function() {}
Vue.filter = function() {}
Нетрудно заметить, что все Ядро основано на экземпляре, аVue
Выводятся свойства волны. После прохождения Ядра весьVue
становится таким:
Продолжайте следовать маршруту, разобранному в первой главе, посмотрим, правильно ли работает время выполнения.Vue
Что вы наделали.
runtime(src/platforms/web/runtime/index.js)
Здесь не забудьте сначала начать с макроса, не смотрите на детали каждого метода. в состоянии пройти
debugger
чтобы приостановить выполнение кода, а затем передать консольconsole.dir(Vue)
наблюдать в любое времяVue
Перемена,
-
Здесь для веб-платформы в Vue.config добавлена небольшая волна методов.
Vue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement
-
Добавлены директивы к опциям
model
а такжеshow
инструкция:// extend(Vue.options.directives, platformDirectives) Vue.options.directives = { model: { componentUpdated: ƒ …} show: { bind: ƒ, update: ƒ, unbind: ƒ } }
-
Добавлены компоненты в опции
Transition
а такжеTransitionGroup
:// extend(Vue.options.components, platformComponents) Vue.options.components = { KeepAlive: { name: "keep-alive" …} Transition: {name: "transition", props: {…} …} TransitionGroup: {props: {…}, beforeMount: ƒ, …} }
-
Добавить к прототипу
__patch__
а также$mount
:// 虚拟dom所用到的方法 Vue.prototype.__patch__ = patch Vue.prototype.$mount = function() {}
-
И поддержка devtools.
entry(src/platforms/web/entry-runtime-with-compiler.js)
-
В записи перезаписать
$mount
метод. -
монтировать компилировать,
compileToFunctions
метод заключается вtemplate
компилируется вrender
функцияVue.compile = compileToFunctions
резюме
До сих пор мы полностью прошли процесс изменения конструктора Vue в Интернете:
- Монтировать свойства и методы Vue.prototype через экземпляр.
- Vue монтирует статические свойства и методы через ядро.
- Добавлена конфигурация, компоненты и инструкции, относящиеся к случаю платформы === 'web' во время выполнения.
- Добавить компиляцию в метод $mount через запись
template
Способность.
4. Что делать
В предыдущей главе мы наблюдали за процессом изменения всего конструктора Vue с точки зрения макросов, поэтому в этой главе мы рассмотрим, что сделал новый Vue() с точки зрения микро.
Измените app.js в нашей демонстрации на следующий код:
var demo = new Vue({
el: '#demo',
data() {
return {
text: 'hello world!'
}
}
})
Помните конструктор Vue в instance/init? когда код выполняетсяthis._init(options)
, то начинаем с_init
Начните и начните путешествие этой главы.
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
// 浏览器环境&支持window.performance&非生产环境&配置了performance
if (process.env.NODE_ENV !== 'production'
&& config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
// 相当于 window.performance.mark(startTag)
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
// 将options进行合并
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production'
&& config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
Что делает этот метод?
- В текущем экземпляре добавьте
_uid
,_isVue
Атрибуты. - Когда не работает, отметьте начало инициализации vue с помощью window.performance.
- Поскольку в нашей демонстрации _isComponent не обрабатывается вручную, мы войдем в ветку else и объединим Vue.options с входящими опциями.
- добавить к текущему экземпляру
_renderProxy
,_self
Атрибуты. - инициировать жизненный цикл,
initLifecycle
- событие инициализации,
initEvents
- Инициализировать рендеринг,
initRender
- вызов в жизненном цикле
beforeCreate
- Инициализировать введенное значение
initInjections
- инициализированное состояние
initState
- Инициализировать Предоставить
initProvide
- вызов в жизненном цикле
created
- В непроизводственной среде инициализация логотипа завершена, и он добавляется в текущий экземпляр
_name
Атрибуты - в соответствии с
options
входящийel
, который вызывает текущий экземпляр$mount
Хорошо, у нас есть макрос посмотреть на все_init
Затем мы объединяем нашу демонстрацию, чтобы более подробно рассмотреть влияние каждого шага и конкретного метода вызова.
mergeOptions(src/core/util/options.js)
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
if (Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions
if (superOptions !== cachedSuperOptions) {
// super option changed,
// need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options =
mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
Помните, что в главе 3 среда выполнения в пареVue
После замены что изменилось в опциях? Если забыли, то вот еще раз напомним:
Vue.options = {
components: {
KeepAlive: { name: "keep-alive" …}
Transition: {name: "transition", props: {…} …}
TransitionGroup: {props: {…}, beforeMount: ƒ, …}
},
directives: {
model: { componentUpdated: ƒ …}
show: { bind: ƒ, update: ƒ, unbind: ƒ }
},
filters: {},
_base: ƒ Vue
}
Сначала мы дизассемблируем приведенный выше код.this.constructor
входящийresolveConstructorOptions
, потому что в нашей демонстрации нет операции наследования, поэтому вresolveConstructorOptions
В методе без ввода if возвращается сразу результат, который находится вruntime
обработано вoptions
опции. иoptions
что мы звонимnew Vue({})
когда входящийoptions
. В этот момент метод mergeOptions становится:
vm.$options = mergeOptions(
{
components: {
KeepAlive: { name: "keep-alive" …}
Transition: {name: "transition", props: {…} …}
TransitionGroup: {props: {…}, beforeMount: ƒ, …}
},
directives: {
model: { componentUpdated: ƒ …}
show: { bind: ƒ, update: ƒ, unbind: ƒ }
},
filters: {},
_base: ƒ Vue
},
{
el: '#demo',
data: ƒ data()
},
vm
)
Далее начать звонитьmergeOptions
метод. После открытия файла мы обнаруживаем, что при обращении к файлу сразу же выполняется часть кода:
// config.optionMergeStrategies = Object.create(null)
const strats = config.optionMergeStrategies
Глядя внимательно на спину, есть также рядstrats
Операции над методами и свойствами монтирования и, в конечном счете,strats
станет:
На самом деле эти операции монтирования разбросаны по коду, я не понимаю, что Вы Да не поместили их в один метод, чтобы единообразно с ними справляться?
Продолжаем прокручивать вниз и видим цель, которую мы ввели в этот файл, то естьmergeOptions
метод:
function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
debugger;
if (process.env.NODE_ENV !== 'production') {
// 根据用户传入的options,检查合法性
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
// 标准化传入options中的props
normalizeProps(child, vm)
// 标准化注入
normalizeInject(child, vm)
// 标准化指令
normalizeDirectives(child)
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
Потому что мы используем самые простыеhello world
, так что вmergeOptions
, вы можете начать прямо со строки 30, где инициализируются переменныеoptions
, 32 строки, 35 строкfor
Петли были объединены в соответствии со стратегией объединения соответственно. Увидев это, я вдруг понял, чтоstrats
Является ли определить некоторые стандартные стратегии слияния, если они не определены в них, использовать стратегию слияния по умолчаниюdefaultStrat
.
Здесь есть небольшая деталь, то есть при зацикливании дочерних опций объединяются только те элементы, которых нет в родительских опциях для повышения эффективности слияния.
Давайте продолжим и рассмотрим описанный выше процесс самым простым способом:
// 初始化合并策略
const strats = config.optionMergeStrategies
strats.el = strats.propsData = function (parent, child, vm, key) {}
strats.data = function (parentVal, childVal, vm) {}
constants.LIFECYCLE_HOOKS.forEach(hook => strats[hook] = mergeHook)
constants.ASSET_TYPES.forEach(type => strats[type + 's'] = mergeAssets)
strats.watch = function(parentVal, childVal, vm, key) {}
strats.props =
strats.methods =
strats.inject =
strats.computed = function(parentVal, childVal, vm, key) {}
strats.provide = mergeDataOrFn
// 默认合并策略
const defaultStrat = function (parentVal, childVal) {
return childVal === undefined
? parentVal
: childVal
}
function mergeOptions (parent, child, vm) {
// 本次demo没有用到省略前面代码
...
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
Как, понятнее? Эта демонстрация проходитmergeOptions
После этого становится так:
Хорошо, потому что мы здесь, чтобы увидеть_init
, поэтому, чтобы попасть сюда, вам нужно очиститьVue
С помощью стратегии слияния родительский и дочерний элементы могут быть объединены. Далее продолжаем возвращаться к_init
правильноoptions
Что вы делали после обработки слияния?
initProxy(src/core/instance/proxy.js)
После объединения параметров будет принято решение, что если это нерабочая среда, будет введен метод initProxy.
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
vm._self = vm
С помощью тумана я вошел в файл определения метода и увиделProxy
Это ключевое слово, если вам здесь не ясно, вы можете прочитать ES6 г-на Руана, который упоминается выше.
- Здесь в непроизводственной среде некоторые ключевые слова в config.keyCodes запрещено назначать.
- вернулся
vm._renderProxy = new Proxy(vm, handlers)
,здесьhandlers
, так как в наших опциях нет рендера, здесь значение hasHandler.
Для чего используется эта часть?Пока знаю,что есть такая штука.Не отказывайтесь от основной линии,и продолжайте возвращаться к основной линии.
initLifecycle(src/core/instance/lifecycle.js)
Инициализируются свойства, связанные с жизненным циклом.
function initLifecycle (vm) {
const options = vm.$options
// 省去部分与本次demo无关代码
...
vm.$parent = undefined
vm.$root = vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
initEvents(src/core/instance/events.js)
function initEvents (vm) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// 省去部分与本次demo无关代码
...
}
initRender(src/core/instance/render.js)
function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
vm.$slots = {}
vm.$scopedSlots = {}
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement= (a, b, c, d) => createElement(vm, a, b, c, d, true)
vm.$attrs = {}
vm.$listeners = {}
}
callHook(vm, 'beforeCreate)
Вызов функции жизненного цикла перед созданием
initInjections(src/core/instance/inject.js)
Поскольку значение инъекции не используется в этой демонстрации, оно не имеет фактического влияния на эту виртуальную машину, поэтому этот шаг пока игнорируется, и вы можете прочитать его самостоятельно, если вам интересно.
initState(src/core/instance/state.js)
На этот раз только для самой простой демонстрации, анализа
initState
, многие процессы могут быть упущены из виду, и мы продолжим анализировать волну более сложных демонстраций в будущем.
Здесь вы можете сначала заметить несколько ключевых словObserver
,Dep
,Watcher
. каждыйObserver
иметь независимыйDep
. оWatcher
, Пока не пользовался, но поверьте, скоро увидите.
initProvide(src/core/instance/inject.js)
Поскольку эта демонстрация не используется и не имеет фактического влияния на эту виртуальную машину, этот шаг пока игнорируется, и вы можете прочитать его самостоятельно, если вам интересно.
callHook(vm, 'created')
Знай, почему здесь
created
Когда невозможно манипулировать DOM? Потому что здесь нет фактического рендеринга DOM.
vm.$mount(vm.$options.el)
Перед этим стоит суждение «если», поэтому, когда вы не в
new Vue
серединаoptions
нет входящихel
Если фактический рендеринг не запускается, вам нужно вызвать его вручную.$mount
.
здесь$mount
Куда это в итоге пойдет? Вспомните, что мы видели в главе 3.compiler
заниматься вещами? просто прикрытьVue.prototype.$mount
, далее входим вместе$mount
функцию, чтобы увидеть, что она делает.
// 只保留与本次相关代码,其余看太多会影响视线
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
const options = this.$options
if (!options.render) {
let template = getOuterHTML(el)
if (template) {
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
return mount.call(this, el, hydrating)
}
Покрытие здесь$mount
Раньше, оригинал$mount
придерживаться вариативностиmount
, весь переопределенный метод заключается вtemplate
Преобразовать вrender
Функция монтируется наvm
изoptions
, а затем вызвать исходныйmount
. так что помниmount
Откуда это? давай тогдаruntime/index
, метод очень простой, вызывая жизненный циклmountComponent
.
// 依然只保留和本demo相关的内容
function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
callHook(vm, 'beforeMount')
let updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
Хорошо, а вот и захватывающая часть,Watcher
, оживляет весь ряд вещей, которые мы разложили перед собой. Открытымsrc/core/observer/watcher.js
,покажи намWatcher
функция конструктора. ясно видетьWatcher
процесс. По-прежнему сохраняйте только те методы, на которые нам нужно обратить внимание:
constructor (vm, expOrFn, cb, options, isRenderWatcher) {
this.vm = vm
vm._watcher = this
vm._watchers.push(this)
this.getter = expOrFn
this.value = this.get()
}
get () {
pushTarget(this)
let value
const vm = this.vm
value = this.getter.call(vm, vm)
popTarget()
this.cleanupDeps()
return value
}
- существует
Watcher
В конструкторе передано в это времяupdateComponent
в видеWather
изgetter
. - существует
get
При вызове метода передатьpushTarget
метод, текущийWatcher
назначить вDep.target
- перечислить
getter
, что эквивалентно вызовуvm._update
, позвони сначалаvm._render
, а на этот разvm._render
, на данный момент готовrender
вызывается функция. -
render
снова используется в функцииthis.text
, так что он будет звонить сноваtext
изget
метод, который вызываетdep.depend()
-
dep.depend()
перезвонитWatcher
изaddDep
, тогдаWatcher
записал текущийdep
пример. - Продолжай звонить
dep.addSub(this)
,dep
записал текущийWatcher
экземпляр, текущийWatcher
депозитdep.subs
середина. - Вот, кстати, в этот раз
demo
не использовался, то есть когдаthis.text
Когда происходит изменение, оно вызываетObserver
серединаset
метод, который вызываетdep.notify()
способ выполненияupdate
работать.
Последний абзац слишком сух, вы можете пройти точку останова и терпеливо пройти весь процесс. Если у вас не хватает терпения читать это описание, вы можете прочитать эту статью автора100 строк кода позволят вам играть в отзывчивый Vue.
это все,Vue
система ответа на данные, черезObserver
,Watcher
,Dep
Идеально натянуты вместе. Я также надеюсь, что, пройдя этот процесс, вы сможете иметь определенное представление о реальной картине.
Конечно,$mount
Есть еще один шаг, который я недооценил, то есть эта часть, когда шаблон конвертируется в рендер, когда рендер фактически вызывается, он будет проходить_render
, $createElement
, __patch__
, метод, если вам интересно, вы можете просмотреть файлы в каталоге 'src/core/vdom/', чтобы понятьvue
Для использования виртуального дома.
Наконец
Если хотите, вы можете продолжить просмотр статьи автора о части преобразования шаблона vue.«Что Vue делает с шаблоном».