LoginSignup
2
1

More than 5 years have passed since last update.

Vue.jsのレンダリングがどうなってるか調べてみたけど、、、

Posted at

背景

id, classも振らずにどうやってレンダリングしてるんだろ?と思い、どんな実装になってるのか調べてみた。jQueryなどでDOM操作してた感覚からすると不思議だったので。
Vueのバージョンは2.5.17になります。

<!DOCTYPE html>
<script src="./vue2.5.17.js"></script>

<div id="app"></div>

<script>
  const vm = new Vue({
    template: '<p>{{ msg }}</p>',
    data: { msg: 'hello world!' }
  }).$mount('#app')
</script>

このように実装してみて、defineReactiveにブレークポイント設定してみると以下の順に処理されてることがわかった。

Vue
function Vue (options) {
   // options = {template: "<p>{{ msg }}</p>", data: {…}}
  ...
  this._init(options);
}
_init
Vue.prototype._init = function (options) {
  // vm = Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
  var vm = this;
  ...
  initState(vm);
  ...
};
initState
function initState (vm) {
  ...
  // opts = {components: {…}, directives: {…}, filters: {…}, _base: ƒ, template: "<p>{{ msg }}</p>", …}
  var opts = vm.$options;
  ...
  if (opts.data) {
    initData(vm);
  }
  ...
}
initData
function initData (vm) {
  // data = {msg: "hello world!", __ob__: Observer}
  var data = vm.$options.data;
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {};
  ...
  observe(data, true /* asRootData */);
}
observe
function observe (value, asRootData) {
  ...
  var ob;
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    // value = {msg: "hello world!", __ob__: Observer}
    ob = new Observer(value);
  }
  ...
  return ob
}
Observer
var Observer = function Observer (value) {
  this.value = value;
  ...
  if (Array.isArray(value)) {
    ...
  } else {
    // value = {msg: "hello world!", __ob__: Observer}
    this.walk(value);
  }
};
walk
Observer.prototype.walk = function walk (obj) {
  // obj = {msg: "hello world!", __ob__: Observer}
  var keys = Object.keys(obj); // keys = ["msg"]
  for (var i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i]);
  }
};
defineReactive
function defineReactive (
  obj, // obj = {msg: "hello world!", __ob__: Observer}
  key, // key = "msg"
  val,
  customSetter,
  shallow
) {
  var dep = new Dep(); // dep = Dep {id: 3, subs: Array(0)}
  // property = {value: "hello world!", writable: true, enumerable: true, configurable: true}, obj = {msg: "hello world!", __ob__: Observer}, key = "msg"
  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  var getter = property && property.get;
  if (!getter && arguments.length === 2) {
    val = obj[key];
  }
  var setter = property && property.set;

  var childOb = !shallow && observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if ("development" !== 'production' && customSetter) {
        customSetter();
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify();
    }
  });
}

defineReactivemsgにゲッター・セッターを持たせてるようです。
そこで、

vm.msg = 'こんにちは世界!'

とすると、defineReactiveで設定したセッターが反応して

defineReactive
dep.notify();

によりウォッチャに通知が行くみたいです。そこでウォッチャがレンダリング関数を呼び出してレンダリングされるようですが、結局このレンダリング関数を読み解かないとidclassなしでレンダリングできてる不思議は解決しなさそうだなと思ったところで力尽きました。
この記事は千 Advent Calendar 2018の23日目の予定でしたが、過ぎた上に調べきれてなくすみません。
近々続きを書きたいと思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1