起因
由于国内的搜索引擎对单页面应用支持不友好,所以一般网站的网站做的是多页面应用
选择
做网站当然是世界最好的语言PHP啦,开始也是想这样做的,但是在写这篇文章的时候,自己是一枚前端开发,
考虑到可维护性,其他的前端未必能看懂PHP代码,所以还是在nodejs方面选型,
nodejs有名的express我觉得挺合适,但是最终部署的生产环境是虚拟主机,不支持node环境,-_-||
所以只能想办法生成多个静态html文件,也就是网站静态化
基于以上种种考虑,最终选择用express开发,最终生成静态页面
准备
1.新建项目文件夹mpa
,运行npm init
,该填的填写,然后一路回车,得到package.json
2.安装express,npm i express --save
3.安装ejs,npm i ejs --save
ejs
是一个模板引擎,因为express默认的模板引擎是jade
,jade
与html语法相差较大,
所以我们要安装ejs
,ejs
可以认为就是html语言+js混编
4.安装supervisor,npm i supervisor --save-dev
nodejs的supervisor
是一个热部署工具,直接运行express项目只会监听模板文件的修改,而js文件的修改需要停止
再启动才能生效,使用supervisor
启动它会监听所有文件的修改,一旦有文件修改,立马重启,从而实现热部署
配置目录
项目需要包含路由,国际化,模板,静态文件,布局文件,所以目录设置如下:
|-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编写代码
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"
}
脚手架完毕,可以愉快的开发了^_^