如何把 Vue 网页打包体积增大 1500%
免责声明:这篇文章主要是讽刺。我并不因为我曾经写过一些 TypeScript 就认为我比你强。我也不认为把网页的体积做大对我们来说是好事。请随意歪曲这些观点,以帮助我骗更多点击量。
作者:伯克·霍兰 译者:ssh
https://css-tricks.com/how-to-increase-your-page-size-by-1500-with-webpack-and-vue/
你懂的,有很多文章告诉你如何使你的页面尺寸变小:优化你的图像,删除无关的 CSS 规则,在 Dreamweaver 中使用框架集重写整个东西。看,沃尔玛又缩小了他们的页面大小,差不多这样。
为啥我们没有足够的文章告诉你怎么让页面变大?其实我能找到的唯一的文章就是这篇[1]来自 Geek Squad 的文章,它最后是关于把字体变大的。不错的开始,但我们可以做得更好!
增加点体重
那么,为什么要增大页面大小呢?这对那些使用低带宽网络的人不太好吧?我有几个很好的理由,而且不是刻意的,这里是其中的三个,因为三这个数字更令人满意[2]。
你有千兆的网络连接,而且你住在田纳西州(译者注:网速慢),所以肯定其他人的状况都比你好。 浏览器会缓存,傻瓜。这意味着你只需要下载一次站点。别抱怨了,第一世界问题[3]。 你并不关心人们是否访问过你的网站,因为你“工作是为了生活,而生活不是为了工作”。
如果这些完全能够认同的原因与你产生了共鸣,我想向你展示我是如何将我的 CSS 的大小增加 1500%,我行,你也行!用一个简单的 webpack 技巧足矣。
一个奇怪的技巧
这一切都始于我决定重构我的退休计划项目 The Urlist[4],用Bulma CSS 框架[5]。
网站的最初版本是纯手工研发的,我的 Sass 看起来就像《囤积者》(Hoarders)中的一集。
“伯克,你给我搞出 13 种不同的
.button
样式?你就不能挑一个,我们把剩下的 12 个都干掉这样你就有地方睡觉了?”
Bulma 还包括模态框,我之前一直使用第三方 Vue 组件来搞定它。
它还有一个汉堡式菜单,因为有一个众所周知的科学事实,没有汉堡就没有成功的网站。
听着,规则不是我定的。这就是商业运作的方式。
我对结果很满意。Bulma 风格鲜明,布局系统很容易学习。这就好像世界上有个地方的人理解 CSS,而且不讨厌我。这是一个很难找到的组合。
经过几周的重构之后(在此期间我会问自己,“你在做什么?!?网站明明正常运行的!),我终于完成了。顺便提醒一下,下次你考虑重构时,不要这样做。别管它了。如果你不给下一代留下任何技术债务,他们就会非常无聊,而这将是你的责任。
当我构建这个项目时,我注意到一些奇怪的事情:我的 CSS 的大小明显增加了。我手写的鬼东西东西压缩后只有 30KB,重构后却增加到 260KB。
更糟糕的是,Vue CLI 还在教训我……
当然,我没有理会。我不接受机器人的指令。
我所做的是部署它,到生产环境上,到互联网上。因为我没有花这么多时间重构,却不部署它。对,沉没成本之类的……恕我直言,我比你那些逻辑谬误的海报更务实。我想说的是,我来参加派对可不想没喝酒就回家。
然后我在推特上向矛盾的大伙宣布了我的成就,像往常一样。
此后不久,Jeremy Thomas,也就是 Bumla 的创始人(而且显然很喜欢龙珠[6])回应了。真的很快,就像有个白痴发推,就会发射出蝙蝠信号一样。(译者注:布尔玛是龙珠角色,悟空名场面那个。)
(译者注:这里作者回应的意思大概是说同一个样式出现了 13 次,比如 .section.is-medium[data-v-xxx]
,看起来是 Vue CSS 命名空间的问题)
重复的样式?13 次?命名空间到底是什么?那是 π 符号还是 Jeremy Thomas 的个性化标志?
就在那一刻,我意识到我不知道我在做什么。
抛弃 Sass,慢慢回退
我首先承认我对 CSS 了解不多,对 Sass 了解更少。Get 到我的点没?关于 Sass 更少!算了忘了吧。我不想要你怜悯的笑。(译者注:作者这里用的是 even Less about Sass,Less 有更少的意思,双关梗)
当我在 Vue CLI 项目中接入 Bulma 时,我创建了一个src/styles
文件夹,并放入了一个 bulma-but-not-all-of-bulma-only-some-of-it.scss
文件。他们说命名很难,但我想不通为什么。
那个文件导入了我想用的 Bulma 样式的片段。是 Bulma,但不是全部。只有一部分。(译者注:和 scss 文件的命名相呼应)
@import 'bulma/sass/utilities/_all.sass';
@import 'bulma/sass/base/_all.sass';
@import 'bulma/sass/form/shared.sass';
@import 'bulma/sass/form/input-textarea.sass';
// etc...
然后我把这个文件导入一个自定义的 Sass 文件,我称之为…… site.scss
。我喜欢把命名简单化。
@import "./bulma-but-not-all-of-bulma-only-some-of-it.scss";
html,
body {
background-color: #f9fafc;
}
// etc...
我想全局地将这些文件导入 Vue,这样我就可以在每个组件中使用它们。我想用正确的方法、规范的方法。我觉得已经很明显了,从我愿意在生产中部署 2+ MB 的 CSS 来看,我想用“正确的方式”做事情。
我读了 Sarah Drasner 写的一篇很棒的博文[7],名为《如何将 Sass 文件导入到你的 Vue 应用的每个组件中》。她展示了如何通过修改 vue.config.js
文件来实现这一点。
module.exports = {
css: {
loaderOptions: {
sass: {
additionalData: `@import "~@/styles/site.scss";`,
},
},
},
};
我不明白的是,这会把 Sass 导入到 Vue 应用的每个组件中。你知道,就像这篇博客文章的标题所说的那样。这也是我如何以一堆有着 data-v-
属性选择器的重复的样式收尾。感谢它,我才有了 scoped styles。
Vue 如何处理 scoped
Vue 允许你在组件中 “scope” 样式。这意味着样式只影响它所在的组件,而不影响页面的其余部分。并不是什么神奇的浏览器 API 来做到这一点的。Vue 是通过在元素和选择器中动态插入一个 data-
属性来搞定的。例如这个:
<template>
<button class="submit">Submitbutton>
<template>
<style lang="scss" scoped>
.submit {
background-color: #20ae96;
}
style>template
>template
>
……变成这样:
<button class="submit" data-v-2929>Submitbutton>
<style>
.submit[data-v-2929] {
background-color: #20ae96;
}
style>
这个动态 data 标记也被添加到组件中的每个子元素中。因此,这个组件的每个元素和每个样式在运行时都会有一个 data-v-2929
。
如果你导入一个 Sass 文件到你的组件中,其中有实际的样式,Vue(通过 Webpack)将拿到这些样式,并使用动态 data-
属性“命名空间”它们。结果就是你丫的把 Bulma 包含在你的应用中整整 13 次,并且前面有一堆奇怪的 data-v
。
但这引出了一个问题:如果 webpack 在每个组件中都渲染 CSS,为什么你还想使用 vue.config.js
的手段呢?一句话:变量 。
变量共享问题
你不能在一个组件中定义 Sass 变量,然后从另一个组件引用它。这也很难管理,因为你会到处定义和使用变量。只有我才会写出这样的代码。
另一方面,你可能会把所有的变量放到一个 variables.scss
文件中。然后,每个组件将引用这个变量的中心存储仓库。在每个组件中导入变量文件是多余的。这也是过度的。没有必要的。且冗长的。
这正是 Sarah 的文章所要解决的问题:将 Sass 文件导入项目中的每个组件。
把变量之类的东西导入到每个组件中没问题,因为变量不会被渲染。如果导入 200 个变量,只引用其中一个,谁会在意呢?这些变量在渲染后的 CSS 中并不存在。
例如,这个:
<style lang="scss" scoped>
$primary: #20ae96;
$secondary: #336699;
.submit {
background-color: $primary
}
style>
……变成这样:
<style>
.submit[data-v-2929] {
background-color: #20ae96;
}
style>
所以,这里有两个问题:
Bulma 需要全局化。 Bulma 的变量应该在组件中可访问。
我们需要的是和 Sarah 的技术巧妙结合,以及关于 Bulma 结构的一些专有知识。
在 Vue 中使用 Bulma
通过在src/styles
目录下创建三个文件,我们将以最少的重复来完成这一任务:
variables.scss
:这个文件将是你拉入 Bulma 的变量和覆盖/定义你自己的。请注意,必须包括以下三个文件,以获得所有 Bulma 的变量。它们的顺序是这样的……
// Your variables customizations go up here
// Include Bulma's variables
@import 'bulma/sass/utilities/initial-variables.sass';
@import 'bulma/sass/utilities/functions.sass';
@import 'bulma/sass/utilities/derived-variables.sass';
bulma-custom.scss
:在这个文件里你可以按需引入 Bulma 的变量。它应该引用 variables.scss
文件。
@import './variables.scss';
/* UTILTIES */
@import 'bulma/sass/utilities/animations.sass';
@import 'bulma/sass/utilities/controls.sass';
@import 'bulma/sass/utilities/mixins.sass';
// etc...
site.scss
:这里引入了 bulma-custom.scss
文件,也是定义整个项目使用的全局样式的地方。
@import url('https://use.fontawesome.com/releases/v5.6.3/css/all.css');
@import './bulma-custom.scss';
html,
body {
height: 100%;
background-color: #f9fafc;
}
// etc...
在你的 main.js
中导入 site.scss
。或者在我的例子中,main.ts
。用了 TypeScript 所以我比你更强吗?是的。是的确实是的。
import Vue from 'vue';
import App from './App.vue';
import router from './router';
// import styles
import '@/styles/site.scss';
这使得我们在每个组件中都可以使用所有的 Bulma 片段。它们是全局性的,但只会包含一次。
根据 Sarah 的文章,添加 variables.scss
文件到 vue.config.js
文件。
module.exports = {
css: {
loaderOptions: {
sass: {
additionalData: `@import "~@/styles/variables.scss";`,
},
},
},
};
这让你可以从任何 .vue
组件中引用任何 Bulma 变量或自己的变量。
现在两全其美了:Bulma 在全局范围内可用,并且你仍然可以在所有组件中访问所有的 Bulma 变量。
现在的 CSS 总大小?约比以前小 1500%……
拿去吧,沃尔玛。
一个 PR 的救赎
为了弥补自己的错误,我向 Bulma 文档提交了一份 PR,讲述了如何在 Vue CLI 项目中定制 Bulma。这是一种悔悟,因为我发了推特让布尔玛看起来像个问题,其实伯克才是个问题。
你可能察觉到我已经弄明白的事情:伯克一直都是问题所在。
参考资料
这篇: https://www.bestbuy.com/site/tech-tips/change-web-page-size/pcmcat748301880661.c?id=pcmcat748301880661&intl=nosplash
[2]令人满意: https://en.wikipedia.org/wiki/Rule_of_three_%28writing%29
[3]第一世界问题: https://zh.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E4%B8%96%E7%95%8C%E9%97%AE%E9%A2%98
[4]The Urlist: https://www.theurlist.com/
[5]Bulma CSS 框架: https://css-tricks.com/how-to-increase-your-page-size-by-1500-with-webpack-and-vue/#:~:text=over%20to%20the-,Bulma%20CSS%20framework,-.
[6]龙珠: https://dragonball.fandom.com/wiki/Bulma
[7]一篇很棒的博文: https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app