大家好,我是右子。
nuxt3现在已经正式发布nuxt@3.0.0版本了,之前的都是rc版本,很多功能相比之下比较稳定。
最近想把站点的文章页做一下SSG(Static Site Generation),也为了网站提升缓存使用率和SEO优化。
过程中遇到了一些坑,在这里也分享一下我的趟坑之旅。
前言
工具准备
- nuxt3
- node
- shell
- webpack
- vscode
安装nuxt3
执行以下命令
# 使用npx安装
$ npx nuxi init <project-name>
# 如果用pnpm安装
$ pnpm dlx nuxi init <project-name>
你可能因为网络的问题无法完成下载,那么你也可以到这里下载zip包。
nuxt3的教程不多说,去官网看就行,目前中文版还不全面,建议去官网看:nuxt.com/。
安装node
node官网下载:nodejs.org/zh-cn/
选择稳定版本即可。
安装完node你就会拥有npm、npx,如果你想使用pnpm,可以通过npm命令安装pnpm。
正题开始
原本我有个人博客,随着vue3的推广并且版本的稳定,我的博客也重构了一下。内容页相关的数据,迁移到了阿里云的RDS和OSS里。
本着前端的“同构”理念:一套代码多端使用。所以选择了nuxt3来完成SSG的工作。
首先我的基于webpack5的前端工程:
我的想法是这样的:
graph TD
中台 --> 工具平台 --> 前端工程
工具平台 --> nuxt3工程
在中台场景中,开发效能工具,用于解决工程与工程之间的协作。
功能类比:代码转译(es6->es5/vue code->minipragram code),反编译,文件类型转换等等。
我的 主要目标
是站点 文章做SSG
。
下面我们来拆解的看下每一步如何做。
我需要我的工具可以将vue工程的项目文件,一键转到nuxt工程中。
在我的工具平台中操作:
会生成配置:
{
// 我的vue工程Git地址
project: git@github.com:host166/powerful.git
// 我的nuxt工程Git地址
target: git@github.com:host166/nuxt3.git,
// 功能之一:拷贝
mode: "copy"
}T
我的程序会根据 mode
类型进行工作,大致如下。
cd /Users/**/**/works
$ git clone git@github.com:host166/powerful.git
$ git clone git@github.com:host166/nuxt3.git
$ cp -rf powerful/pages powerful/components powerful/shared powerful/composables powerful/assets nuxt3
$ rm -rf powerful
# 处理一些语法转译,比如<router-view />转成<NuxtPage />、去掉一些import引入等等
$ node astVue2Nuxt.js
$ cd nuxt3
$ pnpm i
我这里用
AST(abstract syntax code)
做的处理,代码就补贴出来了,工具用的是:esprima
和estraverse
。
不知道AST是什么的小伙伴可以搜索一下,这里推荐一个在线看ast结构的网站;
我就得到了一个nuxt3工程目录:
接下来,打开 package.json
可以看到 generate
命令。
{
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate", // 生成静态页面用的
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
}
}
nuxt3有一个规则:“约定大于规范”
。他的路由规则是pages文件夹里存在文件的话,在 npm dev
可以通过 local/路径访问。
接下来我需要把数据库中的文章数据导入到nuxt的pages里,让他可以构建打包出我想要的静态资源,我的方案如下:
// 1. 在js中获取100条列表数据,得到文章的id号;
// 2. 生成pages/atricle/下对应[id].vue文件;
const needle = require("needle");
let listUrls = [...]; // 请求列表的url地址,是一个集合。最好是分页获取,不然数据太大了内存回满。
let sh = [];
function concurrencyRequest(urls, maxNum=1) {
// 验证类型
if (!Array.isArray(urls)) {
throw "urls must be an array type";
}
return new Promise((resolve) => {
if (!urls.length) {
resolve([]);
return;
}
const results = [];
let page = 0; // 请求的下标志
let count = 0; // 当前请求的完成数量
async function req() {
// 请求结束
if (page === urls.length) {
return;
}
let index = page;
let url = urls[page];
page++;
try {
// 接口获取
const resp = await axios.get(url);
results[index] = resp; // resp加入到results
} catch (e) {
results[index] = err; // 错误信息加入到results
} finally {
// 判断是否所有请求都已完成
count++;
if (count === urls.length) {
resolve(results);
}
req();
}
};
// 使用Math.min来避免数组长度小于maxNum。
for (let i = 0, len = Math.min(maxNum, urls.length); i < len; i++) {
req();
}
});
};
// 执行并发请求
requestConcur(listUrls,10).then(res=>{
let ids = res.data; // 得到所有id
// 生成vue文件,可以通过shell脚本创建软连来完成。
ids.forEach(code=>{
let template = `${process.cwd()}/pages/article/index.vue`;
let target = `${process.cwd()}/pages/article/${code}.vue`
sh.push(`ln -s ${template} ${target} \n`);
});
// 创建文件,serverPath是自己指定你的一个目录
fs.writeFileSync(`${serverPath}/softln.sh`, sh.join(""), {
encoding: "utf8",
flags: "a",
mode: 438
});
});
得到一个sh脚本文件后,执行:
$ sh 目录/softln.sh
会得到以下信息,并可以开始 npm generate 了。
在打包之前,我们要先配置好 nuxt.config.ts
文件,我的配置是这样的。
// 详情配置查看:https://nuxt.com/docs/guide/directory-structure/nuxt.config
export default defineNuxtConfig({
app: {
// baseURL: "", // 执行generate得注销这句,有影响。
head: {},
cdnURL: "http://xxxxx.com/assets/xxx/" // 配置一个绝对地址,指向资源存放目录,类似于webpack中的output.publicPath
},
// 官方有介绍这里的配置,不配置则使用官方自定义的配置。
webpack: {}
});
在 npm generate
的之后,对应pages下的文件目录打包产出:
打完包的资源,如果用的是vscode软件,可以安装Live Server
预览一下是否正常显示。
会看到预览是正常的,我们打开开发者工具
会发现有一个_payload.js文件404。这个文件地址与我们配置cndURL没有关联,官方给的信息也很少,他的作用就是在模板上生成我们请求的数据做静态化。nuxt payload
引入路径也有问题,因为并不是对应域名的根目录下。
<link rel="modulepreload" href="/article/15232e7574/_payload.js" />
<script type="module">import p from "/article/15232e7574/_payload.js";</script>
解决他!
建议直接修改打包出来的dist/_nuxt/index.html文件,修正_payload.js的引入。
// 去掉html文件里无效的、重复的style标签和内容
const fs = require("fs");
const path = require("path");
const tools = require("./utils/tools");
// 清理一些数据
function emptyHtmlStyleTag(path) {
let pwdPath = tools.resolve(path);
fs.readdir(pwdPath, (err, files) => {
if (err) {
throw err;
}
files = files.filter(it => !['_nuxt'].includes(it));
for (let i = 0, len = files.length; i < len; i++) {
let item = files[i];
let dirPath = `${pwdPath}/${item}`;
let stats = fs.statSync(dirPath);
let isDir = stats.isDirectory();
let isFile = stats.isFile();
if (isDir) {
emptyHtmlStyleTag(dirPath);
} else if (isFile) {
let _regext = new RegExp(`(?<=(\\/))(index)+?(\\.html)$`, 'gi');
let _p = (dirPath.match(_regext) || []);
if (_p.length) {
let content = fs.readFileSync(dirPath);
content = content.toString();
// _payload.js正确指向
content = content.replace(/(?<=(href="))[\/\_\.0-9a-zA-Z]+?(_payload\.js)/gi, "./_payload.js");
content = content.replace(/(?<=(from "))[\/\_\.0-9a-zA-Z]+?(_payload\.js)/gi, "./_payload.js");
fs.writeFileSync(dirPath, content, {
encoding: "utf8",
flags: "a",
mode: 438
});
// console.log(content);
}
}
}
});
};
emptyHtmlStyleTag("./dist/");
当你修正之后,去预览你的index.html会发现router的问题:
因为我们使用了<NuxtPage>,启动了路由机制,所以静态页面里会加载这个路由规则。还记得我们生产之后的目录结构吧?
文件里有个[id]/index.html,这样就跟nuxt的pages约束不一致了(我觉得这是nuxt的一个没考虑到的问题)。
找到 /dist/_nuxt/entry.[hash].js
并修正一下。
// entry.[hash].js
(Qo=y==null?void 0:y.path)!=null?Qo:"/article/0169807e2f/index.html",file:"/Users/path/Desktop/works/nuxt/pages/article/0169807e2f.vue",children:[],meta:y,alias:(y==null?void 0:y.alias)||[]
// 修正代码
function changeFileContent(path) {
let pwdPath = tools.resolve(path);
fs.readdir(pwdPath, (err, files) => {
if (err) {
throw err;
}
files = files.find(item => /(?<=(entry\.))[A-z0-9]+?(?=\.js)/gi.test(item));
let filepath = `${pwdPath}/${files}`;
let stats = fs.statSync(filepath);
let isFile = stats.isFile();
if (isFile) {
fs.readFile(filepath, (err, resp) => {
let text = resp.toString();
text = text.replace(
/(?<=\")(\/article)[\/0-9a-zA-Z]+?(?=\")/gi,
(p => {
return `${p}/index.html`;
})
);
fs.writeFileSync(filepath, text, {
encoding: "utf8",
flags: "a",
mode: 438
});
});
}
})
};
changeFileContent("./dist/_nuxt/");
我们再结合nginx,配置一下代理的转发,就可以完成了。
location ^~ /article/ {
# add_header Content-Type text/html;
# add_header Content-Disposition inline;
# https://xxx.com/prod/0169807e2f/index.html
proxy_pass https://xxx.com/prod/article/;
access_log off;
}
效率
我们说了很多工作流程,如果一条命令一条命令的执行,必然太繁琐了,不符合人类对效率的追求。所以我们把命令写到一个sh文件中,可以一键执行任务。
# 删除文件夹,省的有缓存。
rm -rf ./dist ./.output/public/
# 获取id,创建软连。
npm run softConnection
# 创建[id].vue
sh services/shell/softlink.sh
# 执行程序:generate
npm run generate --appname=blog --appenv=prod
# 修复 _payload.js指向
npm run delsty
# 补齐文件
cp -p services/template/vue3/_payload.js dist/_nuxt/_payload.js
# 修改nuxt路由
npm run cfc
# 上传oss
npm run pushoss --appname=blog --appenv=prod
# 删除软连
sh services/shell/delSoftLink.sh
至于什么时候执行这些事务,可以结合自身需求。
如果想在部署环境中执行,建议通过事务触发,或者分出一台小机器,做定时任务跑一下。
伙伴们也可以结合docker使用,还不会docker使用的小伙伴可以看下我这篇文章:docker命令操作;
可以看下我的博客效果:传送门;
希望可以帮到遇到类似问题的小伙伴们。
补充
- 如果想在nuxt3里面使用
window、document
的api需要通过process.client||process.server
来判断。
推荐文章
今天的文章带你趟坑儿 nuxt3@3.0.0 generate分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15892.html