计算属性和监听器
一般情况下属性都是放到data中的,但是有些属性可能是需要经过一些逻辑计算后才能得出来,那么我们可以把这类属性变成计算属性。
<div id="app">
<label for="length">长:</label>
<input type="number" name="length" v-model:value="length">
<label for="width">宽:</label>
<input type="number" name="width" v-model:value="width">
<label for="area">面积:</label>
<input type="number" name="area" v-bind:value="area" readonly>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
length: 0,
width: 0,
},
computed: {
area: function(){
return this.length*this.width;
}
}
});
</script>
可能有的小伙伴会觉得这个计算属性跟我们之前学过的函数好像有点重复。实际上,计算属性更加智能,他是基于它们的响应式依赖进行缓存的。也就是说只要相关依赖(比如以上例子中的area)没有发生改变,那么这个计算属性的函数不会重新执行,而是直接返回之前的值。这个缓存功能让计算属性访问更加高效。
计算属性的set
计算属性默认只有get,不过在需要时你也可以提供一个set,但是提供了set就必须要提供get方法。
<div id="app">
<div>
<label>省:</label>
<input type="text" name="province" v-model:value="province">
</div>
<div>
<label>市:</label>
<input type="text" name="city" v-model:value="city">
</div>
<div>
<label>区:</label>
<input type="text" name="district" v-model:value="district">
</div>
<div>
<label>详细地址:</label>
<input type="text" name="address" v-model:value="address">
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
district: "",
city: "",
province: ""
},
computed: {
address: {
get: function(){
let result = "";
if(this.province){
result = this.province + "省";
}
if(this.city){
result += this.city + "市";
}
if(this.district){
result += this.district + "区";
}
return result;
},
set: function(newValue){
let result = newValue.split(/省|市|区/)
if(result && result.length > 0){
this.province = result[0];
}
if (result && result.length > 1){
this.city = result[1];
}
if(result && result.length > 2){
this.district = result[2];
}
}
}
}
});
</script>
监听属性
监听属性可以针对某个属性进行监听,只要这个属性的值发生改变了,那么就会执行相应的函数。
<div id="app">
<div>
<label>搜索:</label>
<input type="text" name="keyword" v-model:value="keyword">
</div>
<div>
<p>结果:</p>
<p>{{answer}}</p>
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
keyword: "",
answer: ""
},
watch: {
keyword: function(newKeyword,oldKeyword){
this.answer = '加载中...';
let that = this;
setTimeout(function(){
that.answer = that.keyword;
},Math.random()*5*1000);
}
}
});
</script>
表单输入绑定
v-model指定可以实现表单值与属性的双向绑定。即表单元素中更改了值会自动的更新属性中的值,属性中的值更新了会自动更新表单中的值。
绑定的属性和事件:
v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text和textarea元素使用value属性和input事件。
- checkbox和radio使用checked属性和change事件。
- select字段将value作为prop并将change作为事件。
表单元素绑定
input绑定
v-model是v-model:value的缩写,改变 input标签中的值 可以改变下面的属性
<input v-model="message" placeholder="请输入...">
<input v-model:value="message" placeholder="请输入...">
<p>输入的内容是:{{ message }}</p>
new Vue({
el: '#example-3',
data: {
message: ""
}
})
textarea绑定
<span>输入的内容是:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="请输入多行内容..."></textarea>
checkbox绑定
<div id='example-3'>
<input type="checkbox" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
})
radio绑定
<div id="example-4">
<input type="radio" value="男" v-model="gender">
<label>男</label>
<br>
<input type="radio" value="女" v-model="gender">
<label>女</label>
<br>
<span>Picked: {{ gender }}</span>
</div>
new Vue({
el: '#example-4',
data: {
gender: ''
}
})
select绑定
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
# 如果有value值 选择的就是value值
<option value="1">A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '...',
data: {
selected: ''
}
})
修饰符
.lazy
在默认情况下,v-model在每次input事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加lazy修饰符,从而转变为使用change事件进行同步:
<!-- 在"change"时而非"input"时更新 光标移除input输入框的时候 -->
<input type="text" v-model:value.lazy="message">
<input v-model.lazy="message" >
new Vue({
el: '#app',
data: {
message: ''
}
})
.number
如果想自动将用户的输入值转为数值类型,可以给v-model添加number修饰符
<input v-model.number="age" type="number">
这通常很有用,因为即使在type="number"时,HTML输入元素的值也总会返回字符串。如果这个值无法被parseFloat()解析,则会返回原始的值。
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符
<input v-model.trim="msg">
自定义组件
有时候有一组html结构的代码,并且这个上面可能还绑定了事件。然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的。那么这时候我们就可以把这些代码封装成一个组件,以后在使用的时候就跟使用普通的html元素一样,拿过来用就可以了。
基本使用
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
Vue.component('button-counter', {
template: '<button v-on:click="count+=1">点击了{{ count }}次</button>'
data: function(){
return {
count: 0
}
},
});
let vm = new Vue({
el: "#app",
data: {}
});
</script>
以上我们创建了一个叫做button-counter的组件,这个组件实现了能够记录点击了多少次按钮的功能。后期如果我们想要使用,就直接通过button-counter使用就可以了。然后因为组件是可复用的Vue实例,所以它们与new Vue接收相同的选项,例如data、computed、watch、methods以及生命周期钩子等。仅有的例外是像el这样根实例特有的选项。另外需要注意的是:组件中的data必须为一个函数!
给组件添加属性
像原始的html元素都有自己的一些属性,而我们自己创建的组件,也可以通过prop来添加自己的属性。这样别人在使用你创建的组件的时候就可以传递不同的参数了
<div id="app">
<blog-post v-for="blog in blogs" :title="blog.title"></blog-post>
<blog-post v-bind:blogs="blogs"></blog-post>
</div>
<script>
Vue.component('blog-post', {
props: ['blogs'],
template: `
<table>
<tr>
<th>序号</th>
<th>标题</th>
</tr>
<tr v-for="(blog,index) in blogs">
<td>{{index+1}}</td>
<td>{{blog.title}}</td>
</tr>
</table>
`
})
new Vue({
el: "#app",
data: {
blogs: [
{"title":"钢铁是怎样练成的?","id":1},
{"title":"AI会毁灭人类吗?","id":2},
{"title":"如何学好Vue!","id":3},
]
}
});
</script>
单一根元素
如果自定义的组件中,会出现很多html元素,那么根元素必须只能有一个,其余的元素必须包含在这个根元素中。比如以下是一个组件中的代码,会报错:
<h3>{{ title }}</h3>
<div v-html="content"></div>
我们应该改成:
<div class="blog-post">
<h3>{{ title }}</h3>
<div v-html="content"></div>
</div>
子组件事件和传递事件到父组件
子组件中添加事件跟之前的方式是一样的,然后如果发生某个事件后想要通知父组件,那么可以使用this.$emit函数来实现。
<div id="app">
<blog-item v-for="blog in blogs" v-bind:blog="blog" @check-changed="checks"></blog-item>
<div v-for="blog in componentblog">
{{blog.title}}
</div>
</div>
<script>
Vue.component('blog-item',{
props:['blog'],
template:`
<div>
<span>{{blog.title}}</span>
<input type="checkbox" @click="onCheck">
</div>
`,
methods:{
onCheck:function(){
// console.log(123)
this.$emit('check-changed',this.blog)
}
}
})
new Vue({
el: '#app',
data: {
blogs:[
{"title":"欢迎来到这里!","id":1},
{"title":"你今天吃了嘛?","id":2},
{"title":"如何学好Vue!","id":3},
],
componentblog:[]
},
methods:{
checks:function(blog){
// indexOf 判断某个元素在数组中的位置,返回下标
var index = this.componentblog.indexOf(blog)
if(index >= 0){
this.componentblog.splice(index,1)
}else{
this.componentblog.push(blog)
}
console.log(blog)
}
}
})
</script>
需要注意的是,因为html中大小写是不敏感的,所以在定义子组件传给父组件事件名称的时候,不要使用myEvent这种驼峰命名法,而是使用my-event这种规则。
自定义组件v-model
一个组件上的v-model默认会利用名为value的prop(属性)和名为input的事件,但是像单选框、复选框等类型的输入控件可能会将value特性用于不同的目的。这时候我们可以在定义组件的时候,通过设置model选项可以用来实现不同的处理方式
<div id="app">
<stepper v-model:value="goods_count"></stepper>
</div>
<script>
Vue.component('stepper',{
props:['count'],
model:{
event: 'count-changed',
prop: "count"
},
template:`
<div>
<button @click="sub">-</button>
<span>{{count}}</span>
<button @click="add">+</button>
</div>
`,
methods:{
sub:function(){
this.$emit("count-changed", this.count-1)
},
add:function(){
this.$emit("count-changed", this.count+1)
}
}
});
new Vue({
el: "#app",
data:{
"goods_count":0
}
})
</script>
其中的props定义的属性分别是给外面调用组件的时候使用的。model中定义的prop:'count’是告诉后面使用v-model的时候,要修改哪个属性;event:'count-changed’是告诉v-model,后面触发哪个事件的时候要修改属性。
插槽
我们定义完一个组件后,可能在使用的时候还需要往这个组件中插入新的元素或者文本。这时候就可以使用插槽来实现。
<div id="app">
<navigation-link url="/profile/">
个人中心
</navigation-link>
</div>
<script>
Vue.component('navigation-link', {
props: ['url'],
template: `
<a v-bind:href="url" class="nav-link">
<slot></slot>
</a>
`
})
new Vue({
el: "#app"
});
</script>
当组件渲染的时候,将会被替换为“个人中心”。插槽内可以包含任何模板代码,包括HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
个人中心
</navigation-link>
如果没有包含一个元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
生命周期函数
生命周期函数代表的是Vue实例,或者是Vue组件,在网页中各个生命阶段所执行的函数。生命周期函数可以分为创建阶段和运行期间以及销毁期间。
其中创建期间的函数有beforeCreate、created、beforeMount、mounted;
运行期间的函数有beforeUpdate、updated;
销毁期间有beforeDestroy、destroyed。
以下是官方文档给到的一张图,从这种图中我们可以了解到每个部分执行的函数。
创建期间:
- beforeCreate:
Vue或者组件刚刚实例化,data、methods都还没有被创建。 - created:
此时data和methods已经被创建,可以使用了。模板还没有被编译。 - beforeMount:
created的下一阶段。此时模板已经被编译了,但是并没有被挂在到网页中。 - mounted:
模板代码已经被加载到网页中了。此时创建期间所有事情都已经准备好了,网页开始运行了。
运行期间:
- beforeUpdate:
在网页网页运行期间,data中的数据可能会进行更新。在这个阶段,数据只是在data中更新了,但是并没有在模板中进行更新,因此网页中显示的还是之前的。 - updated:
数据在data中更新了,也在网页中更新了。
销毁期间: - beforeDestroy:
Vue实例或者是组件在被销毁之前执行的函数。在这一个函数中Vue或者组件中所有的属性都是可以使用的。 - destroyed:
Vue实例或者是组件被销毁后执行的。此时Vue实例上所有东西都会解绑,所有事件都会被移除,所有子元素都会被销毁。
过滤器
过滤器就是数据在真正渲染到页面中的时候,可以使用这个过滤器进行一些处理,把最终处理的结果渲染到网页中。
过滤器使用
过滤器可以用在两个地方:双花括号插值**和v-bind表达式 (后者从2.1.0+开始支持)。过滤器应该被添加在JavaScript表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message|capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId|formatId"></div>
过滤器定义
你可以在一个组件的选项中定义本地的过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者在创建 Vue 实例之前全局定义过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
网络应用
Vue结合网络数据开发应用,axios,网络请求库,使用的时候,需要导入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
get请求
axios.get(地址).then(function(response){},function(err){})
请求地址:https://autumnfish.cn/api/joke/list?num=3
post请求
axios.post(地址).then(function(response){},function(err){})
请求地址:https://autumnfish.cn/api/user/reg
axios+vue
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
<button @click='getJoke'>获取笑话</button>
<p>{{ joke }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
joke: ""
},
methods: {
getJoke: function(){
var that = this
// console.log(this.joke)
axios.get('https://autumnfish.cn/api/joke').then(
function(response){
// console.log(this.joke)
// console.log(response.data)
that.joke = response.data
},function(err){
}
)
}
}
})
</script>