xzgz.top 網站記錄
主要技術棧
應用主要是由 koa 構建,其中 nextjs 只負責頁面構建完成服務端渲染。以下是主要應用的技術
後台
- Koa:網站主題內容
- koa-router:網站後端路由
- koa-session:應用的 session 寫入
- nodemailer:發送郵件
- mysql2:數據庫操作
- nodemon:開發模式下熱更新
- typescript
- ts-node:完成開發模式下運行 ts 文件
- tsconfig-paths:修復開發模式下 tsconfig.json 里路徑映射在 ts-node 下不起作用。
前台
- Nextjs:react 的服務端渲染
- mobx:前端應用的全局狀態管理
- sass:前端應用的樣式
- github api:完成筆記儲存,及markdown文件轉html功能
部署
- 利用github的webhook進行自動化部署。
-
webhook觸發後執行shell腳本完成自動拉代碼、docker構建、運行應用。
#!bin/sh exitError() { echo "---starting $1---" $1 if [ $? -eq 0 ]; then echo "---$1 successfully---" else echo "!!!!!!!!!!$1 error!!!!!!!!!!" exit 0 fi } echo "==========starting rebuild==========" cd /root/xzgz exitError "git pull" exitError "docker build -t xzgz_i ." docker rm -f xzgz_i docker stop xzgz_c docker rm -f xzgz_c exitError "docker run -d -p 3000:3000 --name xzgz_c xzgz_i" echo "==========end rebuild=========="
遇到的一些問題
tsconfig.json 里路徑映射問題
開發模式下:
vscode 讀取 tsconfig.json 裏的路徑映射沒有問題,引用不會報錯,但是 ts-node 啓動腳本時會出現路徑映射丟失的問題,解決方法是使用 tsconfig-paths:
# 使用以下命令開啓腳本
npx ts-node -r tsconfig-paths/register --project ./tsconfig.json app_server/index.ts
在 nodemon 的配置文件 nodemon.json 裏設置
{
"watch": ["app_server/", "types/"],
"ext": "js,json,ts",
"execMap": {
"ts": "ts-node -r tsconfig-paths/register --project ./tsconfig.json"
}
}
生產模式下:
直接使用 ts-node 啓動服務器不適合生產模式,使用 tsc 將 ts 文件編譯成 js 文件進行運行。
tsc --project tsconfig.prod.json
生成的文件執行時和上面有同樣的問題,因為 tsc 編譯時不會對路徑映射進行編譯,直接執行會報錯找不到模塊。
按照 tsconfig-paths/register 的解決方法,在運行腳本之前對 Module.\_resolveFilename 進行重寫,達到解析路徑映射的問題。具體代碼:
const Module = require("module");
const path = require("path");
// 具體的路徑映射根據實際情況改動
const replaceUrl = /^@server\//;
function findModel(request, _module) {
const pathArr = request.replace(replaceUrl, "");
let i = _module;
while (i) {
const filename = path.resolve(i.path, "../");
if (/app_server$/.test(filename)) {
break;
} else {
i = _module.parent;
}
}
return path.join(i.path, "../", pathArr);
}
function preRunApp() {
var originalResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (request, _parent) {
if (replaceUrl.test(request)) {
const findModule = findModel(request, _parent);
return originalResolveFilename.call(this, findModule);
}
return originalResolveFilename.call(this, request, _parent);
};
}
preRunApp();
然後在執行腳本時使用命令
node -r ./preRunApp.js dist/app_server/index.js
使用 nodemailer 發送 163 郵箱時超時
nodemailer 使用 163 郵箱發郵件時遇到超時的問題,主要是因為使用 163 郵箱發送郵件時必須開啓 secure:
transporter = nodemailer.createTransport({
host: "smtp.163.com", // 第三方郵箱的主機地址
port: 465, // 25/465, secure為true - 465;false - 25
secure: true, // 163郵箱必須為true,outlook郵箱必須為false
auth: {
user: SENDER, // 發送方郵箱的賬號
pass: SMTP_CODE, // 郵箱授權密碼
},
});
生產環境請求時間數據和開發環境請求同樣的數據不一致(docker 容器和系統時區不一致)
應用運行在 docker 容器中,docker 容器的日期和系統的不一致。
在 Dockerfile 裏配置將系統使用時區拷貝進 docker 容器中,具體如下:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
生產環境請求接口的頁面會報錯,提示水合失敗,開發環境沒有這個問題
這個問題也是由上面的問題影響的。docker 容器裏 react 的脱水執行生成時間的太平洋時間。而到了客户端注水的時候生成東 8 區時間,導致服務端和客户端渲染不一致,因此報錯。
nextjs 中使用 mobx 服務端和客户端數據無法保持一致
因為 mobx 在服務端初始化時生成的數據沒有被傳到客户端,所以需要客户端也會完成一次初始化。某些頁面需要提前在 store 裏調用方法完成服務端渲染。
那麼需要手動的完成 mobx 的注水過程(類似於服務端渲染的脱水注水)。使用如下方法解決:
// pages/_app.ts
function MyApp({
Component,
pageProps,
store, // 重點
}: AppProps & { store: AppStore }) {
// 注水
const _defaultStore = initializeStore(store);
return (
<MyAppContext.Provider value={_defaultStore}>
<WithLayout {...pageProps} store={_defaultStore} />
</MyAppContext.Provider>
);
}
MyApp.getInitialProps = async (appContext: AppContext) => {
const { Component, ctx } = appContext;
// 服務端完成初始化
const store = initializeStore();
ctx.store = store;
let appProps = {};
if (Component.getInitialProps) {
appProps = await Component.getInitialProps(ctx);
}
return { pageProps: appProps, store };
};
// store/index.ts
class Store {
this.user = null
// ...
// 補水
hydrate(initState: Store) {
Object.assign(this, initState);
}
}
let store: Store;
function initializeStore(initDate?: Store): Store {
enableStaticRendering(typeof window === "undefined");
const _store = store ?? new Store();
if (initDate) {
_store.hydrate(initDate);
}
// 客户端渲染總是new一個新對象
if (typeof window === "undefined") return _store;
if (!store) store = _store;
return _store;
}
export { initializeStore, store };
剩餘問題
現在session沒有寫入持久層,每次重新構建導致所有用户登錄失效。
歡迎大佬來我的網站留言,討論問題。