頁面點擊觸發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