那些與 IE 相伴的日子
前言
Internet Explorer(簡稱:IE)是 微軟公司 (https://baike.baidu.com/item/微軟公司/732128) 為了對抗 網(wǎng)景瀏覽器 (https://baike.baidu.com/item/網(wǎng)景瀏覽器)(NetscapeNavigator)從而投入開發(fā),并于 1995 年推出的一款網(wǎng)頁瀏覽器,曾經(jīng)一度成為同 Windows 系統(tǒng)捆綁安裝的流氓軟件橫行于世,也占據(jù)了極高的市場份額,但在近些年里,它卻一直因為本身的落后而被眾多用戶和開發(fā)者詬病。
如今,即便是連微軟公司自己都放棄了更新 IE,但一眾 Web 開發(fā)者們?yōu)榱瞬糠秩栽趫猿质褂?IE 瀏覽器的用戶,卻依然不得不向下兼容,筆者也是其中的一員,本篇文章記錄了我在工作期間為了兼容 IE( IE9及以上 )做過的一些調(diào)整。
模擬 IE 版本環(huán)境
許多開發(fā)者們的電腦本身是 Mac 系統(tǒng),是無法安裝 IE 瀏覽器的,這個時候就需要安裝虛擬機提供 Windows 環(huán)境測試 IE 瀏覽器下的效果了。然而安裝的虛擬機比較占用空間,這個時候,借用另一臺 Windows 系統(tǒng)的電腦,訪問 Webpack (https://webpack.docschina.org/) (或其他編譯打包器)配置的局域網(wǎng)下的頁面地址,以此調(diào)試,也不失為一個好選擇。
許多國產(chǎn)瀏覽器也提供了極速、兼容的雙內(nèi)核模式,極速模式下使用 Chrome 等非 IE 內(nèi)核、兼容模式下使用 IE 內(nèi)核,以應(yīng)對不同頁面的使用,打開控制臺,可以切換模擬不同的 IE 版本(盡管只是模擬,有些時候并不準確)。

兼容 IE 下的樣式
其實很多瀏覽器不兼容的問題我們都可以從這個網(wǎng)站 caniuse (https://www.caniuse.com/) 上查詢到,不止 IE,還包括 Safari、Firefox 以及他們在安卓系統(tǒng)中對應(yīng)的瀏覽器兼容能力也被很好的總結(jié)在這里了。然而,我們是很難一次性查完所有的差異點再投入開發(fā)的,這里分享幾個我在開發(fā)中遇到的問題,以及對應(yīng)的解決方法吧。
1)圖片定寬不定高會變形
在我平常做首屏 Banner 大圖的時候,有時候為了快,直接寫一個寬度 width: 1200px 就覺得萬事大吉了,在 Chorme 上確實也表現(xiàn)良好,不負所望,但是當測試到 IE9、IE10 時,都會存在一個問題是圖片變形,如下圖所示。


當我打開 IE 瀏覽器的 DOM 資源管理器的時候發(fā)現(xiàn),IE 瀏覽器對我 <img /> 標簽多添加了一段這樣的屬性: width="824" height="300",而這個寬度和高度是從哪里來的呢?我選中下載下來的圖片,右擊查看詳情,發(fā)現(xiàn)這個圖片文件本身的寬度和高度就是 824px 和 300px,于是答案便可以知曉了。
當我設(shè)置圖片標簽的 src 的時候, IE 瀏覽器自動將原圖片的寬、高設(shè)置成了 <img /> 的屬性,這樣導(dǎo)致我使用 CSS 只設(shè)置寬度為 1200px 而沒有設(shè)置高的時候,<img /> 的生效高度便是原圖的高度 300px。而 Chrome 對 <img /> 標簽什么都沒有添加,所以標簽的高度 height 也就是按照圖片等比例縮放后的高度,不會變形。
Chrome 下的表現(xiàn)

IE 下的表現(xiàn)

解決方法也很簡單,就是在 <img /> 標簽的的 class 樣式里,再添加一個簡單的 height: auto;,同時對寬高進行設(shè)置,覆蓋掉原標簽自動添加的寬度和高度,這樣就可以解決變形的問題了。
2)IE 下 8 位色值不生效
在之前的開發(fā)中,我都習(xí)慣了使用 6 位色值,也不曾出現(xiàn)過問題,直到有一次,運營同學(xué)反饋在組件配置平臺下選中了某個顏色,卻一直不生效,通過排查問題,才發(fā)現(xiàn)了原來輸出的色值是 8 位,而正是這多余的兩位,在 IE 瀏覽器下并不通用。

我們知道,CSS 顏色使用組合了紅、綠、藍顏色值 (RGB) 的十六進制 (hex) 表示法進行定義,十六進制值使用三個雙位數(shù)來編寫,并以 # 符號開頭(如:#FF0000),同時, Chrome 瀏覽器支持 8 位色值(如 #FF0000ee),最后兩位表示不透明度 Alpha 值,其中 00 表示不透明度為 0,也就是全透明狀態(tài),F(xiàn)F 表示不透明度 100%,也就是全不透明狀態(tài),但在 IE 瀏覽器下不支持。
IE 情況下,使用 8 位色值,不但最后兩位的不透明度無法生效,反而整個顏色設(shè)置都不能生效,下面是一個簡單的 Demo 來模擬這種情況,標題的顏色設(shè)置不生效,所以呈現(xiàn)出默認的黑色狀態(tài)。



解決方法也比較簡單,在這種場景下,不透明度不是必須的,可以刪除掉最后兩位,僅使用 6 位色值即可。如果實在需要不透明度,我們可以使用 rgba 的格式,用最后一位值來實現(xiàn)透明度,如 background-color: rgba(255,0,0,0.3),即使在 IE9 上也可以表現(xiàn)良好。

3)處理左右鏡像
IE9 支持了 CSS3 的許多屬性,但還是有許多力所不能及的地方。比如,有一次的開發(fā)場景是希望在標題的兩邊做出對稱的兩種圖樣,于是我對這張圖拷貝出來的第二份設(shè)置了 transform:rotateY(180deg); 讓圖片繞 Y 軸旋轉(zhuǎn),IE9 雖然已經(jīng)支持了 trasform 2D 旋轉(zhuǎn),但是并不支持 trasform 3D 旋轉(zhuǎn),所以會出現(xiàn)如下所示的問題。

這里我們可以使用 IE9 支持的 canvas 畫布將坐標軸翻轉(zhuǎn) ,繪制圖像,就能得到一個左右對稱的圖片了。Html 中需要對原始 <img /> 標簽進行寬度和高度的顯式設(shè)置,才能保證 <canvas> 中有準確的寬高。代碼如下。
getRotateImg = (imgSourceId = '') => {
const imgNode = document.getElementById(imgSourceId);
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'canvas');
// 設(shè)置 canvas 的寬高,防止變形
canvas.setAttribute('width', imgNode.style.width);
canvas.setAttribute('height', imgNode.style.height);
const width = parseInt(imgNode.style.width);
const height = parseInt(imgNode.style.height);
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = imgNode.src;
imgNode.parentNode.appendChild(canvas);
img.onload = function() {
console.log(imgNode.style.width);
// 將坐標原點移動到畫布最右端,使反向圖片向左繪制,呈現(xiàn)在畫布范圍內(nèi)
ctx.translate(width, 0);
//左右鏡像翻轉(zhuǎn)坐標系
ctx.scale(-1, 1);
ctx.drawImage(img, 0, 0, width, height);
}
}
實際效果如圖所示。

4)放棄 Flex 布局
在初識 Flex 布局(彈性布局)的時候,會喜歡上它的靈活簡單,但是 IE9 下并不支持 Flex 布局,我們可以用其他方式來代替。
比如我們可以這樣通過 display: table、display: table-cell 實現(xiàn)一個簡單的等分效果,在這種情況下,傳統(tǒng)的 margin 無法提供外邊距,我們可以使用 border-space 代替。

<div class="wrapper-2">
<div class="flex-2">4 等分</div>
<div class="flex-2">4 等分</div>
<div class="flex-2">4 等分</div>
<div class="flex-2">4 等分</div>
</div>
CSS 代碼
.wrapper-2 {
height: 100px;
width: 80%;
margin: 20px auto;
background-color: wheat;
display: table; /* 主要代碼 */
border-spacing: 30px; /* 主要代碼 */
}
.flex-2 {
background-color: pink;
padding: 10px;
text-align: center;
border: solid 2px purple;
display: table-cell; /* 主要代碼 */
}
或者使用 text-align: center,vertical-align: middle 配合 display: inline-block 達到類似的效果,如下:

.wrapper-3 {
height: 200px;
width: 80%;
margin: 20px auto;
background-color: wheat;
line-height: 200px;
text-align: center; /* 主要代碼 */
}
.flex-3 {
width: 80px;
line-height: 100px;
background-color: pink;
margin: auto 20px;
height: 100px;
border: solid 2px purple;
text-align: center;
display: inline-block; /* 主要代碼 */
vertical-align: middle; /* 主要代碼 */
}
關(guān)于 CSS Hack
CSS Hack 的原理是根據(jù)不同瀏覽器和瀏覽器不同的版本對 CSS 的解析不同,分別書寫不同的代碼加以應(yīng)對。常見的寫法有 3 種:條件注釋法、CSS 屬性前綴法、選擇器前綴法,一般寫 Hack 的順序是:從最新版本到低版本,比如:新版本、IE(10/9/8)、IE(7/6),具體寫法可以參考這篇文章 CSS Hack 合集 (https://www.w3cschool.cn/lugfe/lugfe-vxfp25zq.html)。
但是過多地依賴 CSS hack 會導(dǎo)致代碼非常的不整潔,也可能會對后續(xù)的兼容留下隱患,所以實際很少使用。
例如這些:
只在 IE 下生效
<!--[if IE]>
這段文字只在IE瀏覽器顯示
<![endif]-->
只在 IE6 下生效
<!--[if IE 6]>
這段文字只在IE6瀏覽器顯示
<![endif]-->
IE9 不支持 History 路由
在單頁面應(yīng)用中,存在著前端路由的概念,哈希路由兼容性好,但是 URL 總是存在著/#會讓人覺得有些不好看,于是我們想到了清爽簡潔的 History 路由。
然而,在 IE 9 條件下,由于缺少 window.history 對象,自然也不能調(diào)用 history.pushState,history.replaceState 方法,所以 Chrome 下能夠正常使用的 History 路由模式不能生效。這個時候我們有幾種解決方案了,一是選擇哈希路由,二是直接做成多頁面應(yīng)用,跳轉(zhuǎn)時刷新整個頁面,也可以選擇使用 history.js (https://github.com/browserstate/history.js/) ,里面已經(jīng)實現(xiàn)了常見的 History 路由的 Api。

在 IE 上使用 ES6
@babel/polyfill
IE 不支持許多 ES6 的語法,比如 Array.from(),Object.assign() 等常見函數(shù),所以我們可以使用工具鏈 Babel (https://www.babeljs.cn/docs/) 中的 @babel/polyfill (https://www.babeljs.cn/docs/babel-polyfill) 將代碼轉(zhuǎn)換成可以向后兼容、在低版本上也能夠使用的的語法,比如這樣:
// 我們書寫的原始代碼
[1, 2, 3].map((n) => n + 1);
// 經(jīng)過轉(zhuǎn)換后的代碼
[1, 2, 3].map(function(n) {
return n + 1;
});
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
在 node 環(huán)境中使用
require("babel-polyfill");在 es6 中使用
import "babel-polyfill";在 webpack 中使用
module.exports = {
entry: ["babel-polyfill", "./app/js"]
};
以在 webpack 中配置為例,webpack.config.js 代碼如下:
var path = require("path");
module.exports = {
entry: {
entry: ["@babel/polyfill", "./index.js"], // 在入口文件 index.js 前面加入 "@babel/polyfill" 這個配置
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader", // 需要安裝 babel-loader 此配置可將所有 js,jsx 后綴的文件進行轉(zhuǎn)換
options: {
babelrc: false,
presets: [
[require.resolve("@babel/preset-env"), { modules: false }], // webpack 已做了模塊化打包,所以此處 modules 里
],
cacheDirectory: true,
},
},
},
],
},
plugins: [],
};
總結(jié)
以上是我在兼容 IE(IE9 及以上) 過程中踩過的坑和進行的調(diào)整了。技術(shù)是死的,應(yīng)用卻是活的,我們應(yīng)當掌握常見的兼容能力,但有時候,絞盡腦汁地向下兼容反而不如換一個更靈活、成本更低的方式表達。我們期待著多年以后,用戶們能夠放棄 IE,擁抱更敏捷好用的瀏覽器,迎接一個新的時代。
參考文檔
JS 實現(xiàn)兼容 IE 圖片向左或向右翻轉(zhuǎn)(https://blog.csdn.net/weixin_30920091/article/details/98890519)
CSS Hack 合集 (https://www.w3cschool.cn/lugfe/lugfe-vxfp25zq.html)
最后
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:
點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)
歡迎加我微信「huab119」拉你進技術(shù)群,長期交流學(xué)習(xí)...
關(guān)注公眾號「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


