目录
1.Vue的事件修饰符
1.1在vue中,为标签绑定事件的方式:
<div id="bb" @click="clickbb">
<div id="ez" @click="clickez"></div>
</div>
<input @keyup="">
<form @submit=""></form>
在上述事件的处理函数中,可能需要:阻止事件冒泡、阻止浏览器默认行为、获取按键键值等需求。这些需求都要在事件处理函数中依靠事件对象(event)来实现。
vue认为:事件处理函数内部应该更多的聚焦在业务功能的实现上,而不是这些琐碎的事件对象的处理。所以vue提供了很多事件修饰符来帮助简化这些代码:
<div id="bb" @click="clickbb">
<div id="ez" @click.stop="clickez"></div>
</div>
<input @keyup.enter="">
<form @submit.prevent=""></form>
1.2 .native的作用
native事件修饰符的作用是:将当前绑定的事件当做是html原生事件来看待。
<input @keyup=""> keyup为dom原生事件:按键抬起时触发
<el-input @keyup=""> keyup将被理解为elinput的自定义事件,何时被触发由组件决定
<el-input @keyup.native=""> 将keyup当做dom原生事件来看待
所以,以后为自定义组件绑定原生事件时,长个心眼,一般都会加一个.native修饰符来告诉vue,这是一个原生事件:
<el-input @keyup.native=""></el-input>
<el-button @click.native=""></el-button>
<el-input @blur.native=""></el-input>
2.实现新增演员页面相关业务
业务需求: 点击侧边栏菜单:新增演员,看到新增演员的表单。填写表单点击提交,将演员的数据添加到数据库,提示添加成功,重置表单即可。el-upload在element有现成代码可供使用。
实现步骤:
-
准备好ActorAdd.vue的静态表单页面。做好表单验证。
-
基于el-uploader组件实现头像的上传,上传成功后,将会获取服务端返回的头像访问地址,保存起来。
-
点击提交按钮后,收集表单的数据,做好验证,发送post请求,成功添加后提示消息。
-
做好表单验证。
-
焦点失去的表单验证。
-
点击提交时的表单验证。
-
<!-- 表单 -->
<el-form ref="form" :model="form" :rules="rules" label-width="100px" style="width:600px;">
<el-form-item label="演员姓名" prop="actorName">
<el-input v-model="form.actorName" placeholder="请输入姓名"/>
</el-form-item>
<el-form-item label="演员头像" prop="actorAvatar">
<el-upload
class="avatar-uploader"
:action="`${UPLOADURL}/upload`"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="form.actorAvatar" :src="form.actorAvatar" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
//上传成功后执行
handleAvatarSuccess(res, file) {
console.log('上传成功',res)
this.form.actorAvatar = res.data
},
//上传之前执行:格式 大小的验证 该方法需要返回true 或者 fasle
//ture:通过验证可以上传
//false:验证不通过,不可上传
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isPNG = file.type === 'image/png';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG & !isPNG) {
this.$message.error('上传头像图片只能是 JPG / PNG格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return (isJPG || isPNG) && isLt2M;
}
}
3.实现导演模块相关页面功能(抄演员模块)
-
实现导演列表页面
与演员列表页面功能雷同。不一样的是需要访问不同的接口:
加载导演列表: http://localhost:3010/movie-directors?page=1&pagesize=100
模糊加载导演列表: http://localhost:3010/movie-directors/name?name=关键字
-
实现新增导演页面
上传文件接口一样 http://localhost:9000/upload
新增导演接口:http://localhost:3010/movie-director/add POST
实现步骤:
-
将ActorList.vue与ActorAdd.vue,复制到src/director目录下
改名为:DirectorList.vue,DirectorAdd.vue。
-
把两个文件中的代码进行单词的替换:
演员 --> 导演
actor --> director
Actor --> Director
-
配置嵌套路由即可。
4.实现演员与导演的删除
上图可说明,每次点击叉子时,都会触发事件,在Person组件中就需要执行删除业务;但是在Person中并不能确定发送删除演员请求,还是删除导演请求,在事件处理函数中将两种业务都实现也不现实。应该如何去做?
在子组件捕获到叉子被点击后,不应该直接发请求,而是把该事件抛出,抛给父组件执行后续业务。逻辑如下图所示:
person.vue
<template>
<div class="person">
<img :src="avatar" width="100px" :title="name">
<div>{{ name }}</div>
<i @click="clickx" class="el-icon-circle-close"></i>
</div>
</template>
<script>
export default {
methods: {
clickx() { // 点击叉子之后执行
// 手动抛出一个自定义事件,通知父组件用户点了叉子
},
},
actorList.vue
methods: {
deleteActor(id,e){
// console.log('捕获到用户点了叉子...', id)
// 执行删除演员业务,发送删除请求
this.$confirm('是否移除该演员?','提示',{
confirmButtonText: '确定',
cancelButtonText: '取消',
type:'warning'
}).then(res=>{
httpApi.actorApi.delete({id}).then(res=>{
if(res.data.code==200){
console.log('删除',id)
this.search()
}
})
})
5.Vue中自定义组件的自定义事件(子向父传参)
实现细节如下:
父组件使用子组件时可以捕获子组件的自定义事件:
ActorList.vue
<person @delete="deleteEvent($event)"></person>
deleteEvent(e){
// 当父组件捕获到person子组件抛出的delete事件后自动调用
// 执行删除操作即可
//e 就是子组件抛出来的参数
}
子组件在发现达到某些时间点时,主动抛出delete事件即可:
clickX(){
// 主动抛出一个事件,让父组件捕获
this.$emit('delete', {a:100, b:20})
}
同一个页面由很多组件组成,有些是父子组件;有些是爷孙组件;有些是兄弟组件;当这些组件之间需要共享数据时,就涉及到了组件间传参的问题。
-
父子组件传参
-
父向子传参 使用自定义属性。
<person :name="参数">
-
子向父传参 使用自定义事件。
<person @delete="deletePerson($event)">
-
爷孙组件传参
-
可以使用provide与inject配套完成爷孙组件传参。
6.封装 Axios
当前业务中使用Axios发送请求时API的设计弊端:
每次发请求,都需要写url的前缀,写的还都一样。但是前缀应该分为两种:
测试环境下:
http://localhost:3010/ 业务模块请求前缀
http://localhost:9000/ 上传文件请求前缀
生产环境下:
https://web.codeboy.com/bmdapi/ 业务模块请求前缀
https://web.codeboy.com/bmduploadapi/ 上传文件请求前缀
-
测试环境下: http://localhost:3010/ 业务模块请求前缀 http://localhost:9000/ 上传文件请求前缀 生产环境下: https://web.codeboy.com/bmdapi/ 业务模块请求前缀 https://web.codeboy.com/bmduploadapi/ 上传文件请求前缀
现阶段的写法如果要切换生产环境与测试环境的url前缀时,非常麻烦。
-
如果在项目中有多个地方都需要发送相同类型的请求(例如:查询演员列表),那么每次发送请求时,都需要使用相应的url、请求参数发送(地址不好记,每次都得查接口文档),更恶心的是,如果领导修改了接口,那么所有使用该接口的地方都需要改一遍,非常麻烦。
-
7.Axios的封装的设计实现流程
-
解决每次发请求都需要重复的写请求路径的问题。
在src/http目录中新建一个index.js,将发送请求的方法写在里面并导出对象即可。
// src/http/index.js
import myaxios from "./MyAxios";
const httpApi = { // 封装http接口
/**
* 根据关键字模糊查询演员列表
* @param {Object} params 请求参数 {name:关键字}
* @returns Promise
*/
queryActorsByName(params){
let url = "http://localhost:3010/movie-actors/name"
return myaxios.post(url, params)
},
/** 查询所有演员 */
queryAllActors(){
let url = "http://localhost:3010/movie-actors"
return myaxios.get(url, {page:1, pagesize:100})
}
}
export default httpApi;
需要的使用,引入该index.js,调用导出对象中的方法即可。
listActorsByName(){ // 通过关键字查询演员列表
httpApi.queryActorsByName({name:this.name}).then(res=>{
this.actors = res.data.data
})
}
listActors() { // 加载默认的首页演员列表
httpApi.queryAllActors().then(res=>{
this.actors = res.data.data
})
}
2.当解决了上述问题后,又发现了httpApi对象中需要定义所有的请求接口方法,导致httpApi对象臃肿不堪,方法繁杂,所有接口的访问方法阿都在这一个对象里。需要进一步封装:按照业务模块将API拆分开来,把相应Api方法放入单独的Api模块文件中:
httpApi.actorApi.queryAllActors()
httpApi.directorApi.add()
httpApi.cinemaApi.queryAll()
实现思路:
-
为每一个业务模块准备子接口模块文件:
src/http/apis/ActorApi.js 存放所有演员模块相关的接口方法 src/http/apis/DirectorApi.js 存放所有导演模块相关的接口方法 src/http/apis/MovieApi.js 存放所有电影模块相关的接口方法 ......
2.在相应的子模块文件中声明相关接口:
// src/http/apis/ActorApi.js import myaxios from "../MyAxios"; const actorApi = { // 封装http接口 /** * 根据关键字模糊查询演员列表 * @param {Object} params 请求参数 {name:关键字} * @returns Promise */ queryActorsByName(params){ let url = "http://localhost:3010/movie-actors/name" return myaxios.post(url, params) }, /** 查询所有演员 */ queryAllActors(){ let url = "http://localhost:3010/movie-actors" return myaxios.get(url, {page:1, pagesize:100}) } } export default actorApi;
3.重构index.js,index.js的功能将成为所有子模块的入口:
// src/http/index.js import actorApi from "./apis/ActorApi"; const httpApi = { // 封装http接口 actorApi } export default httpApi;