這部分示例將致力于用 Node.js 模擬一個類似于 Apache 的 Web 服務器,處理瀏覽器端的請求,將相關的頁面響應給瀏覽器。首先,我們要在code目錄下執行mkdir 03_webSever命令來創建用于存放這一組示例的目錄。然后執行以下步驟:
1、index.htm:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>首頁</title>
</head>
<body>
<h1>你好,nodejs!</h1>
<p> <a href="login.htm">請登錄!</a> </p>
</body>
</html>
2、login.htm:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>登錄頁面</title>
</head>
<body>
<h1>你已經登錄。。。</h1>
<p> <a href="index.htm">回首頁!</a> </p>
</body>
</html>
3、style.css:
body {
background: gray;
}
2、在code/03_webSever目錄下執行touch 03-webServer.js命令,創建腳本文件,并輸入如下代碼:
const http=require('http')
const fs=require('fs')
const server=http.createServer()
server.on('request', function(req, res) {
const webRoot='./www'
const url=req.url
if ( url==='/' ) {
url='/index.htm'
}
fs.readFile(webRoot+url, function(err, data) {
if ( err !==null ) {
console.error('錯誤信息:' + err.message)
return res.end('<h1>404 頁面沒找到!</h1>')
}
res.end(data)
})
})
server.listen(8080, function(){
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
3、保存所有文件后,在code/03_webSever目錄下執行node 03-webServer.js命令,然后打開瀏覽器并訪問http://localhost:8080/,就會看到如下頁面:
這一部分本示例將以生成個人信息頁面為例,演示在服務器端基于 Node.js 使用art-template模板引擎來生成網頁。為此,我們需要在code目錄下執行mkdir 04_templatingEngine命令來創建用于存放這一組示例的目錄。
首先來示范一下如何使用art-template模版引擎的渲染單一模版文件,請跟著以下步驟來構建示例:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>{{ name }}的個人信息</title>
</head>
<body>
<h1>{{ name }}的個人信息</h1>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年齡:</td><td>{{ age }}</td></tr>
<tr><td>性別:</td><td>{{ sex }}</td></tr>
<tr>
<td>愛好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
</body>
</html>
3、在code/04_templatingEngine目錄下執行touch 04-useTemplating_engine.js命令,創建一個腳本文件,具體如下:
const http=require('http')
const fs=require('fs')
const template=require('art-template')
class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}
const server=http.createServer()
server.on('request', function(req, res){
const url=req.url
let boy=null
if ( url==='/' ) {
boy=new human('凌杰', '37', '男', ['看書', '看電影','旅游'])
} else if ( url==='/wang' ) {
boy=new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
}
if ( boy===null ) {
return res.end('<h1>404 頁面沒找到!</h1>')
}
fs.readFile('./singleTpl.htm', function(err, data){
if ( err !==null ) {
return res.end('<h1>404 沒找到模版文件!</h1>')
}
const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})
res.end(strHtml)
})
})
server.listen(8080, function(){
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
4、保存所有文件后,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine.js命令,然后打開瀏覽器并訪問http://localhost:8080/wang,就會看到如下頁面:
在同一 Web 應用中,所有的頁面通常都由相同的頭部和底部元素,所以為了減少代碼的冗余,提高重用率,開發者們通常會考慮將重復的部分獨立成一個單獨的模版文件,然后用相互包含的方式組合成頁面。下面就繼續以art-template模板引擎為例來演示一下如何將多個模版組合渲染成單一的 HTML 頁面,其具體步驟如下:
1、tpl1.art :
<header>
<h1>查看個人信息</h1>
<br>
</header>
2、tpl2.art :
<footer>
<div>
<p>? 2016 owlman.org;本站系純HTML5站點。</p>
</div>
</footer>
2、在code/04_templatingEngine目錄下執行touch multiTpl.htm命令創建用于組合的 HTML 頁面文件,并在其中輸入以下代碼:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>查看個人信息</title>
</head>
<body>
{{ include './tpl1.art' }}
<h2>{{ name }}的個人信息</h2>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年齡:</td><td>{{ age }}</td></tr>
<tr><td>性別:</td><td>{{ sex }}</td></tr>
<tr>
<td>愛好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ include './tpl2.art' }}
</body>
</html>
3、在code/04_templatingEngine目錄下執行
cp 04-useTemplating_engine.js 04-useTemplating_engine2.js命令,將之前的代碼復制一份,并修改如下:
const http=require('http')
const fs=require('fs')
const template=require('art-template')
template.defaults.root=__dirname // 配置模版的查找根目錄
class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}
const server=http.createServer()
server.on('request', function(req, res){
const url=req.url
let boy=null
if ( url==='/' ) {
boy=new human('凌杰', '37', '男', ['看書', '看電影','旅游'])
} else if ( url==='/wang' ) {
boy=new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
}
if ( boy===null ) {
return res.end('<h1>404 頁面沒找到!</h1>')
}
fs.readFile('./multiTpl.htm', function(err, data){ // 修改了要讀取的模版文件
if ( err !==null ) {
return res.end('<h1>404 沒找到模版文件!</h1>')
}
const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})
res.end(strHtml)
})
})
server.listen(8080, function(){
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
4、保存所有文件后,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine2.js命令,然后打開瀏覽器并訪問http://localhost:8080,就會看到如下頁面:
當然,如果重復的元素只有頭部和尾部的話,有時候使用模版繼承語法來渲染頁面會是一個更好的選擇,下面就來繼續演示一下art-template模板引擎的繼承語法來渲染 HTML 頁面,其具體步驟如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>{{ name }}的個人信息</title>
</head>
<body>
<header>
<h1>查看個人信息</h1>
<br>
</header>
{{ block 'message' }}
{{ /block }}
<footer>
<div>
<p>? 2016 owlman.org;本站系純HTML5站點。</p>
</div>
</footer>
</body>
</html>
2、在code/04_templatingEngine目錄下執行touch extendTpl.htm命令,創建子模版文件,然后在該文件中輸入以下代碼:
{{ extend 'baseTpl.art' }}
{{ block 'message' }}
<h1>{{ name }}的個人信息</h1>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年齡:</td><td>{{ age }}</td></tr>
<tr><td>性別:</td><td>{{ sex }}</td></tr>
<tr>
<td>愛好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ /block }}
3、在code/04_templatingEngine目錄下執行cp 04-useTemplating_engine.js 04-useTemplating_engine3.js命令,將之前的代碼復制一份,并修改如下:
// 用Node.js生成動態頁面
// 作者:owlman
// 時間:2019年07月12日
const http=require('http')
const fs=require('fs')
const template=require('art-template')
template.defaults.root=__dirname
class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}
const server=http.createServer()
server.on('request', function(req, res) {
const url=req.url
let boy=null
if (url==='/') {
boy=new human('凌杰', '37', '男', ['看書', '看電影','旅游'])
} else if (url==='/wang') {
boy=new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
}
if (boy===null) {
return res.end('<h1>404 頁面沒找到!</h1>')
}
fs.readFile('./extendTpl.htm', function(err, data) {
if ( err !==null ) {
return res.end('<h1>404 沒找到模版文件!</h1>')
}
const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})
res.end(strHtml)
})
})
server.listen(8080, function(){
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
4、保存所有文件后,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine3.js命令,然后打開瀏覽器并訪問http://localhost:8080,就會看到與之前相同的頁面。
這一部分示例將致力于演示用 Node.js 處理 Web 表單,我們將會分別示范如何用get和post兩種方法來處理表單的請求。首先,我們要在code目錄下執行mkdir 05_webForm命令來創建用于存放這一組示例的目錄。
先用一個信息查詢程序來演示一下如何處理使用get方法來發送請求的表單。首先,在code/05_webForm目錄下執行mkdir get_form命令,并執行以下步驟:
在code/05_webForm/get_form目錄下執行npm install art-template命令,將art-template安裝到當前示例項目中。
在code/05_webForm/get_form目錄下執行touch index.htm,創建一個模版文件,具體如下: <!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>個人信息查詢</title>
</head>
<body>
<h1>個人信息查詢</h1>
<form action="/query" method="GET">
<label for="message">請輸入要查詢的姓名:</label>
<input type="text" name="qname" />
<input type="submit" value="查詢" />
</form>
<br />
{{ if name }}
<table>
<caption>{{ name }}的個人信息</caption>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年齡:</td><td>{{ age }}</td></tr>
<tr><td>性別:</td><td>{{ sex }}</td></tr>
<tr>
<td>愛好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ else if query_error }}
<h2>沒有找到相關信息!</h2>
{{ /if }}
</body>
</html>
3、在code/05_webForm/get_form目錄下執行touch app.js,創建一個腳本文件,具體如下: const http=require('http')
const fs=require('fs')
const url=require('url')
const template=require('art-template')
class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}
const db=[
new human('凌杰', '37', '男', ['看書', '看電影','旅游']),
new human('蔓兒', '25', '女', ['看書', '看電影','寫作']),
new human('張語', '32', '女', ['看書', '旅游','繪畫'])
]
const server=http.createServer(function(req, res){
const query=url.parse(req.url, true)
let obj=null
let query_error=false
if ( query.pathname==='/' ) {
query_error=false
}
else if (query.pathname==='/query') {
for(let i=0; i < db.length; ++i) {
if (db[i].name==query.query["qname"]) {
obj=db[i]
}
}
if ( obj===null ) {
query_error=true
}
} else {
return res.end('<h1>404 頁面沒找到!</h1>')
}
fs.readFile('./index.htm', function(err, data){
if ( err !==null ) {
return res.end('<h1>404 沒找到模版文件!</h1>')
}
let strHtml=null
if ( obj !==null ) {
strHtml=template.render(data.toString(), {
name : obj.name,
age : obj.age,
sex : obj.sex,
items: obj.items,
query_error: query_error
})
} else {
strHtml=template.render(data.toString(), {
name : false,
query_error: query_error
})
}
res.end(strHtml)
})
})
server.listen(8080, function() {
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
4、保存所有文件后,在code/05_webForm/get_form目錄下執行node app.js命令,結果如下:
先來演示如何處理使用post方法來發送請求的表單。首先,在code/05_webForm目錄下執行mkdir post_form命令,并執行以下步驟:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>個人信息管理</title>
</head>
<body>
<h1>個人信息管理</h1>
<table>
<caption>個人數據表</caption>
<tr><th>姓名</th><th>年齡</th><th>性別</th><th>愛好</th></tr>
{{ each db }}
<tr>
<td>{{ $value.name }} </td>
<td>{{ $value.age }} </td>
<td>{{ $value.sex }} </td>
<td>{{ each $value.items }} {{ $value }} {{ /each }}</td>
</tr>
{{ /each }}
</table>
<form action="/add" method="POST">
<table>
<caption>錄入新人員</caption>
<tr><td>姓名:</td><td><input type="text" name="uname" /></td></tr>
<tr><td>年齡:</td><td><input type="text" name="age"></td></tr>
<tr><td>性別:</td><td><input type="text" name="sex"></td></tr>
<tr><td>愛好:</td><td><input type="text" name="items"></td></tr>
</table>
<input type="submit" value="添加" />
</form>
</body>
</html>
3、在code/05_webForm/post_form目錄下執行touch app.js,創建一個腳本文件,具體如下:
const http=require('http')
const fs=require('fs')
const url=require('url')
const querystring=require('querystring')
const template=require('art-template')
class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}
const db=[
new human('凌杰', '37', '男', ['看書', '看電影','旅游']),
new human('蔓兒', '25', '女', ['看書', '看電影','寫作']),
new human('張語', '32', '女', ['看書', '旅游','繪畫'])
]
const server=http.createServer(function(req, res){
const query=url.parse(req.url, true)
if ( query.pathname==='/' ) {
fs.readFile('./index.htm', function(err, data) {
if ( err !==null ) {
return res.end('<h1>404 沒找到模版文件!</h1>')
}
const strHtml=template.render(data.toString(), {
"db": db
})
res.end(strHtml)
})
}
else if ( query.pathname==='/add' ) {
req.on('data', function(chunk) {
const obj=querystring.parse(chunk.toString())
db.push(new human(
obj['uname'],
obj['age'],
obj['sex'],
obj['items'].split(','),
))
})
res.writeHead(302, {
'location': `/`
})
res.end()
} else {
return res.end('<h1>404 頁面沒找到!</h1>')
}
})
server.listen(8080, function(){
console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
})
4、保存所有文件后,在code/05_webForm/post_form目錄下執行node app.js命令,結果如下:
一篇基礎部分,介紹了Node-RED的功能,應用場景、安裝、配置及基本使用。那今天將繼續深入高級用法,還有初步實現原理。這樣的話,就可以對Node-RED更深入的掌握,可以更加靈活應用起來。如果默認的功能如果不能滿足需求,我會介紹擴展方法,大家熟悉了就可以定制自己的流式處理程序了。我已經把它移植到了邊緣計算網關當中了,充當規則引擎。你們也充分發揮想象力,盡情發揮它的威力。
我們挑選一些重點的節點做下說明。
節點類型 | 節點名稱 | 節點說明 |
輸入 | Inject | 注入節點,向flow中注入payload |
Catch | 捕獲節點異常 | |
Status | 從其它節點發送狀態消息 | |
MQTT | MQTT節點消息輸入節點 | |
HTTP | HTTP 請求輸入節點 | |
Web socket | Web socket協議輸入節點 | |
TCP | TCP協議輸入節點 | |
UDP | UDP協議輸出節點 | |
Serial | 串口數據輸入節點 | |
處理 | Template | 按照預制的模板,設置message屬性,或者數據轉換 |
Delay | 增加延時 | |
Function | 用戶直接編寫javascript處理函數。 | |
Change | 可以對payload進行搜索替換 | |
社交 | 發送郵件 | |
發送twitter消息 | ||
存儲 | file | 用來將信息保存到文件當中。 |
高級 | exec | 可以設置調用執行的命令,或者腳本 |
Raspberry Pi | rpi gpio | 控制樹莓派GPIO端口。輸入或者輸出兩種 |
rpi mouse | 控制樹莓派鼠標控制。 | |
rpi keyboard | 樹莓派鍵盤控制。 |
在使用的時候,上面的Node應該可以滿足的大部分應用了。
2.自定義節點
Node-Red除了系統內置的節點,還提供了自定義節點的功能,是它的一大特點。具體文檔參考:https://nodered.org/docs/creating-nodes/first-node.html
場景:當系統默認組件功能不足,用戶需要個性化功能,外觀設置的時候,就會用到。
可以把npm module封裝為node.每個node包含兩部分:
該模塊可以通過npm安裝到Node-Red當中。
JS文件中需要完成:
注冊節點類型
2.定義如何接收消息
定義如何接收消息
3.定義如何發送消息
定義如何發送消息
還有如何關閉節點,在節點日志處理,國際化等操作。官方文檔中做了非常詳細的說明。
3.在線Node及Flow
Node-Red除了提供可視化編輯器以外,自己動手創建flow。還有一個強大的在線社區,上面已經提供大量資源。包括Node與Flow。我們在使用Node-Red的過程中,首先搜索一下是不是已經有現成的Node, Flow可以用。打開:https://flows.nodered.org 就可以訪問到在線資源。比如,在工業里常用的modbus協議,我們看看如何支持:
搜索Node節點
安裝modbus節點
點擊進去就可以訪問到該node的詳細信息。告訴用戶如何安裝,如何使用。安裝完成后,重啟Node-Red就可以使用了。
4.綜合例子
綜合例子中我們綜合MQTT輸入,email節點。來看復雜一些的flow創建及執行過程。在我們日常的使用過程中,大部分就是這種場景。
MQTT處理流程,Node-RED處理MQTT消息體,進行預警
這就是一個典型的物聯網+Node-RED應用。Node-RED充當了物聯網平臺的規則引擎。這是放在云端。當然把它放在硬件網關中,充當邊緣計算引擎也是可以的。大家可以動手親自試一下。要是有問題,可以留言或者自私。
今天的介紹就到這里。下一期,我將帶大家深入Node-RED內部工作機制,以及源代碼分析。可以幫助大家透徹了解它。 其實這個軟件,大家可以充分發揮想象力,它能干的事情非常多。日常工作,可以定制為各種商業化軟件。
過一個階段測試,終于找到兩個 markdown 轉 html 并實現代碼高亮的工具,并以最簡代碼(幾十行)實現了炫酷的展示效果。步驟很少,也很簡單,零基礎也應該一看就會。往下看吧......,需要安裝有Node環境哦!
因為markdown 文檔編輯器具有語法簡單、文件簡潔、標準化、使用廣泛等特點,所以一直習慣采用 markdown 編輯器寫文章,但仍有一些網站不支持 markdown 編輯器,想嘗試將 markdown 轉成 html 再發布,采用 Javascript 來實現,尤其想達到 CSDN 博客、簡書這樣的展示效果。
圖1 CSDN 博客 markdown 文章及代碼塊高亮效果
圖2 簡書 markdown 文章及代碼高亮效果
比較常用的 M2H 插件有:
從網上獲得的 markdown-it 和 marked 的比較,主要表現在:在性能、使用簡單程度、正則解析(中文支持)方面,marked 比較好;在擴展性上,markdown-it 表現則更好一些。 由于 markdown 應用比較成熟,而且一般使用也不會太復雜,所以選擇性能好、中文支持好的 marked,至于 marked 的擴展性,也嘗試了一下,滿足常見的應用是沒問題的。
代碼塊高亮插件有:
對 heiglightjs 和 prismjs 分別做了簡單測試,發現 prismjs 使用非常簡單,易于上手,且代碼精簡,效率高,而 heiglightjs 相對比較復雜,使用起來不習慣,也許是各人喜好不同吧。所以選擇使用 prismjs 插件。
M2H 一般有兩種應用模式:一種是前端渲染,如在 vue 中使用;另一種是在后端生成 html 文件,在瀏覽器展示,如在 node 中使用。 此處我選擇在 node 中生產靜態 html 文件的應用模式,用戶可以在電腦端當作 markdown 轉 node 的工具使用; 至于前端渲染,vue 頁面打包時總會包含一個 chunk-vendors.js 文件,會引起頁面加載變慢,有很多辦法壓縮該文件,但這已經超出了 M2H 的范疇了,在此不再贅述。
圖3 最終實現的markdown 轉化及代碼塊渲染效果
創建目錄:marked-prismjs,使用 vsCode 打開該目錄。
在 vsCode 里的終端輸入相關命令進行插件安裝 1)安裝 marked 插件
npm install marked --save
2)安裝 prismjs
npm install prismjs --save
訪問官網下載頁:https://prismjs.com/download.html
圖4 themes 選項
圖5 編程語言包選項
圖6 插件選項
# 這是H1標簽
## 下面是 javascript 代碼:
```javascript let a=1; console.log(a); ```
創建 m2h.js 文件,內容為:
const { marked }=require('marked') // 引入 marked 模塊
const prism=require('prismjs') // 引入 prism 模塊
const fs=require('fs') // 引入文件處理模塊
const mdContent=fs.readFileSync('test.md', 'utf-8') // 讀取 markdown 文件內容
// 下面是 marked 擴展功能,當節點(token)類型是代碼(code)時,自定義渲染節點功能,及使用自定義的 renderer 函數來代替默認 renderer 函數,達到改寫當前節點生成的 html 代碼的樣式
marked.use({
extensions: [{
name: 'code',
renderer(token) {
if (token.type==='code') {
let codeHtml=`<pre class="language- round"><code class="language-`+token.lang+` line-numbers">`
codeHtml+=Prism.highlight(token.text, Prism.languages.javascript, 'javascript')
codeHtml+=`</code></pre>`
return codeHtml
}
}
}]
})
// 定義 html 文件頭部代碼
var htmlContent=`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="prism.css">
<script src="prism.js"></script>
</head>
<style>
.round {
box-shadow: 10px 10px 5px #888888;
border-radius: 10px;
}
</style>
<body>
`
htmlContent +=marked.parse(mdContent) // 疊加上 marked 生成的 html 代碼
htmlContent+=`</body> // 疊加底部代碼(補齊)
</html>`
fs.writeFileSync('test.html', htmlContent) // 將最終的 html 內容寫入到文件 test.html
測試運行:
node m2h.js
每運行一次,就會重新生成新的 test.html 文件。
在 vsCode 里鼠標右鍵點擊 test.html 文件,選擇“Open with Live Server”,
圖7 Open with LiveServer
會在瀏覽器里打開 test.html 頁面,test.md 已經成功生成了 test.html,實現了代碼塊高亮、語言顯示及拷貝按鈕及行號。得到的最終結果如下圖:
圖8 效果展示
達到了當初計劃實現的功能需求。
以上通過簡單的幾十行代碼就實現了如此炫酷的功能展示,體現了插件的強大功能,同時通過自定義擴展節點渲染函數,也展現了插件的靈活性和可擴展性。此次測試僅僅是個引子,后續會繼續進行研究和測試,感興趣請收藏、關注,方便共同學習、進步。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。