Ниже приводится оригинальный текст официального сайта: когда компонент определен, данные должны быть объявлены как функция, которая возвращает исходный объект данных, поскольку компонент может использоваться для создания нескольких экземпляров. Если бы данные по-прежнему были чистым объектом, все экземпляры имели бы общую ссылку на один и тот же объект данных! Предоставляя функцию данных, каждый раз, когда создается новый экземпляр, мы можем вызывать функцию данных, которая возвращает новую копию исходного объекта данных.
В последнее время на собеседование пришло много людей. Я всегда задаю вопрос: «Почему во Vue данные — это метод, возвращающий объект, а не присваивающий его непосредственно объекту?», лишь немногие ответят, что они боятся многократного создания экземпляров и заставляют несколько экземпляров совместно использовать объект данных. . Больше людей отвечают, что не знают, или что официальная документация требует, чтобы это было написано так.
На самом деле, контрольная точка этого вопроса — не что иное, как знакомство с vue, изучение способности кандидата к самостоятельному обучению и жажда знаний о технологиях. Такие люди склонны быстро развиваться в технологиях и обладают сильными навыками независимого решения проблем. Это также человек, которого любит каждая техническая команда.
Во-первых, в исходном коде Vue есть такая обработка:
// vue/src/core/instance/state.js
function initData (vm: Component) {
var data = vm.$options.data;
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
if (!isPlainObject(data)) {
data = {};
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
);
}
...
}
Очевидно, что vue поддерживает использование объекта в качестве значения атрибута данных в параметре построения vue, и, если данные являются методом, он сначала получит результат объекта, возвращаемый внутренне. И в vuex есть такое использование:
// vuex/src/store.js
function resetStoreVM (store, state, hot) {
...
const silent = Vue.config.silent
Vue.config.silent = true
store._vm = new Vue({
data: {
?state: state
},
computed
})
...
}
Что тут происходит? Поскольку он поддерживается, он не позволит нам его использовать, а когда мы находимся в vue-файле, прямое присвоение объекта данным вызовет красное предупреждение:
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
Это предупреждение исходит из vue/src/core/util/options.js в исходном коде Vue.
strats.data = function (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) {
if (childVal && typeof childVal !== 'function') {
process.env.NODE_ENV !== 'production' && warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.',
vm
)
return parentVal
}
return mergeDataOrFn(parentVal, childVal)
}
return mergeDataOrFn(parentVal, childVal, vm)
}
Прежде всего, нам нужно понять, что процесс создания экземпляра кода из файла vue в компонент vue должен пройти следующие этапы:
- Файл vue обрабатывается загрузчиком, шаблон компилируется в функцию рендеринга, а скрипт компилируется в объектную переменную
- Передайте скомпилированный объект сценария в рендеринг и вызовите vue.createElement (из vue/src/core/vdom/create-element.js) в функции рендеринга, чтобы построить компонент vue.
- В createElement, если это компонент vue, создайте компонент через createComponent (vue/src/core/vdom/create-component.js)
- Выньте объектную переменную, скомпилированную сценарием, через $options контекста и используйте Vue.extends (vue/src/core/global-api/extend.js) для создания нового объекта Vue через этот объект.
В 4 из-за использования mergeOptions срабатывала проверка типа данных и отображалось предупреждение в начале.
Так действительно ли присвоение объекту свойств объекта создает общий объект? Давайте посмотрим на код ниже:
class A {
constructor(opt) {
this.opt = opt;
}
update() {
this.opt.data.a++;
}
notify() {
console.log(this.opt);
}
}
Мы используем этот класс для виртуализации конструкции Vue. Затем проверьте:
// test
let c = new A({ data: { a: 1 }});
let d = new A({ data: { a: 1 }});
c.update();
d.update();
c.notify(); // Object data: a: 2
Мы передаем свойство объекта для параметра конструктора буквально, но с удивлением обнаруживаем, что нет проблемы с общей ссылкой. Что за чертовщина?
О, нет, когда мы используем vue, мы обычно экспортируем объект в файл vue, а затем этот объект будет скомпилирован и передан в функцию рендеринга после компиляции шаблона во время vue-loader. Итак, давайте проведем эксперимент по-другому:
// test.js文件,用于虚拟vue文件导出的vue options对象
export default {
data: {
a: 1
}
}
// index.js
let a = new A(test);
let b = new A(test);
a.update();
b.update();
a.notify(); // Object data: a: 3
Какой? Здесь возникает проблема общих ссылок, упомянутая в документации vue. Почему это?
Причина кроется в процессе компиляции vue и импортированном процессе импорта.При компиляции babel test.js будет преобразован в файл js с синтаксисом es5:
var Re = {
data: {
a: 1
}
};
var Oe = function () {
function e(t) {
Object(i["a"])(this, e), this.opt = t
}
return Object(o["a"])(e, [{
key: "update",
value: function () {
this.opt.data.a++
}
}, {
key: "notify",
value: function () {
console.log(this.opt)
}
}]), e
}(),
Fe = new Oe(Re),
Ne = new Oe(Re);
Fe.update(), Ne.update(), Fe.notify();
var $e = new Oe({
data: {
a: 1
}
}),
Ve = new Oe({
data: {
a: 1
}
});
$e.update(), Ve.update(), $e.notify(),
какие? Получается, что каждый наш vue-файл компилируется babel, напрямую заменяя экспортируемый объект объектной переменной, а затем передавая эту переменную в соответствующий конструктор компонента. Поэтому возникает проблема разделения ссылок (ссылаются на все js-объекты).
Поскольку исходный код vue не был прочитан, сообщите, если есть какие-либо ошибки.