css 滚动条导致页面偏移问题 
问题描述 
水平居中的元素因为纵向滚动条的出现,导致位置出现了偏移
问题重现 
水平居中元素因为横向滚动条的出现和隐藏产生偏移(点击蓝色按钮可以看到效果)
水平居中通过 margin:0 auto; 实现
 下面是详情 
<script setup lang="ts">
import { ref } from 'vue'
const rows = ref<number>(0)
const onClick = () => rows.value = rows.value === 0 ? 10 : 0
</script>
<template>
  <div class="w-400px mx-auto h-20px bg-pink-400" />
  <div class="h-200px overflow-auto">
    <!-- 设置 margin-left: auto; margin-right: auto; -->
    <div class="w-400px mx-auto bg-blue-300">
      <button class="succeed" @click="onClick">
        {{ rows !== 0 ? '恢复原状' : '加载更多' }}
      </button>
      <div class="text-lg py-2">
        下面是详情
      </div>
      <li v-for="idx in rows" :key="idx">
        {{ idx + 1 }} 这只伶俐的棕色狐狸跳过一只懒惰的狗
      </li>
    </div>
  </div>
</template>
水平居中通过 display: flex;justify-content: center; 实现
 下面是详情 
<script setup lang="ts">
import { ref } from 'vue'
const rows = ref<number>(0)
const onClick = () => rows.value = rows.value === 0 ? 10 : 0
</script>
<template>
  <div class="w-400px mx-auto h-20px bg-pink-400" />
  <!-- 设置 flex 布局 和  justify-content: center; -->
  <div class="h-200px overflow-auto flex justify-center ">
    <div class="w-400px mx-auto bg-blue-300">
      <button class="succeed" @click="onClick">
        {{ rows !== 0 ? '恢复原状' : '加载更多' }}
      </button>
      <div class="text-lg py-2">
        下面是详情
      </div>
      <li v-for="idx in rows" :key="idx">
        {{ idx + 1 }} 这只伶俐的棕色狐狸跳过一只懒惰的狗
      </li>
    </div>
  </div>
</template>
问题原因 
- 上面的居中是相当于视口的居中,而 
win的滚动条是需要占据视口宽度的(mac滚动条默认不占据视口宽度)。 - 因此当滚动条出现,那么视口就变窄了,那么原来居中元素的位置也需要重新调整
 
问题解决 
1. 动态计算 margin-left 
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const rows = ref<number>(0)
const onClick = () => rows.value = rows.value === 0 ? 10 : 0
const innerRef = ref<HTMLDivElement>()
const resizeObserver = new ResizeObserver(() => {
  if (!innerRef.value) return
  const { parentElement, offsetWidth } = innerRef.value
  const { offsetWidth: parentOffsetWidth } = parentElement as HTMLDivElement
  const marginLeft = (parentOffsetWidth - offsetWidth) / 2
  Object.assign(innerRef.value.style, { marginLeft: `${marginLeft}px` })
})
function onChangeWidth() {
  if (!innerRef.value) return
  const { offsetWidth } = innerRef.value
  if (offsetWidth === 300)
    Object.assign(innerRef.value.style, { width: '200px' })
  else
    Object.assign(innerRef.value.style, { width: '300px' })
}
onMounted(() => {
  if (!innerRef.value) return
  resizeObserver.observe(innerRef.value)
})
</script>
<template>
  <div class="w-400px mx-auto h-20px  bg-pink-400" />
  <div class="h-200px overflow-auto">
    <div ref="innerRef" class="w-400px mx-auto bg-blue-300">
      <div>
        <button class="succeed" @click="onClick">
          {{ rows !== 0 ? '恢复原状' : '加载更多' }}
        </button>
      </div>
      <div>
        <button class="succeed" @click="onChangeWidth">
          改变居中元素宽度
        </button>
      </div>
      <div class="text-lg py-2">
        下面是详情
      </div>
      <li v-for="idx in rows" :key="idx">
        {{ idx + 1 }} 这只伶俐的棕色狐狸跳过一只懒惰的狗
      </li>
    </div>
  </div>
</template>
2. 动态计算滚动条宽度 
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const rows = ref<number>(0)
const onClick = () => rows.value = rows.value === 0 ? 10 : 0
const containerRef = ref<HTMLDivElement>()
const innerRef = ref<HTMLDivElement>()
const resizeObserver = new ResizeObserver(() => {
  if (!containerRef.value || !innerRef.value) return
  const { offsetWidth, clientWidth } = containerRef.value
  /* 计算滚动条的宽度 */
  const scrollBarWidth = offsetWidth - clientWidth
  Object.assign(containerRef.value.style, { paddingLeft: `${scrollBarWidth}px` })
})
function onChangeWidth() {
  if (!innerRef.value) return
  const { offsetWidth } = innerRef.value
  if (offsetWidth === 300)
    Object.assign(innerRef.value.style, { width: '200px' })
  else
    Object.assign(innerRef.value.style, { width: '300px' })
}
onMounted(() => {
  if (!containerRef.value) return
  resizeObserver.observe(containerRef.value)
})
</script>
<template>
  <div class="w-400px mx-auto h-20px  bg-pink-400" />
  <div ef="containerRef" class="h-200px overflow-auto flex justify-center">
    <div ref="innerRef" class="w-400px mx-auto bg-blue-300">
      <div>
        <button class="succeed" @click="onClick">
          {{ rows !== 0 ? '恢复原状' : '加载更多' }}
        </button>
      </div>
      <div>
        <button class="succeed" @click="onChangeWidth">
          改变居中元素宽度
        </button>
      </div>
      <div class="text-lg py-2">
        下面是详情
      </div>
      <li v-for="idx in rows" :key="idx">
        {{ idx + 1 }} 这只伶俐的棕色狐狸跳过一只懒惰的狗
      </li>
    </div>
  </div>
</template>
3.不使用浏览器默认滚动条 
如:采用 el-scroll 这种 js 模拟浏览器滚动条的方案