原创

Vue基础(三)

温馨提示:
本文最后更新于 2022年10月27日,已超过 918 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

九 事件处理

1 监听事件

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

<div id="app1">
  <span>当前数字:{{num}}</span>
  <button v-on:click="num++">点击数据加1</button>
</div>
  var app1 = new Vue({
    el:"#app1",
    data:{
      num:1
    }
  })

2 事件处理方法

然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

<div id="app2">
  <p>随机获取的uuid====={{uuid}}</p>
  <button @click="getUUID">点击获取uuid</button>
  <input v-model="token" placeholder="请传入token">
</div>
  var app2 = new Vue({
    el:"#app2",
    data: {
      uuid:"无",
      token:""
    },
    methods:{
      getUUID:function () {
        var vm = this
        axios.get("getUUID",{
          headers:{
            token:"bearer "+vm.token
          }
        })
                .then(function (data) {
                  vm.uuid = data.data
                })
                .catch(function (data) {
                  console.log("出错了=="+data)
                })
      }
    }
  })

3 内联处理器中的方法

除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:

<div id="app3">
  <button @click="say('hello')">hello</button>
</div>
  new Vue({
    el:"#app3",
    methods: {
      say:function (msg) {
        alert(msg)
      }
    }
  })

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<div id="app3">
  <button @click="say('hello')">hello</button>
  <button @click="test('domtest',$event)">原生事件测试</button>
</div>
  new Vue({
    el:"#app3",
    methods: {
      say:function (msg) {
        alert(msg)
      },
      test:function (msg,event) {
        if (event){
          event.preventDefault()
        }
        alert(msg)
      }
    }
  })

4 事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

2.1.4 新增

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

2.3.0 新增

Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你想阻止事件的默认行为。

5 按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

<input v-on:keyup.page-down="onPageDown">

6 按键码

keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。

使用 keyCode attribute 也是允许的:

<input v-on:keyup.13="submit">

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。

还可以通过全局 config.keyCodes 对象自定义按键修饰符别名

// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

7 系统修饰键

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

  • .ctrl
  • .alt
  • .shift
  • .meta

注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。

比如

<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCodekeyup.17

exact 修饰符

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

鼠标按钮修饰符

  • .left
  • .right
  • .middle

十 表单输入绑定

1 基础用法

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

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

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

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

对于需要使用[输入法](https://zh.wikipedia.org/wiki/输入法) (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。

1 文本

  <div id="app1">
    <input v-model="msg" placeholder="绑定了下面的元素">
    <p>{{msg}}</p>
  </div>
  new Vue({
    el:"#app1",
    data:{
      msg:"天王盖地虎"
    }
  })

2 多行文本

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

在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替。

3 复选框

<div id="app2">
  <input type="checkbox" v-model="check" id="check-box">
  <span for="check-box">{{check}}</span>
</div>
new Vue({
  el:"#app2",
  data: {
    check:false
  }
})

多个复选框,绑定到同一个数组

<div id="app3">
  <input type="checkbox" id="box1" value="box1" v-model="checkboxs">
  <label for="box1">box1</label>
  <input type="checkbox" id="box2" value="box2" v-model="checkboxs">
  <label for="box2">box2</label>
  <input type="checkbox" id="box3" value="box3" v-model="checkboxs">
  <label for="box3">box3</label>
  <p>当前选中的box===={{checkboxs}}</p>
</div>
  new Vue({
    el:"#app3",
    data:{
      checkboxs:[]
    }
  })

4 单选按钮

<div id="app4">
  <input type="radio" value="张三" v-model="radioCheck">
  张三
  <input type="radio" value="李四" v-model="radioCheck">
  <label>李四</label>
  <input type="radio" value="王五" v-model="radioCheck">
  <label>王五</label>
  <p>{{radioCheck}} 是今天的天选之子</p>
</div>
new Vue({
  el:"#app4",
  data:{
    radioCheck:""
  }
})

5 选择框

单选

<div id="app5">
  <select v-model="selected">
    <option value="">请选择</option>
    <option value="张三">张三</option>
    <option value="李四">李四</option>
    <option value="王五">王五</option>
  </select>
  <p>挑选的天选之子为:{{selected}}</p>
</div>
new Vue({
  el:"#app5",
  data:{
    selected:""
  }
})

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

多选时:

<div id="app5">
  <select v-model="selected" multiple>
    <option value="">请选择</option>
    <option value="张三">张三</option>
    <option value="李四">李四</option>
    <option value="王五">王五</option>
  </select>
  <p>挑选的天选之子为:{{selected}}</p>
</div>
new Vue({
  el:"#app5",
  data:{
    selected:[]
  }
})

2 值绑定

对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值):

<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">

<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

但是有时我们可能想把值绑定到 Vue 实例的一个动态 property 上,这时可以用 v-bind 实现,并且这个 property 的值可以不是字符串。

1 复选框

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no"
>
// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'

这里的 true-value 和 false-value attribute 并不会影响输入控件的 value attribute,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交,(即“yes”或“no”),请换用单选按钮。

2 单选按钮

<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a

3 选择框的选项

<select v-model="selected">
    <!-- 内联对象字面量 -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123

3 修饰符

1 .lazy

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

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

2 .number

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

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

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

3 .trim

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

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

4 在组件上使用v-model

HTML 原生的输入元素类型并不总能满足需求。幸好,Vue 的组件系统允许你创建具有完全自定义行为且可复用的输入组件。这些输入组件甚至可以和 v-model 一起使用!

要了解更多,请参阅组件指南中的自定义输入组件

十一 组件基础

1 基本示例

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

2 组件的复用

<div id="app1">
    <componet1></componet1>
    <componet1></componet1>
    <componet1></componet1>
    <componet1></componet1>
</div>
Vue.component('componet1',{
    data:function(){
      return {
          count:0
      };
    },
    template:'<button @click="count++">你点了我{{count}}次</button>'
})

new Vue({
    el:"#app1"
})

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

data必须是一个函数

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

3 组件的组织

通常一个应用会以一棵嵌套的组件树的形式来组织:

组件树

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

Vue.component('my-component-name', {
  // ... options ...
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

到目前为止,关于组件注册你需要了解的就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把组件注册读完。

4 通过prop向子组件传递数据

早些时候,我们提到了创建一个博文组件的事情。问题是如果你不能向这个组件传递某一篇博文的标题或内容之类的我们想展示的数据的话,它是没有办法使用的。这也正是 prop 的由来。

Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

然而在一个典型的应用中,你可能在 data 里有一个博文的数组:

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})

并想要为每篇博文渲染一个组件:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

如上所示,你会发现我们可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。

5 单个根元素

将传入的prop变更为一个对象,在模板内使用对象的属性值

Vue.component('blog',{
    props: ['blog'],
    template:'<div class="blog-post">\n' +
        '      <h3>{{ blog.title }}</h3>\n' +
        '      <div v-html="blog.content"></div>\n' +
        '      <div>"发布时间":{{blog.time}}</div>\n' +
        '    </div>'
})
<div id="app3">
    <blog
        v-for="blog in blogs"
        v-bind:key="blog.id"
        v-bind:blog="blog"
    ></blog>
</div>
new Vue({
    el:"#app3",
    data:{
        blogs:[
            {id:1,title:"张三他不小心....",content:"张三他不小心摔倒拉。哈哈哈",time:Date.now()},
            {id:2,title:"李四他不小心....",content:"李四他不小心摔倒拉。哈哈哈",time:Date.now()},
            {id:3,title:"王五他不小心....",content:"王五他不小心摔倒拉。哈哈哈",time:Date.now()},
            {id:4,title:"赵六他不小心....",content:"赵六他不小心摔倒拉。哈哈哈",time:Date.now()},
        ]
    }
})

上述的这个和一些接下来的示例使用了 JavaScript 的[模板字符串](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals)来让多行的模板更易读。它们在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用[折行转义字符](https://css-tricks.com/snippets/javascript/multiline-string-variables-in-javascript/)取而代之。

6 监听子组件事件

在我们开发 <blog-post> 组件时,它的一些功能可能要求我们和父级组件进行沟通。

实际上就是能调用父组件中设置的 监听事件,绑定关键字$emit(方法名)

v-on:click="$emit(\'changecolor\')"

示例

    Vue.component('zujian',{
        props:['user'],
        template:'<div>\n' +
            '<p>学生姓名:{{user.name}}<p>\n' +
            '<p>学生电话:{{user.phone}}</p>\n' +
            '<button v-on:click="$emit(\'changecolor\')">改变颜色</button>\n' +
            '</div>'
    })
<div id="app4">
<div :style="{color:color}">
    <zujian
            v-for="user in users"
            v-bind:key="user.id"
            v-bind:user="user"
            v-on:changecolor="changecolor"
    >
    </zujian>
</div>
</div>
new Vue({
    el:"#app4",
    data:{
        users:[
            {id:1,name:"张三",phone:"1334451775"},
            {id:2,name:"李四",phone:"145478612187"},
            {id:3,name:"王五",phone:"17851213464"},
            {id:4,name:"赵六",phone:"14752135489"}
        ],
        color: 'red'
    },
    methods:{
        changecolor:function () {
            if (this.color==='blue'){
                this.color = 'red'
            } else {
                this.color = 'blue'
            }
        }
    }
})

1 使用事件抛出一个值

可以理解为使用父组件设定的值

Vue.component('zujian',{
    props:['user'],
    template:'<div>\n' +
        '<p>学生姓名:{{user.name}}<p>\n' +
        '<p>学生电话:{{user.phone}}</p>\n' +
        '<button v-on:click="$emit(\'changecolor\',\'cyan\')">改变颜色</button>\n' +
        '</div>'
})
new Vue({
    el:"#app4",
    data:{
        users:[
            {id:1,name:"张三",phone:"1334451775"},
            {id:2,name:"李四",phone:"145478612187"},
            {id:3,name:"王五",phone:"17851213464"},
            {id:4,name:"赵六",phone:"14752135489"}
        ],
        color: 'red'
    },
    methods:{
        changecolor:function (param) {
            // if (this.color==='blue'){
            //     this.color = 'red'
            // } else {
            //     this.color = 'blue'
            // }
            this.color = param
        }
    }
})

2 在组件上使用v-model

自定义事件也可以用于创建支持 v-model 的自定义输入组件。记住:

<input v-model="searchText">

等价于

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

当用在组件上时,v-model 则会这样:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

示例

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})
<custom-input v-model="value"></custom-input>

7 通过插槽 分发内容

和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:

<alert-box>
  Something bad happened.
</alert-box>

幸好,Vue 自定义的 <slot> 元素让这变得非常简单:

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})
正文到此结束