2019年2月15日 星期五

[HTML] ribody

怕自己忘記,稍微紀錄一下。
各種各種理由(其中一個就是Vue,不能綁定在body上),導致時常需要一個包在body中的div,當作body來用,像這樣:
===
<body>
    <div id="divBody">
        <ALL CONTENT HERE>
    </div>
</body>
===.
以前我叫這玩意兒divBody,之後打算改一個更像關鍵字的名字,於是想叫他rib──肋骨,又索性改叫做ribody,一方面表示他就是用來取代body的,一方面這個單字是自創的,辨識性更高一點。

2019年2月14日 星期四

[Node.js] 用Express建立伺服器

首先npm install express。
通常會把伺服器寫在server.js裡。
首先使用express()建立一個網埠監聽器(貌似大家都稱他為app,不過在Electron中可能跟electron.app搞混,或是當我們需要多個網埠監聽器的時候,總之app這個名字是可以變的,反正就是變數名稱)。
===
const express = require("express");
const bodyParser = require("body-parser");

let app = express();
app.use(bodyParser.text());
app.use(bodyParser.urlencoded({ extended: true }));
===.
bodyParser是為了後面可以使用req.body來接收客戶端的資料。沒有使用bodyParser的話會導致req.body未定義。
基本的都OK了。接下來要先定義各種路由的監聽函式。簡單來說就是當客戶端GET「/about/」的時候,要用哪個listener回應;當客戶端POST「/weather/today/」的時候,又要用哪個listener回應。其中,「/about/」和「/weather/today/」就稱為路由。
在listener中,req表示客戶端傳來的請求,res代表準備送出的伺服器回應。
res.send(response)後,這個res就不能再被傳送第二次了。
===
app.get(route, function (req, res) {
  res.send("server response"); 
  res.send("this is no use"); // 這個send是無效的
}); 
===.
在客戶端,為了向伺服器傳送請求,通常是這樣做的:
===
// 客戶端
let xhttp = new XMLHttpRequest(); 
xhttp.onreadystatechange = function () {
  console.log(this.readyState); 
  console.log(this.status); 
  console.log(this.responseText); 
}; 
xhttp.open(method, url, async); 
xhttp.send("client request"); 
===.
所謂的method指的就是"GET"或"POST"(字串型別);async則是是否使用非同步處理。
在onreadystatechange的監聽器中,有關readyState和status的差異,可以參考W3School
簡單來說,readyState是指說一個請求是否被傳送出去了、是否收到回應了,其中4代表收到回應。status描述的則是伺服器對於你的請求給出了什麼樣的回應。200表示正常,404表示找不到你所請求的頁面等等。
一般來說若可以檢查到readyState === 4 && status === 200表示收到了正常的伺服器回應。
經過xhttp.send()之後,請求就會被傳送到伺服器。
由於前面有使用bodyParser,因此傳送到伺服器的附加資訊──也就是"client request"──可以由req.body取得。
===
app.get(route, function (req, res) {
  console.log(req.body); 
}); 
===.
基本上我的作法是這樣的:不論是req.body還是xhttp.send()的附加資訊,他們傳送和接收的都是字串。因此我會把JSON物件資訊先轉換為字串,接收後再解析回JSON物件。
===
// 客戶端
xhttp.send(JSON.stringify(content)); 
// 伺服端
console.log(JSON.parse(req.body)); 
===.
回到路由的部分,那個route可以是字串,比如"/about/",也可以是正規表達式。
註冊多個路由監聽函式的時候,先註冊的有高的優先權。
===
app.get("/", listener1); 
app.get("/", listener2); // 這個是無用的,因為會被上面那個監聽函式搶走。
===.
而我則是習慣讓所有請求都衝著"/"來,不管甚麼方法都用同一個函式監聽。
===
app.all("/", listener); 
===.
講了這麼多,沒有實際開始監聽是沒有用的。
這個頁面紀錄了較知名的服務所使用的網埠,盡量不要衝突。
===
app.listen(port);
===.

2018年12月14日 星期五

[JavaScript] 索引類別是否影響速度

一個物件的索引可以是數字也可以是字串。是否影響速度?
LeetCode一道題目「Longest String Without Repeating Characters」中,我使用了以下方法解題:
===
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let head = -1;
    let max = 0;
    let last = {};
    for (let i = 0, len = s.length; i < len; i++) {
        let c = <INDEX>; // INDEX
        if (!(c in last)) {
            last[c] = -1;
        } else {
            head = Math.max(head, last[c]);
        }
        max = Math.max(max, i - head);
        last[c] = i;
    }
    return max;
};
===.
在<INDEX>那邊我有了兩個版本:一個是直接拿s[i]當索引的字元版本;另一個是用s.charCodeAt(i)取得ASCII碼,以數值作為索引的數值版本。
字元版本跑了160ms,數值版只要76~80ms,有著十分明顯的差距。
因此,若求速度的話,還是要用數值索引會比較適當。

2018年12月13日 星期四

[JavaScript] require

為了做模組化,可以在module.js中用export把想要的變數匯出。在main.js中用import匯入。
===
// module.jslet variable;
let func;
export {vairable, func};

// main.js
import {variable, func} from "module.js";
===.
可惜的是這個標準支援度不高,在NodeJS中也無法使用。因此可以用require代替。
基本上跟export/import是一樣的道理。
===
// module.js
let variable;
let func;
exports.variable = variable;
exports.func = func;

// main.js
const {variable, func} = require("module.js");
===.

2018年11月19日 星期一

[JavaScript] random

let random = (start, end, base = 1) => {
    if (Array.isArray(start)) {
        return start[random(0, start.length - 1)];
    } else {
        return Math.floor(Math.random() * ((end - start) / base  + 1)) * base + start;
    }
};

2018年10月20日 星期六

[JavaScript] MutationObserver(監聽DOM物件的改變)

MutationObserver可以用來監聽一個DOM物件的內容是否被修改。然而,監視對象並不是在初始化階段設置的,而是在之後用方法observe時提供的。
初始化階段只聲明這個observer被觸發時的callback函式,參數有兩個:mutations,那些被監聽到的變化;target,監聽對象。
===
var observer = new MutationObserver(function (mutations, target) {
    // callback
});
var options = {
    childList: true,
    attributes: true,
    characterData: true,
    subtree: true,
    attributeFilter: undefined
};
observer.observe(target, options);
observer.disconnect();
===.
呼叫observe方法時監聽才開始。options中childList代表是否監聽子節點變化;attributes代表屬性的變化;characterData代表資料;subtree代表是否往下遞迴。

2018年8月7日 星期二

[SQL Server+Node.js] 建立連線

在Node.js伺服器上要跟Microsoft的SQL Server連線需要套件mssql。
只談連線的話基本上是這樣做的。
===
const mssql = require("mssql");
var dbConfig = {
  user: "<username>",
  password: "<password>",
  server: "localhost\\SQLEXPRESS",
  database: "<database-name>"
};
mssql.connect(dbConfig, (error) => {
  // 連線以後的東西在這邊不講
});
===.
問題在於有些必須做的東西很多人沒講。
這篇可以看到,在Windows的「電腦管理/服務與應用程式」裡可以找到SQL Server的傳輸協定的設定,TCP/IP必須開啟,然後SQL Server Browser也必須開啟。為了重新啟動服務最好直接重新開機。
然而還有一種問題是SQL Server Browser無法開啟(右鍵選單啥都沒有),這種狀況下參考這篇,到「服務」裡找到SQL Server Browser右鍵內容,把他從停用改為自動或啟用。
另外有時候腦袋蠢蠢的,在<database-name>填入資料表的名稱,當然她媽不會動,這種狀況下回傳的錯誤是登入失敗(因為他找不到那個資料庫)。

[HTML] ribody

怕自己忘記,稍微紀錄一下。 各種各種理由(其中一個就是Vue,不能綁定在body上),導致時常需要一個包在body中的div,當作body來用,像這樣: === <body>     <div id="divBody">   ...