在接手一个离职同事的项目准备开发新需求,将项目拉下来后 install 时,发现一个安装包的错误,这是一个 pdf 预览的插件,尝试多次无法顺利安装后去 npm 网站上 查看,显示这个包含有恶意代码的提醒并已经被 npm 安全团队从注册表中删除。我直接好家伙,赶紧对这个模块进行了替换。

具体现象

分析原因

看了下 git 记录发现最少已经是 10 个月之前就已经安装,之前一直没发现问题,可能因为这个同事一直没有再重新安装过整个 node_modules ;另外在 ci/cd 项目发布过程中没问题,是因为公司私有库中会缓存已经安装过的包

在我删除本地的 node_modules 和.lock 文件,指定到公司的私有库地址重新 install 后安装成功了。

1
npm install --registry=http://xxxx/repository/xinnpm-ali

本地运行后查看 bsd-pdf 这个包在项目中的使用,一切正常。随后查看具体这个包到底有哪些问题。

npm 查看

npm 中查询这个包,可以发现因为含有恶意代码,已经被删除只保留了一个占位符

通过 npm 中的链接查看可以看到 4 条关于 bsd-pdf 这个包的不安全信息

点一个看下,就......嗯......看着还挺危险的

替换 pdf 插件

发现问题,接下来就是解决问题,因为项目是一个 vue2 的项目,尝试采用vue-pdf 插件,但在替换过程中发现 vue-pdf 有很多问题(公司项目没问题,但新建的其他项目有错误信息),因而又尝试了另一个插件vue-pdf-embed,比 vue-pdf 更方便使用,因而最后使用了 vue-pdf-embed,对 vue-pdf 也进行一下记录。

vue-pdf

1
yarn add vue-pdf

基础使用

在项目中引入 vue-pdf,默认只展示第一页

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
<template>
<div class="pdf-wrap">
<pdf class="pdf-content" :src="javaScriptPdf"></pdf>
</div>
</template>

<script>
import pdf from "vue-pdf";
import javaScriptPdf from "@/assets/javaScript.pdf";

export default {
components: {
pdf,
},
data() {
return {
javaScriptPdf,
};
},
};
</script>

<style scoped>
.pdf-wrap {
height: 100vh;
}
</style>

显示 pdf 的所有页内容

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 class="pdf-wrap">
<pdf
v-for="(pdf, index) in numPages"
:key="index"
:src="src"
:page="pdf"
></pdf>
</div>
</template>

<script>
import pdf from "vue-pdf";
import javaScriptPdf from "@/assets/javaScript.pdf";

export default {
components: {
pdf,
},
data() {
return {
javaScriptPdf,
src: "",
numPages: 0,
};
},
mounted() {
this.initPdfView();
},
methods: {
initPdfView() {
const loadingTask = pdf.createLoadingTask(
"http://localhost:8081/8a36320a5811132b30cafd656cb6b75e.pdf" //import引入的本地文件pdf地址
);
this.src = loadingTask;
this.src.promise.then((pdf) => {
this.numPages = pdf.numPages;
});
},
},
};
</script>

<style scoped>
.pdf-wrap {
height: 100vh;
}
</style>

vue-pdf 使用问题

vue-cli@5.x使用 vue-pdf 问题

vue-cli@5 的版本使用会有很多错误信息,但不影响 pdf 加载和打包,可以尝试配置取消全屏错误弹层

环境:vue-cli@5.x + vue-pdf@4.3.0

1
2
3
4
5
6
7
module.exports = defineConfig({
devServer: {
client: {
overlay: false, // 编译错误时,取消全屏覆盖(建议关掉)
},
},
});

v-on handler: "TypeError: Cannot read properties of undefined (reading 'catch')"错误

这个错误,在 vue-cli@4 和@5 版本都可能出现,解决方案是 vue-pdf 版本降级,需要同时安装 pdfjs-dist, 参考方案

1
yarn add pdfjs-dist@2.5.207 vue-pdf@4.2.0

vue-pdf-embed

默认全部显示,只需要设置 source 即可

vue2 使用

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
<template>
<div class="pdf-wrap">
<VuePdfEmbed :source="javaScriptPdf"></VuePdfEmbed>
</div>
</template>

<script>
import VuePdfEmbed from "vue-pdf-embed/dist/vue2-pdf-embed";
import javaScriptPdf from "@/assets/javaScript.pdf";

export default {
components: {
VuePdfEmbed,
},
data() {
return {
javaScriptPdf,
};
},
};
</script>

<style scoped>
.pdf-wrap {
height: 100vh;
}
</style>

vue3 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup>
import { ref } from "vue";
import VuePdfEmbed from "vue-pdf-embed";
import javaScriptPdf from "@/assets/javaScript.pdf";

import "vue-pdf-embed/dist/style/index.css";
import "vue-pdf-embed/dist/style/annotationLayer.css";
import "vue-pdf-embed/dist/style/textLayer.css";

const pdfSource = ref(
"http://localhost:8081/8a36320a5811132b30cafd656cb6b75e.pdf"
);
</script>

<template>
<VuePdfEmbed annotation-layer text-layer :source="pdfSource" />
</template>

报错信息

在 vue3 使用时,会报错,提示需要安装 babel 的插件

1
Syntax Error: Class private methods are not enabled. Please add `@babel/plugin-transform-private-methods` to your configuration.

详细如下图

安装@babel/plugin-transform-private-methods 后在 babel 配置文件中添加,重新运行即可

1
2
3
4
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [["@babel/plugin-transform-private-methods"]],
};

总结

根据这次替换 pdf 插件带来的感觉,因为 vue-pdf 已经很久没有更新也不清楚是否还会维护,vue-pdf-embed 同时支持 vue2 和 vue3,在后续项目中可以优先考虑 vue-pdf-embed 来使用