chrome扩展应用开发教程之天气预报应用开发

上一篇讲到了chrome应用开发的基础知识,这一篇通过周末天气预报插件的开发来讲解下chrome扩展应用的开发过程。熟悉前端开发的童靴,知道了chrome应用开发的基本配置,应该很快就可以开发一个chrome应用,因为chrome插件就是纯粹的javascript和html!

开发前的准备

在开发之前需要统筹下,chrome天气预报插件的基本功能和界面,本插件,会这chrome顶部添加一个带有当前天气icon的应用,点击应用打开弹窗页面,天气的数据来自weather.com.cn的接口,所以我们需要跨域授权应用可以请求weather.com.cn的数据。于是chrome天气预报插件的manifest.json部分代码就出来了:

{
  "name": "Chrome Weather", //name
  "version": "0.1.0", //version
  "description": "weather.", //description
  "background_page": "background.html",//背景页面,应用请求数据,处理icon显示实时气温
  "browser_action": {
    "default_icon": "icon.png" ,//默认的icon
    "default_title": "Weather",//默认鼠标overtitle
    "default_popup": "popup.html"//弹窗页面
  },
    "permissions": [ "tabs", "http://*.weather.com.cn/" ]//跨域请求授权
}

先来个最终效果截图:
chrome天气预报应用最终效果截图

通过背景页面来请求天气数据

chrome天气插件用到了weather.com.cn的三个接口:

  1. 根据ip获取当前用户所在城市天气编号:http://61.4.185.48:81/g/
  2. 根据城市天气编号获取五天内天气:http://m.weather.com.cn/data/'+id+'.html
  3. 根据城市天气编号获取实时天气:http://www.weather.com.cn/data/sk/'+id+'.html
    chrome天气插件使用了jQuery开发,所以我们需要在html的头部添加jquery库:


    `

根据ip获取当前用户所在城市

当用户第一次运行chrome时候,需要请求背景页面,即background.html,这时,我们需要请求接口来获取当前用户所在的城市信息,以根据cityid获取天气情况。代码如下:

$.getScript('http://61.4.185.48:81/g/',function(){
    window.localStorage.cityid = id;//保存cityid到localStorage
    getWeather(id);//获取天气信息
});

根据cityid获取天气信息

上一步,涉及到了一个函数getWeather,这个函数的作用就是做一个xmlHttpRequest请求,获取当前cityid的天气信息,包括实时天气。getWeather中运用了send函数来做xmlHttpRequest请求。

function getWeather(id){
    var url = 'http://m.weather.com.cn/data/'+id+'.html';
    var url2 = 'http://www.weather.com.cn/data/sk/'+id+'.html';
    send(url,function(json){
        json0 = json;
        window.localStorage.json0 = JSON.stringify(json);
        doit++;
        deal();//处理json0
    }).call(this,url2,function(json){
        json1 = json;
        window.localStorage.json1 = JSON.stringify(json);
        doit++;
        deal();//处理json1
    });

}
function send(url,cb){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        var json = JSON.parse(xhr.responseText);//解析json,保证安全性
        typeof cb==='function' && json.weatherinfo && cb(json.weatherinfo);
        xhr = null;
      }
    }
    xhr.send();
    return arguments.callee;
}

chrome扩展应用开发教程之开发chrome应用基础

不得不说chrome做的真的不错,听了貘大大关于chrome插件机制的讲座,就有亲手做个chrome应用的冲动,这个周末终于抽时间做了一个简单的天气预报插件,原理还是很简单的,采用了weather.com.cn的数据,因为比较熟悉这个weather.com.cn的接口,所以很快就搞定一个简单的chrome天气预报应用了。废话不多说了,开始正文。

chrome应用开发基础知识

chrome扩展应用是由html、css和javascript组成的,所以chrome应用的门槛比较低,对于一个前端开发者,只要读懂了chrome的开发文档,就可以很快的上手一个chrome应用。

每个chrome扩展都应该包含下面的文件:

一个包括一个manifest.json文件,是个配置文件,json格式,通过manifest.json可以设置应用的icon,指定默认的background.html或者popup.html……
一个或多个html文件(除非这个应用是一个皮肤)
可选的一个或多个javascript文件
可选的任何需要的其他文件,例如图片

在开发应用(扩展)时,需要把这些文件都放到同一个目录下。发布应用(扩展)时,这个目录全部打包到一个应用(扩展)名是.crx的压缩文件中。如果使用Chrome Developer Dashboard,上传应用(扩展),可以自动生成.crx文件。

下图是我天气预报插件的文件列表:
chrome天气预报插件目录结构

chrome应用界面控制

chrome应用会以browser action或page action的形式在chrome浏览器界面上展现出来。每个应用扩展最多可以有一个browser action或page action。当应用(扩展)的图标是否显示出来是取决于单个的页面时,应当选择page action;当其它情况时可以选择browser action。

例如gmail提醒应用使用了browser action,它在工具栏上增加一个图标:

gmail应用使用browser action提示

这个新闻阅读应用也使用了browser action,当点击时会弹出一个气泡窗口popup.html:
news使用的是popup的方式

另外chrome还包括了桌面提醒(gmail新邮件提醒),主题(chrome皮肤),应用设置页面(应用设置页面),选项卡页面(新选项卡页面)等多种界面形式,本文示例使用的是browser action和popup页面来做一个简单的应用。

chrome应用基本架构

绝大多数chrome应用会包含一个背景页面(background page),用来执行chrome应用扩展的主要功能。该背景页面时再manifest.json中设置的:

"background_page": "bg.html",//默认背景页面

chrome通过background.html来实现主要功能[/caption]

上图显示了安装了应用扩展的chrome。黄色图标代表的browser action和蓝色图标代表的page action。在background.html文件里定义了browser action和javascript代码。在两个窗口里browser action都可以工作。

背景页面并不是chrome应用中唯一的页面。例如,本例天气预报插件中还包括了一个弹窗页面(popup.html),此页面也是又html页面实现的。在应用内部还可以使用chrome.tabs.create()或者window.open()来显示内部的HTML文件。

chrome应用的弹窗界面也是可以在manifest.json中设置的:

"browser_action": {
    "default_icon": "icon.png" ,
    "default_title": "Weather",
    "default_popup": "popup.html"
  },

值得一提的是chrome应用里面的HTML页面可以互相访问各自DOM树中的全部元素,或者互相调用其中的函数。

下图显示了一个chrome应用的弹窗的架构。弹窗的内容是由HTML文件(popup.html)定义的web页面。它不必复制背景页面(background.html)里的代码,因为它可以直接调用背景页面中的函数!

chrome应用的弹窗界面

应用文件的引用

任何需要的文件都可以放到应用(扩展)中,但是怎么使用它们呢?一般的说,可以像在普通的HTML文件中那样使用相对地址来引用一个文件。下面的例子演示了如何引用images子目录下的文件myimage.png

<img src="img/myimage.png" alt="">

javascript嵌套函数的效率问题

javascript自诞生以来就是一门受争议的编程语言,很多人也对javascript的语法表示不解,例如javascript嵌套函数。本文来自Nettuts+的一篇教程,详细的介绍了javascript中嵌套函数效率问题,从小处说起,一直说到匿名函数、继承,感觉不错。

嵌套函数效率

很多jser喜欢在javascript代码中使用嵌套函数,例如下面的例子就是一个典型的嵌套函数:

function foo(a, b) {
    function bar() {
        return a + b;
    }

    return bar();
}

foo(1, 2);

上面的代码中foo()中嵌入了bar(),当foo()运行的时候,就会调用bar()。javascript引擎不会创建bar()函数,直到外部引用了foo(),随着foo()的运行结束,bar()也会销毁。

当多次运行foo的时候,javascript引擎就要在每次的运行foo时创建bar函数,而每次foo结束就要销毁bar函数,这是一个很费劲的工作。

那么为什么我们不把bar函数拿出来,做为一个独立的函数,它在foo外部只被创建一次,而不是多次,这样就大大的提高了代码效率。例如下面的代码:

function foo(a, b) {
    return bar(a, b);
}

function bar(a, b) {
    return a + b;
}

foo(1, 2);

当然这样做可能随着程序的复杂性,可能存在命名冲突的危险,所以jser需要在这方面权衡,或者采用命名空间来解决这个方式。下面是在jsperf中做的关于上面两个函数大量运行的速度测试http://jsperf.com/nested-named-functions。不同的浏览器测试的结果不同,但是总体来看,两个独立的函数要比相互嵌套的javascript函数效率提高10%~90%。

HTMl5的sessionStorage和localStorage

html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage。

sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。

localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。

web storage和cookie的区别

Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可以跨域调用。

除此之外,Web Storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie。

但是Cookie也是不可以或缺的:Cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生(来自@otakustay 的纠正)

html5 web storage的浏览器支持情况

浏览器的支持除了IE7及以下不支持外,其他标准浏览器都完全支持(ie及FF需在web服务器里运行),值得一提的是IE总是办好事,例如IE7、IE6中的UserData其实就是javascript本地存储的解决方案。通过简单的代码封装可以统一到所有的浏览器都支持web storage。

要判断浏览器是否支持localStorage可以使用下面的代码:

if(window.localStorage){
    alert("浏览支持localStorage")
}else{
   alert("浏览暂不支持localStorage")
}
//或者
if(typeof window.localStorage == 'undefined'){
    alert("浏览暂不支持localStorage")
}

localStorage和sessionStorage操作

localStorage和sessionStorage都具有相同的操作方法,例如setItem、getItem和removeItem等

localStorage和sessionStorage的方法

setItem存储value

用途:将value存储到key字段
用法:.setItem( key, value)

代码示例:

    sessionStorage.setItem("key", "value");
    localStorage.setItem("site", "js8.in");

基于express+socket.io的nodejs聊天室

前几天晚上边看水浒边写的nodejs的聊天室,前面说了,放假之前要把近日学习nodejs的所有心得整理下,今天就是30号鸟~撒欢~,最后放这个聊天室出来给大家作为学习nodejs的参考示例,希望对大家有用。

感谢:cnodejs群里的老雷,及其微博上的基友们!顺祝大家长假快乐,顺祝自己明天动车不出轨,顺祝明年不再过节,感慨多了……

特点

聊天室主要功能及其特点:

  1. 采用nodejs(屁话)
  2. express框架,jade做模板
  3. socket.io做前后端的websocket通信
  4. 支持session
  5. 支持@私信功能
    废话不多说了,注意点,基本前面的文章都提到了,下面罗列下:
    配置nodejs.exe的windows目录结构
    安装express及配置app.js文件
    使用socket.io和node.js搭建websocket应用
    在Express和Socket.IO中使用session

nodejs聊天室下载地址

基于express+socket.io的聊天室

聊天室服务器端js代码

在Express和Socket.IO中使用session

在nodejs项目中对于一些认证需要用到session,例如我写的nodejs 聊天室的demo,就是通过session实现的认证。当存在session,直接进入聊天室,而不会重新登录。

在网上也找到不少关于Express框架中的session调用方法,可是发现真正能用的不是很多,今天根据聊天室的制作过程,整理下Express和socket.IO中使用session的具体方法。

Express的session是通过cookie实现的,用到了connect中的两个module:parseCookie和MemoryStore,前者是用来解析cookie,后者用来存储sesion。

引入所需module

Express框架中使用session必须先引入上面的两个模块,例如下面的代码:

var parseCookie = require('connect').utils.parseCookie,
    MemoryStore = require('connect/middleware/session/memory');
//建立一个memory store的实例
var storeMemory = new MemoryStore({
        reapInterval: 60000 * 10
    });

Express中app的配置

在app需要添加如下的配置:

app.configure(function(){
    app.use(express.bodyParser());//解析post
    app.use(express.cookieParser());//解析cookie
    //设置session
    app.use(express.session({
        secret: 'wyq',
        store:storeMemory
    }));
});

在请求中使用session

在请求中我们可以使用request.session来调用session,例如下面的代码:

app.get('/',function(req,res){
    //使用request.session来判断是否登录
    if( req.session.name && req.session.name!==''){
        //需要判断下是否已经登录
        res.redirect('/chat');
    }else{
        //读取登录页面,要求登录
        var realpath = __dirname + '/views/' + url.parse('login.html').pathname;
        var txt = fs.readFileSync(realpath);
        res.end(txt);
    }
});

websocket通信中使用session

在nodejs项目中,我们常常使用websockt来实现通信,所以websocket中也需要通过session来认证用户。本例使用socket.io来举例实现nodejs中websocket通信session的认证。关于socket.io的使用参考文章《使用socket.io和node.js搭建websocket应用

上面代码中引入了解析cookie的parseCookie,所以session是通过cookie来解析的。首先我们建立了socket的监听之后需要对监听到的头文件处理,解析出来cookie中的session。例如下面的代码:

var io = sio.listen(app);
//设置session
io.set('authorization', function(handshakeData, callback){
    // 通过客户端的cookie字符串来获取其session数据
    handshakeData.cookie = parseCookie(handshakeData.headers.cookie)
    var connect_sid = handshakeData.cookie['connect.sid'];
    if (connect_sid) {
        storeMemory.get(connect_sid, function(error, session){
            if (error) {
                // if we cannot grab a session, turn down the connection
                callback(error.message, false);
            }
            else {
                // save the session data and accept the connection
                handshakeData.session = session;
                callback(null, true);
            }
        });
    }
    else {
        callback('nosession');
    }
});

使用socket.io和node.js搭建websocket应用

websocket是HTML5的一种新的通信协议,它是实现了浏览器与服务器的双向通讯。在 WebSocket API 中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
关于websocket的更多信息,请移步维基百科

使用WebSocket

在客户端使用websocket需要创建WebSocket对象,通过提供的open、send、message、close等方法实现创建、发送、监听信息、关闭连接。例如下面的代码:

if('WebSocket' in window){
    // 创建websocket实例
    var socket = new WebSocket('ws://localhost:8080');
    //打开
    socket.onopen = function(event) {
      // 发送
      socket.send('I am the client and I\'m listening!');
      // 监听
      socket.onmessage = function(event) {
        console.log('Client received a message',event);
      };
      // 关闭监听
      socket.onclose = function(event) {
        console.log('Client notified socket has closed',event);
      };
      // 关闭
      //socket.close()
    };
}else{
    alert('本浏览器不支持WebSocket哦~');
}

现在chrome、firefox等浏览器都已经支持了websocket,而IE却没有。下面我们来简单说说服务器端对websocket的支持。

服务器端支持websocket的语言不少,而且都有相关的开源项目,例如php的phpwebsockets:http://code.google.com/p/phpwebsockets/,java的jWebsocket:http://jwebsocket.org/
更多的信息可以浏览这篇文章:Start Using HTML5 WebSockets Today

socket.io

socket.IO是一个websocket库,包括了客户端的js和服务器端的nodejs。官方地址:http://socket.io

客户端使用socket.io

去github clone socket.io的最新版本,或者直接饮用使用socket.io的CDN服务:

<script src="http://cdn.socket.io/stable/socket.io.js"></script>

下面可以创建使用socket.io库来创建客户端js代码了:

var socket = io.connect('http://localhost');
socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
});

socket.on是监听,收到服务器端发来的news的内容,则运行function,其中data就是请求回来的数据,socket.emit是发送消息给服务器端的方法。

使用socket.io和nodejs搭建websocket服务器端

socket.io不仅可以搭建客户端的websocket服务,而且支持nodejs服务器端的websocket。

nodejs安装socket.io

使用node插件管理包,运行下面的命令就可以安装成功socket.io

npm install socket.io

没有npm的或者windows用户可以使用github下载socket.io并且放入到node_modules文件夹中,具体配置可以参考文章:《nodejs教程:配置nodejs.exe的windows目录结构》

nodejs建立socket.io服务

通过nodejs的http模块就可以方便的搭建websocket服务器环境,例如下面的代码:

// 引入需要的模块:http和socket.io
var http = require('http'), io = require('socket.io');
//创建server
var server = http.createServer(function(req, res){
  // Send HTML headers and message
  res.writeHead(200,{ 'Content-Type': 'text/html' });
  res.end('

# Hello Socket Lover!
');
});
//端口8000
server.listen(8080);
//创建socket
var socket = io.listen(server);
//添加连接监听
socket.on('connection', function(client){
    //连接成功则执行下面的监听
    client.on('message',function(event){
        console.log('Received message from client!',event);
    });
    //断开连接callback
    client.on('disconnect',function(){
        console.log('Server has disconnected');
    });
});

nodejs教程:安装express及配置app.js文件

express.js是nodejs的一个MVC开发框架,并且支持jade等多种模板。下面简单来说说express的安装和app.js文件的配置,然后在今后的教程中一步一步使用express.js搭建个聊天室。

安装express.js

如果你安装了npm,安装变得很简单,只需要在终端中运行下面的代码即可:

npm install express -gd
-g代表安装到NODE_PATH的lib里面,而-d代表把相依性套件也一起安装。如果沒有-g的话会安装目前所在的目录(会建立一个node_modules的文件夹),你可以透过以下指令来比较两者的不同:

npm list -g
npm list

如果没有npm,那么我可以使用github来git下来最新的express。
好了,现在你可以通过express testapp来建立express实例。以下是示例:

cd ~
express testapp
cd testapp
node app.js
这样就建立了一个testapp的nodejs应用,而app.js是默认的应用主js。下面来详细的说说app.js中的各项配置。

引入模块

var express = require('express');
 var app = module.exports = express.createServer();

require()是node.js提供的函数,可以让你引入其他模块以调用模块的函数和变量,默认下node.js会在$NODE_PATH和目前js所在目录下的node_modules文件夹下去寻找模块。require也可以用来载入自己写的模块哦~这样涉及到node.js的模块机制,后面有机会就在介绍。

第二行的express.createServer()就是在建立server,而中间的module.exports也是涉及到node.js的模块机制,以后再说。

express的app.js的详细配置说明

express.js继承自connect模块,所以如果你的node_modules文件夹下没有connect模块也是不行的。

设置views路径和模板

我们再来看下面两行:

app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

使用html5 postMessage和window.name实现多浏览器跨域

跨域是个“很古老”的问题,因为浏览器的同源策略,导致不同域名下不能进行跨域名请求数据,虽然这样设计安全了很多,但是对于大型的网站同时维护多个域名就需要进行跨域操作。例如:微博开放平台的jssdk实现的跨域请求数据,再例如weibo.com和sina.com的同步登录。

之前我也说过不少跨域的方式了,有结合服务器端的,有纯粹javascript实现的跨域,例如:《利用跨域资源共享(CORS)实现ajax跨域调用》、《用document.domain+iframe实现Ajax跨子域》、《通过JSONP实现完美跨域》。在我的上一篇文章《javascript入门到高级PPT》中也提到了跨域。
当然也有不少关于跨域的文章,例如口碑UED的文章《跨域资源共享的10种方式》等等。大家可以去阅读一下。

今天我说的html5 postMessage和window.name也不是一种新的跨域方式,因为有不少人写文章写了,而实际应用的我不知道有哪些人?不过新浪微博的新旧两个jssdk都是采用这种方式,包括之前的人人网的xd.html,当然现在的人人和facebook都是通过flash实现的跨域,这不是今天说的内容。

html5 postMessage实现跨域

postMessage是html5的一个新功能,可以实现不同域名之间的通信,通过给postMessage方式发送数据,监听则通过在父子窗口添加onmessage事件进行。
缺点也就很明显了,只有支持html5的浏览器才支持这种跨域方式,像IE6、7当然就拒之门外了!

window.name实现跨域

window.name实现跨域也是一个比较老的问题,之前kejun写过一个demo,可是给的却是同域名的通信。
其实kejun的实例中就是实现跨域的,不过他采用了同一个域名,而且过程比较崎岖:

  1. 建立iframe,指定src为被跨域的页面
  2. 被跨域文件修改window.name,将数据传给window.name
  3. 将iframe.src修改为本域代理文件,然后就可以取到contentWindow.name
  4. 进行处理数据,清除iframe
    充分的运用了window.name因为页面的url改变而name不改变的特性。
    但是如果我们是自己用,还是可以的,而如果我们放出去要别人使用我们写的东西,那样学习成本太大。

多浏览器双向跨域

为了解决上面的问题,我们使用的方法就是如果支持postMessage的浏览器就使用postMessage,如果不支持的就采用window.name的方式,幸运的是在IE6、7中支持跨域设置window.name,而我们就可以简单的通过window.name来跨域。然后建立计时器来监听window.name是否发生了变化,如果变化则接收并分析window.name,然后做请求。