Vue Vue Vue Calyee 2023-10-25 2023-10-29
Vue对于axios的并行请求处理 使用该方法, 优化性能与数据渲染效果 , axios.all和axios.spread , 所有请求同时进行, 等请求全部发送后再对数据处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function getUserAccount ( ) { return axios.get ('/user/12345' ); } function getUserPermissions ( ) { return axios.get ('/user/12345/permissions' ); } axios.all ([getUserAccount (), getUserPermissions ()]) .then (axios.spread (function (resp1, resp2 ) { let permissions = resp1.data ; if (permissions.code == 200 ){} }));
axios.all方法接受一个数组作为参数,数组中的每个元素都是一个请求,返回一个promise 对象,当数组中所有请求均已完成时,执行then方法。 在then方法中执行了 axios.spread 方法。该方法是接收一个函数作为参数,返回一个新的函数。接收的参数函数的参数是axios.all方法中每个请求返回的响应
应用场景:
并发请求 : 当您需要同时发送多个请求,并在所有请求完成后处理它们的响应时,可以使用这些方法。例如,在一个页面上加载多个资源或从多个 API 端点获取数据时,您可以使用 axios.all 来同时发送这些请求,并使用 axios.spread 来处理每个请求的响应数据。
依赖关系请求 : 有时,您可能需要在一个请求的结果中使用另一个请求的结果。使用 axios.all,您可以并发发送这些请求,并在它们都完成后使用 axios.spread 来处理它们的响应。这可以帮助您更高效地处理具有依赖关系的请求。
批量操作 : 如果您需要执行一系列类似的操作,例如创建、更新或删除多个资源,您可以使用 axios.all 方法来同时发送这些请求,并使用 axios.spread 来处理每个请求的结果。这样可以减少请求的数量和网络延迟。
MVVM模型 什么是MVVM? MVVM 其实表示的是 View-ViewModel-Model
其中对应的就是:视图层-视图模型层-模型层, Model 是作为模型层,它是负责处理业务逻辑 以及和服务器端进行交互 的;ViewModel 是作为视图模型层,也就是 Vue 框架所起到的作用了,主要是作为 View 层和 Model 层之间的通信桥梁 ,
遇到的问题 在页面视图渲染完成之后再往已经渲染完成的data模型添加新属性, 从而后续的Vue操作在页面渲染不生效, 但是通过debug查找, 渲染模型已经有属性了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 displayPage (jsonModel ) { const _this = this if (jsonModel.code === 200 ){ _this.pageInfo = jsonModel.data _this.foods = jsonModel.data .data _this.foods .forEach ((item, index ) => { item.status = false }) } }
当前为问题样例:
jsonModel是后台传的原始数据对象, 当前操作是先给模型固定数据, 然后在进行对模型添加字段, 所带来的问题是: 页面模型已经渲染, 并且当前新添加的字段数据更新, 它不会触发Vue的Observe监听
解决方案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 displayPage (jsonModel ) { const _this = this if (jsonModel.code === 200 ){ jsonModel.data .data .forEach ((item, index ) => { item.status = false }) _this.pageInfo = jsonModel.data _this.foods = jsonModel.data .data } },
在页面对象模型渲染前 , 对模型数据进行修改
ES6异步 Promise Promise是一个对象
Promise 状态(对象,需要实例化) Promise操作有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)
除了异步操作的结果,任何其他操作都无法改变这个状态
Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
缺点:1.无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。 2.如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。 3.当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
.then then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var a = new Promise (function (resolve, reject ){ var jsonModel = {name :'then' ,age=18 } resolve (jsonModel) }).then (function (value ){ console .log (value) return value }).then (function (value ){ console .log (value.age ) }).then (function (value ){ console .log (value) }).catch (function (value ){ })
then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。
.catch Promise.catch()方法是promise.then(undefined,onRejected)方法的一个别名,该方法用来注册当promise对象状态变为Rejected的回调函数。
通俗来说就是:发生异常执行的
.all(静态方法) Promise.all可以接受一个元素为Promise对象的数组作为参数,当这个数组里面所有的promise对象都变为resolve时,该方法才会返回。Promise.all方法中会按照数组的原先顺序将结果返回;
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var promise1 = new Promise (function (resolve )[ setTimeout (function ( ){ resolve ('请求1' ) },3000 ) ]) var promise2 = new Promise (function (resolve )[ setTimeout (function ( ){ resolve ('请求2' ) },1000 ) ]) Promise .all ([ promise1,promise2 ]),then (function ( ){ console .log (value) })
当前小结处可以查看当前文档的 #Vue对于axios的并行请求处理
.race Promise.race的含义是只要有一个promise对象进入FulFilled或者Rejected状态的话,程序就会停止,且会继续后面的处理逻辑
可以参考ES6的promise对象研究 - 龙恩0707 - 博客园 (cnblogs.com)
Generator 构建器
可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
和一般方法不同的是: 1.在 function 后面,函数名之前有个 * 2.函数内部有 yield 表达式
1 2 3 4 function * fun ( ){ console .log ("one" ); yield '1' ; }
用法:
调用 Generator 函数和调用普通函数一样,在函数名后面加上()即可,但是 Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行
这个就有点类似于Java的迭代器
1 2 3 4 Iterator<String> it = list.iterator(); while (it.hasNext()){ it.next(); }
单向移动, 指针后移
文件对象 在日常, 对于文件的上传操作是必要的
图片上传 页面代码案例:
需求:我需要对id=fphoto
的文件对象进行处理
1 2 3 4 5 6 7 8 9 10 11 <form > <div class ="form-group" > <label for ="fphoto" > 图片</label > <input type ="file" id ="fphoto" name ="fphoto" > </div > <div class ="form-group" > <label for ="detail" > 详情</label > <textarea v-model ="detail" rows ="5" cols ="50" id ="detail" > </textarea > </div > <button @click ="submit" type ="button" class ="btn btn-default" > 上架 </button > </form >
常见方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 submit ( ) { const _this = this let fphoto = document .querySelector ("#fphoto" ).files [0 ] let formdata = new FormData () formdata.append ("fphoto" ,fphoto) formdata.append ("detail" , _this.detail ) const req = { headers :{ "Content-Type" :"multipart/form-data" } } axios ({ url :'/upload' , method :'post' , data :formdata, headers : req.headers }).then (res => { let jsonModel = res.data if (jsonModel.code === 200 ){ let jsonModel = res.data if (jsonModel.code === 200 ){ alert ("上传成功!" ) } } }) }
该案例总结
对于文件上传, 必须为post
并且需要在headers指定Content-Type: multipart/form-data
剩下的参数都用formdata包装即可
前端导出Excel 前端代码, 后端用EasyExcel实现, 详情见文档
导出excel, 浏览器弹窗下载关于Easyexcel | Easy Excel (alibaba.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 axios ({ method : "post" , url : "http://localhost:9000/upload/export/forEmployee" , responseType : "blob" , data : this .employeeList , }).then ((res ) => { let blob = new Blob ([res.data ], { type : "application/xlsx" }); let url = window .URL .createObjectURL (blob); const link = document .createElement ("a" ); link.href = url; link.download = "员工信息_" + new Date ().getTime () + ".xlsx" ; link.click (); URL .revokeObjectURL (url); });
组件通信 案例需求: 点击左边/右边元素, 然后点击左移, 实现元素移动
left
operate
right
A
左移 | 右移
D
B
E
C
F
App.vue
查看代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <template > <div id ="app" > <MyCross :left ="this.left" :right ="this.right" @moveLeft ="moveLeft" @moveRight ="moveRight" @moveAllLeft ="moveLeftAll" @moveAllRight ="moveRightAll" > </MyCross > </div > </template > <script > import MyCross from "./components/Cross.vue" ;export default { components : { Cross , }, data ( ) { return { left : [ { id : 1 , name : "林冲" }, { id : 2 , name : "武松" }, { id : 3 , name : "白胜" }, ], right : [ { id : 1 , name : "林冲" }, { id : 2 , name : "哈哈" }, ], }; }, methods : { moveRight (item ) { this .right .push (item); this .left = this .left .filter ((i ) => i !== item); }, moveLeft (item ) { this .left .push (item); this .right = this .right .filter ((i ) => i !== item); }, moveLeftAll ( ) { this .right .forEach ((item ) => { this .left .push (item); }); this .right = []; }, moveRightAll ( ) { this .left .forEach ((item ) => { this .right .push (item); }); this .left = []; }, }, }; </script >
Cross.vue
子组件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <template > <div style ="display: flex" > <ul > <li v-for ="item in left" :key ="item.id" @click ="moveRight(item)" > {{ item.name }} </li > </ul > <div style ="padding-top: 50px" > <button @click ="moveAllLeft()" > ←←</button > <br /> <button @click ="moveAllRight()" > →→</button > </div > <ul > <li v-for ="item in right" :key ="item.id" @click ="moveLeft(item)" > {{ item.name }} </li > </ul > </div > </template > <script > export default { props : { left : { type : Array , required : true , }, right : { type : Array , required : true , }, }, methods :{ moveRight (item ){ this .$emit('moveRight' ,item) }, moveLeft (item ){ this .$emit('moveLeft' ,item) }, moveAllLeft ( ){ this .$emit('moveAllLeft' ) }, moveAllRight ( ){ this .$emit('moveAllRight' ) }, } }; </script >
说明: App.vue为父组件, Cross.vue为子组件
先注册组件
1 2 3 4 5 6 7 import MyCross from "./components/Cross.vue" ;export default { components : { Cross , },
父传子props 1 2 3 4 5 6 7 8 9 <MyCross :left ="this.left" :right ="this.right" @moveLeft ="moveLeft" @moveRight ="moveRight" @moveAllLeft ="moveLeftAll" @moveAllRight ="moveRightAll" > </MyCross >
父组件中引用子组件
通过语法糖(1) :left , 将父组件中的this.left数据传递给子组件, 子组件中同样需要使用该名接收
语法糖(2) @moveLeft: 子组件通知的事件名, 通知后会调用父组件中等号右侧的方法
在子组件中:
1 2 3 4 5 6 7 8 9 10 11 12 export default { props : { left : { type : Array , required : true , }, right : { type : Array , required : true , }, },
此时假如父组件中传了值过来, 即可以直接使用该字段了, 在子组件中使用例如: this.left
子传父$emit 子传父, 例如此时需要通过点击事件触发
1 <button @click ="moveLeft()" > ←←</button >
此按钮作用为: 点击了此按钮并且选中了一个元素, 则实现左移
1 2 3 4 5 6 methods :{ moveLeft (item ){ this .$emit('moveLeft' ,item) }, }
通过语法 $emit通知父组件, 并且传了一个参数item过去
可以很容易的看出, 通知父组件的方法为 moveLeft, 那么父组件中调用子组件的语法中我们可以看到如下操作:
1 <MyCross @moveLeft ="moveLeftFunc" > </MyCross >
我们通过@moveLeft接收通知, 并且触发调用父组件中的方法moveLeftFunc, 形参接收子组件传输的对象
1 2 3 4 moveLeftFunc (item ) { this .left .push (item); this .right = this .right .filter ((i ) => i !== item); },
Vue-Admin-Template初始化 vue.config.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 module .exports = { publicPath : "/" , outputDir : "dist" , assetsDir : "static" , lintOnSave : false , productionSourceMap : false , devServer : { port : port, open : true , overlay : { warnings : false , errors : true , }, proxy : { [process.env .VUE_APP_BASE_API ]: { target : "http://127.0.0.1:9000" , changeOrigin : true , pathRewrite : { ["^" + process.env .VUE_APP_BASE_API ]: "" , }, }, }, },
关闭本地mock模拟接口, 使用自定义后端接口
.env.development 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # just a flag ENV = 'development' # base api # VUE_APP_BASE_API = 'api' VUE_APP_BASE_API = 'http://localhost:9000' # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, # to control whether the babel-plugin-dynamic-import-node plugin is enabled. # It only does one thing by converting all import() to require(). # This configuration can significantly increase the speed of hot updates, # when you have a large number of pages. # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js VUE_CLI_BABEL_TRANSPILE_MODULES = true
routr/index.js 调整路由
utils/request.js 把响应返回值判断改成200