多人博客网站第一版上线啦,欢迎围观,吐槽
时间大概是在两年前吧,那时候刚接触到 nodejs,立志成为一名优秀全栈攻城狮的我,心心念着得有一个属于自己的网站。于是说干就干,一边学着一边捣鼓的各种花样。
曾梦想仗剑走天涯,然而后面因为工作忙就没去成,我的这个网站也因为各种原因迟迟没能上线,这其中也做过了好几个版本,一开始是前后端分离的,前端使用 angularjs(后面又改成了 vue),后端是 nodejs + mongodb,等这一版的也差不多做好的时候,感觉不是自己想要的样子,所以也就没上线。到后面觉得 golang 好玩,又去用它写了一些后端的接口。
到了今年三月份的时候,终于发现网站这个目标确实是拖了很长时间了。抽时间好好想了下自己想要做什么,决定抛弃之前的那套代码,不用自己比较熟悉的 前后端分离,vue 等技术,而使用 ejs 模板渲染,nodejs 的 sequelize orm 库,使用 session 而不是 jwt 来持久化用户登录等。
代码基本上是晚上抽时间写的,后面因为加班,也停了一段时间。中途如果遇到一些问题,有时候进度也会耽搁个几天。再加上一些设计以及前端展示上的修修补补,导致整个项目也是花了比较长的时间。好在是一个萝卜一个坑的慢慢踩了过来,现在觉得网站终于可以上线了,后面要做的工作就是完善网站的一些没有功能(包括后端的管理界面等),修改一些的实现方式(准确的说就是优化代码),以及更重要的是丰富网站内容,也就是写博客记录一些成长路上的风景吧。
到此为止,废话也不多说了,代码也开源到了github上,有兴趣的朋友可以去围观一下,欢迎 start,也欢迎在 issue 里提出,指正各种问题。
说一下项目的运行方式吧,首先要 nodejs 环境,需要全局安装 gulp、nodemon 等包,然后数据存储使用的 mysql + redis。等环境准备好了之后,就需要添加一些配置文件,比如在 config/env/ 下创建 development.js 文件,里面需要提供发邮箱的邮件地址,以及 github 第三方登录的 clientid 等。
module.exports = {
email: {
account: '',
pwd: ''
},
github: {
clientid: '',
clientsecret: '',
callbackurl: ``
}
};
接下来就是安装开发运行所需要的各种依赖包吧,跑一下 yarn install 就行了,这个过程可能需要花个几分钟时间。
等到所有的包都安装完毕后,直接跑 npm start 命令,看到 终端上显示 所有的 gulp 任务 finished,server is running at port 3000 的时候,就可以打开浏览器访问 localhost:3000 了。
接下来贴上一些项目里面的代码吧,如果上不了首页就悲剧了。
1 require('dotenv').config() 2 3 const express = require('express') 4 const passport = require('passport') 5 const models = require('./config/db/model') 6 const port = process.env.port 7 8 const app = express() 9 10 require('./config/passport')(passport) 11 require('./config/express')(app, passport) 12 require('./config/routes')(app, passport) 13 14 models 15 .sequelize 16 .sync() 17 .then(() => { 18 app.listen(port, () => { 19 console.log(`server is running at port ${port}`) 20 }) 21 }) 22 23 module.exports = app
gulp 的打包配置
1 // vinyl 是一个简单的描述文件的元数据对象 2 // https://github.com/gulpjs/vinyl 3 const path = require('path') 4 const gulp = require('gulp') 5 const del = require('del') 6 const glob = require('glob') 7 const babelify = require('babelify') 8 const runsequence = require('run-sequence') 9 const plumber = require('gulp-plumber') 10 const notify = require('gulp-notify') 11 const gulpif = require('gulp-if') 12 const sass = require('gulp-sass') 13 const debug = require('gulp-debug') 14 const cached = require('gulp-cached') 15 const remember = require('gulp-remember') 16 const autoprefixer = require('gulp-autoprefixer') 17 const sourcemaps = require('gulp-sourcemaps'); 18 const size = require('gulp-size'); 19 const cssnano = require('gulp-cssnano') 20 const uglify = require('gulp-uglify') 21 const rename = require('gulp-rename') 22 const htmlmin = require('gulp-htmlmin') 23 const imagemin = require('gulp-imagemin') 24 const browserify = require('browserify') 25 const source = require('vinyl-source-stream'); 26 const buffer = require('vinyl-buffer'); 27 const rev = require('gulp-rev'); 28 const watchify = require('watchify') 29 const lazypipe = require('lazypipe') 30 const revcollector = require('gulp-rev-collector'); 31 const es = require('event-stream') 32 const argv = require('yargs').argv 33 34 // 将打包后的静态资源 放到nginx服务器上 35 const bundleassetsdir = argv.build_mode === 'deploy' && argv.assets_path ? argv.assets_path : './public/static/' 36 const jsassetsdir = path.join(bundleassetsdir, 'js') 37 const cssassetsdir = path.join(bundleassetsdir, 'css') 38 const imgassetsdir = path.join(bundleassetsdir, 'image') 39 const revassetsdir = path.join(bundleassetsdir, 'rev') 40 const htmlassetsdir = path.join('./app/view') 41 42 const autoprefixer_browsers = [ 43 'ie >= 10', 44 'ff >= 30', 45 'chrome >= 34', 46 'safari >= 7', 47 'opera >= 23' 48 ]; 49 50 const jschannel = lazypipe() 51 .pipe(uglify) 52 .pipe(gulp.dest, jsassetsdir) 53 54 let watch = false 55 56 function getentryfiles (path, option) { 57 return glob.sync(path, option) 58 } 59 60 /** 61 * 打包js任务 62 * @param {object} bundle 各入口文件的browserify对象 63 * @param {string} filename 入口文件名 64 * @return {stream} stream 对象 65 */ 66 function jstask ({ bundle, filename }) { 67 return bundle 68 .bundle() 69 .pipe(plumber({ 70 errorhandler: notify.onerror('error: <%= error.message %>') 71 })) 72 .pipe(source(filename)) 73 // 代替 gulp-streamify,来转换 vinyl 流 74 .pipe(buffer()) 75 .pipe(rename({ dirname: '' })) 76 .pipe(sourcemaps.init()) 77 .pipe(debug({ title: 'script' })) 78 .pipe(size({ title: 'script' })) 79 .pipe(sourcemaps.write('')) 80 .pipe(gulp.dest(jsassetsdir)) 81 } 82 83 /** 84 * 如果一个文件被删除了,则将其忘记 85 * @param {*} event 86 */ 87 function watchdel (event) { 88 if (event.type === 'deleted') { 89 // gulp-cached 的删除 api 90 delete cached.caches.scripts[event.path] 91 // gulp-remember 的删除 api 92 remember.forget('scripts', event.path) 93 } 94 } 95 96 gulp.task('style', () => { 97 return gulp.src('./src/scss/*.scss') 98 .pipe(plumber({ 99 errorhandler: notify.onerror('error: <%= error.message %>') 100 })) 101 // .pipe(cached('style-task')) 102 .pipe(sourcemaps.init()) 103 .pipe(sass()) 104 .pipe(cssnano({ 105 // 不修改 z-index 106 safe: true 107 })) 108 .pipe(autoprefixer(autoprefixer_browsers)) 109 .pipe(debug({ title: 'style' })) 110 // .pipe(remember('style-task')) 111 .pipe(size({ title: 'style' })) 112 .pipe(sourcemaps.write('')) 113 .pipe(gulp.dest(cssassetsdir)) 114 }) 115 116 gulp.task('script', () => { 117 let entryjs = getentryfiles('./src/js/*.js') 118 let bundletasks = entryjs.map(filename => { 119 const bundle = browserify({ 120 entries: [filename], 121 cache: {}, 122 packagecache: {}, 123 plugin: [watch ? watchify : null], 124 transform: babelify 125 }) 126 if (watch) { 127 bundle.on('update', function () { 128 jstask.call(null, { bundle, filename }) 129 }) 130 } 131 return { bundle, filename } 132 }) 133 return es.merge(bundletasks.map(jstask)); 134 }) 135 136 gulp.task('image', () => { 137 return gulp.src(['./src/image/*', './src/image/**/*']) 138 .pipe(cached('image-task')) 139 .pipe(imagemin([ 140 imagemin.gifsicle({ interlaced: true }), 141 imagemin.jpegtran({ progressive: true }), 142 imagemin.optipng({ optimizationlevel: 5 }), 143 imagemin.svgo({ 144 plugins: [ 145 { removeviewbox: true }, 146 { cleanupids: false } 147 ] 148 }) 149 ])) 150 .pipe(debug({ title: 'image' })) 151 .pipe(remember('image-task')) 152 .pipe(size({ title: 'image' })) 153 .pipe(gulp.dest(imgassetsdir)) 154 }) 155 156 gulp.task('html', () => { 157 return gulp.src('./src/page/**/*.html') 158 .pipe(cached('html-task')) 159 .pipe(debug({ title: 'html' })) 160 .pipe(remember('html-task')) 161 .pipe(gulp.dest(htmlassetsdir)) 162 163 }) 164 165 gulp.task('rev', () => { 166 return gulp.src([path.join(jsassetsdir, '*.js'), path.join(cssassetsdir, '*.css')]) 167 .pipe(rev()) 168 .pipe(gulpif('*.js', jschannel())) 169 .pipe(gulpif('*.css', gulp.dest(cssassetsdir))) 170 .pipe(rev.manifest({ 171 merge: true 172 })) 173 .pipe(gulp.dest(revassetsdir)) 174 }) 175 176 gulp.task('rev-collector', () => { 177 return gulp.src([path.join(revassetsdir, '*.json'), path.join(htmlassetsdir, '*.html')]) 178 .pipe(revcollector({ 179 replacereved: true 180 })) 181 .pipe(htmlmin({ 182 removecomments: true, 183 collapsewhitespace: true, 184 collapsebooleanattributes: true, 185 removeattributequotes: true, 186 removeredundantattributes: true, 187 removeemptyattributes: true, 188 removescripttypeattributes: true, 189 removestylelinktypeattributes: true, 190 removeoptionaltags: true 191 })) 192 .pipe(size({ title: 'html' })) 193 .pipe(gulp.dest(htmlassetsdir)) 194 }) 195 196 gulp.task('clean', () => del([bundleassetsdir, htmlassetsdir], { force: true })) 197 198 gulp.task('style:watch', () => { 199 const watcher = gulp.watch(['./src/scss/**/*.scss'], ['style']) 200 watcher.on('change', watchdel) 201 }) 202 203 gulp.task('image:watch', () => { 204 const watcher = gulp.watch(['./src/image/**/*'], ['image']) 205 watcher.on('change', watchdel) 206 } 207 ) 208 gulp.task('html:watch', () => { 209 const watcher = gulp.watch(['./src/page/**/*.html'], ['html']) 210 watcher.on('change', watchdel) 211 }) 212 213 gulp.task('watch', () => { 214 watch = true 215 runsequence( 216 'clean', 217 ['script', 'style', 'html', 'image'], 218 ['style:watch', 'html:watch', 'image:watch'] 219 ) 220 }) 221 222 gulp.task('build', cb => { 223 watch = false 224 return runsequence( 225 'clean', 226 ['script', 'style', 'html', 'image'], 227 'rev', 228 'rev-collector', 229 cb 230 ) 231 }) 232 233 gulp.task('default', ['build'])
最后再一次贴上网站以及开源地址,欢迎各路大佬围观,吐槽,指正。
推荐阅读