Привет всем, меня зовут Мокоу. В последнее время я работаю над контентом, связанным с vue3, таким как анализ исходного кода и разработка mini-vue3.
Оглядываясь назад на содержание предыдущих глав, следующее содержание было в основном описано в предыдущих главах.
- новые инструменты сборки
viteПринцип и реализация с нуля -
vue3Используйте новую позу - новый API:
reactiveИспользование и анализ исходного кода - коллекция треков
trackРеализация и анализ исходного кода - триггер трассировки
triggerРеализация и анализ исходного кода - Отзывчивое ядро
effectа такжеtrack、triggerПринцип работы и анализ исходного кода
Хорошо, цель этой главы: создать Vue3 с нуля!
Предпосылки знатьeffectа такжеtrack、triggerПринцип работы, подробности смотрите в официальном аккаунте ->前端进阶课, внешний технический публичный аккаунт с температурой и без рекламы.
Вот простой анализ роли этих трех функций.
- отслеживать: собирать зависимости и хранить их в
targetMap - триггер: зависимость триггера, использование
targetMap - эффект: обработка побочных эффектов
Пожалуйста, смотрите исходный код этой главыuuzЗвезда отчаянно нуждается в том, чтобы свести концы с концами.
Первые две главы сериализованы:
- Vue3.x объясняет серию простых языков (сериализация 1)
- Vue3.x объясняет серию простых языков (сериализация 2)
Рука об руку для достижения Vue3
первый. Наши две глобальные переменные используются для хранения и определения местоположения зависимостей отслеживания, что должно датьtrackа такжеtriggerсклад б/у.
let targetMap = new WeakMap();
let activeEffect;
Таким образом, первый метод, который необходимо разработать, этоtrack, помнитеtrackКак это называется в vue3?
track(obj, 'get', 'x');
trackбудет искатьobj.xБудет ли отслеживаться, если не найдено, положить obj.x вtargetMap(выполнить задание по отслеживанию), поставитьobj.xИспользуйте activeEffect в качестве ключа карты в качестве значения карты.
Помимо обработки исключений значений и т.п.,trackделать только одно,activeEffectчучелоtargetMap;
function track(target, key) {
// 首先找 obj 是否有被追踪
let depsMap = targetMap.get(target);
if (!depsMap) {
// 如果没有被追踪,那么添加一个
targetMap.set(target, (depsMap = new Map()));
}
// 然后寻找 obj.x 是否被追踪
let dep = depsMap.get(key);
if (!dep) {
// 如果没有被追踪,那么添加一个
depsMap.set(key, (dep = new Set()));
}
// 如果没有添加 activeEffect 那么添加一个
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
}
}
Тогда просто напишитеtrigger,все еще помнюtriggerКак это называется в vue?
trigger(obj, 'set', 'x')
triggerпросто идиtargetMapнаходясь в поискеobj.xЗадача отслеживания, если она найдена, дедуплицирует ее, а затем выполняет задачу.
То есть: помимо ненормальной корреляции ценностей,triggerИ только одно: отtargetMapВозьмите значение, а затем вызовите значение функции.
function trigger(target, key) {
// 寻找追踪项
const depsMap = targetMap.get(target);
// 没找到就什么都不干
if (!depsMap) return;
// 去重
const effects = new Set()
depsMap.get(key).forEach(e => effects.add(e))
// 执行
effects.forEach(e => e())
}
Ну наконец тоeffect, помните как в vue3 вызывается апи воркера?
effect(() => {
console.log('run cb')
})
effectПолучает функцию обратного вызова, которая затем будет отправлена наtrack. Так что мы можем сделать этоeffect
- определить внутреннюю функцию
_effectи выполнить. - возвращать закрытие
находясь внутри_effectтакже сделал две вещи
- присвоить себе
activeEffect - воплощать в жизнь
effectПерезвоните
Отличный код уже на горизонте.
function effect(fn) {
// 定义一个内部 _effect
const _effect = function(...args) {
// 在执行是将自身赋值给 activeEffect
activeEffect = _effect;
// 执行回调
return fn(...args);
};
_effect();
// 返回闭包
return _effect;
}
Все предварительные условия выполнены, теперь приступайте к выполнению одногоreactive, Что отзывчиво к тому типу объекта API. Помните, как использовать Vue3reactive?
<template>
<button @click="appendName">{{author.name}}</button>
</template>
setup() {
const author = reactive({
name: 'mokou',
})
const appendName = () => author.name += '优秀';
return { author, appendName };
}
С помощью приведенного выше превосходного кода легко реализовать отзывчивую работу vue3. Изучив содержание предыдущих глав, мы знаем, чтоreactiveЭто достигается путем проксирования данных через Proxy.
Так что мы можем пройтиProxyзвонитьtrackа такжеtrigger,угонgetterа такжеsetterПолный адаптивный дизайн
export function reactive(target) {
// 代理数据
return new Proxy(target, {
get(target, prop) {
// 执行追踪
track(target, prop);
return Reflect.get(target, prop);
},
set(target, prop, newVal) {
Reflect.set(target, prop, newVal);
// 触发effect
trigger(target, prop);
return true;
}
})
}
Отлично. Все готово, далее монтируем нашуfake vue3Бар
export function mount(instance, el) {
effect(function() {
instance.$data && update(el, instance);
})
instance.$data = instance.setup();
update(el, instance);
}
function update(el, instance) {
el.innerHTML = instance.render()
}
Напишите демо с mini-vue3
есть тест. Обратитесь к методу записи vue3. определитьsetupа такжеrender.
const App = {
$data: null,
setup () {
let count = reactive({ num: 0 })
setInterval(() => {
count.num += 1;
}, 1000);
return {
count
};
},
render() {
return `<button>${this.$data.count.num}</button>`
}
}
mount(App, document.body)
Выполните его, это действительно отличный код. Reactive выполняется нормально, каждый разsetIntervalПосле выполнения страница перезаписывается и обновляетсяcount.numДанные.
Пожалуйста, посмотрите исходный кодuuz, ps: На 23 июля исходники уже поддерживают jsx.
над перевалом50+Строка кода, легко достижимаяvue3отзывчивый. Но закончилось ли это?
И следующие вопросы
-
ProxyОбъект должен быть передан в -
renderфункция иhфункция и правильная (функция h Vue3 теперь равна 2, а не раньшеcreateElement) - Рекурсия виртуального дома
- Перестаньте разговаривать
- -!, я не слушаю.
ref
Одним из недостатков использования реактивного является то, что Proxy может проксировать только объекты, но не примитивные типы.
Если вы вызовете этот кодnew Proxy(0, {}), браузер сообщит вамUncaught TypeError: Cannot create proxy with a non-object as target or handler
Итак, для прокси базового типа. Нам нужен новый способ, и вvue3, новый API для базовых типовref
<button >{{count}}</button>
export default {
setup() {
const count = ref(0);
return { count };
}
}
Реализация ref на самом деле очень проста: ее можно реализовать с помощью геттера, который поставляется с объектом js.
Возьмите каштан:
let v = 0;
let ref = {
get value() {
console.log('get')
return v;
},
set value(val) {
console.log('set', val)
v= val;
}
}
ref.value; // 打印 get
ref.value = 3; // 打印 set
Затем по предыдущим главамtrackа такжеtriggerможет быть легко реализованref
непосредственно на завершенный код
function ref(target) {
let value = target
const obj = {
get value() {
track(obj, 'value');
return value;
},
set value(newVal) {
if (newVal !== value) {
value = newVal;
trigger(obj, 'value');
}
}
}
return obj;
}
computed
Итак, как добитьсяcomputed?
Первое: ссылкаvue3изcomputedКак использовать
let sum = computed(() => {
return count.num + num.value + '!'
})
Слепое угадывание может получить идею, преобразуя следующуюeffectможет быть достигнуто, то есть вeffectНе выполняется в момент вызоваrunметод. Таким образом, мы можем добавитьlazyпараметр.
function effect(fn, options = {}) {
const _effect = function(...args) {
activeEffect = _effect;
return fn(...args);
};
// 添加这段代码
if (!options.lazy) {
_effect();
}
return _effect;
}
Такcomputedможно написать так
- Внутренняя реализация
effect(fn, {lazy: true})гарантияcomputedОбратные вызовы не запускаются при выполнении. - через объект
getterсвойства, вcomputedОбратный вызов выполняется при использовании. - пройти через
dirtyПредотвращение переполнения памяти.
Выходит отличный код:
function computed(fn) {
let dirty = true;
let value;
let _computed;
const runner = effect(fn, {
lazy: true
});
_computed = {
get value() {
if (dirty) {
value = runner();
dirty = false;
}
return value;
}
}
return _computed;
}
Тогда вот проблемаdirtyПосле первого выполнения установлено значениеfalseКак сбросить?
В настоящее времяvue3Решение состоит в том, чтобы датьeffectдобавить одинschedulerбороться с побочными эффектами.
function effect(fn, options = {}) {
const _effect = function(...args) {
activeEffect = _effect;
return fn(...args);
};
if (!options.lazy) {
_effect();
}
// 添加这行
_effect.options = options;
return _effect;
}
Так как естьschedulerтогда надо менятьtriggerсправиться с новымscheduler.
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const effects = new Set()
depsMap.get(key).forEach(e => effects.add(e))
// 更改这一行
effects.forEach(e => scheduleRun(e))
}
// 添加一个方法
function scheduleRun(effect) {
if (effect.options.scheduler !== void 0) {
effect.options.scheduler(effect);
} else {
effect();
}
}
Затем объедините приведенный выше код,computedЭто завершено
function computed(fn) {
let dirty = true;
let value;
let _computed;
const runner = effect(fn, {
lazy: true,
scheduler: (e) => {
if (!dirty) {
dirty = true;
trigger(_computed, 'value');
}
}
});
_computed = {
get value() {
if (dirty) {
value = runner();
dirty = false;
}
track(_computed, 'value');
return value;
}
}
return _computed;
}
Суммировать
- Ядром реактивного является
track+trigger+Proxy - ref принадлежит объекту
getterа такжеsetterСотрудничатьtrack+triggerосуществленный - вычисляется на самом деле
effectУлучшения на базе
Содержание следующей главы:vue3как совместитьjsx?
наконец
Оригинальность не так проста, пожалуйста, успокойте своего брата три раза подряд.
- Пожалуйста, посмотрите исходный кодuuz
- Содержание этой статьи взято изGitHub.com/Китай-США…
- Добро пожаловать, чтобы обратить внимание на общедоступную учетную запись «Продвинутый курс по интерфейсу», чтобы серьезно изучить интерфейс и продвигаться вместе. Отвечать
全栈илиVueЕсть подарок для вас