1. <input
  2. :tabindex="tabindex"
  3. v-if="type !== 'textarea'"
  4. class="el-input__inner"
  5. v-bind="$attrs"
  6. :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
  7. :disabled="inputDisabled"
  8. :readonly="readonly"
  9. :autocomplete="autoComplete || autocomplete"
  10. ref="input"
  11. @compositionstart="handleCompositionStart"
  12. @compositionupdate="handleCompositionUpdate"
  13. @compositionend="handleCompositionEnd"
  14. @input="handleInput"
  15. @focus="handleFocus"
  16. @blur="handleBlur"
  17. @change="handleChange"
  18. :aria-label="label"
  19. >
  1. <el-input maxlength="5" minlength="2">
  2. </el-input>
  • 通过 v-bind="$attrs" 来将父元素 el-input 上 maxlength,minlength 等这些 input 的原生属性绑定在子元素 input 上,这样子就不用通过 prop 来进行传递配置

    mixins

    ```typescript import emitter from ‘element-ui/src/mixins/emitter’

mixins: [emitter, Migrating],

  1. ```typescript
  2. // element-ui/src/mixins/emitter
  3. /**
  4. * 广播,就是父组件向后代组件广播事件
  5. * 通过不断递归子组件,触发所需组件的对应事件
  6. * @param {*} componentName 目标组件名称
  7. * @param {*} eventName 要触发的事件名
  8. * @param {*} params 参数
  9. */
  10. function broadcast (componentName, eventName, params) {
  11. // 遍历当前组件实例的所有子组件
  12. this.$children.forEach(child => {
  13. // 拿到子组件名称
  14. var name = child.$options.componentName
  15. // 如果当前子组件就是目标组件
  16. if (name === componentName) {
  17. // 通知子组件触发对应事件
  18. child.$emit.apply(child, [eventName].concat(params))
  19. } else {
  20. // 递归遍历深层子组件
  21. broadcast.apply(child, [componentName, eventName].concat([params]))
  22. }
  23. })
  24. }
  25. export default {
  26. methods: {
  27. // 派发,就是子组件向父组件派发事件
  28. dispatch (componentName, eventName, params) {
  29. // 获取当前组件的父组件
  30. var parent = this.$parent || this.$root
  31. // 拿到父组件名称
  32. var name = parent.$options.componentName
  33. // 通过循环的方式不断向父组件查找目标组件
  34. while (parent && (!name || name !== componentName)) {
  35. parent = parent.$parent
  36. if (parent) {
  37. name = parent.$options.componentName
  38. }
  39. }
  40. // 当循环结束,证明目标父组件已找到(如果存在),就通知父组件触发相应事件
  41. if (parent) {
  42. parent.$emit.apply(parent, [eventName].concat(params))
  43. }
  44. },
  45. broadcast (componentName, eventName, params) {
  46. // 把 this 指向调用它的组件实例身上
  47. broadcast.call(this, componentName, eventName, params)
  48. }
  49. }
  50. }
  1. handleBlur (event) {
  2. this.focused = false
  3. this.$emit('blur', event)
  4. if (this.validateEvent) {
  5. this.dispatch('ElFormItem', 'el.form.blur', [this.value])
  6. }
  7. }
  • 当 input 失去焦点时进行校验,通过 dispatch 和 broadcast 来解决深层次的父子组件通信问题

    依赖注入

    1. inject: {
    2. elForm: {
    3. default: ''
    4. },
    5. elFormItem: {
    6. default: ''
    7. }
    8. },

    组件嵌套较深时使用,避免使用 this.$parent 来不断递归寻找所属组件

    监听属性

    在 watch 中尽量执行一些 异步或开销大的操作

    1. watch: {
    2. // 监听 value 的变化
    3. value (val) {
    4. // 当 value 变化了需要重新改变文本域的大小
    5. // 这个属于 DOM 操作,所以放在 $nextTick() 中
    6. this.$nextTick(this.resizeTextarea)
    7. // 如果需要校验,那就要通知父组件触发 change 方法
    8. if (this.validateEvent) {
    9. this.dispatch('ElFormItem', 'el.form.change', [val])
    10. }
    11. },
    12. // 监听 type 的变化,type 为 input 或者 textarea
    13. type () {
    14. // 同样当 type 变化时,DOM 也会发生改变
    15. this.$nextTick(() => {
    16. this.setNativeInputValue()
    17. this.resizeTextarea()
    18. this.updateIconOffset()
    19. })
    20. }
    21. }

    $nextTick( DOM 更新后执行回调函数以获取最新的 DOM) 使用时机

  • 生命周期created()函数中进行的 DOM 操作一定要放在$nextTick()中,因为created()函数执行时 页面中 DOM 节点还没有渲染,拿不到 DOM 节点

  • 当你的数据更新之后,需要手动操作 DOM 元素时,可以讲逻辑写在回调函数里

    参考资料

  1. Element源码分析系列5-Input(输入框)
  2. 超详细 ElementUI 源码分析 —— Input