Skip to content

错误收集

vscode 控制台执行不了 node 命令

node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

原因:没有下载 node。nvm 没有执行 nvm use xx.xx.xx。

Promise 无法捕获 setTimeout 里的异常错误

把 throw new Error 语句放延时函数里,只会报错,不执行 reject 函数或者 catch 函数。

js
var promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        throw new Error('nono') //这里主动抛出错误
    }, 500);
})
    .then(()=>{},(err)=>{
        console.log(1) //这里是 reject 时应该调用的函数,但是这里并没有执行,只会在控制台报错
        console.log(err) //根本不执行这里
    })
    .catch((err)=>{
        console.log(2) //这里 catch 都不执行
        console.log(err)
    })

而我不把 throw new Error 语句放延时函数里,就能在 reject 函数里执行。

js
var promise = new Promise((resolve, reject) => {
    throw new Error('nono') //这里直接抛出错误,就能被后面的 reject 函数执行到
})
    .then(()=>{},(err)=>{
        console.log(1) //这里就执行了
        console.log(err)
    })
    .catch((err)=>{
        console.log(2)
        console.log(err)
    })

解决异步错误怎么办呢?答案是 try catch。

js
setTimeout(() => {
    try {
        throw new Error('出错了')
    } catch (e) {
        console.log(e)//代码执行的时候,这里就能捕获到上面抛出的异常错误了
    }
}, 400)

下载依赖使用哪个包管理器

ValidationError: Invalid options object. Sass Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'data'. These properties are valid: object { implementation?, sassOptions?, prependData?, sourceMap?, webpackImporter? }

解决:

下载依赖时应该使用包管理器 yarn,而不是 npm。

为什么在 npm 下载依赖失败后不尝试使用 yarn 呢?因为 *.lock 文件被忽略了,项目没有出现 yarn.lock 文件。我哪知道必须要用 yarn 来下载呢。

严格模式

ReferenceError: MP is not defined at xxxxxx

解决:

MP 是通过 cdn 引入的一个全局对象。有时候网络不佳可能获取不到。然后我们就可能这样判断:

js
	if (!MP || !MP?.MGS) {
		console.warn('MGS sdk init fail');
		return;
	}

但实际上代码走不到 if 判断里。因为 webpack 项目打包时默认为 js 代码开启了严格模式,当变量没有显式声明时,使用该变量会直接抛出错误。

当 CDN 脚本违背同源策略

给 script 脚本添加属性 crossorigin="anonymous"。

js
<script
  src="https://xxx"
  crossorigin="anonymous"
></script>

如果项目基于 vue-cli 创建,还可以在 vue.config.js 文件中配置 crossorigin 属性。

某些依赖的 es6 代码未被转为 es5 代码

这导致在低版本浏览器出现白屏问题。如果是 vue-cli 项目,可以在 vue.config.js 文件中配置 transpileDependencies 属性。

volta 不能启动使用 yarn 下载依赖的项目

Executing task: yarn run dev:test:mp-alipay

Internal Error: mpaas-uniapp-tutorial@workspace:.: This package doesn't seem to be present in your lockfile; run "yarn install" to update the lockfile at Yb.getCandidates (C:\Users\lukecheng\AppData\Local\Volta\tools\image\yarn\4.0.0-rc.43\bin\yarn.js:199:8150) at xd.getCandidates (C:\Users\lukecheng\AppData\Local\Volta\tools\image\yarn\4.0.0-rc.43\bin\yarn.js:135:1311) at C:\Users\lukecheng\AppData\Local\Volta\tools\image\yarn\4.0.0-rc.43\bin\yarn.js:204:8096 at Vy (C:\Users\lukecheng\AppData\Local\Volta\tools\image\yarn\4.0.0-rc.43\bin\yarn.js:134:53714) at de (C:\Users\lukecheng\AppData\Local\Volta\tools\image\yarn\4.0.0-rc.43\bin\yarn.js:204:8076)

问题原因是没有指定 yarn 版本。volta 默认使用最新版的 yarn 来启动项目,导致失败。

解决:

在 package.json 中指定 node 版本的同时,也指定 yarn 版本。

json
	"volta": {
		"node": "14.17.0",
    "yarn": "1.22.17"
	}

IOS 不支持的正则表达式

ios 不支持该正则表达式:

js
productNo.replace(/(?<=.{3}).(?=.{4})/g, '*');

否则会报错:

Invalid regular expression: invalid group specifier name

强缓存与协商缓存问题

端外生活缴费应用打包时,会使用 webpack 插件 copy-webpack-plugin 将 node_modules/@bestpay/mpaas-mgs-sdk/mpaas-mgs-sdk 目录下的 gwcli1-1-4.wasm、mgssdk.min.js 文件复制到打包后的 dist/static/mpaas-mgs-sdk 目录下。

在 public/index.html 文件中,添加 script 标签并设置 src 将 mgssdk.min.js 引入:<script type="text/javascript" src="static/mpaas-mgs-sdk/mgssdk.min.js"></script>

这样进入生活缴费应用时,会去请求资源 mgssdk.min.js。

问题复现:

请求 mgssdk.min.js 时,返回的响应头设置了 Cache-Control: public,must-revalidate,max-age=1296000,这代表该资源会缓存 15 天。这导致上线时,用户浏览器请求 mgssdk.min.js 资源时如果缓存没有过期直接取缓存,而不会去请求服务器获取最新的 mgssdk.min.js。

然后最近依赖包 @bestpay/mpaas-mgs-sdk 进行了升级。升级前的 mgssdk.min.js 引用 gwcli1-2-5.wasm 文件,升级后引用 gwcli1-1-4.wasm 文件。

请求 .wasm 后缀文件资源时,返回的响应头设置了 Cache-Control:must-revalidate,max-age=3600,这代表该资源会缓存 1 小时。

昨晚 10 点发版后,用户访问应用,请求的 mgssdk.min.js 命中缓存,然后去请求 gwcli1-2-5.wasm 文件也命中缓存。此时功能验证无误。1 小时后,gwcli1-2-5.wasm 的缓存失效,去请求服务器。但服务器上只有 gwcli1-1-4.wasm 文件,请求 404,导致 mgs 接口无法调通。

问题解决

在引入 mgssdk.min.js 的资源路径中加入时间戳,这样每次发版后,用户都会请求服务器,拿到最新版本 mgssdk.min.js。

在 vue.config.js 中:

js
const notCacheDir = `cache-${dateNow()}`

module.exports = {
  // ...
  configureWebpack: {
    plugins: [
			new HtmlWebpackPlugin({
				env: ENV,
        // ============
				notCacheDir,
        // ============
				template: 'public/index.html',
				inject: true,
				minify: {
					caseSensitive: true, //是否大小写敏感
					collapseWhitespace: true, //是否去除空格
					removeComments: true, //去注释
					removeAttributeQuotes: false // 去掉属性引用
				}
			}),
    ]
  },
  chainWebpack: (config) => {
    config.resolve.symlinks(true);
    config.plugin('copy').use(require('copy-webpack-plugin'), [
      [
        {
          from: 'node_modules/@bestpay/mpaas-mgs-sdk/mpaas-mgs-sdk',
          to: path.resolve(__dirname, `./dist/static/${notCacheDir}/mpaas-mgs-sdk`),
          ignore: ['.*']
        }
      ]
    ]);
  }
  // ...
}

然后改造 public/index.html 文件,使用 ejs 语法将 notCacheDir 变量加入到请求 mgssdk.min.js 的资源路径中。

html
<script type="text/javascript" src="static/<%= htmlWebpackPlugin.options.notCacheDir %>/mpaas-mgs-sdk/mgssdk.min.js"></script>

volta 全局下载依赖失败

解决办法:以管理员身份启动 vscode 或命令行窗口。

Promise 捕获不到错误

你觉得高亮行的代码会打印输出吗?答案是不会的。

因为在返回 promise 之前,程序就终止执行了!

js
function mgsHttp(api, params, options = {}) {

  // ...
	const { method, operationType } = api;
	if (!operationType) {
		console.warn('operationType undefined');
		return;
	}
  // ...
  
  // 假设在下面这行出错了
  throw new Error('出了某些错误')

	return new Promise((resolve) => {
		// xxx
    // 发请求
	});
}

mgsHttp(api, params, options).then(response => {

}).catch(error => {
  console.log(error) 
})

Slot invoked outside of the render function - 解决使用 slot 时遇到的问题

Slot "default" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.

最后发现了问题所在:useSlots 返回的 slots 是一个代理对象。

如果像一开始那样写,只是给 isShowIcon 赋了一个初始值。

想要具有响应式,需要放在 computed 计算属性里。

diff
<script setup lang="ts">
import { computed, useSlots } from 'vue';

/** 获取插槽 */
const slots = useSlots()

- // const isShowIcon = !(slots.default && slots.default() && slots.default()[0].children)

+ const isShowIcon = computed(() => {
+   return !(slots.default && slots.default() && slots.default()[0].children)
+ })

</script>
<template>
  <button>
    <span v-show="isShowIcon">🤩</span>
    <slot />
  </button>
</template>
<style lang="scss" scoped>

</style>

Released under the MIT License.