vue2.5组件化

vue2.5组件化-零碎知识点记录

Posted by ddxg on August 11, 2019

vue2.5组件化

vm.$watch

监听器有另一种写法: immediatedeep参数

vm.$watch( expOrFn, callback, [options] )

watch: {
    cart: {
        handler(newVal, oldVal) {
            // 处理逻辑
        }
    },
    deep: true
}

webpack devServer.before

vue.config.js中:

module.exports = {
    devServer: {
        before(app) {
            // 可以做数据mock使用
            app.get('/some/path', function(req, res) {
                res.json({ custom: 'response' });
            });
        }
    }
}

挂载在vue上的EventBus

vue项目的main.js中

Vue.prototype.$EventBus = new Vue();

在组件中就可以this.$EventBus直接使用。主要用途是组件之间的通信:

// 注册事件
this.$EventBus.$on('evnetName', function(params) {
  // 事件处理
  console.log(params);
});
// 触发事件
this.$EventBus.$emit('eventName', params);

如果是子组件要通知父组件的话,可以直接使用this.$emit,这里的this指向的是当前组件。

父组件中使用myButton组件

// 父组件中绑定aaabbb事件
<my-button @aaabbb="doAaabbb"></my-button>

export default {
    methods: {
        doAaabbb(params) {
            // 处理自定义事件aaabbb
        }
    }
}

myButton组件中触发自定义事件:

this.$emit('aaabbb', params);

$EventBus是一个新的Vue实例,不使用$EventBus也是可以的,可以直接使用this.$root.$on 代替this.$EventBus.$on,不过通常都不这么做。

自定义组件的v-model

自定义双向绑定的组件需要满足两个条件:

  • 绑定value,
  • 响应input事件

对于输入框来说的话,默认会利用名为 valueprop 和名为 input 的事件。

当自定义像单选框、复选框等类型的组件的时候,就需要绑定checked 和 响应 change 事件了。这时就需要使用 model 选项来处理。

自定义组件的 v-model model

表单组件包含关系: Form > FormItem > Input

  • From 组件提供 model表单数据 和 rule校验规则
  • FormItem 组件负责校验表单数据 和 显示错误信息
  • Input 派发数据更新通知给FormItem做校验

FormItemFrom 的层级是不确定的,FormItem 要获取model数据和rule数据 做校验, 数据使用 props 传递是不太靠谱的,所以使用 provide & inject

使用第三方的 async-validator 库进行校验。

代码:myLearn/vue-project/vue-home/src/components/task/form/MyForm.vue

Input -> FormItemFormItem -> Form 都用通知的操作,一开始的做法是:

// this.$parent写太死了,如果又嵌套了一层,那所有逻辑都会出错
this.$parent.$emit('addFormItem', this);

如果层级结构改变,那 this.$parent 就指向错误了,功能就不能正常进行了。

后来参考了 element-ui 的方法,一层一层的向上找对应的组件,element-ui 的源码:

// 参考element-ui
// see,https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    var name = child.$options.componentName;
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}

export default {
  methods: {
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;
      // 存在父组件 && 组件名称不符合时,就再网上一层寻找。这里 !name 是为了过滤普通标签,这里找的都是自定义组件
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};
Vue.prototype.$dispatch = function(eventName, data) {
  let parent = this.$parent;
  // 查找父元素
  while (parent) {
    if (parent) {
      // 父元素用$emit触发
      parent.$emit(eventName, data);
      // 递归查找父元素
      parent = parent.$parent;
    } else {
      break;
    }
  }
};

Vue.prototype.$boardcast = function(eventName, data) {
  boardcast.call(this, eventName, data);
};
function boardcast(eventName, data) {
  this.$children.forEach(child => {
    // 子元素触发$emit
    child.$emit(eventName, data);
    if (child.$children.length) {
      boardcast.call(child, eventName, data);
    }
  });
}