# 章節導航
[TOC]
## express - generator
1. 全局安裝 express-generator 項目構建工具,express-generator 會幫忙我們快速創建一個基于 express 的 web后端服務代碼,已經完成了基礎需要依賴及配置。
```
npm install -g express-generator
```
創建項目并進入
```
cd ~/desktop && express expressName && cd expressName
```
下載依賴
```
npm install
```
啟動start
```
npm start
```
瀏覽器輸入http://localhost:3000/, 就能看見
## 文件結構
app.js 主文件 (實例化express還有引入依賴包)
bin/www 啟動入口(很少用到)
package.json 依賴包管理文件(暫時沒什么用到就是配置依賴包)
public 靜態資源目錄 (存放css,js,img等文件)
routes 路由目錄 (存放你頁面路由!impotent)
views 模版目錄(放置你寫好的html.tpl格式)
配置統一共用模版會比較好`{% extends './../admin_ayout.tpl' %} {% endblock %}`在頁面頭部應用
## 介紹app.js中的配置
```
var indexRouter = require('./routes/indexs');//頁面路由存放調用事件
var usersRouter = require('./router/api') //存放接口例如post,put,delete 發送數據為主
```
在indexs中我們經常存放每個頁面調用的事件如果事件沒生效那就得即使查看router/indexs
在api中我們放置著我們鏈接網頁的API接口這是把數據post,put上去的主要文件有很多不同的請求方法
```
~~~
// 使用 morgan 日志打印
app.use(logger('dev'));
// 使用對 Post 來的數據 json 格式化
app.use(express.json());
// 使用對 表單提交的數據 進行格式化
app.use(express.urlencoded({ extended: false }));
// 使用 cookie 例如用戶登陸產生的cookie
app.use(cookieParser());
// 設置靜態文件地址路徑為 public
app.use(express.static(path.join(__dirname, 'public')));
~~~
```
捕獲報錯
```
// 捕捉404錯誤
app.use(function(req, res, next) {
next(createError(404));
});
```
監聽頁面異常
```
app.use(function(err,req,res,next){
res.locals.message = err.message //設置錯誤信息
res.locals.error = req.app.get('env') === 'development' ? err: { } ;
res.status(err.status || 500);
res.render('error');//把錯誤信息渲染到模版 (render:渲染器)
});
```
輸出模版`module.exports = app;`
## nunjucks
Nunjucks 是為了方便我們使用JavaScript編寫的一個模版引擎因為可以復用所以我們就選它了!
第一步當然是要安裝它啦!start!
`npm install --save nunjucks`
第二步在app.js中引入
`var nunjucks = require('nunjucks');`
```
app.set('view engine','tpl');//設置模版后綴為.tpl
nunjucks.configure('views',{
autoescape: true,
express: app,
watch: true
})
```
修改原來存在的layout,index,error為其添上tpl后綴
**layout.tpl**
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
{% block css %}
{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
{% block js %}
{% endblock %}
</body>
</html>
~~~
**index.tpl**
~~~
{% extends './layout.tpl' %}
{% block css %}
<link rel="stylesheet" href="/stylesheets/style.css">
{% endblock %}
{% block content %}
<h1>{{title}}</h1>
<p>Welcome to {{title}} with nunjucks!</p>
{% endblock %}
~~~
**error.tpl**
~~~
{% extends './layout.tpl' %}
{% block css %}
<link rel="stylesheet" href="/stylesheets/style.css">
{% endblock %}
{% block content %}
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>
{% endblock %}
~~~
## 常用的模塊語句
`if`為分支語句,和JavaScript中的`if`類似
```
{% if variable %}
It is true //判斷如果為true就顯示反之隱藏
{% endif %}
```
`for`遍歷數組(arrays)和對象(dictionaries)
~~~js
var items = [{ title: "foo", id: 1 }, { title: "bar", id: 2}];
~~~
~~~jinja
<h1>Posts</h1>
<ul>
{% for item in items %}
<li>{{ item.title }}</li>
{% else %}
<li>This would display if the 'item' collection were empty</li>
{% endfor %}
</ul>
~~~
上面的示例通過使用`items`數組中的每一項的`title`屬性顯示了所有文章的標題。如果`items`數組是空數組的話則會渲染`else`語句中的內容。
## axios
Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。[axios]([https://github.com/axios/axios](https://github.com/axios/axios))
下載 axios
`npm install -save axios`
記得修改路由app.js
```
var apiRouter = require('./routes/apo');//引入文件
app.use('.api'/apiRouter); //使用
```
# 實例
## knex
### 增刪改查的實現
以下我們使用 knex 使用 mysql 鏈接操作數據庫,使用前請確保安裝以下工具:
數據庫下載: [Sequel Pro](https://www.sequelpro.com/)
下載依賴:`npm install -save knex mysql`
在項目根目錄下,新建配置信息**config.js**,之后的配置信息涉及到數據庫和密碼,不上傳到 Github 等托管平臺,所以需要單獨設置,之后使用**.gitignore**避免上傳。之后敏感的配置信息,都將在此配置。host 地址為數據庫的服務地址,本地為 127.0.0.1 ,也可能在虛擬機上 192.168.64.2 ,以 XAMPP 開放的地址為準。
**config.js**
~~~
const configs = {
mysql: {
host: '127.0.0.1',
port: '3306',
user: 'root',
password: '',
database: 'expressapp'
}
}
module.exports = configs
~~~
在項目根目錄下,新建**.gitignore**避免上傳**config.js**及**node\_modules**等不需要被上傳到 Github 的文件。
**gitignore**
~~~
.DS_Store
.idea
npm-debug.log
yarn-error.log
node_modules
config.js
~~~
新建**models/knex.js**數據庫配置,初始化配置 knex
~~~
// 引用配置文件
const configs = require('../config');
// 把配置文件中的信息,設置在初始化配置中
module.exports = require('knex')({
client: 'mysql',
connection: {
host: configs.mysql.host,
port: configs.mysql.port,
user: configs.mysql.user,
password: configs.mysql.password,
database: configs.mysql.database
}
~~~
新建**models/user.js**用戶模型,并設置獲取所有用戶的方法
```
const knex = require('./../models/knex');//引入
const TABLE = 'users'; //數據庫中的用戶表
const User = {
all:function(){ //獲取all用戶信息
return new Promise((reslove,reject) =>{
knex.select().table(TABLE).then(res =>{
reslove(res)
}).catch(err =>{ //捕獲報錯
reject(err) //返回拒絕
})
})
}
}
module.exports = User //輸出方法
```
新建用戶在用戶列表上使用到了for循環語句參考上面
~~~
{% for val in users %}
<li>
<span>id: {{val.id}}</span>
<span>email: {{val.email}}</span>
<input class="user-name" type="text" name="name" placeholder="姓名" value="{{val.name}}">
<button class="update-user" data-id="{{val.id}}">修改姓名</button>
<button class="delete-user" data-id="{{val.id}}">刪除用戶</button>
</li>
{% endfor %}
~~~
新建**controllers/user.js**用戶控制器,并設置 show 方法
我們想要把數據渲染到模版上當然需要一個方法才能實現。
1 .引入用戶數據模版user.js
2 .使用異步請求把數據渲染到頁面/async可以無刷新頁面
3 .渲染res.render()
~~~
// 引用用戶模版數據
const User = require('./../models/user.js');
const userController = {
// show 獲取用戶數據并返回到頁面
show: async function(req,res,next){
try{
// 從模型中獲取所有用戶數據
const users = await User.all();
// 把用戶數據設置到 res.locals 中
res.locals.users = users;
// 渲染到 views 視圖目錄的 user/show.tpl 路徑中。
res.render('user/show.tpl',res.locals)
}catch(e){
res.locals.error = e;
res.render('error',res.locals)
}
},
}
module.exports = userController;
~~~
### index添加路由
~~~
var userController = require('./../controllers/user.js');
router.get('/user',userController.show)'//獲取上面所用到的show方法
~~~
### 我們需要添加增刪改的功能的話那么必須給他們添加上需要的方法
1.添加用戶我們使用(insert:插入)聽起來有點像造孩子運動呢?
~~~
insert: function(params){
return new Promise((reslove,reject)=>{
knex(TABLE).insert(params)//慢慢插入
.then( res => {
reslove(res) //完事才幾行太不爭氣了
}).catch( err => {
reject(err) // o shit!
})
})
},
~~~
修改用戶 (update:更新) 給他弄潮一點!update
~~~
update: function(id, params ){
return new Promise((reslove,reject)=>{
knex(TABLE).where('id', '=', id).update( params ) //給他換上新衣服
.then( res => {
reslove(res) //掏錢換唄!刷哇滴卡
}).catch( err => {
reject(err) //窮逼被趕出店了—。—
})
})
},
~~~
報廢它!Get out of here (delete:再見了世界)
~~~
delete: function(id){
return new Promise((reslove,reject)=>{
knex(TABLE).where('id', '=', id).del() //對比一下沒錯就是你滾吧!
.then( res => {
reslove(res) //滾的好遠了哈哈哈
}).catch( err => {
reject(err) //老板突然覺得還不想讓你滾,回來敲代碼
})
})
}
~~~
既然我們設置造孩子,給孩子打扮,以及它長大成人是時候滾了!呸不對!是讓他去工作了那么我們就需要給他判斷一下孩子它爸是否以及成功插入并且內se了咳咳(小火車污污污)
我們去`controllers/user.js`給他設置一個邏輯筆記它有點弱智不會自力更生啥都要我們程序員操心!連sex都是
```
insert: async function(req,res,next){
let name = req.body.name;
let email = req.body.email;
let password = req.body.password;
console.log(name,email,password);
if(!name || !email || !password){
res.json({ code: 0, data: 'params empty!'})
//炮友名字/聯系方式/銀行卡密碼
//沒有這些?還想約
}
try{
const users = await Use.insert({name,email,password});
res.json({ code: 200, data: users})
}catch(e){
res.json({ code: 0, data: e })
}
}
```
判斷孩子是否需要買衣服更新一下樣子了
~~~
update: async function(req,res,next){
let id = req.body.id;
let name = req.body.name;
console.log(id,name);
if(!name || !id){
res.json({ code: 0, data: 'params empty!' });
return
}
try{
const user = await User.update(id,{ name });
res.json({ code: 200, data: user})
}catch(e){
res.json({ code: 0, data: e })
}
},
~~~
判斷一下是否炒了這個程序員
~~~
delete: async function(req,res,next){
let id = req.body.id;
if(!id){ //沒有id還炒個鬼 啊
res.json({ code: 0, data: 'params empty!' });
return
}
try{
const user = await User.delete(id);
res.json({ code: 200, data: user})
}catch(e){
res.json({ code: 0, data: e })
}
}
}
~~~
上面弄好后我們需要到router/api.js給他設置接口
```
~~~
var userController = require('./../controllers/user');
router.post('/user' , userController.insert); //創建
router.put('/user' , userController.update); //更新
router.delete('/user' , userController.delete); //刪除
~~~
```
我們還有最關鍵的一步就是得給我們的頁面設置js呀
找到并且綁定 新增,更新,以及刪除三個按鈕。我們要找到相關的內容做判斷決定是否進行異步處理
例如新增:有沒有用戶名和id呢,刪除對象的id,更新了什么呢?
~~~
$.ajax({
url: '/api/user',
data: { id },
type: 'DELETE', //不同處
success: function(data) {
if(data.code === 200){
alert('刪除成功!') //不同處
location.reload()
}else{
console.log(data)
}
},
error: function(err) {
console.log(err)
}
})
~~~
# BaseModel
結合上面的代碼我們不難發現有很多重復的代碼這樣會使我們工作量增大以及更多重復的代碼會使人眼花繚亂,所以我們需要抽離出他們共有的簡單方法,不僅方便還可以復用
建立models/base.js
使用了Class構造函數
~~~
const knex = require('./knex');
class Base {
constructor(props) {
this.table = props;
}
all( ){
return new Promise((reslove,reject)=>{
knex(this.table).select().then( res => {
reslove(res)
}).catch( err => {
reject(err)
})
})
}
select(params) {
return new Promise((reslove,reject)=>{
knex(this.table).select().where(params)
.then( res => {
reslove(res)
}).catch( err => {
reject(err)
})
})
}
insert(params){
return new Promise((reslove,reject)=>{
knex(this.table).insert( params )
.then( res => {
reslove(res)
}).catch( err => {
reject(err)
})
})
}
update(id, params ){
return new Promise((reslove,reject)=>{
knex(this.table).where('id', '=', id).update( params )
.then( res => {
reslove(res)
}).catch( err => {
reject(err)
})
})
}
delete(id){
return new Promise((reslove,reject)=>{
knex(this.table).where('id', '=', id).del()
.then( res => {
reslove(res)
}).catch( err => {
reject(err)
})
})
}
}
module.exports = Base;
~~~
修改用戶模型**models/user.js**,使其繼承基礎模型,并設置其**table**表格名稱。
**models/user.js**
~~~
// 引用基礎模型
const Base = require('./base.js');
// 定義用戶模型并基礎基礎模型
class User extends Base {
// 定義參數默認值為 users 表
constructor(props = 'users') {
super(props);
}
}
module.exports = User
~~~
修改用戶控制器**controller/user.js**,修改后的用戶模型是一個**class**類而不是對象,因此需要配合**new**來使用。
**controller/user.js**
~~~
const UserModel = require('./../models/user.js');
const User = new UserModel();
~~~
# Cookie
我們使用cookie完成登陸權限控制以及清除cookie退出登陸,登陸成果我們需要加密cookie設置頁面過濾組件
主要步驟有以下幾點
* cookie 的加密算法
* 登錄接口,登錄成功設置 cookie
* 用戶信息過濾組件,每個用戶進來更具 cookie 鑒定身份
* 登錄頁面,如果登錄的話重新定向到首頁
* 用戶管理頁面,如果沒有登錄的話重定向到登錄頁面
先引用網上關于jquery的加密解密方法[小本的早晨](https://www.cnblogs.com/syp172654682/p/8858358.html)
**utils/authCode.js**
~~~
/**
* 加密解密
*/
const crypto = require('crypto');
const key = Buffer.from('aitschool!@#$123', 'utf8');
const iv = Buffer.from('FnJL7EDzjqWjcaY9', 'utf8');
const authcode = function (str, operation){
operation ? operation : 'DECODE';
if (operation == 'DECODE') {
let src = '';
let cipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
src += cipher.update(str, 'hex', 'utf8');
src += cipher.final('utf8');
return src;
}else {
let sign = '';
let cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
sign += cipher.update(str, 'utf8', 'hex');
sign += cipher.final('hex');
return sign;
}
}
module.exports = authcode;
~~~
### 在index和apl中調用渲染視圖模版
~~~
index:
router.post('/login' , authController.login);
apl:
router.post('/login' , authController.login);
~~~
這是調用登陸以及輸入賬號密碼的方法
~~~
const authController = {
login:async function(req,res,next){
// 獲取郵件密碼參數
let email = req.body.email;
let password = req.body.password;
// 參數判斷
if(!email || !password){
res.json({ code: 0, data: 'params empty!' });
return
}
try{
// 通過用戶模型搜索用戶
const users = await User.select({ email, password });
// 看是否有用戶存在
const user = users[0];
// 如果存在
if(user){
// 將其郵箱、密碼、id 組合加密
let auth_Code = email +'\t'+ password +'\t'+ user.id;
auth_Code = authCodeFunc(auth_Code,'ENCODE');
// 加密防止再 cookie 中,并不讓瀏覽器修改
res.cookie('ac', auth_Code, { maxAge: 24* 60 * 60 * 1000, httpOnly: true });
// 返回登錄的信息
res.json({ code: 200, message: '登錄成功!'})
}else{
res.json({ code: 0, data: { msg: '登錄失敗,沒有此用戶!'} })
}
}catch(e){
res.json({ code: 0, data: e })
}
},
// 渲染登錄頁面的模版
renderLogin:async function(req,res,next){
res.render('login',res.locals)
}
}
~~~
## 過濾中間件
filters/index.js
```
module.exports = function(app) {
app.use(require('./loginFilter.js'));
app.use(require('./initFilter.js'))
}
```
loginFilter.js
```
const authCodeFunc = require('./../utils/authCode.js');
module.exports = function (req,res,next){
res.locals.isLogin =false;
res.locals.iserInfo = {};
let auth_code = req.cookies.ac;
if(auth_code){
auth_Code = authCodeFunc(auth_Code,'DECODE');
authArr = auth_Code.split('\t');
let email = authArr[0];
let password = authArr[1];
let id = authArr[2];
// 注意:為了防止刪改
// 這里其實應該再調用一次用戶模型進行登錄校驗
// 然后把狀態保存在 session 中來聯合判斷。
// 當前為了體驗,所以直接把用戶名和密碼和id直接解密返回
res.locals.isLogin = true;
res.locals.userInfo = {
email,password,id
}
}
}
```
**filters/initFilter.js**
~~~
module.exports = function (req, res, next) {
res.locals.seo = {
title: 'ExpressApp',
keywords: 'Express、Nodejs',
description: 'ExpressApp to study Nodejs on Web'
}
next();
}
~~~
記得要在app.js中引入才會其效果哦
~~~
var filters = require('./filters/index')
...
filters(app);
app.use('/', indexRouter);
app.use('/api', apiRouter);
~~~
### 登陸頁面如果登陸的話重新定向到首頁
```
const authController = {
...
renderLogin: async function(req,res,next){
if(res,locals.isLogin){
res.redirect('/user');
return
}
res.render('login',res.locals)
}
}
```
如果沒有登陸那肯定是得定向回登陸頁的呀
~~~
const userController = {
show: async function(req,res,next){
// 如果用戶沒有登錄,重定向到登錄頁面
if(!res.locals.isLogin){
res.redirect('/login')
return
}
try{
const users = await User.all();
res.locals.users = users;
res.render('user/show.tpl',res.locals)
}catch(e){
res.locals.error = e;
res.render('error',res.locals);
}
},
}
~~~
- 前言
- 你真的懂This嗎?
- 對象和對象構造函數
- 工廠功能和模塊模式
- API的使用
- async and await
- 關于async的很棒的一篇文章
- 掘金:關于forEach,map,fiter循環操作
- Node.js 實例與基礎
- 原創: Express 學習使用筆記
- 零碎知識點方法
- 關于滾動吸頂的方法
- Vue學習筆記
- Vue向導
- vuex是啥?
- vue代碼風格指南
- 關于vue的初體驗
- 超詳細解毒Vue
- Vue實例
- 模版語言
- 組件基礎
- 條件渲染、列表渲染、Class與style綁定
- Todolist的制作超詳細
- vue-router
- Vue基礎2.0x 筆記
- 搭建vuepress
- JavaScript之ES6
- 箭頭函數
- 這就是This