2016年11月3日星期四

阮一峰的网络日志

阮一峰的网络日志


IntersectionObserver API 使用教程

Posted: 02 Nov 2016 04:53 PM PDT

网页开发时,常常需要了解某个元素是否进入了"视口"(viewport),即用户能不能看到它。

上图的绿色方块不断滚动,顶部会提示它的可见性。

传统的实现方法是,监听到scroll事件后,调用目标元素(绿色方块)的getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件密集发生,计算量很大,容易造成性能问题

目前有一个新的 IntersectionObserver API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

一、API

它的用法非常简单。

 var io = new IntersectionObserver(callback, option); 

上面代码中,IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。

构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点。

 // 开始观察 io.observe(document.getElementById('example'));  // 停止观察 io.unobserve(element);  // 关闭观察器 io.disconnect(); 

上面代码中,observe的参数是一个 DOM 节点对象。如果要观察多个节点,就要多次调用这个方法。

 io.observe(elementA); io.observe(elementB); 

二、callback 参数

目标元素的可见性变化时,就会调用观察器的回调函数callback

callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。

 var io = new IntersectionObserver(   entries => {     console.log(entries);   } ); 

上面代码中,回调函数采用的是箭头函数的写法。callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。

三、IntersectionObserverEntry 对象

IntersectionObserverEntry对象提供目标元素的信息,一共有六个属性。

 {   time: 3893.92,   rootBounds: ClientRect {     bottom: 920,     height: 1024,     left: 0,     right: 1024,     top: 0,     width: 920   },   boundingClientRect: ClientRect {      // ...   },   intersectionRect: ClientRect {     // ...   },   intersectionRatio: 0.54,   target: element } 

每个属性的含义如下。

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

上图中,灰色的水平方框代表视口,深红色的区域代表四个被观察的目标元素。它们各自的intersectionRatio图中都已经注明。

我写了一个 Demo,演示IntersectionObserverEntry对象。注意,这个 Demo 只能在 Chrome 51+ 运行。

四、实例:惰性加载(lazy load)

有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载"。

有了 IntersectionObserver API,实现起来就很容易了。

 function query(selector) {   return Array.from(document.querySelectorAll(selector)); }  var observer = new IntersectionObserver(   function(changes) {     changes.forEach(function(change) {       var container = change.target;       var content = container.querySelector('template').content;       container.appendChild(content);       observer.unobserve(container);     });   } );  query('.lazy-loaded').forEach(function (item) {   observer.observe(item); }); 

上面代码中,只有目标区域可见时,才会将模板内容插入真实 DOM,从而引发静态资源的加载。

五、实例:无限滚动

无限滚动(infinite scroll)的实现也很简单。

 var intersectionObserver = new IntersectionObserver(   function (entries) {     // 如果不可见,就返回     if (entries[0].intersectionRatio <= 0) return;     loadItems(10);     console.log('Loaded new items');   });  // 开始观察 intersectionObserver.observe(   document.querySelector('.scrollerFooter') ); 

无限滚动时,最好在页面底部有一个页尾栏(又称sentinels)。一旦页尾栏可见,就表示用户到达了页面底部,从而加载新的条目放在页尾栏前面。这样做的好处是,不需要再一次调用observe()方法,现有的IntersectionObserver可以保持使用。

六、Option 对象

IntersectionObserver构造函数的第二个参数是一个配置对象。它可以设置以下属性。

6.1 threshold 属性

threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。

 new IntersectionObserver(   entries => {/* ... */},    {     threshold: [0, 0.25, 0.5, 0.75, 1]   } ); 

用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。

6.2 root 属性,rootMargin 属性

很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在iframe窗口里滚动)。容器内滚动也会影响目标元素的可见性,参见本文开始时的那张示意图。

IntersectionObserver API 支持容器内滚动。root属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

 var opts = {    root: document.querySelector('.container'),   rootMargin: "500px 0px"  };  var observer = new IntersectionObserver(   callback,   opts ); 

上面代码中,除了root属性,还有rootMargin属性。后者定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小。它使用CSS的定义方法,比如10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

七、注意点

IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

规格写明,IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。

八、参考链接

(完)

文档信息

2016年10月18日星期二

阮一峰的网络日志

阮一峰的网络日志


中文技术文档的写作规范

Posted: 17 Oct 2016 05:19 PM PDT

很多人说,不知道怎么写文档,都是凭着感觉写。

网上也很少有资料,教你写文档。这已经影响了中文软件的发展。

英语世界里,文档非常受重视,许多公司和组织都有自己的文档规范,清楚地规定写作要求,比如微软MailChimpAppleYahoodockerStruts 等等(维基百科有一份完整的清单)。中文的也有不少,但都不令人满意,要么太简单,要么不太适用。

我就动手,参考上面的规范,也结合自己的实践,总结了一份简单的《中文技术文档的写作规范》

  1. 标题
  2. 文本
  3. 段落
  4. 数值
  5. 标点符号
  6. 章节结构

我希望,这样可以抛砖引玉,让更多人重视文档,进而真正出现大家普遍接受的文档规范。

下面是关于写作风格的一个片段。欢迎提交 IssuePR 补充。

=================================

写作风格

(摘自《中文技术文档的写作规范》

如果使用了被动语态,应考虑更改为主动语态。

 错误:假如此软件尚未被安装,  正确:假如尚未安装这个软件, 

不使用非正式的语言风格。

 错误:Lady Gaga 的演唱会真是酷毙了,从没看过这么给力的表演!!!  正确:无法参加本次活动,我深感遗憾。 

用对"的"、"地"、"得"。

 她露出了开心的笑容。 (形容词+的+名词)  她开心地笑了。 (副词+地+动词)  她笑得很开心。 (动词+得+副词) 

使用代词时(比如"其"、"该"、"此"、"这"等词),必须明确指代的内容,保证只有一个含义。

 错误:从管理系统可以监视中继系统和受其直接控制的分配系统。  正确:从管理系统可以监视两个系统:中继系统和受中继系统直接控制的分配系统。 

名词前不要使用过多的形式词。

 错误:此设备的使用必须在接受过本公司举办的正式的设备培训的技师的指导下进行。  正确:此设备必须在技师的指导下使用,且指导技师必须接受过由本公司举办的正式设备培训。 

句子的长度尽量保持在20个字以内;20~29个字的句子,可以接受;39~39个字的句子,语义必须明确,才能接受;多于40个字的句子,在任何情况下都不能接受。

 错误:本产品适用于从由一台服务器进行动作控制的单一节点结构到由多台服务器进行动作控制的并行处理程序结构等多种体系结构。  正确:本产品适用于多种体系结构。无论是由一台服务器(单一节点结构),还是由多台服务器(并行处理结构)进行动作控制,均可以使用本产品。 

同样一个意思,尽量使用肯定句表达,不使用否定句表达。

 错误:请确认没有接通装置的电源。  正确:请确认装置的电源已关闭。 

避免使用双重否定句。

 错误:没有删除权限的用户,不能删除此文件。  正确:用户必须拥有删除权限,才能删除此文件。 

(正文完)

====================================

下面是推广时间。不过我想先说一些题外话。

如果你经常来这里,可能会注意到,有的文章结尾有市场推广信息。上一篇文章就是这样,我的《财新周刊》专栏写到了 Udacity,他们正好在推广纳米学位,就在那篇文章后面做了一个广告。有的朋友因此指责我写"软文",这不是事实。

事实是,我的博客上没有一篇是"软文",尽管一直有人来问价格。所有的文章都是真实想法,都是我真心想分享的东西。每一个来访问的读者,我都当作是朋友,不会把一篇广告伪装成正常的文章,去欺骗朋友。这一直是我做人的信条,不会为了一点点钱,就把这么多年的坚持都抛弃了。

推广活动都放在文章的结尾,明确注明是推广,并且我只接受那些我认可的产品。对我来说,这点收入可以补贴服务器支出;对许多读者来说,有些信息可能非常有用,比如下面这条。

====================================

今天推广的主角是"海棠学院",一个前端开发的在线教育平台。

创始人小河就是培训班出身,通过自身努力进入百度,深知自学者的苦恼:企业不愿意要培训班出来的学生,而学生不知道应该选哪一家培训机构。他创业的时候,立志要做一家靠谱的、有信誉、有口碑的在线教育公司。

"海棠学院"的很多讲师都有百度背景,开发过用户上亿的产品。为了做出最容易学会的课程,他们反复尝试,不惜放弃已经录好的500多个课时,推倒重来。功夫不负有心人,"海棠学院"现在已经取得了很好的成绩。

  • 两门免费课在腾讯课题排名第一第三,已经稳定了两个月。
  • 网易云课堂百度传课等平台也是名列前茅。
  • 区别于别家,他们免费课的评价与报名人数真真实实,没有水分。

更难得的是,"海棠学院"是工程师的创业项目,甚至都没有市场销售人员,完全靠用户的口碑来推广。如果课程质量不好,他们马上就完蛋了。

现在,为了发展更多的用户,也是因为对课程很有信心,他们搞了一个活动,抛弃 "先付费、再学习" 的模式,让用户零成本体验他们提供高质量培训。

  1. 只需要缴纳 99 元,即可感受海棠学院一周的课程,与正式学员享受一模一样的待遇(录播+直播+教学监管+技术辅导)。
  2. 一周后,如果觉得不满意,99 元可以退款。
  3. 如果想继续学下去,已经缴纳的 99 元可以抵扣学费,并且学费还可以再优惠 900 元。也就是说,参加这个活动比起直接报名,可以一共节省 999 元的学费。
  4. 我的读者还可以使用独家优惠码"ruanyifeng",学费再抵扣 300 元。

整个课程一共需要 4.5 个月左右,涉及前端开发的各个方面,目标是通过一次系统的培训,你就能从事前端开发这项工作。遇到不懂的地方,可以重学,他们保证让你学会。

想从事前端开发,却不知道从何学起的朋友,不要错过这个机会。只需99元,就可以感受一下专业级、全方位的培训服务,如果不满意,99元还可以退款。

这个活动10月31日截止,因为当天就开课了,后面就恢复原价了。现在 就点击这里了解详情吧。

(完)

文档信息

2016年10月12日星期三

阮一峰的网络日志

阮一峰的网络日志


网络文凭,你要不要

Posted: 11 Oct 2016 04:56 PM PDT

(说明:本文原载2016年第35期《财新周刊》。)

1.

我一直相信,互联网教育是未来的方向。美国三个主要的在线教育网站----UdacityCoursera可汗学院----我都经常访问。

今年四月,Udacity 进入中国,推出了中文版"优达学城",一下子引起了我的兴趣。因为它干了一件没有先例的事情:颁发网络文凭。它办了一个网上的"硅谷大学",自己发文凭,名称是"纳米学位"

"纳米学位(Nanodegree)是优达学城此前与 Google、Facebook、亚马逊等互联网公司联合推出的学历认证项目。学员在线学习,所有项目考核合格之后即可获得纳米学位。"

现在总共有12种纳米学位,包括机器学习、无人驾驶车开发、VR 开发这样非常前沿的领域。

官网这样介绍

"我们没有严格意义上的录取流程,对报名者唯一的要求是学习该纳米学位项目所必须的先修知识和技能。纳米学位项目采取自主学习模式,你可以按照你喜欢的速度完成项目。12个月内完成纳米学习,可以得到50%学费返回。"

该公司宣称,国内的许多互联网公司(比如滴滴出行、优酷土豆、京东、新浪)已经认可了纳米学位。我忍不住想,会不会以后找工作,大家手里拿的不是大学文凭,而是网站颁发的文凭?如果雇主认可网络文凭,我们是否还需要大学文凭?

下面是我的一些思考。另外,我与优达学城的工程师有过一些个人交流,他们最近正好在搞活动,我想做一下推广,文章的结尾处有详细信息。

2.

当代的大学起源于欧洲修道院的模式。学生要经过多年的苦修,经过考核,才能毕业。如果想成为高级僧侣,就必须再多熬几年。另外,还有导师作为监督人,防止你学到歪门邪说。

这种模式的两大弊端,演变到今天,已经越来越严重了:一个是传授的知识老化,另一个是极其浪费学生的时间。

3.

什么知识才是有用的知识?

农业社会,上一代人的知识可以一成不变地用在下一代。而在信息社会,前几年的知识,再过几年就不能用了。

举例来说,眼下就业前景最好的行业,我觉得有两个:区块链和VR。它们在五年前都是不存在的,那时就业最好的是苹果iOS系统的应用开发,可是再往前推五年,它也是不存在的。伴随着它们的是,很多旧工作岗位的消失,比如塞班、黑莓、Windows Phone的开发。

这种情况下,大学应该教什么,我们根本不知道。学生毕业后的行业,现在根本还没有出现。因此,大学只能重点教基础类课程,但这样就会学到大量没用的东西。学生感叹,考试一结束,许多课程这辈子再没有用到的机会了。

更糟糕的是,学生的培养计划,都是一些二三十年前毕业、然后一直待在大学里、与社会生产实践脱节的人制定的。他们的知识和思维早已过时了。这样的人指定你应该学习的知识,很可能在你学的时候就已经没用了。

4.

退一步说,就算你在大学里能学到真正的知识,也不应该在那里待四年。如果只学最需要学习的东西,一年就够了。

四年时间足以让一个人在任何领域成为资深业者,甚至专家。可是我们的大学生呢,经过本科四年,不要说领域专家,甚至能力强的学生都寥寥无几。我们的大学制度用了四年时间,培养出了大量一无所长的、迷茫困惑的、市场滞销的年轻人。

18岁是人生最有热情和精力投入一项事业的时候,但是,大学将你一连四年关在教室和图书馆里,把考试和绩点伪装成你奋斗的目标,人为将你与真实世界隔离,引导你去关注那些对未来人生毫不重要的事情。经过这样四年的歧途,等你真正走上社会、要跟全世界竞争的时候,你的竞争力不是变强了,而是变弱了。换句话说,四年制大学很可能是削弱你,而不是让你变得更强。

强烈推荐阅读,2014年诺贝尔物理学奖得主中村修二的观点《东亚教育浪费了太多的生命》

世界著名程序员Jamie Zawinski曾经解释,为什么他只读了一个学期的大学就退学。

"进了大学以后,每天8点就要起床,开始训练记忆力。有一门课我早就会了,想申请免修。教务长说不行,你必须上,这是政策。见鬼,我为什么要自己付钱,来这种地方。我就退学了,从来没后悔过。"

我们时代的很多成功者----乔布斯、比尔盖茨、扎克伯格等等----都是退学生,这绝不是偶然的。不是他们在大学待不下去,而是他们发现,没必要在那个地方待四年。如果他们咬着牙忍受下去,熬到拿到文凭的那一天,苹果公司和微软公司可能都不会有了。

读大学,只是18岁时很多种选择中的一种,不是唯一的选择,更谈不上是最好的选择。校园是一个美丽的地方,但是如果一定要在里面待上四年,那还是算了吧。

5.

以前,人生的选择很少,你不得不去读大学,因为没有其他地方可以接受高等教育。社会还把很多机会与文凭挂钩,先有文凭,然后才能有就业、职称、住房等等。

但是,时代已经变了,文凭正变得越来越不重要。那些与文凭挂钩的东西,正在一项项脱钩。

互联网将教育的自主权,交到了每个人自己手里。上什么课程、什么时间上,都完全由你决定。你可以一边工作,一边利用夜晚和周末,学习网络课程。这样的话,不仅早早就会有收入,而且只学那些对自己最有用、最感兴趣的内容,学习的效率很高。如果发现对学术有兴趣,将来再回大学,攻读更高的学位,也是完全可以的。

等到22岁,别人刚刚开始找工作,还在为归还学贷发愁,你已经有了四年工作经验和一些积蓄,认清了自己的人生道路,开始向事业的高峰冲刺了。

(正文完)


下面是推广时间。

首先,感谢优达学城为我的读者准备了优惠码。你可以先去看一下他们的课程目录,那么多的来自硅谷的优质学习资源,等待你去探索。

大部分是免费课程,不花一分钱,就能学到最新的技术。收费课程则会对学习者提供纳米学位和非常细致的辅导,可以大大加速学习。这些课程都由一段段小视频组成,随时随地花上五分钟,就可以开始学习。

我个人推荐人工智能机器学习课程,因为这是他们的强项,最受好评。所有收费课程可以免费体验7天,如果觉得喜欢,缴费的时候用"ruanyf "这个优惠码,就可以优惠300元。

另外,优达学城正在举办"程序员感谢月",整个十月份都有活动,目的是让更多的人去关注程序员、工程师对社会的贡献。只要你通过一个小 Quiz,证明自己真的是程序员,就有机会拿到各种福利,比如上门按摩券、亚马逊AWS代金券、GrowingIO代金券、与大公司CTO共进午餐的机会等等,详情看这里

还犹豫什么,现在就开始选课吧!

微信号:youdaxue

(完)

文档信息

2016年10月11日星期二

阮一峰的网络日志

阮一峰的网络日志


npm scripts 使用指南

Posted: 11 Oct 2016 04:03 AM PDT

Node 开发离不开 npm,而脚本功能是 npm 最强大、最常用的功能之一。

本文介绍如何使用 npm 脚本(npm scripts)。

一、什么是 npm 脚本?

npm 允许在package.json文件里面,使用scripts字段定义脚本命令。

  {    // ...    "scripts": {      "build": "node build.js"    }  }  

上面代码是package.json文件的一个片段,里面的scripts字段是一个对象。它的每一个属性,对应一段脚本。比如,build命令对应的脚本是node build.js

命令行下使用npm run命令,就可以执行这段脚本。

  $ npm run build  # 等同于执行  $ node build.js  

这些定义在package.json里面的脚本,就称为 npm 脚本。它的优点很多。

  • 项目的相关脚本,可以集中在一个地方。
  • 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行npm run test即可。
  • 可以利用 npm 提供的很多辅助功能。

查看当前项目的所有 npm 脚本命令,可以使用不带任何参数的npm run命令。

  $ npm run  

二、原理

npm 脚本的原理非常简单。每当执行npm run,就会自动新建一个 Shell,在这个 Shell 里面执行指定的脚本命令。因此,只要是 Shell(一般是 Bash)可以运行的命令,就可以写在 npm 脚本里面。

比较特别的是,npm run新建的这个 Shell,会将当前目录的node_modules/.bin子目录加入PATH变量,执行结束后,再将PATH变量恢复原样。

这意味着,当前目录的node_modules/.bin子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。比如,当前项目的依赖里面有 Mocha,只要直接写mocha test就可以了。

  "test": "mocha test"  

而不用写成下面这样。

  "test": "./node_modules/.bin/mocha test"  

由于 npm 脚本的唯一要求就是可以在 Shell 执行,因此它不一定是 Node 脚本,任何可执行文件都可以写在里面。

npm 脚本的退出码,也遵守 Shell 脚本规则。如果退出码不是0,npm 就认为这个脚本执行失败。

三、通配符

由于 npm 脚本就是 Shell 脚本,因为可以使用 Shell 通配符。

  "lint": "jshint *.js"  "lint": "jshint **/*.js"  

上面代码中,*表示任意文件名,**表示任意一层子目录。

如果要将通配符传入原始命令,防止被 Shell 转义,要将星号转义。

  "test": "tap test/\*.js"  

四、传参

向 npm 脚本传入参数,要使用--标明。

  "lint": "jshint **.js"  

向上面的npm run lint命令传入参数,必须写成下面这样。

  $ npm run lint --  --reporter checkstyle > checkstyle.xml  

也可以在package.json里面再封装一个命令。

  "lint": "jshint **.js",  "lint:checkstyle": "npm run lint -- --reporter checkstyle > checkstyle.xml"  

五、执行顺序

如果 npm 脚本里面需要执行多个任务,那么需要明确它们的执行顺序。

如果是并行执行(即同时的平行执行),可以使用&符号。

  $ npm run script1.js & npm run script2.js  

如果是继发执行(即只有前一个任务成功,才执行下一个任务),可以使用&&符号。

  $ npm run script1.js && npm run script2.js  

这两个符号是 Bash 的功能。此外,还可以使用 node 的任务管理模块:script-runnernpm-run-allredrun

六、默认值

一般来说,npm 脚本由用户提供。但是,npm 对两个脚本提供了默认值。也就是说,这两个脚本不用定义,就可以直接使用。

  "start": "node server.js",  "install": "node-gyp rebuild"  

上面代码中,npm run start的默认值是node server.js,前提是项目根目录下有server.js这个脚本;npm run install的默认值是node-gyp rebuild,前提是项目根目录下有binding.gyp文件。

七、钩子

npm 脚本有prepost两个钩子。举例来说,build脚本命令的钩子就是prebuildpostbuild

  "prebuild": "echo I run before the build script",  "build": "cross-env NODE_ENV=production webpack",  "postbuild": "echo I run after the build script"  

用户执行npm run build的时候,会自动按照下面的顺序执行。

  npm run prebuild && npm run build && npm run postbuild  

因此,可以在这两个钩子里面,完成一些准备工作和清理工作。下面是一个例子。

  "clean": "rimraf ./dist && mkdir dist",  "prebuild": "npm run clean",  "build": "cross-env NODE_ENV=production webpack"  

npm 默认提供下面这些钩子。

  • prepublish,postpublish
  • preinstall,postinstall
  • preuninstall,postuninstall
  • preversion,postversion
  • pretest,posttest
  • prestop,poststop
  • prestart,poststart
  • prerestart,postrestart

自定义的脚本命令也可以加上prepost钩子。比如,myscript这个脚本命令,也有premyscriptpostmyscript钩子。不过,双重的prepost无效,比如prepretestpostposttest是无效的。

npm 提供一个npm_lifecycle_event变量,返回当前正在运行的脚本名称,比如pretesttestposttest等等。所以,可以利用这个变量,在同一个脚本文件里面,为不同的npm scripts命令编写代码。请看下面的例子。

  const TARGET = process.env.npm_lifecycle_event;    if (TARGET === 'test') {    console.log(`Running the test task!`);  }    if (TARGET === 'pretest') {    console.log(`Running the pretest task!`);  }    if (TARGET === 'posttest') {    console.log(`Running the posttest task!`);  }  

八、简写形式

四个常用的 npm 脚本有简写形式。

  • npm startnpm run start
  • npm stopnpm run stop的简写
  • npm testnpm run test的简写
  • npm restartnpm run stop && npm run restart && npm run start的简写

npm startnpm stopnpm restart都比较好理解,而npm restart是一个复合命令,实际上会执行三个脚本命令:stoprestartstart。具体的执行顺序如下。

  1. prerestart
  2. prestop
  3. stop
  4. poststop
  5. restart
  6. prestart
  7. start
  8. poststart
  9. postrestart

九、变量

npm 脚本有一个非常强大的功能,就是可以使用 npm 的内部变量。

首先,通过npm_package_前缀,npm 脚本可以拿到package.json里面的字段。比如,下面是一个package.json

  {    "name": "foo",     "version": "1.2.5",    "scripts": {      "view": "node view.js"    }  }  

那么,变量npm_package_name返回foo,变量npm_package_version返回1.2.5

  // view.js  console.log(process.env.npm_package_name); // foo  console.log(process.env.npm_package_version); // 1.2.5  

上面代码中,我们通过环境变量process.env对象,拿到package.json的字段值。如果是 Bash 脚本,可以用$npm_package_name$npm_package_version取到这两个值。

npm_package_前缀也支持嵌套的package.json字段。

    "repository": {      "type": "git",      "url": "xxx"    },    scripts: {      "view": "echo $npm_package_repository_type"    }  

上面代码中,repository字段的type属性,可以通过npm_package_repository_type取到。

下面是另外一个例子。

  "scripts": {    "install": "foo.js"  }  

上面代码中,npm_package_scripts_install变量的值等于foo.js

然后,npm 脚本还可以通过npm_config_前缀,拿到 npm 的配置变量,即npm config get xxx命令返回的值。比如,当前模块的发行标签,可以通过npm_config_tag取到。

  "view": "echo $npm_config_tag",  

注意,package.json里面的config对象,可以被环境变量覆盖。

  {     "name" : "foo",    "config" : { "port" : "8080" },    "scripts" : { "start" : "node server.js" }  }  

上面代码中,npm_package_config_port变量返回的是8080。这个值可以用下面的方法覆盖。

  $ npm config set foo:port 80  

最后,env命令可以列出所有环境变量。

  "env": "env"  

十、常用脚本示例

  // 删除目录  "clean": "rimraf dist/*",    // 本地搭建一个 HTTP 服务  "serve": "http-server -p 9090 dist/",    // 打开浏览器  "open:dev": "opener http://localhost:9090",    // 实时刷新   "livereload": "live-reload --port 9091 dist/",    // 构建 HTML 文件  "build:html": "jade index.jade > dist/index.html",    // 只要 CSS 文件有变动,就重新执行构建  "watch:css": "watch 'npm run build:css' assets/styles/",    // 只要 HTML 文件有变动,就重新执行构建  "watch:html": "watch 'npm run build:html' assets/html",    // 部署到 Amazon S3  "deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",    // 构建 favicon  "build:favicon": "node scripts/favicon.js",  

十一、参考链接

(完)

文档信息

2016年9月23日星期五

阮一峰的网络日志

阮一峰的网络日志


React 技术栈系列教程

Posted: 22 Sep 2016 04:05 PM PDT

上周中秋节,我待在家里,写完了 Redux 教程。

至此,《React 技术栈系列教程》算是比较完整了。

它们都针对初学者,尽量通俗易懂,帮大家节省一些看文档的时间,让你快速上手。其中,React 教程在 Github 已经得到 6000 颗星,Webpack 教程也有 2000 颗星了。

这两年没停过,一直在学习新东西,学习心得就写成了上面的教程。虽然看上去数量不少,但是下一代的互联网开发技术,我还是只学了很小一部分,像 PostCSS、GraphQL、Electron 这些感兴趣的东西,都没时间搞。

面对技术的高速发展和百花齐放,我有时也感到疲倦烦躁。但是,每当看到它们带来的生产力的飞跃,让你一个人快速搞定前后端的全部开发时,就觉得这终究还是一条正确的道路。

(完)

文档信息