目前的打算是前端用Vue+element,後端用Nodejs的Express框架,實現一個展示頁面和後臺管理頁面。打算記錄學習過程。
推薦兩個教程:
https://www.cnblogs.com/xifengxiaoma/tag/element/
https://juejin.im/post/59097cd7a22b9d0065fb61d2#heading-1
https://panjiachen.github.io/vue-element-admin-site/zh/guide/
https://blog.csdn.net/sps900608/article/category/7482283/1
1.Nodejs介紹
JS和Nodejs
1、ES 定義了語法規則,JS和Nodejs必須遵守
2、js js=ES+web API(DOM,BOM,事件綁定,ajax)
3、node.js node.js=ES+nodejs API(處理http,處理文件等) ,兩者結合,可完成server端的任何操作
Commonjs和ES6模塊使用規範
//Commonjs
//math.js
let add=(x,y)=>{
return x+y;
}
let sub=(x,y)=>{
return x-y;
}
module.exports={
add:add,
sub:sub
};
let math=require('./math');
let test=math.add(3,3);
console.log(test);
//ES6模塊化
//lib.js
let bar=function(){
console.log('this is bar funciton');
};
let foo=function(){
console.log('this is foo function');
};
export {bar,foo}
//加載 lib.js文件
import {bar,foo,test,obj} from './lib'
foo();//this is foo function
server開發和前端開發的區別
環境思維工具的區別,而不是語言的區別
1.服務的穩定性:server端可能會遭受各種攻擊和誤操作,單個客戶端可以意外掛掉,但是服務端不能
2.考慮內存和CPU(優化和擴展):客戶端獨佔一個瀏覽器,內存和CPU都不是問題,但是 server 端要承載很多請求,CPU和內存都是稀缺資源
3.日誌記錄:前端也會參與寫日誌,但是隻是日誌的發起方,不關心後續;server端要記錄日誌,存儲日誌,分析日誌
4.安全: server端要隨時準備接受各種惡意攻擊(如越權操作,數據庫攻擊等),前端則少很多
5.集羣和服務拆分:流量擴大,解決機器的負荷
2.HTTP請求(get和post)
HTTP請求解析過程 (簡單概括)
1.域名解析
用戶輸入網址,由域名系統DNS解析輸入的網址;
2.TCP的3次握手
通過域名解析出的IP地址來向web服務器發起TCP連接請求,如果3次握手通過,則與web服務端建立了可靠的連接;
3.發送http請求
web客戶端向web服務端發送請求;
4.接收http響應
web客戶端接收來自web服務端的響應,包含各種根據請求反饋的數據;
5.web客戶端(瀏覽器)解釋響應
最後由瀏覽器解析響應裏的數據,即HTML代碼,以及HTML代碼中請求的資源,然後由瀏覽器呈現給用戶。
get請求和post請求
get請求:
1.get請求,即客戶端要向server端獲取數據,如查詢博客列表
2.通過querystring來傳遞數據,如a.html?a=100&b=200
3.瀏覽器直接訪問,就發送get請求
const server = http.createServer((req,res) => {
console.log(req.method); //GET
const url = req.url; //獲取請求的完整 url
//querystring.parse("name=whitemu&sex=man&sex=women");
/*
return:
{ name: 'whitemu', sex: [ 'man', 'women' ] }
*/
req.query = querystring.parse(url.split('?')[1]); //解析 querystring
res.end(JSON.stringfy(req.query));//將對象轉換成字符串,res.end返回一個字符串
})
附註:
querystring.parse將字符串轉成對象,其實就是把url上帶的參數串轉成數組對象。
JSON.stringify() 方法是將一個JavaScript值(對象或者數組)轉換爲一個 JSON字符串,JSON.parse() 方法用來解析JSON字符串,構造由字符串描述的JavaScript值或對象。
//JSON.parse()
var json = '{"result":true, "count":42}';
obj = JSON.parse(json);
console.log(obj);
console.log(obj.result);
> { result: true, count: 42 }
> true
//JSON.stringify()
JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify("foo"); // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify({ x: 5 }); // '{"x":5}'
參考文章:
https://blog.csdn.net/qq_34645412/article/details/80087829
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
post請求:
1.post請求,即客戶端要向服務端傳遞數據,如新建博客
2.通過post data傳遞數據
3.瀏覽器無法直接模型,需要手寫j,或者通過postman
post接收數據是通過數據流的方式,數據流就是所謂的stream。
post方式發送的數據,可大可小。小的話還好,數據量大的時候,不闊能一次性接收,而是分流接收
每次接收的到數據都會觸發 req.on(‘data’,chunk=>{}),當接收完之後就會觸發req.end 事件
const server = http.createServer((req,res) => {
if(req.method === 'POST'){ //POST
//數據格式
console.log('content-type',req.headers['content-type']);
//接收數據
let postData = ""
req.on('data',chunk => {
postData += chunk.toString();
})
req.on('end', () => {
console.log(postData);
res.end('hello world')
})
}
})
get和post
const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
const method = req.method
const url = req.url
const path = url.split('?')[0]
const query = querystring.parse(url.split('?')[1])
// 設置返回格式爲 JSON
res.setHeader('Content-type', 'application/json')
// 返回的數據
const resData = {
method,
url,
path,
query
}
// 返回
if (method === 'GET') {
res.end(
JSON.stringify(resData)
)
}
if (method === 'POST') {
let postData = ''
req.on('data', chunk => {
postData += chunk.toString()
})
req.on('end', () => {
resData.postData = postData
console.log(postData)
// 返回
res.end(
JSON.stringify(resData)
)
})
}
})
server.listen(8000)
console.log('OK')
3.搭建開發環境
1.初始化 npm 環境: npm init -y
2. package.json 的 main 值默認 index.js 爲主程序
3. bin 目錄裏面一般爲可執行文件
4. nodemon: 檢測文件變化,自動重啓 node
5.cross-env: 設置環境
5. 安裝 nodemon 和 cross-env: npm install nodemon cross-env --save-dev --registry=https://registry.npm.taobao.org
6. process.env.NODE_ENV
: 獲取當前運行環境
nodemon監測文件變化,自動重啓Node
cross-env設置環境變量,之後做環境區分使用,運行跨平臺設置和使用環境變量的腳本
process是node的全局變量,process.env.NODE_ENV可以獲取當前運行環境
cross-env介紹
這個小插件主要解決 windows 下 無法設置 NODE_ENV=development 這個東西. 因爲程序可以根據 NODE的環境變量,做出不同選擇,進行不同的設置.
這個在 linux 下是可以設置的,但在 windows 下不行
package.json配置
//process是node提供的全局變量。
dev: cross-env NODE_ENV=dev nodemon ./bin/www.js //開發環境
prd: cross-env NODE_ENV=production nodemon ./bin/www.js //生產環境
參考文章:
http://elickzhao.github.io/2017/06/nodejs%20%E7%9A%84%20cross-env%20%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BB%8B%E7%BB%8D/
https://segmentfault.com/a/1190000005811347
4.接口設計
app.js
//引用和處理路由(handleBlogRouter和handleUserRouter)
const handleBlogRouter = require('./src/router/blog')
const handleUserRouter = require('./src/router/user')
const serverHandle = (req,res) => {
//設置返回格式 JSON
res.setHeader('Content-type','application/json')
//處理blog路由
const blogData = handleBlogRouter(req,res)
if(blogData){
res.end(
JSON.stringify(blogData)
)
return
}
//處理user路由
const userData = handleUserRouter(req,res)
if(userData){
res.end(
JSON.stringify(userData)
)
return
}
//未命中路由,返回404
res.writeHead(404,{"Content-type":"text/plain"})
res.write("404 Not Found\n")
res.end()
}
module.exports = serverHandle
src/router/blog.js
const handleBlogRouter = (req,res)=> {
const method = req.method //GET POST
const url = req.url //獲取url
const path = url.split('?')[0]
//獲取博客列表
if (method === 'GET' && path === '/api/blog/list') {
return {
msg : '這是獲取博客列表的接口'
}
}
//獲取博客詳情
if(method === 'POST' && path === '/api/blog/detail'){
return {
msg : '這是獲取博客詳情的接口'
}
}
//新建一篇博客
if(method === 'POST' && path === '/api/blog/new'){
return {
msg : '這是新建博客詳情的接口'
}
}
//更新一篇博客
if(method === 'POST' && path === '/api/blog/update'){
return {
msg : '這是更新博客詳情的接口'
}
}
//一篇博客
if(method === 'POST' && path === '/api/blog/del'){
return {
msg : '這是刪除一篇博客的接口'
}
}
}
module.exports = handleBlogRouter
src/router/user.js
const handleUserRouter = (req,res)=> {
const method = req.method //GET POST
const url = req.url //獲取url
const path = url.split('?')[0]
//獲取博客列表
if (method === 'POST' && path === '/api/user/login') {
return {
msg : '這是登陸的接口'
}
}
}
module.exports = handleUserRouter
雖然路由沒有寫具體處理代碼,但是展示了路由的使用和路由引用和處理,基本的路由創建,路由內容和路由使用有個框架,下一步就是具體代碼編寫和邏輯進一步劃分。
5.博客系統的分層
上面只是把路由分離出來,我們要如何進一步劃分代碼見邏輯呢?
第一層是bin/www.js,這一層完全是createServer的邏輯。這個邏輯跟外面的業務代碼沒有任何關係。(關於數據的處理,使用回調函數,放在了app.js裏面)
第二層是app.js,這是一個系統中關於http的基本設置(比如:設置返回格式JSON、獲取path、解析jquery處理相關模塊的路由和設置404情況),跟業務代碼關係也不大。(只是負責調用路由,關於路由的具體邏輯放在router中)
第三層router,第二層中引用的路由,所以第三層就是路由。路由就有涉及到一些業務代碼,有了會返回給請求一個正確的格式數據。這個路由會去處理一些數據,返回正確的格式。具體數據的處理它不用管,它只負責路由相關的事情。
第四層controller,處理和管理數據,到了controller裏面就不用關心request和response、path、query等東西,就只關心數據。然後返回數據,數據之後怎麼處理,controller是不管的。
第五層model
附註
第一層和第二層中的www.js 和 app.js 的目的不一樣,第一個是啓動服務,第二個是加載註冊各種業務組件,兩者應該分離,符合設計原則。
第一層(創建服務器)、第二層(系統基本設置)、第三層(路由)都屬於 MVC 的C ,即 controller ,MVC 是一個大概念上的分層,不代表每個部分不能再分層。如果項目再複雜,C 和 M 之間可能還要加一個 services 層,如 egg.js 的目錄,就有 services 層。
router層負責拿參數
controller層負責數據的計算 最終拿到數據
model層負責 生成一個實例 將data message errno 返回給前端
6.Nodejs讀取文件
const fs = require('fs')
const path = require('path')
//resolve可以通過拼接多個步驟的方式把文件名拼出來,__dirname是指當前目錄
const fullFileName = path.resolve(__dirname,'files','a.json')
fs.readFile(fullFileName,(err,data) => {
if(err){
console.error(err)
return
}
//fs.readFile 讀出來的data數據是二進制的流文件,所以要經過toString轉換
console.log(data.toString())
})
我們要注意resolve和toString的使用。