做前端有時會采用一些復雜框架,在文件系統中直接打開頁面(用file:///方式打開),往往會報跨域的錯,類似於“XMLHttpRequest cannot load ...(文件名). Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.”。這時,我們可以快速搭建一個簡單的靜態文件服務器,用Node.js最合適。
看過龍太的文章后(地址:http://www.jianshu.com/p/76c1a9f39a36),我覺得這篇文章里的服務器實現還是“麻雀雖小,五臟俱全”的。不過代碼有Bug,比如當用戶請求一個目錄時,代碼雖然可以自動搜索目錄里的index.html,但在顯示過程中出現異常。經過研究和修改,我把原文章中的3個js腳本合為一個,並解決了這個問題。而且優化了體驗,比如當運行這個js文件時,載入項目主頁的瀏覽器會自動彈出來。
1.在前端項目根目錄下創建server.js,代碼如下:
1 "use strict"; 2 //加載所需要的模塊 3 var http = require('http'); 4 var url = require('url'); 5 var fs = require('fs'); 6 var path = require('path'); 7 var cp = require('child_process'); 8 9 //創建服務 10 var httpServer = http.createServer(processRequest); 11 12 var port = 8080; 13 14 //指定一個監聽的接口 15 httpServer.listen(port, function() { 16 console.log(`app is running at port:${port}`); 17 console.log(`url: http://localhost:${port}`); 18 cp.exec(`explorer http://localhost:${port}`, function () { 19 }); 20 }); 21 22 //響應請求的函數 23 function processRequest (request, response) { 24 //mime類型 25 var mime = { 26 "css": "text/css", 27 "gif": "image/gif", 28 "html": "text/html", 29 "ico": "image/x-icon", 30 "jpeg": "image/jpeg", 31 "jpg": "image/jpeg", 32 "js": "text/javascript", 33 "json": "application/json", 34 "pdf": "application/pdf", 35 "png": "image/png", 36 "svg": "image/svg+xml", 37 "swf": "application/x-shockwave-flash", 38 "tiff": "image/tiff", 39 "txt": "text/plain", 40 "wav": "audio/x-wav", 41 "wma": "audio/x-ms-wma", 42 "wmv": "video/x-ms-wmv", 43 "xml": "text/xml" 44 }; 45 46 //request里面切出標識符字符串 47 var requestUrl = request.url; 48 //url模塊的parse方法 接受一個字符串,返回一個url對象,切出來路徑 49 var pathName = url.parse(requestUrl).pathname; 50 51 //對路徑解碼,防止中文亂碼 52 var pathName = decodeURI(pathName); 53 54 //解決301重定向問題,如果pathname沒以/結尾,並且沒有擴展名 55 if (!pathName.endsWith('/') && path.extname(pathName) === '') { 56 pathName += '/'; 57 var redirect = "http://" + request.headers.host + pathName; 58 response.writeHead(301, { 59 location: redirect 60 }); 61 //response.end方法用來回應完成后關閉本次對話,也可以寫入HTTP回應的具體內容。 62 response.end(); 63 } 64 65 //獲取資源文件的絕對路徑 66 var filePath = path.resolve(__dirname + pathName); 67 console.log(filePath); 68 //獲取對應文件的文檔類型 69 //我們通過path.extname來獲取文件的后綴名。由於extname返回值包含”.”,所以通過slice方法來剔除掉”.”, 70 //對於沒有后綴名的文件,我們一律認為是unknown。 71 var ext = path.extname(pathName); 72 ext = ext ? ext.slice(1) : 'unknown'; 73 74 //未知的類型一律用"text/plain"類型 75 var contentType = mime[ext] || "text/plain"; 76 77 fs.stat(filePath, (err, stats) => { 78 if (err) { 79 response.writeHead(404, { "content-type": "text/html" }); 80 response.end("<h1>404 Not Found</h1>"); 81 } 82 //沒出錯 並且文件存在 83 if (!err && stats.isFile()) { 84 readFile(filePath, contentType); 85 } 86 //如果路徑是目錄 87 if (!err && stats.isDirectory()) { 88 var html = "<head><meta charset = 'utf-8'/></head><body><ul>"; 89 //讀取該路徑下文件 90 fs.readdir(filePath, (err, files) => { 91 if (err) { 92 console.log("讀取路徑失敗!"); 93 } else { 94 //做成一個鏈接表,方便用戶訪問 95 var flag = false; 96 for (var file of files) { 97 //如果在目錄下找到index.html,直接讀取這個文件 98 if (file === "index.html") { 99 readFile(filePath + (filePath[filePath.length-1]=='/' ? '' : '/') + 'index.html', "text/html"); 100 flag = true; 101 break; 102 }; 103 html += `<li><a href='${file}'>${file}</a></li>`; 104 } 105 if(!flag) { 106 html += '</ul></body>'; 107 response.writeHead(200, { "content-type": "text/html" }); 108 response.end(html); 109 } 110 } 111 }); 112 } 113 114 //讀取文件的函數 115 function readFile(filePath, contentType){ 116 response.writeHead(200, { "content-type": contentType }); 117 //建立流對象,讀文件 118 var stream = fs.createReadStream(filePath); 119 //錯誤處理 120 stream.on('error', function() { 121 response.writeHead(500, { "content-type": contentType }); 122 response.end("<h1>500 Server Error</h1>"); 123 }); 124 //讀取文件 125 stream.pipe(response); 126 } 127 }); 128 }
2.在前端項目根目錄下打開命令提示符或終端,輸入以下命令就可以啟動小服務器啦。
node server.js