如何实现一个 CMD 模块加载器

cmd 是阿里大神玉伯提出的基于浏览器的前端模块化规范,并在 seajs 中实现了这个规范。相对于另一个在国外比较流行的前端模块化规范 amd,cmd 对于 nodejs 的使用者来说更加友好,使得类似 commonJS 模块的写法可以在浏览器中使用,同时解决了浏览器中模块异步加载的困扰。
关于 cmd 更详细的内容可以移步 https://github.com/cmdjs/specification/blob/master/draft/module.md
今天,我们一起来学习如何实现一个浏览器端的简单的 cmd loader。

模块加载流程

下图展示了一个 cmd loader 的模块加载大体流程:

cmd loader

  1. 首先,通过 use 方法来加载入口模块,并接收一个回调函数, 当模块加载完成, 会调用回调函数,并传入对应的模块。use 方法会 check 模块有没有缓存,如果有,则从缓存中获取模块,如果没有,则创建并加载模块。
  2. 获取到模块后,模块可能还没有 load 完成,所以需要在模块上绑定一个 “complete” 事件,模块加载完成会触发这个事件,这时候才调用回调函数。
  3. 创建一个模块时,id就是模块的地址,通过创建 script 标签的方式异步加载模块的代码(factory),factory 加载完成后,会 check factory 中有没有 require 别的子模块:
    • 如果有,继续加载其子模块,并在子模块上绑定 “complete” 事件,来触发本身 的 “complete” 事件;
    • 如果没有则直接触发本身的 “complete” 事件。
  4. 如果子模块中还有依赖,则会递归这个过程。
  5. 通过事件由里到外的传递,当所有依赖的模块都 complete 的时候,最外层的入口模块才会触发 “complete” 事件,use 方法中的回调函数才会被调用。

Read More

在 fis 项目中使用 react.js

fis 具有非常灵活的插件扩展机制,对于 react 的 jsx 也有现成的 parser 插件可用。
通过几条简单的配置,就可以在你的 fis 项目中使用 react 了。 但是在使用过程中,有一些小坑还是需要注意的,在此记录一下。

安装插件

npm install fis-parser-react -g

注意,如果你使用的 react 版本是 0.13.x, 则要安装另一个版本的插件:

npm install fis-parser-react-0.13.x -g

编译配置

编辑 fis-conf.js 文件, 加入以下内容:

// 将 jsx 文件作为文本处理
fis.config.set('project.fileType.text', 'jsx');

// 后缀名为 jsx 的文件用 fis-parser-react 插件编译
// 如果你使用的 react 版本是 0.13.x,将下面第二个参数换成 'react-0.13.x'
fis.config.set('modules.parser.jsx', 'react');

// 将 jsx 文件编译结果输出为 js 文件
fis.config.set('roadmap.ext.jsx', 'js');

模块化配置

默认情况,fis 不会将 widget 目录下的 jsx 文件包裹成 cmd 模块, 而是直接输出编译后的 js 文件。
但是我们更希望遵循 fis 的目录规范,将 widget 目录下的 jsx 文件也输出为 module, 所以我们还需要配置一下 roadmap.path。
编辑 fis-conf.js 文件, 加入以下内容:

fis.config.set('roadmap.path', [{
    reg: /^\/widget\/(.*)\.jsx$/i,
    useMap: true,
    useHash: true,
    isMod: true,
    release: '${statics}/${namespace}/widget/$1.js'
}].concat(fis.config.get('roadmap.path', [])));

最后执行 fis release 就可以看到所有的 jsx 都可以被正确编译和引用到了~

使用 imitator 实现前后端分离开发中的数据模拟与静态资源映射

imitator 是一个简单易用的 nodejs 服务器, 主要用于模拟 HTTP 接口数据, 请求代理与转发 。
使用imitator,可以解决前后端分离开发中的痛点之一:数据模拟,也可以作为代理服务器与静态资源服务器使用。

github: https://github.com/hanan198501/imitator

为什么会有 imitator?

最近几个java(前后端都在一个工程里)项目交接过来,没时间重构成fis项目,组里好多前端同学想搞分离开发。
我推荐了 nginx,有童鞋反应配置文件相对前端来说还是不够友好,而且有些个性的接口格式无法满足。
于是写了 imitator,使用 nodejs 并基于 express.js 实现, 配置文件相当简单, 而且易于订制,前端同学使用起来非常顺手。

快速上手

  1. 安装——首先你要先安装 nodejs 和 npm, 然后全局安装imitator。

    npm install imitator -g
    
  2. 编写配置文件——在你的用户目录(比如我的是/User/hanan)下新建一个名为 Imitatorfile.js 的文件(这是 imitator 的默认配置文件), 内容如下。

    module.exports = function(imitator) {
        // 返回一个json
        imitator('/json', {name: 'hello world'});
    }
    
  3. 启动服务——命令行输入以下命令,启动 imitator server.

    imitator
    
  4. 浏览器访问 127.0.0.1:8888/json , 将会看到:

    {"name":"hello world"}
    

Read More

smarty 模板模板中的 assigin 函数

使用Smarty内置函数{assign},在模板运行时为模板变量赋值,也可以为数组元素赋值,和在赋值时使用一些表达式。{$var=…}是{assign}函数的简写版。该函数有三个属性(var、value和scope)和一个选项标签(nocache),其中var和value是必须使用的属性,分别用来设置要分配值的变量名和分配的值。而scope是可选属性,用来指定分配的变量范围,可以指定parent、root和global三个值,用来设定变量的有效范围。{assign}函数使用如下所示:

{assign var="name" value="brophp"}  {*为变量$name赋值上brophp的值*}
{assign "name" "brophp"}            {*这是assign函数属性的简写*}
{$name="brophp"}                    {*这是assign函数的简写,也是为变量$name赋值上brophp的值*}

在模板中声明的变量和从PHP中分配给模板中给模板的变量具有相同的使用方式。上例是在模板中声明一个$name变量三中书写方式:第一种是Smarty模板中标准函数的方式;第二种是省略属性名称简写的方式;第三种也是一种简写方法,更像是PHP变量的声明。除了上面简单声明一个变量以外,还可以为变量赋一些相对复杂的值,如使用数组和表达式,如下所示:

{*定义数组*}
{assign var=foo value=[1,2,3]}          {*为变量$foo赋上一个索引数组值*}
{assign var=foo value=['y'=>'yellow']}  {*为变量$foo赋上一个关联数组值*}
{assign var=foo value=[1,[9,8],3]}      {*可以使用嵌套声明多维数组*}
{assign var=foo value=$x+$y}            {*可以在属性中使用变量*}
{assign var="foo" value="'$foo+$bar'"}  {*可以在属性值的字符串中使用变量及表达式*}

{*短变量分配*}
{$foo=$bar+2}                       {*短变量分配值的方式*}
{$foo = strlen($bar)}               {*PHP函数在变量值中使用*}
{$foo = myfunct(($x+$y)*3)}         {*作为函数参数*}
{$foo.bar=1}                        {*数组元素赋值*}
{$foo.bar.baz=1}                    {*多维数组元素的赋值*}
{$foo[]=1}                          {*为数组添加新元素*}
{$foo[$x+3]}                        {*变量作为数组索引*}
{$foo={counter}+3}                  {*在标签里嵌套标签*}
{$foo="this is message{counter}"}   {*在引号里使用标签*}

Read More

新的 javascript Battery API

javascript Battery 接口允许你通过 javascript 来获取电池的状态。

navigator.getBattery()方法

在新的标准里,电池的状态信息是通过navigator.getBattery()方法获取的。
navigator.getBattery()是一个异步方法,会返回一个es6标准的promise对象。
所以我们获取电池状态的回调方法,必须通过该promise对象的then方法来注册:

navigator.getBattery().then(function(result) {
    console.log(result);
});

BatteryManager接口

上个例子里的result是一个表示电池状态的对象, 它是由BatteryManager接口实现的,具有如下属性:

Read More

使用 AddJS 为网页自动添加自定义javascript脚本

addJS是一个chrome插件。view in github

在浏览网站的时候,常常会想自己写js操作一下页面的内容(比如:抓图片,提取下载链接,屏蔽广告什么的)。于是,就有了这个插件。

功能:自动在指定的url的网页中添加自定义的javascript脚本并运行。

现在就开始使用AddJS

首先从chrome商店安装AddJS: https://chrome.google.com/webstore/detail/addjs/aoahkpekljaimojhfefaiibfdcnmlhdi?hl=zh-CN

或者下载安装:/assets/download/addjs.zip

至此,AddJS就可以使用了。下面是一些基本功能的使用操作方法:

Read More

命令行推送个人文档至kindle

昨天同事发了一本pdf格式的电子书,以往的方式,我都是登陆自己的qq邮箱,然后发pdf作为附件,推送到自己的kindle接收邮箱上。
突然觉得,其实nodejs应该有这样的命令行工具,可以一条命令搞定,于是在npm上搜索了一下,果然有一个叫“kindle”的包。
但是安装以后,总是推送失败,大概开了下源码,估计是作者使用的nodemailer组件做了不兼容升级导致。

于是,为了以后传文件到kindle时偷个懒,就自己动手写了一个命令行工具,名叫ksend( https://github.com/hanan198501/ksend ),简单好用。 O(∩_∩)O~

使用指南

  1. 安装

    npm install ksend -g
    
  2. 设置默认发送邮箱,格式: 邮箱地址:密码

    ksend --from yourname@qq.com:yourpassword
    
  3. 推送,如下示例,推送 a.pdf 至 hanan@kindle.cn 这个kindle接收邮箱:

    ksend -m hanan@kindle.cn a.pdf
    

    以上命令,参数 -m 表示接收邮箱。自此,完成推送。

如果脚得每次都要敲 -m 接收邮箱 麻烦,可以设置默认接收邮箱:

ksend --to hanan@kindle.cn

这样,以后只需要如下命令即可推送:

ksend a.pdf

也可以同时推送多个文档:

ksend a.pdf b.pdf ../img/photo.jpg /Users/hanan/book/ooxx.txt

查看帮助:

ksend --help

ps: 记得把发送邮箱添加到您的kindle已认可的发件人电子邮箱列表哦。

基于 postMessage 和 localStorage 的跨域本地存储方案

HTML5 的 postMessage 为解决跨域页面通信提供了一套可控的机制, 而 localStorage 则提供了易用简洁的本地存储方案?
这两者结合起来,能否实现跨域本地存储呢 ?

答案是可以的。假设有 a.com 和 b.com 两个页面。我们想通过 a 页面去修改 b 页面的本地数据。 我们需要做如下步奏:

  • 在 a 页面创建一个 iframe ,嵌入 b 页面
  • a 页面通过 postMessage 传递指定格式的消息给 b 页面
  • b 页面解析 a 页面传递过来的消息内容,调用localStorage API 操作本地数据
  • b 页面包装 localStorage 的操作结果,并通过 postMessage 传递给 a 页面
  • a 页面解析 b 页面传递回来的消息内容,得到 localStorage 的操作结果

整个过程如下图:

Read More

如何绕过chrome的弹窗拦截机制

在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的。举个例子:

var btn = $('#btn');
btn.click(function () {

    //不会被拦截
    window.open('http://cssha.com')
});

上面的代码中,window.open是用户触发的时候,是不会被拦截的,可以正常打开新窗口。再看下面这个:

var btn = $('#btn');
btn.click(function () {

    $.ajax({
        url: 'ooxx',
        success: function (url) {

            //会被拦截
            window.open(url);
        }
    })
});

上面的代码中,用户没有直接出发window.open,而是发出一个ajax请求,window.open方法被放在了ajax的回调函数里,这样的情况是会被拦截的。

那么,当用户点击按钮,发出ajax请求,如何在ajax请求完成后再打开新窗口,又不被拦截呢? 接着往下看:

var btn = $('#btn');
btn.click(function () {

    //打开一个不被拦截的新窗口
    var newWindow = window.open();

    $.ajax({
        url: 'ooxx',
        success: function (url) {

            //修改新窗口的url
            newWindow.location.href = url;
        }
    })
});

上面的代码中,用户安点击按钮的时候,先打开一个空白页,再发ajax请求,在ajax回调里面修改新窗口的.location.href,这样就不会被拦截啦啦啦啦~~~