⚠️Эта статья является первой подписанной статьей сообщества Nuggets, перепечатка без разрешения запрещена.
предисловие
Много раз мы правы源码
Выказал определенное желание, но на вопрос, зачем он хочет увидеть исходники, ответы были не более чем следующие:
- для интервью
- Для того, чтобы написать собственный исходный код в резюме
- Понять основные принципы и изучить идеи мастеров
- Изучите некоторые приемы через исходный код (Сан-операция)
- Любопытно, как фреймворк реализует различные функции
- Серьезная инволюция, не могу этого сделать, если не видишь, иди против течения
- Я хочу построить колесо сам, давайте сначала посмотрим, как это сделают другие
- Всевозможные паблики и продавцы курсов торгуют тревогой и промывкой мозгов
Но на самом деле мало кто действительно поймет исходный код: с одной стороны, слишком много кода, с другой стороны, когда мы читаем чужой код, легко запутаться. Поскольку стиль и привычки кодирования у всех очень разные, может быть очень утомительно смотреть на код человека, чьи привычки кодирования отличаются от ваших собственных.
Более того, не только стиль кодирования у каждого человека очень разный, но и техническое направление и технический уровень, в котором каждый человек хорош.横看成岭侧成峰
,远近高低各不同
. После устранения вышеуказанных причин более важной причиной является то, что многие люди недостаточно опытны, чтобы использовать фреймворк.API
Есть только несколько общих, другие не часто используются, но очень высокого уровня.API
Я им мало пользовался, и мне даже не нужно в нем разбираться.Конечно, такие люди будут сбиты с толку, когда посмотрят исходный код!
Тогда кто-то обязательно скажет: «Ты, Юси, должен использовать 6 для своего кадра?» Я использую его фреймворк для написания кода каждый день, и он не обязательно хорошо со мной разбирается!
В этом действительно есть доля правды, но когда дело доходит до сути, он понимает это лучше, чем кто-либо другой. Одна из важных причин, по которой мы не можем разжевывать исходный код, заключается в том, что в нем слишком много деталей, и каждый может легко упустить из виду ключевые моменты. Эти мелочи, естественно, имеют свои причины для своего существования, но они действительно стали для нас камнем преткновения при изучении исходного кода.
Не по теме
Как узнать исходный код наиболее научным способом? Давайте посмотрим на пример: есть некоторые высокотехнологичные продукты, которые звучат очень высоко, например,电磁轨道炮
. Различные военные силы изо всех сил пытаются исследовать эту область, предположим, однажды мы проснемся как国家电磁轨道炮首席研究员
, специализируется на исследовании базовой технологии электромагнитных рельсотронов. Тогда когда мы будем разбирать электромагнитный рельсотрон, есть большая вероятность, что вы не сможете понять его внутреннее устройство. Потому что он будет содержать много очень сложных高强度材料
,控制磁力的电极
,蜿蜒曲折的电线
,提高精准度的装置
и немного利于使用者操控的封装
и т.д…
Тогда вам может быть нелегко понять电磁轨道炮的真正原理
, пока я случайно не увидел видео в Интернете, человек в видео использовал несколько магнитов, несколько стальных шариков и несколько материалов, которые мы можем получить в нашей повседневной жизни, чтобы сделать简易版的电磁轨道炮
. Так что мы можем понять сразу电磁轨道炮的真正原理
, Хотя такая рельсовая пушка не может быть использована в реальном бою, пока мы понимаем самую основную часть, мы можем шаг за шагом расширяться на этой основе и постепенно понимать всю сложную рельсовую пушку, которую можно использовать в реальном бою. .
Исходный код тот же, следуем
电磁轨道炮
Идея идти шаг за шагом, сначала выяснить самые основные основные части, а затем постепенно продвигаться шаг за шагом. Такой метод обучения лучше, чем мы обязательно разберем полную версию, как только появится.电磁轨道炮
гораздо сильнее
Раз у нас есть такая потребность, автор популярного фреймворка должен откликнуться: в ходе обучения,尤雨溪
Веди всех, чтобы написать очень миниатюрныйVue3
. К сожалению, это однодневный тренинг, который он провел за границей, и нашему отечественному зрителю не посчастливилось им насладиться.被框架作者培训
такое учение. но к счастью尤雨溪已经把代码全部上传到了codepen上
, вы можете нажатьэта ссылкаПриходите и прочитайте код, написанный Ю Юйси, или вы можете остаться в этой статье и посмотреть, как я объясню вам его на китайском языке.尤雨溪的亲笔代码
!
Отзывчивые статьи
尤雨溪
В прямом эфире он сказал:Vue3 的源码要比 Vue2 的源码要好学很多
.Vue3
По сравнению с дизайном архитектуры и взаимосвязью модулейVue2
Лучше посмотреть модуль за модулем, так легче понять. Если вы только начинаете, вы можетеReactivity
Посмотрите. так какReactivity
это всеVue3
Модуль без связи с внешним миром.
Reactivity
это то, что мы всегда говорим响应式
, известныйReact
В этом же и смысл, если не верите, внимательно сравните первые пять букв. Так что же такое отзывчивость? подумай об этомReact
Что такое рамки?MVVM
Правильно?MVVM
Главный лозунг:
Представления, управляемые данными!
То есть, когда данные изменяются, мы повторно визуализируем компонент, так что после изменения данных место, где данные используются на странице, будет меняться в режиме реального времени. Но вы можете сделать больше, чем просто обновить представление при изменении данных! Юйси создает@vue/reactivity
В этом модуле ссылка@nx-js/observer-utilэта библиотека. Давайте посмотрим на этоGitHub
начальствоREADME.md
Фрагмент примера кода, показанный в:
import { observable, observe } from '@nx-js/observer-util';
const counter = observable({ num: 0 });
const countLogger = observe(() => console.log(counter.num));
// 这行代码将会调用 countLogger 这个函数并打印出:1
counter.num++;
это похожеVue3
изreactive
иwatchEffect
А? На самом деле мы определяем функцию заранее, и когда зависимые элементы данных в функции изменяются, эта функция будет автоматически выполняться, что является отзывчивым!
Представление, управляемое данными, легче понять, поскольку функция может выполняться при изменении данных, почему эта функция не может выполнить операцию для обновления представления:
import { store, view } from 'react-easy-state';
const counter = store({
num: 0,
up() {
this.num++;
}
});
// 这是一个响应式的组件, 当 counter.num 发生变化时会自动重新渲染组件
const UserComp = view(() => <div onClick={counter.up}>{counter.num}</div>);
react-easy-state
Это они(Библиотека, которую Ю Юйси позаимствовала) специально дляReact
Для упаковки не трудно увидетьview
Эта функцияobserve
вариант функции,observe
Он просит вас передать функцию, и вы можете выполнить все, что хотите, в этой функции. иview
Это вам передать в него компонент, и когда данные изменятся, он выполнит часть логики обновления, которую они написали заранее, так что он будет с вами.observe
Это то же самое, что написать в нем операцию обновления! написано с использованием этой библиотекиReact
как будто пишетVue
Такой же.
исходный код
Разобравшись, что такое отзывчивость, нам удобно просмотреть исходный код, давайте посмотрим尤雨溪
Как это сделать всего с дюжиной строк кода响应式
:
let activeEffect
class Dep {
subscribers = new Set()
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect)
}
}
notify() {
this.subscribers.forEach(effect => effect())
}
}
function watchEffect(effect) {
activeEffect = effect
effect()
}
После завершения реализации давайте посмотрим, как его использовать:
const dep = new Dep()
let actualCount = 0
const state = {
get count() {
dep.depend()
return actualCount
},
set count(newCount) {
actualCount = newCount
dep.notify()
}
}
watchEffect(() => {
console.log(state.count)
}) // 0
state.count++ // 1
Если вы чувствуете себя сбитым с толку, наблюдая за этими десятком-двадцатью строками кода, значит, ваш фундамент не очень хорош. Поскольку проницательный человек может увидеть это с первого взгляда, это очень классический шаблон дизайна:发布-订阅模式
модель публикации-подписки
Если вы не знаете发布-订阅模式
Если да, то мы можем кратко рассказать об этом. Но если вы уже хорошо знаете эти шаблоны проектирования и можете легко прочитать код прямо сейчас, рекомендуется пока пропустить этот раздел.
существует《JavaScript设计模式与开发实践》
книга, автор曾探
за发布-订阅模式
Вот очень яркий пример:
Сяо Мину недавно приглянулся дом, и только после того, как он пришел в офис продаж, ему сообщили, что дома в здании уже распроданы. К счастью, менеджер по продажам сказал Сяо Мину, что скоро будут поздние запуски, и разработчик проходит соответствующие процедуры, и вы можете купить его после завершения процедур. Но когда именно, пока никто не может знать.
Поэтому Сяо Мин записал номер телефона офиса продаж и будет звонить каждый день, чтобы спросить, не пора ли покупать. Помимо Xiao Ming, Xiaohong, Xiaoqiang и Xiaolong также будут ежедневно консультироваться с офисом продаж по этому вопросу. Через неделю менеджер по продажам решил уволиться, потому что устал отвечать на 1000 звонков с одним и тем же содержанием каждый день.
Конечно, такой дурацкой компании по продажам в реальности не существует.На самом деле история такова: Перед уходом Сяо Мин оставил свой номер телефона в офисе продаж. Менеджер по продажам пообещал ему, что он отправит сообщение, чтобы уведомить Сяо Мина, как только новая недвижимость будет запущена. То же самое верно для Xiaohong, Xiaoqiang и Xiaolong. Все их номера телефонов записаны в реестре офиса продаж. Когда новый объект запускается, менеджер по продажам переворачивает список, проходит по указанным выше номерам телефонов и отправляет текстовое сообщение в свою очередь. Уведомить их.
В только что приведенном примере отправка SMS-уведомлений является типичной моделью публикации-подписки.Покупатели, такие как Сяомин и Сяохун, все являются подписчиками, и они подписываются на новости о продаже дома. Как издатель, отдел продаж будет в нужное время просматривать телефонные номера в списке и по очереди отправлять сообщения покупателям.
если вы когда-либо использовалиxxx.addEventListener
Эта функцияDOM
Если событие добавлено, то оно фактически используется.发布-订阅模式
Ла! Подумайте, похоже ли это на этот пример в офисе продаж:
- Нам нужно что-то делать при определенных условиях
- Но чего мы не знаем, так это того, когда это условие будет выполняться
- Итак, мы покидаем нашу функцию
- Автоматически выполняться при соблюдении условий
Итак, давайте просто смоделируемaddEventListener
что случилось, чтобы все могли понять发布-订阅模式
:
class DOM {
#eventObj = {
click: [],
mouseover: [],
mouseout: [],
mousemove: [],
keydown: [],
keyup: []
// 还有很多事件类型就不一一写啦
}
addEventListener (event, fn) {
this.#eventObj[event].push(fn)
}
removeEventListener (event, fn) {
const arr = this.#eventObj[event]
const index = arr.indexOf(fn)
arr.splice(index, 1)
}
click () {
this.#eventObj.click.forEach(fn => fn.apply(this))
}
mouseover () {
this.#eventObj.mouseover.forEach(fn => fn.apply(this))
}
// 还有很多事件方法就不一一写啦
}
Давайте попробуем:
const dom = new DOM()
dom.addEventListener('click', () => console.log('点击啦!'))
dom.addEventListener('click', function () { console.log(this) })
dom.addEventListener('mouseover', () => console.log('鼠标进入啦!'))
dom.addEventListener('mouseover', function () { console.log(this) })
// 模拟点击事件
dom.click() // 依次打印出:'点击啦!' 和相应的 this 对象
// 模拟鼠标事件
dom.mouseover() // 依次打印出:'鼠标进入啦!' 和相应的 this 对象
const fn = () => {}
dom.addEventListener('click', fn)
// 还可以移除监听
dom.removeEventListener('click', fn)
Этот простой случай должен быть в состоянии понять发布-订阅模式
правильно?
Давайте процитируем《JavaScript设计模式与开发实践》
за发布-订阅模式
Подведем итоги по трем основным моментам:
- Сначала укажите, кто будет выступать в роли издателя (например, офис продаж)
在本例中是 dom 这个对象
- Затем добавьте к издателю кеш-лист для хранения callback-функции для уведомления подписчика (ростер офиса продаж)
在本例中是 dom.#eventObj
- Когда сообщение, наконец, опубликовано, издатель будет проходить список кеша и активировать функцию обратного вызова подписчика, хранящуюся в нем, в свою очередь (обходить список и отправлять текстовые сообщения одно за другим).
Запомнив эти три пункта, еще раз взгляните на большой код, чтобы увидеть, соответствует ли он этим трем пунктам:
-
发布者
: отложенный объект -
缓存列表
: отд.подписчиков -
发布消息
: отп.уведомить()
Так что это типичный发布-订阅模式
Расширенное издание
尤雨溪
Реализация первой версии кода все еще слишком рудиментарна, и ее очень неудобно использовать поначалу, потому что нам нужно писать ее вручную каждый раз, когда мы определяем данныеgetter
иsetter
, Вручную выполните функцию сбора зависимостей и сработавшую функцию. Эта часть, очевидно, может продолжать инкапсулироваться, так что взгляните еще раз尤雨溪
Реализована вторая версия:
let activeEffect
class Dep {
subscribers = new Set()
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect)
}
}
notify() {
this.subscribers.forEach(effect => effect())
}
}
function watchEffect(effect) {
activeEffect = effect
effect()
activeEffect = null
}
function reactive(raw) {
// 使用 Object.defineProperty
// 1. 遍历对象上存在的 key
Object.keys(raw).forEach(key => {
// 2. 为每个 key 都创建一个依赖对象
const dep = new Dep()
// 3. 用 getter 和 setter 重写原对象的属性
let realValue = raw[key]
Object.defineProperty(raw, key, {
get() {
// 4. 在 getter 和 setter 里调用依赖对象的对应方法
dep.depend()
return realValue
},
set(newValue) {
realValue = newValue
dep.notify()
}
})
})
return raw
}
Видно, что эта версия намного лучше предыдущей, и по ощущениям尤雨溪
Эта версия кода была написана более тщательно, чем предыдущая версия. Поскольку эта версия кода содержит подробные комментарии, это должен быть тщательно объясненный фрагмент кода. Просто исходные заметки все были написаны на английском языке, а я перевел их на китайский.
Но будьте уверены, все, кроме комментариев, которые я перевел на китайский язык, я не тронул ни одной буквы в других местах, даже пробелы сохранены с отступом, чтобы все могли видеть да
尤雨溪的一手代码
😋
Нетрудно заметить, что эта версия кода использует в реализации два шаблона проектирования, они代理模式
и что мы только что сказали发布-订阅模式
. Так что учись хорошоШаблоны проектированияКакая важная вещь. если правильноШаблоны проектированияЕсли вам интересно, вы можете пойтиСтанция Бпоискпредварительное обучение, сейчас загружаетсяШаблоны проектированияЯ лично считаю, что он продается лучше, чем MOOC.com288
Класс JavaScript Design Patterns гораздо понятнее.
прокси-режим
Режим прокси относительно прост, код не требуется, заимствован《JavaScript设计模式核⼼原理与应⽤实践》
автор修言
Очень интересный пример даст понять всем:
У меня есть коллега, очень техничный, с красивой прической. На протяжении многих лет из-за моего пристрастия к программированию я откладывал важные события в своей жизни. Желание найти вторую половинку более острое, коллега также является зарегистрированным VIP на нескольких качественных сайтах знакомств высокого класса. Вне работы он часто делится с нами недавними
свидание в слепуюЭмоциональный жизненный прогресс.
«Посмотрите, разве аватарка этой девушки не супер милая!» В тот день мой коллега обнаружил новое агентство знакомств, поднял свой мобильный телефон и отчаянно помахал им немногим вокруг него.
"Брат, это Юи Арагаки..." Сосед по парте моего коллеги беспомощно покачал головой, не останавливая кодирующую руку.
Коллега восстановил свое спокойствие и вздохнул: «Механизм этой платформы брака и любви настолько строг, что когда вы входите, вы можете видеть только имена, возраст и представление других участников. собственные фотографии или получить контактную информацию другой стороны, вы должны сначала заплатить платформе, чтобы стать VIP. Эй, я должен снова купить VIP ».
Как только я это услышал, вау, эта платформа о браке и любви играет в прокси-режиме довольно 6! Вдумайтесь, объект — коллега А, а цель — неизвестная девушка с аватаром Юи Арагаки. Коллега А не может напрямую общаться с неизвестной девушкой, а может получить некоторую информацию о другой стороне только косвенно через третье лицо (агентство знакомств). не типичный режим прокси?
использование
Эта версия отзывчивости намного удобнее в использовании:
const state = reactive({
count: 0
})
watchEffect(() => {
console.log(state.count)
}) // 0
state.count++ // 1
В основном так же, какVue3
Использование точно такое же! Можно видеть, что основной принцип отзывчивости на самом деле发布-订阅
+代理模式
. Это не окончательная версия, потому что он используетES5
изObject.defineProperty
сделать代理模式
, если не учитывать совместимостьIE
все ещеES6
изProxy
больше подходит для代理
,так какProxy
переведи это на代理权
,代理人
значение. такVue3
УсыновленныйProxy
Чтобы реорганизовать весь реактивный код, давайте посмотрим尤雨溪
Окончательный вариант написан(Proxy
Версия)
Прокси-версия
let activeEffect
class Dep {
subscribers = new Set()
constructor(value) {
this._value = value
}
get value() {
this.depend()
return this._value
}
set value(value) {
this._value = value
this.notify()
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect)
}
}
notify() {
this.subscribers.forEach((effect) => {
effect()
})
}
}
function watchEffect(effect) {
activeEffect = effect
effect()
activeEffect = null
}
// proxy version
const reactiveHandlers = {
get(target, key) {
const value = getDep(target, key).value
if (value && typeof value === 'object') {
return reactive(value)
} else {
return value
}
},
set(target, key, value) {
getDep(target, key).value = value
}
}
const targetToHashMap = new WeakMap()
function getDep(target, key) {
let depMap = targetToHashMap.get(target)
if (!depMap) {
depMap = new Map()
targetToHashMap.set(target, depMap)
}
let dep = depMap.get(key)
if (!dep) {
dep = new Dep(target[key])
depMap.set(key, dep)
}
return dep
}
function reactive(obj) {
return new Proxy(obj, reactiveHandlers)
}
Вы можете видеть, что эта версия кода немного сложнее, чем предыдущая версия, но использование точно такое же, как и в предыдущей версии:
const state = reactive({
count: 0
})
watchEffect(() => {
console.log(state.count)
}) // 0
state.count++ // 1
Давайте сосредоточимся на финальной версии кода, эта версия кода самая优秀
из. Хоть воробей и маленький, но у него есть все внутренние органы, не только самые основные发布-订阅模式
+代理模式
, а также использовали множество мелких хитростей для оптимизации производительности.
Детальное объяснение
Прежде всего, Юда определяетactiveEffect
пустая переменная для храненияwatchEffect
Функция передана:
// 定义一个暂时存放 watchEffect 传进来的参数的变量
let activeEffect
Далее определитеDep
класс, этоDep
должно бытьDependence
сокращение от依赖
. на самом деле эквивалентно发布-订阅模式
Класс издателя в:
// 定义一个 Dep 类,该类将会为每一个响应式对象的每一个键生成一个发布者实例
class Dep {
// 用 Set 做缓存列表以防止列表中添加多个完全相同的函数
subscribers = new Set()
// 构造函数接受一个初始化的值放在私有变量内
constructor(value) {
this._value = value
}
// 当使用 xxx.value 获取对象上的 value 值时
get value() {
// 代理模式 当获取对象上的value属性的值时将会触发 depend 方法
this.depend()
// 然后返回私有变量内的值
return this._value
}
// 当使用 xxx.value = xxx 修改对象上的 value 值时
set value(value) {
// 代理模式 当修改对象上的value属性的值时将会触发 notify 方法
this._value = value
// 先改值再触发 这样保证触发的时候用到的都是已经修改后的新值
this.notify()
}
// 这就是我们常说的依赖收集方法
depend() {
// 如果 activeEffect 这个变量为空 就证明不是在 watchEffect 这个函数里面触发的 get 操作
if (activeEffect) {
// 但如果 activeEffect 不为空就证明是在 watchEffect 里触发的 get 操作
// 那就把 activeEffect 这个存着 watchEffect 参数的变量添加进缓存列表中
this.subscribers.add(activeEffect)
}
}
// 更新操作 通常会在值被修改后调用
notify() {
// 遍历缓存列表里存放的函数 并依次触发执行
this.subscribers.forEach((effect) => {
effect()
})
}
}
В предыдущих двух редакциях переменная определялась снаружи для сохранения响应式对象
Значение, соответствующее каждому ключу, и на этот раз значение напрямую помещается вDep
В определении класса он определяется какgetter
иsetter
, операция сбора зависимостей выполняется при извлечении значения, а операция обновления выполняется при изменении значения.
Далее определитеVue3
изwatchEffect
Функция с таким же названием:
// 模仿 Vue3 的 watchEffect 函数
function watchEffect(effect) {
// 先把传进来的函数放入到 activeEffect 这个变量中
activeEffect = effect
// 然后执行 watchEffect 里面的函数
effect()
// 最后把 activeEffect 置为空值
activeEffect = null
}
Разве мы не передаем другую функцию в эту функцию, когда используем ее:
watchEffect(() => state.xxx)
Эта функция возложена наactiveEffect
Перейдите к этой переменной, а затем немедленно выполните функцию.Вообще говоря, эта функция будет иметь некоторые响应式对象
правильно? Если есть, он сработаетgetter
Перейдите к операции сбора зависимостей, и коллекция зависимостей должна судитьactiveEffect
Имеет ли эта переменная значение, и если да, то добавьте его в список кеша. После выполнения этой функции сразуactiveEffect
Эта переменная имеет нулевое значение, чтобы предотвратитьwatchEffect
срабатывает в этой функцииgetter
Операция сбора зависимостей также выполняется, когда .
Далее следует определитьProxy
Объект обработки прокси:
const reactiveHandlers = {
// 当触发 get 操作时
get(target, key) {
// 先调用 getDep 函数取到里面存放的 value 值
const value = getDep(target, key).value
// 如果 value 是对象的话
if (value && typeof value === 'object') {
// 那就把 value 也变成一个响应式对象
return reactive(value)
} else {
// 如果 value 只是基本数据类型的话就直接将值返回
return value
}
},
// 当触发 set 操作时
set(target, key, value) {
// 调用 getDep 函数并将里面存放的 value 值重新赋值成 set 操作的值
getDep(target, key).value = value
}
}
если правильноProxy
Если вы мало что знаете об этом, я предлагаю прочитать книгу Жуань Ифэна.«Вводный курс по ES6», письмо все еще хорошо.
этот объект был простоget
иset
используется в эксплуатацииgetDep
Эта функция, которая будет определена позже, будет использовать функцию с именемtargetToHashMap
изWeakMap
Структуры данных для хранения данных:
// 定义一个 WeakMap 数据类型 用于存放 reactive 定义的对象以及他们的发布者对象集
const targetToHashMap = new WeakMap()
Далее идет определениеgetDep
функция:
// 定义 getDep 函数 用于获取 reactive 定义的对象所对应的发布者对象集里的某一个键对应的发布者对象
function getDep(target, key) {
// 获取 reactive 定义的对象所对应的发布者对象集
let depMap = targetToHashMap.get(target)
// 如果没获取到的话
if (!depMap) {
// 就新建一个空的发布者对象集
depMap = new Map()
// 然后再把这个发布者对象集存进 WeakMap 里
targetToHashMap.set(target, depMap)
}
// 再获取到这个发布者对象集里的某一个键所对应的发布者对象
let dep = depMap.get(key)
// 如果没获取到的话
if (!dep) {
// 就新建一个发布者对象并初始化赋值
dep = new Dep(target[key])
// 然后将这个发布者对象放入到发布者对象集里
depMap.set(key, dep)
}
// 最后返回这个发布者对象
return dep
}
Это место немного в обход, давайте сделаем снимок выше:
каждый входящийreactive
Объекты, которые входят, будут существоватьWeakMap
ключ в. Значение, соответствующее каждому ключу, представляет собойMap
:
// targetToHashMap: {
const obj1 = reactive({ num: 1 }) // { num: 1 }: new Map(),
const obj2 = reactive({ num: 2 }) // { num: 2 }: new Map(),
const obj3 = reactive({ num: 3 }) // { num: 3 }: new Map()
// }
это значение (Map) что в нем хранится? Что сохраняется:
Предположим, мыreactive
объект{ a: 0, b: 1, c: 2 }
,ТакMap
Внутри находятся:
{
'a': new Dep(0),
'b': new Dep(1),
'c': new Dep(2)
}
заключается в том, чтобы поместить ключ объекта вMap
ключ, затем используйтеnew Dep
Создайте объект издателя и передайте значение вDep
. Один из очень важных моментов оптимизации, который делает Vue3 намного сильнее, чем Vue2, заключается в следующем.Proxy
. не тоProxy
производительность, чемObject.defineProperty
Насколько выше, но сказать вProxy
Способ обработки вVue2
Времена намного лучше:Vue2
Отзывчивость - это еда, как только она появляется遍历
+递归
Сделайте все данные, которые вы определяете, отзывчивыми, что приведет к тому, что если на странице много сложных структур данных, используйтеVue2
Написанная страница будет пустой в течение короткого времени. после всего遍历
+递归
Все еще относительно медленная операция!
иReact
такой проблемы конечно нетVue3
Там не будет этого неправильно. Как видно из кода, когда мы получаем значение ключа, соответствующего объекту, сначала он определяет значение в конце, нет соответствующих объектов издателя, если не повторно создать объекты издателя. И когда полученное значение, то это значение становится ссылочным типом响应式对象
, а затем создайте новый объект издателя при использовании значения в реактивном объекте.
Если резюмировать одним предложением:
Vue3
Какая часть данных используется, а затем данные становятся чувствительными. иVue2
Это означает, что независимо от того, будет ли это 3721, все это станет для вас ответными данными в начале игры.
Последний шаг – определитьreactive
функция:
// 模仿 Vue3 的 reactive 函数
function reactive(obj) {
// 返回一个传进来的参数对象的代理对象 以便使用代理模式拦截对象上的操作并应用发布-订阅模式
return new Proxy(obj, reactiveHandlers)
}
блок-схема
Для того, чтобы всем было легче понять, мы снова используем егоreactive
иwatchEffect
функция, и, кстати, посмотрите, что происходит:
Сначала мы используемreactive
функция определяет объект{ num: 0 }
, этот объект будет переданProxy
Первый параметр , в это время ничего не произошло, то мы находимся вwatchEffect
распечатать этот объектnum
Атрибуты:
в это время кwatchEffect
Эта функция будет возложена наactibveEffect
Эта переменная идет вверх, а затем функция выполняется немедленно:
В ходе исполнения выяснилось, чтоget
операция, поэтомуProxy
перехватил, пошелget
Этот шаг:
из-заget
требуется в эксплуатацииgetDep
функция, поэтому{ num: 0 }
перешел кgetDep
, ключ числовой, поэтому он эквивалентенgetDep({ num: 0 }, 'num')
. ВходитьgetDep
В теле функции вам нужно использоватьtargetToHashMap
получить{ num: 0 }
Значение, соответствующее этому ключу, но в настоящее времяtargetToHashMap
пуст, поэтому вообще ничего не извлекается. Итак, введите приговор, создайте новыйMap
назначить вtargetToHashMap
, что эквивалентно:targetToHashMap.set({ num: 0 }, new Map())
, а затем получить этоMap
изkey
Соответствующее значение:
так какMap
Он также пуст, поэтому я все еще не могу получить значение, поэтому ввожу суждение и создаю новое.Dep
Объект:
Поскольку он используетсяgetDep(...xxx).value
получить объектvalue
свойство, поэтому оно вызоветgetter
:
следитьgetter
здесь мы сноваdepend
метод, так какactiveEffect
Есть ценность, так что вступай в суждение, ставьactiveEffect
принять участие вsubscribes
этоSet
в структуре. На этом часть сбора зависимостей подошла к концу, и тогда мы изменимobj.num
значение, чтобы увидеть, что происходит:
будет первымProxy
перехватыватьset
операции, затем позвонитеgetDep
функция:
получитьdep
объект, он изменяет свойvalue
свойство, вызывающееsetter
работать:
Наконец доходим до уведомления(notify) мы найдем наш список кэшей (subscribers), а затем по очереди запускать функции внутри:
тогда он будет работать() => console.log(obj.num)
Как вы думаете, эта функция закончилась? конечно, нет! из-за бегаobj.num
Эта операция, поэтому она снова сработаетget
операцияProxy
Перехват:
После получения объекта издателя, который мы создали ранее, он активирует объект издателя.getter
работать:
обойти, обойтиdepend
метод, нам нужно проверитьactiveEffect
эта переменная:
Так как в суд не войду, казню одиночество(Ничего не сделал), то следующий код:
наконец-то распечатано10
.
Эпилог
Я не ожидал, что семьдесят или около того строк кода будут такими извилистыми, верно? Так насколько же важен метод обучения забою коконов. Если посмотреть исходный код напрямую, в нем обязательно будут различные суждения. НапримерwatchEffect
Не вынося никаких суждений сейчас, верно? тогда, когда мы даемwatchEffect
Что происходит, когда передается параметр, не являющийся функцией? когда мы даемreactive
Что происходит, когда объекту передается массив? когда биографияMap
,Set
когда? Как насчет передачи базовых типов данных? И даже если мы сейчас не рассматриваем эти ситуации, просто передаем объект без массивов и прочего в нем,watchEffect
Также только функции передачи. Так что на самом деле есть еще немного опыта использованияVue3
изwatchEffect
другое место, то есть не вwatchEffect
Внутри измените значение реактивного объекта:
И нет никакой проблемы написать это так:
но вVue3
изwatchEffect
Не будет такой ситуации. Это потому, что еслиwatchEffect
Если операция присваивания выполняется над реагирующим объектом, она снова запустится.set
операции, тем самымProxy
перехватить, а затем объехатьnotify
Метод выше пошел,notify
будет сноваwatchEffect
Функция в нем запускается снова, и оказывается, что естьset
работать(Потому что это тот же код), а потом бежатьnotify
метод, продолжайте запускатьset
Операция вызывает бесконечный цикл.
Так что нам тоже нужно рассмотреть ситуацию с этим бесконечным циклом, но если уж так всесторонне рассматривать, то я полагаю, что объем кода довольно большой, и мы еще больше запутаемся. Итак, сначала разберитесь с этим кодом, а затем постепенно давайте посмотрим, как реальный исходный код обрабатывает эти ситуации. Или вы также можете подумать, как справиться с этими проблемами, не заглядывая сначала в исходный код, затем написать свою логику, а затем выполнить тест, если проблемы нет.Vue3
Сравните его с исходным кодом .
На этом статья подходит к концу, но это не конец, просто
响应式部分
. после этого虚拟DOM
,diff算法
,组件化
,根组件挂载
и так далее.
Если вам не терпится прочитать следующую аналитическую статью, вы также можете напрямуюнажмите на эту ссылкуВходитьcodepen
самообучение尤雨溪
написанный код. Объем кода очень маленький, учимсяVue3
принцип绝佳资料
! Изучив принцип, даже если вы не смотрите настоящий исходный код, вы можете поговорить с интервьюером во время интервью. Ведь ни один интервьюер при изучении исходного кода не спросит: Скажи мнеVue3
такого-то документа996
Какая строчка кода написана? Исследование также должно быть сосредоточено на принципе и редко изучать граничные условия различных параметров суждения. так点赞
+关注
, с последующим尤雨溪
Изучите исходный код, не теряясь!
Замечательные статьи в прошлом
- "Менеджер по продукту: Вы можете сделать так, чтобы эта строка чисел прокручивалась? 》
- «Менеджер по продукту: Начальная анимация Hongmeng очень красивая, дайте нам целую страницу»
- «Создайте собственную визуальную карту данных, не полагаясь на какую-либо библиотеку»
- «[Перевод] Юси Ю: Vue3 не будет поддерживать IE11, энергия будет вложена в Vue2.7»
- «Супер забавная новая функция Vue: введение переменных JS в CSS»
- "Какие? Можно ли добиться эффекта втягивания только с помощью тега H5? 》
- «Исправьте нецивилизованный феномен GitHub! Microsoft запускает раздел комментариев! 》
- «Vue 3.0.3: добавлена передача переменных CSS и последнее предложение Ref»
- «Двойной 11 маленький черный ящик — это очень круто? Давайте улучшим его с помощью переменных CSS! 》
- «Не стоит недооценивать Цзюгунге, один вопрос может раскрыть истинную форму кандидата! 》
- «Вопросы на собеседовании по макету мобильного терминала всесторонне изучают ваши навыки CSS (в центре)»
- «Серия запутанных действий после установки объекта-прототипа на прокси»
- «Супер забавная новая функция Vue: портал DOM»
- «Использование суперпопулярной библиотеки React CSS-in-JS в проектах Vue: стилизованные компоненты»
- «Наконец-то настала очередь Vue вдохновлять React? 》
- «Небольшая яма Vue3 под IOS»
- "Использование хуков в новой версии vue-router"
- «[Перевод] Юси Ю: Процесс проектирования Vue3»
- «Отец рефакторинга Node Deno наконец-то выпущен, заменит ли он в конечном итоге Node?» 》