【web系列十三】vue3实操技巧
这里主要记录博主在开发过程中遇到过的问题以及最终的解决方案,博主一般会选择比较通用的方法,希望大家遇到类似的问题可以少走弯路。
目录
写在前面
这里主要记录博主在开发过程中遇到过的问题以及最终的解决方案,博主一般会选择比较通用的方法,希望大家遇到类似的问题可以少走弯路。
纯前端
生命周期钩子
前后端都需要
通信
总结了 Vue3 的七种组件通信方式,别再说不会组件通信了 (baidu.com)
父组件向子组件传值用props
父组件代码
在子组件中定义一个show属性,父组件中通过该属性向子组件传值,并可用于控制子组件中的Button是否显示,注意,data前要加:
<template>
<div class="test">
<Son :show="true" />
</div>
</template>
<script lang="ts" setup>
import Son from '@/components/son.vue'
</script>
<style></style>
子组件代码
由于使用了<script lang="ts" setup>,因此想要使用props需要先引入defineProps。
<template>
<div class="test">
<button v-if="!show">show</button>
</div>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue'
let props = defineProps({
show: Boolean
});
</script>
<style></style>
父组件触发子组件事件用defineExpose
父组件代码
父组件中给button定义一个点击时间notify,用于触发子组件的时间remove。
<template>
<div class="test">
<button @click='notify' />
<Son ref='child' />
</div>
</template>
<script lang="ts" setup>
import Son from '@/components/son.vue'
import { getCurrentInstance } from '@vue/runtime-core'
let currentInstance = getCurrentInstance();
function notify() {
currentInstance.ctx.$refs.child.remove();
}
</script>
<style></style>
子组件代码
子组件中定义待触发的事件remove,注意要用defineExpose将事件抛出,否则父组件是无法调用这个事件的。
<template></template>
<script lang="ts" setup>
import { defineExpose } from 'vue'
let remove=() => {
console.log('remove');
});
// 也可以这么写
// function remove() {
// console.log('remove');
// });
defineExpose({
remove
})
</script>
<style></style>
子组件向父组件传值(触发事件)用$emit
父组件代码
子组件中通过create事件触发父组件的create事件,父组件执行create函数。
<template>
<div class="test">
<Son @create="create" />
<Test v-if="show" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Son from '@/components/son.vue'
import Test from '@/components/test.vue'
let show = ref(false);
function create(data) {
show.value = true;
consolo.log(data);
}
</script>
<style></style>
子组件代码
由于使用了<script lang="ts" setup>,因此想要使用emits需要先引入defineEmits。
<template>
<div class="test">
<button @click="create">show</button>
</div>
</template>
<script lang="ts" setup>
import { defineEmits } from 'vue'
let emits = defineEmits (['create']);
let result = 1;
function create() {
return emits("create", result)
}
</script>
<style></style>
发送axios同步请求
如果我们用以下方法发送请求时,其实是异步的,这时我们打印的data还是空的。
let data = [];
function getData() {
axios.get("getData").then(response => {
data = response.data;
});
}
getData();
console.log(data);
而有时候我们希望等返回了data再执行后面的函数,这时可以实用async+await的方法实现。
let data = [];
async function getData() {
await axios.get("getData").then(response => {
data = response.data;
});
}
getData();
console.log(data);
下载zip文件
网上有很多web下载文件的方式,各有优缺点,以下这篇文章整理的还是比较全的,大家可以参考学习。
Web前端下载文件的几种常见方式_许进进的博客-CSDN博客_前端下载文件的方式
我这边用一个比较通用且兼容性好的方式——blob。
首先,我们封装一个前端的download库。
import axios from "axios";
export interface FileOptions {
type?: string;
name: string;
}
async function downloadFileByPost(
url: string,
formData: FormData,
options: FileOptions
): Promise<void> {
await axios
.post(url, formData, {responseType: 'blob'})
.then(response => download(response, options));
}
async function downloadFileByGet(
url: string,
options: FileOptions
): Promise<void> {
await axios
.get(url, {responseType: 'blob'})
.then(response => download(response, options));
}
functon download(
response: any,
options: FileOptions
): Promise<void> {
let blob = new Blob([response.data], {
type: options.fileType ? options.fileType : 'application/octet-binary'
});
// 创建下载的链接
let href = window.URL.createObjectURL(blob);
// 创建临时元素
let downloadElement = document.createElement('a');
downloadElement.href = blobUrl
// 下载后文件名
downloadElement.download = fileName
document.body.appendChild(downloadElement);
// 点击下载
downloadElement.click()
// 下载完成移除元素
document.body.removeChild(downloadElement);
// 释放掉blob对象
window.URL.revokeObjectURL(blobUrl);
}
export {
downloadFileByPost,
downloadFileByGet
};
然后,来到需要下载文件的vue组件中使用。
<template>
...
</template>
<script lang="ts" setup>
import { FileOptions, downloadFileByGet } from '@/utils/download.ts';
funcion download(): void {
let options:FileOptions = {
name: 'xxx.zip'
};
downloadFileByGet('http://192.168.2.2:8080/download', options);
}
</script>
<style></style>
最后来到服务器端,这里用的是django。
from django.http import HttpResponse
import os
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO
def download(request):
response = BytesIO()
file_root = "D:\\data"
# 创建压缩文件
with ZipFile(response, "w", ZIP_DEFLATED) as f:
# 将需要的文件写入压缩文件
for file in os.listdir(file_root):
# 前一个参数是文件存放位置,后一个参数是打包后的文件路径
f.write(os.path.join(file_root, file), os.path.join('download', file))
# 指向文件头
response.seek(0)
return HttpResponse(response)
这里只写了一些关键代码。
当触发了页面的download函数后,前端会向后端发送请求,后端将需要的文件压缩为zip后返回给前端并触发页面的下载。
这里用到了python的zipfile库,有人做了一个比较详细的整理,感兴趣的大家可以看看。
zipfile 模块详解 - nuoruo - 博客园 (cnblogs.com)
开发小技巧
语法
script
<script>中不需要再写成<script> export default {setup(){}}这种形式了,可以直接写
<script lang="ts" setup></script>
proxy对象解析
使用toRaw可以获取proxy对象包裹的数据
<script lang="ts" setup>
import { toRaw } from '@vue/reactivity'
let list = toRaw(proxy_obj);
</script>
布局
浮动布局
可以参考博主的这篇文章中:其他的一些常用功能示例->浮动布局
【web系列五】CSS_Nicholson07的博客-CSDN博客
flex
使用flex排版比float方便不少,可以参考一下博文
Css 弹性布局(Flex)详细介绍(Flex 属性详解、场景分析)_前端不释卷leo的博客-CSDN博客_弹性布局
动态开关组件
使用v-if控制显示状态的页面一开始是不会挂载的,未挂载的页面中的watch监视器是没有生效的。如果你想用pinia/vuex中的变量var作为v-if的参数控制页面显示状态,同时用watch监视var,你会发现当页面显示状态改变时,watch不会有任何反应。而有时候我们需要watch来监视页面显示状态的变化,应该怎么办呢?其实非常简单,只要把v-if替换成v-show就可以了,v-show的页面在不显示的状态下已经挂载了,只是增加了一个display属性,因此watch可以生效。
页面缓存
添加页面缓存
使用keep-alive组件包裹,则再次进入页面时,会使用缓存中的组件,不会重新渲染(不再执行mounted及之前的生命周期中的代码),用于保存组件的原始状态(所有变量的值保持最终状态不变)
<keep-alive>
<router-view></router-view>
</keep-alive>
清除部分缓存
1、在 <keep-alive> 上添加 exclude属性
exclude属性中罗列出所有不需要页面缓存的vue组件,并用','间隔。
<keep-alive exclude="test1,test2" >
<router-view></router-view>
</keep-alive>
2、通过路由传参实现 $route.meta.keepAlive
<keep-alive >
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
在定义路由的文件中,设置 meta.keepAlive 参数,如在 src/routes.js 中
一些报错汇总
vue-route
使用vue-route实现页面跳转时,有时会出现以下bug,偶发性的。
Uncaught (in promise) TypeError: api.now is not a function
这可能是vue-route的bug,只要卸载后安装4.0.10,然后重启前端工程和devtools就可以了。
npm uninstall vue-router
npm i vue-router@4.0.10
ts引入js库
基于ts建立的vue工程下导入js库会有如下报错。
Could not find a declaration file for module implicitly has an ‘any‘ type
在shims-vue.d.ts
文件中添加
declare module '*.js'
然后重启项目就好了。
ts懒加载
之前开发时遇到一个问题,在vue的app.vue中引入了a.vue组件和b.ts文件,这两个文件中都包含了pinia初始化代码,而pinia是在app创建后才挂载的,导致了报错,大致意思是:未挂载pinia前就调用了pinia初始化。但是很奇怪的是a.vue组件中没有报错,b.ts中却报错了,原因应该是ts文件加载逻辑与vue不同,导致加载ts文件的时候执行了pinia初始化。
解决方案是使用require懒加载推迟ts文件的加载,我们来看一下import和require的区别:
import:编译时就加载(e.g. import { Bear } from './test.ts'),所以在编译时会报错,且导入的内容不能重新复制,防止被意外更改,但当我们修改了导入的文件模块时会随时更新。
require: 运行时再加载(e.g. const { Bear } = require('./test')),所以在编译时不会报错,就算把文件名输错也没关系
参考资料
总结了 Vue3 的七种组件通信方式,别再说不会组件通信了 (baidu.com)
【web系列五】CSS_Nicholson07的博客-CSDN博客
Web前端下载文件的几种常见方式_许进进的博客-CSDN博客_前端下载文件的方式
更多推荐
所有评论(0)