分类目录归档:HTML5

HTML5那点儿不可告人的事儿-。-!

微信小程序架构解析,工作原理解析

作者介绍:

渠宏伟,腾讯高级工程师,从事Web前端开发5年,先后负责企鹅电竞、腾讯视频VIP、腾讯OA开发框架、腾讯微信HR助手等项目。对Web前端架构、.NET架构有丰富的经验。

| 导语 微信小程序的公测掀起了学习小程序开发的浪潮,天生跨平台,即用即走、媲美原生体验、完善的文档、高效的开发框架,小程序给开发者带来了很多惊喜。通过这篇文章和大家一起分析小程序的架构,分享开发经验。

一、小程序介绍

1、小程序特点

微信小程序原理解析

2、小程序演示


视频地址:https://v.qq.com/x/page/w0353d7co6y.html

3、小程序为什么那么快

微信小程序原理解析
Page Frame

Native预先额外加载一个WebView
当打开指定页面时,用默认数据直接渲染,请求数据回来时局部更新
返回显示历史View
退出小程序,View状态不销毁

4、小程序入口

微信小程序原理解析

扫码进入小程序

搜索小程序

小程序发送到桌面(Android)

发送给朋友

二、小程序架构

微信小程序的框架包含两部分View视图层、App Service逻辑层,View层用来渲染页面结构,AppService层用来逻辑处理、数据请求、接口调用,它们在两个线程里运行。

视图层使用WebView渲染,逻辑层使用JSCore运行。

视图层和逻辑层通过系统层的JSBridage进行通信,逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。

微信小程序原理解析

 

小程序启动时会从CDN下载小程序的完整包

微信小程序原理解析

 

三、View (页面视图)

视图层由 WXML 与 WXSS 编写,由组件来进行展示。

将逻辑层的数据反应成视图,同时将视图层的事件发送给逻辑层。

1、View – WXML

WXML(WeiXin Markup Language)

支持数据绑定

支持逻辑算术、运算

支持模板、引用

支持添加事件(bindtap)

微信小程序原理解析

wxml编译器:wcc  把wxml文件 转为 js   执行方式:wcc index.wxml

2、View – WXSS

WXSS(WeiXin Style Sheets)

支持大部分CSS特性

添加尺寸单位rpx,可根据屏幕宽度自适应

使用@import语句可以导入外联样式表

不支持多层选择器-避免被组件内结构破坏

微信小程序原理解析

wxss编译器:wcsc 把wxss文件转化为 js 执行方式: wcsc index.wxss

3、View – WXSS Selectors

WXSS目前支持如下选择器:

微信小程序原理解析
4、View – Component

小程序提供了一系列组件用于开发业务功能,按照功能与HTML5的标签进行对比如下:

微信小程序原理解析

小程序的组件基于Web Component标准

使用Polymer框架实现Web Component

微信小程序原理解析

 

5、View – Native Component

目前Native实现的组件有 <canvas/> <video/> <map/> <textarea/>

Native组件层在WebView层之上

微信小程序原理解析

 

四、App Service(逻辑层)

逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈

1、App( ) 小程序的入口;Page( ) 页面的入口

3、提供丰富的 API,如微信用户数据,扫一扫,支付等微信特有能力。

4、每个页面有独立的作用域,并提供模块化能力。

5、数据绑定、事件分发、生命周期管理、路由管理

运行环境

IOS – JSCore

Android – X5 JS解析器

DevTool – nwjs Chrome 内核

1、App Service – Binding

数据绑定使用 Mustache 语法(双大括号)将变量包起来,动态数据均来自对应 Page 的 data,可以通过setData方法修改数据。
事件绑定的写法同组件的属性,以 key、value 的形式,key 以bind或catch开头,然后跟上事件的类型,如bindtap, catchtouchstart,value 是一个字符串,需要在对应的 Page 中定义同名的函数。

微信小程序原理解析

微信小程序原理解析

 

2、App Service – Life Cylce

微信小程序原理解析

 

3、App Service – API

API通过JSBridge和Native 进行通信

微信小程序原理解析

 

4、App Service – Router

navigateTo(OBJECT)

保留当前页面,跳转到应用内的某个页面,使用navigateBack可以返回到原页面。页面路径只能是五层

redirectTo(OBJECT)

关闭当前页面,跳转到应用内的某个页面。

navigateBack(OBJECT)

关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages()) 获取当前的页面栈,决定需要返回几层。

 

五、小程序开发经验

1、小程序存在的问题

小程序仍然使用WebView渲染,并非原生渲染
需要独立开发,不能在非微信环境运行。
开发者不可以扩展新组件。
服务端接口返回的头无法执行,比如:Set-Cookie。
依赖浏览器环境的js库不能使用,因为是JSCore执行的,没有window、document对象。
WXSS中无法使用本地(图片、字体等)。
WXSS转化成js 而不是css,为了兼容rpx。
WXSS不支持级联选择器。
小程序无法打开页面,无法拉起APP。

小程序不能和公众号重名,于是小程序的名字就成了:自选股+、滴滴出行DiDi 。

 

2、小程序可以借鉴的优点

提前新建WebView,准备新页面渲染。
View层和逻辑层分离,通过数据驱动,不直接操作DOM。
使用Virtual DOM,进行局部更新。
全部使用https,确保传输中安全。
使用离线能力。
前端组件化开发。
加入rpx单位,隔离设备尺寸,方便开发。

 

3、脱离微信的“小程序”:PWA 渐进式应用

PWA 全称是 Progressive Web Apps ,译成中文就是渐进式应用,是 Google 在 2015 年 6 月 15 日提出的概念。
Progressive Web Apps 是结合了 web 和 原生应用中最好功能的一种体验。对于首次访问的用户它是非常有利的, 用户可以直接在浏览器中进行访问,不需要安装应用。随着时间的推移当用户渐渐地和应用建立了联系,它将变得越来越强大。它能够快速地加载,即使在弱网络环境下,能够推送相关消息, 也可以像原生应用那样添加至主屏,能够有全屏浏览的体验。

微信小程序原理解析
PWA具有如下特点:

渐进增强 – 支持的新特性的浏览器获得更好的体验,不支持的保持原来的体验。
离线访问 – 通过 service workers 可以在离线或者网速差的环境下工作。
类原生应用 – 使用app shell model做到原生应用般的体验。
可安装 – 允许用户保留对他们有用的应用在主屏幕上,不需要通过应用商店。
容易分享 – 通过 URL 可以轻松分享应用。
持续更新 – 受益于 service worker 的更新进程,应用能够始终保持更新。
安全 – 通过 HTTPS 来提供服务来防止网络窥探,保证内容不被篡改。
可搜索 – 得益于 W3C manifests 元数据和 service worker 的登记,让搜索引擎能够找到 web 应用。
再次访问 – 通过消息推送等特性让用户再次访问变得容易。

Web App Manifest使Web更像Native

Web App Manifest以JSON的格式定义Web应用的相关配置(应用名称、图标或图像连接、启动URL、自定义特性、启动默认配置、全屏设置等)。

微信小程序原理解析

Service Workers增强Web能力

通过Service Works实现资源离线缓存和更新

微信小程序原理解析

App Shell 提升显示效率

App Shell(应用外壳)是应用的用户界面所需的最基本的 HTML、CSS 和 JavaScript,首次加载后立刻被缓存下来,不需要每次使用时都被下载,而是只异步加载需要的数据,以达到UI保持本地化。

微信小程序原理解析

了解更多pwa资料:
https://developers.google.com/web/progressive-web-apps/

 

video.js 源码分析(组织结构、继承关系、运行机制、插件的运行机制、类的挂载方式)

组织结构

以下是video.js的源码组织结构关系,涉及控制条、菜单、浮层、进度条、滑动块、多媒体、音轨字幕、辅助函数集合等等。

├── control-bar
│   ├── audio-track-controls
│   │   ├── audio-track-button.js
│   │   └── audio-track-menu-item.js
│   ├── playback-rate-menu
│   │   ├── playback-rate-menu-button.js
│   │   └── playback-rate-menu-item.js
│   ├── progress-control
│   │   ├── load-progress-bar.js
│   │   ├── mouse-time-display.js
│   │   ├── play-progress-bar.js
│   │   ├── progress-control.js
│   │   ├── seek-bar.js
│   │   └── tooltip-progress-bar.js
│   ├── spacer-controls
│   │   ├── custom-control-spacer.js
│   │   └── spacer.js
│   ├── text-track-controls
│   │   ├── caption-settings-menu-item.js
│   │   ├── captions-button.js
│   │   ├── chapters-button.js
│   │   ├── chapters-track-menu-item.js
│   │   ├── descriptions-button.js
│   │   ├── off-text-track-menu-item.js
│   │   ├── subtitles-button.js
│   │   ├── text-track-button.js
│   │   └── text-track-menu-item.js
│   ├── time-controls
│   │   ├── current-time-display.js
│   │   ├── duration-display.js
│   │   ├── remaining-time-display.js
│   │   └── time-divider.js
│   ├── volume-control
│   │   ├── volume-bar.js
│   │   ├── volume-control.js
│   │   └── volume-level.js
│   ├── control-bar.js
│   ├── fullscreen-toggle.js
│   ├── live-display.js
│   ├── mute-toggle.js
│   ├── play-toggle.js
│   ├── track-button.js
│   └── volume-menu-button.js
├── menu
│   ├── menu-button.js
│   ├── menu-item.js
│   └── menu.js
├── popup
│   ├── popup-button.js
│   └── popup.js
├── progress-bar
│   ├── progress-control
│   │   ├── load-progress-bar.js
│   │   ├── mouse-time-display.js
│   │   ├── play-progress-bar.js
│   │   ├── progress-control.js
│   │   ├── seek-bar.js
│   │   └── tooltip-progress-bar.js
│   └── progress-bar.js
├── slider
│   └── slider.js
├── tech
│   ├── flash-rtmp.js
│   ├── flash.js
│   ├── html5.js
│   ├── loader.js
│   └── tech.js
├── tracks
│   ├── audio-track-list.js
│   ├── audio-track.js
│   ├── html-track-element-list.js
│   ├── html-track-element.js
│   ├── text-track-cue-list.js
│   ├── text-track-display.js
│   ├── text-track-list-converter.js
│   ├── text-track-list.js
│   ├── text-track-settings.js
│   ├── text-track.js
│   ├── track-enums.js
│   ├── track-list.js
│   ├── track.js
│   ├── video-track-list.js
│   └── video-track.js
├── utils
│   ├── browser.js
│   ├── buffer.js
│   ├── dom.js
│   ├── events.js
│   ├── fn.js
│   ├── format-time.js
│   ├── guid.js
│   ├── log.js
│   ├── merge-options.js
│   ├── stylesheet.js
│   ├── time-ranges.js
│   ├── to-title-case.js
│   └── url.js
├── big-play-button.js
├── button.js
├── clickable-component.js
├── close-button.js
├── component.js
├── error-display.js
├── event-target.js
├── extend.js
├── fullscreen-api.js
├── loading-spinner.js
├── media-error.js
├── modal-dialog.js
├── player.js
├── plugins.js
├── poster-image.js
├── setup.js
└── video.js

video.js的JavaScript部分都是采用面向对象方式来实现的。基类是Component,所有其他的类都是直接或间接集成此类实现。语法部分采用的是ES6标准。

继承关系

深入源码解读需要了解类与类之间的继承关系,直接上图。

  • 所有的继承关系
  • 主要的继承关系

运行机制

首先调用videojs启动播放器,videojs方法判断当前id是否已被实例化,如果没有实例化新建一个Player对象,因Player继承Component会自动初始化Component类。如果已经实例化直接返回Player对象。

videojs方法源码如下:

function videojs(id, options, ready) {
let tag;
// id可以是选择器也可以是DOM节点
if (typeof id === 'string') {
    if (id.indexOf('#') === 0) {
        id = id.slice(1);
    }
    //检查播放器是否已被实例化
    if (videojs.getPlayers()[id]) {
        if (options) {
            log.warn(`Player "${id}" is already initialised. Options will not be applied.`);
        }
        if (ready) {
            videojs.getPlayers()[id].ready(ready);
        }
        return videojs.getPlayers()[id];
    }
    // 如果播放器没有实例化,返回DOM节点
    tag = Dom.getEl(id);
} else {
    // 如果是DOM节点直接返回
    tag = id;
}
if (!tag || !tag.nodeName) {
    throw new TypeError('The element or ID supplied is not valid. (videojs)');
}
// 返回播放器实例
return tag.player || Player.players[tag.playerId] || new Player(tag, options, ready);
}
[]()

接下来我们看下Player的构造函数,代码如下:

constructor(tag, options, ready) {
    // 注意这个tag是video原生标签
    tag.id = tag.id || `vjs_video_${Guid.newGUID()}`;
    // 选项配置的合并
    options = assign(Player.getTagSettings(tag), options);
    // 这个选项要关掉否则会在父类自动执行加载子类集合
    options.initChildren = false;
    // 调用父类的createEl方法
    options.createEl = false;
    // 在移动端关掉手势动作监听
    options.reportTouchActivity = false;
    // 检查播放器的语言配置
    if (!options.language) {
        if (typeof tag.closest === 'function') {
            const closest = tag.closest('[lang]');
            if (closest) {
                options.language = closest.getAttribute('lang');
            }
        } else {
            let element = tag;
            while (element && element.nodeType === 1) {
                if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
                    options.language = element.getAttribute('lang');
                    break;
                }
                element = element.parentNode;
            }
        }
    }
    // 初始化父类
    super(null, options, ready);
    // 检查当前对象必须包含techOrder参数
    if (!this.options_ || !this.options_.techOrder || !this.options_.techOrder.length) {
        throw new Error('No techOrder specified. Did you overwrite ' +
            'videojs.options instead of just changing the ' +
            'properties you want to override?');
    }
    // 存储当前已被实例化的播放器
    this.tag = tag;
    // 存储video标签的各个属性
    this.tagAttributes = tag && Dom.getElAttributes(tag);
    // 将默认的英文切换到指定的语言
    this.language(this.options_.language);
    if (options.languages) {
        const languagesToLower = {};
        Object.getOwnPropertyNames(options.languages).forEach(function(name) {
            languagesToLower[name.toLowerCase()] = options.languages[name];
        });
        this.languages_ = languagesToLower;
    } else {
        this.languages_ = Player.prototype.options_.languages;
    }
    // 缓存各个播放器的各个属性.
    this.cache_ = {};
    // 设置播放器的贴片
    this.poster_ = options.poster || '';
    // 设置播放器的控制
    this.controls_ = !!options.controls;
    // 默认是关掉控制
    tag.controls = false;
    this.scrubbing_ = false;
    this.el_ = this.createEl();
    const playerOptionsCopy = mergeOptions(this.options_);
    // 自动加载播放器插件
    if (options.plugins) {
        const plugins = options.plugins;
        Object.getOwnPropertyNames(plugins).forEach(function(name) {
            if (typeof this[name] === 'function') {
                this[name](plugins[name]);
            } else {
                log.error('Unable to find plugin:', name);
            }
        }, this);
    }
    this.options_.playerOptions = playerOptionsCopy;
    this.initChildren();
    // 判断是不是音频
    this.isAudio(tag.nodeName.toLowerCase() === 'audio');
    if (this.controls()) {
        this.addClass('vjs-controls-enabled');
    } else {
        this.addClass('vjs-controls-disabled');
    }
    this.el_.setAttribute('role', 'region');
    if (this.isAudio()) {
        this.el_.setAttribute('aria-label', 'audio player');
    } else {
        this.el_.setAttribute('aria-label', 'video player');
    }
    if (this.isAudio()) {
        this.addClass('vjs-audio');
    }
    if (this.flexNotSupported_()) {
        this.addClass('vjs-no-flex');
    }

    if (!browser.IS_IOS) {
        this.addClass('vjs-workinghover');
    }
    Player.players[this.id_] = this;
    this.userActive(true);
    this.reportUserActivity();
    this.listenForUserActivity_();
    this.on('fullscreenchange', this.handleFullscreenChange_);
    this.on('stageclick', this.handleStageClick_);
}

在Player的构造器中有一句super(null, options, ready);实例化父类Component。我们来看下Component的构造函数:

constructor(player, options, ready) {
    // 之前说过所有的类都是继承Component,不是所有的类需要传player
    if (!player && this.play) {
        // 这里判断调用的对象是不是Player本身,是本身只需要返回自己
        this.player_ = player = this; // eslint-disable-line
    } else {
        this.player_ = player;
    }
    this.options_ = mergeOptions({}, this.options_);
    options = this.options_ = mergeOptions(this.options_, options);
    this.id_ = options.id || (options.el && options.el.id);
    if (!this.id_) {
        const id = player && player.id && player.id() || 'no_player';
        this.id_ = `${id}_component_${Guid.newGUID()}`;
    }
    this.name_ = options.name || null;
    if (options.el) {
        this.el_ = options.el;
    } else if (options.createEl !== false) {
        this.el_ = this.createEl();
    }
    this.children_ = [];
    this.childIndex_ = {};
    this.childNameIndex_ = {};
    // 知道Player的构造函数为啥要设置initChildren为false了吧
    if (options.initChildren !== false) {
        // 这个initChildren方法是将一个类的子类都实例化,一个类都对应着自己的el(DOM实例),通过这个方法父类和子类的DOM继承关系也就实现了
        this.initChildren();
    }
    this.ready(ready);
    if (options.reportTouchActivity !== false) {
        this.enableTouchActivity();
    }
}

插件的运行机制

插件的定义

import Player from './player.js';
// 将插件种植到Player的原型链
const plugin = function(name, init) {
  Player.prototype[name] = init;
};
// 暴露plugin接口
videojs.plugin = plugin;

插件的运行

// 在Player的构造函数里判断是否使用了插件,如果有遍历执行
if (options.plugins) {
    const plugins = options.plugins;
    Object.getOwnPropertyNames(plugins).forEach(function(name) {
    if (typeof this[name] === 'function') {
        this[name](plugins[name]);
    } else {
        log.error('Unable to find plugin:', name);
    }
    }, this);
}

控制条是如何运行的

Player.prototype.options_ = {
  // 此处表示默认使用html5的video标签
  techOrder: ['html5', 'flash'],
  html5: {},
  flash: {},
  // 默认的音量,官方代码该配置无效有bug,我们已修复,
  defaultVolume: 0.85,
  // 用户的交互时长,比如超过这个时间表示失去焦点
  inactivityTimeout: 2000,
  playbackRates: [],
  // 这是控制条各个组成部分,作为Player的子类
  children: [
    'mediaLoader',
    'posterImage',
    'textTrackDisplay',
    'loadingSpinner',
    'bigPlayButton',
    'progressBar',
    'controlBar',
    'errorDisplay',
    'textTrackSettings'
  ],
  language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
  languages: {},
  notSupportedMessage: 'No compatible source was found for this media.'
};

Player类中有个children配置项,这里面是控制条的各个组成部分的类。各个UI类还有子类,都是通过children属性链接的。

UI与JavaScript对象的衔接

video.js里都是组件化实现的,小到一个按钮大到一个播放器都是一个继承了Component类的对象实例,每个对象包含一个el属性,这个el对应一个DOM实例,el是通过createEl生成的DOM实例,在Component基类中包含一个方法createEl方法,子类也可以重写该方法。类与类的从属关系是通过children属性连接。

那么整个播放器是怎么把播放器的UI加载到HTML中的呢?在Player的构造函数里可以看到先生成el,然后初始化父类遍历Children属性,将children中的类实例化并将对应的DOM嵌入到player的el属性中,最后在Player的构造函数中直接挂载到video标签的父级DOM上。

if (tag.parentNode) {
  tag.parentNode.insertBefore(el, tag);
}

这里的tag指的是video标签。

类的挂载方式

上文有提到过UI的从属关系是通过类的children方法连接的,但是所有的类都是关在Component类上的。这主要是基于对模块化的考虑,通过这种方式实现了模块之间的通信。

存储

static registerComponent(name, comp) {
    if (!Component.components_) {
      Component.components_ = {};
    }

    Component.components_[name] = comp;
    return comp;
}

获取

static getComponent(name) {
    if (Component.components_ && Component.components_[name]) {
      return Component.components_[name];
    }

    if (window && window.videojs && window.videojs[name]) {
      log.warn(`The ${name} component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)`);
      return window.videojs[name];
    }
}

在Componet里有个静态方法是registerComponet,所有的组件类都注册到Componet的components_属性里。

例如控制条类ControlBar就是通过这个方法注册的。

Component.registerComponent('ControlBar', ControlBar);

在Player的children属性里包括了controlBar类,然后通过getComponet获取这个类。

.filter((child) => {
  const c = Component.getComponent(child.opts.componentClass ||
                                 toTitleCase(child.name));

  return c && !Tech.isTech(c);
})

修改输入框placeholder文字默认颜色-webkit-input-placeholder

html5为input添加了原生的占位符属性placeholder,高级浏览器都支持这个属性,例如:

<input type="text" placeholder="我爱北京天安门" value=" ">

默认的placeholder字体颜色是呈浅灰色,如果想改变这个默认颜色,解决方案如下:

:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
    color: #f00;  
}

::-moz-placeholder { /* Mozilla Firefox 19+ */
    color: #f00;
}

input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
    color: #f00;
}

input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
    color: #f00;
}

以上代码摘自Bootstrap框架的bootstrap.css

网页头部声明应该是用lang=”zh-cn”、lang=“zh”还是 lang=“zh-cmn-Hans”

lang属性的取值应该遵循 BCP 47 – Tags for Identifying Languages

单一的 zh 和 zh-CN 均属于废弃用法。

问题主要在于,zh 现在不是语言code了,而是macrolang,能作为语言code的是cmn(国语)、yue(粤语)、wuu(吴语)等。我通常建议写成 zh-cmn 而不是光写 cmn,主要是考虑兼容性(至少可匹配 zh),有不少软件和框架还没有据此更新。

zh-CN 的问题还在于,其实多数情况下标记的是简体中文,但是不恰当的使用了地区,这导致同样用简体中文的 zh-SG(新加坡)等无法匹配。更典型的是 zh-TW 和 zh-HK。所以其实应该使用 zh-Hans / zh-Hant 来表示简体和繁体。那么完整的写法就是 zh-cmn-Hans,表示简体中文书写的普通话/国语。一般而言没有必要加地区代码,除非要表示地区特异性,一般是词汇不一样(比如维基百科的大陆简体和新马简体)。

如何标记的例子:

  1. 简体中文页面:html lang=zh-cmn-Hans
  2. 繁体中文页面:html lang=zh-cmn-Hant
  3. 英语页面:html lang=en

需要加地区代码的情况一般比较少,除非为了强调不同地区汉语使用差异。比如:

<p lang="zh-cmn-Hans">
<b lang="zh-cmn-Hans-CN">菠萝</b>和<b lang="zh-cmn-Hant-TW">鳳梨</b>其实是同一种水果。只是大陆和台湾称谓不同,且新马一带的称谓也是不同的,称之为<b lang="zh-cmn-Hans-SG">黄梨</b>。
</p>

当然,由于历史原因,有时候不得不继续使用zh-CN。比如中文维基百科,沿用了传统的zh-CN/zh-HK/zh-SG/zh-TW(按照标准应该使用 zh-cmn-Hans-CN、zh-cmn-Hant-HK、zh-cmn-Hans-SG、zh-cmn-Hant-TW)。这时候,合理的软件行为,是将 zh-CN 等转化为 zh-cmn-Hans(即转化为最常见的误用所对应的实际标准写法)。

实际上,各相关标准,也存在一定的滞后。例如CSS的:lang选择器,不支持选择仅仅简体/繁体中文(而不管是cmn或是yue或是min等汉语方言)。理想情况是CSS3对:lang选择器的语法进行升级,即BCP 47中的高级匹配算法,支持 :lang(*-Hans)这样的写法。

更新:Selectors Level 4 已经加入了对BCP 47高级匹配算法的支持,即支持 :lang(*-Hans) 的写法。

百度SiteApp转码说明及如何取消转码

SiteApp转码声明

1.Siteapp页面转码的意义?

在百度移动搜索引擎中为更好满足用户信息需求,会同时为用户提供pc网页和mobile网页,但目前大多数PC页在移动终端中直接浏览的体验较差(交互、兼容和流量等)。因此为改善搜索用户的浏览体验,我们会对百度搜索结果中缺乏可替代mobile资源的PC页进行格式转换,转换为适合手机浏览的mobile网页,使其能在移动终端浏览器有较好的浏览体验。百度的Siteapp转码技术即是为实现此目的、服务于百度搜索的附属产品,各大搜索引擎均拥有并提供类似转码技术。

2.百度Siteapp转码的策略?

为了最大程度改善PC页面在手机上的体验,Siteapp转码须去除WEB页面中不能在手机浏览器上浏览的内容,并改善不适用mobile的交互功能。百度移动搜索还会智能地选择转码效果较好的页面进行转码 ,尽可能减少对转码后效果不佳的页面进行转码处理。

3.如果觉得自己的站点转码效果不佳怎么办?

百度提供了siteapp自助建站服务,您可以登录siteapp.baidu.com获取该服务,该服务提供了站长自助优化百度无线搜索访问其站点时的页面效果,也可以通过提供独立的域名创建属于自己的网站,您还可以在siteapp服务中,管理百度网盟广告获取收益。点击进一步了解SiteApp

4.站长如果不希望自己的站点被转码怎么办?

百度一共提供了两种解决方案: A. no-siteapp协议
Siteapp支持的no-siteapp协议为如下两种形式:
a. HTTP Response中显式声明Cache-control为no-siteapp。
b. meta标签中显式声明Cache-control为no-siteapp,格式为:
<head>
<meta http-equiv=”Cache-Control” content=”no-siteapp” />
</head>
如第三方站点不希望某页面被转码,可该页面上添加此协议,当用户通过移动搜索进入第三方网站时,会直接进入原网页。
B.开放适配协议
如第三方站点不希望页面被转码,且本身有对应手机页面时,建议站长使用百度的开放适配服务,百度将会帮助用户直接进入第三方网站自有的手机页面。
由于开放适配有多种参与方式,详细可以点击查看开放适配服务

HTML head 头标签属性详解

HTML head 头部分的标签、元素有很多,涉及到浏览器对网页的渲染,SEO 等等,而各个浏览器内核以及各个国内浏览器厂商都有些自己的标签元素,这就造成了很多差异性。移动互联网时代,head 头部结构,移动端的 meta 元素,显得更为重要。了解每个标签的意义,写出满足自己需求的 head 头标签,是本文的目的。

DOCTYPE

DOCTYPE(Document Type),该声明位于文档中最前面的位置,处于 html 标签之前,此标签告知浏览器文档使用哪种 HTML 或者 XHTML 规范。

DTD(Document Type Definition) 声明以 <!DOCTYPE> 开始,不区分大小写,前面没有任何内容,如果有其他内容(空格除外)会使浏览器在 IE 下开启怪异模式(quirks mode)渲染网页。公共 DTD,名称格式为注册//组织//类型 标签//语言,注册指组织是否由国际标准化组织(ISO)注册,+表示是,-表示不是。组织即组织名称,如:W3C。类型一般是 DTD。标签是指定公开文本描述,即对所引用的公开文本的唯一描述性名称,后面可附带版本号。最后语言是 DTD 语言的 ISO 639 语言标识符,如:EN 表示英文,ZH 表示中文。XHTML 1.0 可声明三种 DTD 类型。分别表示严格版本,过渡版本,以及基于框架的 HTML 文档。

  • HTML 4.01 strict
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  • HTML 4.01 Transitional
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    
  • HTML 4.01 Frameset
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
    
  • 最新 HTML5 推出更加简洁的书写,它向前向后兼容,推荐使用。
    <!doctype html>

在 HTML中 doctype 有两个主要目的。

  • 对文档进行有效性验证。

它告诉用户代理和校验器这个文档是按照什么 DTD 写的。这个动作是被动的,每次页面加载时,浏览器并不会下载 DTD 并检查合法性,只有当手动校验页面时才启用。

  • 决定浏览器的呈现模式对于实际操作,通知浏览器读取文档时用哪种解析算法。如果没有写,则浏览器则根据自身的规则对代码进行解析,可能会严重影响 html 排版布局。浏览器有三种方式解析 HTML 文档。

charset

html5 声明文档使用的字符编码,

<meta charset="utf-8">

html5 之前网页中会这样写:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

lang属性

简体中文

<html lang="zh-cmn-Hans">

繁体中文

<html lang="zh-cmn-Hant">

为什么 lang="zh-cmn-Hans" 而不是我们通常写的 lang="zh-CN" 呢,请移步阅读: 页头部的声明应该是用 lang=”zh-cn” 还是 lang=“zh-cmn-Hans”

优先使用 IE 最新版本和 Chrome

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

360 使用Google Chrome Frame

<meta name="renderer" content="webkit">

360 浏览器就会在读取到这个标签后,立即切换对应的极速核。 另外为了保险起见再加入

<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">

这样写可以达到的效果是如果安装了 Google Chrome Frame,则使用 GCF 来渲染页面,如果没有安装 GCF,则使用最高版本的 IE 内核进行渲染。

百度禁止转码

通过百度手机打开网页时,百度可能会对你的网页进行转码,脱下你的衣服,往你的身上贴狗皮膏药的广告,为此可在 head 内添加

<meta http-equiv="Cache-Control" content="no-siteapp" />

相关链接:百度SiteApp 转码说明及如何取消转码

SEO 优化部分

  • 页面标题<title>标签(head 头部必须)
    <title>your title</title>
    
  • 页面关键词 keywords
    <meta name="keywords" content="your keywords">
    
  • 页面描述内容 description
    <meta name="description" content="your description">
    
  • 定义网页作者 author
    <meta name="author" content="author,email address">
    
  • 定义网页搜索引擎索引方式,robotterms 是一组使用英文逗号「,」分割的值,通常有如下几种取值:none,noindex,nofollow,all,index和follow。
    <meta name="robots" content="index,follow">

viewport

viewport 可以让布局在移动浏览器上显示的更好。 通常会写

<meta name="viewport" content="width=device-width, initial-scale=1.0">

content 参数:

  • width viewport 宽度(数值/device-width)
  • height viewport 高度(数值/device-height)
  • initial-scale 初始缩放比例
  • maximum-scale 最大缩放比例
  • minimum-scale 最小缩放比例
  • user-scalable 是否允许用户缩放(yes/no)
  • minimal-ui iOS 7.1 beta 2 中新增属性,可以在页面加载时最小化上下状态栏。这是一个布尔值,可以直接这样写:
    <meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui">
    

而如果你的网站不是响应式的,请不要使用 initial-scale 或者禁用缩放。

<meta name="viewport" content="width=device-width,user-scalable=yes">

适配 iPhone 6 和 iPhone 6plus 则需要写:

<meta name="viewport" content="width=375">
<meta name="viewport" content="width=414">

大部分 4.7~5 寸的安卓设备的 viewport 宽设为 360px,iPhone 6 上却是 375px,大部分 5.5 寸安卓机器(比如说三星 Note)的 viewport 宽为 400,iPhone 6 plus 上是 414px。

ios 设备

添加到主屏后的标题(iOS 6 新增)

<meta name="apple-mobile-web-app-title" content="标题"> <!-- 添加到主屏后的标题(iOS 6 新增) -->

是否启用 WebApp 全屏模式

<meta name="apple-mobile-web-app-capable" content="yes" /> <!-- 是否启用 WebApp 全屏模式 -->

设置状态栏的背景颜色

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <!-- 设置状态栏的背景颜色,只有在 `"apple-mobile-web-app-capable" content="yes"` 时生效 -->

只有在 “apple-mobile-web-app-capable” content=”yes” 时生效

content 参数:

  • default 默认值。
  • black 状态栏背景是黑色。
  • black-translucent 状态栏背景是黑色半透明。 如果设置为 default 或 black ,网页内容从状态栏底部开始。 如果设置为 black-translucent ,网页内容充满整个屏幕,顶部会被状态栏遮挡。

禁止数字识自动别为电话号码

<meta name="format-detection" content="telephone=no" /> <!-- 禁止数字识自动别为电话号码 -->

iOS 图标

rel 参数: apple-touch-icon 图片自动处理成圆角和高光等效果。 apple-touch-icon-precomposed 禁止系统自动添加效果,直接显示设计原图。 iPhone 和 iTouch,默认 57×57 像素,必须有

<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-57x57-precomposed.png" /> <!-- iPhone 和 iTouch,默认 57x57 像素,必须有 -->

iPad,72×72 像素,可以没有,但推荐有

<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/apple-touch-icon-72x72-precomposed.png" /> <!-- iPad,72x72 像素,可以没有,但推荐有 -->

Retina iPhone 和 Retina iTouch,114×114 像素,可以没有,但推荐有

<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/apple-touch-icon-114x114-precomposed.png" /> <!-- Retina iPhone 和 Retina iTouch,114x114 像素,可以没有,但推荐有 -->

Retina iPad,144×144 像素,可以没有,但推荐有

<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144x144-precomposed.png" /> <!-- Retina iPad,144x144 像素,可以没有,但推荐有 -->

IOS 图标大小在iPhone 6 plus上是180×180,iPhone 6 是120×120。 适配iPhone 6 plus,则需要在中加上这段

<link rel="apple-touch-icon-precomposed" sizes="180x180" href="retinahd_icon.png">

iOS 启动画面

iPad 的启动画面是不包括状态栏区域的。

iPad 竖屏 768 x 1004(标准分辨率)

<link rel="apple-touch-startup-image" sizes="768x1004" href="/splash-screen-768x1004.png" /> <!-- iPad 竖屏 768 x 1004(标准分辨率) -->

iPad 竖屏 1536×2008(Retina)

<link rel="apple-touch-startup-image" sizes="1536x2008" href="/splash-screen-1536x2008.png" /> <!-- iPad 竖屏 1536x2008(Retina) -->

iPad 横屏 1024×748(标准分辨率)

<link rel="apple-touch-startup-image" sizes="1024x748" href="/Default-Portrait-1024x748.png" /> <!-- iPad 横屏 1024x748(标准分辨率) -->

iPad 横屏 2048×1496(Retina)

<link rel="apple-touch-startup-image" sizes="2048x1496" href="/splash-screen-2048x1496.png" /> <!-- iPad 横屏 2048x1496(Retina) -->

iPhone 和 iPod touch 的启动画面是包含状态栏区域的。

iPhone/iPod Touch 竖屏 320×480 (标准分辨率)

<link rel="apple-touch-startup-image" href="/splash-screen-320x480.png" /> <!-- iPhone/iPod Touch 竖屏 320x480 (标准分辨率) -->

iPhone/iPod Touch 竖屏 640×960 (Retina)

<link rel="apple-touch-startup-image" sizes="640x960" href="/splash-screen-640x960.png" /> <!-- iPhone/iPod Touch 竖屏 640x960 (Retina) -->

iPhone 5/iPod Touch 5 竖屏 640×1136 (Retina)

<link rel="apple-touch-startup-image" sizes="640x1136" href="/splash-screen-640x1136.png" /> <!-- iPhone 5/iPod Touch 5 竖屏 640x1136 (Retina) -->

添加智能 App 广告条 Smart App Banner(iOS 6+ Safari)

<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL"> <!-- 添加智能 App 广告条 Smart App Banner(iOS 6+ Safari) -->

iPhone 6对应的图片大小是750×1294,iPhone 6 Plus 对应的是1242×2148 。

<link rel="apple-touch-startup-image" href="launch6.png" media="(device-width: 375px)">

<link rel="apple-touch-startup-image" href="launch6plus.png" media="(device-width: 414px)">

Windows 8

Windows 8 磁贴颜色

<meta name="msapplication-TileColor" content="#000"/> <!-- Windows 8 磁贴颜色 -->

Windows 8 磁贴图标

<meta name="msapplication-TileImage" content="icon.png"/> <!-- Windows 8 磁贴图标 -->

rss订阅

<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" /> <!-- 添加 RSS 订阅 -->

favicon icon

<link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> <!-- 添加 favicon icon -->

移动端的meta

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection"content="telephone=no, email=no" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" /><!-- 删除苹果默认的工具栏和菜单栏 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black" /><!-- 设置苹果工具栏颜色 -->
<meta name="format-detection" content="telphone=no, email=no" /><!-- 忽略页面中的数字识别为电话,忽略email识别 -->
<!-- 启用360浏览器的极速模式(webkit) -->
<meta name="renderer" content="webkit">
<!-- 避免IE使用兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
<meta name="HandheldFriendly" content="true">
<!-- 微软的老式浏览器 -->
<meta name="MobileOptimized" content="320">
<!-- uc强制竖屏 -->
<meta name="screen-orientation" content="portrait">
<!-- QQ强制竖屏 -->
<meta name="x5-orientation" content="portrait">
<!-- UC强制全屏 -->
<meta name="full-screen" content="yes">
<!-- QQ强制全屏 -->
<meta name="x5-fullscreen" content="true">
<!-- UC应用模式 -->
<meta name="browsermode" content="application">
<!-- QQ应用模式 -->
<meta name="x5-page-mode" content="app">
<!-- windows phone 点击无高光 -->
<meta name="msapplication-tap-highlight" content="no">
<!-- 适应移动端end -->

严格模式和怪异模式、IE的文档模式和浏览器模式详解

最近在工作中经常遇到浏览器兼容性的问题,让我焦头烂额,这里总结下浏览器的严格模式和怪异模式以及IE浏览下的文档模式和浏览器模式等内容。

严格模式和怪异模式

现代浏览器一般具有多种渲染模式:

在“标准模式”(standards mode) 页面按照 HTML 与 CSS 的定义渲染,而在“怪异模式(quirks mode) 模式”中则尝试模拟更旧的浏览器的行为。 一些浏览器(例如,那些基于 Mozilla 的 Gecko 渲染引擎的,或者 Internet Explorer 8 在 strict mode 下)也使用一种尝试于这两者之间妥协的“近乎标准”(almost standards) 模式,实施了一种表单元格尺寸的怪异行为,除此之外符合标准定义。

通常,浏览器基于页面中文件类型描述的存在以决定采用哪种渲染模式;如果存在一个完整的 DOCTYPE 则浏览器将会采用标准模式,而如果它缺失则浏览器将会采用怪异模式。 例如,一个以如下 DOCTYPE 开始的网页将会触发标准模式:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">

如下 DOCTYPE 语法上是无效的,因为它包含公共标识符关键字 PUBLIC 却没有公共标识符(指示所用 HTML 版本的名称),也没有 HTML 文档类型定义的系统标识符 URL。这将会触发怪异模式:

<!DOCTYPE html PUBLIC>

此外,一个不含任何 DOCTYPE 的网页将会以 quirks 模式渲染。 对此一个值得一提的例外是微软的 Internet Explorer 6 浏览器,如果 DOCTYPE 之前有一个 XML 声明,不管是否指定完整的 DOCTYPE,它就会以 quirks 模式渲染一个页面。因此以如下代码开始的 XHTML 页面会被 IE 6 渲染为 quirks 模式:

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

在一定程度上,上述代码是有用的,然而它仅仅会对 IE 6 触发怪异模式。如果 DOCTYPE 之前有任何语句,quirks 模式在任何版本的 IE 中(截至 IE 9)同样会被触发。 例如,如果一个超文本文件在 DOCTYPE 前包含一个注释或者任何标签,IE (截至 9) 会使用 quirks 模式:

IE的文档模式和浏览器模式

IE从IE8引入了文档兼容性开始有了浏览器模式和文档模式(按F12可见)

“浏览器模式”:用于切换IE针对该网页的默认文档模式、对不同版本浏览器的条件备注解析、发送给网站服务器的用户代理(User-Agent)字符串的值。网站可以根据浏览器返回的不同用户代理字符串判断浏览器的版本和安装的功能,这样就可以向不同的浏览器返回不同的页面内容。

IE8的浏览器模式:

IE8的浏览器模式

 

“文档模式”用于指定IE的页面排版引擎(Trident)以哪个版本的方式来解析并渲染网页代码。切换文档模式会导致网页被刷新,但不会更改用户代理字符串中的版本号,也不会从服务器重新下载网页。切换浏览器模式的同时,浏览器也会自动切换到相应的文档模式。

IE9下的文档模式

浏览器模式

 

我的理解就是浏览器模式是指IE浏览器 向服务器发送请求时,告诉服务器自己是哪个浏览器哪个版本,这里是指告诉服务器我是IE7还是IE8或者9,然后浏览器根据收到的用户代理字段返回IE7或者IE8或者IE9的页面内容。

而文档模式则是指浏览器将服务器端发送过来的内容按照某种特定的浏览器(IE7,IE8,IE9等)来渲染页面。

这里还涉及到怪异模式(Quirks Mode),IE旧的怪异模式现在被称为IE5怪异模式,一般不予考虑。

DOCTYPE

为什么要研究这些呢,是为了强调按照标准写html语言的重要性,近来在工作中遇到的各种诡异的IEbug,花费了巨大精力调试,最后都是因为没有按照标准来写,没有doctyle头,标签没有闭合,导致各浏览器渲染不同诡异。

DOCTYPE是document type(文档类型)的简写,用来说明你用的XHTML或者HTML是什么版本。 其中的DTD(document type definition)叫文档类型定义,里面包含了文档的规则,浏览器就根据你定义的DTD来解释你页面的标识,并展现出来。 要建立符合标准的网页,DOCTYPE声明是必不可少的关键组成部分;除非你的XHTML确定了一个正确的DOCTYPE,否则你的标识和CSS都不会生效。

HTML5 (向后兼容,推荐使用):

<!DOCTYPE html>

HTML 4.01 Strict

This DTD contains all HTML elements and attributes, but does NOT INCLUDE presentational or deprecated elements (like font). Framesets are not allowed.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

HTML 4.01 Transitional

This DTD contains all HTML elements and attributes, INCLUDING presentational and deprecated elements (like font). Framesets are not allowed.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

HTML 4.01 Frameset

This DTD is equal to HTML 4.01 Transitional, but allows the use of frameset content.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">

XHTML 1.0 Strict

This DTD contains all HTML elements and attributes, but does NOT INCLUDE presentational or deprecated elements (like font). Framesets are not allowed. The markup must also be written as well-formed XML.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

XHTML 1.0 Transitional

This DTD contains all HTML elements and attributes, INCLUDING presentational and deprecated elements (like font). Framesets are not allowed. The markup must also be written as well-formed XML.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

XHTML 1.0 Frameset

This DTD is equal to XHTML 1.0 Transitional, but allows the use of frameset content.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

XHTML 1.1

This DTD is equal to XHTML 1.0 Strict, but allows you to add modules (for example to provide ruby support for East-Asian languages).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

IE兼容模式

IE 支持通过特定的 标签来确定绘制当前页面所应该采用的 IE 版本。除非有强烈的特殊需求,否则最好是设置为 edge mode,从而通知 IE 采用其所支持的最新的模式。

内核控制Meta标签:让360浏览器默认使用极速模式打开网页

进来调试一个网页在IE、Chrome、Firefox下均表现正常,但是在360浏览器兼容模式下表现诡异。(国产的各种shell,我就呵呵了。)查查了内核控制Meta标签:让360浏览器默认使用极速模式打开网页,方法如下:

  1. 网页头部加入
<meta name="renderer" content="webkit">

360浏览器就会在读取到这个标签后,立即切换对应的极速核。

  1. 另外为了保险起见再加入
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">

这样写可以达到的效果是如果安装了Google chrome frame,则使用GCF来渲染页面,如果没有安装GCF,则使用最高版本的IE内核进行渲染。

HTML5 Canvas 学习笔记之需要注意的问题以及知识点记录

  1. canvas元素实际上有两套尺寸。一个是元素本身的大小,还有一个是元素绘图表面(drawing surface)的大小,这两个尺寸默认为300px * 150px。如果给canvas设置width、height属性的话等于同时设置了canvas本身的大小和drawing surface的大小(正规的设置),如果没有给canvas设置width、height属性而只是通过css给canvas设置width、height的话等于只设置了canvas元素本身的大小而没有设置drawing surface的大小,此时浏览器会对drawing surface进行缩放,使其符合元素本身的大小,现象就好像是一张300*150的图片被缩放成其它的尺寸。
  2. fillText(‘bbs0101’, 0, 0) 方法是以文字左下角为基准绘制的
  3. Canvas环境状态的保存与恢复:context.save()   /   context.restore()
  4. Canvas画布状态的保存与恢复:
    drawingSurfaceImageData = context.getImagesData(0, 0, canvas.width, canvas.height);
    context.putImageData(drawingSurfaceImageData, 0, 0);
  5. 绘制线段时,如果要绘制一条真正1像素宽的线段,你必须将该线段绘在一像素中间的位置上(例如绘制横线,y=50.5),这样绘制出来的是一像素宽的线段,不能绘制在两个像素之间(例如绘制横线,y=50),这样绘制出来的是2像素宽的线段

 

ie6/ie7/ie8/ie9/ie10+和firefox和google chrome等一票浏览器hack方式整理

看标题就可以感觉到随着浏览器家族的不断膨胀,随之而来的前端开发兼容处理也貌似愈加复杂,嗯!貌似愈加复杂,其实整理一下也只是换汤不换药,今天整理两套hack方式,个人喜欢第二种,但是要先介绍第一种。

一.嵌入式hack方式(不推荐)

所谓嵌入式hack就是最早被人们所使用的方式,在此只整理ie的hack,其余一票高级浏览器同入口式hack方式

_width: 100px;     /ie6/
*+width: 100px;    /ie7/
*width: 100px      /ie6、ie7/
width: 100px;    /ie8/
width: 100px\9;    /ie6、ie7、ie8/
/* 由于本人不喜欢这种hack所以没有继续测试ie9+浏览器hack */

二.入口式hack方式(推荐)

优点,入口处控制,各个浏览器hack代码耦合性低,便于维护,说白了就是看着舒服,想看实例就看0101后花园首页源代码

首先在源代码中将原有的<html>进行如下替换:

<!--[if lt IE 7 ]><html class="ie ie6"><![endif]-->
<!--[if IE 7 ]><html class="ie ie7"><![endif]-->
<!--[if IE 8 ]><html class="ie ie8"><![endif]-->
<!--[if IE 9 ]><html class="ie ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html><!--<![endif]-->

然后css代码如下,我想你一看就能理解,不做解释

.box {
    background-color: white;    /所有浏览器/
} 
.ie6 .box {
    background-color: brown;    /ie6浏览器/
}
.ie7 .box {
    background-color: green;    /ie7浏览器/
}
.ie8 .box {
    background-color: blue;     /ie8浏览器/
}
.ie9 .box {
    background-color: pink;     /ie9浏览器/
}
.ie .box {
    background-color: #CAF4ED; /所有ie浏览器/
}
@media all and (min-width:0) {
    background-color: black;   /非ie6、7、8的其余高级浏览器/
}
@media all and (-webkit-min-device-pixel-ratio:0) {
    background-color: red;     /Google Chrome、Safari等一票webkit内核浏览器/
}

入口式hack方式的优雅想必大家已经领会了吧^-^Y