ejs模板

ejs模板的三种用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const ejs = require('ejs');
const path = require('path');

const html = '<div><%= user.name %></div>'
const options = {}
const data = {
user: {
name: 'io'
}
}

// 1. 多次复用
const template = ejs.compile(html, options);
const compiled = template(data)
console.log(compiled);

// 2. 只用一次
const rendered = ejs.render(html, data, options);
console.log(rendered);

// 3. 读取html, promise调用
const renderedfile = ejs.renderFile(path.resolve(__dirname, './index.html'), data, options);
renderedfile.then(file => console.log(file))

ejs不同标签的含义

<% if(user){ %> <% } %> ‘脚本’标签,用于流程控制,无输出
<%_ %> 删除前面的空格
<%= user.name %> 输出数据到模板(转义字符, HTML标签)
<%- %> 输出非转义字符
<%# 这是注释%> 注释
%> 结束标签
<%% 输入字符串’<%’
-%> 删除紧随其后的换行符
<% _%> 将结束标签后的空格删除

包含

1
2
<!-- footer.html -->
<div>footer: user is <%= user.name %> </div>
1
<%- include('./footer.html', {user}) %>

分隔符

1
2
3
4
// 传给ejs的加载器
const options = {
delimiter: '?'
}

fileloader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 注意: fileloader里面添加的字符串模板会在每一次读取文件的时候运行一次
// 例如 下面在读取footer.html的时候再次进行了添加
ejs.fileLoader = function (filePath) {
console.log('fileLoader', filePath);
const content = '<div>footer: user is <?= user.name ?> </div>'+fs.readFileSync(filePath, 'utf8').toString();
return content;
}

ejs.renderFile(path.resolve(__dirname, './index.html'), data, options, (err, file) => {
console.log(file);
});
// <div>footer: user is john </div><%if(user){ -%>
// <% for (var i=0; i < 10; i++){ -%>
// <%_ %><div><%= user.name -%></div>
// <%} -%>
// <%} -%>


// <div>footer: user is john </div><div>footer: user is john </div>

Eventloop事件循环

EventLoop 事件循环机制

JavaScript的事件分两种,宏任务(macro-task)和微任务(micro-task)

  • 宏任务:包括整体代码script,setTimeout,setInterval
  • 微任务:Promise.then(非new Promise),process.nextTick(node中)
  • 事件的执行顺序,是先执行宏任务,然后执行微任务,这个是基础,任务可以有同步任务和异步任务,同步的进入主线程,异步的进入Event Table并注册函数,异步事件完成后,会将回调函数放入Event Queue中(宏任务和微任务是不同的Event Queue),同步任务执行完成后,会从Event Queue中读取事件放入主线程执行,回调函数中可能还会包含不同的任务,因此会循环执行上述操作。

promise、async/await

  • 首先,new Promise是同步的任务,会被放到主进程中去立即执行(当做立即执行函数去理解)。而then()函数是异步任务会放到异步队列中去,那什么时候放到异步队列中去呢?当你的promise状态结束(就是执行reject或者resolve)的时候,就会立即放进异步队列中去了。
  • 带async关键字的函数会返回一个promise对象,如果里面没有await,执行起来等同于普通函数;如果没有await,async函数并没有很厉害是不是
  • await 关键字要在 async 关键字函数的内部,await 写在外面会报错;await如同他的语意,就是在等待,等待右侧的表达式完成。此时的await会让出线程,阻塞async内后续的代码,先去执行async外的代码。等外面的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise对象,是一个同步函数,也会等这样操作

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。 可以理解为,是让出了线程,跳出了async 函数体

node多进程

进程的概念

  • 进程是一个实体,每个进程都有自己的地址空间
  • 进程是一个执行中的程序,存在嵌套关系

在macos系统中, 在命令行输入ps -ef, pid表示当前进程id, ppid表示当前进程的父进程id,而操作系统的桌面起始进程/sbin/launchd的的pid是1,ppid是0

可以根据ps -ef|grep node筛选属于node的进程

child_process用法

异步

  • exec
  • execFile
  • fork
  • spawn

exec用法

1
2
3
4
5
6
7
8
const cp = require('child_process')
const path = require('path');

cp.exec('ls -al|grep node_modules', function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
})

execFile用法

第一个参数是可执行文件的路径,第二个参数执行参数,第三个是回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13

cp.execFile('ls', ['-al'], function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
})

// 这里要执行shell文件需要系统提供权限 chmod 777 ./test.shell
cp.execFile(path.resolve(__dirname, 'test.shell'), ['-al'], function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
})

spawn的用法

简单的shell脚本

1
2
3
ls -al|grep node_modules

echo $1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 第一个参数命令或者文件,会在系统中环境变量找到对应的文件
// spawn是流式执行方式,适用于耗时任务,可不断为用户提供日志反馈
const child = cp.spawn(path.resolve(__dirname, 'test.shell'), ['-al', '-bl'], {
cwd: path.resolve('./'),
stdio: 'inherit', //会逐行打印子进程的反馈日志
})

console.log(child.pid);

// 可以逐行显示出执行的结果
child.stdout.on('data', function (chunk) {
console.log('stdout', chunk.toString());
})

child.stderr.on('data', function (chunk) {
console.log('stderr', chunk.toString());
})


// exec/execFile: 开销较小的任务,最后一次性打印日志
cp.exec(path.resolve(__dirname, 'test.shell'), {
cwd: path.resolve('./')
}, function (chunk) {
console.log(chunk);
})

folk的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/** ./main_process.js **/
// fork: node(main) -> node(child)
// 两个进程是完全独立的
const child2 = cp.fork(path.resolve(__dirname, 'child.js'))

child2.send('hello child process', function (err) {
// child2.disconnect();
})

child2.on('message', function (msg) {
console.log(msg);
})
console.log('main process pid:' + process.pid)


/** ./child_process.js **/
console.log('child process')
console.log('child process pid: ' + process.pid)

process.on('message', function (msg) {
console.log(msg);

})

process.send('hello main process', () => {
process.disconnect();
})

Commander常见用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/env node

const commander = require('commander')
const program = new commander.Command()
const pkg = require('../package.json')

program
.name(Object.keys(pkg.bin)[0])
.usage('<cmd> [option]')
.version(pkg.version)
.option('-d, --debug', 'start debug mode', false)
.option('-e --envName <envName>', 'fetch the environment', 'development')

const clone = program.command('clone <source> [destination]');
clone
.description('clone an existing project')
.option('-f, --force', 'clone force')
.action((source, dest, cmdObj) => {
console.log('clone', source, dest, cmdObj.force);
})

const server = new commander.Command('server')
server.command('start [port]')
.description('start server at specified port')
.action((port) => {
console.log(`start server at ${port} ...`);
})

server.command('stop')
.description("stop server")
.action(() => {
console.log("stop server");
})

program.command('install [name] [path]', 'install package at specified path', {
executableFile: 'npm', // 当前install命令执行,相当于yarn add执行
// isDefault: true, // 这里设置为true,当执行脚手架时,默认就会执行npm
hidden: true // 作为一个隐藏的命令
}).alias(i)


program.on('--help', function () {
console.log('help info');
})

// debug模式实现
program.on('--debug', function () {
process.env.LOG_LEVEL = program.debug ? 'verbose' : 'info';
console.log(process.env.LOG_LEVEL);
})

// 对未知命令监听
program.on('command:*', function (obj) {
console.error('unknown command', obj[0]);
const availableCommand = program.commands.map(cmd => cmd.name);
console.log('可用命令:' + availableCommand.join(","));

})


// 适用于所有命令的监听
program.arguments('<cmd> [options]')
.description('test command', {
cmd: 'command',
options: 'options for command',
})
.action((cmd, options) => {
console.log(cmd, options);
})

program.addCommand(server)

program.parse(process.argv)