0
点赞
收藏
分享

微信扫一扫

v-model指令的理解与运用

本文从v-model指令的基本原理入手,逐步介绍到Vue 3.x composable的组合使用。

<input 
  type="text" 
  :value="localStateProperty" 
  @change="localStateProperty = $event.target.value"
> 

可以简化为

<input type="text" v-model="localStateProperty">

组件中依然如此

<message-editor 
  :modelValue="message" 
  @update:modelValue="message = $event"
>

可以简化为

<message-editor v-model="message">

思想就是:传递一个prop给子组件,同时子组件反馈一个事件,父组件利用这个事件更新这个prop。

的确很麻烦,不过也没办法。prop是父组件的数据,子组件没法更改,只能通知父组件,让父组件亲自去处理。

上面是Vue2.x时代的普遍做法,那么Vue 3.x提供的新特性——组合API,会不会有更优雅的解决方式呢?一步步来看。

利用computed 中的set属性,能够简化template为v-model="message".

<template> 
  <label> 
    <input 
      type="text" 
      v-model="message" 
    > 
  <label>
</template>

<script>
import { computed } from 'vue'
export default { 
  props: { 
    'modelValue': String, 
  },
  setup(props, { emit }) { 
    const message = computed({ 
      get: () => props.modelValue, 
      set: (value) => emit('update:modelValue', value) 
    }) 

    return { 
      message, 
    } 
  }
}
</script>

现在template很简洁,但setup()有很多代码。这些代码是通用的,可以提取出来,当作boilerplate.

composable的引入

composable是从setup()中抽离出的函数。

以后我们想实现上面的行为,就可以直接import,简化setup().

import { useModelWrapper } from '../utils/modelWrapper'

export default { 
  props: { 
    'modelValue': String, 
  } 
  setup(props, { emit }) { 
    return { 
      message: useModelWrapper(props, emit), 
    } 
  }
}
// modelWrapper.js
import { computed } from 'vue'

export function useModelWrapper(props, emit) { 
  return computed({ 
    get: () => props.modelValue, 
    set: (value) => emit('update:modelValue', value) 
  })
}

进一步扩展功能,自定义名称

这样props可以为任意名称

import { computed } from 'vue'
export function useModelWrapper(props, emit, name = 'modelValue') { 
  return computed({ 
    get: () => props[name], 
    set: (value) => emit(`update:${name}`, value) 
  })
}
<template> 
  <label> 
    <input type="text" v-model="message" > 
    <label> <label> 
    <input type="checkbox" v-model="isDraft"> Draft 
  </label>
</template>

<script>
import { useModelWrapper } from '../utils/modelWrapper'

export default { 
  props: { 
    modelValue: String, 
    draft: Boolean
  },
  setup(props, { emit }) { 
    return { 
      message: useModelWrapper(props, emit, 'modelValue'), 
      isDraft: useModelWrapper(props, emit, 'draft') 
    }
  }
}
</script>

进一步封装,composable可以为另一个composable提供抽象

下面一段composable 实现了,一旦用户5s没有打字,消息就清空的功能。这里的参数message需要是reactive的。

// useMessageReset.js
function useMessageReset(message) {
  let timeoutId
  const reset = () => {
    if (timeoutId) {
      clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(() => (message.value = ''), 5000)
    watch(message, () => message.value !== '' && reset())
  }
} 

这样组件中就可以联合使用这两个composable

import { useModelWrapper } from '../utils/modelWrapper'
import { useMessageReset } from '../utils/messageReset'
export default {
  props: { modelValue: Boolean },
  setup(props, { emit }) {
    const message = useModelWrapper(props, emit)
    useMessageReset(message)
    return { message }
  }
}

一层层的封装,组合API的强大之处在逐渐体会。

举报

相关推荐

0 条评论