Vue生命周期(Vue笔记三)

一、Vue生命周期和生命周期的四个阶段

1.1 Vue生命周期介绍

Vue生命周期:一个Vue实例从 创建销毁 的整个过程。

1.2 生命周期的四个阶段

生命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 销毁

  1. 创建阶段:创建响应式数据(通过data给当前的Vue实例提供的数据默认的都是普通数据,Vue在底层创建阶段,会将这些普通数据来做一些响应式处理,把它转换成响应式的。)
  2. 挂载阶段:渲染模板
  3. 更新阶段:修改数据,更新视图
  4. 销毁阶段:销毁Vue实例

创建阶段和挂载阶段只会执行一次,而更新阶段可以执行多次

思考:

  1. 什么时候可以发送初始化渲染请求?(越早越好)
    答: 创建阶段的最后。
  2. 什么时候可以开始操作dom?(至少dom得渲染出来)
    答: 等挂载阶段结束后。
    在这里插入图片描述

1.3 Vue生命周期函数(钩子函数)

Vue生命周期过程中,会自动运行一些函数,被称为生命周期钩子
让开发者可以在特定阶段运行自己的代码。
总共8个钩子
在这里插入图片描述

重点:created()mounted()

示例:

<!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>钩子函数</title>
</head>

<body>

  <div id="app">
    <h3>{{ title }}</h3>
    <div>
      <button @click="count--">-</button>
      <span>{{ count }}</span>
      <button @click="count++">+</button>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        count: 100,
        title: '计数器'
      },
      // 1.创建阶段(准备数据)
      beforeCreate() {
        console.log('beforeCreate 响应式数据准备好之前');
        // 此时访问数据,undefined。(在 beforeCreate 里面对数据做任何的处理都是不合理的)
        console.log('beforeCreate 响应式数据准备好之前', this.count);
      },
      created() {
        console.log('created 响应式数据准备好之后', this.count);
        // this.数据名 = 请求回来的数据
        // 可以开始发送初始化渲染的请求了
      },

      // 2.挂载阶段(渲染模版)
      beforeMount() {
        console.log('beforeMount 模版渲染之前', document.querySelector('h3').innerHTML);
        // 结果:{{ title }}
      },
      mounted() {
        console.log('mounted 模版渲染之后', document.querySelector('h3').innerHTML);
        // 结果:计数器
        // 可以开始操作dom了
      },

      // 3.更新阶段(修改数据 -> 更新视图)
      beforeUpdate() {
        console.log('beforeUpdate 数据修改了,视图还没有更新', document.querySelector('span').innerHTML);
      },
      updated() {
        console.log('updated 数据修改了,视图更新完毕', document.querySelector('span').innerHTML);
      },

      // 4.销毁阶段
      /* 官方提供的销毁语法 app.$destroy()(
      并不会把当前页面中的元素清除,之前已经渲染完的还是存在的,但是所有的事件监听,所有和Vue相关的绑定代码,全都卸载掉了。
      所以销毁之后,点击加减按钮没有改变 */
      beforeDestroy() {
        console.log('beforeDestroy 卸载前');
        // 因为销毁后,之前已经渲染完的还是存在的,所以通常会在 beforeDestroy 中清除掉一些Vue以外的资源占用,定时器、延时器……,优化
        console.log('清除掉一些Vue以外的资源占用,定时器、延时器……');
      },
      destroyed() {
        console.log('destroyed 卸载后');
      }
    })
  </script>
</body>

</html>

控制台信息:
在这里插入图片描述
官方提供的销毁语法 app.$destroy()
在这里插入图片描述

1.4 案例

1.4.1 在created中发送数据

需求:一进入页面,立刻发送请求,去获取对应的数据,渲染出来。

created:响应式数据准备好了,可以开始发送初始化渲染请求。

准备代码:

<!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>created应用</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .news {
      display: flex;
      height: 120px;
      width: 600px;
      margin: 0 auto;
      padding: 20px 0;
      cursor: pointer;
    }

    .news .left {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      padding-right: 10px;
    }

    .news .left .title {
      font-size: 20px;
    }

    .news .left .info {
      color: #999999;
    }

    .news .left .info span {
      margin-right: 20px;
    }

    .news .right {
      width: 160px;
      height: 120px;
    }

    .news .right img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  </style>
</head>

<body>

  <div id="app">
    <ul>
      <li class="news">
        <div class="left">
          <div class="title">5G商用在即,三大运营商营收持续下降</div>
          <div class="info">
            <span>新京报经济新闻</span>
            <span>2222-10-28 11:50:28</span>
          </div>
        </div>
        <div class="right">
          <img src="images/新闻.webp" alt="">
        </div>
      </li>

      <li class="news">
        <div class="left">
          <div class="title">5G商用在即,三大运营商营收持续下降</div>
          <div class="info">
            <span>新京报经济新闻</span>
            <span>2222-10-28 11:50:28</span>
          </div>
        </div>
        <div class="right">
          <img src="images/新闻.webp" alt="">
        </div>
      </li>

      <li class="news">
        <div class="left">
          <div class="title">5G商用在即,三大运营商营收持续下降</div>
          <div class="info">
            <span>新京报经济新闻</span>
            <span>2222-10-28 11:50:28</span>
          </div>
        </div>
        <div class="right">
          <img src="images/新闻.webp" alt="">
        </div>
      </li>
    </ul>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script src="./axios.js"></script>
  <script>
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    const app = new Vue({
      el: '#app',
      data: {
        list: []
      },
    })
  </script>
</body>

</html>

在这里插入图片描述

(1)一进页面立刻发请求,拿到了完整的新闻列表的数据。

<script>
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    const app = new Vue({
      el: '#app',
      data: {
        list: []
      },
      async created() {
        // 1.发送请求获取数据
        const res = await axios.get('http://hmajax.itheima.net/api/news')
        console.log(res);
      }
    })
  </script>

在这里插入图片描述

(2)将数据更新给 data 中的list

 <script>
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    const app = new Vue({
      el: '#app',
      data: {
        list: []
      },
      async created() {
        // 1.发送请求获取数据
        const res = await axios.get('http://hmajax.itheima.net/api/news')
        console.log(res);
        // 2.将数据更新给 data 中的list
        this.list = res.data.data
      }
    })
  </script>

在这里插入图片描述

(3)用 v-for 进行渲染。留下一个 < li > 就行

<div id="app">
    <ul>
      <li v-for="(item, index) in list" :key="item.id" class="news">
        <div class="left">
          <div class="title">{{item.title}}</div>
          <div class="info">
            <span>{{item.source}}</span>
            <span>{{item.time}}</span>
          </div>
        </div>
        <div class="right">
          <img :src="item.img" alt="">
        </div>
      </li>
    </ul>
  </div>

完整代码实现

<!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>created应用</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .news {
      display: flex;
      height: 120px;
      width: 600px;
      margin: 0 auto;
      padding: 20px 0;
      cursor: pointer;
    }

    .news .left {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      padding-right: 10px;
    }

    .news .left .title {
      font-size: 20px;
    }

    .news .left .info {
      color: #999999;
    }

    .news .left .info span {
      margin-right: 20px;
    }

    .news .right {
      width: 160px;
      height: 120px;
    }

    .news .right img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  </style>
</head>

<body>

  <div id="app">
    <ul>
      <li v-for="(item, index) in list" :key="item.id" class="news">
        <div class="left">
          <div class="title">{{item.title}}</div>
          <div class="info">
            <span>{{item.source}}</span>
            <span>{{item.time}}</span>
          </div>
        </div>
        <div class="right">
          <img :src="item.img" alt="">
        </div>
      </li>
    </ul>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script src="./axios.js"></script>
  <script>
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    const app = new Vue({
      el: '#app',
      data: {
        list: []
      },
      async created() {
        // 1.发送请求获取数据
        const res = await axios.get('http://hmajax.itheima.net/api/news')
        console.log(res);
        // 2.将数据更新给 data 中的list
        this.list = res.data.data
      }
    })
  </script>
</body>

</html>

运行结果
在这里插入图片描述

个人错误点:
<img :src="item.img" alt=""> 利用v-forx渲染图片

1.4.2 在mounted中获取焦点

需求:一进入页面,搜索框立刻能够获取焦点。

mounted:模版渲染完成,可以开始操作DOM了。

准备代码:


<!DOCTYPE html>
<html lang="zh-CN">

<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>示例-获取焦点</title>
  <!-- 初始化样式 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
  <!-- 核心样式 -->
  <style>
    html,
    body {
      height: 100%;
    }
    .search-container {
      position: absolute;
      top: 30%;
      left: 50%;
      transform: translate(-50%, -50%);
      text-align: center;
    }
    .search-container .search-box {
      display: flex;
    }
    .search-container img {
      margin-bottom: 30px;
    }
    .search-container .search-box input {
      width: 512px;
      height: 16px;
      padding: 12px 16px;
      font-size: 16px;
      margin: 0;
      vertical-align: top;
      outline: 0;
      box-shadow: none;
      border-radius: 10px 0 0 10px;
      border: 2px solid #c4c7ce;
      background: #fff;
      color: #222;
      overflow: hidden;
      box-sizing: content-box;
      -webkit-tap-highlight-color: transparent;
    }
    .search-container .search-box button {
      cursor: pointer;
      width: 112px;
      height: 44px;
      line-height: 41px;
      line-height: 42px;
      background-color: #ad2a27;
      border-radius: 0 10px 10px 0;
      font-size: 17px;
      box-shadow: none;
      font-weight: 400;
      border: 0;
      outline: 0;
      letter-spacing: normal;
      color: white;
    }
    body {
      background: no-repeat center /cover;
      background-color: #edf0f5;
    }
  </style>
</head>

<body>
<div class="container" id="app">
  <div class="search-container">
    <img src="https://www.itheima.com/images/logo.png" alt="">
    <div class="search-box">
      <input type="text" v-model="words" id="inp">
      <button>搜索一下</button>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      words: ''
    }
  })
</script>

</body>

</html>

在这里插入图片描述
完整代码实现

<!DOCTYPE html>
<html lang="zh-CN">

<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>示例-获取焦点</title>
  <!-- 初始化样式 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
  <!-- 核心样式 -->
  <style>
    html,
    body {
      height: 100%;
    }

    .search-container {
      position: absolute;
      top: 30%;
      left: 50%;
      transform: translate(-50%, -50%);
      text-align: center;
    }

    .search-container .search-box {
      display: flex;
    }

    .search-container img {
      margin-bottom: 30px;
    }

    .search-container .search-box input {
      width: 512px;
      height: 16px;
      padding: 12px 16px;
      font-size: 16px;
      margin: 0;
      vertical-align: top;
      outline: 0;
      box-shadow: none;
      border-radius: 10px 0 0 10px;
      border: 2px solid #c4c7ce;
      background: #fff;
      color: #222;
      overflow: hidden;
      box-sizing: content-box;
      -webkit-tap-highlight-color: transparent;
    }

    .search-container .search-box button {
      cursor: pointer;
      width: 112px;
      height: 44px;
      line-height: 41px;
      line-height: 42px;
      background-color: #ad2a27;
      border-radius: 0 10px 10px 0;
      font-size: 17px;
      box-shadow: none;
      font-weight: 400;
      border: 0;
      outline: 0;
      letter-spacing: normal;
      color: white;
    }

    body {
      background: no-repeat center /cover;
      background-color: #edf0f5;
    }
  </style>
</head>

<body>
  <div class="container" id="app">
    <div class="search-container">
      <img src="https://www.itheima.com/images/logo.png" alt="">
      <div class="search-box">
        <input type="text" v-model="words" id="inp">
        <button>搜索一下</button>
      </div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        words: ''
      },
      /* 核心思路:
      1. 等输入框渲染出来
      2. 让输入框获取焦点 */
      mounted() {
        // console.log(document.querySelector('#inp'));
        document.querySelector('#inp').focus();
      }
    })
  </script>

</body>

</html>

二、综合案例:小黑记账清单

2.1 需求图示:

在这里插入图片描述

2.2 需求分析

  1. 基本渲染

  2. 添加功能

  3. 删除功能

  4. 饼图渲染

2.3 准备代码

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

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>小黑记账清单</title>
  <!-- CSS only -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
  <style>
    .red {
      color: red !important;
    }

    .search {
      width: 300px;
      margin: 20px 0;
    }

    .my-form {
      display: flex;
      margin: 20px 0;
    }

    .my-form input {
      flex: 1;
      margin-right: 20px;
    }

    .table> :not(:first-child) {
      border-top: none;
    }

    .contain {
      display: flex;
      padding: 10px;
    }

    .list-box {
      flex: 1;
      padding: 0 30px;
    }

    .list-box a {
      text-decoration: none;
    }

    .echarts-box {
      width: 600px;
      height: 400px;
      padding: 30px;
      margin: 0 auto;
      border: 1px solid #ccc;
    }

    tfoot {
      font-weight: bold;
    }

    @media screen and (max-width: 1000px) {
      .contain {
        flex-wrap: wrap;
      }

      .list-box {
        width: 100%;
      }

      .echarts-box {
        margin-top: 30px;
      }
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="contain">
      <!-- 左侧列表 -->
      <div class="list-box">

        <!-- 添加资产 -->
        <form class="my-form">
          <input type="text" class="form-control" placeholder="消费名称" />
          <input type="text" class="form-control" placeholder="消费价格" />
          <button type="button" class="btn btn-primary">添加账单</button>
        </form>

        <table class="table table-hover">
          <thead>
            <tr>
              <th>编号</th>
              <th>消费名称</th>
              <th>消费价格</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>1</td>
              <td>帽子</td>
              <td>99.00</td>
              <td><a href="javascript:;">删除</a></td>
            </tr>
            <tr>
              <td>2</td>
              <td>大衣</td>
              <td class="red">199.00</td>
              <td><a href="javascript:;">删除</a></td>
            </tr>
          </tbody>
          <tfoot>
            <tr>
              <td colspan="4">消费总计: 298.00</td>
            </tr>
          </tfoot>
        </table>
      </div>

      <!-- 右侧图表 -->
      <div class="echarts-box" id="main"></div>
    </div>
  </div>
  <!-- 以下三个文件需要自行下载,注意路径 -->
  <script src="../echarts.min.js"></script>
  <script src="../vue.js"></script>
  <script src="../axios.js"></script>
  <script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     * 
     * 功能需求:
     * 1. 基本渲染
     * 2. 添加功能
     * 3. 删除功能
     * 4. 饼图渲染
     */
    const app = new Vue({
      el: '#app',
      data: {

      },
    })
  </script>
</body>

</html>

2.4 具体实现

2.4.1 基本渲染

思路分析

  1. 立刻发送请求获取数据 created
  2. 拿到数据,存到data的响应式数据中
  3. 结合数据,进行渲染 v-for
  4. 消费统计 —> 计算属性
  • 立刻发送请求获取数据 created,拿到数据,存到data的响应式数据中

接口地址
在这里插入图片描述

<script>
    
    const app = new Vue({
      el: '#app',
      data: {
        list: []
      },
      async created() {
        // (1)立刻发送请求获取数据 created
        // 有多个接口,所以通过上面给出的接口文档地址,找到相关的具体接口地址
        const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
          params: {
            // 请求参数
            creator: '小林' //名字可以随便取
          }
        })
        console.log(res);

        // (2)拿到数据,存到data的响应式数据中
        this.list = res.data.data
      }
    })
  </script>
  • 结合数据,进行渲染 v-for

在这里插入图片描述

<tbody>
    <!-- 基本渲染:(3)结合数据,进行渲染 v-for -->
    <tr v-for="(item,index) in list" :key="item.id">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <!-- 细节实现:消费价格超过500块,则价格标红 -->
        <!-- .toFixed(2) 保留两位小数 -->
        <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
        <td><a href="javascript:;">删除</a></td>
    </tr>
</tbody>

个人错误点:

  1. 消费价格超过500块,则价格标红(class前面需要打:
    <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
  • 消费统计 —> 计算属性

HTML部分

          <tfoot>
            <tr>
              <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
            </tr>
          </tfoot>

Vue部分:

computed: {
        // 消费总计
        totalPrice() {
          return this.list.reduce((sum, item) => sum + item.price, 0)
        }
      }

个人错误点:

  1. 求和
    return this.list.reduce((sum, item) => sum + item.price, 0)

基本渲染实现:
在这里插入图片描述

2.4.2 添加功能

思路分析

  1. 收集表单数据 v-model,使用指令修饰符处理数据
  2. 给添加按钮注册点击事件,对输入的内容做非空判断,发送请求
  3. 请求成功后,对文本框内容进行清空
  4. 重新渲染列表
  • 收集表单数据 v-model,使用指令修饰符处理数据

(1)Vue部分,在data中添加 name, price两个变量

data: {
        list: [],
        name: '',
        price: ''
      }

(2)找到对应的表单,进行绑定

<input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
<input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
  • 给添加按钮注册点击事件,对输入的内容做非空判断,发送请求

(1)给添加按钮注册点击事件

<button @click="add" type="button" class="btn btn-primary">添加账单</button>

(2)对输入的内容做非空判断,发送请求。请求成功后,对文本框内容进行清空
在这里插入图片描述

methods: {
        // 添加功能:(2)对输入的内容做非空判断,发送请求
        async add() {
          if (!this.name) {
            alert('请输入消费名称')
            return
          }
          if (typeof this.price !== 'number') {
            alert('请输入正确的消费价格')
            return
          }       
             // 发送请求
          const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
            // 只有get和delete请求传参的时候需要额外配置 params
            creator: '小林',
            name: this.name,
            price: this.price
          })
          console.log(res);
          this.name = ''
          this.price = ''
        }
      }

(3)重新渲染列表

  • 注意:运行当前代码,进行添加操作之后,后台数据添加成功,而页面上没有显示刚添加的数据,但是刷新页面后,刚添加的数据显示出来了。所以需要进行重新渲染,获得后台最新的数据。
  • 之前在实现基本渲染功能时,用到了渲染,而且之后很多地方需要重新渲染,我们就可以将那一段代码封装成一个函数,便于多次使用。
created() {
        // 基本渲染
        this.getList()
        /* // (1)立刻发送请求获取数据 created
        // 有多个接口,所以通过上面给出的接口文档地址,找到相关的具体接口地址
        const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
          params: {
            // 请求参数
            creator: '小林' //名字可以随便取
          }
        })
        console.log(res);

        // (2)拿到数据,存到data的响应式数据中
        this.list = res.data.data */
      },
      methods: {
        async getList() {
          const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            params: {
              creator: '小林'
            }
          })
          console.log(res);
          this.list = res.data.data
        },
        // 添加功能:(2)对输入的内容做非空判断,发送请求
        async add() {
          if (!this.name) {
            alert('请输入消费名称')
            return
          }
          if (typeof this.price !== 'number') {
            alert('请输入正确的消费价格')
            return
          }
          // 发送请求
          const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
            // 只有get和delete请求传参的时候需要额外配置 params
            creator: '小林',
            name: this.name,
            price: this.price
          })
          console.log(res);
          // 重新渲染一次
          this.getList()
          // 清空输入框
          this.name = ''
          this.price = ''
        }
      }

2.4.3 删除功能

  1. 注册点击事件,获取当前行的id
  2. 根据id发送删除请求
  3. 需要重新渲染
  • 注册点击事件,获取当前行的id
<!-- 删除功能:(1)注册点击事件,获取当前行的id -->
<td><a @click="del(item.id)" href="javascript:;">删除</a></td>
  • 根据id发送删除请求,需要重新渲染
    在这里插入图片描述
async del(id) {
          // 删除功能
          // (2)根据id发送删除请求
          // 注意:这里要用 ` 符号,才能用${},es6语法
          const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
          // console.log(res);
          // (3)需要重新渲染
          this.getList()
        }

2.4.4 饼图渲染

  1. 初始化一个饼图 echarts.init(dom) mounted钩子中渲染
  2. 根据数据实时更新饼图 echarts.setOptions({…})
  • 初始化一个饼图

用到echarts插件。(echarts使用手册:https://echarts.apache.org/handbook/zh/get-started/)
(1)先前给的准备代码中,已经为 ECharts 准备一个定义了高宽的 DOM 容器。

<div class="echarts-box" id="main"></div>

(2)基于准备好的dom,初始化echarts实例 → mounted钩子中渲染

  1. 在echarts的示例中,找到饼图示例。
    在这里插入图片描述
    在这里插入图片描述
  2. 将整个option对象复制到我们的代码中使用。
mounted(){
        // 基于准备好的dom,初始化echarts实例
        let myChart = echarts.init(document.querySelector('#main'));
        myChart.setOption({
          title: {
            text: 'Referer of a Website',
            subtext: 'Fake Data',
            left: 'center'
          },
          tooltip: {
            trigger: 'item'
          },
          legend: {
            orient: 'vertical',
            left: 'left'
          },
          series: [
            {
              name: 'Access From',
              type: 'pie',
              radius: '50%',
              data: [
                { value: 1048, name: 'Search Engine' },
                { value: 735, name: 'Direct' },
                { value: 580, name: 'Email' },
                { value: 484, name: 'Union Ads' },
                { value: 300, name: 'Video Ads' }
              ],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        })
      }
  • 根据数据实时更新饼图

(可以在echarts使用手册上查看如何实现。)

在这里插入图片描述
(1)为了能够根据数据实时更新饼图,在 getList() 里再调一次 myChart.setOption

async getList() {
          const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            params: {
              creator: '小林'
            }
          })
          console.log(res);
          this.list = res.data.data
          
          // 根据数据实时更新饼图
          myChart.setOption({

          })

        }

注意:这里的 myChart 不能直接用到 mounted() 里的 myChart ,所以我们需要把 mounted() 里的 myChart 挂到当前的实例身上

let myChart = echarts.init(document.querySelector('#main'));
myChart.setOption({ })
//修改成
this.myChart = echarts.init(document.querySelector('#main'));
this.myChart.setOption({ })

getList() 也需要进行修改

myChart.setOption({

          })
//修改成
this.myChart.setOption({

          }) 

(2)把需要更新的配置项写在getList()this.myChart.setOption里面。

async getList() {
          const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            params: {
              creator: '小林'
            }
          })
          console.log(res);
          this.list = res.data.data

          // 根据数据实时更新饼图
          this.myChart.setOption({
            series: [
              {
                /* data: [
                  { value: 1048, name: 'Search Engine' },
                ] */
                // 需要动态生成
                data: this.list.map(item => ({ value: item.price, name: item.name }))
              }
            ]
          })
        }

(3) mounted() 里的 this.myChart.setOption也需要修改一些数据

mounted() {
        // 基于准备好的dom,初始化echarts实例
        this.myChart = echarts.init(document.querySelector('#main'));
        this.myChart.setOption({
          // 大标题
          title: {
            text: '消费账单列表',
            // 控制居中
            left: 'center'
          },
          // 提示框
          tooltip: {
            trigger: 'item'
          },
          // 图例
          legend: {
            orient: 'vertical',
            left: 'left'
          },
          // 数据项
          series: [
            {
              name: '消费账单',
              type: 'pie',
              radius: '50%', //圆的半径
              data: [],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        })
      }

2.5 完整代码实现

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

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>小黑记账清单</title>
  <!-- CSS only -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
  <style>
    .red {
      color: red !important;
    }

    .search {
      width: 300px;
      margin: 20px 0;
    }

    .my-form {
      display: flex;
      margin: 20px 0;
    }

    .my-form input {
      flex: 1;
      margin-right: 20px;
    }

    .table> :not(:first-child) {
      border-top: none;
    }

    .contain {
      display: flex;
      padding: 10px;
    }

    .list-box {
      flex: 1;
      padding: 0 30px;
    }

    .list-box a {
      text-decoration: none;
    }

    .echarts-box {
      width: 600px;
      height: 400px;
      padding: 30px;
      margin: 0 auto;
      border: 1px solid #ccc;
    }

    tfoot {
      font-weight: bold;
    }

    @media screen and (max-width: 1000px) {
      .contain {
        flex-wrap: wrap;
      }

      .list-box {
        width: 100%;
      }

      .echarts-box {
        margin-top: 30px;
      }
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="contain">
      <!-- 左侧列表 -->
      <div class="list-box">

        <!-- 添加资产 -->
        <form class="my-form">
          <!-- 添加功能:(1)收集表单数据 v-model,使用指令修饰符处理数据 -->
          <input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
          <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
          <!-- 添加功能:(2)给添加按钮注册点击事件 -->
          <button @click="add" type="button" class="btn btn-primary">添加账单</button>
        </form>

        <table class="table table-hover">
          <thead>
            <tr>
              <th>编号</th>
              <th>消费名称</th>
              <th>消费价格</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <!-- 基本渲染:(3)结合数据,进行渲染 v-for -->
            <tr v-for="(item,index) in list" :key="item.id">
              <td>{{ index + 1 }}</td>
              <td>{{ item.name }}</td>
              <!-- 细节实现:消费价格超过500块,则价格标红 -->
              <!-- .toFixed(2) 保留两位小数 -->
              <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
              <!-- 删除功能:(1)注册点击事件,获取当前行的id -->
              <td><a @click="del(item.id)" href="javascript:;">删除</a></td>
            </tr>
          </tbody>
          <tfoot>
            <tr>
              <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
            </tr>
          </tfoot>
        </table>
      </div>

      <!-- 右侧图表 -->
      <!-- 为 ECharts 准备一个定义了高宽的 DOM 容器 -->
      <div class="echarts-box" id="main"></div>
    </div>
  </div>
  <!-- 以下三个文件需要自行下载,注意路径 -->
  <script src="../echarts.min.js"></script>
  <script src="../vue.js"></script>
  <script src="../axios.js"></script>
  <script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     * 
     * 功能需求:
     * 1. 基本渲染
     *    (1)立刻发送请求获取数据 created
     *    (2)拿到数据,存到data的响应式数据中
     *    (3)结合数据,进行渲染 v-for
     *    (4)消费统计  —> 计算属性
     * 2. 添加功能
     *    (1)收集表单数据 v-model,使用指令修饰符处理数据
     *    (2)给添加按钮注册点击事件,对输入的内容做非空判断,发送请求
     *    (3)请求成功后,对文本框内容进行清空
     *    (4)重新渲染列表 
     * 3. 删除功能
     *    (1)注册点击事件,获取当前行的id
     *    (2)根据id发送删除请求
     *    (3)需要重新渲染
     * 4. 饼图渲染
     *    (1)初始化一个饼图 echarts.init(dom)    mounted钩子中渲染
     *    (2)根据数据试试更新饼图 echarts.setOptions({...})
     */
    const app = new Vue({
      el: '#app',
      data: {
        list: [],
        name: '',
        price: ''
      },
      computed: {
        // 消费总计
        totalPrice() {
          return this.list.reduce((sum, item) => sum + item.price, 0)
        }
      },
      created() {
        // 基本渲染
        this.getList()
        /* // (1)立刻发送请求获取数据 created
        // 有多个接口,所以通过上面给出的接口文档地址,找到相关的具体接口地址
        const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
          params: {
            // 请求参数
            creator: '小林' //名字可以随便取
          }
        })
        console.log(res);

        // (2)拿到数据,存到data的响应式数据中
        this.list = res.data.data */
      },
      mounted() {
        // 基于准备好的dom,初始化echarts实例
        this.myChart = echarts.init(document.querySelector('#main'));
        this.myChart.setOption({
          // 大标题
          title: {
            text: '消费账单列表',
            // 控制居中
            left: 'center'
          },
          // 提示框
          tooltip: {
            trigger: 'item'
          },
          // 图例
          legend: {
            orient: 'vertical',
            left: 'left'
          },
          // 数据项
          series: [
            {
              name: '消费账单',
              type: 'pie',
              radius: '50%', //圆的半径
              data: [],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        })
      },
      methods: {
        async getList() {
          const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            params: {
              creator: '小林'
            }
          })
          console.log(res);
          this.list = res.data.data

          // 根据数据实时更新饼图
          this.myChart.setOption({
            series: [
              {
                /* data: [
                  { value: 1048, name: 'Search Engine' },
                ] */
                // 需要动态生成
                data: this.list.map(item => ({ value: item.price, name: item.name }))
              }
            ]
          })
        },
        // 添加功能:(2)对输入的内容做非空判断,发送请求
        async add() {
          if (!this.name) {
            alert('请输入消费名称')
            return
          }
          if (typeof this.price !== 'number') {
            alert('请输入正确的消费价格')
            return
          }
          // 发送请求
          const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
            // 只有get和delete请求传参的时候需要额外配置 params
            creator: '小林',
            name: this.name,
            price: this.price
          })
          console.log(res);
          // 重新渲染一次
          this.getList()
          // 清空输入框
          this.name = ''
          this.price = ''
        },
        async del(id) {
          // 删除功能
          // (2)根据id发送删除请求
          // 注意:这里要用 ` 符号,才能用${},es6语法
          const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
          // console.log(res);
          // (3)需要重新渲染
          this.getList()
        }
      }
    })
  </script>
</body>

</html>

在这里插入图片描述

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3中,选项API(如data,methods)已被改为使用setup组合API。setup函数是在组件创建之前执行的,并且它的执行优先级要高于Vue2中的生命周期钩子。因此,在Vue3中不推荐使用Vue2的生命周期钩子,并且要使用生命周期钩子,需要先引入并使用onMounted和onBeforeMount等函数。 然而,在Vue3的setup选项中仍然可以使用Vue2的生命周期钩子。例如,在setup函数中可以使用beforeCreate、created、beforeMount和mounted等生命周期钩子。 在父组件中,可以通过setup函数来定义响应式的数据,并且可以使用onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount和onUnmounted等生命周期钩子。 总结来说,Vue3中的生命周期钩子函数的使用方式和Vue2有所不同,推荐使用setup组合API来处理组件的生命周期。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [vue2生命周期vue3生命周期的区别](https://blog.csdn.net/qq252750818/article/details/124740398)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [前端学习笔记(9)-Vue2和 Vue3生命周期区别](https://blog.csdn.net/JiangZhengyang7/article/details/128000710)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值