- Интервьюер: Вы видели исходный код Vue?
- Кандидат: Я видел это.
- Интервьюер: Тогда скажи мнеЕсли вы по желанию измените свойство в данных Vue, будет ли обновлено представление?
- Кандидат: Нет.
- Интервьюер: почему?
- Кандидат: если свойство не используется в шаблоне, нет необходимости обновлять представление, что часто приводит к плохой производительности.
- Интервьюер: Как это решение реализовано во Vue?
- Кандидат: во время инициализации экземпляра используйте
Object.defineProperty
Данные прослушивают свойства в данных, если используются свойства в шаблоне, они собираются классом DEP, и атрибут будет вызываться при изменении свойства.notify
Обновите вид. - Интервьюер: Тогда как узнать, какие свойства используются в шаблоне?
- Кандидат: ВТФ. . . Это не очень понятно, можете пояснить?
- Интервьюер: Хорошо, тогда я кратко объясню:
Сначала напишите простую демонстрацию, где в данных есть 4 атрибута.a,b,c,d
, атрибуты, используемые в шаблоне,a,b
. посмотреть, если толькоa,b
позвонюDep
Собирать его?
new Vue({
el: '#app',
data() {
return {
a: 1,
b: 2,
c: 3,
d: 4,
};
},
created() {
console.log(this.b);
this.b = 'aaa';
},
template: '<div>Hello World{{a}}{{b}}</div>',
});
- во вью
instance/state.js
внутри, буду использоватьproxy
проксирует каждое свойство
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
// 代理对象的属性
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
- использовать
defineReactive
Захватить каждое свойство в данных
observe(data, true /* asRootData */);
// observe
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
// defineReactive
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
// 重点在这里,后续如果在模板中使用到的属性,都会被执行reactiveGetter函数
// 被Dep类 收集起来
if (Dep.target) {
console.log(`${key} 属性 被Dep类收集了`)
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return;
}
if (setter) {
// 这里是处理computed set 函数
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
// 如果我们在更改属性时,就会调用notify 异步更新视图
dep.notify();
},
});
- воплощать в жизнь
$mount
Подключить вид
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
-
$mount
заключается в вызове метода прототипа Vue, фокус находится на последнем предложенииmount.call(this, el, hydrating)
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el);
const options = this.$options;
// resolve template/el and convert to render function
/**
* 查看render 函数是否存在?如果不存在就解析template模板
* Vue渲染页面时,有两个方式 1. template,2. render,最终所有的模板类的都需要使用render去渲染
*/
if (!options.render) {
let template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this);
}
return this;
}
} else if (el) {
// 如果模板不存在,就创建一个默认的html模板
template = getOuterHTML(el);
}
}
// 重写了Vue.prototype.$mount ,最终调用缓存的mount方法完成对$mount的挂载
return mount.call(this, el, hydrating);
};
- здесь
mount
называетсяmountComponent(this, el, hydrating)
метод, в то время какmountComponent
реализуется_render
функция, окончание_render
это вызовrender
генерироватьvnode
.
const { render, _parentVnode } = vm.$options;
vnode = render.call(vm._renderProxy, vm.$createElement);
На последней картинке видно, что
render
Функция рендерит внутреннюю часть нашего демо.template
шаблон, в конечном итоге толькоa, b
Оба свойства будутDep
Класс собрал.
Если в тексте есть ошибки, пожалуйста, укажите на них, и я буду продолжать совершенствоваться. Спасибо, Если вам нужно отладить исходный код, нажмите здесьздесь, просто следуйте инструкциям в файле readme. Надеюсь на звезду. Спасибо