今天接着上篇博客内容继续讲一讲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项目构建内容请听下回分解,内容如有错误,欢迎评论,指教