Я нахожу время, чтобы читать исходный код каждый день. Нет причин. Я часто использую Vue. Я должен узнать больше после прочтения. Я пришел сюда, чтобы следить за ним некоторое время...
Чтение исходного кода должно начинаться с входного файла, и вы хотите найтиVue
файл ввода исходного кода, вы должны сначала начать сpackage.json
уходить из поля зрения, потому что мы обычно используемnpm
Команда упакована и скомпилирована, поэтому должны быть связанные файлы, на которые можно указать.
"scripts": {
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
"dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev",
"dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm",
"dev:test": "karma start test/unit/karma.dev.config.js",
"dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer",
"dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler ",
"dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework",
"dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory",
"dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler ",
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
"test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex",
"test:unit": "karma start test/unit/karma.unit.config.js",
"test:cover": "karma start test/unit/karma.cover.config.js",
"test:e2e": "npm run build -- web-full-prod,web-server-basic-renderer && node test/e2e/runner.js",
"test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.js",
"test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.js",
"test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
"test:types": "tsc -p ./types/test/tsconfig.json",
"lint": "eslint src scripts test",
"flow": "flow check",
"sauce": "karma start test/unit/karma.sauce.config.js",
"bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
"release": "bash scripts/release.sh",
"release:weex": "bash scripts/release-weex.sh",
"release:note": "node scripts/gen-release-note.js",
"commit": "git-cz"
}
Хм, молодец, я был просто в шоке, когда зашел и увидел густую стопку заказов. Однако, чтобы выделить ключевые моменты, взгляните на первую строку с первого взгляда.dev
Команда очень правильная, и обнаружено, что она выполняется следующим образом:
rollup -w -c scripts/config.js --environment TARGET:web-full-dev
Через эту строку команды выясняется, что команда найдет этот файл:scripts/config.js
, а также естьweb-full-dev
, поэтому найдите расположение соответствующего файла и найдите следующий код:
'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
}
Видетьentry
Будет знать, что это запись в расположении файла, описание сделано наполовину, поэтому я следуюresolve('web/entry-runtime-with-compiler.js')
Путь, по которому был найден файл и обнаружена проблема:
Шокировал весь отдел новостей UC, нетweb
содержание!
Сяосяо на некоторое время успокоился и обнаружил, что проблема должна быть вresolve
Выше (поскольку узел обычно используется непосредственно таким образом, что вызвало недопонимание). Конечно, нашел здесьresolve
является пользовательским методом, следующим образом:
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)
}
}
Оказывается, этот путь/
Предыдущий является псевдонимом, вalias.js
определяется следующим образом:
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')
}
Это довольно ясно, этоentry
В итоге он указывает на:src/platforms/web/entry-runtime-with-compiler.js
, так что я не мог дождаться, чтобы открыть этот файл, и обнаружил, что он представилVue
документ. МОЙ БОГ! Появляется главный герой:
...
import Vue from './runtime/index'
...
следуй, открой сноваruntime/index
Обнаружение файлов было вновь введено:
...
import Vue from 'core/index'
...
оба звонятcore
На первый взгляд каталог очень сердечный, войдите снова и найдите следующий код:
...
import Vue from './instance/index'
...
Окончательное положение окончательно зафиксировано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'
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
Объект: оценивается, генерируется ли он новым и выполняется ли_init
Сюда; - Пучок
Vue
объект передается каждомуMinxin
метод.
Увидев это, у меня сразу возник вопрос, это_init
Где определяется метод и для чего он нужен? Должен~ Если вы хотите узнать ответ, вы должны пойти к тем немногимMixin
Найдите в методе. Итак, я начинаю сinitMixin
метод, поэтому открытыйinit.js
документ:
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
...
}
}
Увидимся, как только вы придетеVue
Чан Вэй висит на цепи прототипов_init
метод. повернуть ум_init
метод, начните изучать:
- Сначала найдите определение
vm
объект, указывающий на текущий экземпляр;
const vm: Component = this
- Кажется, это рекорд производительности:
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
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 {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
первое предложение вышеvm._isVue = true
Судя по комментариям, следует избегать наблюдения (связанного с шаблоном наблюдателя?)
Потом через суд объекты экземпляра обрабатываются по разному.Видя тут я не нашел куда его монтировать._isComponent
properties, так что просто посмотрите прямо ниже.
Нашелvm.$options
черезmergeOptions
Этот метод для объединения параметров, вmergeOptions
есть еще одинresolveConstructorOptions
для обработки, поэтому я нашел метод и нашел:
export 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
}
Открытие заключается в том, чтобы определить, существует лиsuper
Этот атрибут используется для соответствующей операции, по крайней мере, я вижу, что не нашел, куда его примонтировать, поэтому он напрямую возвращает конструктор.options
(Где это определено?), отладка обнаружила, что некоторые объекты были смонтированы:
небо! Откуда это взялось?Можно только сначала сдаться и продолжать смотреть вперед.mergeOptions
Эта функция обнаружила, что ее функция заключается в следующем:
/**
* Merge two option objects into a new one.
* Core utility used in both instantiation and inheritance.
*/
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, 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
}
Выяснено, что это класс инструмента, и аннотация его функции переводится как:
Объединяет два объекта параметров в новый объект.
Основные утилиты для создания экземпляров и наследования.
Среди них выполняются две вещи: одна — определять параметры и выдавать соответствующие подсказки (многие предупреждения, с которыми мы столкнулись во время разработки, можно найти здесь), а другая — объединять параметры, которые слишком сложно читать, поэтому я не буду вникай пока в него.Зайди и пройди процесс сначала(прости мою тарелку).
назадinit.js
файл и нашел этот фрагмент кода:
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
Обнаружил, что в непроизводственной среде я сделалinitProxy
Операция, я нажал на метод и увидел, что люди тупые:
config.keyCodes = new Proxy(config.keyCodes, {
set (target, key, value) {
if (isBuiltInModifier(key)) {
warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`)
return false
} else {
target[key] = value
return true
}
}
})
как может бытьProxy
операция, это неVue3
Используете ли вы его, но, похоже, время отправки истекло раньше...
Короче, вот вам прокси-операция, вернитесь и посмотрите на другие процессы. существуетinit.js
Остальной код нашелся в файле (Боже, очень раздражает этот способ):
/ 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)
}
Нашел три вещи сделанные:
- Инициализировать жизненный цикл, состояние и т. д. (это должно быть сложно, см. далее);
- Тест производительности (используется в сочетании с предыдущим);
- Гора согласно суждению
el
объект (это должно быть написаноel
нет атрибута$mount
Причина, по сути, в$mount
смонтировать).
Я здесь первый, на чтение исходников ушло время, в основном потому, что готовил, и выглядело это трудоемко.
To be continue...