摘要:階段是事件循環的第一階段習慣上往往都會設置數將回調函數添加到事件循環的階段的隊列中等待執行。
后端知識點總結——NODE.JS(高級) 1.Node入門:
什么是: 針對網絡應用開發的平臺
主要特征:
基于Google的JavaScript運行時引擎V8
擴展了Node標準類庫: TCP,同步或異步文件管理,HTTP
為什么使用Node:
可以在服務器端運行js: 現有前端團隊可直接參與后端js開發
js天生支持非阻塞IO:
IO: 代表一切數據進出程序的操作:
包括: 文件讀寫, 數據庫操作, 網絡操作
問題: 有延遲
傳統阻塞IO: IO操作會阻塞當前主線程,直到本次IO操作完成,才能執行后續代碼。
非阻塞IO: 即使處理較慢的IO操作時,主進城仍然能處理其他請求
Js天生支持非阻塞: 回調函數=事件循環+回調隊列
所有非阻塞的操作,返回的結果暫時在回調隊列中等待
由事件循環,自動依次取回到主程序中恢復執行
回調隊列在主程序之外存儲回調函數,所以,不會干擾主程序執行
非阻塞在Web服務器中:
普通服務器端應用: 雖然可實現每個請求獨立線程/進程, 但如果一個請求中,包含多個阻塞IO操作(訪問數據庫,網絡,讀寫硬盤文件),該請求返回的時間就等于所有IO操作的時間總和——慢
Node服務器端應用: 不但每個請求是一個獨立的線程,且,每個請求內的每個IO操作,都是非阻塞的。
一個包含多個IO操作的請求,返回的總響應時間,僅僅等于其中一個時間最長的IO操作的時間。 Node.js vs javascript: Javascript: 編程語言, 依照ECMAScript
2種運行環境:
客戶端瀏覽器: 由各種客戶端瀏覽器中的js解釋器執行
擴展: DOM API 和 BOM API 主要目的是為了操作網頁內容和瀏覽器窗口
獨立的js解釋器:Node.js 應用程序開發和運行的平臺
僅支持ECMAScript
擴展: 各種專門的服務器模塊: TCP, HTTP, 文件讀寫, MYSQL
構建一個簡單的node應用:
創建一個新的node項目: 基本命令:
mkdir 項目文件夾
cd 項目文件夾
npm init //負責在當前所在的項目目錄下自動生成package.json配置文件
運行:node 入口文件.js
Node應用都是由模塊組成
模塊就是組織程序功能的一種文件或文件夾
Node應用采用CommonJS模塊規范
CommonJS規定:
每個文件就是一個模塊,有自己的作用域——避免全局污染
一個文件內定義的變量,函數,類都是該文件私有,對其它文件默認不可見
對象,方法和變量也可以從一個文件/模塊中導出(exports),用在其它文件/模塊中。
實際項目中,都是將各種功能/數據,劃分為不同項目模塊來管理
如何定義一個模塊:2步:
在模塊/文件中定義業務代碼(對象,class,函數)
將內部的功能拋出,用于將來其它js文件調用
2種情況:
2.1面向對象的方式:定義一種class或一個對象,包裹屬性和功能
將class或對象直接賦值給module.exports
其中: module,指當前模塊對象/當前文件
exports是當前module對象的一個屬性 本質上也是一個對象,保存將來要拋出的所有東西 exports是當前模塊對外的唯一接口
今后,只要希望將模塊內部的東西,拋出到外部,供其它文件使用時,都要添加到module.exports上
其它文件要想使用當前模塊的功能,就必須用require引入當前模塊,而require的本質是找模塊的exports.
在文件中,定義多個零散的方法
將多個零散的方法添加到module的exports上
其實,可先將零散的方法,先集中定義在一個對象中,再將整個對象賦值給module.exports屬性
引入模塊: require() 專門負責加載模塊文件
何時: 只要在另一個js文件中,引入自定義模塊并獲取內容時,都用require
本質: 找到js文件,并執行,返回module.exports對象
優化: 單例模式singleton: 始終保持項目中只有一個對象的實例
模塊的引入和加載也是單例模式: 模塊只在第一次被require時,創建。之后,緩存在內存中。反復require不會導致反復創建模塊對象。
強調: 模塊是同步加載:前一個加載完,后一個才能開始
強烈建議: 所有require必須集中在頂部
路徑: 以./開頭,表示使用相對路徑,相對于當前正在執行腳本所在路徑——不能省略!
以/開頭,表示Linux系統根目錄——絕對路徑 以自定義變量開頭,表示在變量保存的地址下繼續查找 什么前綴也不加!只寫模塊名: 表示加載一個核心模塊或項目引入的第三方模塊 路徑查找順序: /usr/local/lib/node/模塊名.js /home/user/projects/node_modules/模塊名.js /home/user/node_modules/模塊名.js /home/node_modules/模塊名.js /node_modules/模塊名.js
坑: 簡寫: module.exports.fun=function(){…}
可簡寫為: exports.fun=function(){…} exports其實是module.exports的別名 var exports=module.exports; 問題: 給exports賦值,無法賦值給module.exports 因為exports只是一個變量,臨時保存module.exports的地址值。再次給exports賦任何新值,都導致exports與module.exports分道揚鑣! 避免: 不要用簡寫exports3.目錄模塊:
何時: 當一個模塊代碼,復雜到需要進一步細分時,一個模塊,就可能由多個文件組成,保存在一個文件夾里。
如何:
創建文件夾,集中保存相關的多個js文件模塊
在文件夾中添加一個主模塊(index.js),主模塊中,引入并組織好多個小模塊一起導出
在文件夾中添加package.json文件,其中:
{ "name":"模塊名", "main":"./主模塊相對路徑" }
其實, 如果沒有main甚至沒有package.json,也行。
會自動優先找文件夾下的index.js
引入目錄模塊: require("./目錄名")
如果希望直接用目錄名引用模塊,不加相對路徑:
將目錄放入node_modules文件夾中
npm: 第三方模塊的包管理工具: 查詢,下載
除了核心模塊和自定義本地模塊,node生態系統還提供了大量優質第三方模塊
如何:
查詢模塊:
模糊查找: npm search 模塊名 精確查找: npm search /^模塊名$/ 如果現實完整描述: npm search /^模塊名$/ --parseable
安裝模塊: 2個位置:
全局安裝: npm install -g 模塊名
路徑: Linux: /usr/local/lib/node_modules
Windows: C:Users用戶名AppDataRoaming pm ode_modules
項目本地安裝: npm install 模塊名 -save
全局對象:
全局作用域對象不是window,而是global
ECMAScript標準中原本規定的就是global
在瀏覽器中被window代替
強調: 交互模式: 直接在命令行中測試node應用,所有全局變量/全局函數自動成為global的成員
腳本模式: 通過加載js文件執行node應用,文件內的"全局變量/全局函數",僅當前文件所有,不會成為global的成員——避免了全局污染
console對象:
測試重要手段: 打樁: 在關鍵位置輸出關鍵變量的值
輸出文本信息: 瀏覽器中4種輸出,node中合并為2中:
console.log/info() 輸出普通的文本信息
console.error/warn() 輸出錯誤信息
其實: console.xxx()都自帶格式化功能
Console.log vs console.error: .error可直接導出到文件日志中
如何: node xxx.js 2> error-file.log 其中:2>表示輸出流,專門向硬盤文件寫入內容
輸出耗時:
Console.time("標簽"); //預備,開始!
正常程序邏輯
Console.timeEnd("標簽"); //完成! 自動輸出與time之間的時間間隔
單元測試:
什么是: 對程序中最小的執行單元進行測試
開發人員主動對自己的函數執行單元測試
如何: console.assert(判斷條件, "錯誤提示")
只有條件不滿足時,才輸出msg
輸出堆棧:
console.trace()
全局對象: process:
process.platform process.pid process.kill(pid);
控制臺輸入輸出:
2步:
讓控制臺進入輸入狀態:
process.stdin.resume()
process.stdin.setEncoding("utf-8")
監聽stdin的data事件:
在控制臺輸入后,按回車,會觸發stdin的data事件
process.stdin.on("data",text=>{ process.stdout.write( … text … ) })
控制臺參數:
2步: 1. 定義關聯數組,保存參數名和參數對應的處理函數
2. 啟動時, process.argv數組可自動獲得傳入的所有參數, 根據參數調用不同的處理函數
process.argv: ["node.exe","xxx.js","參數值1","參數值2",…]
高精度計時:
精確到納秒, 優點: 不受系統時間影響
如何: 2步: 1. 獲得開始的時間戳: var start=process.hrtime();
2. 獲得結束時間戳: var diff=process.hrtime(start);
diff: [秒數, 納秒]
獲得秒差: diff[0]+diff[1]/1e9 獲得毫秒差: diff[0]*1000+diff[1]/1e6
Vs console.time/timeEnd:
time/timeEnd: 缺: 精度低, 優: 效率高
hrtime: 優: 精度高,且不受系統時間影響
缺點: 效率低
非I/O的異步操作(定時器):
何時: 要執行異步回調時
如何:
setTimeout/setInterval() 將回調函數添加到事件循環的timer階段的隊列中等待執行。
Timer階段是事件循環的第一階段
習慣上: setTimeout往往都會設置ms數
setImmediate() 將回調函數添加到事件循環的check階段的隊列中等待執行。
Check階段比Timer要晚執行
習慣上: 并不設置毫秒數,而是普通的追加到等待隊列末尾即可。
process.nextTick() 將回調函數加入nextTickQueue隊列等待執行
nextTickQueue不參與事件循環,而是在開始timer之前,就立刻執行nextTickQueue中的回調函數
優點: 不會有延遲
自定義的EventEmiter
5.EventEmitter類型:Node.js所有異步I/O操作完成時,都會發送一個事件到事件隊列
Node.js中許多對象都會觸發事件:
比如: http模塊: 創建Server對象,監聽http請求
一旦收到一個http請求,則立刻觸發事件,將處理函數放入事件隊列 fs模塊: 在每次讀寫完文件時,也會觸發事件,將處理函數放入事件隊列
什么是EventEmitter: 專門封裝事件監聽和事件觸發的API的一種類型
所有可以觸發事件的對象,都是EventEmitter類型的子對象
如何讓一個對象可以監聽并觸發事件:
引入events模塊: const events=require("events")
創建events.EventEmitter類型的子對象:
var emitter=new events.EventEmitter();
用on,為對象添加事件監聽:
emitter.on("自定義事件名",function 處理函數(參數列表){
… 獲得參數, 執行操作 …
})
在任何情況下,使用對象的emit方法,觸發指定的事件:
emitter.emit("自定義事件名",參數值,…)
觸發一次后,自動解綁:
emitter.once("自定義事件名",處理函數)
錯誤處理:
問題: try catch無法捕獲異步調用中的錯誤
解決: Domain
何時: 只要既希望捕獲主程序錯誤,又希望捕獲異步操作的錯誤時
如何:
引入domain模塊: const domain=require("domain")
創建domain對象: const mpDomain=domain.create();
為domain對象添加error事件監聽
mpDomain.on("error",err=>{
console.log("出錯啦!"+err);
})
將可能出錯的程序放入mpDomain中運行:
mpDomain.run(()=>{
musicPlayer.emit("play");
})
6.協議:什么是: 計算機之間通過網絡實現通信時,事先達成的一種"約定"
為什么: 約定使不同廠商的設備,不同操作系統之間,都可按照統一約定,任意通信
什么是: 將大數據分割為一個個叫做包(packet)的較小單元進行傳輸
8.ISO/OSI模型:ISO(國際標準化組織)
OSI(開放式通信系統互聯參考模型)
7層:
應用層: 規定應用程序中的通信細節
包括: HTTP FTP TELNET SMTP DNS
表示層: 負責數據格式的轉換
會話層: 建立連接
傳輸層: 控制總體數據傳輸
包括:
TCP(傳輸控制協議): 可靠傳輸
優: 可靠,客戶端和服務端可雙向通信
缺: 傳輸效率低
何時: 要求可靠性時
UDP(用戶數據報協議):
何時: 對可靠性要求不高,對傳輸效率要求高,且發送小數據(qq, 微信, 在線視頻播放)
網絡層: 將數據分組傳輸到目的地
數據鏈路層: 負責規劃網絡中節點間的路線
物理層: 負責通過以太網,藍牙,光纖發送0/1的比特流
9.TCP/IP: 互聯網協議套件包含: TCP 傳輸控制協議
IP 互聯網協議
TCP/IP不是ISO標準
TCP/IP 只有四層:
鄙視:
TCP/IP四層協議,分別對應ISO/OSI中的哪一層: 圖6
網絡建立連接需要3次握手,斷開連接需要4次握手,分別是:
圖7
HTTP/1.0 1.1 2.0每次升級有哪些不同
10.net模塊:使用net模塊:
可創建基于TCP的客戶端與服務器端通信
創建TCP服務器:
引入net模塊
使用net.createServer方法創建服務端對象server
接受一個回調函數作為參數: 只要有客戶端連接到當前服務端,就自動執行該回調函數 回調函數接受一個socket參數對象,用于與客戶端通信 Socket對象: 是客戶端在服務器端的一個代理對象 可通過socket和真正的客戶端發送和接受消息 Socket對象的data事件,可監聽客戶端發來的消息 回調函數中, data參數為消息的內容 Socket對象的end事件,可監聽客戶端的斷開 Socket的write方法向客戶端輸出消息
調用server的listen方法,綁定到一個端口,監聽客戶端發來的鏈接請求
也接受一個回調函數參數,但僅在啟動監聽后執行一次
創建TCP客戶端:
引入net模塊
使用net.connect()方法向服務器建立連接
var client=net.connect(服務端端口,ip,function(){})
回調函數在連接建立后,自動觸發一次
為client的data事件綁定處理函數,處理函數的data參數自動接收服務端發來的消息
為client的end事件添加處理函數,當客戶端斷開連接時執行操作
在任何位置可用client.write("消息內容")向服務端發送
在任何位置可用client.end() 斷開與服務端連接
使用HTTP模塊:
實現WEB服務器,接受請求并返回響應(代替了apache,tomcat)
模擬客戶端向一個指定的WEB服務器發送請求
創建HTTP服務端:
引入HTTP模塊
創建HTTP服務端server:
var server=http.createServer(function(req,res){
//只要有請求發送到該服務器,就自動觸發該回調函數 //其中: //req對象,封裝了發來的請求信息 //res對象,專門用于向服務器端返回響應 //res.writeHead(狀態碼,{ 屬性:值, …:… ,…}) //res.write("放入響應主體中") //res.end()
})
啟動監聽: server.listen(端口,function(){ … })
創建HTTP請求:
使用http.request()方法創建一個請求(連接),獲得請求對象req
接收2個參數:
options對象參數: host port method path /index.html?page=12 回調函數: 在服務器端返回響應時執行 參數res: 專門用于獲得響應內容(響應頭和響應主體) HTTP協議規定: 先發響應頭部 用res.headers獲得響應頭部對象,用res.statusCode 獲得狀態碼 強調: 響應主題是稍后才發送過來 必須用res.on("data",function(buffer){ … String(buffer) …}) 強調: 凡是從響應中獲得的data,默認都是字符串
req.end()結束并發送請求。
強調:必須加req.end(),請求才能發送出去
http.get()
專門向服務器端發送get請求
是http.request()的簡化:
自動設置method為get;
自動調req.end
但依然需要使用res.on("data",function(buffer){ … })來接受響應主體
分塊:
問題: 如果響應主體過大,一次性傳不過來
解決:
分塊發送和接受,再拼接,再整體轉換 如果分塊接受,res.on("data",function(buf){ … })每收到一塊,就會反復觸發。 其中buf,僅是其中一塊而已
請求文件,保存在本地:
引入fs模塊:
創建寫入流,指向目標文件: var writable=fs.createWriteStream("相對路徑")
使用管道,將寫入流writable連接到res對象上: res.pipe(writable)
響應頭部: res.writeHead(狀態碼,{ })
允許跨域: "Access-Control-Allow-Origin":"請求來源的網站"
指定內容類型:"Content-Type":"application/json" "text/css"
req對象:
請求頭部: req.headers
請求方法: req.method
請求地址: req.url
url的處理:
引入url模塊 用url.parse(req.url,true)將req.url字符串轉為對象 其中true,表示將search中的參數也轉為對象屬性 如何: var obj=url.parse(req.url, true) 其中: obj.query中保存了所有參數及其值
獲得請求參數:
Get: get方式的參數都通過url中的search傳遞
obj=url.parse(req.url,true) obj.query
Post: post方式的參數都是放在請求主體中,沒有在url中
問題:obj.query無法獲得 解決: req.on("data",function(buf){ … }) 問題: String(buf)獲得的是參數的字符串 解決: querystring模塊12.https模塊:
問題: http協議是明文的
危害: 1. 通信使用明文,內容可能被竊聽
2. 不驗證身份,有可能遭遇偽裝 3. 無法證明消息的完整性,消息有可能被篡改
網絡嗅探器:
13.解決: https協議https是更安全的http協議:
客戶端和服務器端的雙向認證
完整性檢查
內容加密
https=http+ssl
ssl/tls: ssl 安全套接層,對傳統socket進一步提供安全的保護
tls 傳輸層安全, 其實是ssl的繼任者14.提供三大服務:
客戶端和服務器端的雙向認證 ——可靠
完整性檢查 ——完整
數據加密 ——機密性
tls/ssl的執行過程:
在服務器端生成私鑰
用私鑰生成一個證書申請文件
將私鑰和申請文件交給第三方CA,第三方CA經過審查,會生成并頒發證書給申請的服務器
證書包含2樣東西: 公鑰+公司的信息
Step1: 客戶端請求https協議的web服務器
Step2: 服務器返回證書給客戶端
Step3: 客戶端拿到證書后,將證書交給CA。
客戶端利用CA中的公鑰隨機生成自己的私鑰 將私鑰發給服務器端
Step4: 服務器端獲得客戶端發來的客戶端私鑰
到此,客戶端和服務器端,擁有了相同的兩個鑰匙
之后,服務器和客戶端發送的所有消息,都用兩個相同的私鑰加密和解密
申請https網站的認證證書:
Step1: 用openssl生成服務器端私鑰:
openssl genrsa -out d:/privatekey.pem 1024
Step2: 用私鑰生成證書申請文件:
openssl req -new -key d:/privatekey.pem -out d:/certificaterequest.csr
Step3: 用私鑰和證書申請文件共同生成證書文件
openssl x509 -req -in d:/certificaterequest.csr -signkey d:/privatekey.pem -out d:/certificate.pem
2.使用node的https模塊創建服務器
Step1: 引入必須的模塊:
const https=require(“https”);
const fs=require(“fs”);
Step2:讀取服務器私鑰和證書文件,保存到服務器程序的變量中
let privatekey=fs.readFileSync(“d:/privatekey.pem”);
let certificate=fs.readFileSync(“d:/certificate.pem”);
Step3: 用https創建服務器端應用程序,提供私鑰和證書,并定義處理請求的回調函數
https.createServer( { key: privatekey, cert: certificate }, (req,res)=>{ res.write(“…”) res.end(); } ).listen(443)
3.用https模塊向https的服務器發送請求
錯誤: http模塊不支持向https服務器發送請求
正確:
var https=require(“https”);
https.get(“https://...”, res=>{
res.on(“data”,buf=>{
buf…
})
})
什么是: 基于node的http模塊和第三方的Connect框架的web框架
Connect框架: 專門將各種各樣的中間件函數粘合在一起,共同處理http請求中的req對象
何時: 只要對req對象反復執行多種操作時,都要用connect組織多個中間件。
如何:
Step1: 安裝connect模塊: npm install connect –save
Step2: 引入connect模塊: var connect=require(“connect”)
Step3: 用connect模塊創建處理req對象的應用程序實例var app=connect();
Step4: 向connect模塊的應用程序實例中添加中間件函數
app.use(function md1(req,res,next){ //加工req對象 … … next(); })
Step5: connect的應用程序實例,必須要放入createServer中用于處理服務器接收到的req對象
http.createServer(app)
總結: express是在connect基礎上的進一步封裝和簡化,所以express也是采用中間件組合的方式,處理req對象
安裝express框架: 2種:
使用本地express模塊,進能夠提供服務支持,需要自定義添加復雜的程序結構
Step1: npm install –save express
Step2: 引入http和express
const http=require(“http”);
const express=require(“express”);
Step3: 創建express應用實例對象:
let app=express();
Step4: 為app添加各種處理中間件函數
app.use(function md(req,res,next){ … …})
Step5: 將app和createServer相連
http.createServer(app).listen(端口號);
使用腳手架, 簡化生成項目的結構:
Step1: 全局安裝express生成器:
npm install –g express-generator
Step2: 用生成器,生成項目腳手架代碼:
express 項目文件夾名 –e //-e 表示用EJS作為前端頁面模板
強調: 只負責生成項目代碼,并不負責下載依賴包
Step3: 為腳手架代碼下載所有依賴包
cd 項目文件夾下
npm install //根據package.json中的依賴項
Step4: 用腳手架代碼啟動nodejs服務器端應用程序: npm start
express項目結構:
./bin/www.js express項目的啟動文件
package.json中: npm start 時 自動執行 node ./bin/www
2./app.js 對express框架的實例對象的配置
要求: 對express實例對象app的所有配置必須放在一個獨立的文件模塊app.js中
然后,在主程序www.js中引入app.js模塊
3../routes/xxx.js 路由模塊
每個子功能,都應該集中定義在一個路由模塊文件中
在app.js中引入路由文件模塊,并將路由文件模塊添加到app的中間件列表中,并設置上級路徑
在每個子路由模塊文件中,創建路由對象,為路由對象添加不同請求方法和不同子路徑下的處理函數
強調: 子路由中的相對路徑,都是在上級路徑之下的相對路徑
改造腳手架項目結構:
補充缺失的模塊:
express-session 讓express可以處理session
connect-flash 強化自動維護session的功能
passport 綜合的用戶驗證解決方案
( 使用passport模塊,實現qq,微信登錄)
在app.js中添加對新模塊的引用:
為項目添加mongodb支持
Step1: 安裝mongoose模塊和promise模塊
mongoose: node js專用的簡化操作mongodb數據庫的模塊
Step2: 創建文件夾./config,在文件夾下添加config.js
在config.js中定義對象模塊,保存連接字符串
module.exports={
db:”mongodb://主機名或ip/數據庫名”}
Step3: 在./config文件夾下創建mongoose.js,保存創建連接對象的代碼:
var config=require("./config"),
mongoose=require("mongoose");
設置mongoose的promise屬性,使用當前項目的promise模塊
mongoose.Promise=require(‘promise’);
var db=mongoose.connect(config.db) module.exports=db;
Step4: 根據業務需要,定義mongoose模型對象:
創建./models文件夾, 在models內為每種業務對象創建專門的模型文件
3步:
引入mongoose,獲得Schema類型
用Schema創建UserSchema結構
將UserSchema編譯為User模型,并拋出為User模塊
Step5: 回到mongoose.js中,在connect之后,引入User模塊require("../models/user.model");
Step6: 回到app.js中,在路由中間件之前,先請求并初始化mongoose.jsrequire("./config/mongoose");
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94521.html
摘要:后端知識點總結基礎不是是一種軟件開發平臺,它的競爭對象歷史第一次有一種語言可以通吃前后端網站阿里云鏡像版本年初年中年底最新版本功能強大可靠,適合大型企業級項目簡單易用適合互聯網項目易用適合平臺性能好適合服務器端密集型項目不適合密集型項目密集 后端知識點總結——NODE.JS基礎 1.Node.js Node.js不是JS,是一種軟件開發平臺,它的競爭對象JSP/PHP/ASP.NET...
摘要:問題回答者黃軼,目前就職于公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。最后附上鏈接問題我目前是一名后端工程師,工作快五年了。 showImg(https://segmentfault.com/img/bVbuaiP?w=1240&h=620); 問題回答者:黃軼,目前就職于 Zoom 公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。 1. 前端開發 問題 大...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結方便自己復習詳細內容會在之后一一對應地補充上去有些在我的個人主頁筆記中也有相關記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結,方便自...
閱讀 2256·2021-11-25 09:43
閱讀 3123·2021-10-14 09:42
閱讀 3484·2021-10-12 10:12
閱讀 1526·2021-09-07 10:17
閱讀 1901·2019-08-30 15:54
閱讀 3181·2019-08-30 15:54
閱讀 1550·2019-08-30 15:53
閱讀 1908·2019-08-29 11:21