延续上一章的学习继续学嗷~跟我一起敲代码!一起加油!
2.4 表单提交
表单是form,那我们先写一个简单的表单,里面有一个输入框还有一个提交按钮:
<div id="app">
<form>
<input type="text" />
<button>提交表单</button>
</form>
</div>
这里我们可以去输入然后点击一下按钮,会发现呢,每次点击提交按钮之后都会自动刷新页面。那么如何做到,点击提交的时候不让表单刷新呢:
首先我们先自己写一个提交表单的功能
methods:{
post(){
console.log('提交表单')//在控制台输出文字,按F12、右键检查 可以打开控制台
}
}
然后把它交给我们的表单 form,表单提交事件是submit,像我们之前点击事件是click一样:
<div id="app">
<form @submit="post">
<input type="text" />
<button>提交表单</button>
</form>
</div>
这样我们就把新的提交功能给到form了,但是呢表单默认的事件还是存在的,因此我们需要阻止它,用到prevent阻止默认行为。
<form @submit.prevent="post">
这样呢点击提交就不会再刷新页面了。
2.5 数据的双向绑定
上个点我们解决了表单提交的问题,那么如果我想要实现:点击提交之后,在控制台输出我们输入框中的数据。
那么就需要到新的知识点数据的双向绑定:
这里用到的指令是:v-model
想要一个可以变化的数据,我们得先声明一个变量:
data(){
return {
msg:'hello world'//为了能看到效果,给它设定了初始值"hello world"
}
},
接着把它绑定到输入框:
<input type="text" v-model="msg" />
好了保存去看一下页面,刷新,会发现这时候输入框内有了我们msg的值,并且是可以修改的,只要我们去更改输入框的值,msg也会跟着改变,如果不确定 可以把 msg 输出出来看一下,放到<h1>标签里看看:
<div id="app">
<h1>{{msg}}</h1>
<form @submit.prevent="post">
<input type="text" v-model="msg" />
<button>提交表单</button>
</form>
</div>
那我们已经实现了数据的双向绑定了,输入框里的值的改变,会改变 msg,那么我们提交表单就只需要使用msg的值就可以了。
methods:{
post(){
console.log(this.msg)
}
}
学到这里呢,其实v-model也有一些修饰符,就跟我们上面用到的@submit.prevent一样,可以提供一些额外的效果。
情况1:我们的msg是字符串,但如果我们最终想要的东西是 数字 number类型,可以这样写:v-model.number
情况2:这里我们在输入框里面打字,每打一个字msg就会改变一下对吧,但是如果我们只想要在输入框失去焦点的时候,才改变一次数据,比如我们点击按钮的时候才更新最新数据,这样就不用一直更新数据了。可以这样写:v-model.lazy。
情况3:比如我们做了一个表单,让用户输入用户名,但是他一不小心,在开头打了几个空格,或者是最后打了几个空格,他自己没意识到,如果这样被存到数据库的话,那他的用户名就会莫名地多了空格。所以有一个修饰符可以帮忙去除首尾的空格,:v-model.trim。跟JavaScript的去除字符串前后空格是一样的英文。
2.6 一个简单的购物车功能
先放一个效果图在这里对照:分别输入名称和单价后,点击提交,下方列表会增加一条数据,原本列表是空的。
,可以自己去敲一敲,实现一下,复习一下,这里提几个点给萌新们:
输入框前面的文字使用<label>标签,这样使用可以让它们绑定住:
<!-- 用for去绑定input的id,这样点击前面的文字会自动选中输入框 -->
<label for="inp1">商品名称 </label>
<input id="inp1" type="text" v-model.trim.lazy="name" />
换行使用</br>,就可以换行了。
其他的之前都学过,忘记了可以看看上一篇文章哝:juejin.cn/post/716183…
可能有的人会觉得不知道数量怎么来的,只要在传名称和单价的时候顺便传一个数量为1就可以了。这里存进数组里的就得是对象了。
post(){
//list是用来展示商品列表的
this.list.push({name:this.name,price:this.price,num:1})
},
完成了之后嘞看看代码:
<div id="app">
<h1>购物车</h1>
<form @submit.prevent="post">
<label for="inp1">商品名称 </label>
<input id="inp1" type="text" v-model.trim.lazy="name" />
</br>
<label for="inp2">商品单价 </label>
<input id="inp2" type="number" v-model.number.lazy="price" />
</br>
<button>提交表单</button>
</form>
<!-- 上面是表单的部分 -->
<ul>
<li v-for="item in list">
商品名称:{{item.name}} 单价:{{item.price}} 数量:{{item.num}}
</li>
</ul>
<!-- 上面是列表的部分 -->
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data(){
return {
name:'',
price:'',
list:[],
}
},
methods:{
post(){
this.list.push({name:this.name,price:this.price,num:1})
}
}
}).mount('#app')
</script>
这样呢,就完成了一个简易版,接着给他加上一个调节数量的功能,并能算出总价,先上效果图:
同样是上一章讲过的嘞。
最终代码是:
<div id="app">
<h1>购物车</h1>
<form @submit.prevent="post">
<label for="inp1">商品名称 </label>
<input id="inp1" type="text" v-model.trim.lazy="name" />
</br>
<label for="inp2">商品单价 </label>
<input id="inp2" type="number" v-model.number.lazy="price" />
</br>
<button>提交表单</button>
</form>
<!-- 上面是表单的部分 -->
<ul>
<li v-for="item,index in list">
商品名称:{{item.name}} 单价:{{item.price}} 数量:<button @click="minus(item,index)">-</button> {{item.num}} <button @click="add(item)">+</button>
</li>
</ul>
<!-- 上面是列表的部分 -->
<h2>总价{{allPrice}}</h2>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data(){
return {
name:'',
price:'',
list:[],
allPrice:0
}
},
methods:{
post(){
this.list.push({name:this.name,price:this.price,num:1})
//总价加上新添的物品单价
this.allPrice = this.allPrice + this.price
//以下把输入框清空,比较干净
this.name=''
this.price=''
},
add(item){//传入参数,是我们点击的那个物品
//该物品的数量 +1
item.num++
//总价加上该物品单价
this.allPrice = this.allPrice + item.price
},
minus(item,index){//减法,这里我多加一点功能,多传一个下标进来
if(item.num>1){//这里做判断,只有在数量大于1的情况下,才能正常减,否则就要询问是否删除
item.num--
this.allPrice = this.allPrice - item.price
}
else if(confirm("是否删除")){ //这里用到confirm(),返回是一个布尔值true/false 可用来做判断
this.list.splice(index,1)//这里是数组的splice方法
this.allPrice = this.allPrice - item.price
}
}
}
}).mount('#app')
</script>
这一遍主要变化的是函数了。我都加上备注了,应该能看懂吧嘿嘿。
ok,好好理解之后,接着改进,我们发现在每一个函数里面啊,都要去更新这个allPrice,是不是非常多一样的代码呀,非常不爽,那么这个总价呢,其实说到底 就是一个需要计算得到的值吧。那么我们可以用到computed属性,跟methods和data同级,是一个计算属性。它里面的值是函数,拿到的值是它的返回值,
computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新
例如:
computed:{
getAllPrice(){
return 100
}
}
这样写的话,我们在代码中写上{{getAllPrice}}就会展示100了。那我们要得到的总价应该等于什么嘞,应该等于商品的数量乘以单价对吧,所以这样写:
computed:{
getAllPrice(){
//循环遍历数组,把每个商品的价值加到sum中,最终返回
//而每当与之有依赖关系的值发生变化时,它就会自动进行计算更新
let sum = 0
this.list.forEach(v => sum+=v.price*v.num)
return sum
}
}
所以代码的最终形态是:
<div id="app">
<h1>购物车</h1>
<form @submit.prevent="post">
<label for="inp1">商品名称 </label>
<input id="inp1" type="text" v-model.trim.lazy="name" />
</br>
<label for="inp2">商品单价 </label>
<input id="inp2" type="number" v-model.number.lazy="price" />
</br>
<button>提交表单</button>
</form>
<!-- 上面是表单的部分 -->
<ul>
<li v-for="item,index in list">
商品名称:{{item.name}} 单价:{{item.price}} 数量:<button @click="minus(item,index)">-</button> {{item.num}} <button @click="add(item)">+</button>
</li>
</ul>
<!-- 上面是列表的部分 -->
<h2>总价{{getAllPrice}}</h2>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data(){
return {
name:'',
price:'',
list:[],
}
},
computed:{
getAllPrice(){
let sum = 0
this.list.forEach(v => sum+=v.price*v.num)
return sum
}
},
methods:{
post(){
this.list.push({name:this.name,price:this.price,num:1})
this.name=''
this.price=''
},
add(item){
item.num++
},
minus(item,index){
if(item.num>1){
item.num--
}
else if(confirm("是否删除")){
this.list.splice(index,1)
}
}
}
}).mount('#app')
</script>
那这里就顺着computed计算属性学习另一个属性watch监视属性,依然用上面的代码做例子:
1
<script>
2
Vue.createApp({
3
data(){
4
return {
5
name:'',
6
price:'',
7
list:[],
8
}
9
},
10
computed:{
11
getAllPrice(){
12
let sum = 0
13
this.list.forEach(v => sum+=v.price*v.num)
14
return sum
15
}
16
},
17
watch:{
18
'list':{ //监视谁,就写谁 我这里监视list
19
handler(newValue,oldValue){ //当list发生改变时调用
20
console.log(`list从${oldValue}变成了${newValue}`)
21
},
22
immediate:true,//默认是false的,设置为true会在初始化时调用handler()
23
}
24
}
25
}).mount('#app')
26
</script>
而且watch()不仅能监视data()中定义的变量,还能检测computed计算属性中的值,也就是上面的getAllPrice()同样能被监视。然后也可以拿到Vue的实例然后在外部检测:
1
<script>
2
const app = Vue.createApp({ //拿到Vue实例
3
data(){
4
return {
5
name:'',
6
price:'',
7
list:[],
8
}
9
},
10
computed:{
11
getAllPrice(){
12
let sum = 0
13
this.list.forEach(v => sum+=v.price*v.num)
14
return sum
15
}
16
},
17
})
18
19
app.$watch('list',{ //同样能监视
20
immediate:true,
21
handler(newValue,oldValue){
22
console.log(`list从${oldValue}变成了${newValue}`)
23
}
24
})
25
26
app.mount('#app')
27
</script>
那具体怎么分呢,如果说我们在刚创建vue实例的时候就已经知道我们要监视谁,那就可以直接在实例内部使用watch(),但是如果是需要用户后续的行为来决定要监视谁,那就在外部调用这个watch api就可以了。
那么学会了监视,接下来进阶一下深度监视:
1、监视多级结构中某个属性的变化
1 <script>
2 Vue.createApp({
3 data(){
4 return {
5 numbers:{ //创建一个对象,而我单单只想监视a,不监视b
6 a:1,
7 b:1
8 }
9 }
10 },
11 methods:{
12 aAdd(){
13 this.numbers.a++
14 }
15 }
16 watch:{
17 //切记这是字符串,如果是单个属性可以不要引号,但如果像这个numbers是对象,想只监视a,必须用引号
18 'numbers.a':{
19 handler(newValue,oldValue){
20 console.log(`a从${oldValue}变成了${newValue}`)
21 },
22 }
23 }
24 }).mount('#app')
25 </script>
在模板中触发其中的aAdd(),会发现控制到可以监视numbers中a的值了。
2.监视多级结构中所有属性的变化
1//一看到这个是不是在想像下面这样就行了呀
2'numbers':{
3 handler(newValue,oldValue){
4 console.log(`从${oldValue}变成了${newValue}`)
5 },
6}
其实是不行的,因为这样子呢它监视的其实是整个numbers:{} 这个结构,而我们真正改变的是它内部的a、b,地址是不一样的,因此也就不会触发watch()。但其实呢,也只需要加一个配置项就可以了:
1'numbers':{
2 deep:true, //深度监视,默认是false,这下就可以了
3 handler(newValue,oldValue){
4 console.log(`从${oldValue}变成了${newValue}`)
5 },
6}
但这里不能被误导,Vue自身呢是可以监测对象内部值的改变,但Vue提供的watch默认是不能监测到的。
监视的简写形式:简写是需要代价的,不能够使用 immediate和deep,只有handler()
1watch:{
2 //正常写法
3 'list':{
4 immediate:true,
5 deep:true,
6 handler(newValue,oldValue){
7 console.log(`list从${oldValue}变成了${newValue}`)
8 },
9 },
10 //简写
11 list(newValue,oldValue){
12 console.log(`list从${oldValue}变成了${newValue}`)
13 }
14}
15
16//在外部的写法一样可以简写,但一样不能使用 immediate和deep
17app.$watch('list',{
18 immediate:true,
19 deep:true,
20 handler(newValue,oldValue){
21 console.log(`list从${oldValue}变成了${newValue}`)
22 }
23})
24
25app.$watch('list',function(newValue,oldValue){ //直接写函数
26 console.log(`list从${oldValue}变成了${newValue}`)
27})
28//这里有一个需要注意的代诺,如果我这里写成箭头函数,那么里面的this指向的就是window而不是app了
29app.$watch('list',(newValue,oldValue)=>{
30 console.log(`list从${oldValue}变成了${newValue}`)
31})
好嘞,如果你学到这里,基础就学得差不多了,下面一章学习组件化开发。一定要多巩固喔,敲点自己感兴趣的页面或效果出来。一起加油!!!