Engineering note ยท Vue/Nuxt

Vue2, Vue3 Reactivity ๋น„๊ต - Vue2 ํŽธ

2022๋…„ 07์›” 12์ผ์ดํ˜„์ˆ˜
ํƒœ๊ทธVueVue2Vue Reactivity๋ฐ˜์‘์„ฑVue Rendering

Vue2 Vue3 ์˜ Reactivity ๋น„๊ต. Vue2 Reactive์— ๋Œ€ํ•œ ์„ค๋ช…. VNODE, Watcher, Dependency, ref, reactive, update, vue hook ์— ๋Œ€ํ•ด์„œ ์ „๋ฐ˜์ ์ธ ์„ค๋ช….

๋ฐ˜์‘์„ฑ?

Vue ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์—…ํ•˜๋‹ค๋ณด๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๊ฟจ์„ ๋ฟ์ธ๋ฐ ํ™”๋ฉด์— ๋ฐ”๋€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๋กœ๋ฐ”๋กœ ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ์ด ๊ทธ์ € ์ต์ˆ™ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Vue๋ฅผ ๋น„๋กฏํ•œ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ, ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ”๊ฟจ๋‹ค๊ณ  ํ™”๋ฉด์— ๋ฐ”๋กœ ๋ฟŒ๋ ค์คฌ๋Š”๊ฐ€? ์•„๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ DOM์„ ์ˆ˜์ •ํ•ด์„œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ–ˆ๋‹ค. ์•„๋‹ˆ๋ฉด ์„œ๋ฒ„์—์„œ ๋ฐ”๋€ ๋ฐ์ดํ„ฐ๋กœ ๋ Œ๋”๋งํ•œ ๊ฒฐ๊ณผ๋ฌผ์„ ์ƒˆ๋กœ ๋ฐ›์•„์•ผ ํ–ˆ๋‹ค.

Vue์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐ์ดํ„ฐ ์ •๋ณด๋งŒ ๋ฐ”๊พธ๋ฉด ํ™”๋ฉด์— ์•Œ์•„์„œ ์ ์šฉ๋œ๋‹ค. ์ด๋ฅผ ๋ฐ˜์‘์„ฑ์ด๋ผ ํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ณ€๊ฒฝ์ด ๊ฐ์ง€๋˜๋ฉด Virtual DOM์„ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•œ ํ›„ ๋งŒ๋“ค์–ด์ง„ ํŠธ๋ฆฌ๋ฅผ ์‹ค์ œ DOM์— ์ ์šฉ์‹œํ‚จ๋‹ค.

๊ฐœ๋ฐœ์ž๋Š” ์ด๋Ÿฌํ•œ ๋‚ด๋ถ€์  ๋กœ์ง์€ ์•Œ ํ•„์š” ์—†์ด ๋ฐ์ดํ„ฐ์— ์ง‘์ค‘ํ•˜๋Š” ๊ฐœ๋ฐœ์„ ํ•˜๋„๋ก Vue ๋Š” ์œ ๋„ํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐœ๋ฐœ์ž๋Š” ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ; ์—ฌ๊ธฐ์„œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š” ๋…€์„์ธ์ง€ ์•Œ์•„์•ผ ์ž…๋ง›๋Œ€๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฒ˜์Œ์—” ์‰ฌ์›Œ๋„ ์–ธ์  ๊ฐ€ ๊ตฌ์กฐ์  ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๋Š” ๊ฒŒ ๊ฐœ๋ฐœ์ž ์‚ถ์ด๋‹ค.

Vue2์—์„œ๋Š” ๋ฐ˜์‘์„ฑ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‚˜.

Untitled

Vue์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” Watcher ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. ๋˜ data option์— ๋“ฑ๋กํ•œ ๋ฐ์ดํ„ฐ๋Š” ์ž๋™์œผ๋กœ getter ์™€ setter ๋ฅผ ์ฃผ์ž…๋ฐ›๋Š”๋‹ค. ๋ฐ์ดํ„ฐ์˜ setter ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ˆœ๊ฐ„, ์ปดํฌ๋„ŒํŠธ์˜ Watcher ๊ฐ€ ์ด๋ฅผ ๊ฐ์ง€ํ•˜๊ณ , ๋ Œ๋” ํ•จ์ˆ˜๋ฅผ ๋ฐœ๋™์‹œํ‚จ๋‹ค. ๊ทธ๋กœ ์ธํ•ด Virtual DOM์„ ๋‹ค์‹œ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋˜๊ณ , ์ด๊ฒƒ์ด ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์ด๋‹ค. data ๋ฟ ์•„๋‹ˆ๋ผ computed ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. data ์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€ โ†’ computed ๋กœ์ง ์‹คํ–‰ โ†’ computed ๋ณ€๊ฒฝ ๊ฐ์ง€ โ†’ ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค.

Getter/Setter ์ฃผ์ž…

Vue์˜ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ์‚ดํŽด๋ณด๋ฉด์„œ ๋ฐ˜์‘ํ˜•์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

Vue์˜ ๋ฐ์ดํ„ฐ์— ์ฃผ์ž…ํ•˜๋Š” getter/setter์€ defineReactive ๋ผ๋Š” ํ•จ์ˆ˜์—์„œ ์ฃผ์ž…ํ•œ๋‹ค.

jsx
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 ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ๋ฐ˜์‘ํ˜•์ธ ๋Œ€์ƒ์— ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋ฉด ์•„๋ž˜ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.

  1. set ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค. (observe ํ˜น์€ proxy ๋ฅผ ํ†ตํ•ด์„œ)
  2. ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ ๋ณ€๊ฒฝ์ด ๋˜์—ˆ์„ ๋•Œ๋งŒ ์ง„ํ–‰ํ•œ๋‹ค.
  3. ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค.
  4. observe ํ•จ์ˆ˜๋กœ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ observe๋ฅผ ํ• ๋‹นํ•œ๋‹ค.
  5. dep.notify ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ๋ Œ๋”๋ง์„ ํŠธ๋ฆฌ๊น… ํ•œ๋‹ค.
  6. Watcher ๊ฐ€ queue ์— ์ถ”๊ฐ€ํ•˜๊ณ , flushSchedulerQueue ๋ฅผ ํ†ตํ•ด queue ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•œ๋‹ค.
  7. queue ์— ์žˆ๋Š” ๋ชจ๋“  watcher์˜ run ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  vue ์ธ์Šคํ„ด์Šค๋“ค์˜ updated ํ›…์„ ํ˜ธ์ถœํ•œ๋‹ค.

Untitled

์œ„ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ์˜ props ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ, props ๋ฐ์ดํ„ฐ๋„ ๋ฐ˜์‘ํ˜•์ด๋‹ค. (initProps$1)
  • ์ปดํฌ๋„ŒํŠธ์˜ inject ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ (initInjection)
  • ์ปดํฌ๋„ŒํŠธ์˜ data ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ(initData)
  • watch ๋ฅผ ๋“ฑ๋กํ•  ๋•Œ,
  • set ๋“ฑ์œผ๋กœ ์ธํ•œ Observer ํ• ๋‹น์‹œ(ob) (observe)
  • vuex state ์ดˆ๊ธฐํ™” + mutate (initData, set)
  • โ€ฆ

Watcher

jsx
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 ์„ ์‹คํ–‰ํ•œ๋‹ค.

jsx
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๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ถ€๋ชจ๋ถ€ํ„ฐ ์ž์‹ ์ˆœ์„œ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋„๋ก ํ•œ๋‹ค. (๋ถ€๋ชจ๊ฐ€ ์ž์‹๋ณด๋‹ค ์ „์— ๋งŒ๋“ค์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์—)
  2. ์ปดํฌ๋„ŒํŠธ์˜ Watcher๋“ค์€ render watcher ์˜ Watcher๋ณด๋‹ค ๋จผ์ € ์—…๋ฐ์ดํŠธํ•œ๋‹ค.
  3. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ watcher ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŒŒ๊ดด๋˜์—ˆ๋‹ค๋ฉด ๊ทธ watcher ๋“ค์€ ์Šคํ‚ต๋  ์ˆ˜ ์žˆ๋‹ค.

queue ์— ์žˆ๋Š” watcher๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ before ๊ณผ run ์„ ์ฐจ๋ก€๋Œ€๋กœ ํ˜ธ์ถœํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ before ์€ beforeUpdated ํ›…์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

๋ชจ๋‘ ์™„๋ฃŒ๋˜๋ฉด activateChildComponent ์„ ํ†ตํ•ด _inactive ๋ฅผ ์ผœ๊ณ  activated ํ›…์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ๋Œ€์ƒ ์ปดํฌ๋„ŒํŠธ๋Š” keepAlive ์†์„ฑ์„ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋“ค์ด๋‹ค.

๊ทธ ๋‹ค์Œ์œผ๋กœ๋Š” callUpdatedHooks ๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ์˜ updated ํ›…์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

jsx
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 ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ด์•ผ ํ•œ๋‹ค.

jsx
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 ์ดˆ๊ธฐํ™” ์‹œ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ๋ฐ›๋Š”๋‹ค.

jsx
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 ํ•จ์ˆ˜ ์‹คํ–‰ ์‹œ ์ž‘๋™์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. beforeMount ํ›… ์‹คํ–‰
  2. watcher ์˜ต์…˜์œผ๋กœ beforeUpdate ํ›…์„ ๋“ฑ๋กํ•œ๋‹ค.
  3. vm._render ๋ฅผ ์‹คํ–‰ํ•˜๋Š” vm._update ํ•จ์ˆ˜๋ฅผ Watcher ์˜ต์…˜์œผ๋กœ ๋„˜๊ธด๋‹ค.
  4. mounted ํ›… ์‹คํ–‰

์ผ๋ถ€ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ getter ํ•จ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆœ ์žˆ์ง€๋งŒ, ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๊ฒฐ๋ก ์ ์œผ๋กœ๋Š” vm._render ๋ฅผ Watcher ์˜ getter ๋กœ ์‹คํ–‰ํ•œ๋‹ค.

jsx
vnode = render.call(vm._renderProxy, vm.$createElement);

์ตœ์ข…์ ์œผ๋กœ๋Š” ์ปดํฌ๋„ŒํŠธ์— ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ render ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด render ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ํ…œํ”Œ๋ฆฟ์„ ๊ฐ€์ง€๊ณ  ์ƒ์„ฑ๋œ๋‹ค. ํ˜น์€ render ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜.

render ํ•จ์ˆ˜๋กœ ์ƒ์„ฑ๋œ vnode ๋ฅผ ๊ฐ€์ง€๊ณ  vm._update ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

tsx
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 ์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ

jsx
data: {
    some: 1
}

this.other = 2;

์ปดํฌ๋„ŒํŠธ์˜ data option ์— ๊ธฐ์กด์— ์„ ์–ธํ•œ some ์€ ๋ฐ˜์‘ํ˜•์ด์ง€๋งŒ, other ๋Š” ๋ฐ˜์‘ํ˜•์ด ์•„๋‹ˆ๋‹ค. other ๋ฅผ ์•„๋ฌด๋ฆฌ ๋ฐ”๊ฟ”๋„ ํ™”๋ฉด์—๋Š” ์•„๋ฌด๋Ÿฐ ์˜ํ–ฅ์„ ์ฃผ์ง€ ๋ชปํ•œ๋‹ค.

jsx
this.$set(this, 'other', 2);

ํ•˜์ง€๋งŒ Vue ์ธ์Šคํ„ด์Šค์˜ $set ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋ฐ˜์‘ํ˜•์„ ์ด๋Œ์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

object ์— assign ์œผ๋กœ ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ

Object.assign ํ˜น์€ _.extend() ๋ฅผ ํ†ตํ•œ ๊ฐ์ฒด ๋ณต์‚ฌ ์—ญ์‹œ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์†์„ฑ์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” [[Get]] ์„ ์‚ฌ์šฉํ•˜๊ณ  ์†์„ฑ์„ ์ง€์ •ํ•  ๋•Œ [[Set]] ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— getter/setter ๋ฅผ ํŠธ๋ฆฌ๊น…ํ•˜์ง€ ๋ชปํ•œ๋‹ค.

jsx
Object.assign(this.someObject, { a: 1, b: 2 }) // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฐ˜์‘์„ฑ์„ ์ด๋Œ์–ด๋‚ด์ง€ ๋ชปํ•จ.
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

๊ทธ๋ž˜์„œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด์— ๋Œ€์ƒ์„ 2๊ฐœ๋กœ ์—ฎ์–ด์„œ ๋ณ‘ํ•ฉํ•œ ํ›„์— ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

๋ฐฐ์—ด ๋ฌธ์ œ

๋ฐฐ์—ด์„ ๋‹ค๋ฃจ๋ฉด์„œ ์•„๋ž˜ ํ–‰๋™์€ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์—†๋‹ค.

  1. ๋ฐฐ์—ด์˜ ์ธ๋ฑ์Šค์— ๊ฐ’์„ ๋„ฃ์œผ๋ ค๋Š” ๊ฒƒ
  2. ๋ฐฐ์—ด์˜ ํฌ๊ธฐ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ
jsx
var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x'
vm.items.length = 2

์•„๋ž˜ ๋‘ ์ฝ”๋“œ๋Š” ๋ฐ˜์‘์„ฑ์„ ์ด๋Œ์–ด๋‚ด์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ฐฐ์—ด์€ ์–ด๋–ป๊ฒŒ ๋‹ค๋ค„์•ผ ๋ฐ˜์‘ํ˜•์ผ๊นŒ? ๋ฐฐ์—ด์˜ ํŠน์ • ํ•จ์ˆ˜๋“ค์„ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

  • push ๋ฐฐ์—ด์— ๋’ค์— ๊ฐ’์„ ์ถ”๊ฐ€
  • shift ๋ฐฐ์—ด ๋งจ ์ฒ˜์Œ ๊ฐ’์„ ๋นผ๋ƒ„
  • unshift ๋ฐฐ์—ด ์•ž์— ๊ฐ’์„ ์ถ”๊ฐ€
  • splice ์ผ๋ถ€๋ฅผ ๊ต์ฒดํ•จ
jsx
this.$set(this.items, 1, changedValue);

ํ˜น์€ $set ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ํŠน์ • ์ธ๋ฑ์Šค์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•œ๋‹ค.

Object ๋ฐฐ์—ด

jsx
animals: [
    {id: 1, comments: 'cat',},
    {id: 2, comments: 'bird',},
    {id: 3, comments: 'dog',},
]

created() {
    this.animals[3] = {id: 4, comments: 'cow'};
}

์šฐ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ํ”ํ•˜๊ฒŒ Object ๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋‹ด์•„๋†“์€ ๋ฐฐ์—ด์„ ๋‹ค๋ฃจ๊ฒŒ ๋œ๋‹ค. ์ด๋ฅผ ๋ฆฌ์ŠคํŠธ ๋ Œ๋”๋ง์„ ํ†ตํ•ด ํ™”๋ฉด์— ์‹ฌ์‹ฌ์น˜ ์•Š๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์•„๊นŒ ์–˜๊ธฐํ•˜๊ธฐ๋กœ๋Š” Vue2์˜ ๋ฐฐ์—ด์€ ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์„ ๋ฐ”๊พธ๋ ค๊ณ  ํ•˜๋ฉด ๋ฐ˜์‘์„ฑ์ด ์•ˆ๋‚˜ํƒ€๋‚œ๋‹ค๊ณ  ํ–ˆ๋‹ค.

jsx
this.animals[1].comments = "whale";

๋ฐฐ์—ด 1๋ฒˆ์˜ ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๊พธ๋ ค๊ณ  ํ•˜๋‹ˆ, ์„ธ์ƒ์—๋‚˜ ๋ฐ˜์‘ํ•œ๋‹ค! ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๊ฐ์ฒด๋Š” ๋ฐ˜์‘ํ˜•์ด๋‹ค.

Untitled

์ด๋Š” ์ฝ˜์†”์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฐ์–ด๋ณด๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋ฐฐ์—ด์˜ ๊ฒฝ์šฐ getter, setter ํ•จ์ˆ˜๋Š” ์—†์ง€๋งŒ Observer ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค.

Untitled

๋‹ค๋งŒ ๋‚ด๋ถ€์— ์žˆ๋Š” ๊ฐ์ฒด 3๊ฐœ๋Š” ๊ฐ๊ฐ ์†์„ฑ์— ๋Œ€ํ•ด์„œ getter, setter ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ๋Š” ๋ฐ˜์‘ํ˜•์ด๋‹ค.

Untitled

๋‹จ created ์—์„œ ์ธ๋ฑ์Šค๋กœ ํ• ๋‹นํ•œ ๊ฐ์ฒด์˜ ๊ฒฝ์šฐ Observer ํ• ๋‹น์ด ์•ˆ๋˜์–ด ์žˆ๋‹ค. ๋˜ getter, setter ์—ญ์‹œ ์ฃผ์ž…๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, this.animals[3].comments = "some"; ์ฝ”๋“œ๋Š” ๋ฐ˜์‘ํ˜•์ด ์•„๋‹ˆ๋‹ค.

jsx
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 ํ•  ๋•Œ๋Š”, ๋ฐ”๋€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋ฐ์ดํ„ฐ๋Š” ๋ณ€๊ฒฝ์ด ๋˜์–ด ์žˆ์ง€๋งŒ, ํ™”๋ฉด์— ์ ์šฉ๋งŒ ์•ˆ ๋˜์–ด ์žˆ๋˜ ๊ฒƒ์ด๋‹ค. ๋ Œ๋”๋ง ์‹œ์ ์— ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ ์šฉ๋œ๋‹ค.

๋‹ค์ค‘ ๋ฐฐ์—ด

jsx
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'},
  ]
}

๋งŒ์•ฝ ๋‹ค์ค‘ ๋ฐฐ์—ด์˜ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ๋ฐฐ์—ด ์š”์†Œ๊ฐ€ ๋ฐฐ์—ด์ด๊ณ , ๊ทธ ์•ˆ์—๋Š” ๊ฐ์ฒด๋“ค์ด ์žˆ๋Š” ๊ตฌ์กฐ์ด๋‹ค.

jsx
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๋ฒˆ ํ•ญ๋ชฉ์„ ๋ฐ˜์‘ํ˜•์œผ๋กœ ํ• ๋‹นํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

jsx
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 ์ค‘ ์ค‘๋ณต์ธ ํ•ญ๋ชฉ์€ ์ œ๊ฑฐํ•˜๊ณ  ์ตœ์‹ ์œผ๋กœ ์œ ์ง€ํ•œ๋‹ค.

tsx
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์— ํ‘œ์‹œ๊ฐ€ ๋ ๊นŒ ์•ˆ๋ ๊นŒ?

tsx
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 ๋ฅผ ๊ฐ•์ œ๋กœ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

tsx
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๊ฐœ

๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.