因爲國內的搜索引擎對單頁面應用支持不友好,因此通常網站的網站作的是多頁面應用css
作網站固然是世界最好的語言PHP啦,開始也是想這樣作的,可是在寫這篇文章的時候,本身是一枚前端開發,
考慮到可維護性,其餘的前端未必能看懂PHP代碼,因此仍是在nodejs方面選型,
nodejs有名的express我以爲挺合適,可是最終部署的生產環境是虛擬主機,不支持node環境,-_-||
因此只能想辦法生成多個靜態html文件,也就是網站靜態化
基於以上種種考慮,最終選擇用express開發,最終生成靜態頁面html
1.新建項目文件夾mpa
,運行npm init
,該填的填寫,而後一路回車,獲得package.json前端
2.安裝express,npm i express --save
node
3.安裝ejs,npm i ejs --save
ejs
是一個模板引擎,由於express默認的模板引擎是jade
,jade
與html語法相差較大,
因此咱們要安裝ejs
,ejs
能夠認爲就是html語言+js混編express
4.安裝supervisor,npm i supervisor --save-dev
nodejs的supervisor
是一個熱部署工具,直接運行express項目只會監聽模板文件的修改,而js文件的修改須要中止
再啓動才能生效,使用supervisor
啓動它會監聽全部文件的修改,一旦有文件修改,立馬重啓,從而實現熱部署npm
項目須要包含路由,國際化,模板,靜態文件,佈局文件,因此目錄設置以下:json
|-langs //國際化文件夾 |-zh_CN.js |-en_US.js |-layouts //佈局模板文件夾 |-header.html |-footer.html |-public //靜態資源文件夾 |-static |-css |-js |-img |-vendor |-views //內容模板文件夾 |-*.html |-index.js //主啓動程序 |-build.js //打包成靜態文件程序 |-tools.js //自定義函數工具文件
在index.js編寫代碼app
const express = require('express') const fs = require('fs') const path = require('path') const app = express() var ejs = require('ejs'); const tools = require('./tools') app.engine('html', ejs.__express); // 配置模板引擎 app.set('view engine', 'html') app.use(express.static(path.join(__dirname, 'public'))); // 配置 var CONFIG = { port: 100, lang: 'zh_CN' } var langs = require('./langs/'+CONFIG.lang); // 中間件 var setLang = (req, res, next) => { //根據get參數加載對應國際化文件 if (req.query.lang) { CONFIG.lang = req.query.lang langs = require('./langs/'+CONFIG.lang); } else { langs = require('./langs/zh_CN'); } console.log(req.url +' '+ (new Date())) next() } app.use(setLang) fs.readdirSync(path.join(__dirname, 'views')).map(file=>{ //遍歷views文件夾下模板文件,根據模板文件名稱生成對應路由 // 路由 let route = file.substring(0,file.lastIndexOf('.')) if (route==='index') { app.get('/', (req, res) => { //處理/ 和 index首頁路由,代碼幾乎同樣,這塊能夠優化 res.render(file, {...langs[route],header:langs['header'],footer:langs['footer'],url:tools.url(langs.lang)}) //傳遞必要參數 }) } app.get('/'+route, (req, res) => { res.render(file, {...langs[route],header:langs['header'],footer:langs['footer'],url:tools.url(langs.lang)}) }) console.log(file.substring(0,file.lastIndexOf('.'))) }) // 服務啓動 app.listen(CONFIG.port, () => console.log(`app listening on port ${CONFIG.port}!`))
打包的步驟以下函數
1.遍歷langs
文件,有多少個國際化文件就生成幾個國際化文件夾工具
2.遍歷views
文件,根據國際化文件渲染模板,輸出html文件到對應國際化文件夾
3.copy靜態文件到打包目錄
var ejs = require('ejs'); var fs = require('fs'); var path = require('path');//解析須要遍歷的文件夾 const tools = require('./tools') var distType = 1 if (global.process.env.npm_config_argv) { let npmConfig = JSON.parse(global.process.env.npm_config_argv) if (npmConfig['original'][2] && npmConfig['original'][2]==='t2') { distType = 2; } } function delDir(path){ let files = []; if(fs.existsSync(path)){ files = fs.readdirSync(path); files.forEach((file, index) => { let curPath = path + "/" + file; if(fs.statSync(curPath).isDirectory()){ delDir(curPath); //遞歸刪除文件夾 } else { fs.unlinkSync(curPath); //刪除文件 } }); fs.rmdirSync(path); } } var viewPath = path.join( __dirname , 'views'); var outputPath = path.join(__dirname,'dist'); delDir(outputPath); //process.exit(); if (!fs.existsSync(outputPath)) { fs.mkdirSync(outputPath) } const view = (filename)=>{ return path.join(viewPath,filename + '.html'); } var langFiles = fs.readdirSync(path.join(__dirname,'langs')); if (distType===1) { langFiles.forEach((file)=>{ var langPath = path.join(outputPath,file.substring(0,file.lastIndexOf('.'))) if (!fs.existsSync(langPath)) { fs.mkdirSync(langPath) } }) fs.readdir(viewPath,(err,files)=>{ files.forEach((file) => { let stats = fs.statSync(path.join(viewPath,file)); if (stats.isFile()) { langFiles.forEach((langFile)=>{ var local = langFile.substring(0,langFile.lastIndexOf('.')) var langs = require('./langs/'+local); let name = file.substring(0,file.lastIndexOf('.')) ejs.renderFile(view(name),{...langs[name],header:langs['header'],footer:langs['footer'],url:tools.url(langs.lang)},(err,str)=>{ fs.writeFile(path.join(outputPath,local,file), str, (err)=>{ if (err) { console.log(`建立${path.join(outputPath,local,file)}失敗`) } else { console.log(`建立${path.join(outputPath,local,file)}成功`) } }) }); }) } }) }) } else if (distType===2) { fs.readdir(viewPath,(err,files)=>{ files.forEach((file) => { let stats = fs.statSync(path.join(viewPath,file)); if (stats.isFile()) { langFiles.forEach((langFile)=>{ var local = langFile.substring(0,langFile.lastIndexOf('.')) var langs = require('./langs/'+local); let name = file.substring(0,file.lastIndexOf('.')) let tplPtah = path.join(outputPath,name) if (!fs.existsSync(tplPtah)) { fs.mkdirSync(tplPtah) } let tplLangPath = path.join(tplPtah,local) if (!fs.existsSync(tplLangPath)) { fs.mkdirSync(tplLangPath) } let tplLangPathFile = path.join(tplLangPath,'index.html') ejs.renderFile(view(name),{...langs[name],header:langs['header'],footer:langs['footer'],url:tools.url(langs.lang)},(err,str)=>{ fs.writeFile(tplLangPathFile, str, (err)=>{ if (err) { console.log(`建立${tplLangPathFile}失敗`) } else { console.log(`建立${tplLangPathFile}成功`) } }) }); }) } }) }) } const movePath = (fromPath,toPath)=>{ if (!fs.existsSync(toPath)) { fs.mkdirSync(toPath) } fs.readdir(fromPath,(err,files)=>{ files.forEach((file)=>{ let filePath = path.join(fromPath,file) if (fs.statSync(filePath).isDirectory()) { movePath(path.join(fromPath,file),path.join(toPath,file)); } else { fs.readFile(filePath,(err,str)=>{ if (err) { console.log(`拷貝${filePath}失敗`) } else { fs.writeFile(path.join(toPath,file),str, (err)=>{ if (err) { console.log(`建立${path.join(toPath,file)}失敗`) } else { console.log(`建立${path.join(toPath,file)}成功`) } }) } }) } }) }) } movePath(path.join(__dirname,'public','static'),path.join(outputPath,'static'))
主要配置package.json
文件的啓動命令和打包命令
"scripts": { "start": "supervisor index.js", "build": "node build.js" }
腳手架完畢,能夠愉快的開發了^_^