在定义vue组件的时候,属性和方法是非常重要的部分,我们在创建组件时,实现内部的data方法,这个方法会返回一个对象,此对象中定义的数据会存储在组件实例中,并通过响应式的更新原理来影响页面渲染。

方法定义在Vue组件里面的methods选项中。和属性一样,都可以在组件中访问到。组件属性和方法的基础知识和计算属性还有侦听器的应用。

属性和方法基础

组件的数据都放在了data选项里,vue的data选项是一个函数,组件在被创建时会调用此函数来构建响应性的数据系统。

属性基础

在vue组件中定义的属性数据应该如何调用呢?是可以直接使用组件来进行对其的调用的。vue在组织数据的时候,任何定义的属性都会暴露在组件中。这些属性属性都是存储在组件的$data对象中的。运行下面代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="Application"></div>
  <script>
    const App={
      data() {
        return {
          count:0,
        }
      },
    }
    let instance=Vue.createApp(App).mount("#Application")
    console.log(instance.count);
    console.log(instance.$data.count);
  </script>
</body>
</html>

结果:

获取到的都是一样的,修改的时候,无论用哪种方式修改数据,两种方式获取到的值都会改变。

在实际开发中,我们也可以动态地向组件实例属性,但是这种方式添加的属性不能被响应式系统跟踪,变化无法同步到页面元素

方法基础

组件的方法定义被定义在methods选项中,我们在实现组件的方法时,可以放心使用this关键字,vue自动将其绑定到当前组件实例对象本身。例如,添加一个add方法

   methods: {
        add(){
          this.count++
        }
      },

 我们将其绑定在HTML元素中,也可以直接使用组件实例来调用此方法

console.log(instance.count);

    instance.add()

    console.log(instance.count);

结果打印出来的是0和1

计算属性和侦听器

在大多数的情况,vue组件中定义的属性数据直接渲染到HTML元素上,但是有些场合需要进行处理再进行渲染。所以这个时候计算属性和侦听器就可以实现这种逻辑

计算属性

之前定义的都是存储属性,属性只是起到了存储这些值的作用。在vue中,与之相对的还有计算属性,是通过一些计算逻辑实现维护当前属性的值。

以上节代码为例,在组件中定义一个type属性,当count小于等于10的时候,属性值为小,否则为大,代码如下

computed:{
        type(){
          return this.count >10? "da" : "xiao"
        }
      },
<div id="Application">
    <div>{{type}}</div>
    <button @click="add">ADD</button>
  </div>

使用计算属性还是函数

对于上节所示例的场景,也可以采用函数来实现,实现代码如下

<div id="Application">
    <div>{{typefunc()}}</div>
    <button @click="add">ADD</button>
  </div>
  methods: {
        add(){
          this.count++
        },
        typefunc(){
          return this.count >10 ?"da": "xiao"
        }
      },

结果和使用计算属性是完成一致的。但是事实上,计算属性是基于其所依赖的存储属性变化而重新计算的,计算完成后,其结果会被缓存,下次访问计算属性的时候,只要其所依赖的属性没有变化,其内的逻辑代码就不会重复执行。但是函数不一样,每次访问都需要重新执行函数内的逻辑代码得到的结果,所以在实际应用中,我们可以根据是否需要缓存这一标准来选择使用计算属性还是函数。

计算属性的赋值

存储属性主要用于数据的获取,我们可以使用赋值运算来完成属性值的修改。计算属性只是用来取值,不会用来存值,因此计算属性默认提供的是取值的方法,也就是get方法。计算方法也可以支持赋值,存数据的方法需要手动实现,通常称为set方法

例如,修改上面代码里的type计算属性

 computed:{
        type:{
          get(){
            return this.count >10? "da" : "xiao"
          },
          set(newValue){
            if(newValue=="da"){
              this.count=11
            }else{
              this.count=0
            }
          }
        }
      },



let instance=Vue.createApp(App).mount("#Application")
    // console.log(instance.count);
    // console.log(instance.$data.count);
    console.log(instance.count);
    instance.add()
    console.log(instance.type);
    instance.type="da"
    console.log(instance.count);

结果是这样的

计算属性对适用方是透明的,我们不需要关心某个属性是不是计算属性,按照普通属性的方法对其进行使用就可以了。但是需要额外注意的是,如果一个计算属性只实现了get方法而没有实现set方法,则在使用的时候只能进行取值操作,而不能进行赋值操作。在vue中,这种只实现了set方法的计算属性也叫做只读属性,如果对只读属性进行赋值操作,会发生异常

属性侦听器

属性侦听器可以方便的监听某个属性的变化,以完成复杂的业务逻辑。以互联网的搜索引擎为例,百度这种搜索当我们在搜索框里面输入关键字后,网页就会自动关联一些推荐字供用户来选择,这种情况就非常适合使用侦听器来实现

在定义vue组件的时候,可以通过watch选项来定义属性侦听器。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="Application">
    <input type="text" v-model="searchText">
  </div>
  <script>
    const App={
      data(){
        return {
          searchText:""
        }
      },
      watch:{
        searchText(oldValue,newValue){
          if(newValue.length>10){
            alert('文本太长了')
          }
        }
      }
    }
    Vue.createApp(App).mount("#Application")
  </script>
</body>
</html>

 运行上面的代码,在输入框中输入字符,在超过10个后,就会有警告框弹出,提示文本过长。

计算属性和侦听器有类似的应用场景,使用计算属性的set也可以实现上述功能。

进行函数限流

在过程开发中,限流是一个非常重要的概念。在实际开发中,我们也会经常遇到需要进行限流的场景,例如网页上的某个按钮当用户单击后会从后端服务器进行数据的请求,在数据请求回来之前,用户额外的单击是无效的且消耗性能的。或者网页中的某个按钮会导致页面的更新,我们需要限制用户对齐频繁的进行操作。这时候就可以考虑使用限流函数,常见的限流方案是根据时间间隔进行限流,指定的时间间隔内不允许重复执行同一函数。

手动实现一个简易的限流函数

实现功能:单击按钮后通过打印方法在控制台输出当前的时间,要求这个按钮的两次时间触发间隔不能小于2s

分析实现思路:使用一个变量来控制按钮时间是否可触发,在触发按钮时间的时候对此变量进行修改,并使用settimeout函数来控制2s后将变量的值还原。代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
  <div id="Application">
    <button @click="click">按钮</button>
  </div>
  <script>
    const App = {
      data() {
        return {
          throttle: false
        }
      },
      methods: {
        click() {
          if (!this.throttle) {
            console.log(Date());
          } else {
            return
          }
          this.throttle = true
          setTimeout(() => {
            this.throttle = false
          }, 2000);
        }
      },
    }
    Vue.createApp(App).mount("#Application")
  </script>
</body>

</html>

快速点击按钮,无论按钮被单击了多少次,打印都是按照每两秒最多一次来进行限流。限流本身是通用逻辑,打印时间才是业务逻辑,可以封装一下限流:

<script>
    var throttle=false
    function throttleTool(callback,timeout){
      if(!throttle){
        callback()
      }else{
        return
      }
      throttle=true
      setTimeout(()=>{
        throttle=false
      },timeout)
    }
    const App = {
      // data() {
      //   return {
      //     throttle: false
      //   }
      // },
      methods: {
        click() {
          // if (!this.throttle) {
          //   console.log(Date());
          // } else {
          //   return
          // }
          // this.throttle = true
          // setTimeout(() => {
          //   // this.throttle = false
          // }, 2000);
          throttleTool(()=>{
            console.log(Date());
          },2000)
        }
      },
    }
    Vue.createApp(App).mount("#Application")
  </script>

使用lodosh库进行函数限流

目前已经了解限流函数的实现逻辑,也手动实现了一个简单的限流工具,虽然能满足当前需求,但是也有许多需要优化的地方。在实际开发中,每个业务函数需要的限流间隔是不一样的,且需要各自独立的进行限流,我们自己编写的限流工具就无法满足了,得益于js生态的繁荣,我们可以使用第三方工具库。

lodash库是一款高性能的js工具库,提供了大量数组,对象,字符串等边界的操作方法。

 <script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
 methods: {
        click: _.debounce(function(){
          console.log(Date());
        },2000)
        // click() {
          // if (!this.throttle) {
          //   console.log(Date());
          // } else {
          //   return
          // }
          // this.throttle = true
          // setTimeout(() => {
          //   // this.throttle = false
          // }, 2000);
          // throttleTool(()=>{
          //   console.log(Date());
          // },2000)
        // }
      },

表单数据的双向绑定

双向绑定是vue处理用户交互的一种方式,文本输入框、多行文本输入区域、单选框和多选框等都可以进行数据的双向绑定。

文本输入框

文本输入框的数据绑定直接使用vue的v-model指令进行设置

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="Application">
    <input type="text" v-model="textfield">
    <p>文本框输入内容:{{textfield}}</p>
  </div>
  <script>
    const App={
      data() {
        return {
          textfield:""
        }
      },
    }
    Vue.createApp(App).mount("#Application")
  </script>
</body>
</html>

运行代码,当输入框输入的文本发生变化的时候,段落里的文本也会同步产生变化

多行文本输入区域

多行文本可以通过textarea标签来实现,textarea可以方便定义一块区域来显示和输入多行文本,文本可以换行,而且可以设置最多可以输入多少文本,和input的数据绑定方式一样

<textarea name="" id="" cols="30" rows="10" v-model="textarea"></textarea>
    <p style="white-space: pre-line;">多行文本内容:{{textarea}}</p>

white-space是为了让其正常展示多行文本里的换行

需要注意的是textarea元素只能通过v-model指令的方式来设置内容,不能在标签内直接插入文本

复选框和单选框

复选框是为网页提供多项选择的功能,当将HTML中的input标签的类型设置为checkbox,就会以复选框的样式进行渲染。复选框通常成组出现,每个选项的状态只有两种即选中和未选中,如果只有一个复选框,在使用v-model指令进行数据绑定的时候,可以直接绑定其为布尔值

<input type="checkbox" v-model="checkbox">
    <p>{{checkbox}}</p>

上面的代码当复选框状态发生变化的时候,对应的属性checkbox的值也会切换。更多的时候复选框都是成组出现的,我们可以将每一个复选框元素设置一个特殊的值。通过数组属性的绑定来获取每个复选框是否被选中,如果被选中,则数组中会存在其所关联的值;没被选中,数组中的关联的值会被删除掉

<input type="checkbox" value="足球" v-model="checklist">足球
    <input type="checkbox" value="篮球" v-model="checklist">篮球
    <input type="checkbox" value="排球" v-model="checklist">排球
    <p>{{checklist}}</p>

单选框的数据绑定逻辑和复选框是类似的

<input type="radio" value="男" v-model="sex">男
    <input type="radio" value="女" v-model="sex">女
    <p>{{sex}}</p>

 

所有的本小节代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="Application">
    <input type="text" v-model="textfield">
    <p>文本框输入内容:{{textfield}}</p>
    <textarea name="" id="" cols="30" rows="10" v-model="textarea"></textarea>
    <p style="white-space: pre-line;">多行文本内容:{{textarea}}</p>
    <input type="checkbox" v-model="checkbox">
    <p>{{checkbox}}</p>
    <input type="checkbox" value="足球" v-model="checklist">足球
    <input type="checkbox" value="篮球" v-model="checklist">篮球
    <input type="checkbox" value="排球" v-model="checklist">排球
    <p>{{checklist}}</p>
    <input type="radio" value="男" v-model="sex">男
    <input type="radio" value="女" v-model="sex">女
    <p>{{sex}}</p>
  </div>
  <script>
    const App={
      data() {
        return {
          textfield:"",
          textarea:"",
          checkbox:true,
          checklist:[],
          sex:[]
        }
      },
    }
    Vue.createApp(App).mount("#Application")
  </script>
</body>
</html>

 

选择列表

选择列表能够给用户一组选项进行选择,可以单选也可以多选、html中使用select标签定义选择列表。如果是单选的选择列表,可以直接将其绑定在vue组件的一个属性上,如果是支持多选的选择列表,可以将其绑定在数组属性上。单选的选择列:

 <select name="" id="" v-model="select">
      <option value="1">1</option>
      <option value="2">2</option>
    </select>
    <p>{{select}}</p>

 在select内部,option标签用来定义一个选项,若要支持多选操作,加上multiple属性

<select name="" id="" v-model="selectList" multiple>
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <p>{{selectList}}</p>

 两个常用的修饰符

在对表单进行数据绑定的时候,我们可以使用修饰符来控制绑定指令的一些行为。比较常用的有lazy和trim

lazy修饰符的作用类似于属性的懒加载。使用v-model指令对文本输入框进行绑定的时候,每当输入框的文本发生变化的时候,都会同步修改对应属性的值。在某些业务场景里,我们不需要实时关注输入框中文案的变化,只需要当用户输入完成后再进行数据逻辑的处理,这时候就可以使用lazy

<input type="text" v-model.lazy="textfield">
    <p>文本框输入内容:{{textfield}}</p>

运行代码,只有当用户完成输入才会同步到输入框最终的文本数据。

trim的作用是去掉绑定文本数据的收尾空格。

样式绑定

我们可以通过HTML元素的class属性和id属性或者直接使用标签名来进行CSS样式的绑定,最常用的是使用class的方式来完成样式绑定。在vue中,对class属性的数据绑定做了特殊的增强,我们可以方便地通过布尔变量控制其设置的样式是否被选用

为HTML标签绑定class属性

v-bind指令虽然可以直接对class属性进行数据绑定,但如果将绑定的值设置为一个对象,就会产生一种新的语法规则,设置的对象中可以指定对应的class样式是否被选用。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
  <style>
    .red{
      color: red;
    }
    .blue{
      color: blue;
    }
  </style>
</head>

<body>
  <div id="Application">
    <div :class="{blue:isBlue,red:isRed}">
      示例文案
    </div>
  </div>
  <script>
    const App = {
     data() {
      return {
        isBlue:true,
        isRed:false,
      }
     },
    }
    Vue.createApp(App).mount("#Application")
  </script>
</body>

</html>

div元素的属性值会根据isblue和isred的属性的值而改变,当isblue等于true的时候,div的class属性为blue,当只有isred属性的值为true的时候,div元素的class属性为red,需要注意的是,class属性可绑定的值并不会冲突,如果设置的对象中有多个属性的值都是true,就会都添加到class属性中。

在实际的开发中,并不一定要使用内联的方式为class绑定控制对象,我们也可以将其设置为一个vue组件中的数据对象

html代码修改为

 <div id="Application">
    <div :class="style">
      示例文案
    </div>
  </div>

vue组件:

 const App = {
     data() {
      return {
        // isBlue:true,
        // isRed:false,
        style:{
          blue:true,
          red:false
        }
      }
     },
    }
    Vue.createApp(App).mount("#Application")

更多的时候我们将样式对象作为计算属性放回,使用这种方式进行组件样式的控制非常高效。

vue还支持使用数组对象来控制class属性

 <div :class="[redClass,fontClass]">
      示例文案
    </div>
 data() {
      return {
        // isBlue:true,
        // isRed:false,
        redClass:"red",
        fontClass:"font",
        style:{
          blue:true,
          red:false
        }
      }
     },

绑定内联样式

内联样式是指直接通过HTML元素的style属性来设置样式,style属性可以直接通过js对象来设置样式,我们可以直接在其内部使用Vue属性

<div :style="{color:textColor,fontSize:textFont}">
      示例文案
    </div>
 textColor:'green',
        textFont:'50px',

内联设置的css和外部定义的css有一定的区别,外部定义的css属性在命名的时候,多采用“-”进行连接,例如font-size,而内联的css中属性的命名采用的是驼峰命名法,例如fontSize

内联的style同样支持绑定对象属性,直接绑定对象属性在实际开发中更加常用,使用计算属性来承载样式对象可以十分方便地进行动态样式更新。

实现一个功能完整的用户注册页面

尝试完成一个功能完整的用户注册页面,并通过一些简单的css样式来使布局得漂亮一些

搭建用户注册页面

用户注册页面由标题,一些信息输入框,偏好设置和确认按钮这几个部分所组成。

<div class="container" id="Application">
    <div class="container">
      <div class="subTitle">加入我们,一起创造美好世界</div>
      <h1 class="title">创建你的账号</h1>
      <div v-for="{item,index} in field" class="inputContainer">
        <div class="field">{{item.title}}<span v-if="item.required" style="color: red;">*</span></div>
        <input type="text" name="" id="" class="input" :type="item.type">
        <div class="tip" v-if="index==2">请确认密码需要大于六位</div>
      </div>
      <div class="subContainer">
        <div class="setting">偏好设置</div>
        <input type="checkbox" class="checkbox" /><label for="" class="label">接收更新邮件</label>
      </div>
      <button class="btn">创建账号</button>
    </div>
  </div>

上面代码提供了主页面所需要的所有元素,也为元素指定了class属性,也集成了一些vue的逻辑,例如循环渲染和条件渲染,下面定义vue组件

 const App={
      data() {
        return {
          fields:[
            {
              title:"用户名",
              required:true,
              type:"text"
            },
            {
              title:"邮箱地址",
              required:false,
              type:"text"
            },
            {
              title:"密码",
              required:true,
              type:"password"
            }
          ]
        }
      },
    }
    Vue.createApp(App).mount("#Application")

页面布局相关逻辑进行补全,再讲css样式补齐

<style>
    .container{
      margin: 0 auto;
      margin-top: 70px;
      text-align: center;
      width: 300px;
    }
    .subTitle{
      color: gray;
      font-size: 14px;
    }
    .title{
      font-size: 45px;
    }
    .input{
      width: 90%;
    }
    .inputContainer{
      text-align: left;
      margin-bottom: 20px;
    }
    .subContainer{
      text-align: left;
    }
    .field{
      font-size: 14px;
    }
    .input{
      border-radius: 6px;
      height: 25px;
      margin-top: 10px;
      border-color: silver;
      border-style: solid;
      background-color: cornsilk;
    }
    .tip{
      margin-top: 5px;
      font-size: 12px;
      color: gray;
    }
    .setting{
      font-size: 9px;
      color: black;
    }
    .label{
      height: 12px;
      margin-left: 5px;
      height: 20px;
      vertical-align: middle;
    }
    .checkbox{
      height: 20px;
      vertical-align: middle;
    }
    .btn{
      border-radius: 10px;
      height: 40px;
      width: 300px;
      margin-top: 30px;
      background-color: deepskyblue;
      border-color: blue;
      color: white;
    }
  </style>

效果:

实现注册页面的用户交互

 以编写好的注册页面为基础,添加用户交互逻辑。在单击注册按钮的时候,需要获取用户输入的用户名、密码、邮箱和偏好设置,其中用户名和密码是必填,密码的长度要大于6位,对于邮箱也需要使用正则来进行校验。

三个文本框是通过循环动态渲染的,所以在对其进行绑定的时候,也要采用动态的方式进行绑定,首先在HTML元素中就绑定的变量设置好

<body>
  <div class="container" id="Application">
    <div class="container">
      <div class="subTitle">加入我们,一起创造美好世界</div>
      <h1 class="title">创建你的账号</h1>
      <div v-for="(item,index) in fields" class="inputContainer">
        <div class="field">{{item.title}}<span v-if="item.required" style="color: red;">*</span></div>
        <input type="text" name="" id="" class="input" :type="item.type" v-model="item.model">
        <div class="tip" v-if="index==2">请确认密码需要大于六位</div>
      </div>
      <div class="subContainer">
        <div class="setting">偏好设置</div>
        <input v-model="receiveMsg" type="checkbox" class="checkbox" /><label for="" class="label">接收更新邮件</label>
      </div>
      <button @click="createAccount" class="btn">创建账号</button>
    </div>
  </div>

完善vue组件

 const App={
      data() {
        return {
          fields:[
            {
              title:"用户名",
              required:true,
              type:"text",
              model:""
            },
            {
              title:"邮箱地址",
              required:false,
              type:"text",
              model:""
            },
            {
              title:"密码",
              required:true,
              type:"password",
              model:""
            }
          ],
          receiveMsg:false
        }
      },
      computed:{
        name:{
          get(){
            return this.fields[0].model
          },
          set(value){
            this.fields[0].model=value
          }
        },
        email:{
          get(){
            return this.fields[1].model
          },
          set(value){
            this.fields[1].model=value
          }
        },
        password:{
          get(){
            return this.fields[2].model
          },
          set(value){
            this.fields[2].model=value
          }
        }
      },
      methods: {
        emailCheck() {
          var verify=/^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2.14}/;
          if(!verify.test(this.email)){
            return false
          }else{
            return true
          }
        },
        createAccount(){
          if(this.name.length==0){
            alert("请输入用户名")
            return
          }else if(this.password.length<=6){
            alert("密码需要大于六位")
            return
          }else if(this.email.length>0 && !this.emailCheck(this.email)){
            alert("请输入正确的邮箱")
            return
          }
          alert("注册成功")
          console.log(`name:${this.name}
          password:${this.password}
          email:${this.email}
          receiveMsg:${this.receiveMsg}`);
        }
      },
    }
    Vue.createApp(App).mount("#Application")

 

通过输入库field对象来实现数据的动态绑定,为了方便值的操作,使用计算属性对输入库数据进行了便捷的存储。在创建的时候调用createaccount方法,也进行了校验。

在实际应用还需要和后端进行交互。

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐