diff --git "a/Koa\346\212\200\346\234\257\345\210\206\344\272\2532.0.md" "b/Koa\346\212\200\346\234\257\345\210\206\344\272\2532.0.md" index 0fa5d036f24cf864e0c50be6eb6a0ac54a54da5a..db3725c7cfcfc1e77c5b41cbeb476f37891ae8d8 100644 --- "a/Koa\346\212\200\346\234\257\345\210\206\344\272\2532.0.md" +++ "b/Koa\346\212\200\346\234\257\345\210\206\344\272\2532.0.md" @@ -1,13 +1,13 @@ ### 学习交流 -项目同时部署在阿里云,访问地址[www.keepforward.xyz:3000](www.keepforward.xyz:3000) -gihub地址:[https://github.com/a1511870876/myblog](https://github.com/a1511870876/myblog)欢迎star +项目同时部署在阿里云,访问地址[www.keepforward.xyz:3000](www.keepforward.xyz:3000) +gihub地址:[https://github.com/a1511870876/myblog](https://github.com/a1511870876/myblog)欢迎star 一些学习分享,大家共同交流,地址[https://github.com/a1511870876/studyFiles](https://github.com/a1511870876/studyFiles) ### 写在前面 -Koa使用了ES6规范的generator和异步编程是一个更轻量级Web开发的框架,Koa 的先天优势在于 generator。由于是我个人的分享交流,所以Node基础、ES6标准、Web开发基础以及Koa的"Hello World"程序都不在讨论,希望各位小伙伴提出意见和指导。 -PS:Koa 内核中没有捆绑任何中间件,但不用担心,Koa 拥有极其强悍的拓展性,正文所有中间件都可以在npm官网下载安装,但国内域名安装会有一些限制,提供一个国内镜像安装方法,速度非常快,在直接npm模块失败的时候非常好用,使用npm --registry=http://registry.npmjs.org install XXXXX –XX 命令安装,只需要在install后面加上要安装的中间件名称和相应的参数即可。 +  Koa使用了ES6规范的generator和异步编程是一个更轻量级Web开发的框架,Koa 的先天优势在于 generator。由于是我个人的分享交流,所以Node基础、ES6标准、Web开发基础以及Koa的"Hello World"程序都不在讨论,希望各位小伙伴提出意见和指导。 +  PS:Koa 内核中没有捆绑任何中间件,但不用担心,Koa 拥有极其强悍的拓展性,正文所有中间件都可以在npm官网下载安装,但国内域名安装会有一些限制,提供一个国内镜像安装方法,速度非常快,在直接npm模块失败的时候非常好用,使用npm --registry=http://registry.npmjs.org install XXXXX –XX 命令安装,只需要在install后面加上要安装的中间件名称和相应的参数即可。 ### 一、使用Koa搭建Web项目流程 -**1、Koa项目创建** -个人认为不管任何框架,Web项目搭建必需的几个方面,页面、中间件、路由、会话和存储、日志、静态文件指定,以及错误的处理。当然,网站开发不止这些东西,还有许多主题,比如实时通讯,搜索引擎架构,权限控制,邮件优先队列,日志记录分析,对Web开发还刚刚入门属于菜鸟级别,这里就不做深入的讨论了。了解Express框架的小伙伴一定知道Express的部署过程,不管是通过express-generator生成还是WebStorm等编译器直接创建,它的目录结构大概是这样的: +**1、Koa项目创建** +  个人认为不管任何框架,Web项目搭建必需的几个方面,页面、中间件、路由、会话和存储、日志、静态文件指定,以及错误的处理。当然,网站开发不止这些东西,还有许多主题,比如实时通讯,搜索引擎架构,权限控制,邮件优先队列,日志记录分析,对Web开发还刚刚入门属于菜鸟级别,这里就不做深入的讨论了。了解Express框架的小伙伴一定知道Express的部署过程,不管是通过express-generator生成还是WebStorm等编译器直接创建,它的目录结构大概是这样的: ``` |——app.js |——bin @@ -16,15 +16,15 @@ PS:Koa 内核中没有捆绑任何中间件,但不用担心,Koa 拥有极 |——public |——routes |——views -``` -*app.js,是程序启动文件 -*bin,存放执行程序 -*node_modules,存放项目依赖库 -*package.json,是配置和一些相关信息 -*public,存放静态文件(css,js,img) -*routes,存放路由文件 -*views,存放前台页面文件 -这些结构基本包含了上述提到的Web项目搭建的要素,但是目前类似express-generator的Koa部署工具Koa-generator(非官方)并不完善并且个人测试存在些许错误。其实Koa-generator也是仿造上述express-generator生成的目录,既然这样还不如手动创建目录来的爽快(generator-k是另一款生成器,用上去感觉还行),在根目录新建app.js作为程序的启动文件,创建三个文件夹分别命名public、routes和views,最后新建package.json文件存放你的项目的一些信息。完成这些创建之后,用npm命令安装Koa,这样的话一个基本的Koa框架就搭建好了,非常的的轻量级,它的目录结构如下: +``` +  *app.js,是程序启动文件 +  *bin,存放执行程序 +  *node_modules,存放项目依赖库 +  *package.json,是配置和一些相关信息 +  *public,存放静态文件(css,js,img) +  *routes,存放路由文件 +  *views,存放前台页面文件 +  这些结构基本包含了上述提到的Web项目搭建的要素,但是目前类似express-generator的Koa部署工具Koa-generator(非官方)并不完善并且个人测试存在些许错误。其实Koa-generator也是仿造上述express-generator生成的目录,既然这样还不如手动创建目录来的爽快(generator-k是另一款生成器,用上去感觉还行),在根目录新建app.js作为程序的启动文件,创建三个文件夹分别命名public、routes和views,最后新建package.json文件存放你的项目的一些信息。完成这些创建之后,用npm命令安装Koa,这样的话一个基本的Koa框架就搭建好了,非常的的轻量级,它的目录结构如下: ``` |——app.js |——node_modules @@ -45,8 +45,8 @@ PS:Koa 内核中没有捆绑任何中间件,但不用担心,Koa 拥有极 Koa项目运行:node --harmony app.js 必须加 --harmony ,这样才会支持 ES6 语法。 ``` -**2、Koa日志** -日志是项目error调试和日常维护的基本手段,Koa有日志模块Koa-logger,npm install Koa-logger后使用app.use(logger());命令程序就会在控制台自动打印日志,当然如果你对Koa-logger的风格不满意或者想要看到更多得信息也可以自己编辑代码实现有自己风格的日志打印。 +**2、Koa日志** +  日志是项目error调试和日常维护的基本手段,Koa有日志模块Koa-logger,npm install Koa-logger后使用app.use(logger());命令程序就会在控制台自动打印日志,当然如果你对Koa-logger的风格不满意或者想要看到更多得信息也可以自己编辑代码实现有自己风格的日志打印。 例如: ``` auto map route -> [get]/authority/saveAddUser/ @@ -55,10 +55,10 @@ PS:Koa 内核中没有捆绑任何中间件,但不用担心,Koa 拥有极 auto map route -> [get]/authority/deletedUser/ auto map route -> [get]/authority/getSelectValues/ auto map route -> [get]/authority/saveAuthority/ -``` -最后呢,如果有需要,要把日志进行存储。 -**3、Koa的错误处理** -Koa 有 error 事件,当发生错误时,可以通过该事件,对错误进行统一的处理。 +``` +  最后呢,如果有需要,要把日志进行存储。 +**3、Koa的错误处理** +  Koa 有 error 事件,当发生错误时,可以通过该事件,对错误进行统一的处理。 ``` var Koa = require('koa'); var app = Koa(); @@ -67,7 +67,7 @@ app.on('error', function(err,ctx){ }); app.listen(3000); ``` -上面这段代码在如果捕获到错误,页面会打印出 “Internal Server Error” (这是Koa对错误的默认处理)。这个错误我们在综合监控系统中也经常见到,那么我们显然无法根据这条日志得到什么信息 +  上面这段代码在如果捕获到错误,页面会打印出 “Internal Server Error” (这是Koa对错误的默认处理)。这个错误我们在综合监控系统中也经常见到,那么我们显然无法根据这条日志得到什么信息 ``` TypeError: Cannot read property 'split' of undefined at Object.Home.index (d:\test\route\home.js:143:31) @@ -75,21 +75,21 @@ at GeneratorFunctionPrototype.next (native) at Object.dispatch (d:\test\node_modules\koa-router\lib\router.js:97:44) at GeneratorFunctionPrototype.next (native) ``` -这些错误信息是怎么报出来的的呢,其实是Koa-onerror 中间件,它优化错误信息,根据这些错误信息就能更好的捕获到错误。 +  这些错误信息是怎么报出来的的呢,其实是Koa-onerror 中间件,它优化错误信息,根据这些错误信息就能更好的捕获到错误。 Koa-onerror使用方法: ``` var onerror = require('Koa-onerror'); onerror(app); -``` -**4、Koa静态文件指定** -Koa静态文件指定中间件Koa-static,npm install Koa-static之后就可以使用Koa-static负责托管 Koa 应用内的静态资源。映射了静态文件目录,引用的时候直接去该目录下寻找资源,会减少一些消耗。(不知道讲的准确不准确,只是个人的理解)指定public为静态文件目录的代码如下: +``` +**4、Koa静态文件指定** +  Koa静态文件指定中间件Koa-static,npm install Koa-static之后就可以使用Koa-static负责托管 Koa 应用内的静态资源。映射了静态文件目录,引用的时候直接去该目录下寻找资源,会减少一些消耗。(不知道讲的准确不准确,只是个人的理解)指定public为静态文件目录的代码如下: ``` var staticServer = require('koa-static'); var path = require('path'); app.use(staticServer(path.join(__dirname,'public'))); ``` -**5、ejs模板的使用** -渲染页面需要一种模板,这里选择风格接近html的ejs模板。npm install Koa-ejs后就可以在Koa框架中使用ejs模版。 +**5、ejs模板的使用** +  渲染页面需要一种模板,这里选择风格接近html的ejs模板。npm install Koa-ejs后就可以在Koa框架中使用ejs模版。 ``` var render = require('koa-ejs'); render(app, { @@ -103,25 +103,25 @@ Koa静态文件指定中间件Koa-static,npm install Koa-static之后就可以 yield this.render('index',{layout:false}); }); ``` -**6、Koa路由设置** -Koa个极简的web框架,简单到连路由模块都没有配备。自己手写路由是这样的: +**6、Koa路由设置** +  Koa个极简的web框架,简单到连路由模块都没有配备。自己手写路由是这样的: ``` app.use(function *(){ //我是首页 if(this.path==='/'){ } }); -``` -使用更加强大的路由中间件,Koa中设置路由一般安装Koa-router,Koa-router支持五种方法 +``` +  使用更加强大的路由中间件,Koa中设置路由一般安装Koa-router,Koa-router支持五种方法 ``` router.get() router.post() router.put() router.del() router.patch() -``` -GET方法举例: -``` +``` +  GET方法举例: +``` var app = require('koa')(); var Router = require('koa-router'); var myRouter = new Router(); @@ -130,8 +130,8 @@ GET方法举例: }); app.use(myRouter.routes()); app.listen(3000); -``` -Koa-router 拥有丰富的 api 细节,用好这些 api ,可以让页面代码更为优雅与可维护。 +``` +  Koa-router 拥有丰富的 api 细节,用好这些 api ,可以让页面代码更为优雅与可维护。 接收query参数 ``` http://localhost:3000/?a=1(条件) @@ -152,16 +152,15 @@ Koa-router 拥有丰富的 api 细节,用好这些 api ,可以让页面代 <-- GET /?a=1 { a: '1' } { a: '1' } - 接收params参数 - + 接收params参数 http://localhost:3000/users/123(参数) router.get('/user/:id', function *(next) { console.log(this.params.id); }); -``` -param() 用于封装参数处理中间件,当访问 /detail/:id 路由时,会先执行 param() 定义的 generator function 逻辑。函数的第一个是路由参数的值,next 是中间件流程关键标识变量。 -yield next; -表示执行下一个中间件。 +``` +  param() 用于封装参数处理中间件,当访问 /detail/:id 路由时,会先执行 param() 定义的 generator function 逻辑。函数的第一个是路由参数的值,next 是中间件流程关键标识变量。 +yield next; +  表示执行下一个中间件。 ``` app.param('id',function *(id,next){ this.id = Number(id); @@ -173,52 +172,51 @@ yield next; this.body = id; }); ``` -**7、Koa中间件** - Koa的中间件很像Express的中间件,也是对HTTP请求进行处理的函数,但是必须是一个Generator函数即 function *(){} 语法,不然会报错。可以这么说,Nodejs的Web程序中任何请求和响应都是中间件在操作。 +**7、Koa中间件** +  Koa的中间件很像Express的中间件,也是对HTTP请求进行处理的函数,但是必须是一个Generator函数即 function \*(){} 语法,不然会报错。可以这么说,Nodejs的Web程序中任何请求和响应都是中间件在操作。 ``` app .use(logger()) //日志中间件 .use(serve(__dirname + '/public')) //静态文件指定中间件 .use(router.routes()) //路由中间件 .use(router.allowedMethods()); //路由中间件 -``` -app.use 加载用于处理http请求的middleware(中间件),当一个请求来的时候,会依次被这些 middlewares处理。执行的顺序是你定义的顺序。中间件的执行顺序规则是类似“栈”的结构,所有需要执行的中间件都被一个一个放入“栈”中,当没有遇到next()的时候,“栈”里边的这些中间件被逆序执行。 +``` +  app.use 加载用于处理http请求的middleware(中间件),当一个请求来的时候,会依次被这些 middlewares处理。执行的顺序是你定义的顺序。中间件的执行顺序规则是类似“栈”的结构,所有需要执行的中间件都被一个一个放入“栈”中,当没有遇到next()的时候,“栈”里边的这些中间件被逆序执行。 ``` app.use(function *(next){ this; // is the Context this.request; // is a Koa Request this.response; // is a Koa Response }); -``` -说明: -•this是上下文 -•*代表es6里的generator -http模型里的请求和响应 -•this.request -•this.response - - app.use() 究竟发生了什么不可思议的化学反应呢? - 其实 app.use() 就干了一件事,就是将中间件放入一个数组,真正执行逻辑的是:app.listen(3000); - Koa 的 listen() 除了指定了 http 服务的端口号外,还会启动 http server,等价于: +``` +说明: +  •this是上下文 +  •\*代表es6里的generator +  http模型里的请求和响应 +  •this.request +  •this.response +  app.use() 究竟发生了什么不可思议的化学反应呢? +其实 app.use() 就干了一件事,就是将中间件放入一个数组,真正执行逻辑的是:app.listen(3000); +Koa 的 listen() 除了指定了 http 服务的端口号外,还会启动 http server,等价于: ``` var http = require('http'); http.createServer(app.callback()).listen(3000); -``` - 后面这种繁琐的形式有什么用呢? - 一个典型的场景是启动 https 服务,默认 app.listen(); 是启动 http 服务,启动 https 服务就需要: +``` +  后面这种繁琐的形式有什么用呢? +  一个典型的场景是启动 https 服务,默认 app.listen(); 是启动 http 服务,启动 https 服务就需要: ``` var https = require('https'); https.createServer(app.callback()).listen(3000); ``` - ### 二、异步编程 -**1、异步流程控制** - 异步编程对 JavaScript 语言太重要。JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可。 - 以前,异步编程的方法,大概有下面四种。 -回调函数 -事件监听 -发布/订阅 -Promise 对象 -JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。它的英语名字 callback,直译过来就是"重新调用"。 +### 二、异步编程 +**1、异步流程控制** +  异步编程对 JavaScript 语言太重要。JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可。 +  以前,异步编程的方法,大概有下面四种。 +  回调函数 +  事件监听 +  发布/订阅 +  Promise 对象 +  JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。它的英语名字 callback,直译过来就是"重新调用"。 读取文件进行处理,是这样写的。 ``` fs.readFile('/etc/passwd', function (err, data) { @@ -226,7 +224,7 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 console.log(data); }); ``` - 上面代码中,readFile 函数的第二个参数,就是回调函数,也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后,回调函数才会执行。回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假定读取A文件之后,再读取B文件,代码如下。 +  上面代码中,readFile 函数的第二个参数,就是回调函数,也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后,回调函数才会执行。回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假定读取A文件之后,再读取B文件,代码如下。 ``` fs.readFile(fileA, function (err, data) { fs.readFile(fileB, function (err, data) { @@ -234,10 +232,9 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 }); }); ``` -不难想象,如果依次读取多个文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。这种情况就称为"回调函数噩梦"(callback hell)。Promise就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的横向加载,改成纵向加载。采用Promise,连续读取多个文件,写法如下。 -``` +  不难想象,如果依次读取多个文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。这种情况就称为"回调函数噩梦"(callback hell)。Promise就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的横向加载,改成纵向加载。采用Promise,连续读取多个文件,写法如下。 +``` var readFile = require('fs-readfile-promise'); - readFile(fileA) .then(function(data){ console.log(data.toString()); @@ -251,19 +248,19 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 .catch(function(err) { console.log(err); }); -``` - 上面代码中,我使用了 fs-readfile-promise 模块,它的作用就是返回一个 Promise 版本的 readFile 函数。Promise 提供 then 方法加载回调函数,catch方法捕捉执行过程中抛出的错误。可以看到,Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。 - Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。 - 那么,有没有更好的写法呢? - ECMAScript 6 (简称 ES6 )作为下一代 JavaScript 语言,将 JavaScript 异步编程带入了一个全新的阶段。异步编程的语法目标,就是怎样让它更像同步编程。 - Koa 的先天优势在于 generator。 - generator指的是 +``` +  上面代码中,我使用了 fs-readfile-promise 模块,它的作用就是返回一个 Promise 版本的 readFile 函数。Promise 提供 then 方法加载回调函数,catch方法捕捉执行过程中抛出的错误。可以看到,Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。 +  Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。 +  那么,有没有更好的写法呢? +  ECMAScript 6 (简称 ES6 )作为下一代 JavaScript 语言,将 JavaScript 异步编程带入了一个全新的阶段。异步编程的语法目标,就是怎样让它更像同步编程。 +  Koa 的先天优势在于 generator。 +  generator指的是 ``` function* xxx(){ } -``` - 是es6里的写法。 ``` +  是es6里的写法。 +``` var r = 3; function* infinite_ap(a) { for( var i = 0; i < 3 ; i++) { @@ -271,19 +268,17 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 yield a; } } - var sum = infinite_ap(5); - console.log(sum.next()); // returns { value : 8, done : false } console.log(sum.next()); // returns { value : 11, done: false } console.log(sum.next()); // returns { value : 14, done: false } console.log(sum.next()); //return { value: undefined, done: true } ``` - yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。当第一次调用 sum.next() 时 返回的a变量值是5 + 3,同理第二次调用 sum.next() ,a变量值是8 +3,知道循环执行结束,返回done:true标识。大家有没有发现个问题,Koa 中 generator 的用法与上述 demo 演示的用法有非常大得差异,那是因为 Koa 中的 generator 使用了 co 进行了封装。 -**2、co的使用** - Ps:(这里只是简单介绍,后续可以作为一个专题来讲) - co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。 - 比如,有一个 Generator 函数,用于依次读取两个文件。 +  yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。当第一次调用 sum.next() 时 返回的a变量值是5 + 3,同理第二次调用 sum.next() ,a变量值是8 +3,知道循环执行结束,返回done:true标识。大家有没有发现个问题,Koa 中 generator 的用法与上述 demo 演示的用法有非常大得差异,那是因为 Koa 中的 generator 使用了 co 进行了封装。 +**2、co的使用** +  Ps:(这里只是简单介绍,后续可以作为一个专题来讲) +  co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。 +  比如,有一个 Generator 函数,用于依次读取两个文件。 ``` var gen = function* (){ var f1 = yield readFile('/etc/fstab'); @@ -291,34 +286,36 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 console.log(f1.toString()); console.log(f2.toString()); }; -``` - co 函数库可以让你不用编写 Generator 函数的执行器。 +``` +  co 函数库可以让你不用编写 Generator 函数的执行器。 ``` var co = require('co'); co(gen); -``` - 上面代码中,Generator 函数只要传入 co 函数,就会自动执行。 - co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。 +``` +  上面代码中,Generator 函数只要传入 co 函数,就会自动执行。 +  co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。 ``` co(gen).then(function (){ console.log('Generator 函数执行完成'); }) -``` - 上面代码中,等到 Generator 函数执行结束,就会输出一行提示。 - 为什么 co 可以自动执行 Generator 函数? - 前面文章说过,Generator 函数就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。 - 两种方法可以做到这一点。 - (1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。 - (2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。 - co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。 - 参考:[http://www.ruanyifeng.com/blog/2015/05/co.html](http://www.ruanyifeng.com/blog/2015/05/co.html) -**3、Koa 中间件机制实现原理** - 使用 Koa 的同学一定会有如下疑问: - 1. Koa 的中间件机制是如何实现? - 2. 为什么中间件必须是 generator function? - 3. next 实参指向是什么?为什么可以通过 yield next 可以执行下一个中间件? - 4. 为什么中间件从上到下执行完后,可以从下到上执行 yield next 后的逻辑? - 通过实现简单的 Koa 框架(剥离除中间件外所有的逻辑)来解答上述问题,这个框架的名字叫 SimpleKoa: +``` +  上面代码中,等到 Generator 函数执行结束,就会输出一行提示。 +  为什么 co 可以自动执行 Generator 函数? +  前面文章说过,Generator 函数就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。 +  两种方法可以做到这一点。 +  (1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。 +  (2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。 +  co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。 +  参考:[http://www.ruanyifeng.com/blog/2015/05/co.html](http://www.ruanyifeng.com/blog/2015/05/co.html) +**3、Koa 中间件机制实现原理** +使用 Koa 的同学一定会有如下疑问: + +1. Koa 的中间件机制是如何实现? +2. 为什么中间件必须是 generator function? +3. next 实参指向是什么?为什么可以通过 yield next 可以执行下一个中间件? +4. 为什么中间件从上到下执行完后,可以从下到上执行 yield next 后的逻辑? + +  通过实现简单的 Koa 框架(剥离除中间件外所有的逻辑)来解答上述问题,这个框架的名字叫 SimpleKoa: ``` var co = require('co'); function SimpleKoa(){ @@ -350,8 +347,8 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 })(); } }; -``` - 写个 demo 印证下中间件执行顺序: +``` +  写个 demo 印证下中间件执行顺序: ``` var app = new SimpleKoa(); app.use(function *(next){ @@ -369,14 +366,14 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 this.body += '3'; }); app.listen(); -``` - 执行后控制台输出:123456,对照 Koa 中间件执行顺序,完全一致!寥寥几行代码,我们就实现了 Koa 的中间件机制!这就是 co 的魔力。 - ### 三、Koa中涉及但本次没有讲的问题 -**1、Koa中的cookie和session(后续详细讲解)** - web应用程序都离不开cookie和session的使用,是因为Http是一种无状态性的协议。保存用户状态信息的一种方法或手段,Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。 -**2、Koa中nosql(后续技术分享会详细讲解)** - mongodb是一个基于文档的非关系型数据库,所有数据是从磁盘上进行读写的,其优势在于查询功能比较强大,能存储海量数据。 - redis是内存型数据库,数据保存在内存中,通过tcp直接存取,优势是速度快,并发高,缺点是数据类型有限,查询功能不强,一般用作缓存。它由C语言实现的,与 NodeJS工作原理近似,同样以单线程异步的方式工作,先读写内存再异步同步到磁盘,读写速度上比MongoDB有巨大的提升,当并发达到一定程度时,即可考虑使用Redis来缓存数据和持久化Session。 +``` +  执行后控制台输出:123456,对照 Koa 中间件执行顺序,完全一致!寥寥几行代码,我们就实现了 Koa 的中间件机制!这就是 co 的魔力。 +### 三、Koa中涉及但本次没有讲的问题 +**1、Koa中的cookie和session(后续详细讲解)** +  web应用程序都离不开cookie和session的使用,是因为Http是一种无状态性的协议。保存用户状态信息的一种方法或手段,Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。 +**2、Koa中nosql(后续技术分享会详细讲解)** +  mongodb是一个基于文档的非关系型数据库,所有数据是从磁盘上进行读写的,其优势在于查询功能比较强大,能存储海量数据。 +  redis是内存型数据库,数据保存在内存中,通过tcp直接存取,优势是速度快,并发高,缺点是数据类型有限,查询功能不强,一般用作缓存。它由C语言实现的,与 NodeJS工作原理近似,同样以单线程异步的方式工作,先读写内存再异步同步到磁盘,读写速度上比MongoDB有巨大的提升,当并发达到一定程度时,即可考虑使用Redis来缓存数据和持久化Session。 ``` var mongoose = require('mongoose'); // 引入 mongoose 模块 @@ -385,10 +382,8 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 // 其中,前面那个 mongodb 是 protocol scheme 的名称;localhost 是 mongod 所在的地址; // 端口号省略则默认连接 27017;blog是数据库的名称 // mongodb 中不需要建立数据库,当你需要连接的数据库不存在时,会自动创建一个出来。 - module.exports = mongoose; // 导出 mongoose 模块 - var mongoose = require('../modules/db'); // 引入 mongoose 模块 var User = mongoose.model('User',{ @@ -397,8 +392,7 @@ JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函 }); //创建了一个名为 User 的 model var user1 = new User({name:'12345@qqqqqq.com'}); - user1.password = 'a5201314'; - + user1.password = 'a5201314'; user1.save(function(err){ if(err){ console.log("save error"); diff --git a/git.md b/git.md index e0b36bc52d08c69327c1676daf339ed8a11c4ff1..672db6b5ab934e769bfa8a2f88dacc20d8944857 100644 --- a/git.md +++ b/git.md @@ -1,10 +1,10 @@ -Create a new repository on the command line -touch README.md -git init -git add README.md -git commit -m "first commit" -git remote add origin https://github.com/a1511870876/secondWorks.git -git push -u origin master -Push an existing repository from the command line -git remote add origin https://github.com/a1511870876/secondWorks.git -git push -u origin master \ No newline at end of file +Create a new repository on the command line +touch README.md +git init +git add README.md +git commit -m "first commit" +git remote add origin https://github.com/a1511870876/secondWorks.git +git push -u origin master +Push an existing repository from the command line +git remote add origin https://github.com/a1511870876/secondWorks.git +git push -u origin master \ No newline at end of file diff --git a/nodejs+phantomjs+nodemailer.md b/nodejs+phantomjs+nodemailer.md index 796896e49d0780f52fac3a9d72788476feaef895..fbe38be5ab3694c2eda69911a78dbc9ea16affec 100644 --- a/nodejs+phantomjs+nodemailer.md +++ b/nodejs+phantomjs+nodemailer.md @@ -4,7 +4,6 @@ 代码注释已经非常详细,就不多做说明,需要的朋友自己查看代码即可,主文件Mail.js,截图文件capturePart1.js,capturePart2.js,capturePart3.js,这里只展示了capturePart1.js其他两个类似。值得注意的是有登录权限的网站一定要设置Cookie,需要截取高质量图片的话截取时间一定设置长一些。 ### Mail.js ``` - * 定时发送邮件功能说明: * node.js必备安装模块:node_modules-->phantomjs,nodemailer,node-schedule,moment * 涉及JS文件:route-->mail.js,public-->js-->capturePart1.js,capturePart2.js,capturePart3.js @@ -22,7 +21,6 @@ * 改变截图功能请修改public-->js-->server.js * 改变定时功能请修改变量rule * ------Sweety -``` //组件引入开始 var schedule = require("node-schedule"); var path = require('path'); @@ -30,7 +28,6 @@ var childProcess = require('child_process'); var phantomjs = require('phantomjs'); var nodemailer = require("nodemailer"); var moment = require("moment"); -``` //组件引入结束 /*--------------------------------------------------------------------------------------------------------------------------------------------*/ //变量定义开始 @@ -165,9 +162,7 @@ function sent(){ }); } //nodemailer发送邮件结束 - function changeData(){ - } /*---------------------------------------------------------------------------------------------------------------------------------------------*/ ``` diff --git "a/package\347\256\241\347\220\206.md" "b/package\347\256\241\347\220\206.md" index 46027a4905eb73a78b3a3f58ee8eee43202b23af..dbc912c1371e50da676546fd7210bfc4f4037944 100644 --- "a/package\347\256\241\347\220\206.md" +++ "b/package\347\256\241\347\220\206.md" @@ -1,30 +1,30 @@ -## Package -̨npm -˵ -ǰ̨bower -飺 -BowerĹNodejsеnpmPythonеpipwebԽԽÿԴĿйgithubϣbowerֻҪgithubĿһļȿʹbowerʽʹðװΪbowerṩwebwebɾwebwebܣwebͨΪhtml+css+javascriptɡʹã -ļbowernodejsذװʵʹgitأʹbowerǰȷװNodeGitҽʹgit bashִbower install -npmnodeԹ˶ΣҪκĿ¼ʹbowerĻҪnpmһҪȻʾbowerڲⲿnpmͺܼˣpathĿ¼npmȫֵİİװ·node_modulesĿ¼½NODE_PATHֵΪȫֵİİװ·node_modulesĿ¼磺pathD:\nodeNODE_PATHD:\node\node_modulesnpm install XXX CgİװȫʹãʾXXXڲⲿ -ˣϻ˵bowerʹþͺܼˣ -װbower -npm install bower -g -ĿĿ¼ -bower install jquery -гɹ֮ĿлcomponentsļУļjqueryļУjqueryļµjqueryļ -⻹˵NBĵطijjQueryˣDzdownһjQueryأbower -bower update jquery -ͿԶ°jqueryˡ -ټҪʹbootstrapbootstrapɲһļcss,jsͼƬjsjQueryʹbower -bower install bootstrap -bowerԶgithubdownµĴ룬ңԶjqueryҲdownһΡ -㷢ʱһЩĿⷢȥҪԭ̫ C C ĿĿ¼һ bower.json ļ -ĿĿ¼ִ -bower init -ʾͺãӻһbowerļ -װ jquery -bower install jquery --save - bower.json ļͻдһ Jquery -ֻҪĿĿ¼ -bower install -ͻԶװ +## Package管理 +  后台npm +  (不多说) +  前台bower +  简介: +  Bower的功能类似于Nodejs中的npm或者Python中的pip,用于web包管理,如果越来越多得开源项目都托管在github上,bower只需要将github上项目加上一个配置文件既可以使用bower方式使用安装包。作为包管理,bower能提供添加新web包,更新web包,删除web包,发布web包功能,管理包依赖。web包通常认为由html+css+javascript构成。使用: +  根据上面的简介描述,bower依赖于nodejs,下载安装依赖库实际上是使用git进行下载,因此使用bower前确保安装了Node和Git环境,并且建议使用git bash命令行来执行bower install命令。 +  (常用npm、node、大神略过此段)另外可能我们需要在任何目录使用bower命令的话,需要配置npm环境变量,这一步很重要,不然会提示“bower”不是内部或外部命令,配置npm环境变量就很简单了,在path目录下添加npm全局的包的安装路径但不包含node_modules目录,另外新建NODE_PATH,值为全局的包的安装路径包含node_modules目录,例如:path:D:\node,NODE_PATH:D:\node\node_modules,这样我们npm install XXX –g的安装包才能在全局使用,不会出现提示“XXX不是内部或外部命令”。 +  好了,废话不多说,bower使用就很简单了: +  安装bower +  npm install bower -g +  在项目目录中运行 +  bower install jquery +  运行成功之后项目中会多出components文件夹,文件夹中jquery文件夹,jquery文件夹里面就有最新的  jquery文件。 +  这还不能说明他NB的地方,试想下面的场景,jQuery升级了,是不是再down一次jQuery呢?bower可以这样做: +  bower update jquery +  就可以自动升级到最新版的jquery了。 +  再假设我们需要使用bootstrap,bootstrap可不是一个文件,有css,js还有图片。js还依赖于jQuery,如果使用bower: +  bower install bootstrap +  bower会自动从github上down最新的代码,而且,会自动将依赖包jquery也down一次。 +  如果你发布程序的时候不想把一些依赖的库发布上去(主要原因是太大了 – – ),可以在项目根目录下生成一个 bower.json 文件用来管理依赖。 +  在项目目录下执行 +  bower init +  按照提示操作就好,这样子会生成一个bower文件 +  安装 jquery +  bower install jquery --save +  这样子 bower.json 文件就会写入一个 Jquery的依赖项 +  别人只要在项目目录下输入 +  bower install +  就会自动安装了 diff --git "a/python\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\255\246\344\271\240.md" "b/python\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\255\246\344\271\240.md" index 1a4afb1bc88315f15341f28ef9c6d2346334d5ff..311cec04d9e99abcccdfb1f6e94c62ffd760afcb 100644 --- "a/python\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\255\246\344\271\240.md" +++ "b/python\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\255\246\344\271\240.md" @@ -1,11 +1,12 @@ -## 正则表达式学习 -.就是一个占位符,匹配除'\n'之外的任意字符。 -*匹配前面字符0次或任意次 -?匹配前一个字符0次或1次 ------------------------------------------------------------------ -上面的内容全部只需要了解即可,需要掌握的只有下面这一种组合方式(.*?) -.* 贪心算法,尽可能多的匹配,像一个胖子尽可能多的吃 -.*?非贪心算法,尽可能少的匹配,像一个婴儿少量多餐 +## 正则表达式学习 +\.就是一个占位符,匹配除'\n'之外的任意字符。 +\*匹配前面字符0次或任意次 +?匹配前一个字符0次或1次 +___ +上面的内容全部只需要了解即可,需要掌握的只有下面这一种组合方式(.*?) +.* 贪心算法,尽可能多的匹配,像一个胖子尽可能多的吃 +.*?非贪心算法,尽可能少的匹配,像一个婴儿少量多餐 +``` 例如:code = 'hhosahfoxxIxxohofhjosfhxxlovexxoruowhntlnmlxxyouxxljh' re.findall('xx.*xx',code)匹配第一个xx到最后一个xx,即xxIxxohofhjosfhxxlovexxoruowhntlnmlxxyouxx re.findall('xx.*?xx',code)匹配结果['xxIxx','xxlovexx','xxyouxx'] @@ -17,8 +18,10 @@ I love you ------------------------------------------------------------------ -re.S +``` +___ +re.S +``` 例如:code = 'hhosahfoxxI xxohofhjosfhxxlovexxoruowhntlnmlxxyouxxljh' d = re.findall('xx(.*?)xx',code) @@ -35,8 +38,10 @@ re.S re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ ------------------------------------------------------------------ -对比findall与search的区别 +``` +___ +对比findall与search的区别 +``` s2 = 'asdfxxIxx123xxlovexxdfdasdfxxIxx123xxlovexxdfd' f = re.search('xx(.*?)xx123xx(.*?)xx',s2) print f.group(2) @@ -53,13 +58,17 @@ print f2 输出结果: [('I', 'love'), ('I', 'love')] 注:findall返回一个list,有多次匹配在list中嵌套tuple(元组) ------------------------------------------------------------------ +``` +___ sub的使用 +``` s = '123abccssfadjfdj123' output = re.sub('123(.*?)123','123%d123%789',s) print output ------------------------------------------------------------------ +``` +___ 常用技巧 +``` 1、在确定只有一个内容时,使用search方法可以提高效率 2、不要要使用compile str = 'asdfxxIxx123xxlovexxdfdasdfxxIxx123xxlovexxdfd' @@ -71,8 +80,10 @@ a = 'asdsds123456fasd888fas' b = re.findall('(\d+)',a) print b 执行结果:['123456','888'] ------------------------------------------------------------------ +``` +___ 总结 +``` re.match 尝试从字符串的起始位置匹配一个模式。 re.search 扫描整个字符串并返回第一个成功的匹配。 re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。 @@ -80,15 +91,12 @@ re.sub用于替换字符串中的匹配项。 re.split按照能够匹配的子串将string分割后返回列表。 re.findall搜索string,以列表形式返回全部能匹配的子串。 re.finditer搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。 - - re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ re.S 使 . 匹配包括换行在内的所有字符 re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 - . 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 \d 匹配一个数字字符。等价于 [0-9]。 \D 匹配一个非数字字符。等价于 [^0-9]。 @@ -96,7 +104,6 @@ re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得 \S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 \w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 \W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 - [Pp]ython 匹配 "Python" 或 "python" rub[ye] 匹配 "ruby" 或 "rube" [aeiou] 匹配中括号内的任意一个字母 @@ -106,7 +113,6 @@ rub[ye] 匹配 "ruby" 或 "rube" [a-zA-Z0-9] 匹配任何字母及数字 [^aeiou] 除了aeiou字母以外的所有字符 [^0-9] 匹配除了数字外的字符 - . 点 匹配单个任意字符 [...] 字符组 匹配单个字符组出现的字符 [^...] 排除型字符组 匹配单个未在字符组出现的字符 @@ -120,10 +126,9 @@ rub[ye] 匹配 "ruby" 或 "rube" | | 多选结构 (...) 括号 用处有三,请看上面 \1.\2 反向引用 对括号捕捉到的分组进行引用。 - 括号 如果需要使用分组,或者对一个分组进行量词限定,就可以使用它了。使用括号以后,可以: 在后面进行反向引用 作为一个子表达式使用 限制多选结构 ------------------------------------------------------------------ \ No newline at end of file +``` \ No newline at end of file diff --git "a/\350\256\251\344\275\240\347\232\204 Node.js \345\272\224\347\224\250\350\267\221\345\276\227\346\233\264\345\277\253\347\232\204 10 \344\270\252\346\212\200\345\267\247.md" "b/\350\256\251\344\275\240\347\232\204 Node.js \345\272\224\347\224\250\350\267\221\345\276\227\346\233\264\345\277\253\347\232\204 10 \344\270\252\346\212\200\345\267\247.md" index e1d2446a7133ba2561466cbf2145c3f4eb2007a6..c5f5d5d7312889c7462bc9c06ace81b4a5af24e2 100644 --- "a/\350\256\251\344\275\240\347\232\204 Node.js \345\272\224\347\224\250\350\267\221\345\276\227\346\233\264\345\277\253\347\232\204 10 \344\270\252\346\212\200\345\267\247.md" +++ "b/\350\256\251\344\275\240\347\232\204 Node.js \345\272\224\347\224\250\350\267\221\345\276\227\346\233\264\345\277\253\347\232\204 10 \344\270\252\346\212\200\345\267\247.md" @@ -1,202 +1,108 @@ -### 功能 -每天定时截图,并把截到的图片自动通过邮件发送。 -### 说明 -代码注释已经非常详细,就不多做说明,需要的朋友自己查看代码即可,主文件Mail.js,截图文件capturePart1.js,capturePart2.js,capturePart3.js,这里只展示了capturePart1.js其他两个类似。值得注意的是有登录权限的网站一定要设置Cookie,需要截取高质量图片的话截取时间一定设置长一些。 -### Mail.js -``` -/************************************************************************************************************ - * 定时发送邮件功能说明: - * node.js必备安装模块:node_modules-->phantomjs,nodemailer,node-schedule,moment - * 涉及JS文件:route-->mail.js,public-->js-->capturePart1.js,capturePart2.js,capturePart3.js - * 截图保存地址:public-->images-->mainPage.jpeg(1600*4200) - * 截图url:http://www.***********.com - * 程序主要思路: - * (1)phantomjs截图-->参照http://phantomjs.org/ - * (2)nodemailer发送邮件-->参照https://www.npmjs.com/package/nodemailer - * (3)node-schedule定时-->参照https://www.npmjs.com/package/node-schedule - * 注意: - * 改变发件服务器请修改SMTP - * 改变收件人请修改变量receiver - * 改变邮件内容请修改变量html - * 改变邮件附加图片和附件请修改attachments - * 改变截图功能请修改public-->js-->server.js - * 改变定时功能请修改变量rule - * ------Sweety -***************************************************************************************************************/ - -//组件引入开始 -var schedule = require("node-schedule"); -var path = require('path'); -var childProcess = require('child_process'); -var phantomjs = require('phantomjs'); -var nodemailer = require("nodemailer"); -var moment = require("moment"); - -//组件引入结束 -/*--------------------------------------------------------------------------------------------------------------------------------------------*/ -//变量定义开始 -var today; //今天开始时间 -var binPath = phantomjs.path; //获取phantomjs.exe路径 -var jsPath = process.cwd()+"/public/js/"; //获取server.js所在目录 -var childArgs; -//capturePart3(); -//capturePart1(); -//变量定义结束 -/*--------------------------------------------------------------------------------------------------------------------------------------------*/ -//主程序开始 -var rule = new schedule.RecurrenceRule(); //schedule定时器 -rule.hour = 11; -rule.minute = 0; -rule.second = 0; //定时器规则设定(每天11点触发事件) -var j = schedule.scheduleJob(rule, function(){ - var now = moment(); - today = now.clone().add(-1, 'days').format('YYYY-MM-DD'); - capturePart1(); //触发截图事件(邮件发送功能包含在截图事件里边) -}); -//主程序结束 -/*---------------------------------------------------------------------------------------------------------------------------------------------*/ -//phantomjs截图开始(第一张) -function capturePart1(){ - childArgs = [ - path.join(jsPath, 'serverPart1.js'), //server.js - ' https://www.hao123.com ' //要截图的url - ]; - childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { - if(err) - { - console.log(err); //打印错误信息 - }else{ - console.log("Captured Part1 Successful !!"); //打印正确信息 - capturePart2(); - } - }); -} -//phantomjs截图结束(第一张) -//phantomjs截图开始(第二张) -function capturePart2(){ - childArgs = [ - path.join(jsPath, 'serverPart2.js'), //server.js路径 - 'https://www.hao123.com ' //要截图的url - ]; - childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { - if(err) - { - console.log(err); //打印错误信息 - }else{ - console.log("Captured Part2 Successful !!"); //打印正确信息 - capturePart3(); - } - }); -} -//phantomjs截图结束(第二张) -//phantomjs截图开始(第三张) -function capturePart3(){ - childArgs = [ - path.join(jsPath, 'serverPart3.js'), //server.js路径 - ' https://www.hao123.com ' //要截图的url - ]; - childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { - if(err) - { - console.log(err); //打印错误信息 - }else{ - console.log("Captured Part3 Successful !!"); //打印正确信息 - sent(); // 触发发送邮件事件 - } - }); -} -//phantomjs截图结束(第三张) -/*-------------------------------------------------------------------------------------------------------------------------------------------------*/ -//nodemailer发送邮件开始 -function sent(){ - var imgPart1 = fs.readFileSync(process.cwd()+"/public/images/mainPagePart1.jpeg"); //图片来源 - var imgPart2 = fs.readFileSync(process.cwd()+"/public/images/mainPagePart2.jpeg"); //图片来源 - var imgPart3 = fs.readFileSync(process.cwd()+"/public/images/mainPagePart3.jpeg"); //图片来源 - var smtpTransport = nodemailer.createTransport("SMTP",{ //邮件SMTP设定(发送邮箱服务器必须开启SMTP) - host: "smtp.xxxxx.com", // 主机 - secureConnection: false, // 不使用 SSL - port: 587, // SMTP 端口 - auth: { - user: "xxxxx@xxxx.com", //用户名 - pass: "xxxxxx" //密码 - } - }); - var html = '' + - 'XX好:
'+ - '  下面为【XXXX】日报汇报('+today+')的内容,请参考
' + - '  (日报详细信息请点击此处登陆查看)'+ - '
' + - ''+ - ''+ - '';//邮件内容(html代码),img唯一指定地址对应cid(见mailOptions设定) - var receiver = "xxx@xxx.com";//收件人列表 - var cc = "xxxx@xxxx.com,xxx@xxx.com,xxx@xxxx.com"; //抄送人列表 - var bcc = "xxx@xxxx.com,xxx@xxxx.com"; //密抄送人列表 - var mailOptions = { //邮件内容选项设定 - from: "", //发件地址 - //to: "xxx@xxxx.com", - to: receiver, //收件人 - cc:cc, //抄送人 - bcc:bcc, //密抄送人 - subject:"【XXXX】日报汇报("+today+")", //邮件主题 - text: "【XXXX】日报汇报("+today+")", // plaintext body - html:html, //html内容 - attachments: [ - { - filename: 'mainPagePart1.jpeg', //图片名称 - contents: imgPart1, //图片来源 - cid: 'img1' //插入图片标识 - },{ - filename: 'mainPagePart2.jpeg', //图片名称 - contents: imgPart2, //图片来源 - cid: 'img2' //插入图片标识 - },{ - filename: 'mainPagePart3.jpeg', //图片名称 - contents: imgPart3, //图片来源 - cid: 'img3' //插入图片标识 - } - ] - }; - smtpTransport.sendMail(mailOptions, function(error, response){//发送邮件 - if(error){ - console.log(error); //打印错误信息 - }else{ - console.log("Sent Successful !!"); //打印正确信息 - } - }); -} -//nodemailer发送邮件结束 - -function changeData(){ +### 让你的 Node.js 应用跑得更快的 10 个技巧 +引用地址:[http://www.oschina.net/translate/10-tips-make-node-js-web-app-faster](http://www.oschina.net/translate/10-tips-make-node-js-web-app-faster) +英文原文:[http://www.sitepoint.com/10-tips-make-node-js-web-app-faster](http://www.sitepoint.com/10-tips-make-node-js-web-app-faster) +个人技术分享GitHub地址:[https://github.com/a1511870876/studyFiles](https://github.com/a1511870876/studyFiles) +  Node.js 受益于它的事件驱动和异步的特征,已经很快了。但是,在现代网络中只是快是不行的。如果你打算用 Node.js 开发你的下一个Web 应用的话,那么你就应该无所不用其极,让你的应用更快,异常的快。本文将介绍 10 条,经过检验得知可大大提高 Node 应用的技巧。废话不多说,让我们逐条来看看。 +### 1. 并行 +  创建 Web 应用的时候,你可能要多次调用内部 API 来获取各种数据。比如说,假设在 Dashboard 页面上,你要执行下面这几个调用: +  用户信息 -getUserProfile(). +  当前活动 -getRecentActivity(). +  订阅内容 -getSubscriptions(). +  通知内容 -getNotifications(). +  为了拿到这些信息,你应该会为每个方法创建独立的中间件,然后将它们链接到 Dashboard 路由上。不过问题是,这些方法的执行是线性的,上一个没结束之前下一个不会开始。可行解决案是并行调用它们。 +  如你所知由于异步性,Node.js 非常擅长并行调用多个方法。我们不能暴殄天物。我上面提到的那些方法没有依赖性,所以我们可以并行执行它们。这样我们可以削减中间件数量,大幅提高速度。 +  我们可以用 async.js 来处理并行,它是一个专门用来调教 JavaScript 异步的 Node 模块。下面代码演示怎样用 async.js 并行调用多个方法的: +``` +function runInParallel() { + async.parallel([ + getUserProfile, + getRecentActivity, + getSubscriptions, + getNotifications + ], function(err, results) { + //This callback runs when all the functions complete + }); } -/*---------------------------------------------------------------------------------------------------------------------------------------------*/ ``` - -### capturePart1.js - +  如果你想更深入了解 async.js ,请移步它的 GitHub 页面。 +### 2. 异步 +  根据设计 Node.js 是单线程的。基于这点,同步代码会堵塞整个应用。比如说,多数的文件系统 API 都有它们的同步版本。下面代码演示了文件读取的同步和异步两种操作: ``` -//phantomjs截图 -var page = require('webpage').create(), - system = require('system'), - address; -page.viewportSize = { width:1920, height: 1080}; -page.clipRect = { top: 200, left: 210, width: 1680, height: 1530 }; -page.customHeaders={"Cookie":"koa:sess=e*******=;koa:sess.sig=pjadZtLAVtiO6-Haw1vnZZWrRm8"}; -if (system.args.length === 1) { - phantom.exit(1); -} else { - address = system.args[1]; - page.open(address, function (status) { +// Asynchronous +fs.readFile('file.txt', function(err, buffer) { + var content = buffer.toString(); +}); +// Synchronous +var content = fs.readFileSync('file.txt').toString(); +``` +  不过要是你执行那种长时间的阻塞操作,主线程就会被阻塞到这些操作完成为止。这大大降低你应用的性能。所以,最好确保你的代码里用的都是异步版本 API,最起码你应该在性能节点异步。而且,你在选用第三方模块的时候也要很小心。因为当你想方设法把同步操作从你代码中剔除之后,一个外部库的同步调用会让你前功尽弃,降低你的应用性能。 +### 3. 缓存 +  如果你用到一些不经常变化的数据,你应该把它们缓存起来,改善性能。比如说,下面的代码是获取最新帖子并显示的例子: +``` +var router = express.Router(); +router.route('/latestPosts').get(function(req, res) { + Post.getLatest(function(err, posts) { + if (err) { + throw err; + } + res.render('posts', { posts: posts }); + }); +}); +``` +  如果你不经常发贴的话,你可以把帖子列表缓存起来,然后一段时间之后再把它们清理掉。比如,我们可以用 Redis 模块来达到这个目的。当然,你必须在你的服务器上装 Redis。然后你可以用叫做 node_redis 的客户端来保存键/值对。下面的例子演示我们怎么缓存帖子: +``` +var redis = require('redis'), + client = redis.createClient(null, null, { detect_buffers: true }), + router = express.Router(); +router.route('/latestPosts').get(function(req,res){ + client.get('posts', function (err, posts) { + if (posts) { + return res.render('posts', { posts: JSON.parse(posts) }); + } + Post.getLatest(function(err, posts) { + if (err) { + throw err; + } + client.set('posts', JSON.stringify(posts)); + res.render('posts', { posts: posts }); }); -} -setTimeout(function() { - console.log(""); - console.log("### STEP 5: Close page and shutdown (with a delay)"); - page.render('./public/images/mainPagePart1.jpeg', {format: 'jpeg', quality: '100'}); - page.close(); - setTimeout(function(){ - phantom.exit(); - }, 3000); -}, 19000); -``` \ No newline at end of file + }); +}); +``` +  看到了吧,我们首先检查 Redis 缓存,看看是否有帖子。如果有,我们从缓存中拿这些帖子列表。否则我们就检索数据库内容,然后把结果缓存。此外,一定时间之后,我们可以清理 Redis 缓存,这样就可以更新内容了。 +### 4. gzip 压缩 +  开启 gzip 压缩对你的 Web 应用会产生巨大影响。当一个 gzip 压缩浏览器请求某些资源的时候,服务器会在响应返回给浏览器之前进行压缩。如果你不用 gzip 压缩你的静态资源,浏览器拿到它们可能会花费更长时间。 +  在 Express 应用中,我们可以用内建 express.static() 中间件来处理静态内容。此外,还可以用 compression 中间件压缩和处理静态内容。下面是使用例: +``` +var compression = require('compression'); +app.use(compression()); //use compression +app.use(express.static(path.join(__dirname, 'public'))); +``` +### 5. 如果可以,在用客户端渲染 +  现在有超多功能强劲的客户端 MVC/MVVM 框架,比如说 AngularJS, Ember, Meteor, 等等,构建一个单页面应用变得非常简单。基本上,你只要公开一个 API,返回 JSON 响应给客户端就可以了,而不需要在服务端渲染页面。在客户端,你可以用框架来组织 JSON 然后把它们显示在 UI 上。服务端只发送 JSON 响应可以节省带宽,改善性能,因为你不需要在每个响应里面都返回布局标记了,对吧,你只需要返回纯 JSON,然后在客户端渲染它们。 +  看下我的这个教程,它是关于怎样用 Express 4 公开一个 RESTful APIs的。我还写了另一篇教程,演示了怎样把这些 APIs 和 AngularJS 结合起来。 +### 6. 不要在 Sessions 存储太多数据 +  典型的 Express 页面应用, Session 数据默认是保存在内存中的。当你把太多数据保存在 Session 的时候,会导致服务器开销显著增大。所以,要么你切换到别的储存方式来保存 Session 数据,要么尽量减少存储在 Session 中的数据量。 +  比如说,当用户登录到你的应用的时候,你可以只在 Session 中保存他们的 ID 而不是整个用户数据对象。还有,对于那些你能够从 id 拿到对象的查询,你应该会喜欢用 MongoDB 或者 Redis 来存储 session 数据。 +### 7. 优化查询 +  假设你有个博客,你要在主页上显示最新帖子。你可能会通过 Mongoose 这样取数据: +``` +Post.find().limit(10).exec(function(err, posts) { + //send posts to client +}); +``` +  不过问题是 Mongoose 的 find() 方法会把对象的所有字段都查询出来,而许多字段在主页上并不要求。比如说,commentsis 保存的是特定帖子的回复。我们不需要显示文章回复,所以我们可以在查询的时候把它给剔除掉。这无疑会提高速度。可以像这样优化上面那条查询: +``` +Post.find().limit(10).exclude('comments').exec(function(err, posts) { + //send posts to client +}); +``` +### 8. 用标准的 V8 方法 +  集合上的一些操作,比如 map,reduce,和 forEach 不一定支持所有浏览器。我们可以通过前台的库解决部分浏览器兼容性问题。但对于 Node.js,你要确切知道 Google 的 V8 JavaScript 引擎支持哪些操作。这样,你就可以在服务端直接用这些内建方法来操作集合了。 +### 9. 在 Node 前面用 Nginx +  Nginx 是个微小型轻量 Web 服务器,用它可以降低你的 Node.js 服务器的负载。你可以把静态资源配置到 nginx 上,而不是在 Node 上。你可以在 nginx 上用 gzip 压缩响应,让所有的响应都变得更小。所以,如果你有个正在营运的产品,我觉得你应该会想用 nginx 来改善运行速度的。 +### 10. 打包 JavaScript +  最后,你还可以大大提高页面应用速度,通过把多个 JS 文件打包。当浏览器在页面渲染中碰到 元素的时候会被堵塞,直到拿到这个脚本才继续运行(除非设置了异步属性)。比如,如果你的页面有五个 JavaScript 文件,浏览器会发出五个独立的 HTTP 请求来获取他们。如果把这五个文件压缩打包成一个,整体性能将可以大幅提升。CSS 文件也是一样。你可以用诸如 Grunt/Gulp 这样的编译工具来打包你的资源文件。 +### 结论 +  上面 10 条技巧肯定可以提高你的 Web 应用的速度的。不过,我知道还有改善和优化的空间。如果你有任何改善性能的技巧的话,在回复里告诉我。 \ No newline at end of file diff --git "a/\351\207\215\345\255\246html+css+js.md" "b/\351\207\215\345\255\246html+css+js.md" index 8dc9e64daabbd474cec7502700b30bf6c076e09e..d6799f1dec4581014b4500751f62ae13fff36cae 100644 --- "a/\351\207\215\345\255\246html+css+js.md" +++ "b/\351\207\215\345\255\246html+css+js.md" @@ -1,136 +1,138 @@ -## Html -1ıãԶ˫ţá -2
ıãԶ -3

ΪУ Ϊո
Ϊˮƽߡ -4
ַ -5ΪһдʱͿʹǩˡ -6
ԴΣС
-7
-8click here!½дӡ
-9
-һã߲&cc͡Bccܳ͡Subject⡣Bodyݣ
-10 srcΪͼƬַaltΪزɹʱıtitleΪͼɼʱͼ
-11
-12ı
-13ѡ򡢸ѡͬһĵѡťname ȡֵһҪһ£ͬһĵѡťſ𵽵ѡá
-14multiple="multiple"ʹбжѡ
-15ťðť
-16
ı
+8、click here!新建浏览器窗口中打开链接。 +9、发送 +(第一个参数用?隔开后边参数用&隔开,cc:抄送。Bcc:密抄送。Subject:标题。Body:内容) +10、” src为图片地址;alt为下载不成功时描述性文本;title为图像可见时图像的描述。 +11、 +12、文本域。 +13、单选框、复选框。同一组的单选按钮,name 取值一定要一致,这样同一组的单选按钮才可以起到单选的作用。 +14、下拉框。multiple="multiple"使用下拉列表框进行多选。 +15、按钮,重置按钮。 +16、
标题文本
-õԪУ -
、 +常用的内联元素有: +