uniapp-wmp-clone-with-data-patch-webpack-plugin
v1.0.0
Published
用于修复 uni-app HBuilderX 中微信小程序依赖 mp.runtime.esm.js --> cloneWithData 方法中暴力序列化的问题
Downloads
2
Readme
使用方法
- 安装插件
npm i -D uniapp-wmp-clone-with-data-patch-webpack-plugin
- 在项目根目录中添加 vue.config.js 文件并且配置插件
- 根据需要配置参数
// vue.config.js
const CloneWithDataPatchWebpackPlugin = require("uniapp-wmp-clone-with-data-patch-webpack-plugin");
module.exports = {
chainWebpack: (config) => {
config
.plugin("cloneWithDataPatch")
.use(CloneWithDataPatchWebpackPlugin, [
// 【****注意****】
// 下边这些参数都 不是必须的!! 这里写上也只是为了说明参数,并不是实际需要这样配置才行
// 实际上,没有下列参数说明的那些情况最好不要进行任何配置
{
/*
待注入方法的数组 【一般不需要修改】
1. 方法必须包含方法名,不能是匿名方法
2. 必须包含一个方法名叫copy的方法
3. 这些方法在同一个平级作用域
4. utils.copy, utils.typeOf, utils.each 是内置的 copy 以及其依赖的方法。不满足自身需求的可以自行配置
*/
injectMethods: [utils.copy, utils.typeOf, utils.each],
/*
获取说明文件输出路径的方法 【一般不需要修改】
1. 会用此文件的存在与否来检测是否有添加过补丁
2. 默认和 mp.runtime.esm.js 文件同级
3. 最好是放在HBuilderX的软件目录下,这样版本更新的时候会被删除,从而通过检测重新添加补丁
4. 可以通过 this.cwd 获取HBuilderX的Node运行时的路径
*/
getFlagFilePath() {
return PATH.join(this.wmpRuntimePath, "cloneWithDataPatch.txt");
},
/*
获取待修复文件的路径的方法 【一般不需要修改】
1. 应该只有在版本更新获取不到 mp.runtime.esm.js 这个文件的时候去修改为正确的路径
2. 可以通过 this.cwd 获取HBuilderX的Node运行时的路径
*/
getWmpRuntimeFilePath() {
return PATH.join(this.wmpRuntimePath, "mp.runtime.esm.js");
},
/*
获取待修复文件备份路径的方法 【一般不需要修改】
1. mp.runtime.esm.js 修改前的原始文件输出路径
2. 默认在 mp.runtime.esm.js 同级目录下
3. 可以通过 this.cwd 获取HBuilderX的Node运行时的路径
*/
getWmpRuntimeFileCopyPath() {
return PATH.join(this.wmpRuntimePath, "mp.runtime.esm.js.copy.js");
},
},
]);
},
};
相关说明
这个插件主要用于修复 uni-app 使用 HBuilderX 构建项目的时候在微信小程序端对组件 prop 采用
JSON.parse(JSON.stringify(ret))
暴力序列化处理,导致的prop传递的对象或者数组中包含的方法等数据被过滤的问题。参下:
// 父组件
<template>
<v-child :data="data"></v-child>
</template>
<script>
export default {
data(){
return {
// 传递 data 并且其中包含一个方法 fn
data: {
id: 1,
fn(){}
}
}
}
}
</script>
// 子组件
<script>
export default {
props: {
data: Object
},
mounted(){
// 接收 data
// 在 H5 中正常获取 fn
// 但是在微信小程序中 fn 为 undfined
console.log(this.data.fn); // undefined
}
}
</script>
问题原因在于编译到微信小程序端的时候其中HBuilderX中的一个依赖项
mp.runtime.esm.js
中的cloneWithData
方法末尾采用了JSON.parse(JSON.stringify(ret))
的方式去深度复制数据。参下:
// mp.runtime.esm.js
function cloneWithData(vm) {
var ret = Object.create(null);
var dataKeys = [].concat(
Object.keys(vm._data || {}),
Object.keys(vm._computedWatchers || {}));
dataKeys.reduce(function(ret, key) {
ret[key] = vm[key];
return ret
}, ret);
var compositionApiState = vm.__composition_api_state__ || vm.__secret_vfa_state__;
var rawBindings = compositionApiState && compositionApiState.rawBindings;
if (rawBindings) {
Object.keys(rawBindings).forEach(function (key) {
ret[key] = vm[key];
});
}
Object.assign(ret, vm.$mp.data || {});
if (
Array.isArray(vm.$options.behaviors) &&
vm.$options.behaviors.indexOf('uni://form-field') !== -1
) { //form-field
ret['name'] = vm.name;
ret['value'] = vm.value;
}
// ↓↓↓↓↓↓↓↓↓↓↓↓↓也就是这句↓↓↓↓↓↓↓↓↓↓↓↓↓↓
return JSON.parse(JSON.stringify(ret))
}
介于自己在项目中多次遇到这个问题产生的bug,而且也没有很优雅的替代方式。
这个问题19年就有人提出过,但不知道什么原因官方一直没改,所以只能自己动手了。
原理很简单,利用 webpack 插件修改这个依赖文件的这部分代码为标准的深度复制就可以了。
每次运行和打包的时候会去检测这部分逻辑然后修改,这样就避免了更新了HBuilderX之后失效的问题。
修改后的文件参下:
// mp.runtime.esm.js
// 注入深度复制方法
function cloneWithDataPatch_copy(obj){...}
// ...
function cloneWithData(vm) {
// ....
// 修改 JSON.parse(JSON.stringify(ret)) 这段代码为深度复制
return cloneWithDataPatch_copy(ret);
}