v-model与响应式

189 阅读2分钟

结论:v-model其实会让该属性,成为响应式

处理这一块的函数为

function model (
 el,
 dir,
 _warn
) {
 warn$1 = _warn;
 var value = dir.value;
 var modifiers = dir.modifiers;
 var tag = el.tag;
 var type = el.attrsMap.type;

 {
   // inputs with type="file" are read only and setting the input's
   // value will throw an error.
   if (tag === 'input' && type === 'file') {
     warn$1(
       "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" +
       "File inputs are read only. Use a v-on:change listener instead.",
       el.rawAttrsMap['v-model']
     );
   }
 }

 if (el.component) {
   genComponentModel(el, value, modifiers);
   // component v-model doesn't need extra runtime
   return false
 } else if (tag === 'select') {
   genSelect(el, value, modifiers);
 } else if (tag === 'input' && type === 'checkbox') {
   genCheckboxModel(el, value, modifiers);
 } else if (tag === 'input' && type === 'radio') {
   genRadioModel(el, value, modifiers);
 } else if (tag === 'input' || tag === 'textarea') {
   genDefaultModel(el, value, modifiers);
 } else if (!config.isReservedTag(tag)) {
   genComponentModel(el, value, modifiers);
   // component v-model doesn't need extra runtime
   return false
 } else {
   warn$1(
     "<" + (el.tag) + " v-model=\"" + value + "\">: " +
     "v-model is not supported on this element type. " +
     'If you are working with contenteditable, it\'s recommended to ' +
     'wrap a library dedicated for that purpose inside a custom component.',
     el.rawAttrsMap['v-model']
   );
 }

 // ensure runtime directive metadata
 return true
}


看具体实现

主要看callBack

/**
* Cross-platform code generation for component v-model
*/
function genComponentModel (
 el,
 value,
 modifiers
) {
 var ref = modifiers || {};
 var number = ref.number;
 var trim = ref.trim;

 var baseValueExpression = '$$v';
 var valueExpression = baseValueExpression;
 if (trim) {
   valueExpression =
     "(typeof " + baseValueExpression + " === 'string'" +
     "? " + baseValueExpression + ".trim()" +
     ": " + baseValueExpression + ")";
 }
 if (number) {
   valueExpression = "_n(" + valueExpression + ")";
 }
 var assignment = genAssignmentCode(value, valueExpression);

 el.model = {
   value: ("(" + value + ")"),
   expression: JSON.stringify(value),
   callback: ("function (" + baseValueExpression + ") {" + assignment + "}")
 };
}

我们上面发现callback会执行assignment,也就是genAssignmentCode

/**
* Cross-platform codegen helper for generating v-model value assignment code.
*/
function genAssignmentCode (
 value,
 assignment
) {
 var res = parseModel(value);
 if (res.key === null) {
   return (value + "=" + assignment)
 } else {
   return ("$set(" + (res.exp) + ", " + (res.key) + ", " + assignment + ")")
 }
}

总结

v-model在emit处理会设置值外,还会$set。也就是增加响应式
// 对我我们手动实现类似v-model,主要也要加上$set。不然数据不是响应的,也就是不会更新到页面中,导致数据不同步

拓展总结

  1. Vue就是MVVM,力求所有东西都是响应式的(所以能响应的地方会加上响应,除了没法监听到除外,以及给出了补救方法)
  2. 对于对象:Vue 无法检测 property 的添加或移除
// 如果data中,为form:{}
this.form = {...} // Form及后代所有属性有响应,因为能监听到赋值form的Setter
this.form.name = 'hcl' // 是监听不到的,没有监听属性的添加和移除,proxy可以!
  1. 对于数组:数组修改操作(这里注意和对象操作的边界),如果不是有赋值操作(其实这个是对象操作),都监听不到
this.list[2] ={} // 这个是数组的操作,数组没有Setter,监听不到
this.list = [1,2] // 这个是对象操作,即给list属性赋值,当然能监听到
this.list.length // 可以监听到长度变化的响应,但不能反馈到list中,也就是监听不到
  1. 对于数组的补充
由于数组操作没有Setter,所以Vue重写了数组的操作,使之可以响应

var methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  var original = arrayProto[method];
  def(arrayMethods, method, function mutator () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var result = original.apply(this, args);
    var ob = this.__ob__;
    var inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break
      case 'splice':
        inserted = args.slice(2);
        break
    }
    if (inserted) { ob.observeArray(inserted); }
    // notify change
    ob.dep.notify();
    return result
  });
});
  1. 记住一点:我们需要时刻关注哪些会没有响应,这个会带来隐藏的bug。