原生的go推荐目录是
├─bin ├─pkg ├─src bin目录放二进制文件,可以是编译后的可执行文件,也可以是安装的第三方命令行,比如beego的命令行工具会安装在bin目录。pkg存放第三方依赖,src存放代码文件。单个项目没什么疑问,就这么存放,主要是有多个项目的时候对新手来说会有点迷糊,就像我,建立了两个项目就像进了迷宫,提交到git上就更乱了,究其原因是把bin目录和pkg目录当做项目源码的一部分了。其实这两个目录是作为公共目录存在的,多个项目公用。src目录下也是可以有多个项目的,每个项目有自己独立的文件夹。
.src\ ├─project1 ├─project2 ├─project3 所以在创建项目和克隆代码时都是在src目录下进行,不能把bin目录和pkg目录提交到git上。
但是beego并没有完全遵循这一原则,它将二进制文件直接编译到了项目源码目录下,或许可配置生成目录,暂时没有了解到,所以需要在.gitignore中排除掉生成的可执行文件,否则提交的时候就比较费时了。
Promise虽然解决了原生回调的金字塔写法,但是并不能很好的处理if else流程,相比起Java等同步方式编写还是有点难度,不过还是有方法写出优雅的代码。 下面用了一个不复杂的例子来说明:
假设一个人去银行开户,存钱,如果这个人是普通用户,需要先1.排队等候,2.开户,3.存钱。 如果是VIP用户,直接进行第2,3步操作。 如果普通用户是黑名单用户不给开户办卡。
var fs = require("fs"); function queue(){ return new Promise((resolve, reject)=>{ fs.readFile("../queue.txt", "utf8", (err, data)=>{ if (err) reject(err); console.log(data.toString()); resolve(data.toString()); }) }) } function account(id){ return new Promise((resolve, reject)=>{ fs.writeFile('../account.txt', id+'一个新账户', (err) => { if (err) reject(err); console.log('办卡成功!'); resolve(); }); }) } function money(id){ return new Promise((resolve, reject)=>{ fs.writeFile('../money.txt', id+'存了10万', (err) => { if (err) reject(err); console.log('存款成功!'); resolve(); }); }) } //入口 function enter(id) { Promise.
typescript(以下简称ts)中泛型如何使用?(以下代码为ts书写) 个人认为开发语言中一些高级特性如果在不太理解的情况下不使用也无妨,无非是代码写的多一点,烂一点。但是我想作为程序猿大家还是会有所追求的。就以泛型来说,不使用也能正常开发,只不过在个别情况下需要写几份看起来相同的代码。比如在不使用泛型的情况下要求函数参数为number类型,并且返回number。
function a(args: number ): number { return args; } console.log(a(123)); 又有另外一个要求,参数为string类型,并且返回string
function b(args: string ): string { return args; } console.log(b("sdg")); 如果还有其他类似的要求,就要不停的写类似格式的代码,那么改进一下:
function c(args: any ): any { return args; } console.log(c("sdg")); 这样是可以接受任何类型参数并且返回,但缺点是可以知道能传入任何类型而不知道返回的具体类型是什么,只知道是any类型。将上面的c函数稍作修改:
function c(args: any ): any { let n = args + "变成了字符串"; //甚至更多的处理 return n; } console.log(c(1)); c函数参数是数字,返回的可能是其他类型。 知道前面几种写法的不足再对比一下泛型的写法:
function d<A>(args: A ): A { let n = args + "变成了字符串"; //甚至更多的处理 return n; } console.
node_modules/nodemailer/node_modules/socks/node_modules/smart-buffer/typings/index.d.ts(351,5): error TS7010: 'skipTo', which lacks return-type annotation, implicitly has an 'any' return type. 缺少返回类型的注释,隐含地具有’any’返回类型。 tsconfig.json配置文件中noImplicitAny设置为false,该选项的意思是:在表达式和声明上有隐含的’any’类型时报错。
完成了最基本的项目框架以后就是配置编辑器和编译选项,在没有特别配置的情况下,根目录下执行tsc会在ts后缀文件同级目录下生成js后缀的文件, 这样也没什么不可以,但是在编辑器列表中看着有点混乱,生成的js文件是不建议直接修改的,就算修改了下次修改ts文件编译后也会重置文件内容。 所以还是单独有个文件夹存放生成的js文件,这时就要配置tsconfig.json文件了。tsconfig.json文件可以通过tsc --init命令生成,自动生成内容比较简单,是可以直接使用的。 以下的配置是经过一些特别需求配置的,可以直接复制一下内容到tsconfig.json文件中,然后根据自己的需求加减内容。 其余参数可以参考:http://www.tslang.cn/docs/handbook/compiler-options.html
typescript编译配置
{ "compilerOptions": { "module": "commonjs",//模块化规范 "target": "es5",//生成js "noImplicitAny": true,//在表达式和声明上有隐含的'any'类型时报错 "noImplicitReturns": true,//函数没有返回值提示 "noFallthroughCasesInSwitch": true,//switch没有break提示 "removeComments": true,//输出文件移除注释 "noEmitOnError": true,//ts文件错误时不生成js "rootDir": "./",//需要编译的根目录 "outDir": "./build",//编译文件输出目录 "sourceMap": ture//是否生成.map文件,用于ts debug调试 }, "include": [ "*/**/*.ts" ], "exclude": [ //默认排除了node_modules ] } 为了能抛开在命令行中执行tsc命令,能直接通过vscode编辑器来编译,可以使用ctrl+shift+B快捷方式来编译ts文件。第一次使用会有提示
选择TypeScript - Watch-Mode,会在项目根目录下创建.vscode文件夹和tasks.json文件,内容如下:
vscode编译typescript配置
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "0.1.0", "command": "tsc", "isShellCommand": true, "args": ["-w", "-p", "."], "showOutput": "silent", "isWatching": true, "problemMatcher": "$tsc-watch" } 这样就不用每次编译了,只要文件有修改就会自动编译
ES6引入了类的概念,同时也引入了类似于java的注解概念,我们称之为装饰器,用于在某些场景下修改类和类成员。typescript要支持装饰器需要手动开启experimentalDecorators。
命令行编译
tsc --target ES5 --experimentalDecorators /test.ts 或者配置文件设置:
tsconfig.json:
{ "compilerOptions": { "experimentalDecorators": true } } 那么这个装饰器到底什么原理呢?它是什么时候执行的?用下面代码为例:
//test.ts class Route { greeting: string; constructor(greet: string){ this.greeting = greet; } @route("hello") default(): any { console.log(this.greeting); } } function route(name: string) { return function (target: Object, value: string, desc: PropertyDescriptor) { console.log('>>>>', name); } } 生成的js代码:
//test.js var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.
在一切开始之前先具备的开发环境:
nodejs4.0+
推荐的开发工具vscode
我希望通过本文介绍能直接做出一个最简单的项目框架,以便日后参考,这也是我想把之前纯nodejs写的博用typescript客重写一遍。
还是以expressjs为框架来搭建
使用express-generator来生成项目基本框架,需要全局安装
npm install -g express-generator
express -e mpro
参数-e是以ejs为模板引擎,mpor为项目目录名,最后生成这样的目录结构:
. ├── app.js ├── bin │ └── www ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views ├── error.ejs └── index.ejs 然后是全局安装typescript,便于在任何目录下编译,也可以安装在项目目录下。
npm install -g typescript
安装完以后初始化typescript:
tsc –init
会在根目录下生成tsconfig.json文件,用于配置ts的编译选项。 有了基本目录以后就可以着手编写代码了,为了使用ts开发,可以手动将routes目录下的js文件 和app.js后缀名改成.ts. 做完以上步骤以后可以试着在项目根目录下执行> tsc命令,可能什么都没发生, 这个命令会默认编译当前目录下的ts文件,但是我们项目中并没有这样的文件, 你可以自己新建一个后缀为.ts的文件再执行> tsc还是什么都没有发生一样,但是如果我们把app.ts (已经改过后缀的app.js文件)中的
var bodyParser = require('body-parser'); 改成
初识TypeScript 从入职新公司开始算,使用typescript已经有半年多了,这是一个创业公司使用nodejs开发的后台业务管理系统,已经做的很大了,支撑着公司每月千万人民币的业务处理。很多怀疑nodejs是否可以开发后台管理,作为一个过来人告诉你可以放心使用,现在的javascript早已不是10年前用来写前端特效的js了。美中不足的是nodejs对报表这类数据处理量较大的功能有点力不从心,经常拖垮系统,当然也不是完全不能用nodejs来处理报表,可以合理的使用子进程单独处理数据而不影响主进程。但还是不推荐。
使用感受 其实起初对于typescript开发nodejs我是拒绝的,原因是建立起这个项目的人都走了,而这个项目结构实在是复杂,在windows下环境搭建费了好大精力,尤其是为了使用一个bcrypt加密包,需要安装Python,C++来编译,还不能使用nodejs0.12以上版本,最烦人的是每次修改文件后编译速度非常慢,这让我重新体验了开发JAVA时期的痛苦。而且服务启动方法也是前人自己编写shell脚本来同时编译前端文件和后端文件。并没有使用nodemon,supervisor这种监控工具。后来折腾发现可以直接使用vscode单独编译ts文件,一般12秒就编译完成,这与我之前的项目1秒修改重启相比还是慢了很多,不过也免强只能接受,此时只觉得如此麻烦和JAVA开发有什么区别!所以基于这些不好的体验我很不喜欢typescript。
重新认识 最近又折腾了下我用纯nodejs开发的博客,突然发现,写起来确实不如typescript爽,主要是纯nodejs在webstorm和vscode上语法提示和错误提示较弱,而typescript则可以很好的提示并且在开发过程中就能发现一些很低级错误,不用等到运行时才发现。尤其是对ES6的支持比较完整,甚至可以使用async,await这些ES7提案中的特性。虽然现在高版本的nodejs已经支持一些新特性,但是并不是所有项目都可以使用高版本。如果说为什么要使用typescript,那就是typescript集合了js的快速和强类型语言的安全稳定,用于开发一些较大的项目绝对利大于弊。 尤其是对于一些多人合作的项目来说,typescript的强类型能够很好约束对象属性,参数传递,对后续的重构也起到至关重要的作用。 有人可能会担心降低开发效率,这一点大可不必担心,你定义类型消耗的时间会从强类型提示上补回来的。 接下来会写一篇完整的环境搭建教程:使用typescript开发nodejs的环境搭建(一)
为了获取json类型的参数煞费苦心,差点不再爱了。
前端请求代码:
$.ajax({ url: "/user", type: "post", contentType: 'application/json', data: JSON.stringify({username:"张三",mobile:"13265478965"}), //这才是最重要的地方,必须用JSON.stringify序列化成字符串, //直接使用对象死活都接收不到,至于大小写并不影响,只要写对了就行 dataType: "json" }).done(function(res) { if(res.result){ alert("成功") } }); 需要传输json类型数据,同时数据需要使用JSON.stringify序列化。
后端接收代码:
func (c *UserController) Post() { var form struct { Username string `json:"username"` Mobile string `json:"mobile"` } c.BindJSON(&form) user := &User{Username: form.UserName, Mobile: form.Mobile} err := user.AddUser()//这是添加用户函数 if nil != err { c.Data["json"] = map[string]interface{}{"result": false, "msg": err} } else { c.Data["json"] = map[string]interface{}{"result": true, "msg": "新增成功"} } c.
nodejs请求网络资源写到本地 有这样一个需求:抓取网络上的图片或其他类型文件保存的本地,根据以往的经验这样写:
var request=require("request"); var fs=require("fs"); var url="http://h.hiphotos.baidu.com/zhidao/pic/item/6d81800a19d8bc3ed69473cb848ba61ea8d34516.jpg"; HttpRequest.get(url, (err, res, body) => { res.setEncoding("binary");//二进制(binary) FS.writeFile("out.png", body, "binary", function (err) {//以二进制格式保存 if (err) throw err; console.log("file saved"); }); }); 这个例子可能看起来比较眼熟,其实我就是从网上找来的,如果用这种方法成功的话那么恭喜你了,反正我是没有成功。还有一种情况是虽然成功的写文件到本地了,但是打不开,反正提示就是文件损坏之类的。总之,就是上面的方法行……不……通……
还好,有另一种方法可行,而且看起来代码量也少了很多。
var url="http://h.hiphotos.baidu.com/zhidao/pic/item/6d81800a19d8bc3ed69473cb848ba61ea8d34516.jpg"; request(url).pipe(fs.createWriteStream("out.png")); 不过,虽然代码少了很多,但是疑问多了很多,pipe函数是什么东西,还有fs.createWriteStream()函数,借助nodejs帮助文档大概解释一二。
_pipe_函数把他称作为管道吧,回头看一下代码,就是把前面请求的内容通过管道输送到后面的容器里面。虽然这么说还是不太理解,为什么可以调用pipe管道?
非郑重声明:本人也是用蹩脚的英语大概理解nodejs stream的相关知识,如果有不妥的地方误导了人只能表示抱歉
有关网络请求,比如http request,和文件操作方面的都涉及到流的概念,流是可读可写的,
const http = require("http"); var server = http.createServer( (req, res) => { } 其中req是可读Readable Stream的流,res是可写Writable Stream的流,再看request(url).pipe(fs.createWriteStream("out.png"));,
request是继承自stream模块的,所以它也是具有可读可写的特性,自然就能调用pipe函数,从而获取到数据,然后需要写入到一个文件中,就需要由pipe输出到一个可写的流中,fs.createWriteStream(path)返回一个可写WriteStream的流,就可以接受pipe管道的输出,最后写入到本地文件。