vue-表单输入绑定、计算属性、侦听器

表单输入绑定:

你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。

它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。

它会根据控件类型自动选取正确的方法来更新元素。

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

<template>
  <div class="home">
   <div>用户名:<input type="text" v-model.lazy="form_data.username"></div>
   <div>密码:<input type="password" v-model.trim="form_data.password"></div>
    <div>年龄:<input type="text" v-model.number="form_data.age"></div>
   <div>简介:<textarea v-model="form_data.text" ></textarea><br>
   
   </div>
   <div>VIP:<input type="checkbox" v-model="form_data.vip" true-value="yes" false-value="no" ></div>
   <div>爱好:
     <input type="checkbox" v-model="form_data.like" value="0">
     <input type="checkbox" v-model="form_data.like" value="1">
     <input type="checkbox" v-model="form_data.like" value="2">
   </div>
  
   <div>性别:
     <input type="radio" v-model="form_data.sex" value="0">
     <input type="radio" v-model="form_data.sex" value="1">
   </div>
   <div>籍贯:
     <select v-model="form_data.jg" multiple>
       <option value="" disabled>请选择籍贯</option>
       <option>0</option>
       <option value="8" >1</option>
       <option value="2">2</option>
     </select>
   </div>
    {{form_data}}
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  data(){
    return{
      form_data:{
        like:[],
        jg:'',
      }
    }
  },
 name:"Home",
 components:{
   
 }
}
</script>

 复选框

单个

<input type="checkbox" id="checkbox" v-model="checked" />

多个

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />  
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
data() {    
    return {       
        checkedNames: []//如果以对象的方式接收值,那么数组一定要放到对象中    
    }
}

另一种写法

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
<!--修改数据-->

单选框 

如果需要默认选中,我们可以把属性的值,设置为与某个value值相同。

<input type="radio" id="one" value="One" v-model="picked" />
<input type="radio" id="two" value="Two" v-model="picked" />
data() {    
    return {       
        picked: 'One'//与value相同时,那个控件默认选中     
    }
}

选择框

单选

<select v-model="selected">     
<option disabled value="">Please select one</option>     
<option>A</option>     
<option>B</option>     
<option>C</option>   
</select>

多选

<select v-model="selected" multiple>   
<option>A</option>   
<option>B</option>   
<option>C</option>
</select>

修饰符

.lazy 你可以添加 lazy 修饰符,从而转为在 change 事件 

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

.number 自动将用户的输入值转为数值类型

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

.trim 如果要自动过滤用户输入的首尾空白字符

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

计算属性 

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。

对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性。computed

computed: {        
     publishedBooksMessage() {       
     // `this` 指向 vm 实例       
     return this.author.books.length > 0 ? 'Yes' : 'No'     
     }   
 }

计算属性缓存 vs 方法

计算属性 computed:只在相关响应式依赖发生改变时它们才会重新求值,计算属性是基于它们的响应依赖关系缓存的

方法 methods:每调用一次都重新求值

侦听器

当需要在数据变化时执行异步或开销较大的操作时,提供了一个更通用的watch侦听属性,来响应数据的变化

普通属性的侦听

watch: {      
    question(newQuestion, oldQuestion) {               
    }     
}
//新值和旧值

 表单对象数据的侦听

//监测对象数据的变化
watch: { 
    obj:{
         handler: function (val) {
             console.log(val)//数据对象
         },
         deep:true//对象内部的属性,需要开启deep
    }
}
<template>
  <div class="home">
  {{getName()}}
  {{getSex}}
  {{getTime}}
  <div>用户名:<input type="text" v-model="form_data.username"></div>
   <div>密码:<input type="password" v-model="form_data.password"></div>
  </div>
  {{form_data}}
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  data(){
    return{
     name:"zhangsan",
     sex:1,
     time:new Date().getTime(),
     form_data:{},
    //  username:"",
    }
  },
  watch:{
    // username(new_v,old_v){
    //   console.log(new_v,old_v);
    // }
    form_data:{
      handler(v){
        console.log(v)
      },
      deep:true,//深度监听
    },
    name(){
      
    }
  },
 name:"Home",
 methods:{
    getName(){
     return this.name.split('').reverse().join('')//反转
   },
 },
 computed:{
  
   getSex(){
     return this.sex==1?'男':'女'
   },
   getTime(){
     
      let time = new Date(this.time)
      let year = time.getFullYear()
      let month = time.getMonth() + 1
      let date = time.getDate()
      let hours = time.getHours()
      let minute = time.getMinutes()
      let second = time.getSeconds()

      if (month < 10) { month = '0' + month }
      if (date < 10) { date = '0' + date }
      if (hours < 10) { hours = '0' + hours }
      if (minute < 10) { minute = '0' + minute }
      if (second < 10) { second = '0' + second }
      return year + '-' + month + '-' + date + ' ' + hours + ':' + minute + ':' + second
    }
   },
  //  filters:{
  //    getTime(){

  //    }
  //  }vue3中没有过滤器
 
}
</script>

 组件

在项目中,很多时候我们需要在不同的模块使用相同的一个块,而这个块的代码是相同的,这时候会造成代码冗余,我们可以通过封装组件的方式,实现代码的复用。

//计数器组件
<template>
    <p>{{count}}</p>
    <button @click="count++">增加</button>
</template>
<script>
export default {
    data:function(){
        return {
            count:0
        }
    }
}

 组件的复用

<template>
    <my-count />
    <my-count />
    <my-count />
</template>

 组件的引入

组件在导入时,import语句后面的名称我们可以自定义,它会作为标签名进行使用。当我们需要把它放入模板时:

<template>
    <MyCount />
    <my-count /><!--没插槽-->
    <my-count></my-count>
    <MyCount></MyCount>
</template>

import MyCount from './component/MyCount.vue'
export default {
    components:{
        MyCount      
    }
}

通过 Prop 向子组件传递数据

我们使用组件时,如果模板中的内容都固定不变,那么每个模块引入该组件的内容都将一致;其实我们也可以不这样,比如我们可以将组件的某些内容变为动态的,内容由父元素的传值决定,vue给我们提供了props属性,让我们的组件可以对子组件传值。

//子组件
export default {
    props:['title'],
}
//父组件  动态传值
<my-count :title="title"/>
export default {
    data:function(){
        return {
            title:'计数器'
        }
    },
}
//父组件  静态传值
<my-count title="title"/>
<my-count :title="'title'"/>

有时我们也可以提供一个数组,遍历多个相同组件传递不同数据:

<my-count :title="t" v-for="t in title" :key="t"/>
export default {
    data:function(){
        return {
            title:['计数器1','计数器2','计数器3']
        }
    },
}

但是,通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:

props: {   
title: String,
likes: Number,
}//该类型约束只会报警告信息
原生提供的数据类型有:
String
Number
Boolean
Array
Object
Date
Function
Symbol

当我们开启了对prop的验证,那么就需要注意传值的方式

数值:

<blog-post :likes="42"></blog-post>

布尔:

<blog-post is-published></blog-post> //默认为true
<blog-post :is-published="false"></blog-post>

数组:

<blog-post :comment-ids="[234, 266, 273]"></blog-post>

对象:

<blog-post   :author="{     
name: 'Veronica',     
company: 'Veridian Dynamics'}"></blog-post>

一次传入多个prop:

post: {
  id: 1,
  title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

//子组件
prop:['name']
this.name = "李四"//报错

注意:

注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态

//子组件
prop:['obj']//对象比较特殊,引用数据类型
this.obj.name="zhaoliu"//不会报错

poro的验证

props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true//必填项
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default() {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator(value) {
        // 这个值必须匹配下列字符串中的一个
        验证是发生在实例创建前,所以这里用不了实例的data数据
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 具有默认值的函数
    propG: {
      type: Function,
      // 与对象或数组默认值不同,这不是一个工厂函数 —— 这是一个用作默认值的函数
      default() {
        return 'Default function'
      }
    }
  }

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

注意:

注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

属性继承

一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 props 或 emits 定义的 attribute。常见的示例包括 class、style 和 id 属性

如果我们需要通过 data status property 定义 <date-picker> 组件的状态,它将应用于根节点 (即 div.date-picker)。
 

//父组件

<my-count class="a1"  data-status="activated"></my-count>

<style>.a1{}</style>

//子组件

<template><div ></div><template>

如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false。例如:

//子组件
inheritAttrs:false,
<p v-bind="$attrs">1111111</p>
//父组件
<my-count class="a1"  data-status="activated">

与单个根节点组件不同,具有多个根节点的组件不具有自动 attribute 回退行为。如果未显式绑定 $attrs,将发出运行时警告。

自定义事件

有时我们的子组件中会有一些事件和数据需要传递到父组件,例如我们点击了子组件中的按钮,那么这个按钮可能会触发某个方法,甚至传递了一些参数。这时我们的父组件就需要对事件进行处理或验证。

我们来看一个点击子组件按钮放大文字的例子:

//子组件
//这里我们给子组件绑定了一个事件,并通过调用内建的 $emit 方法并传入事件名称让父组件可以触发postfont
<button @click="$emit('postfont')">放大</button>
//父组件
//我们可以通过@+$emit 方法并传入事件名称去触发子组件的方法进行文字的放大
<div :style="{fontSize:fontSize+ 'em'}">
    <my-count :title="title" @postfont="fontSize += 0.1"/>
</div>
//在$emit 方法中我们可以提供第二个参数来告诉父组件我们需要放大多少
//子组件
<button @click="$emit('postfont',0.2)">放大</button>
//父组件  传值的值会保存在$event中
<template>
    <div :style="{fontSize:fontSize+ 'em'}">
    <my-count :title="title" @postfont="fontSize += $event"/>
    </div>
</template>

可以通过 emits 选项在组件上定义已发出的事件。

emits: ['postfont'],

验证抛出事件:

要添加验证,将为事件分配一个函数,该函数接收传递给 $emit 调用的参数,并返回一个布尔值以指示事件是否有效。

emits: {
    // 没有验证
    click: null,
    // 验证submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },

emit语法糖

//如果我们只是想要把值传输到父组件,那么可以通过update的方式进行传值
$emit('update:v',newv);
//父组件
<post-size  v-model:v="size"  />
//传递过来的v会覆盖size

组件的双向数据绑定

//子组件 子组件将需要一个 title prop 并发出 update:title 要同步的事件
<template>
<input
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)">
<p>{{title}}</p>
</template>
export default {
    props: ['title'],
    emits: ['update:title'],
}
//父组件
<my-count v-model:title="bookTitle"></my-count>
export default {
    data:function(){
        return {
            bookTitle:'默认值',
        }
    }
}

允许为组件绑定多个v-model

 <my-count 
    v-model:title="bookTitle"
    v-model:name="bookTitle"></my-count>

插槽

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。

普通插槽

//父组件
<my-count>这是一个计数器组件</mycount>
//子组件
<template>
    <slot></slot>
</template>

插槽还可以包含任何模板代码,包括 HTML

//父组件
<my-count><p>这是一个计数器组件</p></mycount>

插槽中可以使用实例的数据

//父组件
<my-count><p>这是一个计数器组件{{name}}</p></mycount>

如果 template 中没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

备用内容

我们可能希望这个 <button> 内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为备用内容,我们可以将它放在 <slot> 标签内:

<button type="submit">   
<slot>Submit</slot>
</button>

这时,如果你有使用插槽就覆盖该内容,如果未使用,则使用内用内容。

具名插槽

有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout> 组件:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

一个不带 name 的 <slot> 出口会带有隐含的名字“default”。

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

缩写(#)

跟 v-on(@) 和 v-bind(:) 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header:

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

作用局插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。

比如我们的子组件中有一个对象数据:

user:{
    name:'zhangsan',
    age:10
}

我们希望在使用组件时,插槽可以灵活的控制显示name or age。

<slot v-bind:user="user">{{user.name}}</slot>

父组件:

<my-count >
<template v-slot:name="data">{{data.user.age}}</template>
</my-count>

默认插槽

在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:

<my-count 
v-slot:default="slotProps">{{slotProps.user.age}}</my-count>

注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确

解构插槽

v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:

<my-count >
    <template v-slot:default="{user}">{{user.age}}</template>
</my-count>

动态插槽

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

<base-layout>   
<template v-slot:[name]>     ...   </template>
</base-layout>

动态组件

不同组件之间进行动态切换,在一个多标签的界面里:

<component :is="currentTabComponent"></component>

provide/inject

通常,当我们需要从父组件向子组件传递数据时,我们使用 props

使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

//父组件
provide: {
    msg: 'is app msg'
},
//子组件
inject: ['msg'],

异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 有一个 defineAsyncComponent 方法:

import {defineAsyncComponent } from 'vue'
components:{
    MyCount : defineAsyncComponent(() =>
    import('./component/MyCount.vue')
    )   
}

为什么要使用异步组件

  1. 异步组件在创建时什么也不做,在组件第一次渲染时加载并缓存。

  2. 异步组件的性能更高,加载的速度更快。

  3. 异步组件在打包时,体积更小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值