因为国内的搜索引擎对单页面应用支持不友好,因此通常网站的网站作的是多页面应用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" }
脚手架完毕,能够愉快的开发了^_^