๋ฐ์์ฑ?
Vue ํ๋ ์์ํฌ๋ก ์ปดํฌ๋ํธ๋ฅผ ์์ ํ๋ค๋ณด๋ฉด, ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ฟจ์ ๋ฟ์ธ๋ฐ ํ๋ฉด์ ๋ฐ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก๋ฐ๋ก ๋ฐ์ํ๋ ๊ฒ์ด ๊ทธ์ ์ต์ํ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ Vue๋ฅผ ๋น๋กฏํ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ์ง ์์ ๋, ๋ฐ์ดํฐ๋ง ๋ฐ๊ฟจ๋ค๊ณ ํ๋ฉด์ ๋ฐ๋ก ๋ฟ๋ ค์คฌ๋๊ฐ? ์๋๋ค. ๊ฐ๋ฐ์๊ฐ ์ง์ DOM์ ์์ ํด์ ๋ณด์ฌ์ฃผ์ด์ผ ํ๋ค. ์๋๋ฉด ์๋ฒ์์ ๋ฐ๋ ๋ฐ์ดํฐ๋ก ๋ ๋๋งํ ๊ฒฐ๊ณผ๋ฌผ์ ์๋ก ๋ฐ์์ผ ํ๋ค.
Vue์์๋ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ ์ ๋ณด๋ง ๋ฐ๊พธ๋ฉด ํ๋ฉด์ ์์์ ์ ์ฉ๋๋ค. ์ด๋ฅผ ๋ฐ์์ฑ์ด๋ผ ํ๋ค. ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ๊ด์ฐฐํ๊ณ ์๋ค๊ฐ ๋ณ๊ฒฝ์ด ๊ฐ์ง๋๋ฉด Virtual DOM์ ๋ค์ ์ปดํ์ผํ ํ ๋ง๋ค์ด์ง ํธ๋ฆฌ๋ฅผ ์ค์ DOM์ ์ ์ฉ์ํจ๋ค.
๊ฐ๋ฐ์๋ ์ด๋ฌํ ๋ด๋ถ์ ๋ก์ง์ ์ ํ์ ์์ด ๋ฐ์ดํฐ์ ์ง์คํ๋ ๊ฐ๋ฐ์ ํ๋๋ก Vue ๋ ์ ๋ํ๊ณ ์๋ค. ํ์ง๋ง ๊ฐ๋ฐ์๋ ๋ด๊ฐ ์ฌ์ฉํ๋ ๋๊ตฌ; ์ฌ๊ธฐ์๋ ํ๋ ์์ํฌ๊ฐ ์ด๋ป๊ฒ ์๋ํ๋ ๋ ์์ธ์ง ์์์ผ ์ ๋ง๋๋ก ์์ ํ ์ ์๊ณ , ๋ฌธ์ ํด๊ฒฐ์ ํ ์ ์๋ค. ์ฒ์์ ์ฌ์๋ ์ธ์ ๊ฐ ๊ตฌ์กฐ์ ๋ฌธ์ ์ ์ง๋ฉดํ๋ ๊ฒ ๊ฐ๋ฐ์ ์ถ์ด๋ค.
Vue2์์๋ ๋ฐ์์ฑ์ ์ด๋ป๊ฒ ๊ตฌํํ๋.

Vue์ ๋ชจ๋ ์ปดํฌ๋ํธ๋ Watcher ๋ฅผ ๊ฐ๊ณ ์๋ค. ๋ data option์ ๋ฑ๋กํ ๋ฐ์ดํฐ๋ ์๋์ผ๋ก getter ์ setter ๋ฅผ ์ฃผ์
๋ฐ๋๋ค. ๋ฐ์ดํฐ์ setter ๊ฐ ์คํ๋๋ ์๊ฐ, ์ปดํฌ๋ํธ์ Watcher ๊ฐ ์ด๋ฅผ ๊ฐ์งํ๊ณ , ๋ ๋ ํจ์๋ฅผ ๋ฐ๋์ํจ๋ค. ๊ทธ๋ก ์ธํด Virtual DOM์ ๋ค์ ๊ตฌ์ฑํ๊ฒ ๋๊ณ , ์ด๊ฒ์ด ํ๋ฉด์ ๋ํ๋๋ ๊ฒ์ด๋ค. data ๋ฟ ์๋๋ผ computed ๋ ๋ง์ฐฌ๊ฐ์ง๋ค. data ์ ๋ณ๊ฒฝ ๊ฐ์ง โ computed ๋ก์ง ์คํ โ computed ๋ณ๊ฒฝ ๊ฐ์ง โ ๋ ๋๋ง์ ์์๋ก ์ด๋ฃจ์ด์ง๋ค.
Getter/Setter ์ฃผ์
Vue์ ์ฝ๋๋ฅผ ์กฐ๊ธ ์ดํด๋ณด๋ฉด์ ๋ฐ์ํ์ ์ด๋ป๊ฒ ๊ตฌํํ๋์ง ๊ฐ๋จํ๊ฒ ์์๋ณด์.
Vue์ ๋ฐ์ดํฐ์ ์ฃผ์
ํ๋ getter/setter์ defineReactive ๋ผ๋ ํจ์์์ ์ฃผ์
ํ๋ค.
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
if (process.env.NODE_ENV !== 'production') {
dep.depend({
target: obj,
type: "get" /* TrackOpTypes.GET */,
key: key
});
}
else {
dep.depend();
}
if (childOb) {
childOb.dep.depend();
if (isArray(value)) {
dependArray(value);
}
}
}
return isRef(value) && !shallow ? value.value : value;
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
if (!hasChanged(value, newVal)) {
return;
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
}
else if (getter) {
// #7981: for accessor properties without setter
return;
}
else if (isRef(value) && !isRef(newVal)) {
value.value = newVal;
return;
}
else {
val = newVal;
}
childOb = !shallow && observe(newVal, false, mock);
if (process.env.NODE_ENV !== 'production') {
dep.notify({
type: "set" /* TriggerOpTypes.SET */,
target: obj,
key: key,
newValue: newVal,
oldValue: value
});
}
else {
dep.notify();
}
}
});
ํด๋น ํจ์๋ ๊ฐ์ฒด์ ์์ฑ๋ง๋ค ์คํ๋๋๋ฐ, ๊ทธ ์์ฑ์ ๋ํ enumerable , configurable ๊ณผ get , set ์ Object.defineProperty ๋ก ์ถ๊ฐํ๋ค. ๋ฐ์ํ์ธ ๋์์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ฉด ์๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
setํจ์๊ฐ ์คํ๋๋ค. (observe ํน์ proxy ๋ฅผ ํตํด์)- ์๋ก์ด ๋ฐ์ดํฐ๋ก ๋ณ๊ฒฝ์ด ๋์์ ๋๋ง ์งํํ๋ค.
- ์๋ก์ด ๋ฐ์ดํฐ๋ก ๊ฐ์ ๋ฐ๊พผ๋ค.
observeํจ์๋ก ์๋ก์ด ๋ฐ์ดํฐ์ ๋ํ observe๋ฅผ ํ ๋นํ๋ค.dep.notify๊ฐ ๋ฐ์ํ๊ณ , ๋ ๋๋ง์ ํธ๋ฆฌ๊น ํ๋ค.- Watcher ๊ฐ queue ์ ์ถ๊ฐํ๊ณ ,
flushSchedulerQueue๋ฅผ ํตํด queue ๋ฅผ ํ๋ฌ์ํ๋ค. - queue ์ ์๋ ๋ชจ๋ watcher์
runํจ์๋ฅผ ํธ์ถํ๊ณ vue ์ธ์คํด์ค๋ค์updatedํ ์ ํธ์ถํ๋ค.

์ ํจ์๊ฐ ์คํ๋๋ ๋ถ๋ถ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ปดํฌ๋ํธ์ props ๋ฅผ ์ด๊ธฐํํ ๋, props ๋ฐ์ดํฐ๋ ๋ฐ์ํ์ด๋ค. (initProps$1)
- ์ปดํฌ๋ํธ์ inject ๋ฅผ ์ด๊ธฐํํ ๋ (initInjection)
- ์ปดํฌ๋ํธ์ data ๋ฅผ ์ด๊ธฐํํ ๋(initData)
- watch ๋ฅผ ๋ฑ๋กํ ๋,
- set ๋ฑ์ผ๋ก ์ธํ Observer ํ ๋น์(ob) (observe)
- vuex state ์ด๊ธฐํ + mutate (initData, set)
- โฆ
Watcher
var Watcher = /** @class */ (function () {
Watcher.prototype.addDep = function (dep) {};
Watcher.prototype.update = function () {
if (this.lazy) {
this.dirty = true;
}
else if (this.sync) {
this.run();
}
else {
queueWatcher(this);
}
};
// ...
return Watcher;
}());
์ฐ์ ์ฝ๋๋ ๋ง์ ๋ถ๋ถ์ ์๋ตํ๋ค. Watcher ์ ๊ฒฝ์ฐ set ๋ฐ์ ์ notify ๋ฅผ ์ ๋ฌํ Dependency ๋ค์ ๊ด๋ฆฌํ๋ค. notify ๋ฐ์ ์ queueWatcher ๋ฅผ ํตํด ํ์ ์๊ธฐ ์์ ์ ๋ด์์ ์คํ์ ๊ธฐ๋ค๋ฆฐ๋ค. Watcher๊ฐ sync ์์ฑ์ ๊ฐ์ก๋ค๋ฉด ํ์ ๋ฃ์ด์ ๋น๋๊ธฐ๋ก ์๋ํ์ง ์๊ณ , ๋๊ธฐ์ ์ผ๋ก ๋ฐ๋ก run ์ ์คํํ๋ค.
function flushSchedulerQueue() {
currentFlushTimestamp = getNow();
flushing = true;
var watcher, id;
queue.sort(function (a, b) { return a.id - b.id; });
for (index$1 = 0; index$1 < queue.length; index$1++) {
watcher = queue[index$1];
if (watcher.before) {
watcher.before();
}
id = watcher.id;
has[id] = null;
watcher.run();
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1;
if (circular[id] > MAX_UPDATE_COUNT) {
warn$2('You may have an infinite update loop ' +
(watcher.user
? "in watcher with expression \"".concat(watcher.expression, "\"")
: "in a component render function."), watcher.vm);
break;
}
}
}
var activatedQueue = activatedChildren.slice();
var updatedQueue = queue.slice();
resetSchedulerState();
callActivatedHooks(activatedQueue);
callUpdatedHooks(updatedQueue);
}
์ฌ๊ธฐ์ ํ์ Watcher ๋ค์ ์คํํ๊ธฐ ์ ์ ์ ๋ ฌ์ ํ๋๋ฐ, ์ด์ ๋ 3๊ฐ์ง๊ฐ ์๋ค.
- ์ปดํฌ๋ํธ๋ค์ด ๋ถ๋ชจ๋ถํฐ ์์ ์์๋ก ์ ๋ฐ์ดํธ๋๋๋ก ํ๋ค. (๋ถ๋ชจ๊ฐ ์์๋ณด๋ค ์ ์ ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์)
- ์ปดํฌ๋ํธ์ Watcher๋ค์ render watcher ์ Watcher๋ณด๋ค ๋จผ์ ์ ๋ฐ์ดํธํ๋ค.
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์ watcher ๊ฐ ์คํ๋ ๋ ์์ ์ปดํฌ๋ํธ๊ฐ ํ๊ดด๋์๋ค๋ฉด ๊ทธ watcher ๋ค์ ์คํต๋ ์ ์๋ค.
queue ์ ์๋ watcher๋ฅผ ์ํํ๋ฉฐ before ๊ณผ run ์ ์ฐจ๋ก๋๋ก ํธ์ถํ๋ค. ์ฌ๊ธฐ์ before ์ beforeUpdated ํ
์ ๋ฐ์์ํจ๋ค.
๋ชจ๋ ์๋ฃ๋๋ฉด activateChildComponent ์ ํตํด _inactive ๋ฅผ ์ผ๊ณ activated ํ
์ ๋ฐ์์ํจ๋ค. ๋์ ์ปดํฌ๋ํธ๋ keepAlive ์์ฑ์ ๊ฐ์ง ์ปดํฌ๋ํธ๋ค์ด๋ค.
๊ทธ ๋ค์์ผ๋ก๋ callUpdatedHooks ๋ฅผ ํตํด ์ปดํฌ๋ํธ์ updated ํ
์ ๋ฐ์์ํจ๋ค.
Watcher.prototype.run = function () {
if (this.active) {
var value = this.get();
if (value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) || this.deep) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
var info = "callback for watcher \"".concat(this.expression, "\"");
invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info);
}
else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};
๊ฒฐ๊ตญ์ Watcher ์ run ํจ์๋ฅผ ์ํด์ ์ ๊ณผ์ ๋ค์ ์ด์ฌํ ์งํํ ๊ฒ์ด๋ค. ๊ทธ๋ ๋ค๋ฉด run ํจ์๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ค ๊ฒ์ ์ฒ๋ฆฌํ ๊น?
๋๋ฒ๊น
์ ๊ฑธ์ด๋ณด๋ฉด ์๊ฒ ์ง๋ง, ํ๋ฉด์ ๋ ๋๋ง์ this.get ์์ ๋๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ค๋ก๋ ์ฝ๋ฐฑ์ ์คํํ๋ ๊ฒ ๋ฟ์ด๋ค. ๊ทธ๋ผ get ํจ์๋ฅผ ์ดํด๋ด์ผ ํ๋ค.
Watcher.prototype.get = function () {
pushTarget(this);
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm);
}
catch (e) {
if (this.user) {
handleError(e, vm, "getter for watcher \"".concat(this.expression, "\""));
}
else {
throw e;
}
}
finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value;
};
๋จ์ํ this.getter ๋ฅผ ํธ์ถํ๋ค. ์ด getter ํจ์๋ Watcher ์ด๊ธฐํ ์ ์ธ๋ถ๋ก๋ถํฐ ๋ฐ๋๋ค.
function mountComponent(vm, el, hydrating) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
}
callHook$1(vm, 'beforeMount');
var updateComponent;
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = function () {
var name = vm._name;
var id = vm._uid;
var startTag = "vue-perf-start:".concat(id);
var endTag = "vue-perf-end:".concat(id);
mark(startTag);
**var vnode = vm._render();**
mark(endTag);
measure("vue ".concat(name, " render"), startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure("vue ".concat(name, " patch"), startTag, endTag);
};
}
else {
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
}
var watcherOptions = {
before: function () {
if (vm._isMounted && !vm._isDestroyed) {
callHook$1(vm, 'beforeUpdate');
}
}
};
if (process.env.NODE_ENV !== 'production') {
watcherOptions.onTrack = function (e) { return callHook$1(vm, 'renderTracked', [e]); };
watcherOptions.onTrigger = function (e) { return callHook$1(vm, 'renderTriggered', [e]); };
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, watcherOptions, true /* isRenderWatcher */);
hydrating = false;
// flush buffer for flush: "pre" watchers queued in setup()
var preWatchers = vm._preWatchers;
if (preWatchers) {
for (var i = 0; i < preWatchers.length; i++) {
preWatchers[i].run();
}
}
if (vm.$vnode == null) {
vm._isMounted = true;
callHook$1(vm, 'mounted');
}
return vm;
}
mountComponent ํจ์ ์คํ ์ ์๋์ ๋ค์๊ณผ ๊ฐ๋ค.
- beforeMount ํ ์คํ
- watcher ์ต์ ์ผ๋ก beforeUpdate ํ ์ ๋ฑ๋กํ๋ค.
vm._render๋ฅผ ์คํํ๋vm._updateํจ์๋ฅผ Watcher ์ต์ ์ผ๋ก ๋๊ธด๋ค.- mounted ํ ์คํ
์ผ๋ถ ๊ฒฝ์ฐ์ ๋ฐ๋ผ getter ํจ์๊ฐ ๋ฌ๋ผ์ง ์ ์์ง๋ง, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๊ฒฐ๋ก ์ ์ผ๋ก๋ vm._render ๋ฅผ Watcher ์ getter ๋ก ์คํํ๋ค.
vnode = render.call(vm._renderProxy, vm.$createElement);
์ต์ข ์ ์ผ๋ก๋ ์ปดํฌ๋ํธ์ ๋์ ์ผ๋ก ์์ฑ๋ render ํจ์๋ฅผ ์คํํ๊ฒ ๋๋ค. ์ด render ํจ์๋ ์ปดํฌ๋ํธ์ ํ ํ๋ฆฟ์ ๊ฐ์ง๊ณ ์์ฑ๋๋ค. ํน์ render ํจ์๋ฅผ ์ง์ ๊ตฌํํ๊ฑฐ๋.
render ํจ์๋ก ์์ฑ๋ vnode ๋ฅผ ๊ฐ์ง๊ณ vm._update ํจ์๋ฅผ ์คํํ๋ค.
Vue.prototype._update = function (vnode, hydrating) {
var vm = this;
var prevEl = vm.$el;
var prevVnode = vm._vnode;
var restoreActiveInstance = setActiveInstance(vm);
vm._vnode = vnode;
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false);
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode);
}
restoreActiveInstance();
if (prevEl) {
prevEl.__vue__ = null;
}
if (vm.$el) {
vm.$el.__vue__ = vm;
}
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el;
}
};
vm.__patch__ ๋ฅผ ํธ์ถํ๋ฉด์, ์ค์ DOM;์ฌ๊ธฐ์๋ vm.$el ์ ์
๋ฐ์ดํธํ๋ค.
Vue2์ ๋ฐ์์ฑ ๋ฌธ์ ๋ฐ ํน์ง
Vue2์ ๋ฐ์์ฑ ๊ตฌํ์๋ ๋ฌธ์ ๊ฐ ์๋ค. ์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ํ๊ณ ๋๋ฌธ์ ๋ฐ์ํ ๊ฒ์ด๋ค. Vue์ ๋ฐ์ดํฐ๋ค์ ์ถ๊ฐ, ๊ฐ์๊ฐ ๋ฐ์ํ์ ๋๋ ๋ณํ๋ฅผ ๊ฐ์งํ์ง ๋ชปํ๋ค. getter ์ setter ๋ฅผ ์ฃผ์
๋ฐ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ด๋ค.
data option ์ ๋ฐ์ดํฐ ์ถ๊ฐํ๊ธฐ
data: {
some: 1
}
this.other = 2;
์ปดํฌ๋ํธ์ data option ์ ๊ธฐ์กด์ ์ ์ธํ some ์ ๋ฐ์ํ์ด์ง๋ง, other ๋ ๋ฐ์ํ์ด ์๋๋ค. other ๋ฅผ ์๋ฌด๋ฆฌ ๋ฐ๊ฟ๋ ํ๋ฉด์๋ ์๋ฌด๋ฐ ์ํฅ์ ์ฃผ์ง ๋ชปํ๋ค.
this.$set(this, 'other', 2);
ํ์ง๋ง Vue ์ธ์คํด์ค์ $set ํจ์๋ฅผ ์ด์ฉํด ๋ฐ์ํ์ ์ด๋์ด๋ผ ์ ์๋ค.
object ์ assign ์ผ๋ก ์๋ก์ด ํ๋กํผํฐ ์ถ๊ฐํ๊ธฐ
Object.assign ํน์ _.extend() ๋ฅผ ํตํ ๊ฐ์ฒด ๋ณต์ฌ ์ญ์ ๊ฐ์งํ์ง ๋ชปํ๋ค. ์์ฑ์ ๊ฐ์ ธ์ฌ ๋๋ [[Get]] ์ ์ฌ์ฉํ๊ณ ์์ฑ์ ์ง์ ํ ๋ [[Set]] ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ getter/setter ๋ฅผ ํธ๋ฆฌ๊น
ํ์ง ๋ชปํ๋ค.
Object.assign(this.someObject, { a: 1, b: 2 }) // ์ด๋ ๊ฒ ํ๋ฉด ๋ฐ์์ฑ์ ์ด๋์ด๋ด์ง ๋ชปํจ.
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
๊ทธ๋์ ์๋ก์ด ๊ฐ์ฒด์ ๋์์ 2๊ฐ๋ก ์ฎ์ด์ ๋ณํฉํ ํ์ ์๋ก์ด ๊ฐ์ฒด๋ก ๋ฐํํด์ผ ํ๋ค.
๋ฐฐ์ด ๋ฌธ์
๋ฐฐ์ด์ ๋ค๋ฃจ๋ฉด์ ์๋ ํ๋์ ๋ณํ๋ฅผ ๊ฐ์งํ ์ ์๋ค.
- ๋ฐฐ์ด์ ์ธ๋ฑ์ค์ ๊ฐ์ ๋ฃ์ผ๋ ค๋ ๊ฒ
- ๋ฐฐ์ด์ ํฌ๊ธฐ๋ฅผ ๋ฐ๊พธ๋ ๊ฒ
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x'
vm.items.length = 2
์๋ ๋ ์ฝ๋๋ ๋ฐ์์ฑ์ ์ด๋์ด๋ด์ง ๋ชปํ๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ฐฐ์ด์ ์ด๋ป๊ฒ ๋ค๋ค์ผ ๋ฐ์ํ์ผ๊น? ๋ฐฐ์ด์ ํน์ ํจ์๋ค์ ์ด์ฉํด์ผ ํ๋ค.
- push ๋ฐฐ์ด์ ๋ค์ ๊ฐ์ ์ถ๊ฐ
- shift ๋ฐฐ์ด ๋งจ ์ฒ์ ๊ฐ์ ๋นผ๋
- unshift ๋ฐฐ์ด ์์ ๊ฐ์ ์ถ๊ฐ
- splice ์ผ๋ถ๋ฅผ ๊ต์ฒดํจ
this.$set(this.items, 1, changedValue);
ํน์ $set ํจ์๋ฅผ ์ด์ฉํด ํน์ ์ธ๋ฑ์ค์ ๊ฐ์ ๋ณ๊ฒฝํ๋ค.
Object ๋ฐฐ์ด
animals: [
{id: 1, comments: 'cat',},
{id: 2, comments: 'bird',},
{id: 3, comments: 'dog',},
]
created() {
this.animals[3] = {id: 4, comments: 'cow'};
}
์ฐ๋ฆฌ๋ ๊ฐ๋ฐ์ ํ๋ฉด์ ํํ๊ฒ Object ๋ฅผ ์ฌ๋ฌ๊ฐ ๋ด์๋์ ๋ฐฐ์ด์ ๋ค๋ฃจ๊ฒ ๋๋ค. ์ด๋ฅผ ๋ฆฌ์คํธ ๋ ๋๋ง์ ํตํด ํ๋ฉด์ ์ฌ์ฌ์น ์๊ฒ ๋ณด์ฌ์ค๋ค. ๊ทธ๋ฐ๋ฐ ์๊น ์๊ธฐํ๊ธฐ๋ก๋ Vue2์ ๋ฐฐ์ด์ ์ธ๋ฑ์ค์ ํด๋นํ๋ ๊ฐ์ ๋ฐ๊พธ๋ ค๊ณ ํ๋ฉด ๋ฐ์์ฑ์ด ์๋ํ๋๋ค๊ณ ํ๋ค.
this.animals[1].comments = "whale";
๋ฐฐ์ด 1๋ฒ์ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊พธ๋ ค๊ณ ํ๋, ์ธ์์๋ ๋ฐ์ํ๋ค! ๋ฐฐ์ด ๋ด๋ถ์ ๊ฐ์ฒด๋ ๋ฐ์ํ์ด๋ค.

์ด๋ ์ฝ์์ ๋ฐ์ดํฐ๋ฅผ ์ฐ์ด๋ณด๋ฉด ๊ฐ๋จํ๊ฒ ์ ์ ์๋ค. ๋ฐฐ์ด์ ๊ฒฝ์ฐ getter, setter ํจ์๋ ์์ง๋ง Observer ๋ฅผ ๊ฐ๊ณ ์๋ค.

๋ค๋ง ๋ด๋ถ์ ์๋ ๊ฐ์ฒด 3๊ฐ๋ ๊ฐ๊ฐ ์์ฑ์ ๋ํด์ getter, setter ๋ฅผ ๊ฐ๊ณ ์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋ฐฐ์ด ๋ด๋ถ์ ๊ฐ์ฒด์ ๋ํด์๋ ๋ฐ์ํ์ด๋ค.

๋จ created ์์ ์ธ๋ฑ์ค๋ก ํ ๋นํ ๊ฐ์ฒด์ ๊ฒฝ์ฐ Observer ํ ๋น์ด ์๋์ด ์๋ค. ๋ getter, setter ์ญ์ ์ฃผ์
๋์ง ์์๊ธฐ ๋๋ฌธ์, this.animals[3].comments = "some"; ์ฝ๋๋ ๋ฐ์ํ์ด ์๋๋ค.
const newObject = {
"id": 1,
"comments": "์๋ก์ด ์ค๋ธ์ ํธ๋ฅผ ๋ฃ์!",
};
this.animals[1] = newObject; // ๋ฐ์ํ x
this.$set(this.animals, 1, newObject); // ๋ฐ์ํ o
this.animals[1].comments = "๋ด๊ฐ ๋ฐ๊พธ์๋ค!!!"; // ๋ฐ์ํ o
this.$set(this.animals[1], 'comments', "๋ด๊ฐ ๋ฐ๊พธ์๋ค!!!"); // ๋ฐ์ํ o
๋ฌผ๋ก ์ ์ฝ๋๋ฅผ ํ๋์ ํจ์์์ ์คํํ๋ฉด ๋ชจ๋ ๋ณ๊ฒฝ๋์์ด ๋ฐ์ํ์ด๋ค. ์๋ํ๋ฉด ๋ฐ์์ ๊ฐ์งํ๊ณ re-rendering ํ ๋๋, ๋ฐ๋ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ์ ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ์ดํฐ๋ ๋ณ๊ฒฝ์ด ๋์ด ์์ง๋ง, ํ๋ฉด์ ์ ์ฉ๋ง ์ ๋์ด ์๋ ๊ฒ์ด๋ค. ๋ ๋๋ง ์์ ์ ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ด ์ ์ฉ๋๋ค.
๋ค์ค ๋ฐฐ์ด
twoDimensionalArray: [
[
{id: 1, cat: 'first'},
{id: 2, cat: 'first'},
],
[
{id: 1, cat: 'second'},
{id: 2, cat: 'second'},
],
[],
]
created() {
this.twoDimensionalArray[2] = [
{id: 1, cat: 'third'},
{id: 2, cat: 'third'},
]
this.twoDimensionalArray[3] = [
{id: 1, cat: 'forth'},
{id: 2, cat: 'forth'},
]
}
๋ง์ฝ ๋ค์ค ๋ฐฐ์ด์ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ๋ ๊น? ๋ฐฐ์ด ์์๊ฐ ๋ฐฐ์ด์ด๊ณ , ๊ทธ ์์๋ ๊ฐ์ฒด๋ค์ด ์๋ ๊ตฌ์กฐ์ด๋ค.
const newObject = {
"id": 1,
"comments": "์๋ก์ด ์ค๋ธ์ ํธ๋ฅผ ๋ฃ์!",
};
this.twoDimensionalArray[1][1] = newObject // ๋ฐ์ํ x
this.$set(this.twoDimensionalArray[1], 1, newObject); // ๋ฐ์ํ o
this.twoDimensionalArray[0][0].cat = "๋ด๊ฐ ๋ฐ๊พธ์๋ค!!!"; // ๋ฐ์ํ o
this.twoDimensionalArray[2][0].cat = "์๋ก ๋ฃ์ ์ค๋ธ์ ํธ์ธ๋ฐ ๋ฐ๋๊น?"; // ๋ฐ์ํ x
this.$set(this.twoDimensionalArray[3][1], 'comments', "๋ด๊ฐ ๋ฐ๊พธ์๋ค!!!"); // ๋ฐ์ํ x
๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐฐ์ด์ ์ธ๋ฑ์ค์ ์ง์ ํ ๋นํ๋ ๊ฒ์ ๋ฌด์กฐ๊ฑด ๋ฐ์ํ์ด ์๋๋ค.
2๋ฒ ์ธ๋ฑ์ค์ ๊ฐ์ฒด๋, ์ฒ์์๋ ๋น ๋ฐฐ์ด์ด๋ค๊ฐ ์ธ๋ฑ์ค์ ์ง์ ํ ๋น๋์๋ค. ๊ทธ๋์ ๋ฐ์ํ ์ค์ ์ด ๋์ง ์์๊ณ , ๋ค์ค๋ฐฐ์ด ์์ ๊ฐ์ฒด ์์ฑ์ ๋ฐ๊พธ๋ ค๊ณ ํ์ ๋ ๋ฐ์์ด ์๋ค. ์ฒ์๋ถํฐ ํ ๋น์ด ๋์ด ์๊ฑฐ๋, push ๋ฑ์ผ๋ก ์ถ๊ฐ๋ ํญ๋ชฉ์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ ์ํฉ์ผ๋ก 0๋ฒ ์ธ๋ฑ์ค์ 1๋ฒ ์ธ๋ฑ์ค๋ ๋ฐ์ํ์ผ๋ก ๋ด๋ถ ์์ฑ์ ๋ณ๊ฒฝํ๋ ค๊ณ ํ ๋ ๋ฐ์ํ์ด๋ค.
3๋ฒ ์ธ๋ฑ์ค๋ ์ฒ์๋ถํฐ ๊ฐ์ฒด์ ์๋ค๊ฐ, ์ธ๋ฑ์ค๋ก ํ ๋น๋ ๊ฐ์ฒด์ด๋ค. 2๋ฒ๊ณผ ์ฌ์ค์ ๊ฐ์ ์ ํ์ด๋ค. ์ด ๊ฒฝ์ฐ $set ํจ์๋ก๋ ๋ฐ์์ด ์๋ค.
3๋ฒ ํญ๋ชฉ์ด์ผ push ๋ฅผ ํตํด ํญ๋ชฉ์ ์ถ๊ฐํ๋ฉด ๋๋ค๊ณค ํ์ง๋ง, 2๋ฒ ํญ๋ชฉ์ ๋ฐ์ํ์ผ๋ก ํ ๋นํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
this.twoDimensionalArray[2].push(...[
{id: 1, cat: 'third'},
{id: 2, cat: 'third'},
])
this.twoDimensionalArray.splice(2, 1, [
{id: 1, cat: 'third'},
{id: 2, cat: 'third'},
]);
- spread ๋ฌธ์ผ๋ก ๋ด์ฉ๋ฌผ์ ํผ์ณ์ push ๋ฅผ ํ๋ ๋ฐฉ๋ฒ
spliceํจ์๋ก ๋์ ๊ต์ฒดํ๊ธฐ
๋ฐฐ์ด์ ๊ฐ๋จํ๊ฒ ์๊ฐํ๋ฉด ๊ผญ ํจ์๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค. ๊ทธ๋ผ ์ฝ๊ฒ ๋ฐ์ํ์ ๋์ด๋ผ ์ ์๋ค.
๋น๋๊ธฐ ์ ๋ฐ์ดํธ ํ
์ด ๋ถ๋ถ์ ๋ฑํ ๋ฌธ์ ๋ ์๋์ง๋ง ์์๋๋ฉด ์ข์ Vue ์ ํน์ฑ์ด๋ค. Vue ์ UI ์ ๋ฐ์ดํธ๋ฅผ ์ํด ์ปดํ์ผ๋๋ Virtual DOM์ ๋น๋๊ธฐ๋ก ์์ฑ๋๊ณ ํ๋ฉด์ ๋ ๋๋ง๋๋ค. ์ฆ, queue ์ ๋ด๊ธด ์ฌ๋ฌ ๋ณ๊ฒฝ์ฌํญ๋ค์ flush ํ๊ธฐ ์ ๊น์ง๋ ๋ฐ๋ก ํ๋ฉด์ ์ ์ฉ๋์ง ์๊ณ , ํ ๋ฒ์ ๋ ๋๋งํ๋ค. ๊ทธ ์์ ์ ๋ค์ tick ์ด ๋๋ค. ๋, queue ์ ๋ด๊ธด Watcher ์ค ์ค๋ณต์ธ ํญ๋ชฉ์ ์ ๊ฑฐํ๊ณ ์ต์ ์ผ๋ก ์ ์งํ๋ค.
template: `<span>{{msg}}</span>`,
ata: {
msg: "before update",
}
methods: {
update: () => {
this.msg = "updated";
console.log(this.$el.textContent) // => 'not updated'
this.$nextTick(function () {
console.log(this.$el.textContent) // => 'updated'
})
}
}
๊ฐ๋จํ๊ฒ update ํจ์๋ฅผ ํตํด ๋ณ๊ฒฝ๋ msg ๋ ์ค์ DOM์ ์ ์ฉ๋๊ธฐ ์ ์ด๋ผ์ ํจ์ ์คํ ์ค์๋ ์ด์ ๋ฐ์ดํฐ๋ก ์ฌ์ ํ ๋จ์ ์๋ค. ๊ทธ๋ฌ๋ค ํจ์ ํธ์ถ์ด ๋๋๊ณ , ๋ค์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ๋ฐ์ํ ๋๊ฐ ๋์ด์์ผ ๊ฐฑ์ ๋ ๋ฐ์ดํฐ๊ฐ ํ๋ฉด์ ๋ณด์ธ๋ค. ์ด๋ฅผ ์ถ์ ํ ์ ์๋ ๊ฒ this.$nextTick ์ด๋ค. ์ด๋ ํจ์์ ํธ์ถ์ ๋ค์ tick ์ผ๋ก ๋ฏธ๋ฃจ๋ ์์
์ด๋ค. tick ์ ๋ํด์๋ ์ถํ์ ์ด๋ฒคํธ ๋ฃจํ์ ๋ํด์ ์์ฑํ ๋ ์์ธํ๊ฒ ๋ค๋ฃฐ ๊ฒ์ด๋ค.
created ์ mounted
์์ Vue ์ปดํฌ๋ํธ๋ mount ์์ ์ ๋ ๋๋ง์ ํ ๋ฒ ์คํํ๋ค๋ ๊ฒ์ ์์๋ค. beforeMounted โ ๋ ๋๋ง โ mounted ์์๋ก ํ ์ด ์คํ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ํ์์ด ์๋ค. created ํ ๊ณผ mounted ํ ์์ data ์ ๋ฐ์ํ์ด ์๋ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ๋ฅผ ํ๋ฉด UI์ ํ์๊ฐ ๋ ๊น ์๋ ๊น?
template: `
<div v-for="item of notReactive" :key="item.id">
{{item}}
</div>
`,
created() {
this.notReactive[1] = {id: 2, cat: 'reactive from created'};
},
mounted() {
this.notReactive[2] = {id: 3, cat: 'not reactive from mounted'};
},
data: () => {
return {
notReactive: [
{id: 1, cat: 'reactive'},
]
}
}
๋ง๋ก๋ ํท๊ฐ๋ฆด ์ ์์ผ๋ ์์๋ฅผ ๋ณด์. 0๋ฒ ์ธ๋ฑ์ค๋ ๋ฐ์ดํฐ ์ด๊ธฐํ ์์ ์ ์ด๋ฏธ ํ ๋น๋์ด ์๊ธฐ ๋๋ฌธ์ ๋น์ฐํ๊ฒ๋ ๋ฐ์ํ์ด๋ค. ๊ทธ๋ ๋ค๋ฉด, 1๋ฒ ์ธ๋ฑ์ค์ 2๋ฒ ์ธ๋ฑ์ค๋ ๋ฐ์ํ์ผ๊น? ์ฐ๋ฆฌ๊ฐ ํ
์คํธํ์ ๋๋ ๋ฐฐ์ด์ ์ธ๋ฑ์ค์ ํ ๋นํ ๋์์ ๋ฐ์ํ์ด ์๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, UI์๋ 0๋ฒ ์ธ๋ฑ์ค ํญ๋ชฉ๋ง ๋ณด์ผ ๊ฑฐ๋ผ๊ณ ์์ํ๋ค. ๊ฒฐ๊ณผ๋ 0๋ฒ 1๋ฒ ์ธ๋ฑ์ค ํญ๋ชฉ์ ์ถ๋ ฅ๋๊ณ , 2๋ฒ ์ธ๋ฑ์ค ํญ๋ชฉ์ ์ถ๋ ฅ๋์ง ์๋๋ค. ์ฆ, created ์์ ๋ฃ์ ๊ฐ์ฒด๋ ๋ ๋๊ฐ ๋์๊ณ , mounted ์์ ๋ฃ์ ๊ฐ์ฒด๋ ๋ฌด์๋๋ค. ๋ ๋๋ง๊ณผ ํ
์ ์์ ๋๋ฌธ์ด๋ค. created ์์ ์์๋ ๋ ๋๋ง ์ ์ด๊ธฐ ๋๋ฌธ์, ๋ ๋๋ง ์์ ์ ๋ฐ์ดํฐ๋ ์ธ์ง๋๊ณ ์ ์ฉ๋๋ค. ํ์ง๋ง mounted ์์ ์๋ ์ด๋ฏธ ๋ ๋๋ง์ด ๋๋ ์์ ์ด๋ผ, ์ด๋ ์ถ๊ฐ๋ ๋ฐ์ดํฐ๋ ๊ฐ๋ง ๋ฐ๋ ์ฑ ๋ ๋๋ง์ ๋ฌด์๋๋ค. ์ฌ๋ด์ผ๋ก mounted ์์ ๋ฃ์ ๋ฐ์ดํฐ๋ this.$forceUpdate ๋ฅผ ํธ์ถํ๋ฉด ํ๋ฉด์ ๋ณด์ธ๋ค. watcher.update ๋ฅผ ๊ฐ์ ๋ก ํธ์ถํ๊ธฐ ๋๋ฌธ์ด๋ค.
methods: {
onClick() {
// ์๋๋ฅผ ๊ฐ๋ณ๋ก ์คํ
// ํ๋ฒ์ ์คํํ๋ฉด 1๋ฒ ํ์ด ๋ฐ์ํ์ด๊ธฐ ๋๋ฌธ์, ๋ชจ๋ ๋ ๋๋ง ๋๋ค.
this.notReactive[0].cat = 'reactive from onClick'; // ๋ฐ์ํ
this.$set(this.notReactive[1], 'cat', 'reactive from onClick') // ๋ฐ์ํ x
this.notReactive[1].cat = 'reactive from onClick'; // ๋ฐ์ํ x
this.notReactive[2].cat = 'reactive from onClick'; // ๋ฐ์ํ x
}
},
mount ์์ ์ ๋ ๋๋ง๋์ด ํ๋ฉด์ ๋ํ๋ฌ๋ค๊ณ ํด์ ๋ฐ์ํ์ธ ๊ฑด ์๋๋ค. mount ์ดํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊พธ๋ ค๊ณ ํ๋ฉด ๋ฐ์ํ์ด ์๋๊ธฐ ๋๋ฌธ์ ๋ฐ์์ด ์๋ค.
๋๊ธ 0๊ฐ