HTML5之WebSocket介绍

在HTML5规范中,有一个API正迅速变得流行起来:WebSocket API。WebSocket提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术。这个新的API提供了一个方法,从客户端使用简单的语法有效地推动消息到服务器。让我们看一看HTML5的WebSocket API:它可用于客户端、服务器端。而且有一个优秀的第三方API,名为Socket.IO。

一、什么是WebSocket API?

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

Ajax技术很聪明的一点是没有设计要使用的方式。WebSocket为指定目标创建,用于双向推送消息。

二、WebSocket API的用法

只专注于客户端的API,因为每个服务器端语言有自己的API。下面的代码片段是打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。

// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');
// 打开Socket 
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的关闭
  socket.onclose = function(event) {
    console.log('Client notified socket has closed',event);
  };
  // 关闭Socket.... 
  //socket.close() 
};

让我们来看看上面的初始化片段。参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。

onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。WebSocket的语法非常简单,使用WebSockets是难以置信的容易,除非客户端不支持WebSocket。IE浏览器目前不支持WebSocket通信。如果你的客户端不支持WebSocket通信,下面有几个后备方案供你使用:

  • Flash技术 —— Flash可以提供一个简单的替换。 使用Flash最明显的缺点是并非所有客户端都安装了Flash,而且某些客户端,如iPhone/iPad,不支持Flash。
  • AJAX Long-Polling技术 —— 用AJAX的long-polling来模拟WebSocket在业界已经有一段时间了。它是一个可行的技术,但它不能优化发送的信息。也就是说,它是一个解决方案,但不是最佳的技术方案。
    由于目前的IE等浏览器不支持WebSocket,要提供WebSocket的事件处理、返回传输、在服务器端使用一个统一的API,那么该怎么办呢?幸运的是,Guillermo Rauch创建了一个Socket.IO技术。

三、带Socket.IO的WebSocket

Socket.IO是Guillermo Rauch创建的WebSocket API,Guillermo Rauch是LearnBoost公司的首席技术官以及LearnBoost实验室的首席科学家。Socket.IO使用检测功能来判断是否建立WebSocket连接,或者是AJAX long-polling连接,或Flash等。可快速创建实时的应用程序。Socket.IO还提供了一个NodeJS API,它看起来非常像客户端API。

建立客户端Socket.IO

Socket.IO可以从GitHub下载,可以把socket.io.js文件包含到页面中:

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

此时,Socket.IO在此页面上是有效的,是时候创建Socket了:

// 创建Socket.IO实例,建立连接
var socket= new io.Socket('localhost',{
  port: 8080
});
socket.connect();
// 添加一个连接监听器
socket.on('connect',function() {
  console.log('Client has connected to the server!');
});
// 添加一个连接监听器
socket.on('message',function(data) {
  console.log('Received a message from the server!',data);
});
// 添加一个关闭连接的监听器
socket.on('disconnect',function() {
  console.log('The client has disconnected!');
});
// 通过Socket发送一条消息到服务器
function sendMessageToServer(message) {
  socket.send(message);
}

Socket.IO简化了WebSocket API,统一了返回运输的API。传输包括:

  • WebSocket

  • Flash Socket

  • AJAX long-polling

  • AJAX multipart streaming

  • IFrame

  • JSONP polling
    你还可以设置任意的Socket.IO构造器的第二个选项,选项包括:

  • port - 待连接的端口

  • transports - 一个数组,包含不同的传输类型

  • transportOptions - 传输的参数使用的对象,带附加属性
    Socket.IO还提供了由本地WebSocket API提供的普通连接、断开连接、消息事件。Socket还提供了封装每个事件类型的方法。

四、NodeJS和Socket.IO联合开发

Socket.IO提供的服务器端解决方案,允许统一的客户端和服务器端的API。使用Node,你可以创建一个典型的HTTP服务器,然后把服务器的实例传递到Socket.IO。从这里,你创建连接、断开连接、建立消息监听器,跟在客户端一样。
一个简单的服务器端脚本看起来如下:

// 需要HTTP 模块来启动服务器和Socket.IO
var http= require('http'), io= require('socket.io');
// 在8080端口启动服务器
var server= http.createServer(function(req, res){
  // 发送HTML的headers和message
  res.writeHead(200,{ 'Content-Type': 'text/html' });
  res.end('<h1>Hello Socket Lover!</h1>');
});
server.listen(8080);
// 创建一个Socket.IO实例,把它传递给服务器
var socket= io.listen(server);
// 添加一个连接监听器
socket.on('connection', function(client){
  // 成功!现在开始监听接收到的消息
  client.on('message',function(event){
    console.log('Received message from client!',event);
  });
  client.on('disconnect',function(){
    clearInterval(interval);
    console.log('Server has disconnected');
  });
});

 

你可以运行服务器部分,假定已安装了NodeJS,从命令行执行:

node socket-server.js

 

现在客户端和服务器都能来回推送消息了!在NodeJS脚本内,可以使用简单的JavaScript创建一个定期消息发送器:

// 创建一个定期(每5秒)发送消息到客户端的发送器
var interval= setInterval(function() {
  client.send('This is a message from the server! ' + new Date().getTime());
},5000);

 

服务器端将会每5秒推送消息到客户端!

五、dojox.Socket和Socket.IO

Persevere的创建者Kris Zyp创建了dojox.Socket。dojox.Socket以Dojo库一致的方式封装了WebSocket API,用于在客户端不支持WebSocket时,使用long-polling替代。
下面是怎样在客户端使用dojox.Socket和在服务器端使用Socket.IO的例子:

var args, ws= typeof WebSocket!= 'undefined';
var socket= dojox.socket(args= {
  url: ws? '/socket.io/websocket' : '/socket.io/xhr-polling',
  headers:{
    'Content-Type':'application/x-www-urlencoded'
  },
  transport: function(args, message){
    args.content = message; // use URL-encoding to send the message instead of a raw body 
    dojo.xhrPost(args);
  };
});
var sessionId;
socket.on('message', function(){
  if (!sessionId){
    sessionId= message;
    args.url += '/' + sessionId;
  }else if(message.substr(0, 3) == '~h~'){
   // a heartbeat 
  }
});

 

dojox.socket.Reconnect还创建了在套接字失去连接时自动重连。期待包含dojox.Socket的Dojo 1.6版本早日发布。

六、实际应用和WebSocket资源

有很多WebSocke的实际应用。WebSocket对于大多数客户机-服务器的异步通信是理想的,在浏览器内聊天是最突出的应用。WebSocket由于其高效率,被大多数公司所使用。

Browser Storage for HTML5 Apps

For years one of the main advantages of writing desktop applications has been the easy access to local storage on the client machine. Web developers have put up with the inability to store and retrieve data from the client’s machine for a long time, but it looks like that may change soon. You might even think it already has after reading this article. Yes, I’m going to discuss the origins of persisting data on clients machine and then introduce you to the Web Storage standard.

Most web developers know that the only kind of local storage we could expect from a web browser comes in the form of cookies. Well, not entirely. The desire to store data on the client machine is not a new concept and it wasn’t conceived while creating HTML5 specifications. What is even more surprising is that a working implementation was developed by Microsoft as part of the IE6 feature set. They called it userData and it essentially guaranteed at least 640KB of local space per domain depending on the IE security policies set by the user. That may seem like very little space by today’s standard, but when we compare it to the maximum 4KB space available to us by Cookies, the improvement is appreciable.

What’s Wrong With Cookies?

A number of things. The foremost problem with Cookies is that they are sent back and forth between browser and server with every HTTP request. This isn’t a desirable behaviour because more often than not developers do not wish to transmit local data to the server more than once, if once at all. Cookies give developer no choice.

As we said previously, Cookies can only store up to 4KB of data. It’s not a lot of data, nevertheless 4KB is enough data to noticeably slow down page requests.

Also, Cookies are passed back and forth between the client and server in clear text. Therefore, the only way to protect them is by encrypting the data while communicating with the backend server using SSL (Secure Socket Layer). However, most websites on the internet don’t use SSL, which leaves storage open to eavesdropping.

There are other issues that make Cookies less useful. Ideally, developers wish to have the ability to persist large amounts of data on the client machine and not have to transmit that data to the server over and over again.

What Are The Alternative Solutions?

So far we have not discussed non-standard workarounds for persisting data on the client machine. When Adobe (then known as Macromedia) developers were releasing Flash Player 6, they too had to address the same problem. In 2002, Flash Player 6 introduced a new feature called Local Shared Object or more often known as Flash Cookies to effectively introduce the same capabilities as standard HTTP Cookies to Flash movies and sites. Local Shared Object allowed developers to persist up 100KB of data onto the client machine by default.

The second solution is Googleís implementation of Local Storage as part of the Gears plugin for Web Browsers. Gears was (and I tell you why I use was in a moment) the combination of multiple missing and useful features needed for developing Rich Internet Applications (RIA). Gears’ Local Storage was based on the less popular Web SQL specification that took advantage of SQLite. You guessed it right, Gears gave developers a full blown SQL database for persisting an unlimited amount of data on the client machine.

The developers of Ajax Massive Storage System (AMASS) took this opportunity and developed a third party JavaScript library that made it possible for standard HTML sites to take advantage of the Local Shared Object feature in Flash or the Gears plugin in order to persist data on the client machine. Also, DojoJavaScript Library is capable of detecting the availability of Local Storage mechanics (e.g. Google Gears, Local Shared Object. etc.) and provides a unified interface for persisting data across different Web Browsers.

Back to why I said Gears ‘was’ instead of ‘still is’: that’s because Google recently announced that they will be dropping further development of the Gears plugin in favor of HTML5 and the Web Storage specification presented in this tutorial.

HTML5 and Web Storage Specifications

Now that the history lesson is over, we are going to learn about Web Storage and dive into some code to better understand it. The easiest way to describe Web Storage is the ability to persist data on the client machine in the form of one key for one value. This is very similar to how associative arrays are used:

{ "The Key" : "The Value" }

Local Storage is designed to be natively supported by Web Browsers. This means no more third party libraries and messing with Flash. Surprisingly, Web Storage has been one of the more successful specifications in terms of adoption by modern browsers. In fact, almost all modern browsers support Web Storage, including:

  • Internet Explorer 8+
  • Firefox 3.5+
  • Safari 4+
  • Opera 10.5+
  • iPhone Safari
  • Android Web Browser
    While Web Storage aims to provide functionality similar to Cookies, it has been further refined to not carry any of their negative attributes. For instance, Web Storage allows for persisting up to 5MB of data, a significant increase in space compared to how much data can be stored in a Cookie. Also, persisting data using Web Storage will not result in sending that data to the server backend with every page request. This significantly increases performance. According to the Web Storage specification, Web Browsers only expire the persisted data from the local machine when requested to do so by the user and will always avoid deleting data while a script that could access that data is running.

Web Browsers expose Web Storage through the localStorage object in JavaScript. One easy way to determine whether a Web Browser can support Web Storage is to execute this JavaScript code:

var webStorageSupported = ('localStorage' in window) && window['localStorage'] !== null;

According to the W3C’s specification for Web Storage, the localStorage object implements the following set of methods and properties from the Storage interface. Let’s examine each of these methods and properties to find out what they do and how they can be used:

interface Storage {  
    readonly long <strong>length</strong>;  
    void <strong>setItem(String key, Object data)</strong>;  
    Object <strong>getItem(String key)</strong>;  
    void <strong>removeItem(String key)</strong>;  
    void <strong>clear()</strong>;  
    String <strong>key(long index)</strong>;  
};

The length property is very useful. It will return the number of key/value pairs currently saved to Local Storage under the currently accessed domain:

alert(localStorage.length);

If no key/value pairs have previously been saved in the local storage, then the above script will display an alert window with “0″ as the message, otherwise the message will be the number of persisted key/value pairs.

The setItem(key, value) method simply saves a new entry on the local machine. To save the key _name_with the value arman we could execute this script:

localStorage.setItem('name', 'arman');

To ensure that key name was truly saved to the local storage with the value arman we need to use thegetItem(key) method. The getItem method simply accepts a key and searches the local storage to find a matching key and then returns its value.

localStorage.setItem('name', 'arman');  
var value = localStorage.getItem('name');  
alert(value);

If you run the above script you should see an alert window containing the word arman appear on the screen, confirming that we’ve successfully saved a new key/value pair to the local storage. Since the localStorageobject behaves similar to associative arrays, we could simplify the above script to look like this and it will still function just the same:

localStorage['name'] = 'arman';  
var value = localStorage['name'];  
alert(value);

Let’s look at the removeItem(key) method. This method is designed to remove a previously saved key/value pair from the local storage. If the key does not exist, this method simply does nothing. The following code sample demonstrates the use of the removeItem method:

localStorage.setItem('name', 'arman');  
localStorage.removeItem('name');  
var value = localStorage.getItem('name');  
alert(value);

When the above script is executed, you should see an alert window with the value null in the alert box. Using the name key, the above script simply creates a new key/value pair and immediately removes it from the local storage. Subsequently, a null value is returned when accessing the local storage with the same _name_key.

There will come occasions where there will be a need to completely clear the local storage and start with a clean slate. The clear() method is designed exactly for that purpose. This method automatically empties all the previously saved key/value pairs from the local storage. If there are no entries then nothing will change.

localStorage.setItem('name', 'arman');  
localStorage.setItem('name', 'smith');  
localStorage.setItem('name', 'frank');   

alert(localStorage.length);  

localStorage.clear();  

alert(localStorage.length);

Although the above script creates three new key/value pairs (as evidenced by the first alert), the call to the clear() method removes all the entries. Subsequently, the second alert window will display a “0″ message.

The final method we need to look at is the key(index) method. This method will retrieve the name of a key based on the index parameter. localStorage maintains a 0 based list of all entries within itself. Therefore to access the first key from the local storage, we need to use 0 as the index as illustrated in this script:

localStorage.clear();  
localStorage.setItem('age', 5);  
alert(localStorage.key(0));

When the above script is executed it should display an alert window with the message “age”. Note how in the above example the first line of code clears the local storage. This is to ensure we begin with a clean slate. Another useful application of the key() method is in conjunction with the length property. For instance to get all the key/value pairs from the local storage without knowing the keys in advance, we could write a script like the following:

localStorage.clear();  

localStorage.setItem("title", "Mr.");  
localStorage.setItem("fullname", "Aaron Darwin");  
localStorage.setItem("age", 17);  
localStorage.setItem("height", 182.5);   

for(var i = 0; i < localStorage.length; i++)  
{  
    var keyName = localStorage.key(i);  
    var value = localStorage.getItem(keyName);  

    alert(keyName + " = " + value);  
}  
In the above s

In the above script, our code first clears and then adds four new key/value pairs to the local storage. Then it uses the length property in a For loop to work out the key for each key/value pair. On every iteration of the loop the key is assigned to the keyName variable which is then passed to the getItem() method to retrieve its value.

The Subtleties

When accessing data using a key that doesn’t exist in the local storage, instead of an exception, a null value is always returned. This makes it difficult to know if the value of the key is null or the key simply doesn’t exist in the local storage.

The second one to talk about is the setItem(key, value) method. We know that we can pass any type of value to the setItem() method for the value parameter, but that isn’t entirely true. In JavaScript, we are able to create Image objects. However, the current implementation of Web Storage only allows persisting primitive types such as String, Boolean, Integer and Float types. Therefore, we can expect the following script to throw an exception and crash:

var imageObject = new Image("http://www.google.com/logo.gif");  
localStorage.setItem("image", imageObject);

Additionally, even though we can save Boolean, Integer and Float types, the underlying representation of these types falls back onto the String type. This means regardless of the type of value passed to thesetItem() method, the getItem() method will always return a value that is of type String. Let’s look at an example script:

var integerVariable = 34;  
localStorage.setItem("age", integerVariable);  
var newVariable = localStorage.getItem("age");  

alert(typeof(newVariable) == "number");

In the above script, ultimately the value of the integerVariable is saved as String. Therefore when we inspect the type of newVariable after getting its value from the local storage, it’s no longer an Integer type and therefore the comparison statement will evaluate to false. In such situations, we must use the handyparseInt(String) and parseFloat(String) functions for conversion. There is NO function to parseBoolean data type so we must simply compare the retrieved value with “true” or “false” strings.

Finally, to get the comparison statement in the above script to evaluate to true, we must modify our code to use the parseInt() function as shown in the following script:

var integerVariable = 34;  
localStorage.setItem("age", integerVariable);  
var newVariable = parseInt(localStorage.getItem("age"));  

alert(typeof(newVariable) == "number");

Now, when the above script is executed, the type of the value stored in the newVariable will be Integer. Subsequently, the comparison statement will evaluate to true.

When the persisting data reaches the 5MB quota, Web Browsers do not provide the ability to ask for more space in the local storage. They instead throw the QUOTA_EXCEEDED_ERR exception to notify the script that there is no more space. The simplest way to handle this exception is to catch the exception and notify the user of the problem.

Alternatively when catching the exception, the script can delete some key/value entries from the local storage to clear up space for new pairs.

Lastly, when the document is requested directly from the disk and not from a web server, the local storage will be cleared every time you navigate away from the page.

Web Storage Events

Web Storage Standard allows scripts to be notified when other parts of the code add, update or delete entries from the local storage. Thus, whenever something changes in the local storage, a Storage Event is going to fire. However, in the case of deleting an entry using a non existing key, no event will be fired. This is because nothing will change in the local storage. Unfortunately, my tests proved that currently Webkit browsers (such as Safari and Chrome) do not fire the Storage Events as described by the Web Storage specification. Internet Explorer, Firefox and Opera browsers do behave as expected.

A local storage event cannot be cancelled. Its sole purpose is to notify the user code of a change that has happened inside the local storage.

The following script shows you how to listen for changes in the local storage, by registering an event handler which will be called every time a Storage Event is fired:

if (window.addEventListener) {  
    window.addEventListener("storage", handleStorageChange, false);  
}  
else  
{  
    window.attachEvent("onstorage", handleStorageChange);  
}  

function handleStorageChange(event)  
{  
    alert("Something was changed in the local storage");  
}

Since no version of Internet Explorer (except for version 9 public preview) supports the DOM Level 2 event handling system, the attacheEvent() method has to be used to register event handlers.

The event argument is useful because it carries information about changes in the local storage. It adheres to the StorageEvent interface:

interface StorageEvent : Event {  
    readonly String <strong>key</strong>;  
    readonly Object <strong>oldValue</strong>;  
    readonly object <strong>newValue</strong>;  
    readonly String <strong>url</strong>;  
};

The key property identifies which key/value pair was changed. The oldValue and the newValue properties behave as their names suggest. That is, they respectively contain the previous and the new value for the key.

Finally, the url property contains the address of the document for which the local storage was just modified. However, due to multiple implementation iterations of the Web Storage specification, in some Web Browsers the url property may be implemented as uri. Therefore it’s better to first check the url property and thenuri in case the url property is undefined.

Demo

After reading this much, I usually like to see an example before I believe it all. Just like myself, I’m sure many of you would also like to see a working example. That’s no problem, because I’ve prepared a small script that simply demonstrates the use of the web storage standard in a working application.

It’s made such that it can be also be tested using iPhone Safari and the Android Web Browser. The idea is to have the browser remember which boxes have been opened before using the local storage. I’ll leave you with this code to play with:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta name="viewport" content="width=240,user-scalable=no" />  
        <style>  
            * { padding: 0px; margin: 0px; font-family: "Lucida Sans", "Tahoma", san-serif, arial; }  
            body { background: #333; }  
            h1 {  font-size: 20px;  }  
            p { padding: 5px; margin-bottom: 10px; font-size: 12px; }  
            button { padding: 4px; margin-top: 10px; }  
            #wrap { width: 240px; background: #FFF; margin: 0px auto; padding: 10px; }  
            #blocks .unchecked, a { height: 40px; width: 40px; margin: 4px; display: inline-block; cursor: pointer; text-align: center; line-height: 40px; }  
            #blocks .unchecked { background: #000; }  
            #blocks .unchecked:hover { background: #333; }  
        </style>  

        <script>  
            var colorTable = {  
                "0" : "blue",  
                "1" : "red",  
                "2" : "green",  
                "3" : "orange",  
                "4" : "purple",  
                "5" : "brown",  
                "6" : "gold",  
                "7" : "lime",  
                "8" : "lightblue",  
                "9" : "yellow"  
            };  

            function initializeGame()  
            {  
                if (browserSupportsLocalStorage() == false)  
                {  
                    alert("This browser doesn't support Web Storage standard");  
                    return;  
                }  

                var containerDiv = document.getElementById("blocks");  

                for (var i = 0; i < 10; i++)  
                {  
                    var iid           = i.toString();  
                    var anchor       = document.createElement("a");  
                    anchor.id        = id;  
                    anchor.innerHTML = i + 1;  

                    if (localStorage.getItem(id) != null)  
                    {  
                        anchor.style.backgroundColor = colorTable[id];  
                    }  
                    else  
                    {  
                        anchor.className = "unchecked";  

                        if (anchor.addEventListener)  
                        {  
                           anchor.addEventListener("click", handleBoxClick, false);  
                        }  
                        else  
                        {  
                           anchor.attachEvent("onclick", handleBoxClick);  
                        }  
                    }  

                    containerDiv.appendChild(anchor);  
                }  
            }  

            function handleBoxClick(e)  
            {  
                var target = (e.target) ? e.target : e.srcElement;  

                if (target.className == "")  
                    return;  

                var id = target.id;  
                var color = colorTable[id]  
                target.className = "";  
                target.style.backgroundColor = colorTable[id];  

                localStorage.setItem(id, color);  
            }  

            function resetGame()  
            {  
                var containerDiv = document.getElementById("blocks");  
                containerDiv.innerHTML = "";  
                localStorage.clear();  
                initializeGame();  
            }  

            function browserSupportsLocalStorage()  
            {  
                return ('localStorage' in window) && (window['localStorage'] != null);  
            }  
        </script>  
    </head>  
    <body onload="initializeGame();">  
        <div id="wrap">  
            <h1>Web Storage Demo</h1>  
            <p>  
                This page was design to simply demonstrate the use of Web Storage in modern browsers. In this example boxes  
                that haven't yet been clicked remain black. Once you click a black box, it's real color will be revealed.  
                The browser however will remember which boxes are clicked, even when you navigate away from this page.  
                Go on, give it a try.  
            </p>  
            <div id="blocks">  
            </div>  
            <button onclick="resetGame()">Reset Game</button>  
            <button onclick="document.location.reload(true);">Refresh Page</button>  
        </div>  
    </body>  
</html>

 

Link From: http://mobile.tutsplus.com/tutorials/mobile-web-apps/browser-storage/

WordPress开启Gzip压缩的四种方法

WordPress开启gzip压缩的方法有四种,下面一一讲解其使用方法:

方法一、修改index.php

在根目录 index.php 中找到:

define(’WP_USE_THEMES’, true);

在后面加上:

ob_start(‘ob_gzhandler’);

这种方法最简单,推荐使用,不过这种方法有个弊端,就是当wordpress系统升级后有得重新设置。

方法二、使用.htaccess

2. 如果服务器支持.htaccess文件,那么在wordpress根目录的.htaccess中添加以下代码然后上传到wordpress根目录即可,实现gzip压缩代码共有两种,详细代码如下如下,添加好之后上传到wordpress根目录来测试:

第一种:

php_value output_handler ob_gzhandler

第二种:

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/gif A2592000
ExpiresByType image/jpeg A2592000
ExpiresByType image/png A2592000
ExpiresByType image/x-icon A2592000
ExpiresByType application/x-javascript A604800
ExpiresByType text/css A604800
</IfModule>

<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE text/html text/css image/gif image/jpeg image/png application/x-javascript
</IfModule>

3. 通过php.ini 启用Gzip

如果服务器支持php.ini重定义的话,可以在php.ini中增加以下代码来开启gzip。

zlib.output_compression_level = number #(number为1—9的值,1的压缩比率最低, 建议取 5) 

4. 安装wordpress gzip插件

插件名称:gzippy
下载地址:http://wordpress.org/extend/plugins/gzippy/

需要注意的是如果在.htaccess文件中禁止了gzip功能的话,那在php.ini文件中开启后也需在.htaccess文件中开启 gzip,否则没有效果。开启wordpress的gzip功能后,服务器在页面传输前将页面压缩后传送,可以有效减少服务器的带宽使用,也可以加速页 面下载。另外请注意只能选择一种方式来开启wordpress gzip压缩,否则有可能造成冲突,请大家谨记。

设置好之后大家可以到http://tool.chinaz.com/Gzips/进行测试看看wordpress gzip压缩有没有起作用。

T4MVC介绍文档

英文地址:http://mvccontrib.codeplex.com/wikipage?title=T4MVC_doc&referringTitle=T4MVC

以下是翻译的文档,发现不正确或翻译不到位之处,欢迎指出。

1、介绍

T4MVC是为ASP.NET MVC应用程序创建的强类型T4模板,主要用于消除在使用Controllers、Actions和Views时的字符串。它有助于使你的MVC代码更容易维护,并且能给你在普通状态下输入代码不能提供的智能提示。T4MVC最初是David Ebbo在他博客文章上发表出来的,之后社区的反馈使其增添了许多新的功能。

T4MVC在Visual Studio 2008 SP1和Visual Studio 2010中均可以运行,并同时支持ASP.NET MVC 1.0和2.0。

只需简单地下载zip文件并且将T4MVC.tt和T4MVC.tt.settings.t4同时复制到您的MVC应用程序的根目录下。您应该可以马上看到在T4MVC.tt的作用下Visual Studio产生了一些文件。

这些生成的文件作用于整个应用程序,也就是这些脚本可以实现字符串的强类型替换。下面的文档将给出完整的脚本列表。

2、脚本

下面的文档不同于正式的说明文档,我们只是通过实例来演示在各种情况下T4MVC是如何提高你的代码质量。例子将通过前后的格式对比来指出变化之处。

注:下面给出的例子均是基于Nerd DinnerMVC应用程序。

2.1 使用View Name常量

任何时候,您会使用一个字符串来指向你的View,T4MVC将提供一个强类型的替代。例如,假设您有一个View的代码如下:

<% Html.RenderPartial("DinnerForm"); %>

这么难看的“DinnerForm”字符串让它见鬼去吧!相反,您现在可以这样抒写:

<% Html.RenderPartial(MVC.Dinners.Views.DinnerForm);%>

当您需要使用一个Controller里面的View时,事情变得更加容易。例如,您可以将:

return View("InvalidOwner");

替换成:

return View(Views.InvalidOwner);

注意:因为代码已经在Dinner的controller里面了,所以您可以省略MVC.Dinners前缀。

2.2 关于Controller Actions

许多MVC的API都有这样一个模式,当你需要指向一个Controller Action时需要包含三个信息:

  1. Controller Name (经常会在当前Controller下隐藏Name)

  2. Action Name

  3. 传递给Action的参数

这些可以理解为字符串#1、#2和匿名对象#3。例如:

<%= Html.ActionLink("Delete Dinner", "Delete", "Dinners", new { id = Model.DinnerID }, null)%>

在这里,“Delete”和“Dinners”是我们要替换掉的的字符串。但是这还不是全部,因为“id”参数同样和其他两个字符串一样也需要替换掉。即使它看起来并不像一个字符串,这只不过是它的变相而已。可别让那些匿名对象欺骗了你。

有了T4MVC,相反,您应该这样写:

<%= Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID))%>

我们基本上替换掉了三个不必要的字符串(“Delete”,“Dinner”和“id”),取代他们的一个看起来比较舒服的方式来调用Controller Action。当然,这不是真的去调用Controller Action,这样是非常错误的。它调用方法的原理是将其转化为正确的路径。

这种方法的另外一个很大的好处是,当你在抒写代码时可以得到完整的智能代码提示。事实上,如果你使用类似ReShaper具有重构功能的工具,你甚至可以得到更多关于重构的支持。

添加额外的路径参数

在某些情况下,您可能需要将Action方法参数不存在的值添加到路径中。您可以这样做:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValue("foo", 17))

您可以用这种方式很顺利的添加多个值。例如:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValue("foo", 17)
    .AddRouteValue("bar", "abc"))

作为添加多个值的替代,您也可以这样写:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValues(new { foo = 17, bar = "abc"}))

注意到在某些情况下,一些数据并非来自URL而是来自表单,您可能想在这样的路径中省略Action的参数。这时,您可以使用参数覆盖掉T4MVC生成路径参数。例如

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete()
    .AddRouteValues(new { foo = 17, bar = "abc"}))

现在假设您要在所有T4MVC产生的代码中添加查询字符串,您可以使用AddRouteValues()重载需要的NameValueCollection。例如:

RedirectToAction(MVC.Home.MyAction().AddRouteValues(Request.QueryString));

这些都在现有的API中增加了一套 AddRouteValue / AddRouteValues方法,使其更容易应对各种不同的场景。

这些API使得我们在给路径添加额外的参数时不再需要重载所有的T4MVC helper了(例如上面提到的Html.ActionLink这种情况)。相反地,它会自动应用到任何使用T4MVC模式的方法里。

许多使用T4MVC的地方常常可以看见伪调用Controller Action的方式:

2.2.1 Html.ActionLink

Ok,关于Html.ActionLink的使用方法上面的例子已经涵盖了(这里就不在赘述)。

2.2.2 Url.Action
<%= Url.Action("Delete", "Dinners", new { id = Model.DinnerID })%>

变成:

<%= Url.Action(MVC.Dinners.Delete(Model.DinnerID))%>

2.2.3 Ajax.ActionLink

<%= Ajax.ActionLink( "RSVP for this event",
                     "Register", "RSVP",
                     new { id=Model.DinnerID }, 
                     new AjaxOptions { UpdateTargetId="rsvpmsg", OnSuccess="AnimateRSVPMessage" }) %>

变成:

<%= Ajax.ActionLink( "RSVP for this event",
                     MVC.RSVP.Register(Model.DinnerID),
                     new AjaxOptions { UpdateTargetId="rsvpmsg", OnSuccess="AnimateRSVPMessage" }) %>
2.2.4 RedirectToAction(Controller代码)
return RedirectToAction("Details", new { id = dinner.DinnerID });

变成:

return RedirectToAction(MVC.Dinners.Details(dinner.DinnerID));
2.2.5 routes.MapRoute(通常在Global.asax)
routes.MapRoute(
    "UpcomingDinners", 
    "Dinners/Page/{page}", 
    new { controller = "Dinners", action = "Index" }
);

变成:

routes.MapRoute(
    "UpcomingDinners", 
    "Dinners/Page/{page}", 
    MVC.Dinners.Index(null)
);
2.2.6 Html.BeginForm

从本质上讲,BeginForm()和ActionLink()的使用方法相同。但要注意,因为表单提交的数据通常都是来自于form,这一点不同于其它数据是来自URL参数。BeginForm()相比其他方法运用起来会比较复杂一些。

下面教你如何使用:

using (Html.BeginForm(MVC.Account.LogOn(), FormMethod.Post)) { ... }

如果您的Action方法有很多参数,您会在调用时提交这些参数。然而,当您这样做时,您要确保您在表单中提交时没有相同名字的参数名。(例如,使用一个文本框)。

一般来说,经验法则告诉我们当您的Action方法参数刚好和URL的一样时,Html.BeginForm()也能处理的相当好。

2.2.7 Html.RenderAction和Html.Action(只在MVC 2或更新版本)

Html.RenderAction和Html.Action对于MVC 2来说是新的东西。更多详细信息可以参考Phil Haack’s post

<%= Html.Action("Menu", new { options = new MenuOptions { Width=400, Height=500} })%>

变成:

<%= Html.Action(MVC.Home.Menu(new MenuOptions { Width=400, Height=500})); %>

Html.RenderAction同样也是这样使用。

2.3 强类型的脚本链接和静态资源

T4MVC会为您的静态内容和脚本文件生成一些静态的helper。因此,下面代码可以

<img src="/Content/nerd.jpg" />

替换成:

<img src="<%= Links.Content.nerd_jpg %>" />

同样的,脚本链接可以这样替换:

<script src="/Scripts/Map.js" type="text/javascript"></script>

替换成:

<script src="<%= Links.Scripts.Map_js %>" type="text/javascript"></script>

显然,这样做的一个好处是当您在移动或重命名静态资源后,可以尽早地在编译时就能发现这个错误(而不必等到部署之后才发现)。

另一个好处是,您可以得到一个更全面的参考。当您抒写 src=”/Content/nerd.jpg”,它只有在您的网站页面部署在根目录下才会有效。但是您使用了这个helper后,无论您的根目录在哪里,它在服务端执行一些判断后就能正确产生资源的路径。它是通过调用VirtualPathUtility.ToAbsolute(“~/Content/nerd.jpg”)来实现的。

不幸的是,有些原因致使VS在View中抒写代码时不能智能地提示。但有一种变通的方法,您可以在标签外部用智能提示的抒写后再复制到链接中。

重要说明:如果您看到您的<head>标签生成了奇怪的链接,您只需要删除runat=”server”即可。

支持压缩的Javascript文件

通常情况下你运用各种方法来压缩JavaScript文件,再去替换版本,这样做只是为了使它们尽可能的小,比如说去除JavaScript文件中的空白部分。比如在您的MVC应用程序的Script文件夹中通常可以看到两个文件存在jQuery.1.3.2.js和jQuery.1.3.2.min.js。在部署环境中,您通常想使用压缩后的版本,但是在开发环境下喜欢更容易调试的原版本。

T4MVC可以根据场景自动地使用一个版本。例如,假设您有:

<script src="<%= Links.Scripts.jquery_1_2_6_js %>" type="text/javascript"></script>

您不需要改变任何代码,T4MVC根据您正在运行Production环境来决定将做过标记的jquery_1_2_6_js自动指向jQuery.1.3.2.js或jQuery.1.3.2.min.js。它是如何知道您是否是在Production环境下呢?它是通过调用了一个在T4MVC.tt.settings.t4定义好的方法来判断的。在默认情况下,它会先判断下这个方法:

// Logic to determine if the app is running in production or dev environment
public static bool IsProduction() { 
    return (HttpContext.Current != null && !HttpContext.Current.IsDebuggingEnabled); 
}

如果您对“Production”有另外的环境定义,也可以很容易地改变这个判断。

2.4 在MVC 2 的“Areas”使用T4MVC

MVC 2中的主要新功能之一是将一个大型应用程序分解成许多的“Areas”。这种结构可以这样来描述:

T4MVC会自动作用于“Areas”并且使得他们同样存在于Model中。下面是一个例子:

<%= Html.ActionLink("Delete Dinner", MVC.NerdDinner.Dinners.Delete(Model.DinnerID))%>

请注意我们是如何引用MVC.NerdDinner.Dinners Controller的。如果您碰巧有一个和“Areas”名称一样的顶层Controller,这样的命名模式将会导致冲突。在这种情况下,T4MVC会在名字后面追加“Area”关键字来避免冲突。例如,如果您同时有一个Home Area和一个Home Controller(顶级的),您应该使用MVC.HomeArea.Hello来指向Area。

您可以在设置文件中可选地设置IncludeAreasToken为true。这样的命名方式就变成:

<%= Html.ActionLink("Delete Dinner", MVC.Areas.NerdDinner.Dinners.Delete(Model.DinnerID))%>

注意到在上面这种情况下,上述的冲突就不会发生了。但是这样做需要付出在更多的地方使用分段的代价。我也在考虑是否要保持这种方式,但是现在的默认方式已经很好了。

3、调整行为的T4MVC

当您下载T4MVC,您不仅获得了它主要的T4MVC.tt模板,同时您也获得了名为T4MVC.tt.settings.t4的文件。这个文件包含了T4MVC的各种设置,您可以用它来调整T4MVC生成的代码。本节将介绍它的各种设置:

Set AlwaysKeepTemplateDirty to false to turn on the always-dirty behavior
// If true, the template marks itself as unsaved as part of its execution.
// This way it will be saved and update itself next time the project is built.
// Basically, it keeps marking itself as unsaved to make the next build work.
// Note: this is certainly hacky, but is the best I could come up with so far.
bool AlwaysKeepTemplateDirty = true;
Set SplitIntoMultipleFiles to false to generate a single file with everything
// If true,the template output will be split into multiple files.
bool SplitIntoMultipleFiles = true;
Use HelpersPrefix to change the MVC prefix of the genrated classes
// The prefix used for things like MVC.Dinners.Name and MVC.Dinners.Delete(Model.DinnerID)
const string HelpersPrefix = "MVC";
Use ControllersFolder and ViewsRootFolder to rename the Controllers and Views folders
// The folder under the project that contains the controllers
const string ControllersFolder = "Controllers";

// The folder under the project that contains the views
const string ViewsRootFolder = "Views";
Use LinksNamespace the generated links’ namespace
// The namespace that the links are generated in (e.g. "Links", as in Links.Content.nerd_jpg)
const string LinksNamespace = "Links";
// Folders containing static files for which links are generated (e.g. Links.Scripts.Map_js)
readonly string[] StaticFilesFolders = new string[] {
    "Scripts",
    "Content",
};

4、相关资源

David Ebbo has written a series of blog posts on T4MVC
Scott Hanselman also blogged about it here

Last edited Aug 14 2011 at 4:09 PM by davidebbo, version 5

原文地址:http://mvccontrib.codeplex.com/wikipage?title=T4MVC_doc&referringTitle=T4MVC

TortoiseSVN的简单介绍

SVN = 版本控制 + 备份服务器。可以把SVN当成备份服务器,更好的是,他可以记住每次上传到这个服务器的文件内容,并自动的赋予每次的变更一个版本。

TortoiseSVN

工具清单里面提到了SVN主要是用于作用版本控制的,第一个就联想到了我现在日常使用的版本控制器:Team Foundation Server (缩写: TFS )。这也是一个进行版本控制和协同开发的常用工具。由于之前只接触过TFS(现在也是每天在用),所以SVN是新工具,但是他们有很多共同之处,所以下面我将以类比的方式来介绍下SVN的日常使用。

一、SVN的介绍

SVN是为了提高协同开发效率,在项目开发中进行源代码管理、版本控制、文档共享等功能。它可以在SVN服务器上为我们维护所有源代码的每一个版本,安装在windows下的SVN可以在windows资源管理器中右键看到它,如文章开头处的截图一样。

对于一个团队,SVN的源码管理及版本控制带来这些好处:

1. 可多人同时开发同一项目,甚至更改同一个文件;

2. 开发人员每天(或按照阶段性目标)使用 “ check in ” 操作,将今天修改的源代码自动上传到开发机服务器;

3. 其它开发人员使用 “ 同步 ” 功能即可将开发机上最新代码同步至本机,无须人肉逐个 Q 传文件;

4. 可定时在开发机上编译并自动化部署到线上机器;

5. 可随时把开发机上的版本回滚为任意一个历史版本;

6. 可以查看每个人的每一次 “ check in ” 与 “ check out” ,可比较任意两个版本的代码差异,方便查看。

二、SVN的下载和安装。

1、官方下载地址:http://tortoisesvn.net/downloads.html 注意选择32bit or 64bit。

TortoiseSVN下载

2、接着下载中文包:http://tortoisesvn.net/translator_credits.html

TortoiseSVN语言包下载

3、安装就和普通的程序安装一样,语言包是安装完成后再安装的。

三、具体使用和简单介绍

1、安装完成TortoiseSVN后,会发现文件夹图标被重载,不同的重载图标所表示不同的意义。所有的TortoiseSVN命令都是通过windows资源管理器的右键菜单执行。右键打开windows资源管理器菜单就可以看到。下面以sinaapp提供的SVN服务器做为演示。

2、登录你的开发机服务器。

右键菜单中选择“SVN 检出(K)…”,即check out。可以将服务器上的代码下载下来。

检出代码

弹出登录框,输入服务器的用户名和密码。

 

3、检出代码(check out)。

填写服务器地址和你本地希望代码存放的文件夹。同时你也可以选择版本号,选择单个文件或某个文件夹进行check out。一般第一次都是将你负责的代码check out即可。

检出代码

正在check out代码,可以看到,TortoiseSVN是通过命令提交的方式给服务器的,我们也知道TortoiseSVN是有Linux版本的,所以可以猜想在Linux下使用TortoiseSVN的一些命令,当然你先得使用英文版的SVN了。
检出代码

check out完毕,可以看到你的文件夹的图标有了一个勾,没关系,以后还会看到更多的符号。

检出文件夹

SVN能进行版本控制就是因为在这个文件夹里面有个隐藏文件.svn。它加上了“.”说明在Linux下也是隐藏的,倘若你不想这个文件夹进行版本控制了,就手动删掉这个文件夹即可。

检出文件夹

4、进行开发。

完成了check out就可以在本地进行开发了,一旦这些文件被添加、修改、删除、改名、移动、替换等操作引起文件md5值不同,SVN会自动以不加锁的方式检出修改的文件,而且该文件的图标由勾变成了感叹号。

修改的文件图标

具体的图标的意思可以在这里看到:右键“TortoiseSVN”->“设置”->“图标集”。

图标含义
5、提交代码(check in)。

当你进行了一天的开发后,应该及时将代码(及其它文件变更)都提交到开发机上,避免本地文件丢失(或笔记本遗失、损坏等不测因素)造成严重后果。提交时SVN会自动识别出那些文件做了更改,你也可以人肉勾选其中一部分进行提交。

提交

新增的文件若没有执行增加操作,都会显示“无版本控制”状态,SVN默认是不会勾选它进行提交。每个文件都有自己的版本号,每次提交都会使文件产生一个新的版本号,可以查看所有历史版本或查看任意两个版本的差别。

每一次提交需要养成良好的习惯,即写上提交的信息,这样方便日后版本查看和回滚。每一次开始修改文件最好先更新一下这个文件,避免在提交时有文件冲突。

6、文件冲突(conflict)。

出现文件冲突是在你在提交某个文件正好被某个人修改了且提交了(即文件的副本和最新的不一致),服务器不能识别应该保存哪个版本时。

提交冲突

解决冲突:先升级本地工作副本,即获取到现在服务器上这个文件的最新版本。

升级工作副本

解决冲突

这个时候点击“显示日志”就可以看到是谁在什么时候提交了上一个版本。从信息中可以大致看出是做了什么修改,这都要基于平时提交时有写日志的习惯。

文件夹中多了单个文件:footer.php.mine,footer.php.r205和footer.php.r206 。从命名中就很容易看出这三个文件的含义:.mine是本地编辑后的文件版本,.204是你在编辑前更新的工作副本,.205是服务器上最新的文件。

冲突产生的文件

对footer.php右键选择“编辑冲突”,弹出著名的TortoiseMerge,其实也可以用WinMerge来完成。

TortoiseMerge

可以看到冲突的地方,左边是服务器上的文件,右边是本地的文件,下边是合并的文件。右键选择好保存即可。

TortoiseMerge

TortoiseMerge

保存好之后,再footer.php右键选择“已解决的”。

已解决冲突已解决冲突

最后一步,提交解决冲突后的footer.php。可以看到版本号为207了。文件夹的冲突也是类似这样处理,这里就不在赘述了。

提交代码

四、结语。

TortoiseSVN的功能远不止上面介绍的这些,还有些可以类比使用,这些也可以满足日常比较常用的操作了。

更多详细信息可以参考TortoiseSVN中文手册
nn1

标签属性介绍

<link>标签定义了当前文档与 Web 集合中其他文档的关系。link 元素是一个空元素,它仅包含属性。此元素只能存在于 head 部分,不过它可出现任何次数。

在 html 中,<link> 标签没有结束标签。在 XHTML 中,<link> 标签必须被正确的关闭。 除了HTML的标准通用属性之外,link元素还包括很多可选属性: charset, href, hreflang, media, rel, rev, target, title和type。这些属性中,target只允许在Transitional和Frameset两种DTD中使用,其它都可在Strict, Transitional和Frameset三种DTD中使用。 这些属性中,rel属性是核心。

本文将介绍一些自己知道的rel属性,以及在wordpress中对一些link元素的处理,适合新手朋友学习。

1. 调用外部样式表

(1). 显示器样式表。 link标签最多的使用就是用来调用外部样式表,例如下面这样:

   1:  <link rel="stylesheet" href="style.CSS" type="text/css" media="screen" />

其中href是目标文档的URL, type则规定了目标URL的MIME类型,而media规定了文档将显示在什么设备上。

(2). 打印设备样式表。 下面这个webdesignerwall的样式表调用就规定了文档显示在打印设备上时的CSS样式 :

   1:  <link rel="stylesheet" href="print.css" type="text/css" media="print" />

(3). 可替换样式表。你可能还会在一些网页中看到诸如下面的样式表调用代码:

   1:  <link rel="alertnate stylesheet" href="red.css" type="text/css" media="screen" />

这段代码定义了一个可替换的样式表,它和第一个link元素同时使用,第一个定义了首选样式,而这个则让用户可选择替换的样式。但这个替换操作需要浏览器支持,但很多浏览器比如IE都是不支持的。 所以使用到替换样式的网页,一般都用一些样式表切换的JS,让用户可以自由切换界面样式。这个应该大家都见过,一些网站会给网页定义多种配色。 WordPress用户有兴趣的话,可以下载Small Potato的WPDesigner7这款wordpress主题试用研究一下,它利用一个简单的JS和多个可替换样式,让用户可对网页改变配色。稍高阶的一些,还可以利用JS弄成随时间变化样式的,比如白天的时候显示成明色,晚上的时候显示成暗色。

注释: 为首选样式指定media=”all”,再添加一个打印样式,会比较符合Web标准(尽管对于普通网站来说,不会有几个人想要打印你的网页)。

注释: 是否使用可替换样式是个值得斟酌的问题。如果仅改变配色,整体主调还是不变,那可以接受。但有一些朋友,比如WordPress用户,会启用多个完全不同风格的主题,再利用插件让用户自由变换。这看上去似乎挺酷的,但我的建议是千万别这么做。是否影响seo且不谈,但会让人对你的网站缺乏一种固定形象的认知感。

2. 定义网站收藏夹图标

关于favicon收藏夹图标的详细介绍可以查看百度百科,使用下面的代码调用即可。

   1:  <link rel="shortcut icon" href="favicon.ico" type="images/x-icon" />
   2:  <link rel="icon" href="favicon.png" type="images/png" />

关于这个调用我自己也还有些迷糊,我实验的结果是: IE只支持ico格式的favicon; rel属性必须包含shortcut, 才会在IE下显示; 我在制作透明格式的ico时总出问题,总会出现黑底,就算弄了IE下非黑底了,在Chrome下又变成黑底。于是,制作一个透明的ico和一个透明的png, 第一段供IE浏览器调用,第二段供其它浏览器调用;

注释: 你也可以不使用这个link元素,而直接制作一个favicon.ico文件,并放到网站根目录。 顺便分享: 为你的网站添加Apple Touch图标 iphone或iPad设备允许用户添加网站的链接到主屏上,使用下面的代码可以为你的网站指定一个Apple Touch图标:

   1:  <link rel="apple-touch-icon" href="ipad_72_72.png" />

该图标的尺寸是5757~7272不等的PNG格式,如果不是,会自动缩放,而且一定不要弄成iPhone/iPad风格那种漂亮的圆角,iPhone/iPad会自动按它的风格把图标弄成圆角渐变的。

3. WordPress中的link元素

(1). RSS地址和Pingback地址。下面是WordPress默认主题对RSS2地址,Atom地址和Pingback地址的定义。

   1:  <link rel="alternate" type="application/rss+xml" title="&amp;lt;?php bloginfo('name'); ?> RSS Feed" href="<?php bloginfo('rss2_url'); ?>"  />
   2:  <link rel="alternate" type="application/atom+xml" title="&amp;lt;?php bloginfo('name'); ?> Atom Feed" href="<?php bloginfo('atom_url'); ?>"  />
   3:  <link rel="pingback" href="&amp;lt;?php bloginfo('pingback_url'); ?>"  />

(2). 用于远程发布的link元素 如果你的主题中有<?php wp_head(); ?>这个函数,下面这两个link元素就会出现:

   1:  <link rel="EditURI" type="application/rsd+xml" title="RSD" href="xmlrpc.php?rsd" />
   2:  <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="wlwmanifest.xml" />

这两个元素主要供远程发布使用,比如你使用Windows Live Write等桌面博客编辑器来发布文章。如果你并不需要这个功能,那完全可以把这两个元素删除,删除的方法是,打开你WordPress主题的functions.php, 在最底部的<?php } ?> 或者 ?> 标签之前,插入下面的代码:

   1:   remove_action('wp_head', 'rsd_link');
   2:   remove_action('wp_head', 'wlwmanifest_link');

注释: 你可能在想,既然是<?php wp_head(); ?>函数生成了这两个东西,把它删除不就可以了。是的,如果你预计你其它任何插件都不会需要到这个函数,那就删吧。

4. 防止重复内容的canonical属性

Canonical属性主要作用是为网页指定权威链,以解决重复内容问题。例如本页面中就含有:

   1:  <link rel="canonical" href="http://javier.sinaapp.com/link-attr/" />

关于这个属性的详细介绍请看谷歌中文网站管理员中的指定您的URL范式。

5. link的可选属性

DTD 指示此属性允许在哪种 DTD 中使用。S=Strict, T=Transitional, F=Frameset.

属性 描述 DTD
charset _charset_ 定义被链接文档的字符编码方式。 STF
href _URL_ 定义被链接文档的位置。 STF
hreflang _language_code_ 定义被链接文档中文本的语言。 STF
media
  • screen

  • tty

  • tv

  • projection

  • handheld

  • print

  • braille

  • aural

  • all

规定被链接文档将显示在什么设备上。 STF
rel
  • alternate

  • appendix

  • bookmark

  • chapter

  • contents

  • copyright

  • glossary

  • help

  • home

  • index

  • next

  • prev

  • section

  • start

  • stylesheet

  • subsection

定义当前文档与被链接文档之间的关系。 STF
rev
  • alternate

  • appendix

  • bookmark

  • chapter

  • contents

  • copyright

  • glossary

  • help

  • home

  • index

  • next

  • prev

  • section

  • start

  • stylesheet

  • subsection

定义被链接文档与当前文档之间的关系。 STF
target
  • _blank

  • _parent

  • _self

  • _top

  • framename

定义在何处加载被链接文档。 TF
type _MIME_type_ 规定被链接文档的 MIME 类型。 STF