Timeless
花开成景,花落成诗
从一开始的WordPress,再到现在自个用Node写博客,我为啥这么喜欢折腾呐,也许是因为闲的慌吧
一开始使用WordPress,觉得有些臃肿,PHP会的不多改起来有点麻烦。后面换成Hexo,支持Markdown静态优秀,不过动态功能像评论、文件管理得自己实现,不如自个写一个。
业余时间快速开发,计划主体功能2-3个月内完成,实际上因为拖延症陆陆续续做了大半年。
Express、Koa 都是非常优秀的框架,简单且扩展性强,非常适合做个人项目,但框架本身缺少约定,基础设施需要寻找或自己搭建。而我的初衷是快速搭建,所以我把目光转向阿里巴巴团队的Egg.js和360团队的Thinkjs,两者中文文档详细容易扩展,社区资源丰富。
Egg.js 选择了 Koa 作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。
Thinkjs,框架底层也是基于 Koa 2.x 实现,相对于Egg.js来说,相对于Egg.js它处于更上层,封装了很多常用的功能,符合快速搭建一个博客的初衷,所以我选用了Thinkjs。
2 年前入坑 thinkjs ,现在有没有必要改换 eggjs ?
更新于2021-10-08:18年选用Thinkjs的时候,它和Egg.js差不多,其实两个都能满足博客应用的需求,虽然它不更新了,但是已经相对稳定,所以暂时不会闲着更换成Egg.js,后续的新项目再考虑Egg.js
因为前后端都是使用javascript开发,前台(nuxt-ssr模式)、后台(nuxt-spa模式)都是用element-ui+vue,所以考虑放到同一个项目,使用一个node_modules。

client 前台
├── admin 内容管理平台
| ├── api 整合管理所有的api接口
| ├── layouts 外部容器
| ├── middleware
│ | └── permission.js 登录鉴权
| ├── plugins
│ | ├── api.js 使上下文能使用api请求
│ | ├── axios.js axios全局拦截器
│ | ├── element-ui element-ui按需引入
│ | └── svg-icon 注册svg icon组件
| ├── store vuex
| ├── styles 公共样式
| ├── utils 工具集
| ├── views 页面组件
| └── router.js @nuxtjs/router路由配置
├── common 内容管理平台和博客前台功能模块
├── front 博客前台
| ├── layouts 外部容器
│ | ├── app.vue 工具页面使用的外部容器,没有菜单
│ | └── default 默认容器,有菜单
| ├── pages nuxt页面
| ├── plugins
│ | ├── axios.js axios全局拦截器
│ | ├── element-ui element-ui按需引入
│ | └── filters 注册全局过滤器
| ├── store vuex
│ | └── index.js nuxtServerInit获取菜单及系统配置
| ├── vendor 第三方库
│ | ├── aplayer 音乐播放器
│ | ├── live2d Live2D Cubism SDK v2看板娘
│ └── └── spine-ts spine骨骼动画
├── config 配置
| ├── adapter.model.js 数据库配置
│ ├── nuxt.admin.js 内容管理平台nuxt配置
│ └── nuxt.front.js 博客前台nuxt配置
├── logs 日志文件
├── runtime
| ├── cache 缓存
| ├── session 会话信息
├── src 后台
| ├── config 配置
│ | ├── adapter 数据库、缓存、日志等配置
│ | ├── middleware 中间件配置
│ | | └── nuxt.js nuxt中间件
│ | ├── extend.js thinkjs扩展配置
│ | └── router.js thinkjs路由
│ ├── controller 控制器
│ | ├── admin 内容管理平台api
│ | └── front 博客前台api
│ ├── logic 校验层
│ ├── model 模型,数据库操作
│ ├── service 服务,可复用,抽离controller逻辑
├── views
│ ├── rss.xml rss模板
│ └── sitemap.xml 网站地图模板
├── www 静态资源,nuxt打包目标目录
│ └── upload 上传目录
├── development.js thinkjs开发环境入口
├── nuxt.config.js
├── package.json
├── production.js thinkjs生产环境入口
nuxt作为node中间件使用Renderer处理和服务所有 SSR 和资源请求,开发环境先Builder然后再渲染,生产环境直接使用nuxt build构建好的资源进行渲染
const { Nuxt, Builder } = require('nuxt');
module.exports = options => {
const config = require('config/nuxt.front.js'));
config.dev = options.isDev;
const nuxt = new Nuxt(config);
if (options.isDev) {
new Builder(nuxt).build();
}
const middleware = async(ctx, next) => {
// Default 404
ctx.status = options.status || 200;
ctx.req.session = await ctx.session();
await nuxt.render(ctx.req, ctx.res);
let err = null;
return next().catch(e => {
err = e;
}).then(() => {
if (err) {
return Promise.reject(err);
}
// 如果后续执行逻辑有错误,则将错误返回
return new Promise((resolve, reject) => {
return { resolve, reject };
});
});
};
return middleware;
};
front、admin都用nuxt,根据命令里面是否包含--admin,获取对应的配置
nuxt.config.js
const adminConfig = require('./config/nuxt.admin.js')
const frontConfig = require('./config/nuxt.front.js')
const isAdmin = process.argv.includes('--admin')
module.exports = isAdmin ? adminConfig : frontConfig
admin不需要SEO,所以使用nuxt的spa模式
config/nuxt.admin.js
const path = require('path')
const isPro = process.env.NODE_ENV === 'production'
const srcDir = 'client/admin/'
function resolve(dir) {
return path.join(__dirname, '../', srcDir, dir)
}
module.exports = {
alias: {
'#': resolve('../common')
},
axios: {
proxy: true,
prefix: '/admin'
},
build: {
babel: {
plugins: [
[
'component',
{
'libraryName': 'element-ui',
'styleLibraryName': 'theme-chalk'
}
]
]
},
extractCSS: true,
extend(config, ctx) {
// set svg-sprite-loader
const svgRule = config.module.rules.find(rule => rule.test.test('.svg'))
svgRule.exclude = [resolve('assets/icons/svg')]
// Includes /icons/svg for svg-sprite-loader
config.module.rules.push({
test: /\.svg$/,
include: [resolve('assets/icons/svg')],
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]'
}
})
},
publicPath: '//cdn.timelessq.com/nuxt-admin/' // 只需将www/admin上传cdn
},
buildDir: 'www/nuxt-admin',
buildModules: [
'@nuxtjs/router'
],
css: [
'@/styles/index.scss'
],
plugins: [
'@/plugins/axios',
'@/plugins/api',
'@/plugins/element-ui',
'@/plugins/svg-icon'
],
generate: {
dir: 'www/nuxt-admin',
fallback: 'index.html'
},
head: {
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' }
]
},
modern: isPro, // 现代模式
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
proxy: {
'/admin': {
target: 'http://127.0.0.1:8360', // 目标接口域名
changeOrigin: true // 表示是否跨域
}
},
render: {
compressor: false // 禁用中间件压缩
// resourceHints: false // 添加prefetch并preload链接以加快初始页面加载时间。
},
router: {
middleware: ['permission']
},
server: {
port: 9528
},
srcDir,
ssr: false,
target: 'static',
telemetry: false // 关闭收集遥测数据
}
front前台就需要开启SSR
config/nuxt.front.js
const path = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
const isPro = process.env.NODE_ENV === 'production'
const srcDir = 'client/front/'
function resolve(dir) {
return path.join(__dirname, '../', srcDir, dir)
}
module.exports = {
alias: {
'#': resolve('../common')
},
buildModules: [
'@nuxtjs/color-mode',
'@nuxtjs/style-resources'
],
build: {
babel: {
plugins: [
[
'component',
{
'libraryName': 'element-ui',
'styleLibraryName': '../packages/theme-chalk/src',
'ext': '.scss'
}
],
[
'prismjs',
{
'languages': ['markup', 'css', 'javascript', 'json', 'less', 'scss', 'shell', 'typescript'],
'plugins': ['show-language', 'highlight-keywords', 'toolbar'],
'css': false
}
]
]
},
extractCSS: true,
plugins: [
new CompressionPlugin({
test: /.(js|css|woff|ttf)$/, // 匹配需要压缩的文件后缀 看需求
threshold: 10240 // 大于10kb的会压缩,默认为0
})
],
publicPath: '//cdn.timelessq.com/nuxt-front/dist/client' // 只需将www/front上传cdn
},
buildDir: 'www/nuxt-front',
css: [
'@/styles/global.scss'
],
head: {
meta: [
{ charset: 'utf-8' },
{ name: 'renderer', content: 'webkit' },
{ name: 'viewport', content: 'width=device-width,initial-scale=1,shrink-to-fit=no' }
]
},
modern: isPro, // 现代模式
modules: [
'@nuxtjs/axios'
],
plugins: [
'@/plugins/element-ui',
'@/plugins/axios',
'@/plugins/filters'
],
render: {
compressor: false // 禁用中间件压缩
},
srcDir,
styleResources: {
scss: resolve('/styles/element-variables.scss')
},
telemetry: false // 关闭收集遥测数据
}
Power by Node.js + Nuxt.js,由 腾讯云提供服务, 已经稳稳地存活了 8年95天
Copyright © 2018 - 2026 Timeless. All rights reserved.桂ICP备18003656号-1桂公网安备45092102000147号
评论