摘要:博主目前的這個項目還不算很大,模塊依賴簡單,但期望完成諸如版本號替換,壓縮代碼,合并文件,發布到服務器等和模塊化關系不大的工作,所以使用了。同時,對和附加緩存,配合和版本號實現服務器更新,這一部分其實已經幫我們實現好了。
經常在各種論壇、博客還有 github 上活躍的朋友不難發現,許多大牛都有自己的網站,也多以博客為主。博主作為一個立志前端的大白,難道不應該和大牛學習么?UI設計 和 架構設計說干就干,前端部分和 web 開發博主做了很多學習和總結,不少也寫成了博客。對于后端,博主不敢說完全沒有經驗,但接觸的也都比較簡單。于是乎,博主去年六月底開始看 Node 和 Express,利用空閑時間做了自己的博客,現階段還有許多不足,需要后續不斷改進。不過這不妨礙博主先總結一下自己的感受。
博主深刻的感受到自己并沒有什么藝術細胞,只能去看各種設計稿刺激一下自己的神經,比如 Dribbble、water Chen UI、 千庫、 DotSpin、LogoSpire、Tencent ISUX 等等。其實不止這些,比較我看見的鏈接就會點進去,不知道點了多少,然后就這么憋出了一份入門級都算不上的 UI 設計圖(不要笑,要指點)。
對,就是這么簡單粗暴,連 ps 都沒用!
架構設計部分也沒有太深入的知識,簡單的 MVC 結構,對于架構入門來說再適合不過了,即使現在的前端,有了 MV 結構的 React,MVVM 的 Angular 和 vue。架構如下圖,這次終于用到 ps,其實找個 word 就可以畫的。
(還是這句話:不要笑,要指點)
由于自己還沒有看到Angular、vue、React這寫流行的框架,前端樣式的實現還是選擇使用了 Bootstrap 和 Sass。而結構選擇了 jade 模板引擎。這里會有以下幾個問題:
為什么使用 Bootstrap ?
這個不復雜,畢竟我希望實現兼容移動端和 PC 端的響應式頁面,所以我只能說,媒體查詢是利器,Bootstrap 是別無可選。
為什么我使用 Sass ?
寫習慣了 css 就不太習慣 Scss 的縮進,畢竟還有 less 和 Sass, 而選擇了 Sass 的原因如下:
Sass有一些成熟穩定的框架,如是Compass,Foundation,側面說明 Sass 比 Less 更好吧。
還有一個原因是國外討論Sass的同行要多于Less,使用 Sass 的也更多一些。
Sass有更清晰的語法,像一門編程語言一樣,不像 Less 的混合大法使用起來感覺不舒服。
如果說 Sass 有么不好,那就是它還是建立在 ruby 上,有點不直接,比較麻煩。
為何使用 jade ?
這個不需要太多理由,比 html 簡潔,還比 html 強大!比起其他模板引擎,他又是專門為 html 服務的。和 EJS 相比,jade 支持模板繼承,而 EJS 不支持。
其實我是個人開發,如果是團隊開發,我還是覺得 EJS 會更好,可能是博主適應了挖坑的寫法,用 html 會對前后端開發都方便許多。而且 jade 的縮進風格跨 IDE 會不方便。當然還有其他的模板引擎,比如 swig,對博主來說完全是不習慣 Django 的風格。當然這里還有個其它原因:Express 默認 jade !!!
后端開發使用了 Nodejs + Express + MongoDB, 不得不說這個組合的資料真的很多,學習起來方便一些。這里沒有為什么,就是想學 Node,而不是世界上最美的語言,畢竟有 js 基礎嘛。
為何使用 MongoDB ?說實在的,這是博主第一次接觸并使用非關系數據庫,突然發現很喜歡它。之前使用的更多的是 MySQL。畢竟博客系統,存儲的數據是文章,所以關系數據庫定義數據類型實在是個坑,分配大了浪費,分配小了怎么寫東西!當然,還有其他的問題。由于我發現許多文章都使用 MongoDB,其實也就沒有再猶豫。這里想要說的還是使用它的感受,一句話概括:JSON 的存儲方式真的是既直接,又爽快!
為何使用 Express ?
我們都知道,Express 其實不能說是對 Node 的二次開發,而是一個擴展。博主開始學習前端,從基礎做起來是不會有錯的,如果你希望快速的開發,僅僅當它是個工具,那用 sail 會省事很多,再配合腳手架,事半功倍。當然 sail 除了官方文檔好像也沒啥別的可以學習了,這個......
其實 sail 發展不起來也是有原因的:當然,如果有時間,博主倒還想學習一下,koa 和 hapi
最后一個選擇就是項目構建工具了,這里可以直接解釋我為何使用了 Gulp
為什么選擇 Gulp ?項目的整體流程其實這里可選的常用構建工具還有 webpack 和 grunt。gulp 和 webpack 其實各有優勢,比如 gulp 任務定義和管理,Webpack 做不到,Webpack 基于模塊的依賴構建。gulp 也做不好。而 grunt 的確是比較笨重復雜,明顯能看出來現在用的已經比較少了。
其實 gulp 和 webpack 使用的感覺也不一樣,gulp 是在寫一個個的任務,而 webpack 是在寫一個配置文件,注意我說的是感覺。
從他們的核心功能來講,Gulp 定義任務和阻止,基于文件流的構建。而 webpack 按照模塊的依賴構建目標文件,具有支持不同模塊的 loader 體系。如果要用 Webpack,請確保項目模塊化,模塊之間充分依賴。除此之外的構建工作,都應該交給 gulp 繼續完成。博主目前的這個項目還不算很大,模塊依賴簡單,但期望完成諸如版本號替換,壓縮代碼,合并文件,發布到服務器等和模塊化關系不大的工作,所以使用了 gulp。博主使用 Gulp 也是自己的學習過程,正是因為 Gulp 和 Webpack 各有優缺點,下一步博主還是計劃能用 Webpack 重新構建一下這個項目,親自感受一下它們的魅力。
確定功能,根據需求選擇合適的框架和工具
設計界面、架構、數據庫的模式
構建 express 的服務器框架和目錄結構
設計 gulp task 和 livereload
編寫頁面
測試功能模塊
做必要的性能優化
遇到的難點和坑總結這個問題,博主真的感覺好為難。以博主的性格,做之前會覺得:“這個東西沒做過,好難,但查查資料今天上午一定能做出來”,做完了以后總覺得:“沒想到挺簡單的!!!”所以說呢,現在做完了,感覺都不難了。
其實最難的應該屬于快速學習并使用這些工具,快速查找并解決問題。博主用了一個月的每個晚上和周末學習了 Nodejs + Express + MongoDB + Gulp, 做了這個博客,可以說大致理解他們的開發邏輯,結構,和基本使用方法。很多難以解決的問題,也無非是 google、知乎、github。
登錄功能實現
登錄功能,利用了 passport 和 validator 組件實現,首先是驗證登錄,在 user model 中實現方法 verifyPassword:
UserSchema.methods.verifyPassword = function(password){ return password === this.password; }
而對于需要驗證的頁面,在 user 的 controller 中實現了 中間件:
var needLogin = function(req, res, next){ if(req.user){ next(); } else { req.flash("error", "請先登錄"); res.redirect("/admin/users/login"); } } module.exports.requireLogin = needLogin;
對于與 validator 在 express.js 中按要求添加 use 就可以啦。
app.use(validator({ errorFormatter: function(param, msg, value) { var namespace = param.split(".") , root = namespace.shift() , formParam = root; while(namespace.length) { formParam += "[" + namespace.shift() + "]"; } return { param : formParam, msg : msg, value : value }; } }));
加密使用了 md5, 這個不多說了。還有一點就是登錄狀態驗證:在服務器端,登錄狀態被存儲在數據庫的 sessions 集合中,通過 passport 和 express-session 實現:
app.use(session({ secret: "nodeblog", resave: false, saveUnitialized: true, cookie: {secure: false}, store: new MongoStore({ mongooseConnection: connection }) })); app.use(passport.initialize()); app.use(passport.session()); app.use(function(req, res, next){ req.user = null; if(req.session.passport && req.session.passport.user){ User.findById(req.session.passport.user, function(err, user){ if(err) return next(err); user.password = null; req.user = user; next(); }) } else { next(); } })
markdown 和內容驗證
這個功能之前去找了 github 中 "markdown" 排第一的 node 插件 marked 插件實現。關于插件這里不多說了。這里重點說一下的內容驗證,因為內容驗證是安全性保障的重要一個環節。這里即要保障惡意腳步不會被提交,或運行,而且還要保障 markdown 可以很好的轉換數據。對于輸入的字符串處理,博主自己實現了幾個工具函數:
var clearUtil = function(app){}; // 清除 XML 標簽 clearUtil.clearXMLTags = function(str, deeply){ var deeply = deeply || false; var temp = str.replace(/<[^>]+>/g, ""); if(deeply){ temp = temp.replace(/[ ][ ]+/g, ""); } return temp; }; // 清除 script 標簽及內容 clearUtil.clearScripts = function(str){ return str.replace(//ig,""); }; // 清除換號符 CR/LF clearUtil.clearReturns = function(str){ return str.replace(/[ ]/g, ""); }; // 轉義 xml 尖括號 clearUtil.TransferTags = function(str){ var temp = str.concat(); temp = temp.replace(//g, ">"); return temp; }; module.exports = clearUtil;
性能優化
這個部分,主要工作是代碼壓縮,和緩存利用。
壓縮方法都是十分主流的方法,利用
imagemin, clean-css, uglify, 壓縮了圖片,css 和 js, 同時網頁以 gzip 格式傳輸,減小體積。
同時,對 css 和 js 附加緩存,配合 E-Tag 和 版本號實現服務器更新,這一部分其實 Express 已經幫我們實現好了。所以這個部分,主要十幾個 gulp 配置
gulp.task("sass", function () { gulp.src("./source/css/**/*.scss") .pipe(autoprefix()) .pipe(sass()) .pipe(clearCss()) .pipe(gulp.dest("./dist/css")) .pipe(browserSync.reload({stream:true})); }); gulp.task("imagemin", function(){ gulp.src("./source/img/*.{png,jpg,gif,ico}") .pipe(imagemin()) .pipe(gulp.dest("./dist/img")); }); gulp.task("minifyjs", function() { return gulp.src("./source/js/**/*.js") .pipe(rename({suffix: ".min"})) .pipe(uglify()) .pipe(gulp.dest("./dist/js")); });
偷懶配置
博主用了一個 CentOS 的服務器,其實通過 scp 和 ssh 就可以實現上傳和配置了,但依據“進步源于懶惰”的碼農提高準則,博主還是做了一個自己上傳部署的功能:
/* part of gulpfile.js */ var shell = require("gulp-shell"); var ssh = require("gulp-ssh"); var deployConfig = require("./deploy-config"); var gulpSequence = require("gulp-sequence"); var zip = require("gulp-zip"); var through = require("through2"); var async = require("async"); var scpClient = require("scp2"); var gulpUtil = require("gulp-util"); var deploySSH = require("./deploy-ssh"); //上傳功能實現主任務 gulp.task("up", function (){ shell.task(["rm -rf publish"]); gulpSequence("copyFile", "zipFile", "deploy", function() { gulpUtil.log(PLUGIN_NAME, "***** Deploy Finished!!!!"); process.exit(0); }); }); gulp.task("copyFile", function() { return gulp.src([ "node_modules/**", "*.json", "*.js", "app/**", "config/**", "dist/**", "!config.js" ], { base: "./"}) .pipe(gulp.dest("./publish")); }); gulp.task("zipFile", function() { return gulp.src(["publish/**"], { base: "./" }) .pipe(zip("publish.zip")) .pipe(gulp.dest("./publish")); }); gulp.task("deploy", function() { var config = deployConfig.production; config.deployPath = "/home/faremax/website/publish/"; return gulp.src("publish/publish.zip", { base: "./" }) .pipe(deploySSH({ servers: config.servers, dest: config.deployPath + "publish.zip", logPath: "deploy", shell:[ "cd " + config.deployPath, "shopt -s extglob", "rm -rf !(logs|node_modules|config.js|publish.zip)", "unzip -o publish.zip", "cp -rf publish/** .", "rm -rf publish", "rm publish.zip", "npm install --production", "pm2 startOrRestart pm2-start.json" ], })); });
/* deploy-ssh.js */ var gulp = require("gulp"); var gutil = require("gulp-util"); var through = require("through2"); var ScpClient = require("scp2").Client; var ssh = require("gulp-ssh"); var async = require("async"); var ProgressBar = require("progress"); const PLUGIN_NAME = "deploy-ssh" module.exports = function (options) { var servers = options.servers; var dest = options.dest; var shell = options.shell; var logPath = options.logPath; return through.obj(function (file, enc, callback) { if (file.isNull()) { callback(null, file); return; } if (file.isStream()) { return callback(new gutil.PluginError(PLUGIN_NAME, "No stream support")); } var i = 0; async.eachSeries(servers, function(server, done) { var hostName = server.sshConfig.host; gutil.log(PLUGIN_NAME, "start deploy:" + hostName) var client = new ScpClient(server.sshConfig); var bar = null; client.on("transfer", function(buffer, uploaded, total){ if(bar == null){ bar = new ProgressBar(hostName + " uploading [:bar] :percent :elapsed s", { complete: "=", incomplete: " ", width: 50, total: total }); } bar.tick(1); }); client.write({ destination: dest, content: file.contents }, function () { ssh(server).shell(shell, {filePath: logPath + "-" + hostName + ".log", autoExit: true}).on("error", function (err) { done(err); gutil.PluginError(PLUGIN_NAME, err) }).on("finish", function () { gutil.log(PLUGIN_NAME, "finish deploy:" + hostName); done(); if (++i === servers.length) { callback(null, file); } }).pipe(gulp.dest("logs")); }); }); }); };
/* deploy-config.js */ var config = { production:{ servers:[ { sshConfig: { host: "114,214,132,211", port: 80, username: "******", password: "******", readyTimeout: 600000 } }] } }; module.exports = config;網站實現效果
從功能上來看,其實僅僅實現了博客展示、博客的管理和分類管理3個基本部分,前臺是給用戶看的,花的時間比較多,還有就是安全,這個不能大意。
前臺效果,這個大家都能看到,不說了:
當然,前臺還有移動端:
后臺效果,東西不多,界面很簡潔,很多東西還需要逐步添加:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97657.html
摘要:博主目前的這個項目還不算很大,模塊依賴簡單,但期望完成諸如版本號替換,壓縮代碼,合并文件,發布到服務器等和模塊化關系不大的工作,所以使用了。同時,對和附加緩存,配合和版本號實現服務器更新,這一部分其實已經幫我們實現好了。 經常在各種論壇、博客還有 github 上活躍的朋友不難發現,許多大牛都有自己的網站,也多以博客為主。博主作為一個立志前端的大白,難道不應該和大牛學習么?說干就干,前...
摘要:設置什么是本用于介紹托管在的項目,不過,由于他的空間免費穩定,用來做搭建一個博客再好不過了。你可以通過來訪問你的個人主頁。執行過程中可能需要讓你輸入賬戶的用戶名及密碼,按照提示操作即可。推薦使用騰訊公益。 系統環境配置 要使用Hexo,需要在你的系統中支持Nodejs以及Git,如果還沒有,那就開始安裝吧! 安裝Node.js 下載Node.js參考地址:安裝Node.js 安裝Git...
摘要:此文章用于記錄本人學習歷程,有共同愛好者可加好友一起分享。從上周天,由于本周有公司籃球比賽,所以耽誤兩天晚上,耗時三個晚上勉強做了一個登錄功能。這里的用戶信息和登錄狀態都是直接取的中的用戶信息進行屬性值初始化。 此文章用于記錄本人VUE學習歷程,有共同愛好者可加好友一起分享。從上周天,由于本周有公司籃球比賽,所以耽誤兩天晚上,耗時三個晚上勉強做了一個登錄功能。中間的曲折只有自己知道,有...
摘要:下面我以主題舉例,覆蓋默認主題。其他元素使用相同的方法都可以修改。像這種超鏈接跳轉的修改,在文件中找到直接把屬性的值改變即可。 準備:搭建環境 大致分為以下兩步: 安裝Node.js 安裝git 配置Node.js環境 下載Node.js安裝 Windows Installer 32-bithttps://nodejs.org/dist/v4.2.3/node-v4.2.3-x...
摘要:下面我以主題舉例,覆蓋默認主題。其他元素使用相同的方法都可以修改。像這種超鏈接跳轉的修改,在文件中找到直接把屬性的值改變即可。 準備:搭建環境 大致分為以下兩步: 安裝Node.js 安裝git 配置Node.js環境 下載Node.js安裝 Windows Installer 32-bithttps://nodejs.org/dist/v4.2.3/node-v4.2.3-x...
閱讀 1882·2021-11-11 16:55
閱讀 2064·2021-10-08 10:13
閱讀 739·2019-08-30 11:01
閱讀 2155·2019-08-29 13:19
閱讀 3277·2019-08-28 18:18
閱讀 2620·2019-08-26 13:26
閱讀 579·2019-08-26 11:40
閱讀 1864·2019-08-23 17:17