Vue2(二):生命周期、案例、工程化开发入门、组件(scoped,data函数)、组件通信(props)、v-model详解、.sync修饰符、ref和$refs、Vue异步更新和nextTick

目录

1、生命周期

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

created应用 - 初始化渲染

钩子函数应用 - 获取焦点

2、案例:小黑记账清单

3、工程化开发入门

脚手架目录文件介绍&项目运行流程

组件化开发&根组件

App.vue 文件的组成部分

普通组件的注册使用

局部注册

全局注册

4、组件的三大组成部分

组件的样式冲突scoped

data函数

5、组件通信

父子组件通信

父传子

子传父

props详解

props校验

props & data、单向数据流

非父子通信 - event bus 事件总线

非父子通信 - provide & inject

6、v-model 详解

v-model原理

表单类组件封装

v-model简化代码

7、.sync修饰符

8、ref 和 $refs

9、Vue异步更新、$nextTick


1、生命周期

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

        初始化渲染请求越早越好(before creat阶段响应式数据还没有准备好),要在响应式数据准备好才能进行,因此在created中执行。

  <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. 销毁阶段 app.$destroy() 并不会把之前渲染的页面情况,会把所有的监听事件等等销毁
      beforeDestroy () {
        console.log('beforeDestroy, 卸载前')
        console.log('清除掉一些Vue以外的资源占用,定时器,延时器...')
      },
      destroyed () {
        console.log('destroyed,卸载后')
      }
    })
  </script>
created应用 - 初始化渲染

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


  <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>
钩子函数应用 - 获取焦点

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

<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>

2、案例:小黑记账清单

  <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/echarts@5.4.0/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>

3、工程化开发入门

按 win+x 然后,点击(Windows)终端管理员 ,用yarn global add @vue/cil 或 npm i @vue/cli -g 全局安装

脚手架目录文件介绍&项目运行流程

public下面的index.html 如下:

<body>
  <!-- 兼容:给不支持js的浏览器一个提示 -->
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
        Please enable it to continue.</strong>
  </noscript>
  <!-- Vue所管理的容器:将来创建结构动态渲染这个容器 -->
  <div id="app">
    <!-- 工程化开发模式中:这里不再直接编写模板语法,通过 Vue.app 提供结构渲染 -->
  </div>
  <!-- built files will be auto injected -->
</body>

main.js 文件:

文件核心作用:导入App.vue,基于App.vue创建结构渲染index.html

import App from './App.vue':导入App.vue 根组件

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

.$mount('#app') 与el: '#app' 作用相同

组件化开发&根组件

组件化:一个页面拆分成一个个组件,每个组件都有独立的结构、样式、行为。便于维护,复用

分类:普通组件、根组件(整个应用最上层的组件,包裹所有普通小组件)

App.vue 文件的组成部分

template:结构(有且只能一个根元素,Vue2是这样的,Vue3已经没有这个特性了)

script:js逻辑

style:样式(可支持less,需要装包)

script 中 export default {} 导出的是当前组件的配置项,可以提供data(特殊) methods compute watch 生命周期

普通组件的注册使用
局部注册

组件名使用大驼峰命名

全局注册

编写导入的代码,往代码的顶部编写(规范)

在main.js中导入

import Vue from 'vue'
import App from './App.vue'
//编写导入的代码,往代码的顶部编写(规范)
import Button from './components/Button.vue'

Vue.config.productionTip = false

// 进行全局注册   Vue.component(组件名,组件对象)
Vue.component('Button', Button)
new Vue({
  render: h => h(App),
}).$mount('#app')

在使用组件的地方调用

4、组件的三大组成部分

组件的样式冲突scoped

scoped原理:

1、给当前组件模板的所有元素都会被添加上一个自定义属性 data-v-hash 值

2、css选择器后面被自动处理,添加上了属性选择器  eg: div[ data-v-hash值 ]

data函数

5、组件通信

--- 组件与组件之间的数据传递

组件关系:父子关系、非父子关系

组件通信解决方案:父子关系 --- props 和 $emit ;非父子关系 --- provide & inject / eventbus

通用解决方案:Vuex(适合复杂业务场景)

父子组件通信

父传子

props 实际上就是给组件标签身上加上自定义属性

子传父

子组件不能直接修改数据,要通过$emit 向父组件发送消息通知,父组件对消息进行监听,提供处理函数,提供逻辑

props详解

作用:向子组件传递数据

特点:可以传递任意数量、任意类型的props

props传的是属性名

props校验

语法:类型校验、非空校验、默认值、自定义校验

类型校验语法:

完整语法:

props & data、单向数据流

自己的数据在data中,可以改;props中的数据是父组件的,不能改。

单向数据流:父组件的prop更新,会单向向下流动,影响到子组件。

渲染数据注意:提供数据是提供在公共的父组件 App.vue中;然后通过父传子,使用 v-for 渲染。

非父子通信 - event bus 事件总线

作用:非父子组件之间,进行简易消息传递(复杂场景使用 Vuex)

非父子通信 - provide & inject

作用:跨层级共享数据

6、v-model 详解

v-model原理

        原理:一种语法的简写,eg:应用在输入框上,就是value属性和input事件的合写;如果应用在复选框上,就是checked属性和change事件的合写。

作用:提供数据的双向绑定。数据变,视图跟着变 :value;视图变,数据跟着变@input

表单类组件封装

        数据在父组件中(以便后续表单提交),v-model不可以直接和父组件的数据绑定(因为v-odel是双向绑定,是可以修改的,但是子组件不能直接修改父组件的数据),将v-model拆解绑定数据,监听数据的变化,子传父,由父组件修改数据。

v-model简化代码

实现子组件和父组件数据双向绑定。

7、.sync修饰符

作用:可以实现子组件和父组件数据的双向绑定,简化代码

特点:prop属性名,可以自定义,非固定为value

8、ref 和 $refs

作用:利用ref和$refs可以用于获取dom元素或组件实例

特点:查找范围 -> 当前组件内(更精确稳定)

9、Vue异步更新、$nextTick

点击编辑后,输入框不能聚焦,因为Vue是异步更新DOM(提升性能)

$nextTick:等DOM更新后,立刻会触发执行此方法里的函数体。

使用setTimeout也可以,但是不精准。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值