【事故记录】子组件样式没有被正确应用?关于style写的时候为啥应该嵌套在一个总的容器class里

项目技术栈

vue3 js OA 后台管理类型的项目

场景

代码发布到生产环境后,测试反馈某些页面的图标样式大小错乱,具体表现为:

  1. 首次加载进入到某个使用 SVG 组件的页面内,样式一切正常显示
  2. 点击左侧菜单跳转进入其他页面后,然后点击回退返回前一个页面发现 SVG 子组件的大小错乱
  3. 所有应用 svg 组件的页面都有这种问题
  4. 仅生产环境有此问题,测试环境和本地代码无问题

问题代码展示

  • 有问题的子组件 SvgIcon
xml 复制代码
<template>
  <svg :class="svgClass">
    <use :xlink:href="iconName" />
  </svg>
</template>
<script>
export default {
  name: "SvgIcon",
  props: {
    iconClass: {
      type: String,
      required: true,
    },
    className: {
      type: String,
      default: "",
    },
    size: {
      type: Number,
      default: 16,
    },
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`;
    },
    svgClass() {
      if (this.className) {
        return "svg-icon " + this.className;
      } else {
        return "svg-icon";
      }
    },
  },
};
</script>

<style lang="less" scoped>
@icon-bg: #1890ff;
.svg-icon {
  width: 1.5em;
  height: 1.5em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
  margin: 0 5px;
  cursor: pointer;
}
</style>
  • 使用子组件的父组件(关键代码节选,并非完整页面)

DOM:

xml 复制代码
<template>
  <div class="mainpage">
                 ...
		<SvgIcon v-if="record.selfStatus === 0" className="statusIcon lineMiddle" icon-class="submit-waitSubmit"></SvgIcon>
		<SvgIcon v-if="record.selfStatus === 1" className="statusIcon lineMiddle" icon-class="submit-success"></SvgIcon>
		<SvgIcon v-if="record.selfStatus === 2" className="statusIcon lineMiddle" icon-class="submit-waitEdit"></SvgIcon>
                ...
  </div>
</template>

CSS:

xml 复制代码
<style scoped>
.mainpage {
  width: 100%;
  height: 100%;
}
.statusIcon {
	width: 12px;
	height: 12px;
	display: inline-block;
	vertical-align: top;
	margin-right: 6px;
	margin-left: 0;
}
</style>

问题排查

  1. 关于样式错乱,直接体现为父组件定义了12像素宽高,但是在错乱之后,控制台显示12像素被子组件原本定义的宽高1.5em覆盖。
  • 最开始我是直接通过class 绑定在 SvgIcon标签上,怀疑父子组件样式权重问题,因此通过className props传入父组件的class,保证是处于子组件绑定的class后面,但无效。
  1. 为什么生产环境才有这种毛病,为什么子组件的样式始终在父组件后面
  • 在本地设置环境为production,然后打包代码后发现,打包会有多个chunk文件,而非production环境的打包只有一个chunk
  • 在生产环境下切换页面,然后查看控制台发现,当进入且仅第一次某个页面后,会在html头部的尾部添加其样式引入文件,而非production环境下没有这种引入的过程
  • scoped本来会添加唯一标识的,但SvgIcon具有两个data-v标识,一个是它自己在所有页面共用的一种标识,一个是父组件渗透下来的标识。而子组件选择器样式,和父组件定义的样式层级同级,造成二者权重相冲,也就是说,谁后声明谁就牛逼。而切换页面后,这个子组件定义的宽高样式跑到了父组件定义的样式之后,冲掉了当前页面本该展示的模样。

反思和知识点

  1. 关于样式scoped
  • scoped样式的data-v会给页面的所有dom节点加上,而子组件自身由于也使用了scoped,导入的过程中又会有一个在各父组件里固定的data-v,因此在这个意义上,子组件data-v并不是"唯一"的,会有两个。而只有页面的根节点(即我例子所示最外层容器mainpage),它只具备一个data-v且唯一,也就是说,如果我们页面的所有选择器都挂在这个页面选择器之后,可以保证其样式选择器是有唯一性的,不会被其他data-v选到同一个元素
  • 在本次踩坑之前,我认为scoped绑定的style全都是唯一的,样式也就写的比较随意,没有考虑非要放在mainpage里
  1. 关于css打包 本次踩坑,包括现在我都有一些疑问,关于production环境下的打包会生成多个chunk文件,而其他我自己定义的环境只有一个;每次切换不同的页面,production环境会加载对应的样式,而其他环境则像是打包完一开始全部加载完毕。
  • 在vue.config.js里有一个相关的css配置,此前没有去了解
css 复制代码
css: {
  	loaderOptions: {
  		less: {
  			lessOptions: {
  				modifyVars: {
  					'primary-color': '#3C53FF',
  				},
  				javascriptEnabled: true,
  			},
  		},
  	},
  	// extract: true
  },

这里的extract是我后来配置的,默认情况下这个配置在判断当前env为production的情况下,它是true,其他环境是false.因此产生差异。如果自己主动配置为true,那么任何环境都会打包出css,而不是内联打包到js里面

最终解决方案

按照反思1中所提,将父组件内所有样式全部写在mainpage,即最外层容器class下面.可保证样式选择器前额外带上父组件的classname,权重会大于子组件选择器

xml 复制代码
<style lang='less' scoped>
    .mainpage {
      width: 100%;
      height: 100%;
      .statusIcon {
          width: 12px;
          height: 12px;
          display: inline-block;
          vertical-align: top;
          margin-right: 6px;
          margin-left: 0;
      }
    }
</style>

如果不想改动css写法,也可以考虑按照反思2的点将vueconfig.js里extract设置为false,这样他就不会打包出css,直接内联打包到js脚本里,这样的话也可以保证顺序,子组件样式先生成,而后父组件样式覆盖子组件。

但这种方式感觉是破坏了他逐步加载css的初衷,我最后没有采用

css 复制代码
  css: {
		loaderOptions: {
			less: {
				lessOptions: {
					modifyVars: {
						'primary-color': '#3C53FF',
					},
					javascriptEnabled: true,
				},
			},
		},
		extract: false
	},
相关推荐
be or not to be4 小时前
CSS 背景(background)系列属性
前端·css·css3
冴羽4 小时前
CSS 新特性!瀑布流布局的终极解决方案
前端·javascript·css
牛奶皮子6 小时前
合并 CSS 文件可以减少 HTTP 请求数,因为每个请求都会带来额外的网络开销
css·网络·http
幻影星空VR元宇宙12 小时前
9D裸眼轨道影院投资多少钱与5D动感影院设备的市场潜力分析
css·百慕大冒险·幻影星空
proud121214 小时前
使用thymeleaf生成PDF方案
javascript·css·pdf
霍理迪16 小时前
CSS——背景样式以及雪碧图、渐变
前端·css
wordbaby1 天前
Flexbox 布局中的滚动失效问题:为什么需要 `min-h-0`?
前端·css
前端小黑屋1 天前
查看 Base64 编码的字体包对应的字符集
前端·css·字体
hqwest1 天前
码上通QT实战04--主窗体布局
开发语言·css·qt·布局·widget·layout·label
狗哥哥2 天前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·css·架构