likes
comments
collection
share

Vue3写一个面向对象插件系列3(使用篇3)

作者站长头像
站长
· 阅读数 44

背景

创作背景纯粹是因为本人喜欢面向对象风格,并不是说其他风格不好,无意挑起风格流之争,不喜欢的道友求放过,我的目标是兼容现有的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

  • 字段同时被getset修饰

    @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) {
        
        }
    }
    

    通过依赖注入即可获取到RouterRoute对象

  • 监听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
评论
请登录