浏览器兼容性解决方案
一、JS 兼容
推荐使用 legacy
推荐原因:为打包后的文件提供传统浏览器兼容性支持(不支持 esm 的浏览器),同时还有语法降级处理
缺点:打包时间较长
注意: vite4 的 legacy 插件最高目前是 4.1.1,再高的版本只支持 vite5 了,这里以 vite5 为例。
集成
- 安装依赖
使用 npm
npm i @vitejs/plugin-legacy -D
npm i terser -D
使用 yarn
yarn add @vitejs/plugin-legacy -D
yarn add terser -D
使用 pnpm
pnpm add @vitejs/plugin-legacy -D
pnpm add terser -D
- 配置
vite.config.ts
文件
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import legacy from "@vitejs/plugin-legacy";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ["defaults", "Chrome >= 64"], // 指定需要支持的浏览器版本
additionalLegacyPolyfills: ["regenerator-runtime/runtime"], // 可选, 额外的 polyfill 需要安装
renderLegacyChunks: true, // 可选,是否生成传统版本的 chunk,默认true // 可选,不配置使用默认。具体的 polyfill 列表
polyfills: [
"es.symbol",
"es.array.filter",
"es.promise",
"es.promise.finally",
"es.object.assign",
"es.map",
"es.set",
"es.array.for-each",
"es.object.define-properties",
"es.object.define-property",
"es.object.get-own-property-descriptor",
"es.object.get-own-property-descriptors",
"es.object.keys",
"es.object.to-string",
"web.dom-collections.for-each",
"esnext.global-this",
"esnext.string.match-all",
"es.array.iterator",
"es.string.includes",
"es.string.starts-with",
"es.object.values",
],
}),
], // 配置打包后的目标esm的版本
build: {
minify: "terser", // 使用 terser 进行压缩
},
});
legacy 插件中如果我们没有指定targets
参数时,插件会默认的取项目中的支持的浏览器范围的文件 .browserslistrc
,个人对此进行了一个大概的配置
Chrome >= 51
Edge >= 15
Safari >= 10
Firefox >= 54
Opera >= 38
iOS >= 10
not ie <= 11
Android >= 5
not IE <= 11
更多配置请参考 legacy 官方文档
- 构建
通过npm run build
打包可以看到 script 发生了变化,除了type="module"
还有一个nomodule
脚本,nomodule
这个属性表示在支持 esm 的浏览器不运行里面的代码,而不支持 esm 的浏览器又无法识别type="module"
,反而会去运行 nomodule 的 script,从而实现了降级区分。而降级的脚本都会携带一个legacy
文本。
大概效果如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link
rel="stylesheet"
href="https://printjs-4de6.kxcdn.com/print.min.css"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script type="module" crossorigin src="/assets/index-BUwO137t.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BsmRYvi8.css" />
<script type="module">
import.meta.url;
import("_").catch(() => 1);
(async function* () {})().next();
if (location.protocol != "file:") {
window.__vite_is_modern_browser = true;
}
</script>
<script type="module">
!(function () {
if (window.__vite_is_modern_browser) return;
console.warn(
"vite: loading legacy chunks, syntax error above and the same error below should be ignored"
);
var e = document.getElementById("vite-legacy-polyfill"),
n = document.createElement("script");
(n.src = e.src),
(n.onload = function () {
System.import(
document
.getElementById("vite-legacy-entry")
.getAttribute("data-src")
);
}),
document.body.appendChild(n);
})();
</script>
</head>
<body>
<div id="app"></div>
<script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
<!-- 这里是legacy处理后的 -->
<script nomodule>
!(function () {
var e = document,
t = e.createElement("script");
if (!("noModule" in t) && "onbeforeload" in t) {
var n = !1;
e.addEventListener(
"beforeload",
function (e) {
if (e.target === t) n = !0;
else if (!e.target.hasAttribute("nomodule") || !n) return;
e.preventDefault();
},
!0
),
(t.type = "module"),
(t.src = "."),
e.head.appendChild(t),
t.remove();
}
})();
</script>
<script
nomodule
crossorigin
id="vite-legacy-polyfill"
src="assets/polyfills-legacy-BuMl3xCl.js"
></script>
<script
nomodule
crossorigin
id="vite-legacy-entry"
data-src="assets/index-legacy-BrIJujGY.js"
>
System.import(
document.getElementById("vite-legacy-entry").getAttribute("data-src")
);
</script>
</body>
</html>
二、CSS 兼容
推荐使用 postcss-preset-env
插件
推荐原因:
- 自动添加浏览器前缀
- 减少手动工作
- 无缝集成
- 提高兼容性和性能
集成
- 安装依赖
使用 npm
npm i postcss-preset-env -D
使用 yarn
yarn add postcss-preset-env -D
使用 pnpm
pnpm add postcss-preset-env -D
- 配置文件
vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import poscssPresetEnv from "postcss-preset-env";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
// ⚠️关键代码
plugins: [
poscssPresetEnv({
stage: 1, // 配置阶段,1 表示启用大多数新特性
features: {
"nesting-rules": true, // 启用嵌套规则
"custom-properties": true, // 启用自定义属性 // 其他特性配置
}, // 启用 autoprefixer
autoprefixer: {
grid: true,
}, // 一定要指定浏览器版本,否则autoprefixer将无法正确处理
browsers: ["last 2 versions", "ie >= 11"],
}),
],
},
},
});
- 构建
打包前:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link
rel="stylesheet"
href="https://printjs-4de6.kxcdn.com/print.min.css"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
</body>
<style>
:root {
--main-color: '#fef'
}
.card {
width: 240px;
margin: 10px;
border: 1px solid #ccc;
box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.3);
border-radius: 10px;
padding: 10px;
transition: all .3s;
display: grid;
&:hover {
color: var(--main-color);
}
}
.buttom {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</html>
打包后:
可以看到对自定义属性 --main-color
的使用,会多一种兼容写法;对box-shadow transition flex
等属性,自动添加了浏览器的内核的前缀
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link
rel="stylesheet"
href="https://printjs-4de6.kxcdn.com/print.min.css"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
</body>
<style>
:root {
--main-color: '#fef'
}
.card {
width: 240px;
margin: 10px;
border: 1px solid #ccc;
-webkit-box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.3);
box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.3);
border-radius: 10px;
padding: 10px;
-webkit-transition: all .3s;
transition: all .3s;
display: grid;
}
.card:hover {
color: '#fef';
color: var(--main-color);
}
.buttom {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
</style>
</html>
三、CSS 原子化插件
推荐使用 unocss
推荐原因:unocss 是一个 css 引擎,unocss 本身不提供任何类名 css,它只是解决 Tailwind 以及 Windi 的编译和打包的某些问题,也就是它可以配合 Tailwind 或者 windi 使用,以提供更快的编译打包速度。可以在开发中尽可能的减少自定义样式的编写。
参考页面:unocss.dev/guide/
集成
- 安装依赖
使用 npm
npm i unocss -D
使用 yarn
yarn add unocss -D
使用 pnpm
pnpm add unocss -D
- 配置文件
- 创建一个
uno.config.ts
文件
// uno.config.ts
import { defineConfig } from "unocss";
export default defineConfig({
// ...UnoCSS options
});
- 配置
vite.config.ts
文件
// vite.config.ts
import UnoCSS from "unocss/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [UnoCSS()],
});
- 在入口页面导入,一般为
main.ts
import "virtual:uno.css";
- 额外配置:
推荐 VSCode 下载一个扩展 UnoCSS 可以在开发过程中有很好的提示效果。