Skip to content

[BUG]v8.0.0-rc.1,使用createDecorator创建自定义装饰器,如果用在多次继承的类里,会导致混乱 #548

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

Open
gowiny opened this issue Jul 19, 2021 · 5 comments

Comments

@gowiny
Copy link

gowiny commented Jul 19, 2021

代码文件:prop.ts

import { createDecorator } from 'vue-class-component'

function createProp(params:any, target: any, propertyKey: string,descriptor?: PropertyDescriptor){
createDecorator(function (options, key) {
if(!options.props){
options.props = {}
}
const props = options.props
if(descriptor && typeof params.default === 'undefined'){
params.default = function(){ return descriptor.value }
if(options.methods){
delete options.methods[key]
}
}
//添加新的prop 到 options.props里
props[key] = params
})(target, propertyKey);
}

export function Prop(params?:any):any;
export function Prop(target: any, propertyKey: string):void;
export function Prop(target: any, propertyKey: string,descriptor: PropertyDescriptor):void;
export function Prop(target: any={}, propertyKey="",descriptor?: PropertyDescriptor):void | any {
if(propertyKey){
createProp({},target,propertyKey,descriptor)
}else{
const params = target;
return function (target: any, propertyKey: string,descriptor?: PropertyDescriptor) {
createProp(params,target,propertyKey,descriptor)
}
}
}


代码文件:my-test-input.vue
<template>
<div></div>
</template>
<script lang="ts">
import { Vue} from 'vue-class-component';
import { Prop } from '@/lib-ref/vue-class';
class MyTestSuper extends Vue {
@prop({
type:String,
default:'MyTestSuper'
})
declare readonly label:string
}

class MyTestBase1 extends MyTestSuper {
@prop({
type:String,
default:'MyTestBase1'
})
declare readonly label:string
}

export default class MyTestInput extends MyTestSuper {

}
</script>


测试文件:Test.vue

<template>
<div class="test">
<my-test-input ref="testComp" ></my-test-input>
<el-button @click="test">测试</el-button>
</div>
</template>
<script lang="ts">
import { Options,Vue } from 'vue-class-component';
import { refs} from '@/utils/app-utils';
import MyTestInput from '@/components/my/my-test/my-test-input.vue';
@Options({
components:{
MyTestInput
}
})
export default class Test extends Vue {
test(){
const testComp = refs(this,'testComp')
console.log('testComp label',testComp.label)
}
}
</script>


使用createDecorator方法定义了一个新的装饰器:Prop

MyTestSuper 类是基类
MyTestBase1 和 MyTestInput 都继承MyTestSuper 类
MyTestBase1 类里覆盖了MyTestSuper 类的 label属性定义

在测试页面点击按钮测试。
**实际输出的 label 值 是 'MyTestBase1' **
期望输出的 label 值应该是 MyTestInput 的父类MyTestSuper 里定义的 默认值:'MyTestSuper'


经过调试源码发现,createDecorator 方法里 是把 工厂函数 factory,添加到 类的构造函数的 __d 属性里,
但因为多重继承的原因,createDecorator 里取到的 Ctor.__d 都是基类 MyTestSuper 的引用,而非重新创建的数组。
所以,所有子类 使用 自定义装饰器 都 添加了一条记录到 基类的 __d里了。而子类是引用的 基类 __d,导致
MyTestSuper 的所有子类里定义的prop 都应用到所有子类,即便是非直接继承的类。

附上源代码文件:
bug-src.zip

@gowiny
Copy link
Author

gowiny commented Jul 19, 2021

我把createDecorator方法里的源代码修改了一下,请参考

原代码:
export function createDecorator(
factory: (options: ComponentOptions, key: string, index: number) => void
): VueDecorator {
return (
target: VueBase | VueConstructor<VueBase>,
key?: any,
index?: any
) => {
const Ctor =
typeof target === 'function'
? target
: (target.constructor as VueConstructor)
if (!Ctor.__d) {
Ctor.__d = []
}
if (typeof index !== 'number') {
index = undefined
}
Ctor.__d.push((options) => factory(options, key, index))
}
}


修改后的新代码:

export function createDecorator(
factory: (options: ComponentOptions, key: string, index: number) => void
): VueDecorator {
return (
target: VueBase | VueConstructor<VueBase>,
key?: any,
index?: any
) => {
const Ctor:any =
typeof target === 'function'
? target
: (target.constructor as VueConstructor)
if (!Ctor.__d) {
Ctor.__d = []
Ctor.__d.__n = Ctor.name
}else if(Ctor.__d.__n !== Ctor.name){
Ctor.__d = [].concat(Ctor.__d)
Ctor.__d.__n = Ctor.name
}
if (typeof index !== 'number') {
index = undefined
}
Ctor.__d.push((options:any) => factory(options, key, index))
}
}

就是把原来的
if (!Ctor.__d) {
Ctor.__d = []
}

改为了

if (!Ctor.__d) {
Ctor.__d = []
Ctor.__d.__n = Ctor.name
}else if(Ctor.__d.__n !== Ctor.name){
Ctor.__d = [].concat(Ctor.__d)
Ctor.__d.__n = Ctor.name
}

@Niekvdm
Copy link

Niekvdm commented Aug 5, 2021

Had to use google translate to understand what is going on but this does solve the problem with inherited classes.
@gowiny Perhaps you can make a PR on the main branch?

@nseb
Copy link

nseb commented Aug 17, 2021

There is a release final date for support vue 3 ?

@gowiny
Copy link
Author

gowiny commented Aug 17, 2021

I don't know how to PR

@df257
Copy link

df257 commented Aug 28, 2021

这里说中文,外国人看到懂吗。。。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants