Vue3写一个面向对象插件系列3(使用篇3)
背景
创作背景纯粹是因为本人喜欢面向对象风格,并不是说其他风格不好,无意挑起风格流之争,不喜欢的道友求放过,我的目标是兼容现有的vue3的项目,也就是说可以在现有的项目中也可以使用。
响应性
@Component({
template: `<div>{{ testProxy }}</div>`
})
export default class OOPDemo {
// 字段会通过Proxy包裹在 reactive
testProxy = 'testProxy'
// 使用了ref初始化的字段不会被reactive包裹
refField = ref([]);
// 使用了@Input()修饰的字段不会被reactive包裹
@Input() inputField!: string;
// 使用了@Output()修饰的字段不会被reactive包裹
@Output() outputField = new EventEmitter<string>();
// 使用了@ViewChild()修饰的字段不会被reactive包裹
@ViewChild() htmlRef!: HTMLDivElement;
}
上述例子中只有字段testProxy
具有响应性,也就是说 this.testProxy='xxx' 会触发UI更新,当然refField字段本身就具有响应性
getter/setter
-
字段只有
get
修饰@Component({ template: `<div>{{ getter }}</div>` }) export default class OOPDemo { // 字段会通过Proxy包裹在 reactive testProxy = 'testProxy' get getter() { return this.testProxy + '1' } }
当accessor字段只有被
get
修饰时, 底层会被转换为只读的computed
-
字段同时被
get
和set
修饰@Component({ template: `<div>{{ accessorField }}</div>` }) export default class OOPDemo { // 字段会通过Proxy包裹在 reactive testProxy = 'testProxy' get accessorField() { return this.testProxy + '1' } set accessorField(value: string) { return this.testProxy = value } }
当accessor字段只有被
get
修饰时, 底层会被转换为可写的computed
, 使用者无需通过.value
去更新computed
, 也就是说这样 this.accessorField='xxx' 就可以来更新computed
路由
-
获取路由对象
@Component({ template: `` }) export default class OOPDemo { constructor(public router: Router, public route: Route) { } }
通过依赖注入即可获取到
Router
和Route
对象 -
监听
Route
属性route
对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该避免监听整个route
对象。在大多数情况下,你应该直接监听你期望改变的参数。@Component({ template: `` }) export default class OOPDemo { @Watch<OOPDemo>(context => context.route.parmas.id) idChange(newId: string) { console.log(newId); } constructor(public router: Router, public route: Route) { } }
-
路由守卫
@Component({ template: `` }) export default class OOPDemo implements LifecycleHook { @Watch<OOPDemo>(context => context.route.parmas.id) idChange(newId: string) { console.log(newId); } constructor(public router: Router, public route: Route) { } onBeforeRouteLeave(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext): ReturnType<NavigationGuard> { console.log(to, 'onBeforeRouteLeave'); next() } onBeforeRouteUpdate(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext): ReturnType<NavigationGuard> { console.log(to, 'onBeforeRouteUpdate'); } }
-
onBeforeRouteLeave
在当前路由改变,但是该组件被复用时调用, 举例来说,对于一个带有动态参数的路径/users/:id
,在/users/1
和/users/2
之间跳转的时候, 由于会渲染同样的OOPDemo
组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例this
-
onBeforeRouteUpdate
在导航离开渲染该组件的对应路由时调用
-
Pina
-
定义state
export class UpdateUser {
constructor(public payload: any) {
}
}
@State<UserEntity>({
name: "User",
default: {
age: 10,
name: "张三",
sex: 1
}
})
export default class UserState {
@Getter()
static getName(state: UserEntity): string {
return state.name
}
@Getter()
static sexFormat(state: UserEntity): string {
return state.sex ? '男' : '女'
}
constructor(public userService: UserService) {
}
@Action(UpdateUser)
async updateUser(ctx: StateContext<UserStateModel>, action: UpdateUser) {
const newState = await this.userService.updateUser(action.payload);
const state = ctx.getState();
ctx.patchState({
...state,
newState
})
}
}
@State() 对应 Pina中的state @Getter() 对应 Pina中的getter @Action() 对应 Pina中的action
-
获取state
@Component({
template: ``
})
export default class OOPDemo {
@Select(UserState.getName) username: string;
@Select(UserState.sexFormat) userSex: string;
// 获取所有状态
@Select(UserState) user: UserEntity;
}
-
变更state
@Component({
template: `<div>{{ username }} | {{ userSex }}</div>`
})
export default class OOPDemo {
@Select(UserState.getName) username: string;
@Select(UserState.sexFormat) userSex: string;
constructor(public store: Store) {
}
change() {
this.store.dispatch(new UpdateUser());
}
}
-
订阅state
@Component({
template: `<div>{{ username }} | {{ userSex }}</div>`
})
export default class OOPDemo {
@Select(UserState.getName) username: string;
@Select(UserState.sexFormat) userSex: string;
constructor(public store: Store) {
this.store.subscribe(UserState, (mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
}
}
eventBus
-
基础使用
@Component({})
export default class OOPDemo implements LifecycleHook {
constructor(public event: EventBus) {
}
onMounted() {
this.event.on(EVENT_TOKEN, payload => {
console.log(payload);
})
}
change() {
this.event.emit(EVENT_TOKEN, payload);
}
}
默认情况下组件销毁时自身的订阅者会自动被销毁
-
保留订阅者
@Component({})
export default class OOPDemo implements LifecycleHook {
constructor(public event: EventBus) {
}
onMounted() {
// 此订阅者不会自动随着组件销毁而释放
this.event.on(EVENT_TOKEN, payload => {
console.log(payload);
}, { detached: true })
}
change() {
this.event.emit(EVENT_TOKEN, payload);
}
}
组件销毁时不自动销毁
-
主动释放订阅者
@Component({})
export default class OOPDemo implements LifecycleHook {
eventHnadler = (payload) => {
console.log(payload);
}
constructor(public event: EventBus) {
}
onMounted() {
// 此订阅者不会自动随着组件销毁而释放
this.event.on(EVENT_TOKEN, this.eventHnadler, { detached: true })
}
destroy() {
// 主动释放
this.event.off(EVENT_TOKEN, this.eventHnadler);
}
}
未完待续
后续会补充实现的过程 内容比较多 最后可能还会出适配这个插件的Webstrom插件的实现过程(视情况而定)
转载自:https://juejin.cn/post/7256566990802288698