项目地址:es6+express+ejs+webpack+sass+mongoDB
使用
npm i //安装
npm start //开启服务
npm run dev //开发模式
npm run bulid //发布
项目目录
--root
|--config //配置文件
|--middlewares //中间件
|--pubulic //静态资源
|--js
|--css/sass
|--images
|--router //路由控制器
|--tools //工具
|--lib
|--webpack.config.js
|--views //视图文件
|--components
|--package.json
|--...
es6预设置
使用babel对es6进行转换:
下载
"devDependencies": {
"babel": "6.23.0",
"babel-cli": "6.23.0",
"babel-core": "6.23.1",
"babel-loader": "6.4.0",//webpack的babel-loader
"babel-plugin-transform-es2015-modules-commonjs": "6.23.0",//允许使用es6的modules
"babel-preset-env": "1.2.1",
"babel-preset-react": "6.23.0",//支持jsx转换
"babel-preset-stage-0": "6.22.0"
}
配置可以不写在.babelrc里,直接写在package.json
"babel": {
"presets": [
"react",
"stage-0"
],
"plugins": [
"transform-es2015-modules-commonjs"
]
}
npm script直接运行babel-node
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "babel-node tools/start",//表示执行tools文件夹的start文件,执行之前进行es6转码
"dev": "supervisor -w tools,router,middlewares,config,views -- -r babel-register tools/dev"
}
es6 - 异步操作 async/await 语法
async函数允许你像同步写法那样去写异步操作,async函数里面关键字await后面接一个Promise,此时函数执行会在await后的Promise执行完成后才会往下执行。
举个例子,创建服务器是异步操作,我们需要在服务器创建完成后才布置路由。首先创建服务器,并把它包在Promise里面:
let runServer = (app)=>{
return new Promise(resolve=>{
var server = app.listen('3000',()=>{
var host = server.address().address;
var port = server.address().port;
console.log('server running at http://%s:%s', host, port);
resolve();
})
})
}
然后在主体事件中,写异步操作:
let appPro = async ()=>{
let app = express();
//等待启动服务
await runServer(app);
//启动路由
router(app);
}
此时,路由 router() 会在服务器创建完成后执行。
express 将session 存入Redis
import session from 'express-session';
import connectRedis from 'connect-redis';
let sessionCfg = {
secret:'huangxinzheng',
resave:true,//即使 session 没有被修改,也保存 session 值,默认为 true(!不添加报错,下同)
saveUninitialized: false,// 设置为 false,强制创建一个 session,即使用户未登录
cookie: {
maxAge: 1000*60*60*24// 过期时间,过期后 cookie 中的 session id 自动删除
}
}
//设置session,将session存在 Redis 中
var RedisStore = connectRedis(session);
let rs = new RedisStore();
app.use(session(extend(true,{},serverCfg,rs)));
express 中间件的使用
express的中间件很重要,甚至可以说express就是通过中间来实现应用的。express有很多开源中间件可以给我们下载使用(比如express-session)。把中间件放进app.use()里就代表每次服务器请求都会经过这个中间件(假设中间件没有停止下一个的情况下)。我们也可以自己写中间件,其实中间件就是一个函数,这个函数有req
,res
,next
三个参数,分别代表请求对象,响应对象,和next方法。执行next方法可过渡当前中间件。
写一个记录访问次数的中间件,配合session记录一天内用户的访问次数:
let showReqTimes = (req,res,next) =>{
if(!req.session.reqTimes){
req.session.reqTimes =1;
res.send('你第1次访问了本网站');
}else{
req.session.reqTimes++;
res.send('<p>你第'+req.session.reqTimes+'次访问了本网站</p>');
}
next();//执行下一个中间件
}
export default showReqTimes
假设是记录在访问’/‘的时候:
import showReqTimes from './showReqTimes.js'
app.get('/', showReqTimes, (req, res) => {//此时每次访问根目录之后都会进入showReqTimes这个中间件
//do something..
})
webpack新版本的写法变化-Loader
webpack2.2较上一代在语法方面有了变化,特别是loader:
原先写法:
module: {
loaders: [{
test: /\.css$/,
loader: 'style!css'
}]
}
现在写法:
module: {
rules: [{
test: /\.css$/,
use: [ 'style-loader', 'css-loader']
}]
}
loaders改为了rules,loader改为了use。语义上更加容易理解了,“对不同的文件类型使用不同的规则”。webpack 虽好,但是配置的时候很麻烦,更新了新版本之后,写法不习惯,报错也是莫名其妙。开发的要一边看官方文档,花了很多时间,看来要多花点时间去适应。
问题&解决
1. supervisor 监听 es6 文件
supervisor 默认监听不经过处理的js文件,所如果是直接监听es6语法的js文件,那么会出现语法错。
所以,采用babel-register
对es6文件进行预转换,并且设置supervisor
:
supervisor -w tools,router -- -r babel-register tools/start
supervisor
的官方文档可参见 : https://www.npmjs.com/package/supervisor
2. ejs模板引擎不同页面引用不同样式文件
ejs是款简单好用的模板引擎,但是网上很多人说ejs在不同的页面引用不同的文件非常不方便,有人给出在res.locals给模板传参的方法,把页面的引用样式文件从后台传给模板引擎进行解析。个人感觉但是这种方法不妥,管理起来不方便,样式文件就应该写在视图页面,不应该在路由里面写。
其实可以通过ejs的include解决这个问题。
ejs2.0+的include函数已经支持传参功能。利用这个特性我们可以简单实现ejs模板引擎不同页面引用不同样式文件了。具体实现:
在首页的视图中往header里面穿参数:
<%- include('layouts/header',{styles:['index','jquery']}) %>
然后在header的视图中修改引用样式:
<% for(var i=0; i<styles.length; i++) {%>
<link rel="stylesheet" href="/css/<%= styles[i] %>.css">
<% } %>
3. server.address().address 获取到IPv6格式的主机地址”::”?
以为如果计算机默认启用了IPv6,那么 server.address().address 获取到的就是IPv6的地址。如下:
var express = require('express');
var app = express();
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('running at http://' + host + ':' + port)
});
res:'server running at http://:::3000'
如果想得到IPv4的主机地址,请显式得给listen传递主机名:
var express = require('express');
var app = express();
var server = app.listen(3000,'localhost', function () {
var host = server.address().address;
var port = server.address().port;
console.log('running at http://' + host + ':' + port)
});
res:'server running at http://127.0.0.1:3000'
4. browser-sync 与 webpack-dev-middleware 的配合?
const bundler = webpack(webpackCfg);
const wpdm = webpackDevMiddleware(bundler,{
publicPath: webpackCfg.output.publicPath
});
let handleBundleComplete = async () => {
if(!app){
await appPro().then(exApp=>{app = exApp});
//app.use(wpdm)
//启动 browser-sync
bs = browserSync.create();
bs.init({
files: path.resolve(__dirname, '../public/*/**'),//监听目录
proxy: {
target: serverCfg.host+':'+serverCfg.port,
middleware:[wpdm]
},
open: false,
reloadOnRestart: true
},resolve);
}else{
bs.reload();
}
};
bundler.plugin('done', () => handleBundleComplete());
具体流程:
- 1.先webpack生成bundler(此阶段并不执行生成)
- 2.用webpack-dev-middleware把bundler包成中间件wpdm
- 3.第一步生成bundler成功后启动服务器
- 4.启动服务器成功后创建并初始化browser-sync
- 5.第二步生成的中间件wpdm给到browser-sync中间件设置
- 6.每次触发browser-sync的时候都执行中间件wpdm
- 7.第一次之后的中间件wpdm执行都不用再启动服务器和创建browser-sync,只是执行browser-sync的reload事件