uni-app 【报Bug】vue组件样式不写scoped属性 启动第一次编译默认是scoped,修改组件热更新后恢复正常

发布于 1周前 作者 eggper 来自 Uni-App

uni-app 【报Bug】vue组件样式不写scoped属性 启动第一次编译默认是scoped,修改组件热更新后恢复正常

示例代码:

<template>  
  <view class="content">  
    <group>  
      <group-body>  
        <textarea-group></textarea-group>  
      </group-body>  
    </group>  
  </view>  
</template>  

<script>  
import Group from "../../components/Group.vue";  
import GroupBody from "../../components/GroupBody.vue";  
import TextareaGroup from "../../components/TextareaGroup.vue";  

export default {  
  components: {TextareaGroup, GroupBody, Group},  
  data() {  
    return {  
      title: 'Hello',  
    }  
  },  
  onLoad() {},  
  methods: {},  
}  
</script>  

<style>  

</style>

操作步骤:

  • 重新运行项目

预期结果:

修改GroupHeader.vue触发热更新后查看TextareaGroup.vue组件的 .header-title

.group-header > .header-title {  
    display: inline-block;  
    font-size: 16px;  
    font-weight: bold;  
    flex-grow: 1;  
    padding-left: 10px;  
}

实际结果:

重新运行项目后 .group-header 带有作用域

.group-header[data-v-fe515197] {  
    display: flex;  
    padding: 10px 5px 10px 10px;  
    background-color: #d4ebff;  
    color: #646cff;  
    align-items: center;  
    flex-grow: 1;  
    line-height: 24px;  
    box-sizing: border-box;  
}

bug描述:

我创建一个Group组件,把Group组件拆分成GroupHeader, GroupBody组件; 我又创建了一个TextareaGroup组件 并复用Group, GroupHeader, GroupBody组件的结构,并使用子组件中的class; 然后就出现了scoped问题.

Group.vue组件

<template>  
  <view class="group">  
    <slot name="header">  
      <group-header/>  
    </slot>  
    <slot>  
      <group-body/>  
    </slot>  
  </view>  
</template>  

<script setup>  
import GroupHeader from './GroupHeader.vue';  
import GroupBody from './GroupBody.vue';  
</script>  

<style lang="scss">  
.group {  
  display: flex;  
  flex-direction: column;  
  margin-bottom: 10px;  
  border: 1px solid #d4ebff;  
  border-radius: 5px;  
}  
</style>

GroupHeader.vue组件

<template>  
    <view class="group-header">  
        <slot>  
        <image class="header-icon" src="/static/logo.png"></image>  
        <text class="header-title" v-text="title"></text>  
        <i class="header-icon close-icon">X</i>  
        </slot>  
    </view>  
</template>  

<script setup>  
const props = defineProps({  
  title: { type: String, default: '标题' }  
})  
</script>  

<style lang="scss">  

.group-header{  
  display: flex;  
  padding: 10px 5px 10px 10px;  
  background-color: #d4ebff;  
  color: #646cff;  
  align-items: center;  
  flex-grow: 1;  
  line-height: 24px;  
  box-sizing: border-box;  
  >.header-icon{  
    display: inline-block;  
    width: 24px;  
    height: 24px;  
    text-align: center;  
  }  
  >:last-child.header-icon{  
    width: 24px;  
  }  
  >.header-title{  
    display: inline-block;  
    font-size: 16px;  
    font-weight: bold;  
    flex-grow: 1;  
    padding-left: 10px;  
  }  
  >:first-child.header-title{  
    padding-left: 0;  
  }  
}  
</style>

GroupBody.vue 组件

<template>  
    <view class="group-body">  
    <slot></slot>  
    </view>  
</template>  

<script>  

</script>  

<style lang="scss">  
.group-body{  
  padding: 10px 0 0 10px;  
  >.group{  
    border-right: none;  
  }  
}  
</style>

TextareaGroup.vue组件

TextareaGroup组件中复用GroupHeader.vue组件 class header-title

<script setup>  

import Group from "./Group.vue";  
import GroupHeader from "./GroupHeader.vue";  
import GroupBody from "./GroupBody.vue";  
</script>  

<template>  
  <group>  
    <template #header>  
      <group-header>  
        <image class="header-icon" src="/static/logo.png"></image>  
        <text class="header-title" v-text="'文本域'"></text>  
        <i class="header-icon close-icon">X</i>  
      </group-header>  
    </template>  
    <GroupBody>  
      <textarea class="textarea" placeholder="请输入内容" rows="5"></textarea>  
    </GroupBody>  
  </group>  
</template>  

<style lang="scss">  
  .textarea{  
    display: block;  
  }  
</style>
开发环境 版本号 项目创建方式
Windows 11 64位 HBuilderX

style-scoped-bug.zip


2 回复

我不明白官方文档的意思 https://uniapp.dcloud.net.cn/matter.html#h5正常但app异常的可能性

组件和页面样式相互影响 非H5端默认并未启用 scoped,如需要隔离组件样式可以在 style 标签增加 scoped 属性,H5端为了隔离页面间的样式默认启用了 scoped

H5热更新导致scoped失效才是bug吗


在uni-app中,确实存在组件样式默认行为可能与开发者预期不符的情况。特别是在使用Vue组件时,如果样式没有显式声明为scoped,其行为可能会受到uni-app框架内部处理的影响。针对你提到的“启动第一次编译默认是scoped,修改组件热更新后恢复正常”的问题,这可能是由于框架初始化时样式处理逻辑与热更新时的处理逻辑存在差异导致的。

为了规避这类问题,并确保样式的正确应用,建议开发者显式声明样式的作用域。以下是一个示例,展示如何在uni-app的Vue组件中正确使用scoped属性:

<template>
  <view class="my-component">
    <text>Hello, uni-app!</text>
  </view>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {};
  }
};
</script>

<style scoped>
.my-component {
  padding: 20px;
  background-color: #f0f0f0;
}

text {
  color: #333;
  font-size: 16px;
}
</style>

在上述示例中,<style scoped>明确指定了样式仅作用于当前组件。这样做的好处是:

  1. 避免样式冲突scoped属性会为组件中的每个元素生成一个唯一的属性(如data-v-xxxxxxx),样式选择器会包含这个属性,确保样式只作用于当前组件的DOM元素。

  2. 提高样式维护性:显式声明作用域使得样式与组件紧密绑定,便于理解和维护。

  3. 解决启动与热更新不一致问题:通过显式声明作用域,可以避免框架在不同编译阶段对样式作用域处理不一致的问题。

如果你确实需要在全局范围内应用某些样式,可以将这些样式放在没有scoped属性的<style>标签中,或者定义在全局样式文件中。

此外,为了确保热更新功能的正常运作,建议检查uni-app和依赖库的版本,确保使用的是最新稳定版本。有时候,这类问题可能是由于框架或依赖库的bug导致的,升级到最新版本可能已经解决了这些问题。

总之,显式声明样式作用域是避免样式冲突和提升代码可维护性的最佳实践。希望这能帮助你解决遇到的问题。

回到顶部