• vue自定义组件实现vmodel(含vue3) 飞鸟和蝉


    v-model原理

    image

    <input type="text" v-model="age">
    <input type="text" v-bind="age" v-on:input="age = $event.target.value">
    

    v-model的原理就是: v-bind 和 v-on的语法糖

    vue2组件双向绑定

    第一种: v-bind(★)

    原理: 子组件通过监听父组件数据,子组件改变数据之后通知给父组件

    错误写法: 不可以直接修改props的值

    image

    正确写法

    父组件

    // Users.vue 
    <template>
      <div>
        <Son :ageValue="age" @changeInput="changeInput"/>
        <el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          age: ''
        }
      },
      methods: {
        changeInput(val) {
          this.age = val
        }
      }
    }
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <input type="text" v-model="sonAge" @input="changeInput">
      </div>
    </template>
    
    <script>
    export default {
      props: {
        ageValue: {
          typeof: String
        }
      },
      data() {
        return {
          sonAge: ''
        }
      },
      methods: {
        changeInput() {
          this.$emit('changeInput', this.sonAge)
        }
      },
    
      /*
       为什么要监听:
       因为父组件传递过来属性, 可能有默认值,
       子组件的input需要根据默认值回显,或者别的地方需要
      */
      watch: {
        ageValue: {
          immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
          handler(val) {
            this.sonAge = val
          }
        }
      }
    }
    </script>
    

    第二种.sync修饰符(★★)

    原理:
    .sync:名字 是自己起的, 通过update:名字进行触发对象的事件
    update:是vue为我们约定好的名称部分
    image
    父组件

    // Users.vue
    <template>
      <div>
        <Son :ageValue.sync="age" />
        <el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          age: ''
        }
      },
      methods: {
      }
    }
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <input type="text" v-model="sonAge" @input="changeInput">
      </div>
    </template>
    
    <script>
    export default {
      props: {
        ageValue: {
          typeof: String
        }
      },
      data() {
        return {
          sonAge: ''
        }
      },
      methods: {
        changeInput() {
          // this.$emit('changeInput', this.sonAge)
          // 这样父组件内的值也同时被更改,省略了监听事件这一步
          this.$emit('update:ageValue', this.sonAge)
        }
      },
      watch: {
        ageValue: {
          immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
          handler(val) {
            this.sonAge = val
          }
        }
      }
    }
    </script>
    

    第三种 v-model(★★★)

    原理: 通过 model新属性: 配置一个 props:接受的属性, 和一个事件名
    image

    // Users.vue
    <template>
      <div>
        <Son v-model="age" />
        <el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          age: ''
        }
      }
    }
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <input type="text" v-model="sonAge" @input="changeInput">
      </div>
    </template>
    
    <script>
    export default {
      props: {
        value: {
          typeof: String
        }
      },
      data() {
        return {
          sonAge: ''
        }
      },
      // 超级牛
      model: {
        prop: 'value',
        event: 'change'
      },
      methods: {
        changeInput() {
          this.$emit('change', this.sonAge)
        }
      },
      watch: {
        value: {
          immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
          handler(val) {
            this.sonAge = val
          }
        }
      }
    }
    </script>
    

    image

    vue3组件双向绑定

    第一种(★★)

    这里子组件input 用的是 :value 和 @input
    父组件

    // Users.vue
    <template>
      <div class="user-wrap">
        <Son v-model="message" />
        <h1>{{ message }}</h1>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import Son from './son.vue'
    export default defineComponent({
      name: 'user',
      components: {
        Son
      },
      setup() {
        let message = ref('')
        return {
          message,
        }
      }
    })
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <input type="text" :value="modelValue"
          @input="$emit('update:modelValue', $event.target.value)"
        />
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue'
    export default defineComponent({
      props: ['modelValue'],
      emits: ['update:modelValue'],
    })
    </script>
    

    第二种: 通过computed计算属性(★★★)

    这里用的是 v-model ,精简了 :vlaue 和@input
    父组件

    // Users.vue
    <template>
      <div class="user-wrap">
      	<!-- 两个方法等价 -->
        <!-- <Son :modelValue="message" @update:modelValue="message = $event" /> -->
        <Son v-model="message" />
        <h1>{{ message }}</h1>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import Son from './son.vue'
    export default defineComponent({
      name: 'user',
      components: {
        Son
      },
      setup() {
        let message = ref('')
        return {
          message,
        }
      }
    })
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <!-- 两个方法等价 -->
        <!-- <input type="text" :value="newValue" @input="newValue = $event.target.value" /> -->
        <input type="text" v-model="newValue" />
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, computed } from 'vue'
    export default defineComponent({
      props: ['modelValue'],
      emits: ['update:modelValue'],
      setup(props, { emit }) {
        const newValue = computed({
          // 子组件v-model绑定 计算属性, 一旦发生变化, 就会给父组件传递值
          get: () => props.modelValue,
          set: (nv) => {
            emit('update:modelValue', nv)
          }
        })
        return {
          newValue
        }
      }
    })
    </script>
    

    第三种: 组件绑定多个v-model

    父组件

    // Users.vue
    <template>
      <div class="user-wrap">
        <!-- 这里绑定两个v-model -->
        <Son v-model="message" v-model:title="title" />
        <h1>message:{{ message }}</h1>
        <h1>title:{{ title }}</h1>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import Son from './son.vue'
    export default defineComponent({
      name: 'user',
      components: {
        Son
      },
      setup() {
        let message = ref('')
        let title = ref('')
    
        return {
          message,
          title,
        }
      }
    })
    </script>
    

    子组件

    // Son.vue
    <template>
      <div>
        <!-- 两个方法等价 -->
        <!-- <input type="text" :value="newValue" @input="newValue = $event.target.value" /> -->
        <input type="text" v-model="newValue" />
        -
        <input type="text" v-model="newTitle" />
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, computed } from 'vue'
    export default defineComponent({
      props: {
        // v-model默认的名字
        modelValue: {
          type: String
        },
        title: {
          //这里可以直接使用 v-model:title ,:号后面的名字
          type: String
        }
      },
      emits: ['update:modelValue', 'update:title'],
      setup(props, { emit }) {
        const newValue = computed({
          get: () => props.modelValue,
          set: (nv) => {
            console.log(nv)
            emit('update:modelValue', nv)
          }
        })
    
        const newTitle = computed({
          get: () => props.title,
          set: (nv) => {
            emit('update:title', nv)
          }
        })
    
        return {
          newValue,
          newTitle
        }
      }
    })
    </script>
    

    vue2 对比 vue3的 v-model区别

    vue2在组件中这样设置:

    父组件

    <ChildComponent v-model = "title">
    

    子组件

    export default {
      model: {
        prop: 'title', // v-model绑定的属性名称
        event: 'change' // v-model绑定的事件
      },
      props: {
        value: String, // value跟v-model无关
        title: { // title是跟v-model绑定的属性
          type: String,
          default: 'Default title'
        }
      },
      methods: {
        handle() {
          // 这里的 change, 对应 event
          this.$emit('change', 'xxx')
        }
      }
    }
    

    vue3在组件中这样设置

    父组件

    <!-- 两个方法等价 -->
    <Son v-model="message" />
    <!-- <Son :modelValue="message" @update:modelValue="message = $event" /> -->
    

    子组件

    export default defineComponent({
      props: {
        modelValue: {
          type: String
        }
      },
      emits: ['update:modelValue'],
      setup(props, { emit }) {
        const newValue = computed({
          get: () => props.modelValue,
          set: (nv) => {
            console.log(nv)
            emit('update:modelValue', nv)
          }
        })
    
        return {
          newValue
        }
      }
    })
    

    总结:

    vue2:

    1. v-model: 会把 value 用作 prop 且把 input 用作 event;
    2. 可以通过 .sync修饰符 指定传递名字
    3. 支持model: 可以指定v-model的 value属性名 和 event事件名字
      image

    组件v-model原理:

    <Son v-model="age" />
    <Son :value="age"  @change="age = $event" />
    

    vue3:

    1. v-model: 不在绑定 value 而是 modelValue, 接受方法也不再是 input 而是 update:modelValue
    2. 组件支持多个 v-model, 并且可以指定名字 v-model:名字
      image

    组件v-model原理:

    <Son v-model="formData" />
    <Son :modelValue="formData" @update:modelValue="formData = $event" />
    
  • 相关阅读:
    Python--my first try!
    AB PLC首次IP地址如何分配
    如何解压DMK固件
    罗克韦尔自动化官网如何下载设备固件
    如何使用AB PLC仿真软件Studio 5000 Logix Emulate
    Studio 5000指令IN_OUT管脚实现西门子风格
    AB PLC分类
    罗克韦尔自动化发展简史
    C#曲线分析平台的制作(五,Sqldependency+Signalr+windows 服务 学习资料总结)
    自动化监控上位机系统二次开发之我见
  • 原文地址:https://www.cnblogs.com/cl1998/p/15953250.html
Copyright © 2020-2023  润新知