webpack5, vue3,vue-cli5 使用 prere
背景
环境: webpack5, vue3,vue-cli5
插件: prerender-spa-plugin
报错: [prerender-spa-plugin] Unable to prerender all routes!
ERROR Failed to compile with 1 error
error [prerender-spa-plugin] Unable to prerender all routes!
ERROR Error: Build failed with errors.
两种解决方案
-
node_modules\prerender-spa-plugin\es6\index.js:60
中
把compilerFS.mkdirp
函数改成compilerFS.mkdir
。
- 改用prerender-spa-plugin-next插件
注意这个插件配置项和prerender-spa-plugin
不太一样,不能照抄。
故事开始
某天,我在愉快地学(摸)习(鱼),突然又双叒叕被抓去铲屎(T_T),要把之前做的一个东西加入SEO。
然鹅,这个是静态项目,没有跑在node上,也就(没)用(时)不(间)了(改)ssr...
行叭,Prerender 走一个~
然后...我就收到报错大礼包一份。
error.png 吓得我立马跑去检查配置文件,并自我怀疑:作为一只 CV
攻城狮,配置文件这玩意通常都是老项目拉过来的,除了开荒的时候改过,后面基本都不会再动它,应该没什么问题把...
然后发现配置好像确实没什么问题,场面一度十分尴尬。
好吧,人肉 debug 开始!我熟练地点进 prerender-spa-plugin 里(熟练到让人心疼)
一顿搜索 Unable to prerender all routes 出来两个文件调用,分别是 ES5、ES6 的规范,到 index.js
瞄一眼,就能发现 Node.js 版本在8.0
以上使用 ES6 版。
戳进去发现,抛出的并不是原始错误,原始错误已经被吃掉了(这...)。
code.png
打个断点,发现是函数 compilerFS.mkdirp
调用出错。
一键飞过去看看调用的地方,发现 compilerFS
其实就是 compiler.outputFileSystem
,里面确实是没有这个方法,熟悉 Node.js
和 webpakc
的大佬们这时候脸上应该已经露出了和煦的微笑。
作者很贴心的给了个mkdirp-promise的链接,戳进去就能看见这么一句话:
mkdirp-promise 已经停更3年,并且作者近期发话已经弃用,也用不了了。如果你使用的是最新的 Node.js 那么直接用 fs.mkdir
就行了。
现在,把node_modules\prerender-spa-plugin\es6\index.js
第 60 行compilerFS.mkdirp
函数改成compilerFS.mkdir
。
// From https://github.com/ahmadnassri/mkdirp-promise/blob/master/lib/index.js
const mkdirp = function (dir, opts) {
return new Promise((resolve, reject) => {
// compilerFS.mkdirp -> compilerFS.mkdir
compilerFS.mkdir(dir, opts, (err, made) => err === null ? resolve(made) : reject(err))
})
}
再来一次~
success.png完结撒花~~~
故事背后的故事
compiler.outputFileSystem
其实就是 Node.js 的输出文件系统,调用的是 Node.js 的 fs
模块,
fs.mkdir
这包是给早期 Node.js 递归创建文件夹,并且返回 Promise 用的,现在的 Node.js 早已经支持递归,并且可以返回Promise。所以这个包已经不需要了,直接用fs.mkdir
就行了。
prerender-spa-plugin配置
// vue.config.js
const path = require("path")
const PrerenderSPAPlugin = require("prerender-spa-plugin")
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {
// ...
configureWebpack(config) {
if (process.env.NODE_ENV === "production") {
const prerenderCfg = new PrerenderSPAPlugin({
indexPath: path.join(__dirname, "dist", "index.html"),
staticDir: path.join(__dirname, "dist"),
outputDir: path.join(__dirname, "dist", "prerender"),
routes: ["/", "/about"],
// 自定义触发预渲染的事件
// SPA根元素挂载之后 document.dispatchEvent(new Event("prerender"))抛出 prerender 事件触发预渲染
renderer: new Renderer({ renderAfterDocumentEvent: "prerender" })
})
config.plugins.push(prerenderCfg)
}
}
}
prerender-spa-plugin-next 配置
饮茶啦喂.jpg
三点几了!饮茶先啦~~
本萌新先去饮茶啦~prerender-spa-plugin-next
的配置先咕了,下次再讲
2021.5.17,补充 prerender-spa-plugin-next
配置
const path = require("path")
const PrerenderSPAPlugin = require("prerender-spa-plugin-next")
// 可选
const renderer = require("@prerenderer/renderer-puppeteer")
module.exports = {
lintOnSave: "warning",
publicPath: "/",
assetsDir: "static",
configureWebpack (config) {
if (process.env.NODE_ENV === "production") {
const prerenderCfg = new PrerenderSPAPlugin({
routes: ["/", "/about"],
// 可选
renderer,
postProcess (context) {
context.outputPath = path.join(
// 不要拼__dirname,最终会转成绝对路径,拼了反而出错。
// PS: 为什么要加一层文件夹? 因为默认写入首页叫index.html, 不加会导致写入冲突 ——_——#
"rerender",
(context.route.replace("/", "") || "index") + ".html"
)
return context;
},
renderOptions: {
renderAfterDocumentEvent: "prerender",
},
})
config.plugins.push(prerenderCfg)
}
},
}