node.js是什么
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
Node.js 的包管理器 npm,是全球最大的开源库生态系统。
I/O
阻塞:I/O时进程休眠等到I/O完成后进行下一步
非阻塞:I/O时函数立即返回,进程不等待I/O完成
事件驱动
I/O等异步操作完成后的通知
cpu密集:解压 压缩 加密 解密
I/O密集:文件操作 、网络操作、数据库
node也是单线程
单线程针对的是主进程 I/O操作系统底层多进程调度
单线程不是单进程 例如八核cpu 启动八个进程
常用场景
web server
本地代码构建
实用工具开发
//node 和java?
环境
CommonJs每个文件是一个模块,有自己的作用域
模块内部module变量代表模块本身
module.export代表模块对外的接口
全剧对象是global
控制台中使用 node xx.js来执行
require规则
/表示绝对路径 ./表示相当于当前文件的相对路径
支持js json node拓展名
不写路径则认为是build-in模块或各级node_modules内的第三方模块
require特性
1.module被加载的时候执行,加载后缓存
2.出现某个模块被循环加载 (例如两个模块相互引用),就只输出已被执行部分,(实际上尽量避免这么写)
例子 写两个js文件
cusmod.js
console.log('this is a module');
const testVar = 100;
function test() {
console.log(testVar);
}
module.exports.testVar = testVar;
module.exports.testFn = test;
require.js
const mod = require('./02_cusmod');
console.log(mod.testVar);
mod.testFn();
在git中使用node 文件名的方式就可以看到结果
结果看到
this is a module
100
100
全部打印出来了而不是只执行了test 所以要写在function中,只暴露想让用户调用的方法
缓存的特性可以通过引入两个相同的模块 只打印一次结果可以看出
2.一旦出现某个模块循环加载,只输出已经执行的部分,还未执行的部分不会输出;
05_main.js
const modeA = require('./05_modeA');
const modeB = require('./05_modeB');
05_modeA.js
module.exports.test = 'A';//部分加载完 输出'A’
const modeB = require('./05_modeB');
console.log('modeA', modeB.test);
module.exports.test = 'AA';//全部加载完 输出的应该是AA
05_modeB.js
module.exports.test = 'B';
const modeA = require('./05_modeA');
console.log('modeB', modeA.test);
module.exports.test = 'BB';
执行过程 先到a模块中 test被赋值为A 然后A模块依赖于模块B 于是到了B模块 发模块B的test先被赋值为B然后发现B又依赖A,于是根据第一条加载完缓存,和第二条规则只输出已经执行的部分 ,所以B中打印test的结果为A,然后B执行完毕,test被赋值为BB,所以模块A中的test打印结果为BB,main中的requireA部分执行完毕,到requireB的时候发现已经缓存了B模块,所以不会重复执行
module.exports 和 exports的关系
module.expors的简写是exports 可以给他添加属性 ,但是不能修改他的指向
// exports.test = 100;//成功打印
// exports = {
// a : 1,
// b : 2,
// test: 100
// };//undefined
module.exports = {
a : 1,
b : 2,
test: 100
};//100
global
08_main
// const exps = require('./08_exps');
// console.log(exps.test);
const mod = require('./08_global');
console.log(mod.testVar);//1000
//console.log(testVar);//报错
console.log(testVar2);//200 挂载到global的全局变量
08_global
const testVar = 1000;
global.testVar2 = 200;//会变成全局变量
module.exports.testVar = testVar;
process
//porcess的子对象
const {argv, argv0, execArgv, execPath} = process;
argv.forEach(item => {
console.log(item);
/*C:\Program Files\nodejs\node.exe 保存node安装的目录
C:\Users\admin\Desktop\H5\node.js\入门\nodetest\commonJS\10_argv.js 当前执行文件的路径
*/
})
console.log(argv0)//node 保存argv的第一个值 不常用
console.log(execArgv)//调用node传入的一些特殊参数
//node --inspect 10_argv.js --test --inspect相当于传入的参数 在最后面打印
/*
C:\Program Files\nodejs\node.exe
C:\Users\admin\Desktop\H5\node.js\入门\nodetest\commonJS\10_argv.js
--test
node
[ '--inspect' ]
*/
console.log(execPath);//C:\Program Files\nodejs\node.exe
关于调试
在chrome中可以打开chrome://inspect这个地址 安装一个chrome的插件
详情参考 https://nodejs.org/en/docs/inspector/
webstorm调试node的方法 http://www.cnblogs.com/jinguangguo/p/4809886.html
断点调试的方法 http://blog.youkuaiyun.com/u011277123/article/details/58585259
基础api
path:
normalize 把路径进行简单处理 处理一些小问题 改成标准的路径格式
// -> /
… ->删除
const {normalize} = require('path');
//const normalize = require('path').normalize;等于上一句 上面是es6写法
console.log(normalize('/usr//local/bin'));//\usr\local\bin
console.log(normalize('/usr//local/../bin'));//\usr\bin
join 拼接路径 不用手动处理/的问题
const {join} = require('path');
console.log(join('/usr', 'lcoal', 'haha/'))//\usr\lcoal\haha\
resolve 将相对路径解析成绝对路径
const {resolve} = require('path');
console.log(resolve('./'));//查看当前路径的绝对路径
names
const {basename, dirname, extname} = require('path');
const filepath = '/usr/loacal/bin/no.txt';
console.log(basename(filepath));//文件名no.txt
console.log(dirname(filepath));//所在文件夹/usr/loacal/bin
console.log(extname(filepath));//拓展名.txt
parse format
const {parse, format} = require('path');
const filepath = '/usr/loacal/node_modules/n/package.json';
const ret = parse(filepath);
console.log(ret)//分析一个路径
console.log(format(ret));//将解析的结果变回原来的路径
parse的解析结果如下
{ root: ‘/’,
dir: ‘/usr/loacal/node_modules/n’,
base: ‘package.json’,
ext: ‘.json’,
name: ‘package’ }
用处在于想修改路径中的某一项 先parse修改后再format回去
总结 _dirname _filename总是返回文件的绝对路径
process.cwd()总是返回执行node命令的文件夹
./在require方法中总是相对于当前文件所在的文件夹
Buffer
1.解决二进制的问题 2.实例类似整数数组,大小固定 是一个全局变量 可以直接使用
console.log(Buffer.alloc(10));//默认用0填充的长度是10
console.log(Buffer.alloc(10,1));//用1填充
console.log(Buffer.allocUnsafe(5,1));//内容随机
console.log(Buffer.from([1,2,3]));//以数组内容填充
console.log(Buffer.from('test'));//以字符串填充 每个编码代表一个字母 默认utf-8编码
//实际占了几个字节
console.log(Buffer.byteLength('test'));//4
console.log(Buffer.byteLength('测试'));//6
//是否为buffer对象
console.log(Buffer.isBuffer({}));//false
console.log(Buffer.isBuffer(Buffer.from([1,2,3])));//true
//拼接
const buffer1 = Buffer.from('this');
const buffer2 = Buffer.from(' is');
const buffer3 = Buffer.from(' a');
const buffer4 = Buffer.from(' test');
const buffer5 = Buffer.from('!');
const buf = Buffer.concat([buffer1, buffer2, buffer3, buffer4, buffer5]);
console.log(buf.toString());
//解决中文乱码问题
const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
const buf = Buffer.from('中文字符串');
for(let i = 0; i < buf.length; i+=5){
const b = Buffer.allocUnsafe(5);
buf.copy(b, 0 ,i);
console.log(b.toString());//三个字符表示一个中文字 这里用五个拆 必然是乱码
}
for(let i = 0; i < buf.length; i+=5){
const b = Buffer.allocUnsafe(5);
buf.copy(b, 0 ,i);
console.log(decoder.write(b));
}
events
大多数的node的api都采用异步事件驱动,其中的某些触发器会周期的触发命名事件来调用函数对象(监听器);
const EventEmitter = require('events');
class CustomEvent extends EventEmitter {
}
const ce = new CustomEvent();
ce.on('test', () => {
console.log('this is a test!');
})
setInterval(() => {
ce.emit('test');
}, 500);
每隔500ms会打印一次
可以传参数
const EventEmitter = require('events');
class CustomEvent extends EventEmitter{
}
const ce = new CustomEvent();
ce.on('error', (err, time) => {
console.log(err);
console.log(time);
});
ce.emit('error', new Error('oops!'), Date.now())
响应一次
const EventEmitter = require('events');
class CustomEvent extends EventEmitter{
}
const ce = new CustomEvent();
//只响应一次
ce.once('test', () => {
console.log('this is a test!');
})
setInterval(() => {
ce.emit('test');
}, 500);
移除绑定
const EventEmitter = require('events');
class CustomEvent extends EventEmitter{
}
function fn1() {
console.log('fn1');
}
function fn2() {
console.log('fn2');
}
const ce = new CustomEvent();
ce.on('test',fn1);
ce.on('test',fn2);
setInterval(() => {
ce.emit('test');
}, 500);
setTimeout(() => {
//移除掉fn2
ce.removeListener('test', fn2)
//全部都移除掉
ce.removeAllListeners('test')
},1500);
fs
文件系统file system
所有方法都有同步和异步两种方式 推荐用异步
异步方法最后一个参数都是一个回调函数 但回调函数的第一个参数一定是留个异常的 如果操作成功完成 第一个参数为null或者undefined
读文件
const fs = require('fs');
fs.readFile('./32_readfile.js','utf8', (err, data) => {
if (err) throw err;
console.log(data)
})
//这是同步操作
const data = fs.readFileSync('./02_cusmod.js', 'utf8');
console.log(data);
写文件
const fs = require('fs');
fs.writeFile('./text','this is a test', {
encoding : 'utf8'
}, err => {
if (err) throw err;
console.log('done !');
})
文件信息
const fs = require('fs');
fs.stat('./34_stat.js', (err, stats) => {
if (err) throw err;
console.log(stats.isFile());//是不是文件
console.log(stats.isDirectory());//是不是文件夹
console.log(stats);
})
重命名
const fs = require('fs');
fs.rename('./text', 'test,txt',err => {
if (err) throw err;
console.log('done')
})
删除
const fs = require('fs');
fs.unlink('./test.txt',err => {
if (err) throw err;
console.log('done')
})
读文件夹
const fs = require('fs');
fs.readdir('./', (err, files) => {
if (err) throw err;
console.log(files)
})
创建文件夹
const fs = require('fs');
fs.mkdir('test', err => {
if (err) throw err;
console.log(files)
})
删除文件夹
const fs = require('fs');
fs.rmdir('test', err => {
if (err) throw err;
console.log(files)
})
监视文件
const fs = require('fs');
//常用 和watchfile差不多 watch能监视任何内容 watchfile只能监视文件
fs.watch('./', {
recursive: true //子文件夹也监视
}, (eventType, filename) => {
console.log(eventType, filename)
})//修改会提示change xxx 删除创建文件提示 rename xxx
eventType判断文件是被删除还是修改
流
举个例子 1L的水桶接5L水,一边接一边用勺子舀
又例如网页 加载一点 显示一点
//一个数据流向另外一个
//数据 流向
const fs = require('fs');
const rs = fs.createReadStream('./41_readsteam.js'); // 创建流 流的内容就是这个文件
rs.pipe(process.stdout)//stdout就是命令台那里显示 流的方向
//一个数据流向另外一个
//数据 流向
const fs = require('fs');
const ws = fs.createWriteStream('./test2.txt');
const tid = setInterval(() => {
const num = parseInt(Math.random() * 10);
console.log(num)
if (num < 8){
ws.write(num + '')//接受的字符串或者buffer
}else {
clearInterval(tid);
ws.end();
}
},200)//模拟写一部分数据
ws.on('finish', () => {
console.log('done')
})
解决回调地狱
//回调地狱 一个异步里调用另外一个异步。。。
const fs = require('fs');
const promisify = require('util').promisify;
const read = promisify(fs.readFile);
//解决回调地狱方法一
// read('./43_promisify').then(data => {
// console.log(data.toString());
// }).catch(ex => {
// console.log(ex);
// })
//方法二
async function test() {
try{
const content = await read('./43_promisify.js');
console.log(content.toString())
}catch (ex){
console.log(ex)
}
}
test();
学做一个静态资源服务器
.gitignore
上传的时候忽略上传哪种文件 例如node_modules
复制自己的github地址. 在命令行中 git clone加仓库地址
gitignore的规则
匹配模式前/代表根目录
匹配模式后/代表目录
匹配模式加!表示不忽略这个文件 (忽略一个文件夹 但是不想忽略其中某些文件不想忽略 就用!)
*代表任意字符
?表示一个字符
**匹配多级目录
# 代表注释
readme
采用的markdown语法
editconfig
http://editorconfig.org/
按项目确定代码风格
root = true 不要往上找了 这就是根目录
end_of_line = lf 用unix风格还是windows风格的回车 lf表示unix
[*.{js,py}]
charset = utf-8 js或者py文件用utf8的字符集
[*.py]
indent_style = space 缩进是用space
indent_size = 4 //4个空格
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
gulp的使用
node_modules中bin文件夹代表安装的包暴露出的可执行的
均为gulpfile文件下
给文件搬家
gulp-less把less转为css
[ ]中的内容表示 依赖 也就是先执行less任务再执行default
有个问题 这里会保留之前的less文件 怎么清除呢?
第一种方法在配置中改
第二种在代码中 引入del
写样式的时候加浏览器前缀很麻烦 用gulp-aotuprefixer
can i use 网站可以查找哪些样式需要加前缀
压缩css gulp-clean-css
watch
在配置这添加watch
babel处理js
babel-preset-env 针对es2015后的语法
创建.babelrc
有个问题 如果出现es2015之前的语法 怎么办
需要安装单独的插件
例如object.assign
http://babeljs.io/docs/plugins/
webpack
配置文件为webpack.config.js
entry和output入口和出口文件
怎么解决react或者像less的问题呢 使用webpack-loader 和less-loader
参考https://doc.webpack-china.org/concepts/
爬虫
反爬虫
通过user-agent如果看到是爬虫就拒绝访问
通过验证码
单位时间的访问量和访问次数 监测
关键信息用图片混淆
异步加载
node中的cheerio爬虫有缺陷绕不过反爬虫
我们使用puppeteer 适用chrome
参考https://www.npmjs.com/package/puppeteer