<input
:tabindex="tabindex"
v-if="type !== 'textarea'"
class="el-input__inner"
v-bind="$attrs"
:type="showPassword ? (passwordVisible ? 'text': 'password') : type"
:disabled="inputDisabled"
:readonly="readonly"
:autocomplete="autoComplete || autocomplete"
ref="input"
@compositionstart="handleCompositionStart"
@compositionupdate="handleCompositionUpdate"
@compositionend="handleCompositionEnd"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
:aria-label="label"
>
<el-input maxlength="5" minlength="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],
```typescript
// element-ui/src/mixins/emitter
/**
* 广播,就是父组件向后代组件广播事件
* 通过不断递归子组件,触发所需组件的对应事件
* @param {*} componentName 目标组件名称
* @param {*} eventName 要触发的事件名
* @param {*} params 参数
*/
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
// 通过循环的方式不断向父组件查找目标组件
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) {
// 把 this 指向调用它的组件实例身上
broadcast.call(this, componentName, eventName, params)
}
}
}
handleBlur (event) {
this.focused = false
this.$emit('blur', event)
if (this.validateEvent) {
this.dispatch('ElFormItem', 'el.form.blur', [this.value])
}
}
当 input 失去焦点时进行校验,通过 dispatch 和 broadcast 来解决深层次的父子组件通信问题
依赖注入
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
组件嵌套较深时使用,避免使用 this.$parent 来不断递归寻找所属组件
监听属性
在 watch 中尽量执行一些 异步或开销大的操作
watch: {
// 监听 value 的变化
value (val) {
// 当 value 变化了需要重新改变文本域的大小
// 这个属于 DOM 操作,所以放在 $nextTick() 中
this.$nextTick(this.resizeTextarea)
// 如果需要校验,那就要通知父组件触发 change 方法
if (this.validateEvent) {
this.dispatch('ElFormItem', 'el.form.change', [val])
}
},
// 监听 type 的变化,type 为 input 或者 textarea
type () {
// 同样当 type 变化时,DOM 也会发生改变
this.$nextTick(() => {
this.setNativeInputValue()
this.resizeTextarea()
this.updateIconOffset()
})
}
}
$nextTick( DOM 更新后执行回调函数以获取最新的 DOM) 使用时机
生命周期created()函数中进行的 DOM 操作一定要放在$nextTick()中,因为created()函数执行时 页面中 DOM 节点还没有渲染,拿不到 DOM 节点
- 当你的数据更新之后,需要手动操作 DOM 元素时,可以讲逻辑写在回调函数里
参考资料