Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): 添加开关决定是否进行数组劫持 #2808

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/core/plugins/parser/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ exports = module.exports = function() {
return this.hookUnique(hookPrefix + hookName, name, prefix, source, target, ctx).then(
({ name, prefix, resolved, target, npm }) => {
if (hookName === 'raw') {
if (request.indexOf("weui-miniprogram") === 0) {
if (request.indexOf('weui-miniprogram') === 0) {
resolvedUsingComponents[name] = request;
}else{
resolvedUsingComponents[name] = url;
Expand Down
7 changes: 7 additions & 0 deletions packages/core/weapp/native/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { patchMixins, patchAppLifecycle } from '../init/index';
import { patchArrayProto } from '../observer/array';

export function app(option, rel) {
let appConfig = {};

patchMixins(appConfig, option, option.mixins);
patchAppLifecycle(appConfig, option, rel);

// 根据配置开关来决定是否要进行 Array Proto 的劫持
// 默认会劫持原生 Array 对象
if (!rel.info.DISABLE_ARRAY_PATCH) {
patchArrayProto();
}

return App(appConfig);
}
103 changes: 56 additions & 47 deletions packages/core/weapp/observer/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,69 @@ import { cleanPaths } from './observerPath';
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);

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

function delInvalidPaths(key, value, parent) {
if (isObject(value) && hasOwn(value, '__ob__')) {
// delete invalid paths
cleanPaths(key, value.__ob__.op, parent.__ob__.op);
}
}

/**
* Intercept mutating methods and emit events
* WePY 数组数据绑定时,像 Vue 一样,会对 Array 原生方法进行劫持,从而达到在使用原生方法进行数组调用时,可以同时更新绑定数据
* 但这一行为与小程序插件天然冲突,小程序插件禁止任何对原生对象的覆写。
* 因此增加一个开关 DISABLE_ARRAY_PATCH,让业务自行决定是否进行数组原生对象劫持。
* 若业务不做数组原生对象劫持时,那个任何数组操作,若想进行数组绑定,需要调用 wepy.$set
*/
methodsToPatch.forEach(function(method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const len = this.length;
// 清除已经失效的 paths
if (len > 0) {
switch (method) {
case 'pop':
delInvalidPaths(len - 1, this[len - 1], this);
break;
case 'shift':
delInvalidPaths(0, this[0], this);
break;
case 'splice':
case 'sort':
case 'reverse':
for (let i = 0; i < this.length; i++) {
delInvalidPaths(i, this[i], this);
}
export const patchArrayProto = () => {
const methodsToPatch = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const len = this.length;
// 清除已经失效的 paths
if (len > 0) {
switch (method) {
case 'pop':
delInvalidPaths(len - 1, this[len - 1], this);
break;
case 'shift':
delInvalidPaths(0, this[0], this);
break;
case 'splice':
case 'sort':
case 'reverse':
for (let i = 0; i < this.length; i++) {
delInvalidPaths(i, this[i], this);
}
}
}
}

const result = original.apply(this, args);
const ob = this.__ob__;
const vm = ob.vm;
const result = original.apply(this, args);
const ob = this.__ob__;
const vm = ob.vm;

// push parent key to dirty, wait to setData
if (vm.$dirty) {
if (method === 'push') {
const lastIndex = ob.value.length - 1;
vm.$dirty.set(ob.op, lastIndex, ob.value[lastIndex]);
} else {
vm.$dirty.set(ob.op, null, ob.value);
// push parent key to dirty, wait to setData
if (vm.$dirty) {
if (method === 'push') {
const lastIndex = ob.value.length - 1;
vm.$dirty.set(ob.op, lastIndex, ob.value[lastIndex]);
} else {
vm.$dirty.set(ob.op, null, ob.value);
}
}
}

// 这里和 vue 不一样,所有变异方法都需要更新 path
ob.observeArray(ob.key, ob.value);
// 这里和 vue 不一样,所有变异方法都需要更新 path
ob.observeArray(ob.key, ob.value);

// notify change
ob.dep.notify();
return result;
// notify change
ob.dep.notify();
return result;
});
});
});

function delInvalidPaths(key, value, parent) {
if (isObject(value) && hasOwn(value, '__ob__')) {
// delete invalid paths
cleanPaths(key, value.__ob__.op, parent.__ob__.op);
}
}
}