Vue(十)-看完你就明白的v-model,双向绑定

2,068 阅读6分钟

一、可以使用v-model的标签

你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

二、双向绑定

<div id="app">
    <input v-model="name" type="text">
    {{name}}
</div>

<script>
const vm = new vue({
    el: "#app",
    data: {
        name: "xxx"
    }
});
</script>

双向绑定:数据改变时会自动更新视图,视图发生变化时会更新数据。

 Vue 则采用的是数据劫持与发布订阅相结合的方式实现双向绑定,数据劫持主要通过 Object.defineProperty 来实现。

我们需要做的就是如何检测到数据的变化然后通知我们去更新视图,如何检测到视图的变化然后去更新数据。检测视图这个比较简单,无非就是我们利用事件的监听即可。

这个内容实际上在数据响应式里有提到:把一个普通 Javascript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

每个组件实例都有相应的watcher实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时(data数据变了),会通知watcher重新计算,从而致使它关联的组件得以更新,这就是数据变化,视图变化。watcher会在getter属性里做出相应的操作。

至于怎么视图变化=>数据变化一会讨论。

上面解释了数据=>视图,解释了v-model的data=>input的值

我们操作被劫持的数据,每次改变数据,被getter知道然后触发get里的某个设置好的函数来更新视图

参考文章

三、input的单行文本(对应data的字符串)

<input v-model="message" placeholder="edit me" type="text">
<p>Message is:{{ message }}</p>

四、textarea的多行文本(对应data的字符串)

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

五、input的复选框(对应data的数组)

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

new Vue({
  el: '#example-3',
  data: {
    checkedNames: []
  }
})

六、input的单选框(对应data的字符串)

<div id="example-4">
  <input name='want' type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input name='want' type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: {{ picked }}</span>
</div>
new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

单选框最好加一个name表明是同一个name,虽然vue绑定了同一个data暗示了只能选一个,但最后还是写上name

七、select标签的下拉选项(对应data的数组和字符串)

<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>
new Vue({
  el: '...',
  data: {
    selected: ''
  }
})

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

多选选框在selected加一个multiple即可,一般人不会多选因为多选需要同时按住shift

多选对应数组,单选对应字符串

八、form表单

通常form表单使用一系列input和一个提交按钮实现的,我们通常监听submit事件,阻止提交按钮的默认事件,并且双向绑定input按钮。

九、v-model干了什么

1. vue把下面几个标签改造了一下,所以说input并不是我们熟知的html元素

2. v-model把不同标签对应的属性(等号右边)作为prop(vue固定的)传进去,并且发布对应事件,默认情况下是v-bind:value和v-on:input ="$emit('input', $event.target.value)"的语法糖

v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

十、修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

十一、自定义组件

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

现在在这个组件上使用 v-model 的时候:

<base-checkbox v-model="lovingVue"></base-checkbox>

date里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的属性将会被更新。

这就解释了视图=>数据

我们操作视图,触发了v-model发布的事件,事件给父组件新值,从而改变了数据

十二、使用 ant-design-vue

官网

  1.  创建项目
  2.  使用组件 $ npm i --save ant-design-vue
  3.  完整引入/局部引入

import Vue from 'vue';
import Antd from 'ant-design-vue';
import App from './App';
import 'ant-design-vue/dist/antd.css';
Vue.config.productionTip = false;

Vue.use(Antd);

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
});

局部导入组件

import Vue from 'vue';
import { Button, message } from 'ant-design-vue';
import App from './App';

Vue.config.productionTip = false;

/* v1.1.2 */
Vue.component(Button.name, Button);
Vue.component(Button.Group.name, Button.Group);

/* v1.1.3+ 自动注册Button下组件,如Button.Group */
Vue.use(Button);

Vue.prototype.$message = message;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
});