我爱编程

vue-typescript项目 工作笔记

2018-04-13  本文已影响0人  liuxinya

1. Input组件的封装, 实现v-model功能

<!-- 组件HTML -->
<input type="text" 
        :placeholder="placeholder? placeholder : '请输入...'"
        @input='handleInput'>
export class Input extends Vue {
    @Prop() placeholder: string;   
    handleInput (event) {
        let value = event.target.value;
        this.$emit('input', value);
    }
}
//父组件
<Input  v-model='value':placeholder="'嘟嘟嘟'"></Input>{{value}}
value = '111';
  @Watch('value')
  onValueChange(val: string, oldVal: string) {
    console.log(val)  // 至此 页面和打印日志  都在和输入框里面的值同步变化
  }

主要就是触发vue的input事件, 不仅仅是input才可以用这个事件做到双向绑定。其他的还有(step步骤组件的currStepIndex、selector下拉框的当前选中值 等)只要在这个值或者事件触发的时候,手动触发input的事件即可

2. Vue.set

vue.set(object, objectKey, objectValue);

3. Vue中,用方法动态打开一个组件实现

export class UDynamicService {
    // 动态的打开一个组件
    open<T, K>(option: UDynamicOpenOptionObject<T, K>) {
        option = Object.assign({
            data: {},
            events: {}
        },option)
        let component = Vue.extend(option.component);
        let instance = new component();
        for(let i in option.data) {
            instance.$props[i] = option.data[i];
        }
        for(let i in option.events) {
            instance.$on(i, option.events[i]);
        }
        let temp = instance.$mount();
        if(option.selector) {
            (option.selector as any).appendChild(temp.$el);
        } else {
            document.body.appendChild(temp.$el);
        }
        return instance;
    }
    // 手动的关闭一个组件
    destroy(instance: object & Record<never, any> & Vue) {
        let dom = new DomHelper(instance.$el);
        dom.remove();
    }
    // 清空所有的组件
    destroyAll() {

    }
}
export interface UDynamicOpenOptionObject<T, K> {
    component: T; // 组件
    selector?: Element | string;
    data?: K; // 传入的数据
    events?: any;
}

4. 粗略模仿vue emit(eventName, param);on(eventName, handler)

创建一个单例, 单例中有一个变量是用来保存事件名和事件名对应的处理函数

单例中 on 函数存储 eventName 和 evenName对应handler处理函数

单例中的 emit 函数是触发 eventName 对应的handler处理函数 并往handler中传递参数

// 注意: 使用的时候一定要是一个 "单例"
// 公共的emitter 处理类
export class UEmitter {
  constructor() {}
  // 用于存储events信息 , 由于组件使用的时候可能注册多个事件,这里是个数组
  private events: {
        [prop: string]: {
            handler: Function,
            once: boolean
        }[];
    } = {};
   // 只注册一次, 处理完之后就delete
    once(eventname: string, handler: Function = () => {}) {
       this.addEvent(eventname, handler, true);
       return this;  
    }
    on(eventname: string, handler: Function = () => {}) {
        this.addEvent(eventname, handler, false);
        return this;
    }
    /*
    ** @Param eventname 事件名
    ** @Parma handler 绑定的事件
    ** @Parma once 是否只绑定一次
   */
    private addEvent(eventname: string, handler: Function = () => {}, once: boolean = false) {
        if(typeof eventname === 'function') {
            once = handler as any;
            handler = eventname;
            eventname = 'default';
        }
        if(!this.events[eventname]) {
            this.events[eventname] = [];
        }
        this.events[eventname].push({
            handler: handler,
            once: once
        });
    }
    destroy() {
        for(let i in this.events) {
            let events = this.events[i];
            events.forEach((event) => {
                delete event.handler;
                delete event.once;
            });
            delete this.events[i];
        }
    }
    emit(eventname: string = 'default', ...data) {
        if(this.events[eventname]) {
            // 存在这个处理
            for(let i in this.events[eventname]) {
                let item = this.events[eventname][i];
                item.handler.apply(null, data);
            }
            this.events[eventname] = this.events[eventname].filter((item, index) => {
                let isOnce = item.once;
                if(isOnce) {
                    delete item.handler;
                    delete item.once;
                }
                return !isOnce;
            })
        }
        return this;
    }
}

5. @type -- TypeScript中有些模块需要从@types下 再引入@type

6. vue变量命名问题 (遇到的小坑)

7. extends 和 implements

 export class OneComponent implements OnInit {
   ngOnInit() {} 
}

extends 是继承了父类 它可以直接用父类中的方法, 也可以重写父类中的方法(重新定义和父类中名字一样的变量或方法)

implements 是实现多个接口, 接口的方法一般都是空的, 只有重写才能使用,但是子类不能重新定义和接口父类中名字一样的变量或者函数,即使一样,也会被接口父类取代

extends每次继承的时候只能继承一个, 但是implements可以实现多个接口 用逗号隔开就行了

8. 项目中的组件都是动态打开的 , 动态打开的组件如果需要外界的一些真实dom做一些初始化工作的话, 就会出现一些问题。(动态组件的实例$el,是虚拟的dom,虚拟dom还未转变成真实dom, 就已经去拿虚拟dom的一些样式, 是拿不到的

9. 下载文件的封装优化

{
  "succ": false,
  "msg": "下载异常",
  "data": "下载异常"
}
async download(url: string, params: any = {}, method: string = 'post') {
        try{
            Axios.get(url, params).then((data) => {
                // 错误拦截 不让浏览器触发默认行为
                if(data.data.succ == false) {
                    let uM: UMessageService = Ioc(UMessageService);
                    uM.error(data.data.msg);
                }else {
                    // 这是项目封装的有关dom的一些操作
                    let form = new DomHelper(document.createElement('form'));
                    let net: UNetService = Ioc(UNetService);
                    document.body.appendChild(form.getDom());
                    form.getDom().setAttribute('target', '');
                    form.getDom().setAttribute('method', method);
                    form.getDom().setAttribute('action', net.getUrl(url));
                    for(let i in params) {
                        let input = new DomHelper(document.createElement('input'));
                        input.getDom().setAttribute('type', 'hidden');
                        input.getDom().setAttribute('name', i);
                        input.getDom().setAttribute('value', ((typeof params[i]) === 'object' ) ? JSON.stringify(params[i]) : params[i]);
                        form.getDom().appendChild(input.getDom());
                    }
                    let opt = document.createElement("input");
                    opt.type = "submit";  
                    form.getDom().appendChild(opt); 
                    (form.getDom() as HTMLFormElement).submit();
                    Vue.nextTick(() => {
                        form.getDom().remove();
                    })
                }
            })
        }catch(e) {
            console.log(e);
        }

    }

10. 把静态数据转换成动态的事件(简单的模仿 rxjs的发布订阅值的机制)

export class UObserver<T> extends UEmitter {
    constructor(

    ) {
        super();
    }
     // 因为一个被订阅的值, 伴随其变化有可能有多个操作 每个操作都让其唯一存在于handlers数组里面保存 
    index: number = 0;
    private val: T = null;
    // 用于保存操作
    handlers: {
        [prop: number]: ((val: T) => void)
    } = [];
    get value() {
        return this.val;
    }
    set value(v: T) {
        this.next(v);
    }
    setValue(v: T) {
        this.val = v;
    }
    // 简单的发布值
    next(val: T) {
        this.val = val;
        for(let i in this.handlers) {
            this.handlers[i](val);
        }
    }
    // 有可能是有次序的执行,这里可以使用sync
    async nextSync(val: T) {
        this.val = val;
        for(let i in this.handlers) {
            await this.handlers[i](val);
        }
    }
    map<K>(handler: (val: T, index: number) => K) {
        let subject = new UObserver<K>();
        let count = 0;
        this.subscribe((val: T) => {
            subject.next(handler(val, count++));
        })
        return subject;
    }
    merge<K>(subject: UObserver<K>) {
        let newsubject = new UObserver<T | K>();
        setTimeout(() => {
            let self_res = this.subscribe((v: T) => {
                newsubject.next(v);
            });
            let subject_res = subject.subscribe((v: K) => {
                newsubject.next(v);
            });
            // 这里别忘了取消订阅
            newsubject.once('unsubscribe', () => {
                subject_res();
                self_res();
            })
        })
        return newsubject;
    }
    // 订阅值
    subscribe(handler: (val: T) => void) {
        let index = this.index;
        this.handlers[this.index++] = handler;
        return async () => {
            delete this.handlers[index];
            this.emit('unsubscribe');
        }
    }
    // 取消所有的订阅
    unsubscribe() {
        for(let i in this.handlers) {
            delete this.handlers[i];
        }
        this.index = 0;
        this.emit('unsubscribe-all');
    }
}

11. 关于tsconfig

tsconfig 全部配置项列表 https://www.tslang.cn/docs/handbook/compiler-options.html

{
    "compilerOptions": {
        "emitDecoratorMetadata": true,   // 开启反射
        "experimentalDecorators": true,  //  开启装饰器
        "importHelpers": true,
        "lib": [
            "es2017",
            "dom",
            "es5"
        ],
        "module": "commonjs",
        "moduleResolution": "node",
        "noEmitHelpers": true,
        "noImplicitThis": false,
        "noUnusedLocals": false,
        "removeComments": true,
        "sourceMap": true,
        "strict": false,
        "target": "es5",  // 编译为es5
        "declaration": false, 
         // TypeScript具有三种JSX模式:`preserve`,`react`和`react-native`。
         // 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 
         // 在`preserve`模式下生成代码中会保留JSX以供后续的转换操作使用(比如:[Babel])
        "jsx": "preserve", 
         //  如果工厂定义为React.createElement(默认值),编译器将在检查全局JSX之前检查React.JSX。
         // 如果工厂定义为h,它将在全局JSX之前检查h.JSX
        "jsxFactory": "h"
    },
    "compileOnSave": false,
    "buildOnSave": false
}
上一篇 下一篇

猜你喜欢

热点阅读