深入相识v-model的原理,封装表单类组件并用v-model简化代码,.sync修饰符的介绍与使用

[复制链接]
发表于 2025-9-23 01:53:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1.v-model的原理

v-model本质上是一个语法糖,


  • 在input文本框中,它是由value属性和oninput变乱构成的
  • 在多选框中,它是由checked属性和onchange变乱构成的
  • 在select下拉菜单中,它是由value属性和onchange变乱构成的
1.1.验证:在input文本输入框中不使用v-model实现双向数据绑定

  1. <!-- 1.验证v-model在input文本框中的拆分 -->
  2. <input type="text" v-model="msg">
  3. <p>输出msg的当前值:{{msg}}</p>
  4. <!-- 此时v-model是一个语法糖,由value属性和oninput事件组成
  5. 其中,value是让数据向视图单向绑定,oninput是view向model单向绑定,
  6. 若要验证可以删掉其中一个看效果 -->
  7. <input type="text" :value="msg" @input="msg=$event.target.value">     
复制代码
其中,
1.数据厘革,令视图跟着厘革==>:value
2.视图厘革,令数据跟着厘革==>@input
$event.target.value表现输入框用户输入的值,
msg表现model层的数据
1.2.验证:v-model在下拉菜单中的拆分

  1. <!-- 2.验证v-model在select下拉菜单中的拆分 -->
  2. <!-- 使用v-model双向绑定: -->
  3. <select name="" id="" v-model="cityId">
  4.   <option value="101">北京</option>
  5.   <option value="102">上海</option>
  6.   <option value="103">广州</option>
  7.   <option value="104">深圳</option>
  8. </select>
  9. <!-- 在下拉菜单中,v-model这个语法糖是由value属性和onchange事件构成的,验证如下:  -->
  10. <select name="" id="" :value="cityId" @change="cityId=$event.target.value">
  11.   <option value="101">北京</option>
  12.   <option value="102">上海</option>
  13.   <option value="103">广州</option>
  14.   <option value="104">深圳</option>
  15. </select>
复制代码
2.表单类组件的封装

2.1.原理或步调



  • 父传子:数据从父组件传过来,因为父组件要举行表单提交
    绑定数据:绑定的是v-model的拆解,因为不能让子组件直接修改父组件数据
  • 子传父nchange变乱监听用户对数据的修改,监听的效果传值给父组件,让父组件修改数据
  1. 父组件:(使用)
  2. //<BaseSelect :cityId="selectId" @事件名="selectId=最新值"></BaseSelect>
  3. <BaseSelect :cityId="selectId" @changeId="selectId=$event"></BaseSelect>
  4. //父组件提供数据
  5. data(){return{selectId:102}}
  6. 子组件:(封装)
  7. 注意这里不能用v-model,要把它拆解成value属性和onchange事件
  8. <select :value="cityId" @change="handleChange">
  9.     <option value="101">北京</option>
  10.     ...
  11. </select>
  12. props:{
  13.     cityId:String
  14. },
  15. methods:{
  16.     handleChange(e){
  17.         //console.log(e.target.value);//是否拿到104
  18.         //this.$emit("事件名".e.target.value);
  19.         this.$emit("changeId".e.target.value);
  20.     }
  21.     }
  22. }
复制代码
2.2.示例:表单类组件封装之下拉菜单select的封装

父组件:App.vue
  1. //html
  2.   <!-- $event是获得当前事件的形参 -->
  3.   <base-v-model :cityId="selectId" @changeId="selectId=$event"></base-v-model>
  4.   
  5. //js
  6. import BaseVModel from "./components/BaseVModel.vue"
  7. export default {
  8.           components: {
  9.                       BaseVModel,
  10.          },
  11.          data(){
  12.                  selectId:'102',//此处改成数数字然后子组件的props限制改成Number也可以
  13.          }
  14. }
复制代码
子组件:BaseVModel.vue
  1. //html
  2. <select name="" id="" :value="cityId" @change="handleChange">
  3.   <option value="101">北京</option>
  4.   <option value="102">上海</option>
  5.   <option value="103">广州</option>
  6.   <option value="104">深圳</option>
  7. </select>
  8. //js
  9. props:{
  10.   cityId:String//父传子:接收父组件传过来的数据
  11. },
  12. methods: {
  13.   handleChange(e){
  14.     console.log(e.target.value);//获取用户改变城市后对应的id:102,103...
  15.     this.$emit("changeId",e.target.value);//子传父:把监听到的用户输入传给父组件
  16.   },
  17. }
复制代码
3.使用v-model简化代码

使用v-model举行对上例代码简化,思绪是把绑定的变乱和属性凑成value+input的情势


  • 子组件:把:value=cityId更换为:value=value
  • 子组件:把this.$emit("changeId",e.target.value)更换成this.$emit("input",e.target.value)
  • 父组件:把:cityId:selectId更换成:value:selectId
  • 父组件:把@changeId="selectId=$event"更换成@input="selectId=$event"
  • 当父组件凑够:value和@input后,可以使用v-model更换它们,即v-model="selectId"
完备代码

父组件:App.vue
  1. //html
  2. <!-- <base-v-model :value="selectId" @input="selectId=$event"></base-v-model> -->
  3. <!-- 凑齐了:value和@input之后,就可以把它们改成v-model,从而达到了简化代码的目的 -->
  4. <base-v-model v-model="selectId"></base-v-model>
  5. //js
  6. data:
  7.         selectId:'102'
复制代码
子组件:BaseVModel.vue
  1. //html
  2. <select name="" id="" :value="value" @change="handleChange">
  3.   <option value="101">北京</option>
  4.   <option value="102">上海</option>
  5.   <option value="103">广州</option>
  6.   <option value="104">深圳</option>
  7. </select>
  8. //js
  9. props:{
  10. // cityId:String
  11. value:String//改成用value接收,input的拓展示例也用这个value属性
  12. },
  13.   methods: {
  14. handleChange(e){
  15.    console.log(e.target.value);
  16.    // this.$emit("changeId",e.target.value);
  17.    this.$emit("input",e.target.value);
  18. },
复制代码
4.拓展示例:完成input文本输入框的组件化封装

父组件:App.vue
  1. //html
  2. <!-- 拓展示例 -->
  3. <base-v-model v-model="inputMsg"></base-v-model>
  4. <p>input输入了什么:{{inputMsg}}</p>
  5. //js
  6. data(){
  7.         inputMsg:"你好世界"
  8. }
复制代码
子组件:BaseVModel.vue
  1. //html
  2. <!-- 5.拓展和应用:把一个input文本框改成组件 -->
  3. <input type="text" :value="value" @input="inputFunc">
  4. //js
  5. props:{
  6.   // cityId:String
  7.   value:String//改成用value接收,input的拓展示例也用这个value属性
  8. },
  9. methods: {
  10.   inputFunc(e){
  11.     this.$emit("input",e.target.value);
  12.   }
  13. },
复制代码
5..sync修饰符

5.1.介绍

和v-model一样,.sync修饰符同样可以用于父组件和子组件的数据之间的双向绑定,区别在于
前者的props属性名固定为value,这就限定了它的使用场景,而后者的属性名不固定,更为机动
.sync修饰符的本质是:属性名和@update:属性名的合写
5.2.示例:实现弹框类子组件与父组件的双向绑定,实现父组件控制其显隐,要求props通报的属性名为visible

父组件:App.vue
  1. //html
  2. <base-dialog :visible.sync="visible"><base-dialog>
  3. <button @click="visible=true">点击弹出窗口</button>
  4. //js
  5. data(){
  6.         visible:false
  7. }
复制代码
子组件:BaseDialog.vue
  1. <template>
  2.   <div class="wrapper">
  3.     <!-- step1:弹出框设置v-show并动态绑定visible -->
  4.     <div class="modal-mask" v-show="visible">
  5.       <div class="base-dialog">
  6.         <div class="title">
  7.           <h3>温馨提示</h3>
  8.           <span class="close" @click="handleClick">&times;</span>
  9.         </div>
  10.         <div class="content">
  11.           <p>你确认要退出本系统吗?</p>
  12.         </div>
  13.         <div class="footer">
  14.           <button class="btn cancel">取消</button>
  15.           <!-- step2:点击确认时,触发事件handleClick -->
  16.           <button class="btn confirm" @click="handleClick">确认</button>
  17.         </div>
  18.       </div>
  19.     </div>
  20.   </div>
  21. </template>
  22. <script>
  23. export default {
  24.   // step3:props接收父组件传过来的属性
  25.   props: {
  26.     visible:Boolean//限制传递的属性为布尔类型
  27.   },
  28.   // step4:把监听到的用户行为(确认关闭)返回给父组件
  29.   methods: {
  30.     handleClick(){
  31.       this.$emit("update:visible",false);//固定搭配--update:属性名
  32.     }
  33.   }
  34. };
  35. </script>
  36. <style scoped>
  37. /* 通过deekseek写的样式 */
  38. /* 遮罩层样式 */
  39. .modal-mask {
  40.   position: fixed;
  41.   top: 0;
  42.   left: 0;
  43.   width: 100vw;
  44.   height: 100vh;
  45.   background: rgba(0, 0, 0, 0.5);
  46.   z-index: 1000;
  47.   display: flex;
  48.   justify-content: center;
  49.   align-items: center;
  50. }
  51. /* 弹窗容器 */
  52. .base-dialog {
  53.   background: #fff;
  54.   border-radius: 8px;
  55.   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  56.   width: 480px;
  57.   max-width: 90vw;
  58.   max-height: 80vh;
  59.   display: flex;
  60.   flex-direction: column;
  61. }
  62. /* 标题区域 */
  63. .title {
  64.   padding: 16px 24px;
  65.   border-bottom: 1px solid #e8e8e8;
  66.   display: flex;
  67.   justify-content: space-between;
  68.   align-items: center;
  69. }
  70. .title h3 {
  71.   margin: 0;
  72.   font-size: 18px;
  73.   font-weight: 600;
  74.   color: rgba(0, 0, 0, 0.85);
  75. }
  76. .close {
  77.   cursor: pointer;
  78.   font-size: 24px;
  79.   color: #999;
  80.   transition: color 0.3s;
  81. }
  82. .close:hover {
  83.   color: #666;
  84. }
  85. /* 内容区域 */
  86. .content {
  87.   padding: 24px;
  88.   flex: 1;
  89.   overflow-y: auto;
  90.   line-height: 1.6;
  91.   color: rgba(0, 0, 0, 0.65);
  92. }
  93. /* 底部按钮区域 */
  94. .footer {
  95.   padding: 16px 24px;
  96.   border-top: 1px solid #e8e8e8;
  97.   text-align: right;
  98. }
  99. .btn {
  100.   padding: 8px 20px;
  101.   border: none;
  102.   border-radius: 4px;
  103.   cursor: pointer;
  104.   margin-left: 12px;
  105.   transition: all 0.3s;
  106. }
  107. .btn.confirm {
  108.   background: #1890ff;
  109.   color: white;
  110. }
  111. .btn.confirm:hover {
  112.   background: #40a9ff;
  113.   box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
  114. }
  115. .btn.cancel {
  116.   background: transparent;
  117.   border: 1px solid #d9d9d9;
  118.   color: rgba(0, 0, 0, 0.65);
  119. }
  120. .btn.cancel:hover {
  121.   border-color: #1890ff;
  122.   color: #1890ff;
  123. }
  124. </style>
复制代码
效果:略
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表