Vue2全家桶 - Vue2 - 【2】组件基础【组件的创建和使用、组件通信、生命周期、axios的使用、$refs、$nextTick】

一、组件的创建和使用

1.1 组件概念

  • 组件
    • 可复用的Vue实例,把 标签样式JS 封装成一个单独的 .vue文件;
    • 代码里面体现为一个独立的 .vue 文件;
  • 组件化
    • 封装的思想:
      • 把页面上 可复用的部分 封装为组件,从而方便项目的开发和维护;
    • 一个页面可以拆分成多个组件,一个组件就是一个整体,每个组件有自己独立的结构样式行为。
  • 使用场景
    • 遇到标签可复用的时候;
  • 组件的优点
    • 各自独立:每个组件都有自己独立的结构样式和行为;
    • 便于复用;

1.2 组件的创建和使用

1.2.1 创建组件

  • 创建一个 .vue文件,封装想要复用的html、js、css

1.2.2 导入组件

  1. 全局导入

    • 语法

      • 目标文件:msin.js
      js 复制代码
      import Vue from 'vue'
      import 组件对象 from '组件路径(.vue文件路径)
  2. 局部导入

    • 语法

      js 复制代码
      import 组件对象 from '组件路径(.vue文件路径)'

1.2.3 注册组件

  1. 全局注册
    • main.js 中注册;

    • 语法:

      js 复制代码
      import Vue from 'vue'
      // 组件名 - 字符串
      // 组件对象 - 变量
      Vue.component('组件名', 组件对象)
    • 注意:该组件在所有的.vue文件中都可以使用;

  2. 局部注册
    • 需要组件的文件 中注册;

    • 语法:

      js 复制代码
      export default {
          components: {
              组件名: 组件对象
          }
      }
    • 注意

      • 根据ES6的新特性,属性名和属性值一样,可以简写,所以可以将 组件名组件对象 写成一样 的;
      • 该组件仅限当前文件使用;

1.2.4 使用组件

  • 组件名 当作 自定义标签使用

  • 语法

    js 复制代码
    <template>
        <div id="app">
            <组件名 />
            <组件名></组件名>
        </div>
    </template>
  • 注意

    • 单双标签都可以,单标签 需要 自闭合
    • 该组件是 块元素 还是 行内元素 ,取决于组件的根标签 ,既 template 下面唯一的根标签;
    • 组件运行之后的样子:
      • 使用 组件里的 唯一根标签 替换 组件标签
  • 组件命名规范

    • 使用 大驼峰 命名法;
    • .vue文件名 = 组件对象 = 组件名 = 自定义标签名
  • 示例展示:

    • 全局导入 + 全局注册 + 使用步骤:

      js 复制代码
      // 该行代码已经有,不需重复写
      import Vue from 'vue'
      
      // 全局导入组件
      // 语法: import 组件对象 from '组件路径'
      import PanelX from './components/Panel.vue'
      
      // 全局注册组件
      // 语法:Vuw.component('组件名', 组件对象)
      // 组件名 - 字符串
      // 组件对象 - 变量
      Vue.component("PanelX", PanelX);
    • 局部导入 + 局部注册 + 使用步骤

      js 复制代码
      <template>
          <div id="app">
              <h3>案例:折叠面板</h3>
              <!-- 4.使用组件 - 直接将组件名当作标签使用 -->
              <!-- 单双标签都可以 -->
              <!-- 单标签需要自闭和 -->
      
              <!-- 局部注册的组件 -->
              <Panel />
              <Panel></Panel>
      
              <!-- 全局注册的组件 -->
              <PanelX />
              <PanelX></PanelX>
          </div>
      </template>
      
      <script>
          // 1.封装组件 - 抽取可复用标签 + 对应JS代码 + 对应样式
          // 2.导入组件
          // 语法:import 组件对象 from '组件路径'
          import Panel from "./components/Panel.vue";
          export default {
              // 3.注册组件
              // components: {
              // 	"组件名": 组件对象,
              // }
              components: {
                  // Panel: Panel,
                  // 简写
                  Panel,
              },
          };
      </script>
      
      <style lang="less">
          body {
              background-color: #ccc;
      
              #app {
                  width: 400px;
                  margin: 20px auto;
                  background-color: #fff;
                  border: 4px solid blueviolet;
                  border-radius: 1em;
                  box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
                  padding: 1em 2em 2em;
      
                  h3 {
                      text-align: center;
                  }
              }
          }
      </style>

1.3 scoped 的作用

  • scoped 修饰 style 标签之后,webpack在打包的时候,会尝试去给当前vue文件里的标签添加随机哈希值;
  • 准备:当前 组件内标签 都被添加 data-v-8位哈希值 的属性;
  • 获取:css选择器 都被添加 [data-v-8位哈希值]属性选择器
  • 注意:
    • 每个组件的自定义属性都是相互独立的;
    • 给当前所有标签都添加自定义属性,并且自定义属性是相同的;
    • css选择器,就会形成一个交集选择器
      • 原有的选择器 + 属性选择器

二、组件通信

  • 为什么组件之间需要通信?
    • vue写代码思想:
      • 组件化,代码不会写在一个文件中,而是拆分成一个一个的组件,最后再进行组装,得到一个完整界面,由于代码分散在不同的文件中中,最后是通过这些组件之间的配合来实现需求,这时候难免它们之间必须要传数据。

2.1 父组件向子组件传值(props机制)

  • 父组件 里面引入 子组件
  • 步骤:
    • 1️⃣ 子组件内,声明固定变量(props)接收传递的数据
      • 数组型props
        • props数组里面,准备 字符串型 作为 接收数据变量,接收父组件传递过来的数据;
        • 父组件 未传值 ,则接收到的数据就是 undefined
      • 对象型props
        • 每个变量 都是 一个对象,作为 props 的 子对象
        • 每个对象都有三个属性:
          • type
            • 属性值:Number、String、Boolean、Array、Object、Function、Promise、......
            • 规定传入数据的类型;
            • 如果传入的数据不是规定的类型,就会报错;
            • 注意:
              • nullundefined会通过任何类型验证;
          • required
            • 属性值:true / false
            • 是否必传(不传数据会报错);
            • required default 两个属性二选一;
          • default
            • 默认值(不传数据就用默认值,传了就用传递的数据);
            • 注意:
              • 对象或数组默认值必须从一个工厂函数获取
    • 2️⃣ 引入组件,注册组件,使用组件,传值进去
      • 自定义标签上 (组组件标签),使用v-bind动态绑定属性,子组件内 声明的 接收变量 作为 属性名 ,要 传递的数据(变量) 作为 属性值 传递过去。
  • 这块东西还是比较多的,具体的一些可以去Vue2官网的prop中看看。
  • 两种props接收数据方式的对比:
    • 数组:
      • 方式很单一,也不能做限制;
    • 对象:
      • 可以对传递的数据进行限制;
      • 可以设置默认值;
        • 在子组件使用频率很是频繁的情况下,子组件中的某块数据大多数情况下是一样的,这时候,我们就可以设置默认值;
  • 示例展示:
    • 父组件:

      html 复制代码
      <template>
          <div>
              <!-- 使用组件 -->
              <组件名 :子组件props中的变量名="父组件要向子组件传递的数据"></组件名>
          </div>
      </template>
      
      <script>
          // 导入组件
          import 组件对象 from '组件路径'
          // .vue文件名 = 组件对象名 = 组件名 = 自定义标签名
          export default {
              // 注册组件
              components: {
                  组件名: 组件对象
              }
          }
      </script>
    • 子组件:

      html 复制代码
      <script>
          export default {
              // 1.声明固定变量,接收父组件传递过来的数据属性
              // 2.父组件内,子组件上,通过自定义属性传数据属性
              // 注意:props在根data、methods等平级
              
              // props第一种写法
              props: ['变量名1', '变量名2', '变量名3', ......],
              
              // props第二种写法
              props: {
                  变量1: {
                      type: Number(简单数据类型),
                      required: true
                  },
                  变量2: {
                      type: Number(简单数据类型),
                      default: 写成对应类型的数据,
                  },
                  变量3: {
                      type: Array / Object,
                      dafault: function () {
                          // type = Array
                          return [对应的数据]
                          
                          // type = Object
                          return { 对应数据 }
                      }
                  }
              }
          }
      </script>

2.2 子组件向父组件传值($emit机制)

  • 1️⃣ 父组件内子组件标签 绑定 自定义事件事件处理函数

    • 语法:

      js 复制代码
      <组件标签 @自定义事件名 = "父组件methods中定义的事件名" />
  • 2️⃣ 子组件内 ,在恰当的时机,触发 父组件 给 子组件 绑定的 自定义事件 ,进而触发父组件methods中对应的事件处理函数执行;

    • 语法:

      js 复制代码
      this.$emit('自定义事件名', 要向父组件传递的数据)

2.3 单向数据流

  • 为什么不建议 子组件修改父组件传过来的值?
    • 父子数据不一致,而且子组件是依赖于父组件传入的值;
    • 导致数据的不一致。
  • 单向数据流:
    • 父组件子组件数据流向
  • ❗❗ Vue规定
    • props 里的 变量,本身是 只读 的;
  • 这里只是简单说了一下,想要了解的更深可以去问度娘

❌ 2.4 跨组件通信(了解即可)

  • 跨组件通信:两个 没有任何关系 的 组件 进行 通信;

  • 语法:

    js 复制代码
    // 1. src/EventBus/index.js - 创建空白Vue实例并导出
    // 2. 在要 接收值 的 组件 - eventBus.$on("事件名", 事件处理函数)
    // 3. 在要 传递值 的 组件 - eventBus.$emit("事件名", 事件处理函数的实参1, 事件处理函数的实参2, ......)
  • 示例展示:

    • (图片看不清可以右键,在新标签页中打开图片)

三、Vue生命周期

  • 生命周期
    • 从创建到销毁的整个过程称为组件的生命周期;

3.1 钩子函数

  • 钩子函数
    • Vue框架 内置函数 ,随着组件的生命周期 自动执行
    • 监测Vue生命周期到达了什么阶段;
  • 作用
    • 在特定的时间点,执行特定的操作;
  • 使用场景
    • 组件创建完毕后,可以在 created 生命周期函数中 发起Ajax请求 ,从而 初始化data数据
  • 分类:
    • 四个阶段八个方法

3.2 复习axios使用步骤

  • 下包:

    • yarn add axios -S
  • 导入包:

    • import axios from 'axios'
  • 调用axios函数,对接接口文档,获取相关数据:

    js 复制代码
    // 1. 创建 axios 实例并导出
    // 一般都是在 src/utils/request.js 下写
    import axios from 'axios'
    
    const request = axios.create({
        baseURL: process.env.VUE_APP_BASE_API,
        timeout: 5000
    })
        
    export default request
    
    // 2. 在 src/api/对应的js文件 导出 request(axios实例) 
    import request from '@/utils/request'
    // @ 表示 src 文件夹
    /*
     * 参数根据需求填写
     * method = GET ➡ params(get方式传参)
     * method = POST ➡ data(post方式传参)
     */
    export function 方法名(data 或 params) {
        return request({
            method: '请求方式',
            url: '请求地址',
            data 或 params(根据请求方式填写,此处使用了对象的简写,属性值和属性名一样)
        })
    }
    
    // 3. 在需要使用接口的地方,导入接口,使用即可

3.3 初始化阶段

  • 1️⃣ new Vue()Vue实例化
    • main.js 中实例化Vue,从此刻起,Vue就诞生了;
    • 组件也是一个小的Vue实例;
  • 2️⃣ Init Event & Lifecyle ➡ 给 Vue实例 添加 属性方法
    • 给Vue上户口,给Vue实例添加属性和方法,这些属性和方法我们暂时用不了,无需关心;
  • 3️⃣ ❌ beforeCreate ➡ 执行钩子函数,当前钩子函数 访问不了 data数据methods方法
    • 我们在组件中声明的data数据和methods方法还没有加到Vue实例上;
    • 这个钩子中,无法访问数据和方法,开发中不用,但个别源码会用;
  • 4️⃣ Init injections&reactivity ➡ 开始把 data数据mothods方法 变成 响应式,并且加到vue实例上;
  • 5️⃣ ✅ created ➡ 开始执行钩子函数,现在 可以访问 data数据 和 methods方法
    • 使用场景
      • 发起Ajax请求、开启定时器;
    • 当我们一进入某个组件,就需要获取后台数据展示界面。可以在这个时候发起Ajax请求,早点拿数据,早点渲染界面
  • 示例展示:
  • 小结:
    • Vue实例创建编译模板 执行了 哪些 钩子函数
      • beforeCreate、created
    • created函数 触发 能获取 data 吗?
      • 可以获取,但是 不能获取真实DOM

3.4 挂载阶段

  • Has el option? ➡ 判断new Vue({})的参数对象中是否有el选项 - 检查要挂到那里;
    • el选项
      • 决定Vue实例 编译好模板 以后,要将编译后的 标签结构挂载到哪里
    • NO:
      • 脚手架生成的代码没有el选项;
      • new出来的Vue实例调用 $mount() 方法(脚手架生成的代码就是调用$mount()方法);
    • YES:
      • 继续检查 template 选项;
  • Has "template" option? => 判断参数对象中是否有template选项;
    • YES - 编译 template返回render函数:
      • 插入到 el 或 $mount() 指定的位置;
    • NO:
      • 编译el的外部HTML内容(自己本身+内部的标签)作为模板;
      • App.vue的根标签及其子元素把public/index.html<div id="app"></div>整体给替换掉(真实DOM展示的就是App.vue的所有标签);
  • 虚拟DOM 挂载成 真实DOM 之前;
  • beforeMount => 把App.vue的所有标签(插值,指令等)编译完毕之后,开始执行beforeMount()钩子函数,此时,template中的标签知识编译完毕,但还没有变成真实DOM,也就是说,此时我们无法获取真实DOM(这个钩子是个废物);
  • Crate vm.$el ... - 把 虚拟DOM 和 渲染的数据 一并挂到 真实DOM 上;
    • App.vue中的根标签及其子元素替换掉<div id="app"></div>盒子,此时,我们在template中写的标签(虚拟DOM)才会变成真实DOM,挂载到DOM树上;
  • 真实DOM挂载完毕;
  • mounted - 虚拟DOM变成真实DOM 之后,才执行该函数,因此此时可以获取任何一个真实DOM ,进而可以去操作DOM;
    • 虚拟DOM变成真实DOM之后,才执行mounted钩子,此时可以获取真实DOM;
    • 使用场景:获取真实DOM,进而通过Web API进行操作;
  • 示例展示:
  • 小结:
    • Vue实例创建显示 都经历了哪些 钩子函数
      • beforeCrate、created、beforeMount、mounted
    • created函数里,能获取真实DOM吗?
      • 不能获取真实DOM(模板都还没编译);
    • 在什么钩子函数里面可以获取真实DOM?
      • mounted钩子中可以获取真实DOM;
      • 挂载之前 的是 虚拟DOM挂载之后 的是 真实DOM

3.5 更新阶段

  • 前提
    • 更新阶段的前提是 随着 data数据 的 变化 而 执行的
    • 如果数据不变这个阶段的钩子是不会执行的只要数据变化 了,每次都会执行该钩子函数
  • data里数据改变,更新DOM之前
  • beforeUpdate ➡ 生命周期钩子函数被执行;
    • 数据变了之后,紧接着执行beforeUpdate()钩子,此时能 获取到 最新 的 数据 ,但是DOM还没更新,此时获取 不到 最新的 DOM内容(这个钩子基本上是个废物);
  • Virtual DOM...... ➡ 虚拟DOM重新渲染,打补丁到真实DOM(diff算法的比对);
    • 当数据变了,Vue内部要开始diff算法对比,找出变化的地方,进行打补丁,重新渲染;
  • updated ➡ 生命周期钩子函数被执行;
    • 当组件重新渲染完毕之后,开始执行updated()钩子,此时可以获取最新的DOM内容
  • 当有 data数据改变 ➡ 重复这个循环;
  • 注意
    • 程序刚开始运行,不执行这两个钩子函数
    • 只有当数据改变的时候,才会执行;
  • 示例展示:
  • 小结
    • 什么时候执行 updated 钩子函数?
      • 当数据发生变化并更新页面后;
    • 在哪可以获取更新后的DOM?
      • updated 钩子函数里面;

3.6 销毁阶段

  • 当组件实例的 $destroy() 被调用,就会触发组件的销毁(除了调用 $destroyed() 来销毁组件,还可以通过 v-if销毁组件);
  • beforeDestroy ➡ 生命周期钩子函数被执行;
    • 拆卸当前组件的所有侦听器所有组件的事件监听器(把当前组件占用的资源释放掉,做内存回收工作);
    • 内存回收处理完毕后,此时执行最后一个钩子函数(组件的销毁后钩子);
  • destroyed ➡ 生命周期钩子函数被执行(Vue实例生命周期结束);
  • 示例展示:
  • 小结
    • 一般在 beforeDestory、destoryed 里做什么?
      • 手动 消除计时器、定时器、解绑事件
    • 生命周期中常用的钩子函数:

四、axios的使用

4.1 axios 基本介绍

  • 特点
    • 支持客户端发起Ajax请求;
    • 支持服务端Node.js发起请求;
    • 支持Promise相关用法;
    • 支持请求和响应的拦截器功能;
    • 自动转换JSON数据;
  • Ajax如何给后台传参?
    • url拼接 ==> 查询字符串
    • url路径 上 ==> 需要后端处理
    • 请求体 / 请求头 ==> 传参给后台

4.2 axios 基本使用

  • 这里只做基本展示,想要完整的axios配置对象,大家可以去官网看看Axios官网 - 请求配置

  • 前置工作:

    js 复制代码
    // 下载 axios
    yarn add axios -S
    
    // 导入axios
    import axios from 'axios'

4.2.1 GET请求

  • 语法:

    js 复制代码
    // 默认就是 GET 请求,可以不写
    // 请求方式的 大小写 都可以
    axios({
        method: 'GET',
        url: '请求地址',
        params: {
            // 要携带的参数
        }
    })
        .then(res => console.log(res))	// 得到请求成功的数据
        .catch(err => console.log(err))	// 捕获错误
    
    -----------------------------------------------
    
    async get() {
        const res = await axios({
            method: 'GET',
            url: '请求地址',
            params: {
                // 要携带的参数
            }
        })
        console.log(res.data)
    }

4.2.2 POST请求

  • 语法:

    js 复制代码
    axios({
        method: 'POST',
        url: '请求地址',
        data: {
            // 要传递的参数
       }
    })
    .then(res => console(res))
    .catch(err => console.log(err))
    
    -----------------------------------
    
    async post() {
        const res = await axios({
            method: 'POST',
            url: '请求地址',
            data: {
                // 要写带的数据
            }
        })
        console.log(res.data)
    }

4.3 axios 小结

  • axios 返回的是一个 Promise实例
  • 可以使用 asyncawait 简化 Promise操作;
  • 只要函数里面使用了 await ,则函数就必须被 async 修饰;
    • 是最近一级的函数使用 async
  • 发起 GET 请求,使用 params 配置项 传递参数
    • 参数是拼接在 url地址 上面;
  • 发起 POST 请求,使用 data 配置项 传递参数

五、$ref$nextTick

5.1 获取DOM / 组件对象

5.1.1 获取DOM

  • 通过 原生JS获取DOM的方法ref属性 获取 原生DOM

  • mounted 生命周期中 ➡ 有两种方式获取DOM:

    1. 目标标签 ➡ 添加id、class、...... 或 ref属性:

      html 复制代码
      <标签 ref="" id="" class=""></标签>
    2. 恰当时机,通过id、class 或 ref属性 获取目标元素:

      js 复制代码
      // 挂载后(虚拟DOM变成真实DOM)
      mounted() {
        // 只要是原生JS获取DOM元素的方法都可以获取
        // document.querySelect/document.querySelectAll......
        console.log(document.getElementById('id名称'))
        console.log(this.$refs.ref属性值)
      }
  • 注意

    • ref属性 不展示 在 标签上;
    • 可以在Vue调式面板()看到:
  • 示例展示:

    html 复制代码
    <template>
        <div>
            <!-- 1.给要获取的标签添加ref属性,并设置相应的属性值 -->
            <h4 ref="diJia">迪迦奥特曼</h4>
        </div>
    </template>
    
    <script>
        export default {
            // 2.在恰当的实际,获取DOM元素
            mounted() {
                // 方法一:Web API方法
                // 方法二:this.$refs.ref属性值
                console.log(this.$refs.diJia);
                this.$refs.diJia.style.color = "red";
            },
        };
    </script>

5.1.2 获取组件对象

  • 实现步骤
    *
    1. 组件标签添加 ref属性
      1. 在恰当时机(挂载后:mounted),获取组件对象;
      • 恰当时机 ➡ 虚拟DOM挂载到DOM树上,既虚拟DOM变成真实DOMmounted);
      • 使用 this.$refs.ref名称
  • 总结
    1. 作用
      • 既可以获取原生DOM,也可以获取组件对象;
    2. 使用步骤 (2步):
      • 给需要获取的原生DOM或组件标签添加ref属性,并设置属性值;
      • 在恰当时机(挂在后:mounted):同故宫 this.$refs.ref属性值 获取;
    3. 获取到的组件对象就是该组件中的this;

5.2 Vue - 异步更新DOM

  • Vue更新DOM异步 的、并且是 批量的
    • 这样做的优点
      • 减少 重绘 和 回流
  • Vue 监测 数据更新 ,开启一个DOM更新队列(异步任务);
  • 示例展示:

5.3 $nextTick

  • DOM更新后触发 此方法里的 函数执行

  • 语法:

    js 复制代码
    this.$nextTick(回调函数)
  • 返回值

    • $nextTick() 返回的是一个 Promise,可以配合 async 和 await 使用;
  • 示例展示:

  • 代码展示:

    html 复制代码
    <template>
        <div>
            <h4>4.$nextTick应用场景</h4>
            <button @click="search" v-if="flag">点击搜索</button>
            <input ref="input" type="text" placeholder="请输入关键字" v-else />
        </div>
    </template>
    
    <script>
        export default {
            data() {
                    return {
                            flag: true,
                    };
            },
            methods: {
                async search() {
                    this.flag = false;
                    console.log(this.$refs.input); // undefined
                    // undefined 的原因
                    // data数据变化,需要更新DOM,Vue更新DOM是异步操作
                    // data数据更新完后,input还没有变成真实DOM
    
                    // 等待DOM更新完毕后,再去执行this.$nextTick回调函数的函数体
                    this.$nextTick(() => {
                        console.log(this.$refs.input);	// input标签
                        this.$refs.input.focus();
                    });
    
                    // $nextTick原地返回一个Promise实例,可以async和await简化Promise操作
                    // 这个Promise实例里面保存着异步操作的结果
                    // await可以得到这个异步操作的结果
                    // 也就是说,程序会停止到这一行,直到await等待到这个异步操作结果,才会去执行下面的代码
                    await this.$nextTick();
                    this.$refs.input.focus();
                },
            },
        };
    </script>
相关推荐
_.Switch36 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光40 分钟前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   41 分钟前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   42 分钟前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常4 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js