第一章 建議學習時間8小時·分兩次學習 總項目預計10章
學習方式:詳細閱讀,並手動實現相關代碼(如果沒有node和vue基礎,請學習前面的vue和node基礎博客【共10章】
演示地址:後臺:demoback.lalalaweb.com 前臺:demo.lalalaweb.com
演示過程中可能會發現bug,希望即時留言反饋,謝謝
源碼下載:https://github.com/sutianbinde/classweb //不是全部的代碼,每次更新博客才更新代碼
學習目標:此教程將教會大家 如何一步一步實現一個完整的課程學習系統(包括課程管理後臺/Node服務器/學習門戶三個模塊)。
上次node基礎課程博客大家反響很好,時隔3個月,才更新項目部分,預計2~3天更新一章,我儘量20天更新完畢,學完這個項目Nodejs和vue就基本熟悉了,如發現教程有誤的地方,請及時留言反饋
視頻教程地址:www.lalalaweb.com,後期會上傳教學視頻,大家可前往視頻學習(暫時還沒有視頻)
express項目構建 vue-cli項目構建
我們首先給項目取一個名字 “在線課堂” 好啦,英文名 classweb
先在自己喜歡的位置 創建項目目錄文件夾 classweb
創建node項目(這個項目我們採取前後端分離的模式,所以需要分別創建node和vue項目,但都放在classweb中)
進入目錄,運行 express server 生成服務器端項目(server是我們服務端項目的名字) (這裏注意,得提前安裝node express-generator,學習過前面node基礎的同學這些應該的安裝了的,沒安裝的先學前面的課程)
打開命令行的簡單方法:在文件夾中按住 shift 鼠標右鍵 點擊“在此處打開命令行” / "在此處打開powershell窗口"
先安裝 cnpm 鏡像(它是npm的國內代理,可以使下載速度加快,如果以及安裝了的就不用安裝了),使用如下代碼,安裝完成後測試一下 cnpm -v
npm install -g cnpm --registry=https://registry.npm.taobao.org
進入項目,安裝依賴,運行測試一下
這樣就運行起來了,在瀏覽器輸入http://localhost:3000/ 訪問
上面 Node項目就建好了
創建 vue項目
先全局安裝vue-cli
npm install --global vue-cli
這裏注意,我們最好是另外開一個命令行 來執行,因爲開發時前面的Node項目和vue項目要同時運行
然後在創建vue項目,使用 vue init webpack vueclient
注意:ESLint選項要選擇no(不然代碼一點不規範就報錯) ,如果選錯了,把vueclient文件夾刪了重新創建一遍即可。
進入項目,安裝依賴,運行
這時候瀏覽器中就自動代開網頁了
兩個項目的安裝和測試就完成了
安裝mongodb操作軟件 Robomongo
百度雲鏈接
鏈接:http://pan.baidu.com/s/1jHLSG78 密碼:6dhb
安裝方法參考:https://jingyan.baidu.com/article/9113f81b011ee72b3214c78d.html
鏈接好以後,在nwe connection右鍵 create database 輸入創建 classweb數據庫
創建好以後,展開classweb,然後在cloolections右鍵, create collection 創建一個user表(在彈出框中輸入user),用來放後臺登錄的用戶
創建好以後就有user表了,雙擊就能打開user表,現在裏面沒有數據
往裏面添加一條數據,便於以後登錄使用
user右鍵 insert document,然後輸入後面的數據 ,save, (數據用戶名 admin 密碼是 123456 加密後的字段 還有手機號)
{ "name" : "admin", "phone" : "13388868886", "password" : "4QrcOUm6Wau+VuBX8g+IPg==" }
然後表中就多了這麼一條數據了
實現登錄功能
首先我們把項目導入 編輯器,我這裏使用的Hbuilder,建議大家也使用這個,因爲項目中nodemodules的文件太多,webstrom或sublimetex都會很卡
然後找到App.vue,去掉多餘示例樣式,只留圖中的部分,這是項目的入口頁面
注:每個項目都有很多文件,大家暫時也不用明白他們都表示什麼意思,等用到的時候我會在用到的地方給大家講解的。
預警:第一次進入項目開發,肯定會有很多報錯,大家一定仔細閱讀步驟,仔細實現代碼,如果報錯,有是英文的看不懂,大家可以試着查一查百度/google,也可以在下邊留言,我看到儘量簡答,不要因爲出錯了難以解決就放棄了,我開始學習的時候也遇到很多不知所措的錯誤,心中會有一萬隻草泥馬奔騰的感覺。
然後在componets文件夾中新建 login.vue 文件
在login.vue文件中寫入下面登錄頁面的代碼(實現了基本的登錄佈局,在js中定義就基本的變量和登錄的方法名)
<template> <div class="backlogin"> <div class="login_box"> <div class="title">後臺登錄</div> <div> <input class="myinput" type="text" placeholder="手機號/用戶名" v-model="username" /> </div> <div> <input @keyup.13="login" class="myinput" type="password" placeholder="口令" v-model="password" /> </div> <div class="login_other"> <a href="javascript:;">找回密碼</a> <input type="checkbox" id="remenberme" /><label for="remenberme">記住我</label> </div> <button :disabled="disablebtn" class="login" @click="login">{{loginText}}</button> </div> </div> </template> <script> export default { name: 'backlogin', data () { return { username:"admin",/*TODO:先預存測試值,以免手動輸入*/ password:"123456", disablebtn:false, loginText:"登錄" } }, methods:{ login(){ } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .header{ height: 60px; box-shadow: 0 1px 5px rgba(13,62,73,0.2) ; } .header img{ width: 170px; margin-top: 12px; margin-left: 15px; float: left; } .header span{ float: left; color: #566a80; margin: 21px 0 0 20px; } .login_box{ width: 320px; margin: 50px auto; } .login_box .myinput{ width: 100%; border: 1px solid #cad3de; height: 40px; line-height: 40px; margin: 5px 0 10px; border-radius: 3px; padding: 0 10px; outline: none; box-sizing: border-box; } .login_box .myinput:focus{ border: 1px solid #4289dc; } .login_other{ overflow: hidden; } .login_other a{ float: right; color: #727f8f; } .login_other a:hover{ color: #273444; } .login_other input, .login_other label{ float: left; color: #727f8f; } .login_other input{ margin: 4px 5px 0 0; } .login{ box-sizing: border-box; border: none 0; height: 44px; line-height: 44px; width: 100%; background:#4187db; font-size: 16px; border-radius: 3px; margin-right: 40px; transition: all 0.5s ease; cursor: pointer; outline: none; color: #fff; margin-top: 15px; } .login:hover{ background: #2668b5; } .login[disabled]{ opacity: 0.8; } .login[disabled]:hover{ background:#4187db; } .title{ color: #273444; font-size: 1.5em; text-align: center; margin: 0 0 20px 0; } @media only screen and (max-width: 768px) { .login_box{ width: 280px; margin: 50px auto; } } </style>
然後修改router文件夾下的index.js文件來配置首頁訪問的組件是login.vue (這裏獲取組件的時候 @表示src文件夾路徑 所有vue文件的引入都不需要vue後綴,import後的賦值最好統一給大寫)。
這樣我們會發現剛打開的vue項目自動刷新了,展示效果如下圖(如果沒有刷新請查看命令行窗口有無報錯,有報錯就需要修改代碼,修改正確再重啓 npm run dev)
ajax請求
vue中請求數據,這裏我們使用第三方庫axios,這也是vue作者推薦的,比自帶的http好用很多。
先安裝axios
把原來的服務ctrl+c兩次停掉,然後 運行 cnpm install axios --save 安裝,安裝完成再重新啓動服務。
注:這裏爲什麼要用 --save呢,因爲使用save的話,這個包就會集成到package.json中的,我們上線的時候就能通過npm install去直接安裝了。
安裝完成後大家打開package.json,就可以看到裏面多了 axios:版本號
然後我們在main.js中添加如下代碼 引入axios,並配置基礎路徑(因爲是跨域請求node端,所以所有請求前面都需要添加node端的基礎地址,以後打包上線的時候需要合併的時候再把這個地址刪掉),文件位置和修改後的代碼如下圖
由於是跨域請求,我們需要配置withCredentials爲true,這樣避免每次都被識別爲新的請求。
說明:在vue中,可以使用代理去實現跨域,但是每次新地址都需要配置,還是比較麻煩,這裏我們採用直接配置跨域,一次配置就可以一勞永逸。
import axios from 'axios';//引入axios組件 axios.defaults.withCredentials=true; //跨域保存session有用 axios.defaults.baseURL = "http://localhost:3000"; //打包的時候直接刪掉,默認基礎路徑在這裏配置 //將 axios 賦值給 Vue,方便在子組件裏面使用 Vue.prototype.$reqs = axios;
然後在Login.vue中寫登錄的具體方法 將如下登錄請求代碼寫在 login方法中,登錄的地址爲 “/users/login” ,這個接口我們一會兒在node中去寫。
var _this = this; this.disablebtn = true; this.loginText = "登錄中..."; //this.$reqs就訪問到了main.js中綁定的axios this.$reqs.post("/users/login",{ username:this.username, password:this.password }).then(function(result){ //成功 console.log(result) _this.disablebtn = false; _this.loginText = "登錄"; }).catch(function (error) { //失敗 _this.disablebtn = false; _this.loginText = "登錄" });
然後我們轉到node端
先在routes中創建dbhandler.js文件,寫入下面我們封裝好的mongodb操作方法。增刪改查的具體操作我們在前面的node基礎教程中已經詳細講解了,這是這些方法的封裝,代碼和以前講的封裝有些許差異,大家直接用下面的代碼,不要用以前的。
這些方法這裏直接貢獻給大家,大家就可以不用自己寫了,直接複製就Ok,空了可以好好研究研究
var mongo=require("mongodb"); var MongoClient = mongo.MongoClient; var assert = require('assert'); var url = require('url'); var host="localhost"; var port="27017"; var Urls = 'mongodb://localhost:27017/classweb'; // classweb ===> 自動創建一個 //add一條數據 var add = function(db,collections,selector,fn){ var collection = db.collection(collections); collection.insertMany([selector],function(err,result){ try{ assert.equal(err,null) }catch(e){ console.log(e); result = []; }; fn(result); db.close(); }); } //delete var deletes = function(db,collections,selector,fn){ var collection = db.collection(collections); collection.deleteOne(selector,function(err,result){ try{ assert.equal(err,null); assert.notStrictEqual(0,result.result.n); }catch(e){ console.log(e); result.result = ""; }; fn( result.result ? [result.result] : []); //如果沒報錯且返回數據不是0,那麼表示操作成功。 db.close; }); }; //find var find = function(db,collections,selector,fn){ //collections="hashtable"; var collection = db.collection(collections); collection.find(selector).toArray(function(err,result){ //console.log(docs); try{ assert.equal(err,null); }catch(e){ console.log(e); result = []; } fn(result); db.close(); }); } //update var updates = function(db,collections,selector,fn){ var collection = db.collection(collections); collection.updateOne(selector[0],selector[1],function(err,result){ try{ assert.equal(err,null); assert.notStrictEqual(0,result.result.n); }catch(e){ console.log(e); result.result = ""; }; fn( result.result ? [result.result] : []); //如果沒報錯且返回數據不是0,那麼表示操作成功。 db.close(); }); } var methodType = { // 項目所需 login:find, // type ---> 不放在服務器上面 // 放入到服務器 // 請求---> 根據傳入進來的請求 數據庫操作 // req.query req.body show:find, //後臺部分 add:add, update:updates, delete:deletes, updatePwd:updates, //portal部分 showCourse:find, register:add }; //主邏輯 服務器 , 請求 --》 // req.route.path ==》 防止前端的請求 直接操作你的數據庫 module.exports = function(req,res,collections,selector,fn){ MongoClient.connect(Urls, function(err, db) { assert.equal(null, err); console.log("Connected correctly to server"); // 根據 請求的地址來確定是什麼操作 (爲了安全,避免前端直接通過請求url操作數據庫) methodType[req.route.path.substr(1)](db,collections,selector,fn); db.close(); }); };
然後修改自動生成的 users.js 爲如下代碼
代碼解釋:
引入了express框架,路由router,並且引入了上面封裝的 dbhandler。
crypto是加密包,對傳輸過來的密碼進行加密
post請求使用 post方法接收
handler()調用的是dbhander中的方法,傳入的參數依次 ( req:請求詳細, res:響應信息, “user”操作的表的名稱, 傳入的查詢數據, 回掉函數)
在dbhander.js中配置了login對應的操作是查詢,返回數據放到數組中。如果數組空,就表示沒查到數據,如果非空,比較密碼是否一致,如果都正確,就返回登錄成功。
最後的module.exports = router是ES6的模塊暴露,前面基礎博客中已經講了,這裏就不贅述了
var express = require('express'); var router = express.Router(); var handler = require('./dbhandler.js'); var crypto = require('crypto'); /* POST users listing. */ //登錄 router.post('/login', function(req, res, next) { var md5 = crypto.createHash('md5'); var password = md5.update(req.body.password).digest('base64'); handler(req, res, "user", {name: req.body.username},function(data){ if(data.length===0){ res.end('{"err":"抱歉,系統中並無該用戶,如有需要,請向管理員申請"}'); }else if(data[0].password !== password){ res.end('{"err":"密碼不正確"}'); }else if(data.length!==0&&data[0].password===password){ req.session.username = req.body.username; //存session req.session.password = password; res.end('{"success":"true"}'); } }); }); module.exports = router;
這樣請求的代碼就寫完了,但是跨域請求 需要在node中也作配置纔可以請求到
修改app.js,在11行左右找到 var app= express(),在其後面添加如下代碼
第二段代碼是服務器端存session的,直接使用express-session模塊(後面會帶着大家安裝),然後添加配置項即可(配置項的說明在備註中)
//跨域 後期刪 app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "http://localhost:8080"); //爲了跨域保持session,所以指定地址,不能用* res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Credentials', true); next(); });
//session var session=require('express-session'); app.use(session({ secret:'classweb531234', //設置 session 簽名 name:'classweb', cookie:{maxAge:60*1000*60*24}, // 儲存的時間 24小時 resave:false, // 每次請求都重新設置session saveUninitialized:true }));
停止Node端服務,安裝mongodb
cnpm install [email protected] --save
安裝 express-session
cnpm install express-session --save
重啓服務
刷新vue的登錄頁面,點擊登錄
你會發現,控制檯打印出了返回的登錄成功信息,這樣我們的登錄功能就編寫完成了 (常見出錯原因在後面附錄)
附錄:常見報錯
1. 數據庫連接失敗 :
①可能mongo未自動啓動,請按基礎教程中的介紹正確啓動mongo (Net start MongoDB)
②數據庫名沒寫對 檢查dbhandler.js中的下圖名字是否和數據庫名稱一樣。
③表名稱沒給對 ,檢查user.js 中的表名是否和數據庫中的一致。
2.根本鏈接不到地址,在網頁控制檯打印紅色的鏈接失敗
①請求地址沒寫對,覈對login.vue中的地址和 node端routes/index.js中的地址是否對上
②跨域配置不對,請按上面的步驟把 vue部分和node部分都好好再覈對着寫一遍
好啦,今天就講到這裏。下一篇將講解 首頁路由配置,導航,首頁統計信息,用戶添加/修改/刪除,表格組件封裝。