vue3学习之旅--vue的表单和开发模式--组件化开发初识

Vue3的表单和开发模式

vue3一路的笔记
vue3学习之旅–邂逅vue3-了解认识Vue3
vue3学习之旅–邂逅vue3-了解认识Vue3(二)
Vue3学习之旅–爱上Vue3–Vue3基础语法(一)–以及vscode基本使用和快速生成代码片段
Vue3学习之旅–爱上Vue3–Vue3的Options-API

侦听器的深度侦听问题

上次,我们学习了关于侦听器的相关语法以及侦听器的使用。

但是:我们发现,在深度侦听一个对象的时候,如果一个对象的某个属性发生改变,我们打印出新值和旧值是一样的。

image-20210723195047191

image-20210723195115106

<div id="app"></div>
    <template id="my-app">
      <button @click="info.name = '毛毛1'">改变name</button>
      <h2 v-for="item in info">{{item}}</h2>
    </template>
    <script src="../vue3/vue3.js"></script>
    <script>
      Vue.createApp({
        template:'#my-app',
        data(){
          return {
            info:{
              name:'毛毛',
              age : 22
            }
          }
        },
        watch:{
          info:{
            deep:true,
            handler(newVal,oldVal){
              console.log(newVal,oldVal);
            }
          }
        }
      }).mount('#app')
    </script>

发生这种情况的原因是什么呢?

其实是因为新值newVal和旧值oldVal保存的都是这个侦听的对象的引用。vue并不会深拷贝这个对象。所以导致属性发生改变,打印出来的结果是相同的。

这个在Vue3的官网也是有所说明的。

image-20210723195853965

深拷贝和浅拷贝(补充知识)

    // 1. 对象的引用赋值
    const info = { // info本质上保存的是这个对象的内存地址(堆)
      name: '毛毛',
      age: 22
    }
    const obj = info; // 将info保存的地址给了obj,二者指向同一块内存地址
    obj.name = 'obj';
    console.log(info.name); // obj 证明了二者的确指向同一块内存地址,也就是指向同一个对象

    // 2. 对象的浅拷贝
    const info2 = {
      name: '毛毛',
      age: 22
    }
    // 将这个对象的属性拷贝一份,放到一个空对象里面,然后返回
    const obj2 = Object.assign({}, info2);
    info2.name = 'info2';
    console.log(obj2.name); // 毛毛 可见对象的确被复制了一份,二者不是同一个对象了

    const info3 = {
      name: '毛毛',
      age: 22,
      friend: {
        name: 'aaa',
        age: 222
      } // 属性 friend是一个对象类型,所以保存的也是该对象的内存地址
    }
    const obj3 = Object.assign({}, info3); // 此时仍然进行复制
    obj3.friend.name = 'bbb';
    // 证明了复杂数据类型(数组,对象等)拷贝过去的其实也是内存地址,二者(friend属性)指向的还是同一个对象
    console.log(info3.friend.name); // bbb 可以证明两个对象的friend属性指向的是同一个对象

    // 3. 对象的深拷贝
    const info4 = {
      name: '毛毛',
      age: 22,
      friend: {
        name: 'aaa',
        age: 222
      }
    }
    // 对象的深拷贝可以借助JSON对象提供两个方法
    // 先把需要深拷贝的对象转换为JSON字符串,然后把把JSON字符串还原为对象
    const obj4 = JSON.parse(JSON.stringify(info4));
    info4.friend.name = 'bbb';
    console.log(obj4.friend.name); // aaa 输出的值还是 aaa 所以friend属性不指向同一个对象了

**注意:**使用JSON来完成对象的深拷贝还是有问题的。比如遇到UNdefined,函数等。没办法完成深拷贝。

v-model双向绑定

v-model的基本使用

单提交是开发中非常常见的功能,也是和用户交互的重要手段:

比如用户在登录、注册时需要提交账号密码;

比如用户在检索、创建、更新信息时,需要提交一些数据;

这些都要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用v-model指令来完成:

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

image-20210724134141119

v-model的原理

官方有说到,v-model的原理其实是背后有两个操作:

  1. v-bind绑定value属性的值;
  2. v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

image-20210724134521848

image-20210724134529824

<div id="app"></div>
  <template id="my-app">
    <!-- 输入框的值发生改变,则让下面显示的message也发生改变 -->
    <!-- 所以我们可以监听输入框的输入事件,输入的内容发生了改变,就让message发生改变 -->
    <input type="text" v-bind:value="message" @input="message = $event.target.value">
    <h2>{{message}}</h2>
    <!-- 想要完成上面的效果,需要使用v-bind和v-on两种指令,比较麻烦, -->
    <!-- 而且在输入框等地方,我们一般都会进行这种双向的和数据进行绑定 -->
    <!-- 所以vue给我们提供了一个新的指令 v-model,简化了双向数据绑定 -->
    <!-- 实际上达到的效果和上面一样,但是写法更简单,也可以看成上面的语法糖 -->
    <input type="text" v-model="message">
    <h2>{{message}}</h2>
  </template>
  <script src="../vue3/vue3.js"></script>
  <script>
    Vue.createApp({
      template: '#my-app',
      data() {
        return {
          message: 'hello Vue3!'
        }
      }
    }).mount('#app')
  </script>

v-model绑定其他表单

  1. 绑定textarea

    image-20210724135148198

    <!-- v-model绑定textarea -->
    <label for="intro">
        <textarea id="intro" v-model="intro"></textarea>
    </label>
    <h3>{{intro}}</h3>
    
  2. 绑定复选框checkbox

    image-20210724135216810

    我们来看一下v-model绑定checkbox:单个勾选框和多个勾选框:

    单个勾选框:

    1. v-model即为布尔值。
    2. 此时input的value并不影响v-model的值。

    多个复选框:

    1. 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
    2. 当选中某一个时,就会将input的value添加到数组中。
    <!-- 绑定checkbox -->
    爱好:  
    吃饭:<input v-model="hobbies" type="checkbox" name="hobbies" value="吃饭">
    睡觉:<input v-model="hobbies" type="checkbox" name="hobbies" value="睡觉">
    打豆豆:<input v-model="hobbies" type="checkbox" name="hobbies" value="打豆豆"><br>
    
    您选择的爱好:
    <h3 v-for="hobby of hobbies">{{hobby}}</h3>
    
  3. 绑定单选框radio

    v-model绑定radio,用于选择其中一项;

    image-20210724135234538

    <!-- 绑定 radio -->
    男:<input v-model="gender" type="radio" name="gender" value="">
    女:<input v-model="gender" type="radio" name="gender" value="">
    
    
  4. 绑定下拉选择框select

    和checkbox一样,select也分单选和多选两种情况。

    **单选:**只能选中一个值

    1. v-model绑定的是一个值;
    2. 当我们选中option中的一个时,会将它对应的value赋值到fruit中;

    **多选:**可以选中多个值

    1. v-model绑定的是一个数组;

    2. 当选中多个值时,就会将选中的option对应的value添加到数组fruit中;

    image-20210724135244489

       
    <!-- 下拉选择框 select -->
    课程:
    <select v-model="courses" multiple>
        <!-- <option value="请选择课程" selected>请选择课程</option> -->
        <option value="语文">语文</option>
        <option value="英语">英语</option>
        <option value="高数">高数</option>
        <option value="线代">线代</option>
        <option value="概率论">概率论</option>
    </select>
    
<div id="app"></div>
    <template id="my-app">
      <!-- v-model绑定textarea -->
      <label for="intro">
        <textarea id="intro" v-model="intro"></textarea>
      </label>
      <h3>{{intro}}</h3>

      <!-- 绑定checkbox -->
      爱好:  
      吃饭:<input v-model="hobbies" type="checkbox" name="hobbies" value="吃饭">
      睡觉:<input v-model="hobbies" type="checkbox" name="hobbies" value="睡觉">
      打豆豆:<input v-model="hobbies" type="checkbox" name="hobbies" value="打豆豆"><br>

      您选择的爱好:
      <h3 v-for="hobby of hobbies">{{hobby}}</h3>

      <!-- 绑定 radio -->
      男:<input v-model="gender" type="radio" name="gender" value="">
      女:<input v-model="gender" type="radio" name="gender" value="">
    
    <!-- 下拉选择框 select -->
    课程:
    <select v-model="courses" multiple>
      <!-- <option value="请选择课程" selected>请选择课程</option> -->
      <option value="语文">语文</option>
      <option value="英语">英语</option>
      <option value="高数">高数</option>
      <option value="线代">线代</option>
      <option value="概率论">概率论</option>
    </select>
    </template>

    <script src="../vue3/vue3.js"></script>
    <script>
      Vue.createApp({
        template:'#my-app',
        data(){
          return {
            intro:'hello Vue3!',
            gender:'男',
            hobbies:['吃饭'],
            courses:['高数','线代']
          }
        },
        watch:{
          gender(newVal,oldVal){
            console.log(newVal,oldVal);
          }
        }
      }).mount('#app')
    </script>

v-model的值绑定

目前我们在前面的案例中大部分的值都是在template中固定好的:

  1. 比如gender的两个输入框值male、female;
  2. 比如hobbies的三个输入框值basketball、football、tennis;

在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到data返回的对象中, 再通过v-bind来进行值的绑定,这个过程就是值绑定。

这里不再给出具体的做法,因为还是v-bind的使用过程.

v-model修饰符 - lazy

lazy修饰符是什么作用呢?

  1. 默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定 的属性进行同步;
  2. 如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车) 才会触发;
    <!-- v-model 默认双向绑定数据,触发的事件是input事件,一旦输入值就会触发 -->
    <!-- 我们可以通过添加事件修饰符 -->
    <!-- lazy 修饰符 输入框失去焦点后,或者用户输入回车键后 才会触发 -->
    <input type="text" v-model.lazy="message"><br>
    <h2>{{message}}</h2>

v-model修饰符 - number

我们先来看一下v-model绑定后的值是什么类型的:

message总是string类型,即使在我们设置type为number也是string类型;

image-20210724135844140

image-20210724140008476

如果我们希望转换为数字类型,那么可以使用 .number 修饰符:

    <!-- input输入框,里面的值一直都是字符串类型,即使把type的值改为number -->
    <!-- 也只是禁用了键盘上的abc等字符,并不是说转为了数字。本质还是数字 -->
    <!-- number 修饰符,将字符串转换为数字 -->
    <input type="text" v-model.number="num"><br>
    <h2>num的类型:{{typeof num}}</h2>

image-20210724140018916

另外,在我们进行逻辑判断时,如果是一个string类型,在可以转化的情况下会进行隐式转换的:下面的score在进行判断的过程中会进行隐式转化的;

image-20210724135923568

v-model修饰符 - trim

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

    <!-- trim修饰符 会删除输入框里面的开头和结尾的空白字符(\n,\r,\t等) -->
    没有trim修饰符:<input type="text" v-model="trim">
    有trim修饰符:<input type="text" v-model.trim="trim">
    <h2> ---{{trim}}---</h2>

image-20210724140110976

image-20210724140134288

image-20210724140139720

v-mode组件上使用

v-model也可以使用在组件上,Vue2版本和Vue3版本有一些区别。 具体的使用方法,后面讲组件化开发再具体学习.

人处理问题的方式

人面对复杂问题的处理方式:

  1. 任何一个人处理信息的逻辑能力都是有限的
  2. 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容。
  3. 但是,我们人有一种天生的能力,就是将问题进行拆解。
  4. 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解

image-20210724140314802

认识组件化开发

组件化也是类似的思想:

  1. 如果我们将一个页面中所有的处理逻辑 全部放在一起,处理起来就会变得非常 复杂,而且不利于后续的管理以及扩展
  2. 但如果,我们讲一个页面拆分成一个个 小的功能块,每个功能块完成属于自己 这部分独立的功能,那么之后整个页面 的管理和维护就变得非常容易了;
  3. 如果我们将一个个功能块拆分后,就可 以像搭建积木一下来搭建我们的项目;

image-20210724140432277

组件化开发

现在可以说整个的大前端开发都是组件化的天下,无论从三大框架(Vue、React、Angular),还是跨平台方案 的Flutter,甚至是移动端都在转向组件化开发,包括小程序的开发也是采用组件化开发的思想。

所以,学习组件化最重要的是它的思想,每个框架或者平台可能实现方法不同,但是思想都是一样的。

我们需要通过组件化的思想来思考整个应用程序:

  1. 我们将一个完整的页面分成很多个组件;
  2. 每个组件都用于实现页面的一个功能块;
  3. 而每一个组件又可以进行细分;
  4. 而组件本身又可以在多个地方进行复用;

Vue的组件化

组件化是Vue、React、Angular的核心思想

  1. 前面我们的createApp函数传入了一个对象App,这个对象其实本质上就是一个组件,也是我们应用程序的根 组件;
  2. 组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用;
  3. 任何的应用都会被抽象成一颗组件树;

image-20210724140639619

接下来,我们来学习一下在Vue中如何注册一个组件,以及之后如何使用这个注册后的组件

注册组件的方式

如果我们现在有一部分内容(模板、逻辑等),我们希望将这部分内容抽取到一个独立的组件中去维护,这个时候 如何注册一个组件呢

我们先从简单的开始谈起,比如下面的模板希望抽离到一个单独的组件:

image-20210724140744568

注册组件分成两种:

  1. 全局组件:在任何其他的组件中都可以使用的组件;
  2. 局部组件:只有在注册的组件中才能使用的组件;

注册全局组件

我们先来学习一下全局组件的注册:

  1. 全局组件需要使用我们全局创建的app来注册组件;
  2. 通过component方法传入组件名称、组件对象即可注册一个全局组件了;
  3. 之后,我们可以在App组件的template中直接使用这个全局组件:

image-20210724140904598

image-20210724140913142

全局组件的逻辑

当然,我们组件本身也可以有自己的代码逻辑:

比如自己的data、computed、methods等等

image-20210724140959991

<div id="app"></div>
  <template id="my-app">
    <!-- 使用我们自定义的全局组件,就和使用html标签一样。把组件名称当做标签来用 -->
    <cpt-mao></cpt-mao>
    <cpt-m/>
  </template>

  <!-- 自定义组件模板 -->
  <template id="cpt-m">
    <!-- vue3在模板里面是可以有多个根标签了,而不需要在外面加上一个div进行包裹了 -->
    <h2>我是组件模板 222</h2>
    <h2>我是第二个定义的自定义组件</h2>
    <h2>{{info}}</h2>
  </template>
  <script src="../vue3/vue3.js"></script>
  <script>
    const app = Vue.createApp({
      template: '#my-app',
      data() {
        return {
          message: 'hello Vue3!'
        }
      }
    });
    // 使用app注册一个全局组件
    // component(组件名称 , 组件对象)
    app.component('cpt-mao', {
      template: '<h2>哈哈哈 我是全局组件!</h2>'
    });
    // 注册的全局组件,意味着这个注册的组件在任何的组件模板中都可以使用
    // 组件可以有自己数据,方法等
    app.component('cpt-m', {
      template: '#cpt-m',
      data() {
        return {
          info: '我是组件内部自己的数据'
        }
      }
    })
    app.mount('#app')
  </script>

组件的名称

在通过app.component注册一个组件的时候,第一个参数是组件的名称,定义组件名的方式有两种:

方式一:使用kebab-case(短横线分割符)

当使用kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case, 例如 ;

image-20210724141133275

方式二:使用PascalCase(驼峰标识符)

当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也 就是说 和 都是可接受的

image-20210724141209513

注册局部组件

全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注 册:

  1. 比如我们注册了三个全局组件:ComponentA、ComponentB、ComponentC;
  2. 在开发中我们只使用了ComponentA、ComponentB,如果ComponentC没有用到但是我们依然在全局进行 了注册,那么就意味着类似于webpack这种打包工具在打包我们的项目时,我们依然会对其进行打包;
  3. 这样最终打包出的JavaScript包就会有关于ComponentC的内容,用户在下载对应的JavaScript时也会增加包 的大小;

所以在开发中我们通常使用组件的时候采用的都是局部注册

  1. 局部注册是在我们需要使用到的组件中,通过components属性选项来进行注册;
  2. 比如之前的App组件中,我们有data、computed、methods等选项了,事实上还可以有一个components选 项;
  3. 该components选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象

image-20210724141353292

<div id="app"></div>
  <template id="my-app">
    <cpt-mao></cpt-mao>
  </template>


  <template id="cpt-mao">
    <h2>我是局部组件 哈哈!</h2>
    <h2>{{mes}}</h2>
  </template>
  <script src="../vue3/vue3.js"></script>
  <script>
    Vue.createApp({
      template: '#my-app',
      data() {
        return {
          message: 'hello Vue3!'
        }
      },
      // 局部组件的注册 在属性components里面进行注册
      components: {
        
        // key是组件名称,value是组件对象
        'cpt-mao': {
          template: '#cpt-mao',
          data() {
            return {
              mes: '我是局部组件自己的数据'
            }
          }
        }
      }
    }).mount('#app')
  </script>

Vue的开发模式

目前我们使用vue的过程都是在html文件中,通过template编写自己的模板、脚本逻辑、样式等。

但是随着项目越来越复杂,我们会采用组件化的方式来进行开发

  1. 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等;
  2. 当然我们依然可以把它们抽离到单独的js、css文件中,但是它们还是会分离开来;
  3. 也包括我们的script是在一个全局的作用域下,很容易出现命名冲突的问题;
  4. 并且我们的代码为了适配一些浏览器,必须使用ES5的语法;
  5. 在我们编写代码完成之后,依然需要通过工具对代码进行构建、代码;

所以在真实开发中,我们可以通过一个后缀名为 .vue 的single-file components (单文件组件) 来解决,并且可 以使用webpack或者vite或者rollup等构建工具来对其进行处理

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尤雨东

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值