這次帶給大家node koa2 mysql bootstrap搭建論壇前後端,node koa2 mysql bootstrap搭建論壇前後端的注意事項有哪些,下面就是實戰案例,一起來看一下。
相關mysql影片教學推薦:《mysql教學》
#前言
##在學習了koa2和express並寫了一些demo後,打算自己寫一個專案練練手,由於是在校生,沒什麼好的專案做,即以開發一個前端論壇為目標,功能需求參照一下一些社區擬定,主要有:登入註冊
個人資訊維護、頭像等基本資訊發表文章,富文本編輯器採用wangEditor插件,編輯、刪除文章,文章分類等文章評論、文章收藏、按讚等支援文章分頁、評論分頁載入關注取關用戶資源(檔案)上傳分享、下載、查看學習資源推薦.....作者個人日記but。 。 。 。由於種種原因,目前僅實作了部分功能,資源分享還沒寫專案運作效果:http://120.77.211.212/home專案技術堆疊應用:node-koa2- ejs-bootstrap3—jquery, github網址:https://github.com/Jay214/myblog-koa2,如果覺得對你有幫助或還看得下去,歡迎star~~鼓勵鼓勵我這前端渣渣輝。開發環境
node: v8.3.0koa: ^2.4.1mysql: 5.7.1npm: 5.3.0以上如何運作專案
將專案clone至本地git clone git@github.com:Jay214/myblog-koa2 .git安裝模組中間件npm install安裝mysql
mysql版本推薦使用5.7以下的,5.7的有個bug,圖形化介面建議使用navicat for MySQL運行可以安裝 supervisor(npm install supervisor 專案運行工具,開啟後即處於監聽模式,修改檔案後儲存即可,無需再啟動專案) node index 或npm supervisor indexlocalhost:8080/home 連接埠號碼可自行修改若發現專案有存在什麼bug或有比較好的建議歡迎多多提議,qq:2752402930。準備工作
由於koa2是基於es6的promise和es7的await/async語法,所以如果對es6/es7不懂的話請先過一遍文檔,後台搭建資料庫是關鍵,所以請先安裝好mysql,mysql建議安裝5.7版本以下的,因為5.7.0版本有個bug,需要更改配置文件,具體若你們安裝的時候便知道了。 安裝node環境,使用node -v查看node版本,node需要較新版本能夠支援es6的promise和es7的await/async語法,現在node版本都會自帶npm的,所以不需要再去安裝npm。專案結構
1.config存放預設檔(資料庫連線配置)2.lib存放資料庫檔案3.middlewares存放判斷登陸註冊與否中間件4.public存放靜態文件,js,引用bootstrap框架等文件5.routers存放路由文件6.views存放模板文件7.index是程式主文件,定義接口,資料庫接口,引用模組等8.package.json專案的配置文件,包括專案名,作者,依賴,模組等專案用vscode開發的,用起來很舒服,還沒嘗試過的小夥伴趕緊去試試吧。 專案初始化:cd myblog1 -> npm init 此時已經建立好了package.json檔案了。 由於koa2是輕量級的框架,小巧精悍,所以我們為了促進我們的開發效率和方便性,我們需要安裝一些koa2的模組中間件:npm install i koa koa-bodyparser koa-mysql-session koa-router koa-session-minimal koa-static koa-views md5 moment mysql ejs koa-static-cache --save-dev
各模組用處
koa node框架
koa-bodyparser 表單解析中間件
koa-mysql-session、koa-session -minimal 處理資料庫的中間件
koa-router 路由中間件
koa-static 靜態資源載入中間件
ejs 範本引擎
#md5 密碼加密
moment 時間中間件
mysql 資料庫
koa-views 範本呈現中間件
koa-static-cache 檔案快取
專案基本框架建立
設定資料庫連線
在config資料夾新default.js :
const config = { //启动端口 port: 8080, //数据库配置 database: { DATABASE: 'nodesql', USERNAME: 'root', PASSWORD: '123456', PORT: '3306', HOST: 'localhost' } } module.exports = config;
然後在lib資料夾新mysql.js:
var mysql = require('mysql'); var config = require('../config/default.js') //建立数据库连接池 var pool = mysql.createPool({ host: config.database.HOST, user: config.database.USERNAME, password: config.database.PASSWORD, database: config.database.DATABASE }); let query = function(sql, values) { return new Promise((resolve, reject)=>{ pool.getConnection(function (err,connection) { if(err){ reject(err); }else{ connection.query(sql,values,(err,rows)=>{ if(err){ reject(err); }else{ resolve(rows); } connection.release(); //为每一个请求都建立一个connection使用完后调用connection.release(); 直接释放资源。 //query用来操作数据库表 }) } }) })}
這裡建立了一個資料庫連線池和封裝了一個操作資料庫表的函數,如果對於資料庫連線有不懂的話請自行百度。
建立入口檔
在主目錄新建index.js,也就是專案入口檔:
const koa = require("koa"); //node框架 const path = require("path"); const bodyParser = require("koa-bodyparser"); //表单解析中间件 const ejs = require("ejs"); //模板引擎 const session = require("koa-session-minimal"); //处理数据库的中间件 const MysqlStore = require("koa-mysql-session"); //处理数据库的中间件 const router = require("koa-router"); //路由中间件 const config = require('./config/default.js'); //引入默认文件 const views = require("koa-views"); //模板呈现中间件 const koaStatic = require("koa-static"); //静态资源加载中间件 const staticCache = require('koa-static-cache') const app = new koa(); //session存储配置,将session存储至数据库 const sessionMysqlConfig = { user: config.database.USERNAME, password: config.database.PASSWORD, database: config.database.DATABASE, host: config.database.HOST, } //配置session中间件 app.use(session({ key: 'USER_SID', store: new MysqlStore(sessionMysqlConfig) })) //配置静态资源加载中间件 app.use(koaStatic( path.join(dirname , './public') )) //配置服务端模板渲染引擎中间件 app.use(views(path.join(dirname, './views'),{ extension: 'ejs' })) //使用表单解析中间件 app.use(bodyParser({ "formLimit":"5mb", "jsonLimit":"5mb", "textLimit":"5mb" })); //使用新建的路由文件 //登录 app.use(require('./routers/signin.js').routes()) //注册 app.use(require('./routers/signup.js').routes()) //退出登录 app.use(require('./routers/signout.js').routes()) //首页 app.use(require('./routers/home.js').routes()) //个人主页 app.use(require('./routers/personal').routes()) //文章页 app.use(require('./routers/articles').routes()) //资源分享 app.use(require('./routers/share').routes()) //个人日记 app.use(require('./routers/selfNote').routes()) //监听在8080端口 app.listen(8080) console.log(`listening on port ${config.port}`)
上面程式碼都有註釋,我就不一一說明了,由於資源分享和個人日記還沒寫,所以暫時統一share...替代。
接下來向mysql.js加入資料庫操作語句,建表、增刪改查。
var users = `create table if not exists users( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, pass VARCHAR(40) NOT NULL, avator VARCHAR(100) DEFAULT 'default.jpg', job VARCHAR(40), company VARCHAR(40), introdu VARCHAR(255), userhome VARCHAR(100), github VARCHAR(100), PRIMARY KEY (id) );` var posts = `create table if not exists posts( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, title VARCHAR(100) NOT NULL, content TEXT NOT NULL, uid INT(200) NOT NULL, moment VARCHAR(40) NOT NULL, comments VARCHAR(255) NOT NULL DEFAULT '0', pv VARCHAR(40) NOT NULL DEFAULT '0', likes INT(200) NOT NULL DEFAULT '0', type VARCHAR(20) NOT NULL, avator VARCHAR(100), collection INT(200) NOT NULL DEFAULT '0', PRIMARY KEY (id) , FOREIGN KEY (uid) REFERENCES users(id) ON DELETE CASCADE );` var comment= `create table if not exists comment( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, content TEXT NOT NULL, moment VARCHAR(40) NOT NULL, postid INT(200) NOT NULL, avator VARCHAR(100), PRIMARY KEY ( id ), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE );` var likes = `create table if not exists likes( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, postid INT(200) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE );` var collection = `create table if not exists collection( id INT(200) NOT NULL AUTO_INCREMENT, uid VARCHAR(100) NOT NULL, postid INT(200) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE );` var follow = `create table if not exists follow( id INT(200) NOT NULL AUTO_INCREMENT, uid INT(200) NOT NULL, fwid INT(200) NOT NULL DEFAULT '0', PRIMARY KEY (id), FOREIGN KEY (uid) REFERENCES users(id) ON DELETE CASCADE ) ` let createTable = function(sql){ return query(sql, []); } //建表 createTable(users); createTable(posts); createTable(comment); createTable(likes); createTable(collection); createTable(follow); //createTable(follower); //注册用户 let insertData = function(value){ let _sql = "insert into users(name,pass) values(?,?);" return query(_sql,value); } //更新头像 let updateUserImg = function(value){ let _sql = "update users set avator=? where id=?" return query(_sql,value); } //更新用户信息 let updateUser = function(value){ let _sql = "update users set name=?,job=?,company=?,introdu=?,userhome=?,github=? where id=?" return query(_sql,value); } //发表文章 let insertPost = function(value){ let _sql = "insert into posts(name,title,content,uid,moment,type,avator) values(?,?,?,?,?,?,?);" return query(_sql,value); } //更新文章评论数 let updatePostComment = function(value){ let _sql = "update posts set comments=? where id=?" return query(_sql,value); } .......
總共六張表:使用者表、文章表、文章評論表、文章收藏表、文章按讚表、用戶追蹤表。
這裡引用了外鍵,但是現在的開發不建議使用外鍵了,所以你們可以自行修改,這裡在專案第一次啟動時會出現資料庫建立失敗(由於外鍵原因),只要重新啟動就ok了,如果對mysql還不了解的,這裡附送大家一個傳送門:mysql入門影片教學密碼:c2q7 。
前端頁面開發
專案基本架構建置好後,就可以進行前端頁面的編寫了。用node開發web時我們通常會配合模板引擎,這個專案我採用的是ejs,除了ejs之外較為常用的還有jade,但是jade相對ejs來說的話程式碼結構不夠清晰。關於ejs語法,這裡做個簡單的介紹:
header.ejs
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Myblog</title> <link rel="stylesheet" href="/css/bootstrap.min.css" rel="external nofollow" > <link rel="stylesheet" href="/css/index.css" rel="external nofollow" > <script src="/js/jquery-3.2.1.min.js" type="text/javascript"></script> <script src="/js/bootstrap.min.js" type="text/javascript"></script>
nav.ejs
</head> <body> <header class="nav-head"> <p class="nav container"> <ul> <li><a href="/home" rel="external nofollow" >首页</a></li> <li> <a href="/share" rel="external nofollow" rel="external nofollow" rel="external nofollow" >资源分享</a></li> <li> <a href="/share" rel="external nofollow" rel="external nofollow" rel="external nofollow" >推荐</a></li> <li> <a href="/share" rel="external nofollow" rel="external nofollow" rel="external nofollow" >个人日记</a></li> <li><a href="/about" rel="external nofollow" >关于作者</a></li> <li><input type="text" placeholder="搜索" class="input-sm search"></li> <% if(session.user){ %> <li> <img src="/images/<%= session.avator %>" alt="" class="img-circle img-title"> <ul class="menu"> <li class="personal menulist"><a href="/personal/<%= session.user %>" rel="external nofollow" >主页</a></li> <!-- <li class="collection menulist"><a href="#" rel="external nofollow" >收藏集</a></li> --> <li class="menulist"><a href="/articles" rel="external nofollow" >写文章</a></li> <li class="out"><a href="/signout" rel="external nofollow" >登出</a></li> </ul> </li> <script> var imgTitle = document.getElementsByClassName('img-title')[0], menu = document.getElementsByClassName('menu')[0]; imgTitle.onclick = function (event) { showTap(); event.stopPropagation(); } document.body.addEventListener('click',function (event) { menu.style.display = 'none'; // event.stopPropagation(); },true) function showTap(){ if(menu.style.display == 'block'){ menu.style.display = 'none'; }else { menu.style.display = 'block'; } } //退出登录 var signOut = document.getElementsByClassName('out')[0]; /* signOut.onclick = function(){ ajax('get','/signout',null); xhr.onreadystatechange = function () { if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){ let text = xhr.responseText; //服务器返回的对象 if(text){ window.location.reload = 'localhost:8080/home'; } } } }*/ </script> <% }else{ %> <li class="login"> <a class="loginup" href="javascript:;" rel="external nofollow" rel="external nofollow" rel="external nofollow" ><span class="glyphicon glyphicon-user"></span> 注册 | 登录</a> </li> <% } %> </ul> </p> </header> <script> var searchInput = document.getElementsByClassName('search')[0]; searchInput.onfocus = function () { this.style.width = "300px"; } searchInput.onblur = function () { this.style.width = "180px"; } </script>
login.ejs
<p class="sign"> <a href="javascript:;" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="关闭" class="login-close close">×</a> <p class="sign-title"> <h1>用户注册</h1> <h3>来吧骚年们!</h3> </p> <form class="form signup" role="form"> <p class="form-group"> <input type="text" name="username" placeholder="账号不少于两个字符" class="form-control"> </p> <p class="form-group"> <input type="password" name="pass" class="pass form-control" placeholder="密码"> </p> <p class="form-group"> <input type="password" name="repeatpass" id="repeat" placeholder="重复密码" class="form-control"> </p> <p class="form-group"> <input type="button" value="注册" class="btn btn-primary login-up"> </p> </form> <form class="form signin" role="form"> <p class="form-group"> <input type="text" name="username" placeholder="请输入用户名" class="form-control"> </p> <p class="form-group"> <input type="password" name="pass" class="pass form-control" placeholder="请输入密码"> </p> <p class="form-group"> <input type="button" value="登录" class="btn btn-primary login-in"> </p> </form> <p class="form-tips"> <span>已有账号?</span> <a href="javascript:;" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="register">登录</a> </p> </p> <p class="login-form-mask"></p> <script> // $(document).ready(function () { var $close = $('.login-close'); var $sign = $('.sign'); $close.click(function () { $sign.css("display","none"); }) var $register = $('.register'), //login/loginup切换 $span = $('.form-tips span'), $signup = $('.signup'), $signTitle = $('.sign-title h1'), $signin = $('.signin'); $register.click(function () { if($span.html() == "已有账号?"){ $signin.css('display','block'); $signup.css('display','none'); $(this).html('注册'); $span.html("没有账号?"); $signTitle.html("欢迎登录"); }else{ $signin.css('display','none'); $signup.css('display','block'); $(this).html('登录'); $span.html("已有账号?"); $signTitle.html("欢迎注册"); } }) var $loginup = $('.loginup'); //点击登录/注册,阻止事件冒泡 $loginup.click(function () { $mask.fadeIn(100); $sign.slideDown(200); return false; }) var $close = $('.login-close'), $mask = $('.login-form-mask'), $sign = $('.sign'); $sign.click(function () { return false; }) $close.click(function (e) { // e.stopPropagation(); fadeOut(); }) $(document).click(function (e) { //点击任意位置取消登录框 //e.stopPropagation(); fadeOut(); }) function fadeOut(){ $mask.fadeOut(100); $sign.slideUp(200); } var loginUp = document.getElementsByClassName('login-up')[0], loginIn = document.getElementsByClassName('login-in')[0], signUp = document.getElementsByClassName('signup')[0], signIn = document.getElementsByClassName('signin')[0]; loginUp.onclick = function () { //注册 var data1 = 'username=' + signUp["username"].value + '&' + 'pass='+ signUp["pass"].value + '&' + 'repeatpass=' + signUp["repeatpass"].value; var reg = /^[\u4E00-\u9FA5]{2,5}$/; /* if(!reg.test(signUp["username"].value)){ signUp["username"].classList.add("tips"); signUp['username'].value() } */ ajax('post','/signup',data1,"application/x-www-form-urlencoded"); xhr.onreadystatechange = function () { if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){ let text = JSON.parse(xhr.responseText).code; console.log(text) //服务器返回的对象 if(text == 3){ fadeOut(); alert("注册成功") setTimeout(()=>{ window.location.reload(); },1000) // document.getElementsByClassName('login')[0].outerHTML = "<li class='users'><a href='/'>"+signUp["username"].value+ "(=^ ^=)" +"</a></li>" }else{ fadeOut(); alert("用户已存在") } } } } loginIn.onclick = function () { //登录 var data2 = 'username=' + signIn["username"].value + '&' + 'pass=' + signIn["pass"].value; ajax('post','/signin',data2,"application/x-www-form-urlencoded"); xhr.onreadystatechange = function () { if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){ let text = JSON.parse(xhr.responseText).code; //服务器返回的对象 console.log(text); // document.getElementsByClassName('login')[0].outerHTML = "<li class='users'><a href='/'>"+signUp["username"].value+ "(=^ ^=)" +"</a></li>" if(text===1){ fadeOut(); // let imgTitle = document.getElementsByClassName('img-title')[0]; // imgTitle.setAttribute('src','/images/' + JSON.parse(xhr.responseText).avator) setTimeout(()=>{ window.location.reload(); },1000) }else if(text === 2){ alert('密码错误') }else{ alert('账号不存在') } } } } </script>
footer.ejs
</body> </html>
header為頁面頭部結構,nav為頁面導覽條,login為登入、註冊內容、footer為頁面頂部結構。可以看到我在ejs檔案裡有很多的if else 判斷語句,這是根據session來判斷使用者是否登入渲染不同的內容。現在我們需要我們的頁面編寫樣式:分別是home.css和index.css
為了增強對原生js的理解,在專案裡我用了大量的原生ajax(顯然jquery封裝的ajax比較好哈哈),所以這裡先寫一個原生ajax請求:
ajax.js
var xhr = null; function ajax(method,url,data,types) { //封装一个ajax方法 // var text; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else if(window.ActiveXObject){ xhr = new ActiveXObject("Microsoft.XMLHTTP"); }else { alert('你的浏览器不支持ajax'); return false; } xhr.onerror = function (err) { alert("some err have hapened:",err); } xhr.open(method,url,true); if(method=="post"){ xhr.setRequestHeader("Content-type",types); // xhr.setRequestHeader("Conent-Type",'application/json'"application/x-www-form-urlencoded") } try{ setTimeout(()=>{ xhr.send(data); },0); }catch(err) { alert("some error have hapened in font:",err); } return xhr; }
實作登入註冊
##前端基本頁面開發好後,我們就可以寫後台登入介面了:註冊:signup.jsvar router = require('koa-router')(); var userModel = require('../lib/mysql.js'); var md5 = require('md5') // 注册页面 // post 注册 router.post('/signup', async(ctx, next) => { console.log(ctx.request.body) var user = { name: ctx.request.body.username, pass: ctx.request.body.pass, repeatpass: ctx.request.body.repeatpass } let flag = 0; await userModel.findDataByName(user.name) .then(result => { console.log(result) if (result.length) { //处理err console.log('用户已存在') ctx.body = { code: 1 }; } else if (user.pass !== user.repeatpass || user.pass == '') { ctx.body = { //应把这个逻辑放到前端 code: 2 }; } else { flag = 1; } }) if(flag==1){ let res = await userModel.insertData([user.name, md5(user.pass + 'asd&$BH&*') ]) console.log(res.insertId) await userModel.findDataByName(user.name) .then((result)=>{ // var res = JSON.parse(JSON.stringify(result)) console.log(result[0]['avator']) ctx.session.id = res.insertId; ctx.session.user=user.name; ctx.session.avator = 'default.jpg'; ctx.body = { code: 3 }; console.log('注册成功') }) } }) module.exports = router
var router = require('koa-router')(); var userModel = require('../lib/mysql.js') var md5 = require('md5') router.post('/signin', async(ctx, next) => { console.log(ctx.request.body) var name = ctx.request.body.username; var pass = ctx.request.body.pass; await userModel.findDataByName(name) .then(result => { var res = JSON.parse(JSON.stringify(result)) if (name === res[0]['name']&&(md5(pass + 'asd&$BH&*') === res[0]['pass'])) { console.log('登录成功') ctx.body = { code: 1, } ctx.session.user = res[0]['name'] ctx.session.id = res[0]['id'] ctx.session.avator = res[0]['avator'] }else if(md5(pass + 'asd&$BH&*') != res[0]['pass']){ ctx.body = { code: 2 //密码错误 } } }).catch(err => { ctx.body = { code: 3 //账号不存在+ } console.log('用户名或密码错误!') }) }) module.exports = router
//使用新建的路由文件 //登录 app.use(require('./routers/signin.js').routes()) //注册 app.use(require('./routers/signup.js').routes()) //退出登录 app.use(require('./routers/signout.js').routes())
以上是node+koa2+mysql+bootstrap搭建論壇前後端的詳細內容。更多資訊請關注PHP中文網其他相關文章!