给vue提个PR,没想到回复的这么快,赞赞赞,但关键是mobx修复这个bug呢,还是vue来修复呢?还是我们开发者使用的姿势不对?

最近在开发一个项目,使用到了mobx+react,然后我们需要接入一个第三方的sdk擎天柱,这个sdk是使用vue开发的,然后就悲剧了,一直报错
Uncaught TypeError: 'set' on proxy: trap returned falsish for property '__proto__'
弄了一上午,终于定位到了这个问题,下面是我的debug的整个过程,大概的代码如下所示(公司项目代码就不放了,代码类似于这样)
const data = mobx.observable({
name: 'like',
age: 1,
role: [{
title: 'student',
name: "111"
}, {
title: 'student2',
name: "222"
}]
})
var app = new Vue({
el: '#app',
data:data
})
其中data是一个Proxy对象,把他赋值给vue中的data就会报错,然后,我想是不是data的问题,我试着把data数据toJS还原成一个js对象,发现就不报错了,代码如下
const data = mobx.observable({
name: 'like',
age: 1,
role: [{
title: 'student',
name: "111"
}, {
title: 'student2',
name: "222"
}]
})
var app = new Vue({
el: '#app',
data: mobx.toJS(data)
})
顺着这个报错信息 ,我们一步一步来查看到底是为什么会报错,首先new Vue的时候会调用initState方法,如果传入的data存在,就用调用initData,如图所示

在initData方法中又调用了observe方法,继续往下看(object's property keys into getter/setters,给data的属性转换成vue定制的seteter/getter方法) 调用walk方法,walk就是递归的给每个对象的属性添加方法(set/get)






到这里发现只要mobx属性中有数组,并且这个对象有__proto__,就会执行这个方法,对target.__proto__进行赋值就报错了。
hasProto = '__proto__' in {}
// 来判断浏览器是不是ie
//参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
var noIe = ('__proto__' in {});
alert(noIe);
//或者
if('__proto__' in {}){非ie}
else{ie}
其中src为 var arrayProto = Array.prototype上的方法
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);



在开发者工具中复制下面代码试了下,retturn false 果然报错了
'use strict'
const obj2 = {};
const p2 = new Proxy(obj, {
set: function(target, prop, value, receiver) {
console.log("called: " + prop + " = " + value);
return false;
}
});
p2.__proto__ = Array.prototype
VM765:10 Uncaught TypeError: 'set' on proxy: trap returned falsish for property '__proto__'
at <anonymous>:10:18
试试return true 看看会发生什么情况,哎,果然没报错
'use strict'
const obj3 = {};
const p3 = new Proxy(obj, {
set: function(target, prop, value, receiver) {
console.log("called: " + prop + " = " + value);
return true;
}
});
p3.__proto__ = Array.prototype
called: __proto__ = [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
那么mobx对这个proxy对象做了些什么,我们回过头在mobx源码里面看看,mobx确实是在严格模式下
'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
在看看他的set方法的处理,还记得开始我们对data进行操作的时候调用了observable, 接下来遍历observableFactories,把observableFactories的方法对复制到obserale中, 我们上文中知道我们是在处理数组的时候发生了错误,所以我们来看看array方法中到底做了什么,终于找到罪魁祸首了,原来mobx对array对象进行proxy处理的时候,不允许对__proto__进行改写,但是vue对其进行了改写,原来都是数组惹的祸,那么我们把数组去掉,看看会发生什么,惊奇的发现他不报错了,这么看来这个问题就得到了答案


不报错的代码(toJS或者data里面没有数组就可以)
const data = mobx.observable({
name: 'like',
age: 1,
role: [{
title: 'student',
name: "111"
}, {
title: 'student2',
name: "222"
}]
})
var app = new Vue({
el: '#app',
data: mobx.toJS(data)
})
最后,给vue提个PR,vue的posva回复真快。。。 github:github.com/daydream-li…