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
分类:
uni和小程序
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现