文章目录
计算属性和侦听器
计算属性
- 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
-
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
-
所以,对于任何复杂逻辑,你都应当使用计算属性。
-
基础例子
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
- 结果:
Original message: “Hello”
Computed reversed message: “olleH”
计算属性缓存 vs 方法
- 你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
- 我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
- 这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
<body>
<div id='app'>
<p>{{ reverseMessageMethod() }}</p>
<p>{{ reverseMessageMethod() }}</p>
<p>{{ reverseMessageMethod() }}</p>
<h1>{{ reverseMessageComputed }}</h1>
<h1>{{ reverseMessageComputed }}</h1>
<h1>{{ reverseMessageComputed }}</h1>
</div>
</body>
<script>
new Vue ({
el : '#app' ,
data : {
message : 'hello'
} ,
methods : {
reverseMessageMethod () {
console.log('reverseMessageMethod')
return this.message.split('').reverse().join('')
}
} ,
computed : {
reverseMessageComputed () {
console.log('reverseMessageComputed')
return this.message.split('').reverse().join('')
}
}
})
</script>
侦听器
- 虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
计算属性 vs 侦听属性
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:
<body>
<div id ='app'>
<input type="text" v-model="firstname"> +
<input type="text" v-model="lastname">
{{ fullname }}
</div>
</body>
<script>
new Vue ({
el : '#app' ,
data : {
firstname : '' ,
lastname : '' ,
fullname : '' ,
} ,
watch : {
firstname ( newval , oldval ) {
this.fullname = newval + this.lastname
} ,
lastname ( newval , oldval ) {
this.fullname = newval + this.firstname
}
}
})
</script>
<body>
<div id='app'>
<input type="text" v-model='firstname'> +
<input type="text" v-model='lastname'>
{{ fullname }}
</div>
</body>
<script>
new Vue ({
el : '#app' ,
data : {
firstname : '' ,
lastname : '' ,
} ,
computed : {
fullname () {
return this.firstname + this.lastname
}
}
})
</script>
深度监听
<script>
new Vue ({
el : '#app' ,
data : {
user : {
username : 'ZywOo' ,
age : 20 ,
}
} ,
watch : {
/* user : {
handler ( newVal , oldVal ) {
console.log(newVal , oldVal)
} ,
deep : true , // 深度监听
immediate : true // 该回调会在侦听开始之后立即调用
} */
'user.username' ( newVal , oldVal ) {
console.log(newVal , oldVal)
} ,
'user.age' ( newVal , oldVal ) {
console.log(newVal , oldVal)
}
}
})
</script>
vue的生命周期钩子函数
beforeCreate
- 类型:Function
- 详细:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created
- 类型:Function
- 详细:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前尚不可用。
beforeMount
- 类型:Function
- 详细:在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
mounted
-
类型:Function
-
详细:
-
实例被挂载后调用,这时 el 被新创建的
vm.$el
替换了。 如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el
也在文档内。 -
注意 mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用
vm.$nextTick:
mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) }
-
该钩子在服务器端渲染期间不被调用。
-
beforeUpdate
-
类型:Function
-
详细:
- 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
- 该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated
-
类型:Function
-
详细:
-
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
-
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
-
注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick:
updated: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been re-rendered }) }
-
该钩子在服务器端渲染期间不被调用。
-
beforeDestroy
-
类型:Function
-
详细:
-
实例销毁之前调用。在这一步,实例仍然完全可用。
-
该钩子在服务器端渲染期间不被调用。
-
destroyed
-
类型:Function
-
详细:
-
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
-
该钩子在服务器端渲染期间不被调用。
-
beforeCreate — 怀孕之前
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
一般不用created ---- 怀上了
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前尚不可用。
视情况而定,使用它可以请求数据beforeMount — 马上要生了 – 预产期
在挂载开始之前被调用:相关的 render 函数首次被调用。
一般不用mounted - 生下了
实例被挂载后调用,这时 el 被新创建的 vm. el也在文档内。
用的很多,可以进行数据请求,可以进行DOM操作,可以设置定时器,计时器等,类似于js中的 window.onloadbeforeUpdate ---- 变化前
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
一般不用updated ---- 变化后
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作
可以进行DOM操作,但是千万不要修改状态,状态改变,就会触发此钩子 – 无限循环beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。
移除事件绑定等操作destroyed
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
用的相对较少
数据请求
- 原生 : XMLHttpRequest / ActiveXObject
- jQuery : $.ajax() / $.get / $.post / $.getJson() / $.getScript() / $.load()
- vue : vue-resource / fetch / axios
vue-resouce
- 引入script
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"></script>
Get 请求
this.$http.get('/try/ajax/ajax_info.txt').then(function(res){
document.write(res.body);
},function(){
console.log('请求失败处理');
});
- 如果需要传递数据,可以使用 this.$http.get(‘get.php’,{params : jsonData}) 格式,第二个参数 jsonData 就是传到后端的数据。
POST请求
- post 发送数据到后端,需要第三个参数 {emulateJSON:true}。
- emulateJSON 的作用: 如果Web服务器无法处理编码为 application/json 的请求,你可以启用 emulateJSON 选项。
this.$http.post('/try/ajax/demo_test_post.php',{name:"菜鸟教程",url:"http://www.runoob.com"},{emulateJSON:true}).then(function(res){
document.write(res.body);
},function(res){
console.log(res.status);
});
fetch
- 基于Promise而设计的原生js支持的一种数据请求的方式,不需要安装或者依赖任何的东西,直接就可以使用,好比XMLHttpRequest对象
- fetch 请求默认返回的是一个promise对象,需要先行转换为一个json对象,然后再去做业务逻辑
- fetch 默认使用的就是 get请求
GET请求
<body>
<div id='app'>
<ul>
<li v-for="item in bannerlist" :key="item.bannerid">
<img :src="item.img" alt="">
</li>
</ul>
</div>
</body>
<script>
new Vue ({
el : '#app' ,
data : {
bannerlist : []
} ,
mounted () {
this.fetchGetbanner()
} ,
methods : {
fetchGetbanner () {
fetch('http://localhost:3000/api/banner')
.then( res => res.json() )
.then( data =>{
this.bannerlist = data.data
})
}
}
})
</script>
POST请求
- json格式
'Content-type' : 'application/json'
methods : {
fetchPostlogin () {
fetch ('http://localhost:3000/api/users/login' , {
method : 'post' ,
headers : {
'Content-type' : 'application/json'
} ,
body : JSON.stringify({
tel : '18682461260' ,
password : '123456' ,
})
})
.then ( res => res.json())
.then ( data => {
console.log(data)
})
}
}
- 表单格式 'Content-type ’ : ‘application/x-www-form-urlencoded’
methods : {
fetchPostlogin () {
fetch ('http://localhost:3000/api/users/login' , {
method : 'post' ,
headers : {
'Content-type' : 'application/x-www-form-urlencoded'
} ,
body : 'tel=18682461260&password=123456'
})
.then ( res => res.json() )
.then ( data => {
console.log(data)
})
}
}
axios
安装方法
使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
或<script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
使用 npm:
$ npm install axios
使用 bower:
$ bower install axios
使用 yarn:
$ yarn add axios
GET
<body>
<div id='app'>
<ul>
<li v-for ="item of bannerlist" :key="item.bannerid">
<img :src="item.img" alt="">
</li>
</ul>
</div>
</body>
<script>
new Vue ({
el : '#app' ,
data : {
bannerlist : []
} ,
mounted () {
this.getBanner()
} ,
methods : {
getBanner () {
axios.get('http://localhost:3000/api/banner')
.then (res => {
this.bannerlist = res.data.data
})
.catch ( () =>{
})
}
}
})
</script>
POST
methods : {
userlogin () {
axios.post('http://localhost:3000/api/users/login' , {
tel : '18682461260' ,
password : '123456' ,
})
.then( res =>{
console.log( res)
})
.catch( ()=>{
})
}
}
配置使用
axios ({
method : 'get' ,
url : 'http://localhost:3000/api/banner'
})
.then( res => {
this.bannerlist = res.data.data
})
axios ({
method : 'post' ,
url : 'http://localhost:3000/api/users/login' ,
data : {
tel : '18682461260' ,
password : '123456' ,
}
})
.then ( res => {
console.log(res)
})
- Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
自定义axios
<script>
let instance = axios.create({
baseURL : 'http://localhost:3000/api'
})
// 假设如果很多接口都需要使用 token验证,可以把token信息放在请求头
instance.defaults.headers.token = '1231654984561646546565464654654646321654asdasdasd'
new Vue ({
el : '#app' ,
data : {
bannerlist : []
} ,
mounted () {
this.getBanner()
} ,
methods : {
getBanner () {
instance.get('/banner').then( res => {
this.bannerlist = res.data.data
})
}
}
})
</script>
拦截器
<script>
// 自定义axios
var instance = axios.create({
baseURL: 'http://localhost:3000/api'
})
// 假设如果很多接口都需要使用 token验证,可以把token信息放在请求头
instance.defaults.headers.token = '1231654984561646546565464654654646321654asdasdasd'
// 请求拦截器 - 所有的请求开始之前先到此处
instance.interceptors.request.use((config) => {
// 可以设置 加载的动画效果 的展示
// 在必要的路由设置一些额外的参数 ---- token信息携带放在此处
console.log('正在加载....')
return config
}, (error) => {
return Promise.reject(error)
})
// 相应拦截器 --- 所有请求的相应先到此处
instance.interceptors.response.use((response) => {
// 可以设置 加载的动画效果 的隐藏
console.log('加载完毕')
return response
}, (error) => {
return Promise.reject(error)
})
new Vue({
el: '#app',
data: {
bannerlist: []
},
mounted () {
// instance({
// method: 'get',
// url: '/banner'
// }).then(res => {
// this.bannerlist = res.data.data
// }).catch(() => {})
instance.get('/banner').then(res => {
this.bannerlist = res.data.data
})
}
})
</script>
- 拦截器的使用 - 并不是自定义之后才能设置,只要有axios就可以