背景
创作背景纯粹是因为本人喜欢面向对象风格,并不是说其他风格不好,无意挑起风格流之争,不喜欢的道友求放过,我的目标是兼容现有的vue3的项目,也就是说可以在现有的项目中也可以使用。
使用
用面向对象风格实现vue官网的鼠标位置实时跟踪案例,代码如下:
@Component({
template: `
<div>{{ x }}</div>
<div>{{ y }}</div>
`
})
export default class AngularDemo implements LifecycleHook {
x = ref(0);
y = ref(0);
private update = (event: MouseEvent) => {
this.x.value = event.x;
this.y.value = event.y;
};
onMounted(): void {
window.addEventListener("mousemove", this.update);
}
onUnmounted(): void {
window.removeEventListener("mousemove", this.update);
}
}
上述代码中onMounted
,onUnmounted
是生命周期钩子,命名与vue3保持一致。
如果不想把方法属性暴露出去可以使用private
修饰LifecycleHook
完整签名如下:
export interface LifecycleHook {
onMounted?(): void;
onUpdated?(): void;
onUnmounted?(): void;
onBeforeMount?(): void;
onBeforeUpdate?(): void;
onBeforeUnmount?(): void;
onErrorCaptured?(err: unknown, instance: ComponentPublicInstance | null, info: string): boolean | void;
onRenderTracked?(e: DebuggerEvent): void;
onRenderTriggered?(e: DebuggerRenderEvent): void;
onActivated?(): void;
onDeactivated?(): void;
onServerPrefetch?(): Promise<any>;
}
依赖注入
依赖注入语法基本与Angular的语法一致,手写依赖注入实现,没有使用vue内置的依赖注入,每个组件都会绑定一个注入器跟Angular类似的层级注入,看如下示例:
@Injectable()
class RootClassK {
public update() {
}
private pMethod() {
}
}
@Injectable()
class RootClassK2 {
public update() {
}
constructor(public root: RootClassK) {
console.log(root);
}
private pMethod() {
}
}
class ClassK {
public update() {
}
private pMethod() {
}
}
const demoToken = InjectionToken<ClassK>();
@Component({
styleUrls: ["./demo.less"],
template: `
<div>{{ x }}</div>
<div>{{ y }}</div>
<CommunityIcon></CommunityIcon>
`,
providers: [
// 注册提供商
{ provide: demoToken, useClass: ClassK }
]
})
export default class AngularDemo implements LifecycleHook {
x = ref(0);
y = ref(0);
props = defineProps({
k: {}
});
private update = (event: MouseEvent) => {
this.x.value = event.x;
this.y.value = event.y;
};
constructor(@Optional() @Inject(demoToken) public demoData: ClassK, @Optional() private root: RootClassK2) {
console.log(demoData, root);
}
private pMethod() {
}
onMounted(): void {
console.log(this.demoData);
window.addEventListener("mousemove", this.update);
}
onUnmounted(): void {
window.removeEventListener("mousemove", this.update);
}
}
-
解析修饰符
可以使用
@Optional()
,@Self()
,@SkipSelf()
和@Host()
来修饰依赖解析行为
解析修饰符分为三类:- 如果找不到你要的东西该怎么办,用
@Optional()
- 从哪里开始寻找,用
@SkipSelf()
- 到哪里停止寻找,用
@Host()
和@Self()
默认情况下,始终从当前的
Injector
开始,并一直向上搜索。修饰符使你可以更改开始(默认是自己)或结束位置。另外,你可以组合除
@Host()
和@Self()
之外的所有修饰符,当然还有@SkipSelf()
和@Self()
。@Optional()
@Optional()
允许 你注入的服务视为可选服务。这样,如果无法在运行时解析它, 只会将服务解析为null
,而不会抛出错误。export default class OptionalComponent { // 如果依赖无法解析 optional则为null constructor(@Optional() public optional?: OptionalService) {} }
@Self()
使用
@Self()
仅从宿主元素注册的提供商中查找依赖。@Self()
的一个好例子是要注入某个服务,但只有当该服务在当前宿主元素上可用时才行。为了避免这种情况下出错,请将@Self()
与@Optional()
结合使用。比如,在下面的
SelfComponent
中。请注意在构造函数中注入的LeafService
。@Component({ templateUrl: './self-no-data.component.html', styleUrls: ['./self-no-data.component.css'] }) export default class SelfNoDataComponent { constructor(@Self() @Optional() public leaf?: LeafService) { } }
该例中leaf将被赋值为null,因为宿主元素并没有注册
LeafService
,不过使用了@Optional()
修饰
当依赖不存在时不会抛出异常,会被赋值为null@Component({ templateUrl: './self-no-data.component.html', styleUrls: ['./self-no-data.component.css'], providers: [ // 注册提供商 { provide: LeafService, useClass: LeafService } ] }) export default class SelfNoDataComponent { constructor(@Self() @Optional() public leaf?: LeafService) { } }
该例中leaf会被正确解析出来,因为宿主元素注册相应的提供商
@SkipSelf()
@SkipSelf()
与@Self()
相反。使用@SkipSelf()
,会从父Injector
中往上查找@Host()
当使用
@Host()
装饰器时,VuePlus
将在当前组件所在的宿主元素以及其父元素的注入器中查找依赖项。如果找不到依赖项,VuePlus
将继续向上查找,直到找到或到达根元素。@Host()
与@Self()
有相同之处,下面是不同之处总结:@Host()
会沿着组件树向上查找依赖项,直到找到或到达根元素。@Self()
仅在当前组件的注入器中查找依赖项。
- 如果找不到你要的东西该怎么办,用
Props使用@Input替代
@Component({
templateUrl: './props.component.html',
styleUrls: ['./props.component.css']
})
export default class PropsComponent {
@Input() message: string // string会自动约束message为String
@Input(true) name: string // true 必传
@Input() defaultValue = 'default' // 默认值
}
Emit使用 @Output替代
@Component({
templateUrl: './emit.component.html',
styleUrls: ['./emit.component.css']
})
export default class EmitComponent {
// 相当于 increase = defineEmits(['increase'])
@Output() increase = new EventEmitter<number>();
// 装饰器传参支持别名 相当于 increaseOther = defineEmits(['increase1'])
@Output('increase1') increaseOther = new EventEmitter<number>();
increaseHandler() {
// 相当于
// const emit = defineEmits(['increase'])
// emit(1)
this.increase.emit(1)
}
}
后续
后续会补充实现的过程 内容比较多 最后可能还会出适配这个插件的Webstrom插件的实现过程(视情况而定)
原文链接:https://juejin.cn/post/7242911173723553849 作者:晟东