Vue2: Component Basics (Part 1)

Vue2: Component Basics (Part 1)

Date: July 29, 2023
Sum: Life cycle, Vue-cli, component usage, Xiaohei billing list, Xiaotuxian homepage


life cycle:

Introduction to life cycle

think:

When can an initial render request be sent? (the sooner the better)

When can I start manipulating the dom? (at least the dom has to be rendered)

Vue life cycle: It is the whole process of a Vue instance from creation to destruction.

There are four stages in the life cycle : ① Create ② Mount ③ Update ④ Destroy

1. Creation phase: Create responsive data

Convert normal data to reactive data

2. Mounting phase: rendering template

Render templates with data

3. Update phase: modify data, update view

Data modification and update view loop

4. Destruction phase: Destroy the Vue instance

Untitled

Note: There is only one creation and mount phase, but the update phase will cycle multiple times

Understanding: After understanding the life cycle of Vue, you can understand when to call the corresponding code

For example, after the creation phase is over, we can send the initial rendering request. Dom can only be operated after the mount phase is over



life cycle hook

During the Vue life cycle, some functions will be automatically run , which are called [ life cycle hooks ] → allowing developers to run their own code at [ specific stages ]

Untitled

Note: After the mounted phase is completed, the template has been rendered

Note: The unload phase is called when you close the browser

case :

Untitled

  • Code:

    <!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>
    </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/dist/vue.js"></script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            count: 100,
            title: '计数器'
          },
          // 1. 创建阶段(准备数据)
          beforeCreate () {
            console.log('beforeCreate 响应式数据准备好之前', this.count)
          },
          created () {
            console.log('created 响应式数据准备好之后', this.count)
            // this.数据名 = 请求回来的数据
            // 可以开始发送初始化渲染的请求了
          },
    
          // 2. 挂载阶段(渲染模板)
          beforeMount () {
            console.log('beforeMount 模板渲染之前', document.querySelector('h3').innerHTML)
          },
          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. 卸载阶段
          beforeDestroy () {
            console.log('beforeDestroy, 卸载前')
            console.log('清除掉一些Vue以外的资源占用,定时器,延时器...')
          },
          destroyed () {
            console.log('destroyed,卸载后')
          }
        })
      </script>
    </body>
    </html>
    

Note: After being destroyed in the life cycle, the above components are useless even if you press + or -, because the above DOM has nothing to do with Vue.

Supplement: Enter in the browser console

app.$destory()

can trigger beforeDestory and destroyed



life cycle case

Case 1 :

Requirement : Send a request as soon as you enter the page to obtain the corresponding data and render it

Thinking : send an initialization rendering request in the created hook, and use axios to request data

Untitled

  • Code:

    <!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>
      <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/dist/vue.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.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')
            // 2. 更新到 list 中,用于页面渲染 v-for
            this.list = res.data.data
          }
        })
      </script>
    </body>
    </html>
    

Case 2 :

Requirement : Get the focus as soon as you enter the page

Thinking : Get the focus on the mounted hook

Untitled

Note: After the mounted phase is completed, the template has been rendered

  • Code:

    <!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/[email protected]/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/dist/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          words: ''
        },
        // 核心思路:
        // 1. 等input框渲染出来 mounted 钩子
        // 2. 让input框获取焦点 inp.focus()
        mounted () {
          document.querySelector('#inp').focus()
        }
      })
    </script>
    
    </body>
    
    </html>
    



Comprehensive Case - Xiaohei Bookkeeping List

1- Requirement Diagram

Untitled

2- Demand Analysis

1. Basic rendering

2. Add function

3. Delete function

4. Pie chart rendering

3- Thought Analysis

1. Basic rendering

  • Immediately send a request to get data created
  • Get the data and store it in the responsive data of data
  • Combine data and render v-for
  • Consumption Statistics —> Computed Attributes

2. Add function

  • Collect form data v-model, use instruction modifiers to process data
  • Register the click event for the add button, make a non-empty judgment on the input content, and send the request
  • After the request is successful, clear the content of the text box
  • re-render list

3. Delete function

  • Register the click event to get the id of the current row
  • Send delete request based on id
  • need to re-render

4. Pie chart rendering

  • Initialize a pie chart rendering in echarts.init(dom) mounted hook
  • Try to update the pie chart based on the data echarts.setOptions({...})

Untitled

Implement basic rendering functionality :

  • Code: Implement basic rendering

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- CSS only -->
        <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/[email protected]/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 v-for="(item, index) in list" :key="item.id">
                    <td>{
         
         { index + 1 }}</td>
                    <td>{
         
         { item.name }}</td>
                    <td :class="{ red: item.price > 500}">{
         
         { item.price.toFixed(2) }}</td>
                    <td><a href="javascript:;">删除</a></td>
                  </tr>
                </tbody>
                <tfoot>
                  <tr>
                    <td colspan="4">消费总计: {
         
         { totalPrice.toFixed(2) }}</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: {
              list: [],
            },
            computed: {
              totalPrice() {
                return this.list.reduce((sum, item) => sum += item.price, 0)
              }
            },
            async created() {
              // const res = await axios({
              //   url: 'https://applet-base-api-t.itheima.net/bill',
              //   method: 'get',
              //   params: {
              //     creator: 'Nathan',
              //   }
              // })
              const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
                params: {
                  creator: 'Nathan'
                }
              })
              this.list = res.data.data
            }
          })
        </script> 
      </body>
    </html>
    

Implement the add function :

Untitled

Key point: After submitting the data, you need to re-render the page with axios request data. The following is a suggestion to encapsulate the axios request

Pay attention to the two request formats of get and post:

Params need to be added in {} after get

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

There is no need to add params in the {} after the post


async add() {
  if(!this.name) {
    alert('请输入消费名称')
    return
  }
  if(typeof this.price !== 'number') {
    alert('请输入正确的价格')
  }
  const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
    creator: 'Nathan',
    name: this.name,
    price: this.price,
  })
  this.getList()
  this.name = ''
  this.price = ''
}
  • Code:

    
    

Implement the delete function :

  • Code:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- CSS only -->
        <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/[email protected]/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 v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
                <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
                <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>
                  <tr v-for="(item, index) in list" :key="item.id">
                    <td>{
         
         { index + 1 }}</td>
                    <td>{
         
         { item.name }}</td>
                    <td :class="{ red: item.price > 500 }">{
         
         { item.price.toFixed(2) }}</td>
                    <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>
            
            <!-- 右侧图表 -->
            <div class="echarts-box" id="main"></div>
          </div>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.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) 需要重新渲染
           * 3. 删除功能
           *    (1) 注册点击事件,传参传 id
           *    (2) 根据 id 发送删除请求
           *    (3) 需要重新渲染
           * 4. 饼图渲染
           */
          const app = new Vue({
            el: '#app',
            data: {
              list: [],
              name: '',
              price: ''
            },
            computed: {
              totalPrice () {
                return this.list.reduce((sum, item) => sum + item.price, 0)
              }
            },
            created () {
              // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
              //   params: {
              //     creator: '小黑'
              //   }
              // })
              // this.list = res.data.data
    
              this.getList()
            },
            methods: {
              async getList () {
                const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
                  params: {
                    creator: '小黑'
                  }
                })
                this.list = res.data.data
              },
              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', {
                  creator: '小黑',
                  name: this.name,
                  price: this.price
                })
                // 重新渲染一次
                this.getList()
    
                this.name = ''
                this.price = ''
              },
              async del (id) {
                // 根据 id 发送删除请求
                const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
                // 重新渲染
                this.getList()
              }
            }
          })
        </script>
      </body>
    </html>
    

Pie chart rendering function :

The essential:

1-Use this at mounted to bind mychart to the Vue object instance

mounted() {
  // 利用 this 暂时存取一下myChart
  this.myChart = echarts.init(document.querySelector('#main'))
  // 指定图表的配置项和数据
  var option = {
    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)'
          }
        }
      }
    ]
  };
  // 使用刚指定的配置项和数据显示图表。
  this.myChart.setOption(option);
}

2- After introducing echarts, we need to re-render the content under getList(), plus myChart.setOption, the key point is that we need to use this to bind up and down.

this.myChart.setOption({
  series: [
    {
      // data: [
      //   { value: 1048, name: 'Search Engine' },
      //   { value: 735, name: 'Direct' },
      // ],
      data: this.list.map(item => ({ value: item.price, name: item.name }))
    }
  ]
})
  • Code:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- CSS only -->
        <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/[email protected]/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 v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
                <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
                <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>
                  <tr v-for="(item, index) in list" :key="item.id">
                    <td>{
         
         { index + 1 }}</td>
                    <td>{
         
         { item.name }}</td>
                    <td :class="{ red: item.price > 500 }">{
         
         { item.price.toFixed(2) }}</td>
                    <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>
            
            <!-- 右侧图表 -->
            <div class="echarts-box" id="main"></div>
          </div>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.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) 需要重新渲染
           * 3. 删除功能
           *    (1) 注册点击事件,传参传 id
           *    (2) 根据 id 发送删除请求
           *    (3) 需要重新渲染
           * 4. 饼图渲染
           *    (1) 初始化一个饼图 echarts.init(dom)  mounted钩子实现
           *    (2) 根据数据实时更新饼图 echarts.setOption({ ... })
           */
          const app = new Vue({
            el: '#app',
            data: {
              list: [],
              name: '',
              price: ''
            },
            computed: {
              totalPrice () {
                return this.list.reduce((sum, item) => sum + item.price, 0)
              }
            },
            created () {
              // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
              //   params: {
              //     creator: '小黑'
              //   }
              // })
              // this.list = res.data.data
    
              this.getList()
            },
            mounted () {
              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: [
                      // { value: 1048, name: '球鞋' },
                      // { value: 735, name: '防晒霜' }
                    ],
                    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: '小黑'
                  }
                })
                this.list = res.data.data
    
                // 更新图表
                this.myChart.setOption({
                  // 数据项
                  series: [
                    {
                      // data: [
                      //   { value: 1048, name: '球鞋' },
                      //   { value: 735, name: '防晒霜' }
                      // ]
                      data: this.list.map(item => ({ value: item.price, name: item.name}))
                    }
                  ]
                })
              },
              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', {
                  creator: '小黑',
                  name: this.name,
                  price: this.price
                })
                // 重新渲染一次
                this.getList()
    
                this.name = ''
                this.price = ''
              },
              async del (id) {
                // 根据 id 发送删除请求
                const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
                // 重新渲染
                this.getList()
              }
            }
          })
        </script>
      </body>
    </html>
    

Note :

1- If you need to return an object in the arrow function, you need to add a pair of () outside {}

data: this.list.map(item => ({ value: item.price, name: item.name }))



Getting Started with Engineering Development

Engineering development and scaffolding

Engineering development mode

concept :

Core package traditional development mode: Based on html / css / js files, directly import core package to develop Vue.

Engineering development mode: develop Vue in an environment based on construction tools (eg: webpack)

Untitled

According to the above figure, different source codes can be converted into different versions of js codes through the engineering development mode

Advantages of engineering development mode :

Improve coding efficiency, such as using new JS syntax, Less/Sass, Typescript, etc., can be compiled into ES3/ES5/CSS recognized by browsers through webpack

Engineering development model issues :

  • webpack configuration is not simple
  • Same basic configuration
  • Lack of uniform standards

In order to solve the above problems, we need a tool to generate standardized configuration


Scaffolding Vue-cli

**Concept: **vue-cli is a tool officially provided by vue to quickly generate vue engineering projects. [Integrated webpack configuration]

Features :

  1. Out of the box, zero configuration

  2. Built-in tools such as babel

  3. Standardized webpack configuration

    Standardization: Your different projects can use the same shelf

The homepage of the Chinese official website of vue-cli: https://cli.vuejs.org/zh/



Steps to use Vue-cli

Install vue-cli

Vue-cli is a tool developed based on Node.js, so you need to use npm to install it as a globally available tool:

# 全局安装 vue-cli
npm install -g @vue/cli

# 查看 vue-cli 的版本,检查 vue-cli 是否安装成功
vue --version

Solve the problem that Windows PowerShell does not recognize the vue command:

By default, executing the vue --version command in PowerShell will prompt the following error message:

Untitled

The solution is as follows:

① Run PowerShell as an administrator

② Execute the set-ExecutionPolicy RemoteSigned command

③ Enter the character Y and press Enter

Untitled


create project

vue-cli provides two ways to create projects:

# 基于 [命令行] 的方式创建 vue 项目
vue create 项目名称

# OR

# 基于 [可视化面板] 创建 vue 项目
vue ui

Create a vue project based on vue ui

Step 1: Run the vue ui command under the terminal to automatically open the visual panel of the created project in the browser:

Untitled

Step 2: Fill in the project name on the details page

Untitled

Step 3: Select manual configuration items on the preset page:

Untitled

Step 4: Check the functions to be installed on the function page (Choose Vue Version, Babel, CSS preprocessor, use configuration files):

Untitled

Note: The last item is to store the configuration information of the plug-in separately in a configuration file

Step 5: Check the version of vue and the required preprocessor on the configuration page:

Untitled

Step 6: Save all the configurations just now as presets (templates), so that you can directly reuse the previous configurations when creating a project next time:

Untitled

Step 7: Create a project and automatically install dependencies:

Untitled

The essence of vue ui: After collecting the user's configuration information through the visual panel, the project is automatically initialized in the background based on the command line:

Untitled

Note: the preset is in the .vuerc configuration file

After the project is created, it will automatically enter the project dashboard:

Untitled



Create a vue project based on the command line

Step 1: Run the vue create demo2 command in the terminal to create a Vue project based on the interactive command line:

Untitled

Step 2: Select the features to install:

Untitled

Step 3: Use the up and down arrows to select the version of vue, and use the enter key to confirm the selection:

Untitled

Step 4: Use the up and down arrows to select the css preprocessor to use and confirm the selection with the enter key:

Untitled

Step 5: Use the up and down arrows to select how to store the plugin's configuration information, and use the Enter key to confirm the selection

Untitled

Step 6: Whether to save the configuration just now as a preset:

Untitled

Step 7: Choose how to install the dependent packages in the project

Untitled

Step 8: Start creating a project and automatically install dependencies

Untitled

Step 9: The project is created:

Untitled


To summarize the installation process :

Process :

  1. Global installation (you only need to install it once) yarn global add @vue/cli or npm i @vue/cli -g
  2. View vue/cli version: vue --version
  3. Create a project shelf: vue create project-name (the project name cannot use Chinese)
  4. Start the project: yarn serve or npm run serve (the command is not fixed, find package.json)

Modify according to the red box in the figure below:

Untitled

understand :

After the first two steps are done, you only need two steps 3 and 4 to create a Vue project in the future



Project directory introduction and operation process

Project directory introduction :

Untitled

Although there are many files in the scaffolding, at present we only need to know three files

main.js entry file

App.vue App root component

index.html template file

Operation process and function :

main.js function: import App.vue, and create structure rendering index.html based on App.vue

App.vue: Determines the rendering content of the page

Untitled

Detailed explanation of the main.js file :

// 1. 导入 Vue 核心包
import Vue from 'vue'
// 2. 导入 App.vue 根组件
import App from './App.vue'

// 提示:当前处于什么环境(生产环境 / 开发环境)
Vue.config.productionTip = false

// 3. Vue实例化,提供render方法 -> 基于App.vue创建结构并渲染index.html
new Vue({
  render: h => h(App),
}).$mount('#app')

Supplement :

Prompt environment information:

Untitled

Understanding of Vue instantiation: the effects of the following two methods are consistent

new Vue({
  render: h => h(App),
}).$mount('#app')
new Vue({
	el: 'app'
  render: h => h(App),
})

The complete way to write render:

render: h => h(App),
render: (creatElement) => {
  return creatElement(App)
}


Component development ideas

Component concept:

Concept : A page can be split into components, each component has its own independent structure, style, and behavior.

Benefits : Easy maintenance and reuse → improve development efficiency.

Component classification : common components, root components.

For example: for the following page, if all the codes are written in one page, the code will appear confusing and difficult to maintain.

We can divide components by module:

Untitled

Case website : http://www.ibootstrap.cn/

Untitled

Understanding: For example, if we package the carousel image into a component, which project needs to be used, we will import it into that project.

Component development in vue

Vue is a framework that fully supports componentized development. The suffix of components stipulated in vue is .vue. The App.vue file I came across before is essentially a vue component.


Root component concept:

Concept : the top-level component of the entire application, wrapping all common widgets

Untitled

Syntax highlighting plugin: Vetur

Component composition :

template: structure (with and only one root element)

script: js logic

style: style (can support less, needs to be packaged)

Among them, each component must contain template template structure, while script behavior and style style are optional components.

Let the component support less:

(1) style tag, lang="less" to enable the less function

(2) Package: yarn add less less-loader -D or npm i less less-loader -D

Untitled




Composition of components:

The template node of the component

Vue stipulates that the template structure corresponding to each component needs to be defined in the node.

<template>
	<!-- 当前组件的DOM结构,需要定义到template标签的内部>
</template>

Note: It is the container tag provided by vue, which only plays the role of wrapping, and it will not be rendered as a real DOM element


Using directives in templates

In the component's node, support the use of the previously learned instruction syntax to assist developers in rendering the DOM structure of the current component.

Define the root node in the template

In the version of vue 2.x, the DOM structure in the node only supports a single root node:

The code example is as follows:

<template>
  <div>
    <h1>App根组件</h1>
    <p>{
   
   { num }}</p>
  </div>
</template>

Understanding: As shown in the figure, a div needs to be wrapped outside h1 and h2, otherwise h1 and h2 are considered two root nodes

However, in the vue 3.x version, multiple root nodes are supported:

<template>
    <h1>App根组件</h1>
    <h2>这是副标题</h2>
</template>

Understanding: There is no problem even if there is no unified root node wrapped outside h1 h2 (such as wrapping them both with div)


Summarize:

Summary: template node

  1. The template supports the use of vue instructions to render the DOM structure
  2. The template will not be rendered into a real DOM structure, it only acts as a wrapper
  3. Inside the template, vue3 supports multiple root nodes, and vue2 only supports a single root node


Component's script node

Vue stipulates: within the component


---

### **script 中的 name 节点**

可以通过 name 节点为当前组件定义一个名称:

```jsx
<script>
export default {
	// name 属性指向带式当前组件的名称(建议: 每个单词的首字母大写)
  name: 'App',
}
</script>

When using vue-devtools for project debugging, the custom component name can clearly distinguish each component:

Untitled


data node in script

The data needed during rendering of vue components can be defined in the data node:

<script>
export default {
  name: 'App',
	// 组件的数据(data方法中return出去的对象,就是当前组件渲染期间需要用到的数据对象)
  data() {
    return {
      username: 'test'
    }
  }
}
</script>

The data in the component must be a function

Vue stipulates that the data in the component must be a function and cannot directly point to a data object. Therefore, when defining the data data node in the component, the following way is wrong:

data: { //组件中,不能直接让 data 指向一个数据对象(会报错)
		count: 0
}

methods node in script

The event processing function in the component must be defined in the methods node, the sample code is as follows:

export default {
  name: 'App',
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    addCount() {
      this.count++
    }
  }
}

methods point to an object. In the object pointed to by methods, we can declare the corresponding event handler. In the event handler, this points to the instance of the current component. It is worth noting that the data in the current component (such as data) can be accessed through this, as shown in the figure, we can use this.count++ to increase the count in the data in the instance by one.



The style node of the component

Vue stipulates: within the component


其中 <style> 标签上的 lang="css" 属性是可选的,它表示所使用的样式语言。默认只支持普通的 css 语法,可选值还有 less、scss 等。

---

### 让 style 中支持 less 语法

如果希望使用 less 语法编写组件的 style 样式,可以按照如下两个步骤进行配置:

① 运行 npm install less -D 命令安装依赖包,从而提供 less 语法的编译支持

② 在 <style> 标签上添加 lang="less" 属性,即可使用 less 语法编写组件的样式

```jsx
<style lang="less">
h1 {
	font-weight: normal;
	i {
		color: red;
		font-style: normal;
	}
}
</style>

Personal summary:

The composition structure of vue components :

Each .vue component consists of 3 parts, namely:

template -> component template structure script -> component JavaScript behavior style -> component style

Among them, each component must contain template template structure, while script behavior and style style are optional components.

The template node of the component :

Vue stipulates that the template structure corresponding to each component needs to be defined in the node.

<template>
	<!-- 当前组件的DOM结构,需要定义到template标签的内部>
</template>
  1. The template supports the use of vue instructions to render the DOM structure
  2. The template will not be rendered into a real DOM structure, it only acts as a wrapper
  3. Inside the template, vue3 supports multiple root nodes, and vue2 only supports a single root node

Component's script node :

within the component

<script>
	export default {
		// 组件的名称
		name: 'MyApp',
		// 组件的数据
		data() {
			return {
				username: 'Jack'
			}
		},
		//组件的方法
		methods: {
			addCount() {
				this.count++
			}
		}
	}
</script>

export default:

Component-related data, methods, etc. need to be defined in the object exported by export default.

data() :

The object returned in the data method is the data object that needs to be used during the rendering of the current component

The style node of the component :

within the component

<style lang="css">
	h1 {
		font-weight: normal;
	}
</style>



Basic use of components

Component Registration

Concept : mutual references can be made between components, for example: the reference principle of components in vue: register first and then use.

Untitled

The reference principle of components in vue: register first and then use.

How to use : Just use it as an html tag <component name></component name>

Naming convention : big camel case, such as HmHeader


Component registration naming method:

When registering a component, there are two ways to define the component registration name:

① Use kebab-case nomenclature (commonly known as dash nomenclature, such as my-swiper and my-search)

② Use PascalCase nomenclature (commonly known as Pascal nomenclature or big hump nomenclature, such as MySwiper and MySearch)

Features of dash nomenclature:

Must be used strictly according to the dash name

Features of Pascal's nomenclature:

It can be used strictly according to the name of Pascal, and it can also be converted into a dash name for use

Note: In actual development, it is recommended to use Pascal nomenclature to register names for components, because it is more applicable.

case:

//kebab-case
app.component('my-swiper', Swiper)
//PascalCase
app.component('MyTest', Test)

Replenish:

Write components in the template and use tabs to quickly complete

<HmHeader></HmHeader>

Untitled


There are two ways to register components:

There are two ways to register components in vue: "global registration" and "local registration", among which:

Globally registered components can be used in any global component

Partially registered components can only be used within the scope of the current registration

Untitled

As shown in the figure, the globally registered component Swiper can be used in both the Home component and the About component.

The group Search component registered locally in the Home component can only be used in the Home component.


Globally registered components

Global registration component method :app.component(’被注册的组件名称’, 组件)

Operate in main.js :

Use app.component to register

Untitled

Alternatively, use Vue.component to register

Untitled


Use global registration components

The global components registered with the app.component() method can be used directly in the form of labels, for example:

Untitled

Configure main.js and App.vue as explained above

Untitled


Partially registered components

Operating procedures:

Import the component search in the component App, and then register the component search in components

Untitled

Specific operation:

Untitled


The difference between global registration and local registration

Globally registered components can be used in any global component

Partially registered components can only be used within the scope of the current registration

Application scenario:

If some components are frequently used during development, global registration is recommended;

If some components will only be used in specific situations, local registration is recommended.


Register components via the name attribute

During component registration, in addition to directly providing the registered name of the component, the name attribute of the component can also be used as the name of the registered component. The sample code is as follows:

Untitled

Precautions:

We can omit the component name if the name of the registered component is consistent with the component to be imported:

As shown below, if we need to register the MyList component, we can fill in the components:

components: {
	'MyList': MyList
}

Since the name of the component is consistent with the name of the imported component, it can be simplified to the following form:

components: {
	MyList
}

case:

Untitled



Personal summary:

Component registration : register first, then use

Registration methods : global registration and local registration

Component registration name: Pascal nomenclature is recommended

//PascalCase
app.component('MyTest', Test)

Globally registered components : Globally registered components can be used in any global component

Untitled

Partially registered components : Partially registered components can only be used within the scope of the current registration

Untitled

To resolve style conflicts :

By default, styles written in .vue components will take effect globally, so it is easy to cause style conflicts between multiple components

Assign a unique custom attribute to each component. When writing a component style, control the scope of the style through the attribute selector:

Untitled

Prevent style conflicts between components:

<style scoped> </style>

Component props:

props is a custom attribute of the component, and the user passes the data to the subcomponent for use through props.

Role: the parent component passes data to the child component through props

Examples are as follows:

// 这是父组件 App.vue
<template>
  <div>
    <h1>这是 App.vue 根组件</h1>
    <hr>
    <!-- 通过自定义属性props,把文章标题和作者,传送到 my-article 组件中 -->
    <my-article :title="info.title" :author="info.author" :MyTest="info.MyTest"></my-article>
  </div>
</template>

<script>
import MyArticle from './Article.vue'
export default {
  name: 'MyApp',
  data() {
    return {
      info: {
        title: 'abc',
        author: '123',
        MyTest: 'test'
      }
    }
  },
  components: {
    MyArticle,
  }
}
</script>
// 这是子组件 Article.vue
<template>
  <div>
    <h3>标题:{
   
   {title}}</h3>
    <h5>作者:{
   
   {author}}</h5>
    <h6>发布时间:{
   
   {pubTime}}</h6>
  </div>
</template>

<script>
export default {
  name: 'MyArticle',
  // 外界可以传递指定的数据,到当前的组件中
  props: ['title', 'author', 'pubTime']
}
</script>

Class is bound to Style :

Vue allows developers to dynamically bind the value of the class attribute and the style style in the line to the element through the v-bind attribute binding instruction




Comprehensive Case-Xiaotuxian Homepage

Split Module - Partial Registration

Untitled

Here is a separate module: fresh and good things module

Untitled

We split the commodity items into modules and perform global registration and reuse:

Untitled

First get the code of the product module:

  • Code:

    <template>
      <div>
        <li class="base-goods-item">
          <a href="#">
            <div class="pic"><img src="@/assets/images/goods1.png" alt="" /></div>
            <div class="txt">
              <h4>KN95级莫兰迪色防护口罩</h4>
              <p>¥ <span>79</span></p>
            </div>
          </a>
        </li>
      </div>
    </template>
    
    <script>
    export default {
    
    }
    </script>
    
    <style>
    base-goods-item li {
      width: 304px;
      height: 404px;
      background-color: #EEF9F4;
    }
    base-goods-item li {
      display: block;
    }
    base-goods-item li .pic {
      width: 304px;
      height: 304px;
    }
    base-goods-item li .txt {
      text-align: center;
    }
    base-goods-item li h4 {
      margin-top: 17px;
      margin-bottom: 8px;
      font-size: 20px;
    }
    base-goods-item li p {
      font-size: 18px;
      color: #AA2113;
    }
    base-goods-item li p span {
      font-size: 22px;
    }
    </style>
    

Then perform global registration in main.js:

import BaseGoodsItem from './components/BaseGoodsItem.vue'
Vue.component('BaseGoodsItem', BaseGoodsItem)

Finally use it in XtxNewGoods.vue:

<ul>
  <BaseGoodsItem></BaseGoodsItem>
  <BaseGoodsItem></BaseGoodsItem>
  <BaseGoodsItem></BaseGoodsItem>
  <BaseGoodsItem></BaseGoodsItem>
</ul>

Supplement: generate components multiple times

<BaseGoodsItem v-for="item in 5" :key="item"></BaseGoodsItem>

Note: The item here is a number starting from 1

Guess you like

Origin blog.csdn.net/CaptainDrake/article/details/132099278