Vue进阶

今天接着上篇博客内容继续讲一讲vue的计算属性、侦听属性、vue的生命周期函数、vue的数据请求和vue的组件

1. 计算属性

任何复杂的业务逻辑,我们都应当使用计算属性。
任何复杂的业务逻辑,我们都应当使用计算属性。
任何复杂的业务逻辑,我们都应当使用计算属性。

重要的事说三遍


在特定的条件下,计算属性要优于方法

计算属性具有依赖性,计算属性依赖 data中的初始值,只有当初始值改变的时候,计算属性才会再次计算

计算属性一般书写为一个函数,返回了一个值,这个值具有依赖性,只有依赖的那个值发生改变,他才会重新计算

原始数据: {{ msg }} <br /> 计算属性的值: {{ computedMsg }} - {{ computedMsg }} -{{ computedMsg }} <br /> 
方法实现: {{ reversemsgfn() }} - {{ reversemsgfn() }}- {{ reversemsgfn() }} 

computed: { // 计算属性 ,计算属性可以依据data中的初始数据转换而来,可以像data中的数据一样使用,一般为函数,返回一个值 
  computedMsg () { 
	console.log('computed') // 执行一次 
	  return this.msg.split('').reverse().join('') 
	} 
}, 
methods: { 
  reversemsgfn () { 
    console.log('methods') // 执行3次 return this.msg.split('').reverse().join('') 
	} 
}

2. 侦听属性(监听属性)


vue提供了检测数据变化的一个属性 watch

在特定条件下,计算属性要优于侦听属性

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

3. vue的生命周期函数

在这里插入图片描述
使用方法 — 4个before,4个ed,创造,装载,更新,销毁

beforeCreate () { console.log('在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。') },

created () { console.log('在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。') },

beforeMount () { console.log('在挂载开始之前被调用:相关的 render 函数首次被调用。') },

mounted () { console.log('el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。')},

beforeUpdate () { console.log('数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。') },

updated () { console.log('由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。') },

beforeDestroy () { console.log('实例销毁之前调用。在这一步,实例仍然完全可用。') },

destroyed () { console.log('Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。') }

vue的生命周期周期,就好像我们的人生历程,这样会更好理解点,如下(星号表示运用频率):

初始化阶段
beforeCreate(){} // 准备怀孕
created(){} // 怀上了 *******************************************
beforeMount(){} // 准备生
mounted(){} // 生下来了 *************************************************
运行时阶段
beforeUpdate(){} // 每天都在长大
updated(){} ************************
销毁阶段
beforeDestroy(){} // 回光返照
destroyed(){} // 撒手人寰 ************

综上所述,会在 created 或者 mounted 中请求数据
如果必须使用dom操作,那么可以在 mounted 和 updated 中

4. vue中数据的请求


ajax — 数据请求 — 前后端分离(前后端耦合、不分离开发— 传统的系统架构)— 跨域
js 原生请求:
XMLHttpRequest / ActiveXObject
使用xhr发送一个json请求:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("options, error");
};

xhr.send();

jQuery请求:
jQuery $.ajax() / $.get() / $.post() / $.load() / $.getScript()

vue请求:
vue/react fetch / axios — 基于es6的promise

4.1 fetch


fetch API 是基于 Promise 设计,旧浏览器不支持 Promise,需要使用 polyfill es6-promise

fetch 不需要额外的安装什么东西,直接就可以使用

fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("options, error");
});

写成ES6的箭头函数:

fetch(url).then(response => response.json()) //将promise 对象转换成json对象,供我们使用
  .then(data => console.log(data)) //得到请求的数据,执行后续的业务逻辑
  .catch(e => console.log("options, error", e)) //请求失败打印出错误信息

Get & Post:

<script>
  new Vue({
    el: '#app',
    mounted () {
      /**
      * fetch 可以直接使用,不需要引入任何东西
      * fetch()  --- 返回的是 promise 对象
      *  .then(res => res.json()) 将promise 对象转换成json对象,供我们使用
      *  .then(data => {}) 得到请求的数据,执行后续的业务逻辑
      */
      fetch('https://www.daxunxun.com/douban?start=40', {
        method:'get',
        // body: JSON.stringify({
        //   start: 40
        // })
      }).then(res => res.json()).then(data => {
        console.log(data)
      })

      fetch('https://www.daxunxun.com/users/login', {
        method:'post',
        // body: JSON.stringify({
        //   username: '1322574****',
        //   password: '123456'
        // })
        body: 'username=1322574****&password=123456'
      }).then(res => res.json()).then(data => {
        console.log(data)
      })
    }
  })
</script>

4.2 axios

1.必须得安装之后才能使用
$ npm install axios

2.导入,在哪使用就在哪daoru
import axios from ‘axios’

3.请求数据

<script>
  new Vue({
    el: '#app',
    mounted () {
      axios.get('https://www.daxunxun.com/banner').then(res => {
        console.log(res.data)
      })

      axios.post('https://www.daxunxun.com/users/login', {
        username:'1322574****&',
        password: '1234567'
      }).then(res => {
        console.log(res.data)
      })
    }
  })
</script>

5. 组件


组件其实也是一个Vue的实例 ---- 组件是可复用的 Vue 实例

1、注册一个组件
2、使用组件 ---- 就像HTML标签一样使用即可

5.1 注册组件


5.1.1 全局注册组件

new Vue之前写的组件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>组件初体验</title>
</head>
<body>
  <div id="app">
    <!-- 3、使用组件 -->
    <my-header></my-header>
  </div>
</body>
<script src="vue.js"></script>
<script>
  // 全局注册组件
  // 1、定义组件
  const MyHeader = {
    template: `<header>头部</header>`, 
  }

  // 2、全局注册组件
  Vue.component('my-header', MyHeader)

  new Vue({
    el: '#app'
  })
</script>
</html>

如果页面结构复杂,template选项不好维护

抽离template选项的内容,template标签应该放到和 body以及script标签同级

<template id="header"> 
  <header>头部</header> 
</template>
<script>
  // 全局注册组件
  // 1、定义组件
  const MyHeader = {
    template: `<header>头部</header>`, 
  }

  // 2、全局注册组件
 Vue.component('my-header', { 
   template: '#header'
  })

  new Vue({
    el: '#app'
  })
</script>

5.1.2 局部注册组件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>组件初体验</title>
</head>
<body>
  <div id="app">
    <!-- 3、使用组件 -->
    <my-header></my-header>
  </div>
</body>
<template id="header">
  <header>
    这里是头部
  </header>
</template>
<script src="vue.js"></script>
<script>
  // 局部注册组件

  // 1、定义组件
  const MyHeader = { 
    template: '#header'
  }

  new Vue({
    el: '#app',
    // 2、局部注册组件
    components: {
      'my-header': MyHeader
    }
  })
</script>
</html>

5.2 组件之间传值


5.2.1 父组件给子组件传值

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>父子组件</title>
</head>
<body>
  <div id="app">
    <!-- <my-content test = "测试" :count="100" :flag="true" :tip="tip"/> -->
    <my-content :count="100" :flag="true" :tip="tip"/>
  </div>
</body>
<template id="content">
  <div>
    我这里是内容区域 --- {{ test }} -- {{ count }} --- {{ flag }} --- {{ tip }}
  </div>
</template>
<script src="vue.js"></script>
<script>
  // 组件的名称不能和模板的id同名
  /**
   * 父组件在调用子组件的地方  <my-content />
   * 添加一个自定义的属性      <my-content test = "测试"/>
   * 属性的值就是你要传递给子组件的值             "测试"
   *    如果属性的值是number类型,boolean类型,或者是变量,需要使用到绑定属性
   *    <my-content test = "测试" :count="100" :flag="true" :tip="tip"/>
   * 在子组件定义的地方       const Content = {}
   * 给他添加一个选项 props   const Content = { props: }
   * props的值可以为数组以及对象
   * 如果是数组, 元素则为父组件中自定义的属性名     
   *    ['test', 'count', 'flag', 'tip']
   * 在子组件中就可以通过 你自定义的属性名 得到父组件传递过来的数据
   *      如果是对象,对象的形式有两种写法
   *        写法一:验证传递数据的有效性  ----  可能父子组件不是一个人写的,如果数据类型不对,会报出警告,可以及时调整
   *          props: {
                test: String,
                count: Number,
                flag: Boolean,
                tip: String
              }
            写法二:既要验证数据的类型,又要设定属性的默认值,如果默认值是对象或者是数组,值为一个函数
              props: {
                test: {
                  type: String,
                  default: '测试数据了' 
                }
              }
              eg: 
              props: {
                list: {
                  type: Object,
                  default: function () {
                    return {}
                  }
                }
              }
   * */
  const Content = {
    template: '#content',
    // props: ['test', 'count', 'flag', 'tip']
    // props: {
    //   test: String,
    //   count: Number,
    //   flag: Boolean,
    //   tip: String
    // }
    props: {
      test: {
        type: String,
        default: '测试数据了' // 如果父组件有这个属性,那么就用父组件的值,没有这个属性,直接使用这个值
      }
    }
  }

  new Vue({
    el: '#app',
    data: {
      tip: '提示'
    },
    components: {
      'my-content': Content
    }
  })
</script>
</html>

5.2.2 子组件给父组件传值

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>父子组件</title>
</head>
<body>
  <div id="app">
    <my-content @myevent="getData"/>
  </div>
</body>
<template id="content">
  <div>
    我这里是内容区域
  </div>
</template>
<script src="vue.js"></script>
<script>
  /**
   * 父组件调用子组件的地方,给他绑定一个自定义的事件, 事件不要加()
   *      <my-content @myevent="getData"/>
   * 在父组件选项methods中实现此事件,默认参数为你将从子组件得到的值
   *    methods: {
          getData (val) { // val为从子组件中获取到的值
            console.log(val)
          }
        },
      
      在子组件中,可以是生命周期钩子函数,也可以是组件自己的事件 去 触发 父组件中的自定义事件
          this.$emit('myevent', 10000)
   **/
  const Content = {
    template: '#content',
    mounted () {
      this.$emit('myevent', 10000)
    }
  }

  new Vue({
    el: '#app',
    data: {},
    methods: {
      getData (val) { // val为从子组件中获取到的值
        console.log(val)
      }
    },
    components: {
      'my-content': Content
    }
  })
</script>
</html>

5.2.1 非父子(兄弟)组件之间传值

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>非父子组件传值</title>
</head>
<body>
  <div id="app">
    <my-list></my-list>
    <my-count></my-count>
  </div>
</body>
<template id="list">
  <ul>
    <li>111<button @click="add">+1</button></li>
    <li>222<button @click="add">+1</button></li>
    <li>333<button @click="add">+1</button></li>
    <li>444<button @click="add">+1</button></li>
    <li>555<button @click="add">+1</button></li>
  </ul>
</template>
<template id="count">
  <div>
    总量是:{{ num }}
  </div>
</template>
<script src="vue.js"></script>
<script>
  /**
   *  非父子组件传值 ---- 兄弟组件传值                   (邮差与信的故事)
   *  ------------------ 中央事件总线传值  1. const bus = new Vue()
   * 
   *  先要确保 收信 的那个人是存在的
   *  然后写信的人 写信 给邮差
   *  邮差给送
   * 
   *  // 此处一定要注意this指向,可以使用 => 
   *  2.在接收端通过 bus.$on('收信信号', function (val) {
   * 
   *  })
   * 
   *  3.在发送端通过 bus.$emit('收信信号', val)
   * */
const bus = new Vue()

const List = {
  template: '#list',
  methods: {
    add () {
      bus.$emit('count-event', 1)
    }
  }
}
const Count = {
  template: '#count',
  data () {
    return {
      num: 0
    }
  },
  mounted () { // 一般情况下接收都使用 生命周期钩子函数
    bus.$on('count-event', (val) => { // 此处要注意this指向,可以使用 箭头函数 =>
      console.log(val)
      this.num += val
    })
  }
}

new Vue({
  el: '#app',
  components: {
    'my-list': List,
    'my-count': Count
  }
})
</script>
</html>

5.3 动态组件


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>动态组件</title>
</head>
<body>
  <div id="app">
    <button @click="tem='my-a'">AAA</button>
    <button @click="tem='my-b'">BBB</button>
    <button @click="tem='my-c'">CCC</button>
    <keep-alive include="a,b">
      <component :is="tem"></component>
    </keep-alive>
    
  </div>
</body>
<template id="acom">
  <div>
    <input type="text" placeholder="a组件">
  </div>
</template>
<template id="bcom">
  <div>
    <input type="text" placeholder="b组件">
  </div>
</template>
<template id="ccom">
  <div>
    <input type="text" placeholder="c组件">
  </div>
</template>
<script src="vue.js"></script>
<script>
  /**
   * keep-alive 
   *  保留组件的状态,避免组件的重新渲染  ---  类似于手机软件使用中按了home键之后应用程序的状态
   *  添加include 可以 只给部分组件保留状态,需要定义组件时添加name属性
   * 
   * 每个组件都会有 两个生命周期钩子函数
   *    activated()     正在用的
   *    deactivated()   后台的
   * */
const Acom = {
  name: 'a',
  template: '#acom',
  activated () {
    console.log(' a 正在被使用')
  },
  deactivated () {
    console.log(' a 被雪藏了')
  }
}
const Bcom = {
  name: 'b',
  template: '#bcom',
  activated () {
    console.log(' b 正在被使用')
  },
  deactivated () {
    console.log(' b 被雪藏了')
  }
}
const Ccom = {
  template: '#ccom',
  activated () {
    console.log(' c 正在被使用')
  },
  deactivated () {
    console.log(' c 被雪藏了')
  }
}

new Vue({
  el: '#app',
  data: {
    tem: 'my-a'
  },
  components: {
    'my-a': Acom,
    'my-b': Bcom,
    'my-c': Ccom
  }
})
</script>
</html>

如果每一个组件内部含有 表单元素 ,默认在动态组件切换的时候,表单数据会有所丢失

可以使用 包裹动态组件,避免组件的重新渲染(手机按home键并不是退出了系统的应用程序)

如果书写形式为 ,那么表示只有 a 和 b 的组件的装填会被保留,而其余的组件都是先卸载,需要的时候再装载, a 和 b为定义组件的时候的name选项的值

组件的钩子函数有两大对比

销毁 创建 ---- 动态组件 — 不会触发 activated 和 deactivated
显示 隐藏 ------ 动态组件 + keep-alive — 所有的组件都只创建了一遍

生命周期钩子函数
beforeCreate
created
beforeUpdate
updated
beforeMount
mounted
beforeDestroy
destroyed
activated ----- 跟keep-alive配合使用 — 组件激活时调用
deactivated ----- 跟keep-alive配合使用 — 组件停用时调用

今天的分享就到这里,vue项目构建内容请听下回分解,内容如有错误,欢迎评论,指教

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值