目录

写在前面

纯前端

生命周期钩子

前后端都需要

通信

父组件向子组件传值用props

父组件触发子组件事件用defineExpose

子组件向父组件传值(触发事件)用$emit

发送axios同步请求

下载zip文件

开发小技巧

语法

script

proxy对象解析

布局

浮动布局

flex

动态开关组件

页面缓存

添加页面缓存

 清除部分缓存

一些报错汇总

vue-route

ts引入js库

ts懒加载

参考资料


写在前面

        这里主要记录博主在开发过程中遇到过的问题以及最终的解决方案,博主一般会选择比较通用的方法,希望大家遇到类似的问题可以少走弯路。

纯前端

生命周期钩子

生命周期钩子 | Vue.js (vuejs.org)

VUE3 之 生命周期函数 (bbsmax.com)

前后端都需要

通信

总结了 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)

 生命周期钩子 | Vue.js (vuejs.org)

 VUE3 之 生命周期函数 (bbsmax.com)

【web系列五】CSS_Nicholson07的博客-CSDN博客

 Web前端下载文件的几种常见方式_许进进的博客-CSDN博客_前端下载文件的方式

 zipfile 模块详解 - nuoruo - 博客园 (cnblogs.com)

vue 页面缓存 keep-alive(含配置清除页面缓存 exclude,局部缓存,动态缓存,路由控制缓存 $route.meta.keepAlive)_keep-alive :exclude_朝阳39的博客-CSDN博客

 js require和import区别与应用_ts js require_皮宁澜的博客-CSDN博客

Logo

前往低代码交流专区

更多推荐