webpack學習:uni運行時代碼解讀二 (頁面的交互)


頁面點擊觸發vm(vue實例)的方法

uni里的vue代碼

<template>
        <view
                class="content"
                @click="qq"
                @longpress='fq'
        >
                3345
        </view>
</template>

經過uni轉換后的小程序wxml代碼

<view data-event-opts="{{[['tap',[['qq',['$event']]]],['longpress',[['fq',['$event']]]]]}}"
 class="content" bindtap="__e" bindlongpress="__e">3345</view>

微信的小程序實例methods里只有2個方法(上文有寫),__l是用來關聯vm的父子關系的,__e是用來和data-event-opts屬性一起使用,關聯vm與小程序事件的。__e的代碼如下,
寫的挺長,其實不看無所謂,你看上文data-event-opts屬性的內容就大概知道uni是怎么讓小程序的頁面交互觸發到vm的方法里去了

function handleEvent (event) {
  event = wrapper$1(event);
  // [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
  const dataset = (event.currentTarget || event.target).dataset;
  if (!dataset) {
    return console.warn('事件信息不存在')
  }
  const eventOpts = dataset.eventOpts || dataset['event-opts']; // 支付寶 web-view 組件 dataset 非駝峰
  if (!eventOpts) {
    return console.warn('事件信息不存在')
  }

  // [['handle',[1,2,a]],['handle1',[1,2,a]]]
  const eventType = event.type;

  const ret = [];

  eventOpts.forEach(eventOpt => {
    let type = eventOpt[0];
    const eventsArray = eventOpt[1];

    const isCustom = type.charAt(0) === CUSTOM;
    type = isCustom ? type.slice(1) : type;
    const isOnce = type.charAt(0) === ONCE;
    type = isOnce ? type.slice(1) : type;

    if (eventsArray && isMatchEventType(eventType, type)) {
      eventsArray.forEach(eventArray => {
        const methodName = eventArray[0];
        if (methodName) {
          let handlerCtx = this.$vm;
          if (handlerCtx.$options.generic) { // mp-weixin,mp-toutiao 抽象節點模擬 scoped slots
            handlerCtx = getContextVm(handlerCtx) || handlerCtx;
          }
          if (methodName === '$emit') {
            handlerCtx.$emit.apply(handlerCtx,
              processEventArgs(
                this.$vm,
                event,
                eventArray[1],
                eventArray[2],
                isCustom,
                methodName
              ));
            return
          }
          const handler = handlerCtx[methodName];
          if (!isFn(handler)) {
            throw new Error(` _vm.${methodName} is not a function`)
          }
          if (isOnce) {
            if (handler.once) {
              return
            }
            handler.once = true;
          }
          let params = processEventArgs(
            this.$vm,
            event,
            eventArray[1],
            eventArray[2],
            isCustom,
            methodName
          );
          params = Array.isArray(params) ? params : [];
          // 參數尾部增加原始事件對象用於復雜表達式內獲取額外數據
          if (/=\s*\S+\.eventParams\s*\|\|\s*\S+\[['"]event-params['"]\]/.test(handler.toString())) {
            // eslint-disable-next-line no-sparse-arrays
            params = params.concat([, , , , , , , , , , event]);
          }
          ret.push(handler.apply(handlerCtx, params));
        }
      });
    }
  });

  if (
    eventType === 'input' &&
    ret.length === 1 &&
    typeof ret[0] !== 'undefined'
  ) {
    return ret[0]
  }
}

vm修改了data如何響應到頁面

vue代碼

 data() {
           return {
                  title: "Hello"
          };
  },
  methods: {
          qq() {
                  this.title = 'oo'
          }
  },

在vm的qq方法改變了data里的title的值,就會觸發vue的依賴更新,然后會執行vue的patch方法,代碼如下,uni是修改過vue的patch方法的,
其中cloneWithData和diff是比較關鍵的代碼,比較長就不貼出來了,關於diff方法不是vue原生的算法,代碼很簡單,
但是為什么這么處理我就不知道了,有一點比較特別假如說原來data的title是一個數組[1,2,35],
然后你重新賦值一個新的數組[3,5,67],只有數組長度一樣,uni會setData{title[0]: 3, title[1]: 5, title[2]: 67},
不知道uni這么做出於什么考慮,這樣可能會快一些吧。

var patch = function(oldVnode, vnode) {
    var this$1 = this;
  
    if (vnode === null) { //destroy
      return
    }
    if (this.mpType === 'page' || this.mpType === 'component') {
      var mpInstance = this.$scope;
      var data = Object.create(null);
      try {
          // 將你的vm里的data賦值到一個新的對象
        data = cloneWithData(this);
      } catch (err) {
        console.error(err);
      }
      data.__webviewId__ = mpInstance.data.__webviewId__;
      // mpdata 就是小程序實例mp的data里的值
      var mpData = Object.create(null);
      Object.keys(data).forEach(function (key) { //僅同步 data 中有的數據
        mpData[key] = mpInstance.data[key];
      });
      // vm和mp里的data兩者比較算出差別來,然后通過mp的setData賦值給mp的data里然后頁面就修改了
      var diffData = this.$shouldDiffData === false ? data : diff(data, mpData);
      if (Object.keys(diffData).length) {
        if (process.env.VUE_APP_DEBUG) {
          console.log('[' + (+new Date) + '][' + (mpInstance.is || mpInstance.route) + '][' + this._uid +
            ']差量更新',
            JSON.stringify(diffData));
        }
        this.__next_tick_pending = true;
        mpInstance.setData(diffData, function () {
          this$1.__next_tick_pending = false;
          flushCallbacks$1(this$1);
        });
      } else {
        flushCallbacks$1(this);
      }
    }
  };

用一張圖來快速理解

需要注意的地方:

一 mp和vm這2個實例是分別有自己的data


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM