«Внешняя оптимизация» — не злоупотребляйте этим для чтения данных в данных в Vue.

интервью Vue.js

предисловие

В Vue опция данных — это хорошо: вбрасывая данные, вы можете читать данные в данных через это в любом месте компонента Vue. Но чтобы не злоупотреблять этим, чтобы читать данные в данных, что касается того, где избежать злоупотребления, и к каким последствиям это приведет, этот столбец будет раскрываться один за другим.

1. Процесс чтения данных в данных с этим

В исходном коде Vue данные в data будут добавлены с помощью функции получения и функции установки, чтобы преобразовать их в отзывчивые. Код функции геттера выглядит следующим образом:

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
}

Когда данные в data читаются с этим, будет запущена функция получения, в которойvar value = getter ? getter.call(obj) : val;Выполнить после получения значенияreturn value, для достижения цели чтения данных.

Но есть и промежуточный фрагмент кода, в котором зависимости собираются с помощью ряда сложных логических операций. Как собирать зависимости здесь не рассказывается, если вы хотите узнать больше, вы можете прочитатьэтот столбец. Здесь вам нужно только знать, что зависимости будут собраны, когда существует Dep.target.

Здесь можно сделать вывод, когда существует Dep.target, используйте его для чтения данных в данных для сбора зависимостей. Если вы злоупотребите этим для чтения данных в данных, зависимости будут собираться повторно, что приведет к проблемам с производительностью.

2. Когда существует Dep.target?

Dep.target назначается зависимостями. Зависимость также известна как Наблюдатель (слушатель) или подписчик. В Vue есть три типа зависимостей, два из которых очень распространены, а именно watch (слушатель) и вычисляемый (вычисляемое свойство). Существует также скрытая зависимость, Rendering Watcher, которая создается во время первого рендеринга шаблона.

Dep.target назначается при создании зависимости, а зависимость создается с помощью конструктора Watcher.

function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
    //...
    if (typeof expOrFn === 'function') {
        this.getter = expOrFn;
    } else {
        this.getter = parsePath(expOrFn);
    }
    this.value = this.lazy ? undefined : this.get();
};
Watcher.prototype.get = function get() {
    pushTarget(this);
    try {
        value = this.getter.call(vm, vm);
    } catch (e) {
        
    }
    return value
};
Dep.target = null;
var targetStack = [];
function pushTarget(target) {
    targetStack.push(target);
    Dep.target = target;
}

Метод экземпляра будет выполнен в конце конструктора Watcherget, в методе экземпляраgetвыполнить вpushTarget(this)Назначен Dep.target в .

Зависимость создается при первом рендеринге страницы или компонента Vue, поэтому проблема с производительностью должна заключаться в медленном рендеринге в первый раз.

3. Где злоупотреблять этим, чтобы читать данные в данных

Выполнение этих кодов, которые злоупотребляют этим для чтения данных в данных, когда существует Dep.target, вызовет проблемы с производительностью, поэтому необходимо выяснить, где эти коды записываются для выполнения.Другими словами, чтобы выяснить, где неправильное использование этого для чтения данных в данных может вызвать проблемы с производительностью.

Во втором разделе вводится, что Dep.target будет выполняться после назначенияvalue = this.getter.call(vm, vm)this.getterЭто функция, поэтому, если вы используете ее для чтения данных, она будет собирать зависимости, и если ею злоупотреблять, это вызовет проблемы с производительностью.

this.getterприсваивается при создании зависимостей, и каждая зависимостьthis.getterне то же самое. Давайте представим их один за другим.

  • смотреть (слушатель) зависимыйthis.getterдаparsePathФункция, параметр функции которой является объектом для прослушивания.

    var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]"));
    function parsePath(path) {
        if (bailRE.test(path)) {
            return
        }
        var segments = path.split('.');
        return function(obj) {
            for (var i = 0; i < segments.length; i++) {
                if (!obj) {
                    return
                }
                obj = obj[segments[i]];
            }
            return obj
        }
    }
    

    в коде, показанном нижеaиa.b.cПередать как параметрparsePathФункция возвращает функцию, присвоеннуюthis.getter,воплощать в жизньthis.getter.call(vm, vm)получитеthis.aиthis.a.b.cзначение .В этом процессе не будет сцены злоупотребления этим для чтения данных в данных.

    watch:{
      a:function(newVal, oldVal){
        //做点什么
      }
    }
    vm.$watch('a.b.c', function (newVal, oldVal) {
      // 做点什么
    })
    
  • вычисляемый (вычисляемое свойство) зависимыйthis.getterИх два, если значение вычисляемого свойства является функцией, тоthis.getterэто функция. Если значение вычисляемого свойства является объектом, тоthis.getterЭто значение атрибута получения этого объекта, а значение атрибута получения также является функцией. В этой функции могут быть сценарии, в которых эта функция используется для чтения данных в данных.Например, код выглядит следующим образом.

    computed:{
        d:function(){
            let result = 0;
            for(let key in this.a){
                if(this.a[key].num > 20){
                    result += this.a[key].num + this.b + this.c;
                }else{
                    result += this.a[key].num + this.e + this.f;
                }
            }
            return result;
        }
    }
    

    в вычисляемом свойствеdСуществует злоупотребление этим для чтения данных данных. вthis.aпредставляет собой массив, в настоящее время значение Dep.target является вычисляемым свойствомdЭта зависимость в циклеthis.aиспользуйте это, чтобы получитьa,b,c,e,fданные, которые подвергаются ряду сложных логических операций для повторного сбора вычисляемых свойствdэта зависимость. приводит к получению вычисляемого свойстваdЗначение , замедляет работу, создавая проблемы с производительностью.

  • Наблюдатель за рендерингомthis.getterэто такая функция:

updateComponent = function() {
  vm._update(vm._render(), hydrating);
};

вvm._render()Функция рендеринга, созданная шаблоном шаблона, будет преобразована в виртуальный DOM (VNode):vnode = render.call(vm._renderProxy, vm.$createElement);, приведите пример, иллюстрирующий, что такое функция рендеринга render.

Например шаблон шаблона:

<template>
  <div class="wrap">
    <p>{{a}}<span>{{b}}</span></p>
  </div>
</template>

Функция рендеринга рендеринга генерируется через vue-loader следующим образом:

(function anonymous() {
    with(this) {
        return _c('div', {
            attrs: {
                "class": "wrap"
            }
        }, [_c('p', [_v(_s(a)), _c('span', [_v(_s(b))])])])
    }
})

вwithЦелью оператора является указание объекта по умолчанию для оператора или группы операторов, например.with(this){ a + b }эквивалентthis.a + this.b, затем используйте его в шаблоне шаблона{{ a }}Скорее используйте это для чтения данных вaданные. Следовательно, в рендере функции рендеринга, сгенерированном шаблоном шаблона, также может быть сцена злоупотребления этим для чтения данных в данных. Например, код выглядит так:

<template>
  <div class="wrap">
    <div v-for=item in list>
      <div> {{ arr[item.index]['name'] }} </div>
      <div> {{ obj[item.id]['age'] }} </div>
    </div>
  </div>
</template>

которые используютv-forциклlistВ процессе массива постоянно используйте это для чтения данныхarr,objДанные, заставляющие эти данные выполнять ряд сложных логических операций для повторного сбора этой зависимости, что приводит к более медленному начальному рендерингу, что приводит к проблемам с производительностью.

4. Как не злоупотреблять этим для чтения данных в данных

Таким образом, неправильное использование этого для чтения данных в данных в вычисляемых свойствах и шаблонах шаблонов приведет к повторному сбору зависимостей, что приведет к проблемам с производительностью.Как избежать этой ситуации.

  • Как избежать вычисляемых свойств

Используйте назначение деструктурирования объекта ES6, чтобы избежать, значение вычисляемого свойства является функцией, параметр которой является экземпляром VuethisObject, приведенный выше пример неправильного использования this в вычисляемых свойствах можно оптимизировать следующим образом.

До оптимизации:

computed:{
    d:function(){
        let result = 0;
        for(let key in this.a){
            if(this.a[key].num > 20){
                result += this.a[key].num + this.b + this.c;
            }else{
                result += this.a[key].num + this.e + this.f;
            }
        }
        return result;
    }
}

Оптимизировано:

computed: {
  d({ a, b, c, e, f }) {
    let result = 0;
    for (let key in a) {
      if (a[key].num > 20) {
        result += a[key].num + b + c;
      } else {
        result += a[key].num + e + f;
      }
    }
    return result;
  }
}

Вышеприведенное использует назначение деструктурирования для продвижения данных в данных.a,b,c,e,fприсвоить соответствующей переменнойa,b,c,e,f, а затем данные данных могут быть доступны через эти переменные в вычисляемом свойстве без запуска зависимой коллекции соответствующих данных в данных. Таким образом, данные в data считываются только один раз, и запускается только одна коллекция зависимостей, что позволяет избежать проблем с производительностью, вызванных повторяющимися коллекциями зависимостей.

  • Как избежать шаблона шаблона

обработка досрочноv-forДанные, используемые в цикле, а не вv-forПрочитайте данные массива и типа объекта в цикле. Приведенный выше пример злоупотребления этим в шаблоне шаблона можно оптимизировать следующим образом.

Предположениеlist,arr,objвсе данные, возвращаемые сервером, иarrиobjОн не используется ни в одном модульном рендеринге и может быть оптимизирован таким образом.

До оптимизации:

<template>
  <div class="wrap">
    <div v-for=item in list>
      <div> {{ arr[item.index]['name'] }} </div>
      <div> {{ obj[item.id]['age'] }} </div>
    </div>
  </div>
</template>

Оптимизировано:

<template>
  <div class="wrap">
    <div v-for=item in listData>
      <div{{item.name}} </div>
        <div>{{item.age}}</div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
    }
  },
  created(){
    // 在这里定义arr和obj避免被转成响应式
    this.arr = [];
    this.obj = {};
  },
  computed: {
    listData: function ({list}) {
      list.forEach(item => {
        item.name = this.arr[item.index].name;
        item.age = this.obj[item.id].age;
      })
      return list;
    }
  },
}
</script>