Giter Site home page Giter Site logo

kiinlam.github.io's People

Contributors

kiinlam avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

kiinlam.github.io's Issues

nodejs请求转发

写于 2015-06-19

问题

客户端(浏览器)通过ajax请求第三方服务器,会遇到跨域问题,可通过nodejs服务器做中介来解决跨域问题。

解决方案

客户端发起请求到nodejs服务器,nodejs收到后请求第三方服务器取得数据,返回给客户端。

client ajax --> nodejs recived --> nodejs send request --> respone to client

范例

不使用外部package的代码

var http = require('http');
// 创建http服务
var app = http.createServer(function (req, res) {
    // 查询本机ip
    var sreq = http.request({
        host:     'sneezryworks.sinaapp.com', // 目标主机
        path:     '/ip.php', // 目标路径
        method:   req.method // 请求方式
    }, function(sres){
        sres.pipe(res);
        sres.on('end', function(){
            console.log('done');
        });
    });
    if (/POST|PUT/i.test(req.method)) {
        req.pipe(sreq);
    } else {
        sreq.end();
    }
});
// 访问127.0.0.1:3001查看效果
app.listen(3001);
console.log('server started on 127.0.0.1:3001');

使用superAgent的代码

var http = require('http');
// 创建http服务
var app = http.createServer(function (req, res) {
    // 使用了superagent来发起请求
    var superagent = require('superagent');
    // 查询本机ip,这里需要根据实际情况选择get还是post
    var sreq = superagent.get('http://sneezryworks.sinaapp.com/ip.php');
    sreq.pipe(res);
    sreq.on('end', function(){
        console.log('done');
    });
});
// 访问127.0.0.1:3001查看效果
app.listen(3001);
console.log('server started on 127.0.0.1:3001');

使用Express + superAgent的代码

var express = require('express');
var app = express();
app.get('/', function (req, res) {
    // 使用了superagent来发起请求
    var superagent = require('superagent');
    // 查询本机ip,这里需要根据实际情况选择get还是post
    var sreq = superagent.get('http://sneezryworks.sinaapp.com/ip.php');
    sreq.pipe(res);
    sreq.on('end', function(){
        console.log('done');
    });
});
app.listen(3001);
console.log('Express started on 127.0.0.1:3001');

后记

如果需要对第三方服务器返回的内容做处理,可以在sreqdata事件的回调方法中进行,而不是直接使用pipe方式。


参考资料

react及flux架构范例Todomvc分析

写于2015-05-29

通过分析flux-todomvc源码,学习如何通过react构建web程序,了解编写react应用程序的一般步骤,同时掌握Flux的单向数据流动架构**

关于react

react一个最吸引我的特性是组件,它是模块化的,所有的组件是独立的,又可以通过嵌套来构建更大型的组件,一个个小组件经过层层组装,最终形成web应用程序,它让我开始重新思考如何去构建大型的web应用程序。

关于Flux

Flux是一个**而非框架,强调数据自上而下传递的单向流动理念,已经有很多种实现,比较有名气的是RefluxJs。单向流动让数据走向更清晰:用户UI交互触发事件,事件回调执行Action,并将所有的变化数据作为参数payload传递进去,各种Stores从payload中取出自己所需的数据进行更新,最终重新渲染发生变化的DOM结点。

分析

以下简要分析 react + flux 实现的todomvc源码,主要研究其构建方式与单向数据流动**,对具体代码编写不做深入分析。

TodoApp组件

  • getInitialStates中设置初始值;
  • componentDidMount中添加store监听指定事件_onChange
  • componentWillUnmount中清除store监听的事件及回调;
  • render中将state赋值给各个component的prop,子组件包含Header组件、MainSection组件、Footer组件;
  • 添加_onChange事件回调方法,触发该回调时,执行setState,更新UI。

Store只在顶级的组件中接入,子组件通过prop层层传递,所有子组件不直接从Store里检索数据,这是一种很好的设计方式,有助降低数据耦合度。

Header组件

  • render中包含TodoTextInput组件,并将自身的_onSave方法作为TodoTextInput组件的prop;
  • 添加_onSave方法,在调用时,执行相应Action方法create

Header组件统一管理_onSave方法,该方法将提供给子组件调用。

MainSection组件

  • 基础的prop验证

    • 要求必须提供对象集合
    • 要求提供是否全部完成的布尔值;
  • render中分类处理,数据为空时,返回null,隐藏元素,有数据时,显示列表内容,包含TodoItem组件,同时提供一个checkbox控件用于设置是否全部完成;

  • 添加切换设置全部完成的回调方法_onToggleCompleteAll,在调用时,执行相应Action方法toggleCompleteAll

Footer组件

  • 基础的prop验证

    • 要求必须提供对象集合;
  • render中计算未完成项目数量,并根据是否存在已完成项来显示清除全部完成项按钮,按钮绑定onClick事件;

  • 添加清除全部完成项的回调方法_onClearCompletedClick,在调用时,执行相应Action方法destroyCompleted

TodoItem组件

  • 基础的prop验证

    • 要求项目不可空;
  • getInitialStates中设置初始值;

  • render中根据是否处于编辑状态,显示不同的元素,编辑状态下,包含了TodoTextInput组件,并设置了onSavevalue的prop,非编辑状态下,显示切换是否完成的按钮,绑定了onChange事件,显示项目文字内容,绑定了onDoubleClick事件,显示删除按钮,绑定了onClick事件;

  • 添加切换是否完成的回调方法_onToggleComplete,在调用时,执行相应Action方法toggleComplete

  • 添加双击文字时的回调方法_onDoubleClick,在调用时,通过setState设置当前项目为编辑状态;

  • 添加触发保存时的回调方法_onSave,在调用时,执行相应Action方法updateText,并通过设置setState退出编辑状态;

  • 添加删除时的回调方法_onDestroyClick,在调用时,执行相应Action方法destroy

TodoTextInput组件

  • 基础的prop验证

    • 要求classNameidplaceholdervalue为字符串;
    • 存在onSave方法
  • getInitialStates中设置初始值;

  • render中返回输入文本框,绑定多个事件onBluronChangeonKeyDown

  • 添加失去焦点时的回调方法_save,在调用时,执行父级元素提供的onSave方法,并调用setState清空文字;

  • 添加内容变化时的回调方法_onChange,在调用时,调用setState更新虚拟DOM并刷新DOM元素;

  • 添加按下键盘时的回调方法onKeyDown,在调用时,检测是否按下回车键以执行onSave方法;

TodoTextInput组件仅依赖于父级通过prop传递下来的数据与方法,不直接与ActionStore进行操作

TodoActions

  • 对外提供完成具体功能的action方法,内部会调用事件分发器AppDispatcher发出一个具体的action(携带数据payload),并设置payloadactionType为action对应的类型。

AppDispatcher

  • 注册一个集中的Action管理回调方法
  • 分发actions(在TodoActions中的具体方法中触发)。

AppDispatcher既分发actions,又是actions的接收者,在接到actions后负责调用先前注册的Action管理回调方法。

Action管理回调方法中根据actionType来完成不同的操作,并在操作结束时触发TodoStore绑定的事件。

TodoStore

  • 提供对所有数据进行增删改查的方法
  • 注册事件回调函数,在数据发生改变后,手动触发该事件。

总结

react组件通过用户UI交互触发event,event回调函数被执行,在这些函数中调用Action对象对应的方法,发出action,事件分发器接收到action后,做出相应处理,然后触发Store改变事件,通过setState重新渲染DOM元素。

以TodoMVC为例,分两个阶段完成该应用程序的设计。

初始化阶段:

  1. 构建顶级组件及子组件;
  2. 获取Stroe数据,逐级提供到子组件上;
  3. Store绑定change事件,发生变化时,更新组件的数据;
  4. 子组件绑定各类事件,响应用户操作;
  5. 事件分发器注册一个接收到action时的事件处理回调函数;

交互阶段(以新增一个todo项为例):

  1. 在子组件上触发DOM事件,执行回调函数;
  2. 回调函数中调用Action方法;
  3. Action调用事件分发器,发出一个action;
  4. action中的数据被事件分发器传递给初始化阶段注册的事件处理回调函数;
  5. 事件处理回调函数根据不同的actionType进行处理;
  6. 如在处理中涉及数据变化,则手动触发初始化阶段Store绑定的change事件;
  7. Store接收到事件后,重新检索出所有数据提供给顶级组件的setState方法;
  8. 顶级组件自动调用render方法,重新渲染UI界面。

交互阶段的数据环

Event --> Action --> Dispatcher(携带数据) --> Store --> View --> Event

Flux主要包括三部分:Dispatcher、Store和Views(React组件),与传统的MVC(model-View-Controller)不同。Flux中的Dispatcher以controller-view的形式存在,所有数据操作都在Dispatcher的回调中进行,并反映到View上。view通常处于应用的顶层,它从Stores中获取数据,同时将这些数据传递给它的后代节点。


参考资料

[题]函数定义知多少

题目如下:

var b = 10;
(function b(){
    b = 20
    console.log(b)
})()

问:控制台打印了什么?

解释:

使用function声明函数时,可以给一个可选的函数名称标识符,如果提供了,实际上是声明了一个变量并赋值给该变量。函数名字只存在于函数体中,指代函数本身,成为函数局部作用域内的一个局部变量,该名字被绑定到函数对象上,不可修改。

举例:

var c = function d(){
  console.log(c === d)    // => true
}
c()    // => 打印函数定义
d()    // => Error: d is not defined。 函数d只在函数体内有定义

回到开头的题目:

立即执行函数内部的b,指代函数本身,不可修改,重新赋值没有效果,因此,控制台打印的是函数定义本身。

支持分享的前端在线代码编辑器(JSFiddle、JS Bin、codepen、codesandbox)

支持分享的前端在线代码编辑器

JSFiddle

https://jsfiddle.net/

国内访问慢或资源加载不了,建议使用代理。

完全免费,页面有广告,唯一收入来源,不影响使用体验,不建议屏蔽。

高级特性:

  • 支持从预置模板生成代码集,快速开始
  • 支持实时合作
  • 支持页面嵌入,可设置黑白主题色或自定义颜色
  • 每次保存都会产生历史版本
  • CSS支持SCSS,样式重置可选择使用Normalize.css
  • JS支持CoffeeScriptBabel JSXType ScriptVueReactPreact
  • JS内置可选的常用框架与扩展,可定义加载时机,设置<script>标签属性attribute
  • 外链支持从CDNJS搜索名称来加入对应资源
  • 支持模拟异步请求
  • 可设置界面布局、代码提示(beta)、自动运行、自动保存、高亮匹配标签、快捷键映射方案(Sublime、vim、Emacs)

JS Bin

http://jsbin.com/

页面有广告,不影响使用体验。

高级特性:

  • 将代码集保存到GitHub Gist
  • 将代码集保存为模板
  • 自动保存,可设置自动运行
  • 可打开单独窗口运行代码集
  • ctrl+s保存快照,相当于历史版本,通过Open bin...来选择
  • 支持展示Console窗口
  • HTML支持MarkdownJade,并提供转换为HTML功能
  • CSS支持LessMythSassSCSSStylus,并提供转换为CSS功能
  • JS支持ES6 / BabelJSXCoffeeScriptTraceurTypeScriptProcessingLiveScriptClojureScript,并提供转换为原生JavaScript功能
  • JS内置可选的常用框架与扩展
  • 支持页面嵌入,可选择快照还是最新版本,可选择编辑视图或只有结果界面
  • 支持键盘快捷键,支持部分Sumlime快捷键

升级为付费用户:

  • 上传本地资源
  • 创建私有代码集
  • 自定义嵌入样式
  • 同步到Dropbox
  • 个性域名

codepen

https://codepen.io/

平台特色

  • 支持用markdown语法创建文章,文章可嵌入代码集
  • 免费用户可创建1个项目,包含10个文件
  • 支持创建专辑

高级特性:

  • 将代码集保存为模板
  • 将代码集保存到GitHub Gist
  • 将代码集导出到zip包
  • 可打开单独窗口运行代码
  • 提供一些开箱即用的样式资源
  • 可对代码集进行评论
  • 可设置自动保存、自动运行
  • 支持页面嵌入,可设置黑白主题色、点击后加载,升级付费用户后可设置代码可编辑
  • 保存不产生历史版本,每次访问都是最新代码
  • HTML支持HamlMarkdownSlimPug
  • CSS支持LessPostCSSSassSCSSStylus,样式重置可选择使用Normalize.cssReset.css,前缀生成可选择AutoprefixerPrefixfree
  • JS支持BabelTypeScriptCoffeeScriptLiveScript
  • 支持键盘快捷键

升级为付费用户/团队:

  • 创建私有代码集
  • 自定义嵌入主题样式
  • 更多项目更多文件
  • 项目可部署
  • 合作模式
  • 专家模式
  • 资源文件托管

codesandbox

https://codesandbox.io/

codesandbox更新像是在线IDE,可配置首选项,与GitHub、ZEIT集成,以项目为单位,免费用户可创建50个项目。

这里只涉及与代码分享相关的功能。

高级特性:

  • 支持从预置模板生成项目,快速开始
  • 支持添加npm依赖包
  • 支持上传文件
  • 支持编写配置文件package.json.babelrc.prettierrcsandbox.config.json
  • 支持实时合作
  • 将项目导出到zip包
  • 可打开单独窗口运行代码
  • 支持项目分享
  • 保存不产生历史版本,每次访问都是最新代码

升级为付费用户:

  • 团队权限限制解除
  • 创建私有代码集
  • 无限量代码集
  • 静态文件托管从免费20Mb增加到500Mb

codepen 只能分享最新代码,要比较代码变更情况,需要先Fork再编辑。

codesandbox 接近一个完整的IDE,功能强大,可创建公开的多文件项目,适合用在各种框架配置教程中。

JSFiddle、JS Bin 更适合用于在线分享、学习、制作demo、测试代码。

JS Bin 支持代码下载,保存到Gist。

JSFiddle 提供了一些开箱即用的功能,无需复杂的配置,支持代码提示。

如何克隆github仓库的子目录

原始命令方案有git、svn

参考资料:
https://unix.stackexchange.com/questions/233327/is-it-possible-to-clone-only-part-of-a-git-project

git

git 命令操作复杂,具体查看参考资料

svn

svn export https://github.com/${userName}/${repoName}/trunk/${directory}

需要将URL里的tree/master替换为trunk

工具

python工具ghclone

https://github.com/HR/github-clone

扩展工具

https://gitzip.org/

提供了chrome、Firefox扩展

底部有github源码仓库地址

gulp的基础用法

写于 2016-04-27

(注:最新版本gulp部分方法有变)

gulp

插件页面:http://gulpjs.com/plugins/,或者在npm搜索gulpplugin

taoboa NPM: http://npm.taobao.org/

安装

  1. 全局安装:npm install -g gulp
  2. 项目安装:npm install --save-dev gulp
  3. 创建gulpfile.js

GULP.SRC(glob)

  • js/app.js 精确匹配文件
  • js/*.js 仅匹配js目录下的所有后缀为.js的文件
  • js/*/.js 匹配js目录及其子目录下所有后缀为.js的文件
  • !js/app.js 从匹配结果中排除js/app.js,这种方法在你想要匹配除了特殊文件之外的所有文件时非常管用
  • *.+(js|css) 匹配根目录下所有后缀为.js或者.css的文件

js目录下包含了压缩和未压缩的JavaScript文件,现在我们想要创建一个任务来压缩还没有被压缩的文件,我们需要先匹配目录下所有的JavaScript文件,然后排除后缀为.min.js的文件:

gulp.src(['js/**/*.js', '!js/**/*.min.js'])

GULP.WATCH(glob)

监听文件变化执行回调

// 当文件变化时执行任务数组中的任务build
gulp.task('watch', function () {
   gulp.watch('templates/*.tmpl.html', ['build']);
});

或者

// 当文件变化时执行回调函数,包含事件对象event
gulp.watch('templates/*.tmpl.html', function (event) {
   console.log('Event type: ' + event.type); // added, changed, or deleted
   console.log('Event path: ' + event.path); // The path of the modified file
});

gulp.watch()返回一个watcher对象,watcher可以监听事件,或向watch中添加文件。

// 监听change事件
var watcher = gulp.watch('templates/*.tmpl.html', ['build']);
watcher.on('change', function (event) {
   console.log('Event type: ' + event.type); // added, changed, or deleted
   console.log('Event path: ' + event.path); // The path of the modified file
});

可监听事件

  • end 在watcher结束时触发(这意味着,在文件改变的时候,任务或者回调不会执行)
  • error 在出现error时触发
  • ready 在文件被找到并正被监听时触发
  • nomatch 在glob没有匹配到任何文件时触发

Watcher对象一些可以调用的方法:

  • watcher.end() 停止watcher(以便停止执行后面的任务或者回调函数)
  • watcher.files() 返回watcher监听的文件列表
  • watcher.add(glob) 将与指定glob相匹配的文件添加到watcher(也接受可选的回调当第二个参数)
  • watcher.remove(filepath) 从watcher中移除个别文件

定义任务

参数:任务名称,执行函数

gulp.task('greet', function () {
   console.log('Hello world!');
});

// 运行
gulp greet // Hello world

并行任务

// 运行gulp build时,同时无序执行css、js、imgs三个任务
gulp.task('build', ['css', 'js', 'imgs']);

串行任务

// 运行gulp css时,先执行greet任务,结束后执行回调函数
gulp.task('css', ['greet'], function () {
    // Deal with CSS here
});

默认任务

// 运行gulp时,执行default任务
gulp.task('default', function () {
    // Your default task
});

复制文件

gulp.task('copy', function () {
    gulp.src('src/fonts/**/*')
        .pipe(gulp.dest('dist/fonts/'))
})

GitHub/Gitlab与SSH密钥的生成、设置

写于 2015-05-05

  1. Check for SSH keys(检查SSH密钥)
  2. Generate a new SSH key(生成新的SSH密钥)
  3. Add your key to the ssh-agent(添加密钥到SSH代理)
  4. Add your SSH key to your account(添加密钥到帐号)
  5. Test the connection(测试连接)

通过SSH密钥的方式来识别受信任的计算机,不需要输入密码。下面的步骤将引导你生成SSH密钥和添加公钥到你的GitHub帐户。

建议:定期检查你的SSH密钥列表,并删除任何用不着的SSH密钥。


Check for SSH keys

首先,我们需要先检查你电脑上已有的SSH密钥。打开命令行窗口bash(如果安装了Git的Windows版本,可以打开Git Bash)并输入:

ls -al ~/.ssh
# 列出.ssh文件夹下的所有文件

检查列出来的目录中是否已有SSH公钥。默认情况下,公钥文件名将是下列中的一个:

id_dsa.pub

id_ecdsa.pub

id_ed25519.pub

id_rsa.pub

通常我们使用的是RSA的密钥,默认命名为id_rsa,ssh连接时默认读取该文件,可以通过ssh-agent添加额外的密钥文件,后面会提到。


Generate a new SSH key

  1. 复制下面的内容,并粘贴到打开的命令行窗口中。确保你已经替换为你的email地址。

     ssh-keygen -o -t rsa -b 4096 -C "[email protected]"
     # 创建一个新的SSH密钥,`-C`表示注释,以提供的email为标识,用于区分,可选
     # `-o`表示使用更新、安全的编码格式
     # `-b`表示自定义密钥长度,最小1024,默认2048,可选
     # 生成rsa密钥对
    
  2. 强烈建议保持默认的设置,当出现"Enter a file in which to save the key"时,按下回车键即可。

     # Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]
    

如果你打算生成新的密钥,则需要输入新的文件路径,然后在~/.ssh/config文件中添加新的声明,这样ssh连接时才能识别并读取,声明方式后面会提到。

  1. 输入一个口令

     # Enter passphrase (empty for no passphrase): [Type a passphrase]
     # Enter same passphrase again: [Type passphrase again]
    

    Tip:强烈建议设置一个很好的,安全的口令。更多信息,见“使用SSH密钥的口令”。
    该密码可选,你也可以直接按两次回车跳过。设置密码后,连接时需要输入密码。
    如果想添加或修改密码,可以使用-p标识

     ssh-keygen -p -o -f <keyname>
    
  2. 输入口令之后,你会得到一个指纹码或id,这就是你的SSH密钥。它看起来像这样:

     # Your identification has been saved in /Users/you/.ssh/id_rsa.
     # Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
     # The key fingerprint is:
     # 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db [email protected]
    

Add your SSH key to your account

配置你的GitHub帐户使用你的SSH密钥:

  1. 使用你喜欢的编辑器打开文件:~/.ssh/id_rsa.pub。
  2. 全选并复制到剪贴板。不要添加任何新行或空白。

警告: 复制时不能有额外的新行或空白。

将复制的密钥添加到GitHub/Gitlab:

  1. 在GitHub个人首页右上角点击“齿轮”图标进入设置
  2. 在侧栏中点击“SSH keys”
  3. 点击“Add SSH key”
  4. 在“Title”字段中,添加一个描述性的标签。例如,“我的MacBook”
  5. 在“Key”文本域中粘贴你的密钥
  6. 点击“Add key”
  7. 输入你的GitHub密码确认修改

Test the connection

为了确保一切工作正常,你现在尝试连接到SSH。当你这样做时,会要求输入密码进行验证,这是你之前创建的SSH密钥口令。

  1. 打开命令行窗口然后输入:

     ssh -T [email protected]
     # 如果是gitlab则是
     # ssh -T [email protected]
    
  2. 你将看到警告内容:

     # The authenticity of host 'github.com (207.97.227.239)' can't be established.
     # RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
     # Are you sure you want to continue connecting (yes/no)?
    
     检查验证信息中的指纹是否匹配,然后输入“yes”:
    
     # Hi username! You've successfully authenticated, but GitHub does not
     # provide shell access.
    
  3. 如果信息中的username是你的,表明你成功设置了SSH密钥!

如果你收到一个关于“拒绝访问”的信息,你可以阅读这些问题诊断

或使用-vvvT打印debug信息

    # ssh -vvvT [email protected]

如果你从HTTPS切换到SSH,你需要更新远程仓库的URL。有关更多信息,请参阅更改远程仓库的URL


Add your key to the ssh-agent

如果你生成密钥时使用非默认的文件名,则需配置SSH代理程序来使用你生成的SSH密钥:

  1. 确保SSH代理已开启:

     # bash运行SSH代理
     eval "$(ssh-agent -s)"
     # Agent pid 59566
    
  2. 添加生成的SSH密钥到代理中:

     ssh-add ~/.ssh/other_id_rsa
    
  3. 如果出现Could not open a connection to your authentication agent,则在bash窗口中执行:

     ssh-agent bash
    
  4. 如果想保持这些设置,不会因为bash关闭而失效,则需要配置~/.ssh/config文件

配置示例如下,替换host及文件路径即可

# GitLab.com
Host gitlab.com
  Preferredauthentications publickey
  IdentityFile ~/.ssh/gitlab_com_rsa

# Private GitLab instance
Host gitlab.company.com
  Preferredauthentications publickey
  IdentityFile ~/.ssh/example_com_rsa

参考资料

axios请求拦截的奇怪设定

axios有个interceptor的请求拦截api,官方文档的示例代码如下:

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

它可以设置两个函数参数fulfilledrejected,分别对应resolvereject两种情况。

奇怪的是,不管如何尝试抛出错误或返回Promise.reject(),它永远都不会触发rejected处理函数。

查看源码后发现,原来在promise执行时,它的状态已经是fulfilled,也就是说,请求拦截永远不会触发rejected函数。

如果在fulfilled函数里引发了错误,由于源码采用的是then(fulfilled, rejected)的做法,而不是then(fulfilled).catch(rejected),导致这个抛出的错误不能被rejected函数接收,只能被后续链式调用的响应拦截接收,或者继续向上抛出。

如果采用promise.then().catch()做法,那么,then方法里的错误是可以被后面的catch捕获的。

比较如下两个例子就知道了:

// eg1:`then`方法里返回的reject不会被捕获
var promise1 = Promise.resolve('p1')
    .then(v=>{
        console.log('done:', v);                         // done: p1
        return Promise.reject('p1 reject')         // Uncaught (in promise) p1 reject
    },
    e => console.log('error:', e)                     // never execute
)

// eg2: `then`方法里返回的reject被`catch`方法捕获
var promise2 = Promise.resolve('p2')
    .then(v=>{
        console.log('done:', v);                          // done: p2
        return Promise.reject('p2 reject')
    })
    .catch(e => console.log('error:', e))          // error: p2 reject

相关源码:

https://github.com/axios/axios/blob/0d8765562401910c1c509f6739a3bc558721e123/lib/core/Axios.js#L50

在第50行创建的promise已经resolve

然后从interceptors里分别取出拦截函数,

拦截函数相关代码:https://github.com/axios/axios/blob/master/lib/core/InterceptorManager.js

在第61行调用then方法时,就直接进入fulfilled函数,导致rejected函数形同虚设。

那axios官方文档的示例代码那样子写是什么意思呢?

VSCode 使用 ESLint 检查 TypeScript

1、在 VSCode 中安装 ESLint 插件。

2、在 [文件 > 首选项 > 设置]中,添加以下配置:

{
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "typescript"
    ]
}

3、 package.json 增加脚本命令

{
    "scripts": {
        "eslint": "eslint src --ext .ts"
    }
}

4、安装使用 AlloyTeam 的 ESLint 配置

npm install --save-dev eslint typescript @typescript-eslint/eslint-plugin [email protected]

在项目根目录下创建 .eslintrc.js,并将以下内容复制到文件中:

module.exports = {
    extends: [
        'eslint-config-alloy/typescript',
    ],
    globals: {
        // 这里填入你的项目需要的全局变量
        // 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
        //
        // jQuery: false,
        // $: false
    },
    rules: {
        // 这里填入你的项目需要的个性化配置,比如:
        //
        // // 一个缩进必须用两个空格替代
        // 'indent': [
        //     'error',
        //     2,
        //     {
        //         SwitchCase: 1,
        //         flatTernaryExpressions: true
        //     }
        // ]
        // // 一个缩进必须用两个空格替代
        // '@typescript-eslint/indent': [
        //     'error',
        //     2,
        //     {
        //         SwitchCase: 1,
        //         flatTernaryExpressions: true
        //     }
        // ]
    }
};

5、增加 tsx 支持

需要增加安装 eslint-plugin-react

npm install --save-dev eslint-plugin-react

package.json 中的 script.eslint 添加 .tsx 后缀

{
    "scripts": {
        "eslint": "eslint src --ext .ts,.tsx"
    }
}

VSCode 的配置中新增 typescriptreact 检查

{
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "vue",
        "typescript",
        "typescriptreact"
    ]
}

修改 .eslintrc.js 中的 extends 项:

    extends: [
        'eslint-config-alloy/react',
        'eslint-config-alloy/typescript',
    ]

gulp使用步骤

写于 2016-10-11

安装nodejs

初始化项目

npm init

安装gulp

npm install -g gulp
npm install --save-dev gulp

安装依赖包

npm install

创建gulp配置文件 gulpfile.js

执行gulp任务

gulp [taskname]

官方范例 gulpfile.js

var gulp = require('gulp');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var imagemin = require('gulp-imagemin');
var sourcemaps = require('gulp-sourcemaps');
var del = require('del');

var paths = {
  scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'],
  images: 'client/img/**/*'
};

// Not all tasks need to use streams
// A gulpfile is just another node program and you can use any package available on npm
gulp.task('clean', function() {
  // You can use multiple globbing patterns as you would with `gulp.src`
  return del(['build']);
});

gulp.task('scripts', ['clean'], function() {
  // Minify and copy all JavaScript (except vendor scripts)
  // with sourcemaps all the way down
  return gulp.src(paths.scripts)
    .pipe(sourcemaps.init())
      .pipe(coffee())
      .pipe(uglify())
      .pipe(concat('all.min.js'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('build/js'));
});

// Copy all static images
gulp.task('images', ['clean'], function() {
  return gulp.src(paths.images)
    // Pass in options to the task
    .pipe(imagemin({optimizationLevel: 5}))
    .pipe(gulp.dest('build/img'));
});

// Rerun the task when a file changes
gulp.task('watch', function() {
  gulp.watch(paths.scripts, ['scripts']);
  gulp.watch(paths.images, ['images']);
});

// The default task (called when you run `gulp` from cli)
gulp.task('default', ['watch', 'scripts', 'images']);

Git简易教程

Git简易教程

提交

用git初始化目录,该目录称为工作目录

git init

查看工作目录状态

git status

添加文件到工作目录暂存区,小数点表示所有文件

git add <.|file|directory>

提交暂存区里的文件到本地仓库

git commit -m "<commmit message>"

文件比较

比较工作目录与最后一次提交(HEAD)

git diff

比较工作目录与指定提交

git diff <commit-hash>

比较指定文件

git diff <file-name>

比较最近两次commit

git diff HEAD~2 HEAD

指定文件比较的外部编辑器

git difftool

比较暂存区与最后一次提交

git diff --staged

日志

查看commit历史记录

git log
git log --oneline --decorate //查看各个分支当前所指向的对象
git log --grep="<string>" //筛选包含指定串的commit
git log -p -n 2 //查看完整详情,-n指定数量
git log -g master //查看带有引用记录的日志
git log --graph --pretty=oneline --abbrev-commit //查看分支合并图

自定义log格式

git log --pretty=format:"%h %an %ar - %s"

通过帮助文档查看更多log详情

git log --help

查看包含比较信息的commit详情,默认为 HEAD commit

git show
git show <commit-hash> //查看指定commit详情
git show HEAD@{5} //查看5次前的commit记录
git show master@{yesterday} //查看master分支昨天的提交
git show HEAD@{2.months.ago} //查看2个月的记录

查看引用日志,引用日志只在本地仓库存在

git reflog

远程操作

关联当前目录到远程仓库,通常使用 origin 作为别名

git remote add <ref-name> <remote-url>

克隆远程仓库到本地目录,别名自动设置为 origin,目录名与远程仓库的目录名一致

git clone <remote-url>

克隆远程仓库并指定本地目录名

git clone <url> <foldname>

将本地所有commit推送到远程仓库,默认分支名为 master

git push <ref-name> <branch-name>

拉取远程库并自动合并

git pull <ref-name> <branch-name>

拉取远程库但不合并,拉取的文件将下载到独立分支,命名格式为

remotes/<remote-name>/<remote-branch-name>

git fetch <ref-name>

推送分支上的commit

git push <remote_name> <branch_name>

变更提交

取消文件暂存,不改变工作目录

git reset HEAD <.|files|directories>

撤销最后一次commit,返回前一次commit的状态,不改变工作目录

git reset HEAD^

将最后一次HEAD commit指向给定提交,不改变暂存与工作目录

git reset --soft HEAD^

还原到前一次commit,会改变工作目录

git reset --hard HEAD^

撤销工作目录的修改,还原到最后一次commit

git reset --hard HEAD <.|files|directories>

撤销指定提交,并创建新的提交来应用变更

git revert HEAD...HEAD~2

合并

将fetch下来的分支合并

git merge remotes/origin/master

用外部工具进行合并

git mergetool

直接使用本地文件解决冲突

git checkout --ours <file-name>

直接使用远程文件解决冲突

git checkout --theirs <file-name>

使用变基来移植分支,branch上的提交被依次应用到master上

git rebase master branch
git checkout master
git merge branch

合并指定commit

git cherry-pick <hash-id|ref>
git cherry-pick --abort //放弃合并
git cherry-pick --continue //出现冲突时需要走冲突解决流程,再继续

分支

创建分支

git branch dev
git branch <branch-name> <starting-commit> //基于指定分支的最后一次提交创建分支

切换分支

git checkout <branch-name>
git checkout -m dev //检出分支同时合并本地修改
git checkout -b dev //新建并切换分支
git checkout -b dev origin/dev //从远程检出分支

列出分支

git branch
git branch -r //列出远程分支
git branch -av //列出分支,包含远程分支和commit信息
git show-branch //分支详情
git show-branch -a
git show-branch -r

删除分支

git branch -d <branch-name>
git branch -D <branch-name> // 强制删除分支

删除文件

git rm test.txt
git rm --cached test.txt //仅从暂存区删除

修改commit记录

修改最后一次commit消息

git commit --amend

进入rebase交互模式,使用编辑器修改commit记录

git rebase --interactive --root

工作区状态

git stash //保存当前工作区状态
git stash list //查看已保存的工作区状态
git stash apply stash@{0} //恢复指定工作区状态
git stash drop stash@{0} //删除指定工作区状态
git stash pop //恢复并删除工作区

git常用命令

设置用户

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

推送到远程仓库

# 加上-u参数,将远程仓库分支master关联起来
git push -u origin master
# 再次推送
git push origin master

查看远程库的信息

git remote
git remote -v
git remote show
git remote show <remote-name>

推送分支

git push <remote-name> <branch-name>
git push origin dev
git push

重关联本地仓库与远程仓库

git branch --set-upstream dev origin/dev

重定位远程仓库地址

git remote set-url origin <NEW_URL>

标签

git tag tagname //添加标签
git tag //查看标签
git tag v0.9 <hash-id> //在指定commit id上打标签
git show tagname //查看标签信息
git tag -a v0.1 -m "version 0.1 released" <hash-id> //-a 标签名, -m 标签说明
git tag -d tagname //删除标签
git push origin tagname //推送指定标签到远程仓库
git push origin --tags //推送全部标签到远程仓库
git push origin :refs/tags/tagname //删除远程仓库上本地已删除的标签

设置git显示颜色

git config --global color.ui true

设置命令别名

git config --global alias.st status
git config --global alias.ci commit
# 使用字符串方式
# git unstage test.py --> git reset HEAD test.py
git config --global alias.unstage 'reset HEAD'
# 显示最后一次提交信息
git config --global alias.last 'log -1'
# 格式化最后一次的提交信息
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

配置文件

# 配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
# 每个仓库的Git配置文件都放在.git/config文件中
# 当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中

Git相关资料

Pro Git

postMessage API 介绍及其在window.open及iframe中的双向通信应用与示例

window.postMessage

语法

targetWindow.postMessage(message, targetOrigin, [transfer]);

window.postMessage()向目标窗口派发一个 MessageEvent 消息。

targetWindow
:接收消息的窗口引用,包括:

  • window.open
  • window.opener
  • HTMLIFrameElement.contentWindow
  • window.parent
  • window.frames[i]

message
:具体的信息内容

targetOrigin
:接收消息的窗口的源(origin)。设为*,表示不限制域名,向任意窗口发送。应当避免使用*,以防窗口被重定向,导致发送的消息被恶意获取。

transfer
:可选,一个序列化的Transferable对象,所有权将被转移到目标窗口

监听message事件

event.data
:信息内容

event.origin
:发送消息的源窗口,包括scheme://host:port

event.source
:发送消息窗口的引用,可用于建立双向通信

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event) {
  if (event.origin !== "http://example.org:8080")
    return;

  // ...
}

双向通信

/*
 * In window A's scripts, with A being on <http://example.com:8080>:
 */

var popup = window.open(...popup details...);

popup.postMessage("hello there!", "http://example.com");

function receiveMessage(event)
{
  if (event.origin !== "http://example.com")
    return;

  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/*
 * In the popup's scripts, running on <http://example.com>:
 */

function receiveMessage(event)
{
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source is window.opener
  // event.data is "hello there!"

  // a convenient idiom for replying to a
  // message is to call postMessage on event.source and provide
  // event.origin as the targetOrigin.
  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);

iframe 通信

// main window
<button id="send">send</button>
<script>
window.onload = function(){
  var button = document.getElementById('send');
  
  window.addEventListener('message', function(event){
    console.log(event);
  }, false);
  
  var iframe = document.createElement('iframe');
  iframe.src = "http://iframe.local.com/";
  iframe.style = "display:none";
  document.body.appendChild(iframe);
  button.addEventListener('click', function () {
    iframe.contentWindow.postMessage('from webroot', 'http://iframe.local.com/');
  }, false);
}
</script>
//iframe
<script>
window.onload = function () {
  targetWindow = window.opener || window.parent
  targetWindow.postMessage('from iframe.local ', '*');
  window.addEventListener('message', function (event) {
    console.log(event);
    event.source.postMessage('roger that. (origin message: ' + event.data + ')', event.origin);
  }, false)
}
</script>

如何开始一个Web AssemblyScript项目

如何开始一个Web AssemblyScript项目

简介

AssemblyScript 是一个 TypeScript 到 WebAssembly 的编译器。由 Microsoft 开发的 TypeScript 为 JavaScript 添加了类型。它已经非常流行了,但就算用户不怎么熟悉 TS,AssemblyScript 也只支持 TypeScript 功能的一个有限子集,因此不需要花很长时间就能上手。

因为它与 JavaScript 非常相似,所以 AssemblyScript 使 Web 开发人员可以轻松地将 WebAssembly 整合到他们的网站中,而不必使用某种完全不同的语言。

相关缩写说明:

  • JS:Javascript
  • AS:AssemblyScript
  • WA:WebAssembly

在Web项目中加入AssemblyScript的步骤

  1. 安装assemblyscript依赖,它提供了一个用来创建AS项目的脚手架,同时提供将AS代码转换为WebAssembly代码的功能。
  2. 使用asinit命令创建脚手架文件,项目已经准备好了。
  3. assembly目录下开始编写AssemblyScript 源代码。
  4. 构建AS代码,将AS代码编译为WA代码,Web项目实际用到的就是这些编译后的WA代码。
  5. 编写JS代码,引用WA代码中的模块。
  6. 编写HTML页面,加载JS代码,调用WA模块提供的函数。

下面开始按以上步骤从0构建AS项目。

1、初始化项目文件夹

npm init
npm install --save-dev assemblyscript

2、创建脚手架文件

npx asinit .

现在项目目录下多了一些文件夹及目录

  • assembly 目录:AS源码目录,里面有一个ts文件,含有一个示范函数add,该文件将以模块形式导出add函数,以便在JS中调用:
export function add(a: i32, b: i32): i32 {
  return a + b;
}
  • build 目录:AS构建后生成的WA代码,就存放在此,编译后会出现以下文件:

    optimized.wasm
    optimized.wasm.map
    optimized.wat
    untouched.wasm
    untouched.wasm.map
    untouched.wat

optimized为优化版本,untouched为普通版本;

  • .wasm:二进制文件

  • .wasm.map:源码映射文件

  • .wat:二进制文件的文本表示形式

  • index.js 文件:用户Nodejs环境,读取.wasm文件并导出模块:

const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {
  env: {
    abort(_msg, _file, line, column) {
       console.error("abort called at index.ts:" + line + ":" + column);
    }
  }
};
Object.defineProperty(module, "exports", {
  get: () => new WebAssembly.Instance(compiled, imports).exports
});
  • package.json 文件:增加了构建相关的script
    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",
    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",
    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"

3、构建WA源文件

脚手架已经创建了示例函数,可以直接使用,接下来需要构建WA文件:

npm run asbuild

编译后的文件存放到了build目录下。

如果是用于Nodejs环境,现在已经可用了,脚手架创建的index.js就是入口。

可以进入nodejs测试:

$ node
> const add = require('./index').add;
undefined
> add(3, 5)
8

4、创建js文件

创建一个新的demo.js文件:

(async () => {
    const importObject = {
        env: {
            abort(_msg, _file, line, column) {
                console.error("abort called at index.ts:" + line + ":" + column);
            }
        }
    };
    const module = await WebAssembly.instantiateStreaming(
        fetch("build/optimized.wasm"),
        importObject
    );
    const add = module.instance.exports.add;

    const result = document.querySelector("#result");
    document.querySelector("#add").addEventListener("submit", event => {
        event.preventDefault();
        result.innerText = "";
        const number = event.target.elements.number.value;
        result.innerText = `${number} + ${number} = ${add(number, number)}.`;
    });
})();

这个脚本会获取输入框里的数字,然后调用add函数进行计算。

要加载 WebAssembly 模块有多种方法,但最有效的方法是使用 WebAssembly.instantiateStreaming 函数,以流方式编译和实例化这些模块。请注意,我们需要提供一个中止函数,如果断言失败就会调用这个中止函数。

5、创建html文件

创建一个新的index.html文件:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <form id="add">
            <label for="number">Enter a number:</label>
            <input name="number" type="number" />
            <button type="submit">Submit</button>
        </form>

        <p id="result"></p>

        <script src="demo.js"></script>
    </body>
</html>

这个页面会提供一个输入框用于输入数字,点击submit后,会在下方显示计算结果。

6、启动静态文件服务器

demo.js需要通过fetch方法来加载WA文件,我们需要一个简易的网络服务器。

npm install --save-dev static-server

然后在package.json添加脚本:

{
  "scripts": {
    "serve-demo": "static-server"
  }
}

运行命令启动服务器:

npm run serve-demo

在浏览器打开URL,输入数字,点submit。

到此,我们完成了一个AS项目。

7、添加监视脚本

安装onchange依赖,监控assembly目录的修改,自动运行构建:

npm install --save-dev onchange

在package.json添加监视脚本:

{
  "scripts": {
    "asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild"
  }
}

现在运行watch命令就可以自动构建,不需要每次修改都重新运行构建命令:

npm run asbuild:watch

gulp插件

写于 2016-10-11

插件


gulp官方推荐gulp-changed

gulp-changed

https://github.com/sindresorhus/gulp-changed

Only pass through changed files

npm install --save-dev gulp-changed


gulp官方推荐gulp-cached

gulp-cached

https://github.com/contra/gulp-cached

in-memory file cache, not for operation on sets of files


gulp官方推荐gulp-remember

gulp-remember

https://github.com/ahaurw01/gulp-remember

pairs nicely with gulp-cached


gulp官方推荐gulp-newer

gulp-newer

https://github.com/tschaub/gulp-newer

npm install gulp-newer --save-dev

pass through newer source files only, supports many:1 source:dest


自动加载插件模块

gulp-load-plugins

http://npm.taobao.org/package/gulp-load-plugins

npm install -–save-dev gulp-load-plugins

// 自动加载package.json中的插件,并将插件作为plugins的属性
// 属性名为插件的驼峰式命名,gulp-ruby-sass将被加载成plugins.rubySass
// 仅在回调函数中使用到时才加载(lazyLoad)
var gulp = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    plugins = gulpLoadPlugins();

gulp.task('js', function () {
   return gulp.src('js/*.js')
      .pipe(plugins.jshint())
      .pipe(plugins.jshint.reporter('default'))
      .pipe(plugins.uglify())
      .pipe(plugins.concat('app.js'))
      .pipe(gulp.dest('build'));
});

压缩图片

gulp-imagemin

http://npm.taobao.org/package/gulp-imagemin

npm install --save-dev gulp-imagemin

const gulp = require('gulp');
const imagemin = require('gulp-imagemin');
const pngquant = require('imagemin-pngquant');

gulp.task('default', () => {
    return gulp.src('src/images/*')
        .pipe(imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant()]
        }))
        .pipe(gulp.dest('dist/images'));
});

添加浏览器前缀到CSS

gulp-autoprefixer

npm install --save-dev gulp-autoprefixer

http://npm.taobao.org/package/gulp-autoprefixer

var gulp = require('gulp');
var autoprefixer = require('gulp-autoprefixer');

gulp.task('default', function () {
    return gulp.src('src/app.css')
        .pipe(autoprefixer({
            browsers: ['last 2 versions'],
            cascade: false
        }))
        .pipe(gulp.dest('dist'));
});

压缩css文件

css nano

gulp-cssnano

http://npm.taobao.org/package/gulp-cssnano

npm install --save-dev gulp-cssnano

var gulp = require('gulp');
var cssnano = require('gulp-cssnano');

gulp.task('default', function() {
    return gulp.src('./main.css')
        .pipe(cssnano())
        .pipe(gulp.dest('./out'));
});

css minify

gulp-minify-css


编译 Sass

gulp-ruby-sass

http://npm.taobao.org/package/gulp-ruby-sass

npm install --save-dev gulp-ruby-sass

var gulp = require('gulp');
var sass = require('gulp-ruby-sass');

gulp.task('sass', function () {
  return sass('source/file.scss')
    .on('error', sass.logError)
    .pipe(gulp.dest('result'));
});

压缩JavaScript文件

gulp-uglify

http://npm.taobao.org/package/gulp-uglify

npm install --save-dev gulp-uglify

var gulp = require('gulp'),
   uglify = require('gulp-uglify');

gulp.task('minify', function () {
   gulp.src('js/app.js')
      .pipe(uglify())
      .pipe(gulp.dest('build'))
});

检查JavaScript文件

gulp-jshint

http://npm.taobao.org/package/gulp-jshint

npm install --save-dev gulp-jshint


合并文件

gulp-concat

http://npm.taobao.org/package/gulp-concat

npm install --save-dev gulp-concat

var concat = require('gulp-concat');
gulp.task('scripts', function() {
  return gulp.src('./lib/*.js')
    .pipe(concat('all.js'))
    .pipe(gulp.dest('./dist/'));
});

指定顺序

var concat = require('gulp-concat');
gulp.task('scripts', function() {
  return gulp.src(['./lib/file3.js', './lib/file1.js', './lib/file2.js'])
    .pipe(concat('all.js'))
    .pipe(gulp.dest('./dist/'));
});

浏览器重加载

livereload

gulp-livereload

http://npm.taobao.org/package/gulp-livereload

livereload需要浏览器扩展支持

// 监听文件的变化,生成css,重新加载网页
var gulp = require('gulp'),
    less = require('gulp-less'),
    livereload = require('gulp-livereload'),
    watch = require('gulp-watch');

gulp.task('less', function() {
   gulp.src('less/*.less')
      .pipe(watch())
      .pipe(less())
      .pipe(gulp.dest('css'))
      .pipe(livereload());
});

browser-sync

browser-sync

http://npm.taobao.org/package/browser-sync

npm install --save-dev browser-sync

browser-syncLiveReload非常相似,但是它有更多的功能。改变代码时,BrowserSync会重新加载页面,如果是css文件,则直接添加进css中,页面并不需要再次刷新。在开发单页面应用时尤其高效。BrowserSync也可以在不同浏览器之间同步点击翻页、表单操作、滚动位置。

var gulp = require('gulp'),
    browserSync = require('browser-sync');

gulp.task('browser-sync', function () {
   var files = [
      'app/**/*.html',
      'app/assets/css/**/*.css',
      'app/assets/imgs/**/*.png',
      'app/assets/js/**/*.js'
   ];

   browserSync.init(files, {
      server: {
         baseDir: './app'
      }
   });
});

gulp-usemin

gulp-usemin

npm install --save-dev gulp-usemin

http://npm.taobao.org/package/gulp-usemin

https://github.com/zont/gulp-usemin

var usemin = require('gulp-usemin');
var uglify = require('gulp-uglify');
var minifyHtml = require('gulp-minify-html');
var minifyCss = require('gulp-minify-css');
var rev = require('gulp-rev');


gulp.task('usemin', function() {
  return gulp.src('./*.html')
    .pipe(usemin({
      css: [ rev() ],
      html: [ minifyHtml({ empty: true }) ],
      js: [ uglify(), rev() ],
      inlinejs: [ uglify() ],
      inlinecss: [ minifyCss(), 'concat' ]
    }))
    .pipe(gulp.dest('build/'));
});

获取变动的文件信息

gulp-watch-path

npm install --save-dev gulp-watch-path

http://npm.taobao.org/package/gulp-watch-path

gulp.watch('src/**/*', function (event) {
    var paths = require('gulp-watch-path')(event, 'src/', 'dist/', 'node');
    console.log(paths)
    /*
    paths {srcPath: 'src/file.js',
          srcDir: 'src/',
          distPath: 'dist/file.node',
          distDir: 'dist/',
          srcFilename: 'file.js',
          distFilename: 'file.node' }
    */
    gulp.src(paths.srcPath)
        .pipe(uglify())
        .pipe(gulp.dest(paths.distDir))
})

stream-combiner2

stream-combiner2

http://npm.taobao.org/package/stream-combiner2

var Combine = require('stream-combiner')
var es      = require('event-stream')

Combine(                                  // connect streams together with `pipe`
  process.openStdin(),                    // open stdin
  es.split(),                             // split stream to break on newlines
  es.map(function (data, callback) {      // turn this async function into a stream
    var repr = inspect(JSON.parse(data))  // render it nicely
    callback(null, repr)
  }),
  process.stdout                          // pipe it to stdout !
)

gulp-sourcemaps

gulp-sourcemaps

http://npm.taobao.org/package/gulp-sourcemaps

var gulp = require('gulp');
var plugin1 = require('gulp-plugin1');
var plugin2 = require('gulp-plugin2');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('javascript', function() {
  gulp.src('src/**/*.js')
    .pipe(sourcemaps.init())
      .pipe(plugin1())
      .pipe(plugin2())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist'));
});

gulp工作流

写于 2017-10-31

单个输出和监测

var gulp = require('gulp');
var coffee = require('gulp-coffee');
var uglify = require('gulp-uglify');

gulp.task('js', function(){
  return gulp.src('./js/src/*.coffee')
    .pipe(coffee())
    .pipe(uglify())
    .pipe(gulp.dest('./js/'));
});
gulp.task('watch', function(){
  gulp.watch('./js/src/*.coffee', ['js']);
});

多个输出

var gulp = require('gulp');
var autoprefixer = require('gulp-autoprefixer');
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');

gulp.task('css', function(){
  return gulp.src('./css/src/style.css')
    .pipe(autoprefixer('last 2 versions'))
    .pipe(gulp.dest('./css/'))
    .pipe(minifyCss())
    .pipe(rename({extname: '.min.css'}))
    .pipe(gulp.dest('./css/'));
});

增量重新构建

var gulp = require('gulp');
var cached = require('gulp-cached');
var uglify = require('gulp-uglify');
var remember = require('gulp-remember');
var concat = require('gulp-concat');

gulp.task('script', function(){
  return gulp.src('./js/src/*.js')
    .pipe(cached())
    .pipe(uglify())
    .pipe(remember())
    .pipe(concat('app.js'))
    .pipe(gulp.dest('./js/'));
});

只针对修改过的文件执行任务

var gulp = require('gulp');
var changed = require('gulp-changed');
var uglify = require('gulp-uglify');

gulp.task('css', function(){
  return gulp.src('./src/*.js')
    .pipe(changed('./dist/'))
    .pipe(uglify())
    .pipe(gulp.dest('./dist/'));
});

异步工作流

var gulp = require('gulp');
var merge = require('merge-stream');
var less = require('gulp-less');
var autoprefixer = require('gulp-remember');

gulp.task('css', function(){
  return merge(
    gulp.src('./css/src/first.less')
      .pipe(less())
      .pipe(gulp.dest('./css/')),
    gulp.src('./css/src/second.css')
      .pipe(autoprefixer())
      .pipe(gulp.dest('./css/'))
  );
})

连续并入

var gulp = require('gulp');
var streamqueue = require('streamqueue');
var less = require('gulp-less');
var cssimport = require('gulp-cssimport');
var autoprefier = require('gulp-autoprefixer');

gulp.task('css', function(){
return streamqueue({ objectMode: true },
    gulp.src('./css/src/first.less')
      .pipe(less()),
    gulp.src('./css/src/second.css')
      .pipe(cssimport())
      .pipe(autoprefixer('last 2 versions')))
    .pipe(concat('app.css'))
    .pipe(minifyCss())
    .pipe(gulp.dest('./css/'))
});

数组工作流

var gulp = require('gulp');
var es = require('event-stream');
var consolidate = require('gulp-consolidate');
var rename = require('gulp-rename');
data = [
  {
    name: 'apple',
    title: 'Apple Cake',
    attrs: some_data
  },
  {
    name: 'orange',
    title: 'Orange Cookie',
    attrs: some_data
  }
];

gulp.task('page', function(){
  var tasks = data.map(function(entry){
    return gulp.src('./templates/a.js')
      .pipe(consolidate({
        title: entry.title,
        attrs: entry.attrs
      }))
      .pipe(rename({
        basename: entry.name,
        extname: '.html'
      }))
      .pipe(gulp.dest('./html/'));
    });
  return es.concat.apply(null, tasks);
});

windows本地开发生成证书,nginx配置SSL证书实现https服务

安装Openssl

http://slproweb.com/products/Win32OpenSSL.html

配置环境变量

在环境变量中新建变量名:OPENSSL_HOME,变量值为openssl安装位置,如:C:\OpenSSL-Win64\bin

在Path变量下添加值:%OPENSSL_HOME%

生成证书

在nginx安装目录中创建ssl文件夹用于存放证书

以管理员身份进入命令行模式,进入ssl文件夹

创建私钥

在命令行中执行命令:

openssl genrsa -des3 -out name.key 1024     #name为文件名,可自定义

输入密码后,再次重复输入确认密码。记住此密码,后面会用到。

创建csr证书

在命令行中执行命令:

openssl req -new -key name.key -out name.csr    #name为上一步的文件名

执行上述命令后,需要输入信息。其中的Common Name,需要输入要使用https访问的域名。

其他信息根据提示,随意输入。

完成后可得到两个文件,文件名分别为name.keyname.csr

去除密码

为避免启动nginx要求输入密码,需要去除证书密码

复制name.key并重命名为name.key.org

在命令行中执行命令:

openssl rsa -in name.key.org -out name.key

此时提示输入创建私钥时的密码

生成crt证书

在命令行中执行此命令:

openssl x509 -req -days 365 -in name.csr -signkey name.key -out name.crt

完成后可得到4个文件,配置SSL时需要用到crt、key两个文件

配置nginx

修改nginx.conf文件

找到HTTPS server一节:

# HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

复制一份配置代码,然后修改为:

# HTTPS server
    server {
        listen       443 ssl;
        server_name    www.name.com;
    
        ssl_certificate      C:/nginx/ssl/name.crt;
        ssl_certificate_key  C:/nginx/ssl/name.key;
    
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   C:/nginx/root;
            index  index.html index.htm;
        }
    }

重启nginx,完成

认识promise

写于 2015-11-27

问:下面四个使用 promise 的语句之间的不同点在哪儿?

doSomething().then(function () {
    return doSomethingElse();
}).then(finalHandler);

doSomethin().then(functiuoin () {
    doSomethingElse();
}).then(finalHandler);

doSomething().then(doSomethingElse())
    .then(finalHandler);

doSomething().then(doSomethingElse)
    .then(finalHandler);

答:

Q1

doSomething().then(function () {
  return doSomethingElse();
}).then(finalHandler);

答案:

doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|

Q2

doSomething().then(function () {
  doSomethingElse();
}).then(finalHandler);

答案:

doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                  finalHandler(undefined)
                  |------------------|

Q3

doSomething().then(doSomethingElse())
  .then(finalHandler);

答案

doSomething
|-----------------|
doSomethingElse(undefined)
|---------------------------------|
                  finalHandler(resultOfDoSomething)
                  |------------------|

Q4

doSomething().then(doSomethingElse)
  .then(finalHandler);

答案

doSomething
|-----------------|
                  doSomethingElse(resultOfDoSomething)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|

将thenable的对象转换为promise对象

Promise.resolve({
  'then':function(resolve, reject){
    setTimeout(function(){
      if(Math.random()*10 > 5) {
        resolve(1);
      } else {
        reject(0);
      }
    },1000);
  }
}).then(function(result){
  console.log(result);
}).catch(function(error){
  console.error(error);
})

通过 Promise.resolve(Thenable对象) 形式将 Thenable对象 转为 Promise对象

重点在于创建具有 then 方法的对象,方法参数与 Promisethen 方法一致,在确定时执行 resolve 方法,拒绝时调用 reject 方法。

Thenable主要是用于 Promise类库 之间的转换


参考资料

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.