分类目录归档:前端

ERROR [nuxt] [request error] [unhandled] [500] fetch failed

ERROR [nuxt] [request error] [unhandled] [500] fetch failed

 ERROR  [nuxt] [request error] [unhandled] [500] fetch failed
  at Object.fetch (node:internal/deps/undici/undici:11118:11)  
  at process.processTicksAndRejections (node:internal/process/task_queues:95:5)  
  at async sendProxy (./node_modules/.pnpm/h3@1.11.1/node_modules/h3/dist/index.mjs:1157:20)  
  at async ./node_modules/.pnpm/h3@1.11.1/node_modules/h3/dist/index.mjs:1962:19  
  at async Object.callAsync (./node_modules/.pnpm/unctx@2.3.1/node_modules/unctx/dist/index.mjs:72:16)  
  at async Server.toNodeHandle (./node_modules/.pnpm/h3@1.11.1/node_modules/h3/dist/index.mjs:2249:7)

触发此错误代码

demo.vue

async foo() {
  const resData = await useFetch(`/api/good/1`, {
    method: 'DELETE',
  });
}

nuxt.config.ts

export default defineNuxtConfig({
  nitro: {
    routeRules: {
      "/api/**": {
        proxy: `${env.NUXT_API_URL}api/**`,
      },
  }
}

临时解决方案

把请求方式改为 POST 请求。

async foo() {
  const resData = await useFetch(`/api/good/1`, {
    method: 'POST',
  });
}

网上有说是这些情况:

  1. 代理域名 httphttps 的问题,测试过不是这个问题,和域名无关。
    1. 排查域名请求日志,出现此问题时,请求日志没有记录,说明请求没有到达后端,是前端请求问题。
    2. 尝试改下请求方式,发现 POST 请求正常,DELETE 请求报错,也许是请求方式问题,但是不确定,先临时解决一下。
  2. 降级 h3@1.9.0 版本,降级后,问题依然存在。
  3. 降级 nuxt@3.10.3 版本,降级后,问题依然存在。

参考

  1. 基于nitro的Nuxt3服务端无法请求https,难道是我写错了?
  2. [nuxt] [request error] [unhandled] [500] using proxyRequest or sendProxy · Issue #376 · unjs/h3
  3. Nuxt v3.11.0 proxyRequest in server api routes makes nitro crash if proxy server is down · Issue #26318 · nuxt/nuxt

在prettier3项目中替代pretty-quick

解决方案

.husky/pre-commit 中添加以下内容

# 获取暂存区的文件列表
FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0

# 美化所有暂存区的文件
echo "$FILES" | xargs ./node_modules/.bin/prettier --ignore-unknown --write

# 将修改/美化的文件添加回暂存
echo "$FILES" | xargs git add

背景

在 prettier v3.x 版本的项目中,使用 pretty-quick 会报错:

.../isSupportedExtension.js:12
  ..._prettier.resolveConfig.sync(file, {
                             ^
TypeError: _prettier.resolveConfig.sync is not a function

可以降低 prettier 版本到 2.x 来解决这个问题,但是项目里面额外使用了 tailwindcss,而 tailwindcss 依赖的 postcss 依赖的 prettier 版本是 3.x,所以降低 prettier 版本不是一个好的解决方案。

参考

  1. Pre-commit Hook · Prettier
  2. pretty-quick will break with prettier v3 · Issue #164 · azz/pretty-quick
  3. Editor Setup – Tailwind CSS
  4. tailwindlabs/prettier-plugin-tailwindcss: A Prettier plugin for Tailwind CSS that automatically sorts classes based on our recommended class order.

element-plus el-button 增加 v-blur 指令

element-plus Button focus

element-plus el-button 增加 v-blur 指令

背景 & 问题

解决点击按钮后,触发 el-dialog 等弹窗组件的显示交互,弹窗隐藏之后,按钮的 focus 依然存在的问题。

🤔 思考:这个属于正常的交互,可以理解为:用户点击的哪里触发的这个弹窗,弹窗隐藏之后,用户的焦点应该回到点击的地方。

如果产品经理要求,弹窗隐藏之后,隐藏的按钮不要 focus,那么就需要增加一个指令来解决这个问题。

解决方案

1、在项目中新增一个指令文件 src/directives/blur/index.ts,内容如下:

src/directives/blur/index.ts

import { Directive } from "vue";

export const blur: Directive = {
  mounted(el) {
    if (!el) return;
    /**
     * 是否是点击触发的 blur
     *  - 点击触发 blur 时,不触发 focus
     *  - 非点击触发 blur 时,触发 focus
     */
    let isClick = false;
    el.addEventListener("click", () => {
      isClick = true;
    });
    el.addEventListener("focus", () => {
      if (isClick) {
        el.blur();
      }
    });
    el.addEventListener("blur", () => {
      isClick = false;
    });
  }
};

2、在 main.ts 中引入该指令:

main.ts

import App from "./App.vue";
import { createApp } from "vue";

import { blur } from "./directives/blur";

const app = createApp(App);

// ...

app.directive("blur", blur);

app.mount("#app");

3、在需要使用的地方,使用 v-blur 指令即可:

<script setup lang="ts">
defineOptions({
  name: "Demo"
});

const showDialog = () => {
  // ...
};
</script>

<template>
  <div class="demo">
    <el-button v-blur @click="showDialog">添加</el-button>
  </div>
</template>

<style lang="scss" scoped>
// ...
</style>

最终方案(2023-08-03 更新)

使用 css 覆盖即可解决:

.el-button:focus-visible {
  outline: 0;
}

element-plus Button focus after

stylelint ELIFECYCLE  Command failed with exit code 129.

在项目中执行 stylelint 时,报错如下:

ELIFECYCLE  Command failed with exit code 129.

大概率是因为 stylelint 规则配置有问题,排查了好久,最后因为一个element-plus的一个变量命名导致的。

src/components/TableHeader/base/filterButton.vue
 288:16  ✖  Expected custom property name "--el-dropdown-menuItem-hover-color" to be kebab-case  custom-property-pattern

排查过程中,stylelint 始终输出这个行报错,经过以下步骤排查,最终定位到问题:

  1. 删除 node_modules,重新安装依赖
  2. 删除 pnpm-lock.json,重新安装依赖
  3. 删除 .cache,重新安装依赖
  4. 查看 stytlelint 最新是否有新版本更新,确实有,更新到最新版本
  5. 重新执行 stylelint,报错信息出现

制作一个简单的Link Preview服务

1. 什么是Link Preview

Link Preview是指鼠标移动到一个超链接上,会自动显示该链接的标题、描述、图片等信息,如下图所示:

维基百科

当我们在浏览维基百科时,会发现在发送维基百科的链接时,会自动显示维基百科的标题、描述、图片等信息,这就是Link Preview的效果。效果有点类似,标签的 title 属性,但是Link Preview的效果更加丰富,而且不需要等待太长时间。

也是最近逛推的时候,发现了这个网站:Cali Castle,增加了这个功能,大概实现是一个get请求返回了一张图片,然后在页面上显示了这张图片,这个图片就是Link Preview的效果。

Cali Castle

2. 如何实现(待更新)

待更新……

  1. 接口能力:使用 nodejs 实现一个接口,接收一个 url 参数,返回一个图片。
  2. 业务开发:使用 API 返回的图片。展示出来(显示隐藏等逻辑)。

3. 本地开发(待更新)

  • chrome-aws-lambda
  • puppeteer

4. 部署

这里提供了两种部署方式:Vercel、阿里云;因为我在Vercel上部署的时候,发现内存不足<sup> [问题1] </sup>,部分功能受到限制,所以就在阿里云上部署了。

4.1 部署到Vercel(待更新)

需要注意的是, Vercel 的免费版,内存有限制,我们只能实现基本的功能,访问日志和缓存截图文件<sup> [1] </sup>:

4.2 部署到阿里云ECS

4.2.1 安装依赖

我的服务器是阿里云的ECS,系统是CentOS Linux release 7.9.2009,安装以下依赖:

  • alsa-lib.x86_64
  • atk.x86_64
  • cups-libs.x86_64
  • gtk3.x86_64
  • ipa-gothic-fonts
  • libXcomposite.x86_64
  • libXcursor.x86_64
  • libXdamage.x86_64
  • libXext.x86_64
  • libXi.x86_64
  • libXrandr.x86_64
  • libXScrnSaver.x86_64
  • libXtst.x86_64
  • pango.x86_64
  • xorg-x11-fonts-100dpi
  • xorg-x11-fonts-75dpi
  • xorg-x11-fonts-cyrillic
  • xorg-x11-fonts-misc
  • xorg-x11-fonts-Type1
  • xorg-x11-utils
yum install -y alsa-lib.x86_64 atk.x86_64 cups-libs.x86_64 gtk3.x86_64 ipa-gothic-fonts libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXrandr.x86_64 libXScrnSaver.x86_64 libXtst.x86_64 pango.x86_64 xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-fonts-cyrillic xorg-x11-fonts-misc xorg-x11-fonts-Type1 xorg-x11-utils

安装依赖项后,您需要使用此命令更新 nss 库

yum update nss -y

参见官方文档:Chrome doesn’t launch on Linux

需要安装依赖,否则会报一些奇怪的错误 <sup> [问题2] </sup>。

4.2.2 执行部署

把代码上传到服务器。安装依赖:

阿里云上安装依赖,需要使用淘宝的镜像源,否则会报错,安装依赖:

cnpm install

测试代码是否可以正常运行:

node index.js
> Server is start 9000.

打开一个新的终端,测试接口是否正常:

curl http://localhost:9000/api?url=https://webclown.net

注意这里返回的是一个图片实体文件,终端输出内容比较多。

配置nginx,配置文件如下:

server {
    listen       80;
    server_name  webclown.com;

    location /api {
        proxy_pass http://localhost:9000;
        proxy_redirect http://localhost:9000 https://webclown.net;
    }
}

如果真正用于生成环境,设置防盗链功能,防止被恶意盗用,简单的防盗链可以以 referer、user Agent 为条件,设置防盗链,如下所示:

server {
    ...
    location /api {
        # 如果 reffer 不是 webclown.net 则返回 403
        if ($http_referer !~* (webclown.net)) {
            return 403;
        }
        # 如果没有 ua 则返回 403
        if ($http_user_agent = "") {
            return 403;
        }
        proxy_pass http://localhost:9000;
        proxy_redirect http://localhost:9000 https://webclown.net;
    }
    ...
}

问题

  • 问题1:Vercel 内存不足,无法正常运行。
    Error: Runtime exited with error: exit status 1
    Runtime.ExitError
  • 问题2:阿里云上,无法正常运行,没有安装依赖。
    error while loading shared libraries: libatk-bridge-2.0.so.0: cannot open shared object file: No such file or directory

注:

  1. 缓存截图文件:以链接的上的query参数的 md5 作为文件名,生成缓存截图文件,加快访问速度,这里也只是简单的实现,不考虑文件的持久化储存、定时清理等边界功能,这是一个复杂的点,这里不展开说明。

参考资料:

  1. Express
  2. Vercel
  3. Puppeteer
  4. alixaxel / chrome-aws-lambda
  5. Chrome doesn’t launch on Linux
  6. Cali Castle
  7. michaelkitas / Puppeteer-Vercel

Element-ui 使用经验总结

el-popover

<el-popover
  placement="top"
  width="160"
  trigger="hover"
  ref="popover"
  v-model="visible">
  <p>确认要清空所有互斥关系吗?</p>
  <div style="text-align: right; margin: 0">
    <el-button size="mini" type="text" @click="hidePopover">取消</el-button>
    <el-button type="primary" size="mini" @click="confirm">确定</el-button>
  </div>
  <i class="el-icon-delete" slot="reference"></i>
</el-popover>
export default {
    methods: {
        confirm() {
            this.hidePopover()
        },
        hidePopover() {
            this.visible = false;
            this.$refs['popover'].doClose()
        },
    }
}

Number.isSafeInteger() 安全整数

Number.isSafeInteger() 安全整数

MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger

安全整数范围为 -(2^53 - 1)2^53 - 1 之间的整数,包含 -(2^53 - 1)2^53 - 1

触发机制为:

接口返回了一条数据,id 是 一个大于 99999 99999 99999(15位)的数字。

9085 35711 51050 56000(19位)

结果在对调这条数据修改时候,接口提示查不到这条数据。

登录数据库 查看数据

本文只是提供一个思路,提示无数据,并不一定是这个问题