• Archive by category "Javascript语言实践"
  • (Page 3)

Blog Archives

CNPM搭建私有的NPM服务

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-cnpm-npm/

nodejs-cnpm-npm

前言

随着Nodejs开发的项目越来越多,Node项目管理就成了一个需要思考的问题了。如果所有项目都开源统一用 NPM 进行管理也没什么问题,但总有一些是我们不希望的完全开放的代码,作为企业是核心秘密保留在公司内部,这个时候就需要在公司内网也搭建一套 NPM 依赖管理系统。

CNPM正好就提供了这个功能。从Github上CNPM的主页看,CNPM由国内Alibaba团队开发维护。

目录

  1. CNPM是什么?
  2. 搭建CNPM的服务器
  3. 设置私有注册库的三种方法
  4. CNPM的客户端使用

1. CNPM是什么?

CNPM 是一个Nodejs的库,致力于打造私有的 NPM 注册服务。当然,除了私有库功能以外,CNPM官网 (http://cnpmjs.org/) 还提供了NPM同步的服务。

CNPM官方发布的架构图:
cnpm-architect

从CNPM的架构图中,我们可以看出CNPM是对NPM做的镜像服务,CNPM会定期同步NPM的资源库,同时CNPM支持发布私有的库,这样就非常方便地集成了公有库和私有库,对于公司内部的开发者来说,基本感觉不到两种库的区别。

另外,我们使用NPM下载依赖包时,经常性地会遇到一些包下载失败的情况,主要原因了NPM的注册服务器在国外,国内的网络环境访问国外的IP并不是太好。所以,直接配置到国内的NPM镜像,可以减少NPM下载出错机会。

比如,最近发生的NPM下载时的“No compatible version found”错误,如果不想升级NPM的环境,那么你还选择用CNPM去进行依赖管理,关于错误的详细介绍,请参考文章 NPM下载出错 No compatible version found

2. 搭建CNPM的服务器

从官方文档中,我们看到CNPM服务器环境,只需要Node(0.11.12) + MySQL(>= 0.5.0),另外我们还需要Linux的环境,接下来就让我们动手自己搭建一个私有NPM的服务器。

我的系统环境:

  • Linux: Ubuntu 12.04.2 64bit Server
  • Node: v0.11.2
  • Npm: 1.2.21
  • MySQL: 5.6.11 MySQL Community Server (GPL)
  • IP: 192.168.1.20

通过github下载项目源代码


# 下载项目,进入目录
~ git clone https://github.com/cnpm/cnpmjs.org.git
~ cd cnpmjs.org

在我们开始安装依赖包之前,先要升级NPM的版本,不然会出现“No compatible version found”的错误。


~ sudo npm install npm -g
/usr/local/bin/npm -> /usr/local/lib/node_modules/npm/bin/npm-cli.js
npm@2.0.0-beta.0 /usr/local/lib/node_modules/npm

安装项目依赖


~ sudo npm install 
npm WARN engine koa-generic-session@1.1.3: wanted: {"node":">= 0.11.9"} (current: {"node":"0.11.2","np               m":"2.0.0-beta.0"})
npm WARN engine koa-redis@0.1.1: wanted: {"node":">= 0.11.9"} (current: {"node":"0.11.2","npm":"2.0.0-               beta.0"})
npm WARN engine koa-resource-router@0.3.3: wanted: {"node":"> 0.11.4"} (current: {"node":"0.11.2","npm               ":"2.0.0-beta.0"})
npm WARN engine koa-rt@0.0.2: wanted: {"node":">= 0.11.9"} (current: {"node":"0.11.2","npm":"2.0.0-bet               a.0"})
npm WARN engine koa-router@3.2.3: wanted: {"node":"> 0.11.4"} (current: {"node":"0.11.2","npm":"2.0.0-               beta.0"})
npm WARN engine koa-static-cache@1.1.0: wanted: {"node":"> 0.11.4"} (current: {"node":"0.11.2","npm":"               2.0.0-beta.0"})
npm WARN deprecated lingo@0.0.5: This project is abandoned
co-read@0.1.0 node_modules/co-read

error-formater@1.0.3 node_modules/error-formater
└── utility@0.1.16 (address@0.0.3)

multiline@1.0.0 node_modules/multiline
└── strip-indent@1.0.0 (get-stdin@1.0.0)

jshint@2.5.5 node_modules/jshint
├── strip-json-comments@0.1.3
├── underscore@1.6.0
├── exit@0.1.2
├── shelljs@0.3.0
├── minimatch@0.4.0 (sigmund@1.0.0, lru-cache@2.5.0)
├── console-browserify@1.1.0 (date-now@0.1.4)
├── cli@0.6.4 (glob@3.2.11)
└── htmlparser2@3.7.3 (domelementtype@1.1.1, domutils@1.5.0, entities@1.0.0, domhandler@2.2.0, readabl               e-stream@1.1.13)

koa-middlewares@1.2.0 node_modules/koa-middlewares
├── koa-conditional-get@1.0.2
├── koa-rt@0.0.2
├── koa-session@2.0.0
├── koa-etag@1.3.1 (buffer-crc32@0.2.3)
├── koa-logger@1.2.2 (passthrough-counter@0.0.1)
├── koa-compress@1.0.7 (statuses@1.0.4, koa-is-json@1.0.0, compressible@1.1.1)
├── koa-safe-jsonp@0.2.0 (jsonp-body@0.1.0)
├── koa-rewrite@1.1.0 (path-to-regexp@0.0.2)
├── koa-static-cache@1.1.0 (fs-readdir-recursive@0.0.2, compressible@1.1.1, mime-types@1.0.2)
├── koa-redis@0.1.1 (redis@0.10.3)
├── koa-router@3.2.3 (koa-compose@2.3.0, methods@1.1.0, path-to-regexp@0.2.1)
├── koa-bodyparser@1.0.1 (co-body@1.0.0)
├── koa-favicon@1.1.0 (co-fs@1.2.0)
├── koa-resource-router@0.3.3 (koa-compose@2.2.0, path-to-regexp@0.0.2, debug@0.7.4, lingo@0.0.5, defa               ults@1.0.0)
├── koa-generic-session@1.1.3 (buffer-crc32@0.2.3, uid-safe@1.0.1)
├── koa-csrf@2.1.3 (csrf@2.0.1)
├── koa-ejs@1.0.1 (ejs@1.0.0, co-fs@1.2.0, is-type-of@0.2.1)
└── koa-onerror@1.0.3 (swig@1.4.2)

创建MySQL数据库,我本地的MySQL用户名为root,密码是mysql,可以通过下面语句创建。


~ mysql -uroot -pmysql -e 'DROP DATABASE IF EXISTS cnpmjs_test;' &&\
mysql -uroot -pmysql -e 'CREATE DATABASE cnpmjs_test;' &&\
mysql -uroot -pmysql 'cnpmjs_test' < docs/db.sql &&\
mysql -uroot -pmysql 'cnpmjs_test' -e 'show tables;'
+-----------------------+
| Tables_in_cnpmjs_test |
+-----------------------+
| dist_dir              |
| dist_file             |
| download_total        |
| module                |
| module_deps           |
| module_keyword        |
| module_log            |
| module_maintainer     |
| module_star           |
| module_unpublished    |
| tag                   |
| total                 |
| user                  |
+-----------------------+

接下来,我们需要在项目的./config/index.js文件中,修改MySQL数据库的用户名和密码。


~ vi ./config/index.js

108   /**
109    * mysql config
110    */
111
112   mysqlServers: [
113     {
114       host: '127.0.0.1',
115       port: 3306,
116       user: 'root',
117       password: 'mysql'
118     }
119   ],
120   mysqlDatabase: 'cnpmjs_test',
121   mysqlMaxConnections: 4,
122   mysqlQueryTimeout: 5000,
123

启动CNPM服务器,默认会打开两个端口,7001用于NPM的注册服务,7002用于Web访问。


~  node --harmony_generators dispatch.js
[Tue Sep 02 2014 15:17:54 GMT+0800 (CST)] [worker:25211:common/redis.js] Redis config can not found
[Tue Sep 02 2014 15:17:54 GMT+0800 (CST)] [worker:25211] Server started, registry server listen at 127.0.0.1:7001, web listen at 127.0.0.1:7002, cluster: false
[Tue Sep 02 2014 15:17:54 GMT+0800 (CST)] [worker:25211] mysql ready, got 13 tables

从日志中看到,这两个端口服务都绑定在127.0.0.1的本地网络中,我们需要修改配置文件 ./config/index.js文件,注释bindingHost一行,对外网开放。


~ vi ./config/index.js

 39   /*
 40    * server configure
 41    */
 42   registryPort: 7001,
 43   webPort: 7002,
 44   //bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access

第二次,启动CNPM服务器


~ node --harmony_generators dispatch.js
[Tue Sep 02 2014 15:22:46 GMT+0800 (CST)] [worker:25259:common/redis.js] Redis config can not found
[Tue Sep 02 2014 15:22:46 GMT+0800 (CST)] [worker:25259] Server started, registry server listen at undefined:7001, web listen at undefined:7002, cluster: false
[Tue Sep 02 2014 15:22:46 GMT+0800 (CST)] [worker:25259] mysql ready, got 13 tables

通过浏览器访问:http://192.168.1.20:7002,没想到的情况,应用又崩溃了,这个坑深入了!!


[Tue Sep 02 2014 15:29:00 GMT+0800 (CST)] [worker:25404] Server started, registry server listen at undefined:7001, web listen at undefined:7002, cluster: false
[Tue Sep 02 2014 15:29:00 GMT+0800 (CST)] [worker:25404] mysql ready, got 13 tables

==== JS stack trace =========================================

    2: arguments adaptor frame: 0->1
Security context: 0x185dd4b5e291 <JS Object>#0#
    4: /* anonymous */ [/home/conan/nodejs/cnpmjs.org/node_modules/co/index.js:40] (this=0x2a1e646d07e1 <an Object>#1#,done=0x2d36f5fcdd31 <JS Function>#2#)
    5: /* anonymous */ [/home/conan/nodejs/cnpmjs.org/node_modules/koa/lib/application.js:125] (this=0x2d36f5fab301 <a Server>#3#,req=0x2d36f5fcacc1 <an IncomingMessage>#4#,res=0x2d36f5fcc3f9 <a ServerResponse>#5#)
    6: emit [events.js:100] (this=0x2d36f5fab301 <a Server>#3#,type=0x8305a126379 <String[7]: request>)
    7: arguments adaptor frame: 3->1
    8: onIncoming [_http_server.js:450] (this=0x2d36f5fc8029 <an HTTPParser>#6#,req=0x2d36f5fcacc1 <an IncomingMessage>#4#,shouldKeepAlive=0x185dd4b04161 <true>)
    
//省略日志

经过检查发现,是Node版本的问题。重新下载编译安装Node,详细操作请参考文章准备Nodejs开发环境Ubuntu,升级后的版本为

  • Node v0.13.0-pre
  • NPM 1.4.21

第三次,启动CNPM服务器。


~ node --harmony_generators dispatch.js
[Tue Sep 02 2014 16:00:25 GMT+0800 (CST)] [worker:14842:common/redis.js] Redis config can not found
[Tue Sep 02 2014 16:00:25 GMT+0800 (CST)] [worker:14842] Server started, registry server listen at undefined:7001, web listen at undefined:7002, cluster: false
[Tue Sep 02 2014 16:00:25 GMT+0800 (CST)] [worker:14842] mysql ready, got 13 tables

通过浏览器访问,CNPM服务:http://192.168.1.20:7002

cnpm-local

终于正常了,这样就成功搭建了私有的NPM注册服务。

搜索一下,我之前在NPM发布的自己的包ape-algorithm,发现没有结果,根据界面提示CNPM应用会自动去NPM上同步。

cnpm-ape-algorithm

第一次同步操作,会下载很多的包,大家要耐心等待啊。页面不能切换!

3. 设置私有注册库的三种方法

我们自己搭建的私有服务怎么用呢?

我们先建一个项目目录


~ /home/conan/nodejs
~ mkdir nodejs-cnpm && cd nodejs-cnpm

3.1 下载指定私有库

一种简单的方式就是,下载的时候指定我们自己的私有库,这样就会从我们自己的私有库中下载。如果私有库没有对应的库,CNPM会自动同步到NPM 找到我们要下载的库和版本,先在CNPM中存一份,然后再传给客户端一份,运行原理和Maven的原理一样。

执行下载的操作


~ npm install ape-algorithm --registry=http://192.168.1.20:7001
ape-algorithm@0.0.8 node_modules/ape-algorithm
└── linklist@0.0.3

# 查看下载的库
~ ls -l
drwxrwxr-x 3 conan conan 4096  9月  2 16:36 node_modules

3.2 给项目设置私有库

如果这个项目所有依赖库都从公司内网下载,那么我们可以给整个项目设置私有库,就不需要每次下载的时候单独指定了。

首先,我们查看项目的默设置,通过npm config list命令。


~ npm config list

; cli configs
registry = "https://registry.npmjs.org/"
user-agent = "npm/1.4.21 node/v0.13.0-pre linux x64"

; node bin location = /usr/local/bin/node
; cwd = /home/conan/nodejs/nodejs-cnpm
; HOME = /home/conan
; 'npm config ls -l' to show all defaults.

registry属性是指向NPM的官司位置https://registry.npmjs.org/,我们可以通过npm config set registry命令来修改这个配置。


~ npm config set registry http://192.168.1.20:7001

# 再次查看项目设置
~ npm config list
; cli configs
registry = "http://192.168.1.20:7001/"
user-agent = "npm/1.4.21 node/v0.13.0-pre linux x64"

; userconfig /home/conan/.npmrc
registry = "http://192.168.1.20:7001/"

; node bin location = /usr/local/bin/node
; cwd = /home/conan/nodejs/nodejs-cnpm
; HOME = /home/conan

这个项目再下载新包时,就会通过我们私有库去下载。

3.3 给用户设置私有库

如果我们的开发环境在内网,不允许访问外网,那么我们可以设置全局的NPM库。

在当前用户所在的根目录,找到.npmrc文件,配置NPM私有库。


~  vi ~/.npmrc

registry=http://192.168.1.20:7001

设置成功后,当前用户的所有NPM下载都会通过私有库来完成。

3.4 设置淘宝的开放库

我们除了使用自己的私有库,还可以使用淘宝的NPM库,这样可以有效地避免国内访问国外NPM库,网络不通的问题。

按照上面的方法,把registry配置为https://registry.npm.taobao.org 就行了。


registry = "https://registry.npm.taobao.org/"

4. CNPM的客户端使用

CNPM 不仅提供服务端的功能,还提供了客户端的访问功能,就像NPM一样。通过全局安装cnpm包,可以完全取代了npm的命令操作了。

安装cnpm客户端


~ sudo npm install -g cnpm

/usr/local/bin/cnpm -> /usr/local/lib/node_modules/cnpm/bin/cnpm
/usr/local/bin/cnpm-sync -> /usr/local/lib/node_modules/cnpm/bin/cnpm-sync
/usr/local/bin/cnpm-check -> /usr/local/lib/node_modules/cnpm/bin/cnpm-check
/usr/local/bin/cnpm-web -> /usr/local/lib/node_modules/cnpm/bin/cnpm-web
/usr/local/bin/cnpm-user -> /usr/local/lib/node_modules/cnpm/bin/cnpm-user
/usr/local/bin/cnpm-doc -> /usr/local/lib/node_modules/cnpm/bin/cnpm-doc
/usr/local/bin/cnpm-search -> /usr/local/lib/node_modules/cnpm/bin/cnpm-search
cnpm@1.0.0 /usr/local/lib/node_modules/cnpm
├── commander@2.3.0
├── auto-correct@1.0.0
├── giturl@0.0.3
├── cross-spawn@0.1.7
├── colors@0.6.2
├── bagpipe@0.3.5
├── open@0.0.5
├── debug@1.0.4 (ms@0.6.2)
├── npm-request@0.0.4 (urllib@0.5.11)
├── npm@2.0.0-beta.2
└── urllib@0.5.17 (default-user-agent@0.0.1, debug@0.8.1, digest-header@0.0.1)

测试一下,通过cnpm安装gulp包。


~ cnpm install gulp

> node-v8-clone@0.6.2 install /home/conan/nodejs/nodejs-cnpm/node_modules/gulp/node_modules/gulp-util/node_modules/vinyl/node_modules/node-v8-clone
> node-gyp rebuild

gulp@3.8.7 node_modules/gulp
├── tildify@0.2.0
├── interpret@0.3.2
├── pretty-hrtime@0.2.0
├── deprecated@0.0.1
├── archy@0.0.2
├── minimist@0.2.0
├── semver@3.0.1
├── chalk@0.5.0 (escape-string-regexp@1.0.1, ansi-styles@1.1.0, supports-color@0.2.0, has-ansi@0.1.0, strip-ansi@0.3.0)
├── orchestrator@0.3.0 (sequencify@0.0.7, events@1.0.2, execify@0.0.3)
├── liftoff@0.12.0 (extend@1.2.1, minimist@0.1.0, resolve@0.7.4, findup-sync@0.1.3)
├── gulp-util@3.0.1 (lodash._reinterpolate@2.4.1, dateformat@1.0.8-1.2.3, minimist@1.1.0, multipipe@0.1.0, lodash.template@2.4.1, through2@0.6.1, lodash@2.4.1, vinyl@0.4.0)
└── vinyl-fs@0.3.0 (map-stream@0.1.0, graceful-fs@3.0.2, lodash.defaults@2.4.1, mkdirp@0.5.0, through2@0.5.1, strip-bom@0.3.0, through2-map@1.4.0, glob-watcher@0.0.6, glob-stream@3.1.15, vinyl@0.2.0)

效果同NPM一样,gulp包被成功安装。

当我们要发布包到NPM的时候,由于CNPM默认同步时间差是30分钟,如果想马上同步,需要手动输入同步的命令。

同步gulp包。


~ cnpm sync gulp

Start sync ["gulp"].
sync gulp, PUT http://r.cnpmjs.org/gulp/sync?publish=false&nodeps=false
logurl: http://cnpmjs.org/sync/gulp#logid=10798
[2014-09-02 05:18:09] user: anonymous, sync gulp worker start, 1 concurrency, nodeps: false, publish: false
[2014-09-02 05:18:09] [c#0] [gulp] pkg status: 200, start...
[2014-09-02 05:18:09]   [gulp] found 15 missing star users
[2014-09-02 05:18:09]   [gulp] all versions are exists
[2014-09-02 05:18:09]   [gulp] no versions need to deleted
[2014-09-02 05:18:09]   [gulp] saving 15 star users
[2014-09-02 05:18:09]   [gulp] saving 3/187 missing npm users: ["mittya","160mph","vivainio"]
[2014-09-02 05:18:09] [c#0] [gulp] synced success, 0 versions:
[2014-09-02 05:18:09] [c#0] setImmediate after, gulp done, start next...
[2014-09-02 05:18:09] [done] Sync gulp module finished, 1 success, 0 fail
Success: [ gulp ]
Fail: [  ]
sync gulp, PUT https://registry.npm.taobao.org/gulp/sync?publish=false&nodeps=false
logurl: https://npm.taobao.org/sync/gulp#logid=8354
[2014-09-02 17:18:13] user: anonymous, sync gulp worker start, 1 concurrency, nodeps: false, publish: false
[2014-09-02 17:18:14] [c#0] [gulp] pkg status: 200, start...
[2014-09-02 17:18:14]   [gulp] found 15 missing star users
[2014-09-02 17:18:14]   [gulp] all versions are exists
[2014-09-02 17:18:14]   [gulp] saving 15 star users
[2014-09-02 17:18:14]   [gulp] no versions need to deleted
[2014-09-02 17:18:15]   [gulp] saving 3/188 missing npm users: ["mittya","160mph","vivainio"]
[2014-09-02 17:18:15] [c#0] setImmediate after, gulp done, start next...
[2014-09-02 17:18:15] [c#0] [gulp] synced success, 0 versions:
[2014-09-02 17:18:15] [done] Sync gulp module finished, 1 success, 0 fail
Success: [ gulp ]
Fail: [  ]
Sync all packages done, successed: ["gulp"], failed: []

这样我们就完全地把CNPM私有库在公司内部用起来了。把包管理的问题解决了,让程序员可以踏踏实实地写代码才是最重要的!!

转载请注明出处:
http://blog.fens.me/nodejs-cnpm-npm/

打赏作者

NPM下载出错 No compatible version found

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-npm-no-compatible-version

nodejs-npm

前言

NPM大家都熟,天天都在用。最近,NPM不断出现的下载出错 “npm ERR! Error: No compatible version found” ,已经影响到正常的开发工作,到了不得不解决的地步了。网上到处都是这个错误的问题,但解决问题的文章很难找到。我有必要来写一下。

目录

  1. NPM下载出错 No compatible version found
  2. 官方解决方案
  3. 其他解决方案

1. NPM下载出错 No compatible version found

最近,NPM不断出现的下载出错 “npm ERR! Error: No compatible version found” ,已经影响到了正常的开发工作,到了不得不解决的地步了。

我的系统环境

  • Win7 64bit
  • Node v0.10.5
  • NPM 1.2.19

NPM的下载出错


npm ERR! Error: No compatible version found: gulp-rename@'^1.2.0'
npm ERR! Valid install targets:
npm ERR! ["0.1.0","0.2.1","0.2.2","1.0.0","1.1.0","1.2.0"]
npm ERR!     at installTargetsError (D:\toolkit\nodejs\node_modules\npm\lib\cache.js:689:10)
npm ERR!     at D:\toolkit\nodejs\node_modules\npm\lib\cache.js:611:10
npm ERR!     at saved (D:\toolkit\nodejs\node_modules\npm\node_modules\npm-registry-client\lib\get.js:138:7)
npm ERR!     at Object.oncomplete (fs.js:107:15)
npm ERR! If you need help, you may report this log at:
npm ERR!     
npm ERR! or email it to:
npm ERR!     

像这种类库管理本来不用动脑子的事情,一下子就会变得很麻烦了,小白程序员肯定是各种抓狂,网上所有的文章都解决不了自己的问题,对Nodejs瞬间失去信心….

我的项目中,只是要配置gulp包及相关插件的依赖下载,项目配置文件为 package.json。


{
  "name": "nodejs-cnpm",
  "version": "0.0.1",
  "description": "cnpm",
  "keywords": [
    "cnpm"
  ],
  "author": "Conan Zhang ",
  "main": "cnpm",
  "dependencies": {},
  "devDependencies": {
    "gulp": "^3.8.7",
    "gulp-changed": "^1.0.0",
    "gulp-concat": "^2.3.4",
    "gulp-jshint": "^1.8.4",
    "gulp-minify-css": "^0.3.7",
    "gulp-ngmin": "^0.3.0",
    "gulp-rename": "^1.2.0",
    "gulp-uglify": "^1.0.0",
    "gulp-util": "^3.0.1"
  }
}

无论怎么运行都是No compatible version found的错误。


~ npm install
npm WARN package.json nodejs-cnpm@0.0.1 No readme data!
npm http GET https://registry.npmjs.org/gulp-changed
npm http GET https://registry.npmjs.org/gulp-concat
npm http GET https://registry.npmjs.org/gulp-ngmin
npm http GET https://registry.npmjs.org/gulp-uglify
npm http GET https://registry.npmjs.org/gulp-util
npm http GET https://registry.npmjs.org/gulp
npm http GET https://registry.npmjs.org/gulp-jshint
npm http GET https://registry.npmjs.org/gulp-minify-css
npm http GET https://registry.npmjs.org/gulp-rename
npm http 304 https://registry.npmjs.org/gulp-uglify
npm http 304 https://registry.npmjs.org/gulp-ngmin
npm ERR! Error: No compatible version found: gulp-uglify@'^1.0.0'
npm ERR! Valid install targets:
npm ERR! ["0.0.1","0.0.3","0.0.4","0.1.0","0.2.0","0.2.1","0.3.0","0.3.1","0.3.2","1.0.0-0","1.0.0"]
npm ERR!     at installTargetsError (D:\toolkit\nodejs\node_modules\npm\lib\cache.js:689:10)
npm ERR!     at D:\toolkit\nodejs\node_modules\npm\lib\cache.js:611:10
npm ERR!     at saved (D:\toolkit\nodejs\node_modules\npm\node_modules\npm-registry-client\lib\get.js:138:7)
npm ERR!     at Object.oncomplete (fs.js:107:15)
npm ERR! If you need help, you may report this log at:
npm ERR!     
npm ERR! or email it to:
npm ERR!     

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "D:\\toolkit\\nodejs\\\\node.exe" "D:\\toolkit\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install"
npm ERR! cwd D:\workspace\javascript\nodejs-cnpm
npm ERR! node -v v0.10.5
npm ERR! npm -v 1.2.19
npm http 304 https://registry.npmjs.org/gulp-concat
npm http 304 https://registry.npmjs.org/gulp-jshint
npm http 304 https://registry.npmjs.org/gulp-minify-css
npm http 304 https://registry.npmjs.org/gulp-changed
npm http 304 https://registry.npmjs.org/gulp-util
npm http 304 https://registry.npmjs.org/gulp
npm http 304 https://registry.npmjs.org/gulp-rename
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     D:\workspace\javascript\nodejs-cnpm\npm-debug.log
npm ERR! not ok code 0

2. 官方解决方案

查看NPM官方的说明Issue列表( https://github.com/npm/npm/issues/4984 ),这个问题已经被解决,但解决办法确实很不友好。

如果在Linux系统中,通过一条命令更新npm可以解决。

npm install -g npm

在Window环境中,必须升级NPM到1.4.10以上的版本,我重新安装了node-v0.10.31-x64.msi,对应的NPM为1.4.23,再此运行npm install命令,依赖包下载运行正常。

升级后的系统环境

  • Win7 64bit
  • Node 0.10.31
  • NPM 1.4.23

下载依赖包,运行正常。


~ npm install
npm WARN package.json nodejs-cnpm@0.0.1 No repository field.
npm WARN package.json nodejs-cnpm@0.0.1 No README data
npm WARN deprecated gulp-ngmin@0.3.0: Deprecated in favor of gulp-ng-annotate: https://github.com/Kagami/gulp-ng-annotat
e - Reasoning: https://github.com/btford/ngmin/issues/93
gulp-rename@1.2.0 node_modules\gulp-rename

gulp-changed@1.0.0 node_modules\gulp-changed
└── through2@0.6.1 (xtend@4.0.0, readable-stream@1.0.31)

gulp-util@3.0.1 node_modules\gulp-util
├── lodash._reinterpolate@2.4.1
├── dateformat@1.0.8-1.2.3
├── minimist@1.1.0
├── lodash@2.4.1
├── chalk@0.5.1 (escape-string-regexp@1.0.1, ansi-styles@1.1.0, supports-color@0.2.0, strip-ansi@0.3.0, has-ansi@0.1.0)
├── through2@0.6.1 (xtend@4.0.0, readable-stream@1.0.31)
├── multipipe@0.1.1 (duplexer2@0.0.2)
├── vinyl@0.4.2 (clone-stats@0.0.1)
└── lodash.template@2.4.1 (lodash.values@2.4.1, lodash.defaults@2.4.1, lodash._escapestringchar@2.4.1, lodash.templatesettings@2.4.1, lodash.keys@2.4.1, lodash.escape@2.4.1)

gulp-uglify@1.0.0 node_modules\gulp-uglify
├── deepmerge@0.2.7
├── vinyl-sourcemaps-apply@0.1.1 (source-map@0.1.38)
├── through2@0.6.1 (xtend@4.0.0, readable-stream@1.0.31)
└── uglify-js@2.4.15 (uglify-to-browserify@1.0.2, async@0.2.10, optimist@0.3.7, source-map@0.1.34)

gulp-minify-css@0.3.7 node_modules\gulp-minify-css
├── memory-cache@0.0.5
├── bufferstreams@0.0.1
├── clean-css@2.2.15 (commander@2.2.0)
├── through2@0.5.1 (xtend@3.0.0, readable-stream@1.0.31)
└── gulp-util@2.2.20 (lodash._reinterpolate@2.4.1, dateformat@1.0.8-1.2.3, minimist@0.2.0, chalk@0.5.1, vinyl@0.2.3,lodash.template@2.4.1, multipipe@0.1.1)

gulp-concat@2.3.4 node_modules\gulp-concat
├── through@2.3.4
├── concat-with-sourcemaps@0.1.3 (source-map@0.1.38)
└── gulp-util@2.2.20 (dateformat@1.0.8-1.2.3, lodash._reinterpolate@2.4.1, minimist@0.2.0, chalk@0.5.1, multipipe@0.1.1, vinyl@0.2.3, lodash.template@2.4.1, through2@0.5.1)

gulp-ngmin@0.3.0 node_modules\gulp-ngmin
├── gulp-util@2.2.20 (lodash._reinterpolate@2.4.1, dateformat@1.0.8-1.2.3, minimist@0.2.0, chalk@0.5.1, vinyl@0.2.3,through2@0.5.1, lodash.template@2.4.1, multipipe@0.1.1)
├── through2@0.4.2 (xtend@2.1.2, readable-stream@1.0.31)
└── ngmin@0.5.0 (astral@0.1.0, clone@0.1.18, ngmin-dynamic@0.0.1, astral-angular-annotate@0.0.2, commander@1.1.1, esprima@1.0.4, escodegen@0.0.28)

gulp@3.8.7 node_modules\gulp
├── tildify@0.2.0
├── interpret@0.3.6
├── pretty-hrtime@0.2.1
├── deprecated@0.0.1
├── archy@0.0.2
├── minimist@0.2.0
├── semver@3.0.1
├── orchestrator@0.3.7 (sequencify@0.0.7, stream-consume@0.1.0, end-of-stream@0.1.5)
├── chalk@0.5.1 (escape-string-regexp@1.0.1, ansi-styles@1.1.0, supports-color@0.2.0, strip-ansi@0.3.0, has-ansi@0.1.0)
├── liftoff@0.12.1 (extend@1.3.0, resolve@0.7.4, findup-sync@0.1.3)
└── vinyl-fs@0.3.7 (graceful-fs@3.0.2, strip-bom@1.0.0, mkdirp@0.5.0, lodash@2.4.1, vinyl@0.4.2, through2@0.6.1, glob-stream@3.1.15, glob-watcher@0.0.6)

gulp-jshint@1.8.4 node_modules\gulp-jshint
├── lodash@2.4.1
├── rcloader@0.1.2 (rcfinder@0.1.8)
├── minimatch@0.3.0 (sigmund@1.0.0, lru-cache@2.5.0)
├── through2@0.5.1 (xtend@3.0.0, readable-stream@1.0.31)
└── jshint@2.5.5 (strip-json-comments@0.1.3, underscore@1.6.0, exit@0.1.2, console-browserify@1.1.0, shelljs@0.3.0, cli@0.6.4, htmlparser2@3.7.3)

向这种强制性的升级,对于开发者来说是很不友好的、很难接受的。一个稳定运行的应用,只是为了改动一个小功能,被这种霸王的条件强制升级,有可能应用的整个环境都被破坏了,其他的很多包也要跟着升级,代价很大啊!

3. 其他解决方案

我在测试中发现了,除了强制升级NPM以外,还有一种解决方案,就是利用CNPM代替NPM来进行依赖管理,也能够正常地下载依赖包。

CNPM 是一个Nodejs的库,致力于打造私有的 NPM 注册服务。当然,除了私有库功能以外,CNPM官网还提供了NPM同步的服务。CNPM的配置及使用,将在下一篇文章中介绍,请参考文章CNPM搭建私有的NPM服务。让我们远离NPM包下载的错误吧,专心写好程序才是重点!

转载请注明出处:
http://blog.fens.me/nodejs-npm-no-compatible-version

打赏作者

AngularJS去掉的URL里的#号

AngularJS体验式编程系列文章,将介绍如何用angularjs构建一个强大的web前端系统。angularjs是由Google团队开发的一款非常优秀web前端框架。在当前如此多的web框架下,angularjs能脱颖而出,从架构设计上就高人一等,双向数据绑定,依赖注入,指令,MVC,模板。Angular.js创新地把后台技术融入前端开发,扫去jQuery一度的光芒。用angularjs就像写后台代码,更规范,更结构化,更可控。

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/angularjs-url/

angularjs-url

前言

天天都在用AngularJS,各类文档也都看过好几遍,但总是些编程上的事找不到优雅的解决办法。今天终于把AngularJS的项目访问路径URL里的#号去掉了,这个问题不见得有多难,关键是花多长时间去理解AngularJS框架本身。

目录

  1. URL的#号问题
  2. 找到错误原因
  3. 静态网站的解决方案
  4. 动态网站的解决方案

1. URL的#号问题

使用AngularJS的朋友都应该了解,AngularJS框架定义了自己的前端路由控制器,通过不同URL实现单面(ng-app)对视图(ng-view)的部署刷新,并支持HTML5的历史记录功能,详细介绍可以参考文章:AngularJS路由和模板

对于默认的情况,是不启动HTML5模式的,URL中会包括一个#号,用来区别是AngularJS管理的路径还是WebServer管理的路径。

比如:下面的带#号的URL,是AngularJS管理的路径。

  • http://onbook.me/
  • http://onbook.me/#/
  • http://onbook.me/#/book
  • http://onbook.me/#/about

这种体验其实是不太友好的,特别是像我这种喜欢简洁设计的人,#号的出现非我自愿的,怎么看怎么难受。AngularJS框架提供了一种HTML5模式的路由,可以直接去掉#号。

通过设置$locationProvider.html5Mode(true)就行了。


book.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {

   //..省略代码
    $locationProvider.html5Mode(true);
}]);

支持HTML5的路由URL。

  • http://onbook.me/
  • http://onbook.me/book
  • http://onbook.me/about

接下来的问题就来了,当用这种方式设置了路径以后。如果用户从首页(http://onbook.me/)开始访问,然后跳转到 图书页(http://onbook.me/book)一切正常。但如果用户直接打开 图书页(http://onbook.me/book) ,就会出现404错误。

url1

就是这个问题纠结了我好长时间,让我不得不用带#号的URL。

2. 找到错误原因

那么,这个问题的原因出在哪里了呢? 在路径解析上出错了。

让我从头说起,AngularJS是单页应用,一个ng-app对应一个页面,一个URL。AngularJS实现了自己的前端路由,让一个ng-app可以管理多个URL,再对应到多个ng-vew上面。当我们去访问URL(http://onbook.me/book) 的时候,怎么确定这个路径是 WebServer 后台管理的URL还是AngularJS前台管理的URL呢?

分2种情况看:

  • 1. 用户如果是先访问 首页(http://onbook.me),然后再跳转到 页面(http://onbook.me/book),则这个跳转是由AngularJS前台管理的URL,访问是正常的。
  • 2. 用户直接访问 页面(http://onbook.me/book)时,请求是先被提交到了WebServer后台,后台路由没有对应页面(http://onbook.me/book)的路由管理,就会出现404的错误。

如果能把这层想明白,技术上就非常容易解决了。我们让WebServer把属于AngularJS管理的路由URL,都发转到ng-app就可以解决404的问题了,同时,没有#号,还支持HTML5的历史记录查询!!

实现起来分为2种解决方案:

  • 1. 静态网站:纯前台网站(JS+HTML+CSS),通过Nginx提供Web服务。
  • 2. 动态网站:前台(JS + HTML + CSS) + 后台Node.js提供Web服务。

3. 静态网站的解决方案

静态网站,我们需要修改的地方包括3个文件

  • index.html : ng-app的定义文件
  • app.js : 对应ng-app的控制文件
  • nginx.conf : nginx的网站配置文件

编辑 index.html,增加base标签。


<html lang="zh-CN" ng-app="book">
<head>
    <base href="/">
	
// 省略代码
</head>

编辑app.js,增加 $locationProvider.html5Mode(true);


book.config(['$routeProvider', '$locationProvider', '$sceProvider', 'tplProvider', function ($routeProvider, $locationProvider, $sceProvider, tplProvider) {
    $routeProvider
        .when('/', {templateUrl: tplProvider.html('welcome'), controller: 'WelcomeCtrl'})
        .when('/book', {templateUrl: tplProvider.html('book'), controller: 'BookCtrl'})             //图书
        .when('/book-r1', {templateUrl: tplProvider.html('book-r1'), controller: 'BookR1Ctrl'})   //R的极客理想
        .when('/video', {templateUrl: tplProvider.html('video'), controller: 'VideoCtrl'})         //视频
        .when('/about', {templateUrl: tplProvider.html('about'), controller: 'AboutCtrl'})         //关于作者
        .otherwise({redirectTo: '/'});
    $locationProvider.html5Mode(true);
}]);

编辑nginx的配置文件,增加try_files配置。


server {
        set $htdocs /www/deploy/mysite/onbook;
        listen 80;
        server_name onbook.me;
        location / {
            root $htdocs;
            try_files $uri $uri/ /index.html =404;
        }
}

这样,静态网站就搞定了,没有麻烦的#号了,可以直接访问和任意页面的刷新。

4. 动态网站的解决方案

动态网站,我们同样需要修改的地方包括3个文件。

  • index.html : ng-app的定义文件
  • app.js : 对应ng-app的控制文件
  • server.js : Express框架的路由访问控制文件

index.html 和 app.js两个文件修改,同静态网站的解决方案。动态网站,一般不是通过Nginx直接路由,而是通过Web服务器管理路由。假设我们使用的是Node.js的Express的Web框架。

打开Express框架的路由访问控制文件server.js,增加路由配置。


app.use(function (req, res) {
    console.log(req.path);
    if(req.path.indexOf('/api')>=0){
        res.send("server text");

    }else{ //angular启动页
        res.sendfile('app/index.html');
    }
});

设置当 站内路径(req.path) 不包括 /api 时,都转发到 AngularJS的ng-app(index.html)。所以,我们再直接访问地址 (http://onbook.me/book)时,/book 不包括 /api,就会被直接转发到AngularJS进行路由管理。我们就实现了路由的优化!

我已用了AngularJS有8-9个月了,主要功能都用到了,但还不见其全貌。经常会遇到各种问题,不过比起jQuery的各种无解的问题,还是值得的。优秀的框架值得我们的研究和使用,在摸索中前进!

转载请注明出处:
http://blog.fens.me/angularjs-url/

打赏作者

用UglifyJS2合并压缩混淆JS代码

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-uglifyjs2-js/

nodejs-uglifyjs2

前言

做Web前端开发,总是要考虑页面的打开速度,如果文件数量越少、文件长度越小,就可以直接的提升网页的访问速度。

但在开发的时候,为了保证代码的可读性,我们写的程序文件会很多而且很大,这样就与部署的要求发生背离,通过UglifyJS2这个工具,我们可以在开发完成时,对代码文件进行 合并、混淆、压缩 等的操作,达到最优的访问性能。

目录

  1. UglifyJS介绍
  2. UglifyJS2介绍
  3. UglifyJS2安装
  4. UglifyJS2命令操作
  5. UglifyJS2的API使用

1. UglifyJS介绍

开始UglifyJS2介绍之前,我们先要说一下UglifyJS。UglifyJS是UglifyJS2的前身,是一个Javascript开发的通用的语法分析、代码压缩、代码优化的一个工具包。UglifyJS是基于Nodejs环境开发,支持CommonJS模块系统的任意的Javascript平台。

UglifyJS的实现主要分为2部分:

  • 生成JS代码的抽象语法树(AST),通过parse-js.js完成。
  • 遍历AST语法树,做各种操作,比如自动缩进、缩短变量名、删除块括号{}、去空格、常量表达式、连续变量声明、语块合并、去掉无法访问的代码等,通过process.js完成。

2. UglifyJS2介绍

UglifyJS2是作者对UglifyJS的重写,是完全的重写,而不仅仅是升级。从UglifyJS2官司方网页介绍看,UglifyJS2把整个的JS压缩过程,做了更进一步的细化。

上述所有的功能代码API是​​在6500行的左右,比其他的相同功能的开发包都要小。作者还提供了一个在线版本UglifyJS2的JS压缩工具,http://lisperator.net/uglifyjs/,大家可以测试一下。

3. UglifyJS2安装

系统环境:

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

UglifyJS2的安装非常简单,和Nodejs的其他包一样,全局安装使用如下命令。

npm install uglify-js -g

也可以通过github下载源代码安装。


git clone git://github.com/mishoo/UglifyJS2.git
cd UglifyJS2

我们在使用UglifyJS2的时候主要有2种方式,一种是通过命令行操作,对指定的JS文件进行压缩;另一种是通过程序的API调用,对文件或内存中的JS代码进行压缩。下面我将分两种情况进行介绍。

4. UglifyJS2命令操作

在全局安装好UglifyJS2以后,我们就可以使用UglifyJS2的命令了。

打印uglifyjs命令行的帮助信息,会打出很长一段说明。


D:\workspace\javascript\nodejs-uglifyJS2>uglifyjs -h
D:\toolkit\nodejs\\node.exe D:\toolkit\nodejs\node_modules\uglify-js\bin\uglifyjs input1.js [input2.js ...] [options]
Use a single dash to read input from the standard input.

NOTE: by default there is no mangling/compression.
Without [options] it will simply parse input files and dump the AST
with whitespace and comments discarded.  To achieve compression and
mangling you need to use `-c` and `-m`.

Options:
  --source-map                  Specify an output file where to generate source
                                map.                                    [string]
  --source-map-root             The path to the original source to be included
                                in the source map.                      [string]
  --source-map-url              The path to the source map to be added in //#
                                sourceMappingURL.  Defaults to the value passed
                                with --source-map.                      [string]
  --source-map-include-sources  Pass this flag if you want to include the
                                content of source files in the source map as
                                sourcesContent property.               [boolean]
  --in-source-map               Input source map, useful if you're compressing
                                JS that was generated from some other original
                                code.
  --screw-ie8                   Pass this flag if you don't care about full
                                compliance with Internet Explorer 6-8 quirks
                                (by default UglifyJS will try to be IE-proof).
                                                                       [boolean]
  --expr                        Parse a single expression, rather than a
                                program (for parsing JSON)             [boolean]
  -p, --prefix                  Skip prefix for original filenames that appear
                                in source maps. For example -p 3 will drop 3
                                directories from file names and ensure they are
                                relative paths. You can also specify -p
                                relative, which will make UglifyJS figure out
                                itself the relative paths between original
                                sources, the source map and the output file.
                                                                        [string]
  -o, --output                  Output file (default STDOUT).
  -b, --beautify                Beautify output/specify output options.
                                                                        [string]
  -m, --mangle                  Mangle names/pass mangler options.      [string]
  -r, --reserved                Reserved names to exclude from mangling.
  -c, --compress                Enable compressor/pass compressor options. Pass
                                options like -c
                                hoist_vars=false,if_return=false. Use -c with
                                no argument to use the default compression
                                options.                                [string]
  -d, --define                  Global definitions                      [string]
  -e, --enclose                 Embed everything in a big function, with a
                                configurable parameter/argument list.   [string]
  --comments                    Preserve copyright comments in the output. By
                                default this works like Google Closure, keeping
                                JSDoc-style comments that contain "@license" or
                                "@preserve". You can optionally pass one of the
                                following arguments to this flag:
                                - "all" to keep all comments
                                - a valid JS regexp (needs to start with a
                                slash) to keep only comments that match.
                                Note that currently not *all* comments can be
                                kept when compression is on, because of dead
                                code removal or cascading statements into
                                sequences.                              [string]
  --preamble                    Preamble to prepend to the output.  You can use
                                this to insert a comment, for example for
                                licensing information.  This will not be
                                parsed, but the source map will adjust for its
                                presence.
  --stats                       Display operations run time on STDERR.
                                                                       [boolean]
  --acorn                       Use Acorn for parsing.                 [boolean]
  --spidermonkey                Assume input files are SpiderMonkey AST format
                                (as JSON).                             [boolean]
  --self                        Build itself (UglifyJS2) as a library (implies
                                --wrap=UglifyJS --export-all)          [boolean]
  --wrap                        Embed everything in a big function, making the
                                “exports” and “global” variables available. You
                                need to pass an argument to this option to
                                specify the name that your module will take
                                when included in, say, a browser.       [string]
  --export-all                  Only used when --wrap, this tells UglifyJS to
                                add code to automatically export all globals.
                                                                       [boolean]
  --lint                        Display some scope warnings            [boolean]
  -v, --verbose                 Verbose                                [boolean]
  -V, --version                 Print version number and exit.         [boolean]
  --noerr                       Don't throw an error for unknown options in -c,
                                -b or -m.                              [boolean]

对命令参数进行解释:

  • –source-map [string],生成source map文件。
  • –source-map-root [string], 指定生成source map的源文件位置。
  • –source-map-url [string], 指定source map的网站访问地址。
  • –source-map-include-sources,设置源文件被包含到source map中。
  • –in-source-map,自定义source map,用于其他工具生成的source map。
  • –screw-ie8, 用于生成完全兼容IE6-8的代码。
  • –expr, 解析一个表达式或JSON。
  • -p, –prefix [string], 跳过原始文件名的前缀部分,用于指定源文件、source map和输出文件的相对路径。
  • -o, –output [string], 输出到文件。
  • -b, –beautify [string], 输出带格式化的文件。
  • -m, –mangle [string], 输出变量名替换后的文件。
  • -r, –reserved [string], 保留变量名,排除mangle过程。
  • -c, –compress [string], 输出压缩后的文件。
  • -d, –define [string], 全局定义。
  • -e, –enclose [string], 把所有代码合并到一个函数中,并提供一个可配置的参数列表。
  • –comments [string], 增加注释参数,如@license、@preserve。
  • –preamble [string], 增加注释描述。
  • –stats, 显示运行状态。
  • –acorn, 用Acorn做解析。
  • –spidermonkey, 解析SpiderMonkey格式的文件,如JSON。
  • –self, 把UglifyJS2做为依赖库一起打包。
  • –wrap, 把所有代码合并到一个函数中。
  • –export-all, 和–wrap一起使用,自动输出到全局环境。
  • –lint, 显示环境的异常信息。
  • -v, –verbose, 打印运行日志详细。
  • -V, –version, 打印版本号。
  • –noerr, 忽略错误命令行参数。

通过对命令行各种参数的解释,我们基本上知道了这些参数都是干什么的了,下面我就试一下。

写2个简单地JS文件,demo.js, main.js。


~ vi D:\workspace\javascript\nodejs-uglifyJS2\demo.js

'use strict';

function hello(name){
	if(name==='fens.me'){
		return "Long time no see, "+name;
	}
	return "hello "+name;
}

console.log(hello('Conan'));
console.log(hello('fens.me'));

main.js


~ vi D:\workspace\javascript\nodejs-uglifyJS2\main.js

'use strict';

function book(){
    return [
        {head:'前言',page:'/views/tpl/book-r1/preface.html',active:false},
        {head:'目录',page:'/views/tpl/book-r1/contents.html',active:true},
        {head:'代码',page:'/views/tpl/book-r1/code.html',active:false},
        {head:'试读',page:'/views/tpl/book-r1/sample.html',active:false},
        {head:'勘误',page:'/views/tpl/book-r1/mistake.html',active:false}
    ];
}

var tab=function(arr,idx){
	for(var i=0;i<arr.length;i++){
		arr[i].active = (idx==i?true:false);
	}
	return arr;
}

console.log(tab(book(),3));

接下来,用UglifyJS2命令进行操作,合并两个文件,对变量名用单字母替换,进行压缩,所有代码合并到一个函数,生成source map,指定source map来源网站。


D:\workspace\javascript\nodejs-uglifyJS2>uglifyjs main.js demo.js -o foo.min.js --source-map foo.min.js.map --source-map-root http://onbook.me -p 5 -c -m --wrap --export-all

在当前目录生成了2个新文件:foo.min.js.map, foo.min.js,分别查看这两个文件。

foo.min.js


!function(e,t){"use strict";function o(){return[{head:"前言",page:"/views/tpl/book-r1/preface.html",active:!1},{head:"目录",page:"/views/tpl/book-r1/contents.html",active:!0},{head:"代码",page:"/views/tpl/book-r1/code.html",active:!1},{head:"试读",page:"/views/tpl/book-r1/sample.html",active:!1},{head:"勘误",page:"/views/tpl/book-r1/mistake.html",active:!1}]}function n(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}t["true"]=e,console.log(a(o(),3));var a=function(e,t){for(var o=0;o

foo.min.js.map


{"version":3,"file":"foo.min.js","sources":["?"],"names":["exports","global","book","head","page","active","hello","name","console","log","tab","arr","idx","i","length","this"],"mappings":"CAAC,SAASA,EAASC,GAAnB,YAEA,SAASC,KACL,QACKC,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IACxDF,KAAK,KAAKC,KAAK,mCAAmCC,QAAO,IACzDF,KAAK,KAAKC,KAAK,+BAA+BC,QAAO,IACrDF,KAAK,KAAKC,KAAK,iCAAiCC,QAAO,IACvDF,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IANjE,QAASC,GAAMC,GACd,MAAU,YAAPA,EACK,qBAAqBA,EAEtB,SAASA,EANWN,EAAO,QAAUD,EAY7CQ,QAAQC,IAAIC,EAAIR,IAAO,GADvB,IAAIQ,GAAI,SAASC,EAAIC,GACpB,IAAI,GAAIC,GAAE,EAAEA,EAAEF,EAAIG,OAAOD,IACxBF,EAAIE,GAAGR,OAAUO,GAAKC,GAAE,GAAK,CAE9B,OAAOF,GAGRH,SAAQC,IAAIH,EAAM,UAClBE,QAAQC,IAAIH,EAAM,mBAjBTJ,UAAAI,QASLI,MAX8E,WAAW,MAAOK","sourceRoot":"http://onbook.me"}

通过一条简单的命令,就实现了对JS代码的合并、压缩等的操作,确实非常方便。

下载jquery-2.1.1.js文件自己压缩,并与官方的压缩文件进行对比。


# 下载
~ wget http://code.jquery.com/jquery-2.1.1.js
~ wget http://code.jquery.com/jquery-2.1.1.min.js

# 压缩
~ uglifyjs jquery-2.1.1.js -o jquery-2.1.1.min.uglifyjs2.js -p 5 -c -m

# 比较3个文件大小
~ ls -l
-rwx------  1 4294967295 mkpasswd 247351 Jul  6 16:26 jquery-2.1.1.js
-rwx------  1 4294967295 mkpasswd  84245 Jul  6 16:32 jquery-2.1.1.min.js
-rwx------  1 4294967295 mkpasswd  84113 Jul  6 16:28 jquery-2.1.1.min.uglifyjs2.js

我在本地压缩的文件jquery-2.1.1.min.uglifyjs2.js,与jquery官司网下载的压缩文件jquery-2.1.1.min.js大小差不多,都在84KB左右。

5. UglifyJS2的API使用

另一种使用方式是,把UglifyJS2包放到程序中,通过API对JS文件或JS代码进行压缩。首先,新建一个NPM项目文件package.json,然后在是下载UglifyJS2依赖包。

新建文件package.json


~ vi D:\workspace\javascript\nodejs-uglifyJS2\package.json


{
  "name": "nodejs-uglifyjs2",
  "version": "0.0.1",
  "description": "uglifyjs2",
  "author": "Conan Zhang ",
  "dependencies": {
  }
}

下载UglifyJS2依赖包


D:\workspace\javascript\nodejs-uglifyJS2>npm install uglify-js --save
npm WARN package.json nodejs-uglifyjs2@0.0.1 No readme data!
npm http GET https://registry.npmjs.org/uglify-js
npm http 304 https://registry.npmjs.org/uglify-js
npm http GET https://registry.npmjs.org/async
npm http GET https://registry.npmjs.org/source-map
npm http GET https://registry.npmjs.org/optimist
npm http GET https://registry.npmjs.org/uglify-to-browserify
npm http 304 https://registry.npmjs.org/uglify-to-browserify
npm http 304 https://registry.npmjs.org/optimist
npm http 304 https://registry.npmjs.org/async
npm http 304 https://registry.npmjs.org/source-map
npm http GET https://registry.npmjs.org/wordwrap
npm http GET https://registry.npmjs.org/amdefine
npm http 304 https://registry.npmjs.org/wordwrap
npm http 304 https://registry.npmjs.org/amdefine
uglify-js@2.4.14 node_modules\uglify-js
├── uglify-to-browserify@1.0.2
├── async@0.2.10
├── optimist@0.3.7 (wordwrap@0.0.2)
└── source-map@0.1.34 (amdefine@0.1.0)

我们新建一个文件uglify2.js,用于编写程序。


~ vi D:\workspace\javascript\nodejs-uglifyJS2\uglify2.js

'use strict';

var UglifyJS = require('uglify-js');

//代码压缩
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
console.log("\n===========================");
console.log(result);

//文件压缩
result = UglifyJS.minify(["demo.js"]);
console.log("\n===========================");
console.log(result.code);

//多文件压缩,指定source map和网站来源
result = UglifyJS.minify(["main.js","demo.js"],{
    outSourceMap: "out.js.map",
    sourceRoot: "http://onbook.me",
    mangle:true
});
console.log("\n===========================");
console.log(result.code);
console.log(result.map);

程序输出:


D:\workspace\javascript\nodejs-uglifyJS2>node uglify2.js

===========================
{ code: 'var b=function(){};', map: 'null' }

===========================
"use strict";function hello(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}var tab=function(e,o){for(var n=0;n
<e.length;n++)e[n].active=o==n?!0:!1;return e};console.log(hello("Conan")),console.log(hello("fens.me"));

===========================
"use strict";function book(){return[{head:"前言",page:"/views/tpl/book-r1/preface.html",active:!1},{head:"目录",page:"/v
iews/tpl/book-r1/contents.html",active:!0},{head:"代码",page:"/views/tpl/book-r1/code.html",active:!1},{head:"试读",page
:"/views/tpl/book-r1/sample.html",active:!1},{head:"勘误",page:"/views/tpl/book-r1/mistake.html",active:!1}]}function he
llo(e){return"fens.me"===e?"Long time no see, "+e:"hello "+e}console.log(tab(book(),3));var tab=function(e,o){for(var t=
0;t<e.length;t++)e[t].active=o==t?!0:!1;return e};console.log(hello("Conan")),console.log(hello("fens.me"));
//# sourceMappingURL=out.js.map
{"version":3,"file":"out.js.map","sources":["main.js","demo.js"],"names":["book","head","page","active","hello","name","
console","log","tab","arr","idx","i","length"],"mappings":"AAAA,YAEA,SAASA,QACL,QACKC,KAAK,KAAKC,KAAK,kCAAkCC,QAAO,IACxD
F,KAAK,KAAKC,KAAK,mCAAmCC,QAAO,IACzDF,KAAK,KAAKC,KAAK,+BAA+BC,QAAO,IACrDF,KAAK,KAAKC,KAAK,iCAAiCC,QAAO,IACvDF,KAAK,KAAKC
,KAAK,kCAAkCC,QAAO,ICNjE,QAASC,OAAMC,GACd,MAAU,YAAPA,EACK,qBAAqBA,EAEtB,SAASA,EDMjBC,QAAQC,IAAIC,IAAIR,OAAO,GCDvB,IAAIQ,
KAAI,SAASC,EAAIC,GACpB,IAAI,GAAIC,GAAE,EAAEA,EAAEF,EAAIG,OAAOD,IACxBF,EAAIE,GAAGR,OAAUO,GAAKC,GAAE,GAAK,CAE9B,OAAOF,GAGR
H,SAAQC,IAAIH,MAAM,UAClBE,QAAQC,IAAIH,MAAM","sourceRoot":"http://onbook.me"}

我们看到用操作uglifyJS2包的API,还是挺简单的,如果对AST树有遍历需求,API提供了非常实用的函数支持。

不过我在测试API过程中,发现有2个问题。

  • 通过API设置mangle选项,但输出没有效果。
  • 没有--wrap和--export-all 命令行参数对应的API。

通过本文的介绍,我们基本上了解了uglifyJS2包的功能和使用方法,然后就可以放心大胆地对JS代码进行压缩了。在实际的前端项目中,一般不用自己配置uglifyJS2包,而是通过grunt来调用uglifyJS2进行代码发布前的压缩,关于grunt使用,请参考文章:grunt让Nodejs规范起来

转载请注明出处:
http://blog.fens.me/nodejs-uglifyjs2-js/

打赏作者

Bootstrap3多级导航菜单

AngularJS体验式编程系列文章,将介绍如何用angularjs构建一个强大的web前端系统。angularjs是由Google团队开发的一款非常优秀web前端框架。在当前如此多的web框架下,angularjs能脱颖而出,从架构设计上就高人一等,双向数据绑定,依赖注入,指令,MVC,模板。Angular.js创新地把后台技术融入前端开发,扫去jQuery一度的光芒。用angularjs就像写后台代码,更规范,更结构化,更可控。

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/bootstrap-multilevel-navbar/

bootstrap3-navbar

前言

在上一篇文章中Angular结合Bootstrap3的导航菜单,我们看到直接用Bootstrap3的默认导航菜单只支持到二级的菜单,如果想做成多级导航菜单,那又要自己动手了。

本文将介绍如何实现多限级导航菜单。

目录

  1. 静态多级菜单实现
  2. 动态多级菜单实现

1. 静态多级菜单实现

要实现多级菜单,我们要分两步走,第一步就是把静态菜单的功能实现,通过纯静态的HTML代码完成。第二步,通过Angluarjs进行动态实现,最后把数据和程序分离,通过Ajax加载多级菜单数据。

我们先从静态多级菜单开始动手,一个六级导航菜单是什么样子呢?

m-nav1

如上图所示,我们定义一些功能需求。

  • 1级菜单是导航条上的文字。
  • 当1级菜单导航事件被触发,显示2级菜单导航,在1级菜单的正下方显示。
  • 当2级菜单导航事件被触发,显示3级菜单导航,在2级菜单的右方显示。
  • 当3级菜单导航事件被触发,显示4级菜单导航,在3级菜单的右方显示。
  • 以此类推,不考虑下级菜单显示出界问题。

继续上文中的项目环境,增加一个新HTML文件: page3.html


~ vi D:\workspace\javascript\angular-navbar\page3.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>多级导航菜单</title>
    <meta name="description" content="多级导航菜单">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="fragment" content="!" />

    <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/main.css">
</head>
<body ng-app="page2">

<div class="container">
    <div class="row" ng-controller="NavbarCtrl">
        <nav class="navbar navbar-default" role="navigation">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">多级菜单导航</a>
            </div>

            <div class="collapse navbar-collapse navbar-ex1-collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">Link</a></li>
                    <li><a href="#">Link</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">Multi Level <b class="caret"></b></a>
                        <ul class="dropdown-menu menu-top">
                            <li><a href="#">Level 1</a></li>
                            <li class="dropdown-submenu"> <a tabindex="-1" href="#">More options</a>
                                <ul class="dropdown-menu">
                                    <li><a tabindex="-1" href="#">Level 2</a>
                                    </li>
                                    <li class="dropdown-submenu"> <a href="#">More..</a>
                                        <ul class="dropdown-menu">
                                            <li><a href="#">Level 3</a>
                                            </li>
                                            <li><a href="#">Level 3</a>
                                            </li>
                                            <li class="dropdown-submenu"> <a href="#">More..</a>
                                                <ul class="dropdown-menu">
                                                    <li><a href="#">Level 4</a>
                                                    </li>
                                                    <li><a href="#">Level 4</a>
                                                    </li>
                                                    <li class="dropdown-submenu"> <a href="#">More..</a>
                                                        <ul class="dropdown-menu">
                                                            <li><a href="#">Level 5</a>
                                                            </li>
                                                            <li><a href="#">Level 5</a>
                                                            </li>
                                                        </ul>
                                                    </li>

                                                </ul>
                                            </li>

                                        </ul>
                                    </li>
                                    <li><a href="#">Level 2</a>
                                    </li>
                                    <li><a href="#">Level 2</a>
                                    </li>
                                </ul>
                            </li>
                            <li><a href="#">Level 1</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </nav>
    </div>
</div>

<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>
<script src="/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>

<script src="/js/app.js"></script>

</body>
</html>

新建一个css文件:main.css


~ vi D:\workspace\javascript\angular-navbar\css\main.css

.dropdown:hover .menu-top {
    display: block;
}

.dropdown-submenu{
    position:relative;
}

.dropdown-submenu > .dropdown-menu{
    top:0;
    left:100%;
    margin-top:-6px;
    margin-left:-1px;
    -webkit-border-radius:0 6px 6px 6px;
    -moz-border-radius:0 6px 6px 6px;
    border-radius:0 6px 6px 6px;
}

.dropdown-submenu:hover > .dropdown-menu{
    display:block;
}

.dropdown-submenu > a:after{
    display:block;
    content:" ";
    float:right;
    width:0;
    height:0;
    border-color:transparent;
    border-style:solid;
    border-width:5px 0 5px 5px;
    border-left-color:#cccccc;
    margin-top:5px;
    margin-right:-10px;
}

.dropdown-submenu:hover > a:after{
    border-left-color:#ffffff;
}

.dropdown-submenu .pull-left{
    float:none;
}

.dropdown-submenu.pull-left > .dropdown-menu{
    left:-100%;
    margin-left:10px;
    -webkit-border-radius:6px 0 6px 6px;
    -moz-border-radius:6px 0 6px 6px;
    border-radius:6px 0 6px 6px;
}

刷新一下网页,我们能就看到上面的截图的效果,代码参考:http://firdaus.grandexa.com/2013/09/twitter-bootstrap-3-multilevel-dropdown-menu/

通过HTML和CSS就实现了多级菜单的静态展示效果,如果导航菜单不是经常变化,那么用静态的方式,把代码写死就可以了。但有一些场景,菜单是需要动态生成,比如通过权限控制访问链接,每个用户的权限不一样,那么能看到的菜单选项也就不一样,这个时候就需要做成动态的,用程序去控制菜单的加载和展示。

2. 动态多级菜单实现

有了静态多级导航菜单的HTML代码结构,改写成动态的,其实也不太复杂。

我们需要做2件事:

  • 把导航菜单的数据结构化存储,比如 存放到文件 nav.json。
  • 用Angularjs的API加载nav.json数据,进行展示。

我们先定义一下导航菜单的数据格式,以JSON格式定义,每个菜单项都有3个属性字段

  • label: 导航菜单项显示的名字。
  • link: 导航菜单项的跳转链接,可以不定义。
  • children: 导航菜单项的子菜单,循环对象存储。

{
    "label": "levelA",
    "link": "#",
    "children": [
        {
            "label": "levelB",
            "link": "#",
            "children": []
        }
    ]
}

下面我们用真实的数据定义导航菜单,以我的金融系统为例。

m-nav3

新建JSON数据文件:nav.json。


~ vi D:\workspace\javascript\angular-navbar\js\nav.json

[
    {
        "label": "债券",
        "children": [
            {
                "label": "可转债",
                "children": [
                    {"label": "可转债溢价率分析","link":"#"},
                    {"label": "可转债NS定价","link":"#"},
                    {"label": "可转债归因分析","link":"#"},
                    {"label": "可转债套利实时监控","link":"#"}
                ]
            },
            {
                "label": "信用债",
                "children": [
                    {"label": "交易所债券监控","link":"#"}
                ]
            },
            {
                "label": "利率债","link":"#",
                "children": []
            },
            {
                "label": "国债期货",
                "children": [
                    {"label": "国债期货表现分析","link":"#"},
                    {"label": "国债期货实时套利监控","link":"#"},
                    {"label": "IRR历史时间序列查询","link":"#"},
                    {"label": "IRR实时监控","link":"#"}
                ]
            }
        ]
    },
    {
        "label": "股票",
        "children": [
            {
                "label": "基本面分析",
                "children": [
                    {"label": "上市公司基本面数据查看","link":"#"}
                ]
            },
            {
                "label": "量化选股策略",
                "children": []
            }
        ]
    },
    {
        "label": "宏观",
        "children": [
            {
                "label": "宏观数据",
                "children": [
                    {"label": "宏观数据概览","link":"#"}
                ]
            },
            {
                "label": "宏观经济预测",
                "children": []
            },
            {
                "label": "宏观经济和大类资产表现",
                "children": []
            }
        ]
    }
]

我们看到这个导航菜单的数据,有3级,“债券–>可转债–>可转债归因分析”,那么接下我们就直接实现对三级菜单的编程。创建HTML文件page4.html。


~ vi D:\workspace\javascript\angular-navbar\page4.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>多级动态导航菜单</title>
    <meta name="description" content="多级动态导航菜单">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="fragment" content="!" />

    <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/main.css">
</head>
<body ng-app="page4">

<div class="container">
    <div class="row" ng-controller="NavbarCtrl">
        <nav class="navbar navbar-default" role="navigation">
            <div class="navbar-header">
                <a class="navbar-brand" href="/">量化投资平台</a>
            </div>

            <div class="collapse navbar-collapse navbar-ex1-collapse">
                <ul class="nav navbar-nav">
                    <li ng-repeat="a1 in navbar" class="dropdown">
                        <a href="?{{ a1.label }}" class="dropdown-toggle" data-toggle="dropdown">{{ a1.label }} <b class="caret"></b></a>
                        <ul class="dropdown-menu menu-top">
                            <li ng-repeat="a2 in a1.children" class="dropdown-submenu">
                                <a tabindex="-1" href="?{{ a2.label }}">{{ a2.label }}</a>
                                <ul ng-show="a2.children.length>0" class="dropdown-menu">
                                    <li ng-repeat="a3 in a2.children">
                                        <a href="?{{ a3.label }}" ng-click="go(a3.link)">{{ a3.label }}</a>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </li>
                </ul>
            </div>
        </nav>
    </div>
</div>

<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>
<script src="/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>

<script src="/js/app.js"></script>

</body>
</html>

在Angularjs的控制器文件app.js文件中,增加page4的定义。


var page4 = angular.module('page4', ['ui.bootstrap', 'ngRoute']);

page4.config(['$routeProvider', '$locationProvider', '$sceProvider', function ($routeProvider, $locationProvider, $sceProvider) {
    $routeProvider
        .when('/', {controller: 'DemoCtrl'})
        .otherwise({redirectTo: '/'});
    $locationProvider.html5Mode(true);
}]);

page4.controller('NavbarCtrl', function ($scope,$http,$location) {
    $http.get("/js/nav.json").success(function(json){
        $scope.navbar = json;
    });
});

page4.controller('DemoCtrl', function () {
    // nothing
});

查看一下显示效果,与上面的截图类似。

m-nav4

文章到这里就结束了,已经实现了我的功能需求。但这个话题还有很多可以优化的地方,比如实现无限级的导航菜单,菜单的展示样式替换,展示区间的控制,鼠标动作事件,封装成Angularjs的插件开源项目等。有兴趣的同学,可以我的程序的基础上继续努力,做出优秀的开源项目来。

代码已上传到github:https://github.com/bsspirit/angular-navbar,同学可以根据需要自行下载,也可以直接通过命令下载代码。


git clone https://github.com/bsspirit/angular-navbar.git
cd angular-navbar
bower install
anywhere

转载请注明出处:
http://blog.fens.me/bootstrap-multilevel-navbar/

打赏作者