在node环境下运行脚本文件
终端中执行命令运行js,打印出结果
node run.js
安装nodemon来监视js文件的变化,自动运行,这样每次修改js,就会自动运行
mpm i -g nodemon
nodemon run.js
node中的异步编程
node.js 异步编程的直接提现在回调函数上
异步:不等待 非阻塞 非阻塞式IO操作
优点:性能高,处理大量的并发请求
案例
一边读取文件,一边执行其他命令,读文件的目的 → 读取文件的内容 → 回调函数中返回
- 阻塞代码使用 fs( fileSystem )模块 commonjs
// 解析sf模块 - 文件系统模块,负责读写文件
const fs = require('fs')
//sf模块调用同步读取文件方法,并传入文件路径作为参数
const data = fs.readFileSync('./01-runnode.js')
console.log(data.toString());
console.log('hhh');
//-----------------
const str = ``;
let num = 0;
//....
console.log('maxuan');
hhh
必须先输出文件字符串,再输出hhh
- 非阻塞代码,大大提高了程序的性能
const fs = require('fs')
//fs模块调用异步读取文件方法,并传入文件路径和读取完成后的回调函数,回调函数会接收两个参数,错误和数据
fs.readFile('./01-runnode.js', function(err,data) {
// 在node编程中有个错误优先机制,回调函数的参数第一个为err
if(err) {
console.log(err.stack);
return
}
console.log(data.toString());
})
console.log('先被执行');
/*------------
先被执行
01-runnode.js内容...
*/
非阻塞代码在读取文件的期间,继续运行了下面的代码,输出了在代码最后的继续执行,再回调打印的文件内容
这样的代码就叫做异步编程 setTimeout 也是异步编程
因为阻塞是按顺序执行的,而非阻塞是不需要按顺序执行的,这就需要回调函数来执行后面的命令
使用 promise 操作
const fs = require('fs')
const { promisify } = require('util')
// 通过 promisify 将 fs.readFile包装成promise对象
const readFile = promisify(fs.readFile)
// 接下来进行异步编程
async function asyncReadFile() {
try {
const data = await readFile('./01_runnode.js')
console.log(data.toString())
} catch (error) {
// error.stack 属性是一个字符串,描述代码中 error被实例化的位置
console.log(error.stack);
}
}
asyncReadFile()
console.log('我先被调用');
/*
我先被调用
01_runnode.js内容...
*/
使用 generator (生成器) 操作 — ES6知识
const fs = require('fs')
const { promisify } = require('util')
// 通过 promisify 将 fs.readFile包装成promise对象
const readFile = promisify(fs.readFile)
function* read() {
yield readFile('./01_runnode.js')
}
let ge = read();
ge.next().value.then((data) => {
console.log(data.toString())
}).catch((err) => {
console.log(err);
})
console.log('我先执行');
/*
我先被调用
01_runnode.js内容...
*/
yield是ES6的新关键字,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字
yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成
yield无法单独工作,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性
Buffer对象的相关操作
第一个参数 — 文件路径
第二个参数 — 编码格式
第三个参数 — 回调函数
const fs = require('fs')
fs.readFile('./01-runnode.js', 'utf-8',function(err,data) {
//...
})
不传入'utf-8'时,所打印的是个十六进制的 buffer 对象,添加这个data直接打印的结果就是字符串,无需toString()方法
数据创建
// 创建一个长度为10、且用0填充的buffer对象
const buf1 = Buffer.alloc(10)
console.log(buf1); // <Buffer 00 00 00 00 00 00 00 00 00 00>
// 创建一个Buffer对象并传入字符串 Hellow World!
const buf2 = Buffer.from('Hello World!')
console.log(buf2) // <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64 21>
// 创建对象冰川乳一个数组
const buf3 = Buffer.from([1,2,3])
console.log(buf3) // <Buffer 01 02 03>
数据写入
// buffer中写入 hello,会从前向后写入对应16进制编码
buf1.write('hello')
console.log(buf1)// <Buffer 68 65 6c 6c 6f 00 00 00 00 00>
// buffer中写入 hello buffer,重复的写入会覆盖之前的内容,而且长度超过的部分会因为buffer容量不够而被忽略
buf1.write('hello buffer')
console.log(buf1)// <Buffer 68 65 6c 6c 6f 20 62 75 66 66>
数据读取
// 默认读取为utf-8的数据,可写可不写
console.log(buf1.toString()); // hello buff
// 传入参数转换为指定格式数据
console.log(buf1.toString('base64')); // aGVsbG8gYnVmZg=
数据合并
const buf4 = Buffer.concat([buf1, buf2])
console.log(buf4.toString()) // hello buffHello World!
Stream管道流操作
流的类型
Node.js 中有四种基本的流类型:
Writable: 可以写入数据的流(例如,fs.createWriteStream())Readable: 可以从中读取数据的流(例如,fs.createReadStream())Duplex:Readable和Writable的流(例如,net.Socket)Transform: 可以在写入和读取数据时修改或转换数据的Duplex流(例如,zlib.createDeflate())
流的操作
const fs = require('fs')
// 创建可读流 —— 对应读取目标文件
const readerStream = fs.createReadStream('./readme.md')
// 创建可写流 —— 对应小写入的目标文件,如果不存在将被创建
const writerStream = fs.createWriteStream('./test.txt')
// 通过管道方法进行传输
// 对可读流调用 pipe() 方法,并传入可写流
readerStream.pipe(writerStream)
console.log('执行完毕');
zlib (压缩)
zlib模块提供通过 Gzip 和 Deflate/Inflate 实现的压缩功能,Brotli 也是如此
createGzip() 压缩
// 引入fs模块
const fs =require('fs')
// 引入zlib模块
const zlib = require('zlib')
// 创建gzip压缩对象
const gzip = zlib.createGzip()
const txt = fs.createReadStream('./test.txt')
const zip = fs.createWriteStream('./test.zip')
txt.pipe(gzip).pipe(zip)
console.log('压缩完成');
以上链式调用可以直接用一行代码
fs.createReadStream('./test.txt').pipe(gzip).pipe(fs.createWriteStream('./test.zip'))
console.log('压缩完成');
生成的压缩包内的文件,是没有扩展名的
解压操作会添加
createGunzip 解压
// 引入fs模块
const fs =require('fs')
// 引入zlib模块
const zlib = require('zlib')
// 链式调用
fs.createReadStream('./test.zip').pipe(zlib.createGunzip()).pipe(fs.createWriteStream('./test.txt'))
console.log('解压完成');
事件循环和驱动程序 (了解)
事件循环
Node.js 是单线程应用程序,但是通过事件和回调支持并发,所以性能非常高
Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发
Node.js 基本上所有的事件机制都是用设计模式中的观察者模式实现
Node.js 单线程类似进入一while(true)的事件循环,直到没有事件观察者时退出,每个异步事件都生成一个事件观察者,如果有时间发生就调用该回调函数
事件驱动程序
Node.js 使用事件驱动模型,当 web server 接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求
这个请求完成时,他被放回处理队列,当达到队列开头,这个结果被返回给用户
这个模型非常高效可扩展性非常强,因为 web server 一直接受请求二不等待任何读写操作,这也被称之为非阻塞式IO或者事件驱动IO
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数
整个事件驱动的流程就是这么实现的,非常简洁,有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的函数相当于观察者(Observer)
events实例:
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter类 来绑定和监听事件,下面就是一个例子
// 引⼊event 模块
const events = require("events");
// 创建eventEmitter对象
const eventEmitter = new events.EventEmitter();
// 创建事件处理程序
// 绑定 connection 事件处理程序
eventEmitter.on("connection", function(x) {
console.log("连接成功" + x);
// 触发 data_received事件
eventEmitter.emit("data_received", x);
});
// 使⽤匿名函数绑定 data_received 事件
eventEmitter.on("data_received", function (x) {
console.log("数据接收成功" + x);
});
const x = 'x'
// 触发 connection事件
eventEmitter.emit("connection", x);
console.log("程序执⾏完毕");
连接成功x
数据接收成功x
程序执⾏完毕
上方的事件和函数,实际上是从下方开始触发的,触发conn事件,并传入一个参数,让预先定义的事件函数运行起来,主题事件内触发另一个事件,来执行外部绑定的另一个事件的回调函数,来处理这个事件传入的值,直到事件完成,打印 程序执行完毕
CommonJS规范以及模块系统
common.js写法
命名一个hello.js文件作为模块来存储函数和对象
exports.name = function() {
console.log('Max');
}
exports.high = function() {
console.log('175cm');
}
exports.size = '14cm'
exports.arr = [1,2,3,4,5]
exports.object = {
x: 1,
y: 2,
z: 3
}
node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 是用于从外部获取一个模块的接口
这里通过require,因为模块并不是nodejs内置的,所以值为路径,并且不需要写扩展名 .js
const obj = require('./hello')
console.log(obj)
obj.name()
obj.high()
console.log(obj.size);
通过obj.xxx来调用模块内的函数和变量
{
name: [Function],
high: [Function],
size: '14cm',
arr: [ 1, 2, 3, 4, 5 ],
object: { x: 1, y: 2, z: 3 }
}
Max
175cm
14cm
module 写法
抛出函数写法
function Dog() {
let name;
this.setName = function(myName) {
name = myName
}
this.sayName = function() {
console.log('hello ' + name);
}
}
module.exports = Dog
如果是 module.exports 抛出的对象,new 出来的对象,就是这个对象本身,可以直接通过new出来的变量d1运行这个模块,或者d1.xxx 来调用对象内部的方法或变量,这取决于抛出的到底是函数还是对象
如果是 exports 抛出的对象,它是被挂载到了同一个对象上,需要.xxx来引用抛出的对象上的所有方法和对象
const Dog = require('./module') const d1 = new Dog() d1.setName('Max') d1.sayName()
模块抛出对象的写法
const Dog = {
name: 'Max',
setName(myName) {
this.name = myName
},
sayName() {
console.log('hello ' + this.name);
}
}
module.exports = Dog
如果抛出的是对象,便不再需要new获取这个对象
const Dog = require('./module') // console.log(Dog.name); Dog.setName('xxxx') Dog.sayName()
模块加载策略
Node.js的 require 方法中的文件查找机制如下
- fs/http/url/path等原生模块
- ./module1 相对路径模块
- /pathmodule/module1 绝对路径的文件模块
- module1 非原生模块的文件模块
由于 Node.js 中存在4类模块儿(原生模块儿和三种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同,如下图所示
常用内置模块
fs 文件系统 | Node.js API 文档 (nodejs.cn)
fs 模块 — 只做了解
fs存储数据文件过大占用内存高,导致系统崩溃,将来的应用是要把数据存到数据库,所以这段只做了解
异步和同步
打开文件
fs.open(path, flags[, mode] callback)
参数:
- path — 文件的路径
- flags — 文件打开的行为,具体见下面
- mode — 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)
- callback — 回调函数,带有两个参数,如 callabck(err, fd)
flags 参数可以是以下值:
-
'a': 打开文件进行追加。 如果文件不存在,则创建该文件。 -
'ax': 类似于'a'但如果路径存在则失败。 -
'a+': 打开文件进行读取和追加。 如果文件不存在,则创建该文件。 -
'ax+': 类似于'a+'但如果路径存在则失败。 -
'as': 以同步模式打开文件进行追加。 如果文件不存在,则创建该文件。 -
'as+': 以同步模式打开文件进行读取和追加。 如果文件不存在,则创建该文件。 -
'r': 打开文件进行读取。 如果文件不存在,则会发生异常。 -
'r+': 打开文件进行读写。 如果文件不存在,则会发生异常。 -
'rs+': 以同步模式打开文件进行读写。 指示操作系统绕过本地文件系统缓存。这主要用于在 NFS 挂载上打开文件,因为它允许跳过可能过时的本地缓存。 它对 I/O 性能有非常实际的影响,因此除非需要,否则不建议使用此标志。
这不会将
fs.open()或fsPromises.open()变成同步阻塞调用。 如果需要同步操作,应该使用类似fs.openSync()的东西。 -
'w': 打开文件进行写入。 创建(如果它不存在)或截断(如果它存在)该文件。 -
'wx': 类似于'w'但如果路径存在则失败。 -
'w+': 打开文件进行读写。 创建(如果它不存在)或截断(如果它存在)该文件。 -
'wx+': 类似于'w+'但如果路径存在则失败。
os模块 — 只做了解
os 操作系统 | Node.js API 文档 (nodejs.cn)
操作系统模块
const os = require("os");
// cpu的字节序
console.log("endianness : " + os.endianness());
// 操作系统名
console.log("type : " + os.type());
// 系统内存总量
console.log("endianness : " + os.totalmem() + " bytes");
// 操作系统空闲内存量
console.log("endianness : " + os.freemem() + " bytes");
打印结果
endianness : LE
type : Windows_NT
endianness : 68667461632 bytes
endianness : 52082888704 bytes
url 模块
url 网址 | Node.js API 文档 (nodejs.cn)
当前最新的node中,使用 WHATWG 的 API 解析 URL 字符串
const url = new URL('http://max:123@maxlearn.tom:8080/user/123/?query=name#xxx')
console.log(url);
使用遗留的 API 解析 URL 字符串
const url = require('url')
const myUrl = url.parse('http://max:123@maxlearn.tom:8080/user/123/?query=name#xxx')
console.log(myURL)
打印这个url对象,会发现这个链接被URL构造函数解析为了一个包含各种属性值的对象
URL {
href: 'http://max:123@maxlearn.tom:8080/user/123/?query=name#xxx',
origin: 'http://maxlearn.tom:8080',
protocol: 'http:',
username: 'max',
password: '123',
host: 'maxlearn.tom:8080',
hostname: 'maxlearn.tom',
port: '8080',
pathname: '/user/123/',
search: '?query=name',
searchParams: URLSearchParams { 'query' => 'name' },
hash: '#xxx'
}
path 模块
path 路径 | Node.js API 文档 (nodejs.cn)
path模块着重记住绝对路径获取方法就可以了,以下获取文件绝对路径的两个方法,可以互换使用
const path = require('path')
// __dirname 获取当前项目的绝对路径的目录名字
console.log(path.join(__dirname,'./10_path.js'))
// C:\Users\Maxuan\Desktop\NODEJS\10_path.js
// 将相对路径转换为绝对路径, path.resolve() 相当于 path.join(__dirname)
console.log(path.resolve('./10_path.js'));
// C:\Users\Maxuan\Desktop\NODEJS\10_path.js
// 获取路径文件中的后缀名
console.log(path.extname('./11_path.js'));
// .js
net 模块
net 网络 | Node.js API 文档 (nodejs.cn)
dns模块
dns 域名服务器 | Node.js API 文档 (nodejs.cn)
domain模块
domain 域 | Node.js API 文档 (nodejs.cn)
error模块
Error 错误 | Node.js API 文档 (nodejs.cn)
global全局变量
global 全局变量 | Node.js API 文档 (nodejs.cn)
-
......
这些对象在所有模块中都可用。 以下变量可能看起来是全局的,但实际上不是。 它们只存在于模块的作用域中,参见模块系统文档:
此处列出的对象特定于 Node.js。 有些内置对象是 JavaScript 语言本身的一部分,它们也可以全局地访问
http模块 — 搭建服务器 — 重点
理解内部的处理原理
创建http服务器
新建一个http.js模块
// 引入http模块
const http = require('http')
// 创建appServer对象 request(请求) response(响应)
const app = http.createServer((req, res) => {
res.end('hello node.js')
})
// 监听端口号 http://localhost:3000/
app.listen(3000)
运行当前模块
nodemon http.js
打开浏览器输入localhost:3000 会发现浏览器中出现了返回的字符串
不同的url做不同的响应处理
创建的服务器实例,我们打印req,会获得一个请求对象,保存模块不会打印出新内容
const app = http.createServer((req, res) => {
console.log(req)
res.end('hello node.js')
})
但当修改网页中的地址并回车的时候
终端会打印出这个请求对象,内容长达上千行,终端容不下会覆盖掉
其中比较重要的,就有url属性和方法属性 ( 地址栏发起的请求都属于GET请求 )
接下来就可以通过url来
// 引入http 和 fs模块
const http = require('http')
const fs = require('fs')
const app = http.createServer((req, res) => {
const { url, method } = req
if(url === '/index' && method === 'GET') {
// 返回一个首页给浏览器
fs.readFile('./static/index.html', (err, data) =>{
// 当文件路径读取错误
if(err){
// 设置状态码,如果不设置状态码,页面虽然没有请求成功,仍然不会报红
res.statusCode = 500
// 返回内容
res.end('500 - Interval Serval Error!')
}
res.statusCode = 200
// 限定返回的文件必须为html格式
res.setHeader('Content-Type', 'text/html')
res.end(data)
})
} else if(url === '/about' && method === 'GET'){
}else {
res.end('hello node.js')
}
})
// 监听端口号 http://localhost:3000/
app.listen(3000)
错误响应
- 设置状态码
if (err) { }的条件判断,规范的写法是要更改状态码,这样响应出错才会被浏览器报红
- 设置返回文件格式
在返回数据之前可以设置头文件格式为html,让浏览器按照该格式读取
// 限定返回的文件必须为html格式读取
res.setHeader('Content-Type', 'text/html')
res.end(data)
当设置响应格式为css,返回的数据就会被当做css解析导致错误
res.setHeader('Content-Type', 'text/css')
res.end(data)
所以一般情况下,不要去指定格式
- 设置其他格式的请求
假如我们设置一个图片的请求,当前的路径是正确的
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于我们</title>
</head>
<body>
<h1>这是关于我们页面</h1>
<a href="index"><button>主页</button></a>
<a href="about"><button>关于我们</button></a>
<!-- 写入一个img的请求 -->
<img src="/static/img/1.jpg" alt="">
</body>
</html>
返回的状态也没有报错,读取路径也是对的,但是图片就是没有加载,这是因为后端没有对这个请求作出响应
以往用这么写没有报错,是因为服务器已经帮忙处理了这个请求,当我们自己写node.js服务器的时候,就需要手动配置了
这里需要增加一个条件判断,当浏览器请求了一个,让服务器作出响应
else if(req.headers.accept.indexOf('image/*') !== -1 && method === 'GET') {
console.log(url);
// 流的方式返回数据
fs.createReadStream(__dirname + url).pipe(res)
}
req.headers.accept 返回的是一个字符串
image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8/static/img/1.jpg
通过调用这个字符串的indexOf()方法来确定image/*在这个字符串中出现的位置,未匹配到返回 -1,所以要 !== -1来得到 true
通过流方式返回 绝对路径 + url 的拼接字符串来得到服务器上的正确地址,并通过pipe() 写入到res
- 读取json
添加条件
else if(url === '/user' && method === 'GET'){
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify([{name:'Max'}]));
}
express框架快速上手
以上使用http、fs模块,对请求的处理,以及对错误的响应都非常的复杂,对于一个项目成百上千的请求,不可能做到不出问题
所以就有团队开发出了 express框架,虽然这个框架现在已经停止更新,但是现在用这个来搭建一个简易的小型服务器也是非常便捷的
初始化
在根目录 npm init
npm init
/* 描述
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
*/
package name: (nodejs)
version: (1.0.0) 1.0.1
description:
entry point: (01_runnode.js) index.js
test command:
git repository:
keywords: Max
author: Max
license: (ISC)
About to write to C:\Users\Maxuan\Desktop\NODEJS\package.json:
{
"name": "nodejs",
"version": "1.0.1",
"description": "-vue\r -vue全家桶",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Max"
],
"author": "Max",
"license": "ISC"
}
Is this OK? (yes)
输入yes创建json配置文件
或者直接 一条命令创建
npm init --yes
Wrote to C:\Users\Maxuan\Desktop\NODEJS\package.json:
{
"name": "NODEJS",
"version": "1.0.0",
"description": "-vue\r -vue全家桶",
"main": "01_runnode.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
创建的json配置文件,如果需要也可以后期修改
安装express
npm i express -save
hello world example
// 引入 express 模块
const express = require('express')
// 创建 app 实例
const app = express()
// 监听端口
const port = 3000
// 匹配的路由地址
app.get('/', (req, res) => {
res.send('Hello World!')
})
// app设置监听端口,服务器创建完成后的回调函数 —— 打印这条信息
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
终端输入命令 nodemon 12_express.js 运行模块
nodemon 12_express.js
Example app listening at http://localhost:3000
这时访问 localhost:3000 就会请求/路由做出响应,返回 Hello World!
应用例子
// 引入 express、fs、path 模块
const express = require('express')
const fs = require('fs')
const path = require('path')
// 创建 app 实例
const app = express()
// 监听端口
const port = 3000
// 匹配的路由地址
app.get('/', (req, res) => {
res.send('Hello World!')
})
// 处理首页请求
app.get('/index', (req, res) => {
fs.readFile('./static/index.html', 'utf-8', (err, data) => {
if (err) {
res.statusCode = 500
res.end('500 - Interval Serval Erorr!')
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/html')
res.end(data)
})
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
接下来的使用就是在模块内添加app.get('/xxx', (req, res) => { }) 来完成对各个页面路由的请求了
下面的则是一个处理页面对文件加载的请求
// 匹配所有路由,解决加载其余全部文件的请求
app.get('*', (req, res) => {
//res.setHeader('Content-Type', 'image/*')
fs.readFile(path.join(__dirname, req.url), (err, data) => {
if (err) {
throw err
}
res.send(data)
})
})
在 express框架内path.resolve(req.url) 和 path.join(__dirname, req.url) 表现是不一样的
-
resolve返回的是服务器的路径
C:\static\img\1.jpg
-
join(__dirname,) 返回的是电脑上的路径
C:\Users\Maxuan\Desktop\NODEJS\static\img\1.jpg
这里是在电脑上运行,所以用join就好了
express生成器
如果一个项目过大,那么把所有的路由请求都放在一个js内,就显得杂乱臃肿了
为了解决这个问题 express 提供了一个 Express 应用程序生成器 - Express 中文文档 | Express 中文网 (expressjs.com.cn)
通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。
你可以通过 npx (包含在 Node.js 8.2.0 及更高版本中)命令来运行 Express 应用程序生成器。
$ npx express-generator
对于较老的 Node 版本,请通过 npm 将 Express 应用程序生成器安装到全局环境中并执行即可。
$ npm install -g express-generator
$ express
-h 参数可以列出所有可用的命令行参数:
$ express -h
Usage: express [options] [dir]
Options:
-h, --help 输出使用方法
--version 输出版本号
-e, --ejs 添加对 ejs 模板引擎的支持
--hbs 添加对 handlebars 模板引擎的支持
--pug 添加对 pug 模板引擎的支持
-H, --hogan 添加对 hogan.js 模板引擎的支持
--no-view 创建不带视图引擎的项目
-v, --view <engine> 添加对视图引擎(view) <engine> 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
-c, --css <engine> 添加样式表引擎 <engine> 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
--git 添加 .gitignore
-f, --force 强制在非空目录下创建
例如,如下命令创建了一个名称为 myapp 的 Express 应用。此应用将在当前目录下的 myapp 目录中创建,并且设置为使用 Pug 模板引擎(view engine):
$ express --view=pug myapp
create : myapp
create : myapp/package.json
create : myapp/app.js
create : myapp/public
create : myapp/public/javascripts
create : myapp/public/images
create : myapp/routes
create : myapp/routes/index.js
create : myapp/routes/users.js
create : myapp/public/stylesheets
create : myapp/public/stylesheets/style.css
create : myapp/views
create : myapp/views/index.pug
create : myapp/views/layout.pug
create : myapp/views/error.pug
create : myapp/bin
create : myapp/bin/www
然后安装所有依赖包:
$ cd myapp
$ npm install
在 MacOS 或 Linux 中,通过如下命令启动此应用:
$ DEBUG=myapp:* npm start
在 Windows 命令行中,使用如下命令:
> set DEBUG=myapp:* & npm start
在 Windows 的 PowerShell 中,使用如下命令:
PS> $env:DEBUG='myapp:*'; npm start
然后在浏览器中打开 http://localhost:3000/ 网址就可以看到这个应用了。
通过生成器创建的应用一般都有如下目录结构:
.
├── app.js // 入口文件
├── bin
│ └── www // 整个入口,会加载app.js
├── package.json // 配置文件
├── public // 静态资源
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes // 路由
│ ├── index.js // 访问 index 的时候会加载index.js内的路由
│ └── users.js // 访问 user 的时候会加载user.js内的路由
└── views // 页面文件,在后端语言中不是用html来写页面,而是像pug这类模板语言来进行处理
├── error.pug
├── index.pug
└── layout.pug
7 directories, 9 files
如果需要还可以建立data、db来存放数据文件
通过 Express 应用生成器创建应用只是众多方法中的一种。你可以不使用它,也可以修改它让它符合你的需求。
Express 5.x - API Reference - Express 中文文档 | Express 中文网 (expressjs.com.cn)