最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入探討Vue3組件中的圖片懶加載的實(shí)現(xiàn)原理與應(yīng)用

 更新時(shí)間:2026年03月23日 09:51:19   作者:wuhen_n  
本文將深入探討?Vue3?中圖片懶加載的實(shí)現(xiàn)原理,從底層的?IntersectionObserver?到高級(jí)的?LQIP?漸進(jìn)式加載,最終手寫(xiě)一個(gè)完整的圖片懶加載指令和預(yù)加載組件,感興趣的小伙伴可以了解下

前言

在當(dāng)今的 Web 應(yīng)用中,圖片資源已成為用戶(hù)體驗(yàn)的關(guān)鍵因素。一個(gè)頁(yè)面動(dòng)輒幾十張高清圖片,如果全部同時(shí)加載,不僅浪費(fèi)用戶(hù)流量,還會(huì)導(dǎo)致頁(yè)面卡頓甚至崩潰。據(jù)統(tǒng)計(jì),圖片懶加載可以減少 40-60% 的首屏加載時(shí)間

本文將深入探討 Vue3 中圖片懶加載的實(shí)現(xiàn)原理,從底層的 IntersectionObserver 到高級(jí)的 LQIP 漸進(jìn)式加載,最終手寫(xiě)一個(gè)完整的圖片懶加載指令和預(yù)加載組件。

懶加載原理 - 怎么知道圖片該加載了

傳統(tǒng)懶加載:監(jiān)聽(tīng)滾動(dòng)

傳統(tǒng)懶加載通常通過(guò)監(jiān)聽(tīng)滾動(dòng)事件 + 計(jì)算元素位置實(shí)現(xiàn):

window.addEventListener('scroll', () => {
  // 獲取所有圖片
  const images = document.querySelectorAll('img[data-src]')
  
  images.forEach(img => {
    // 計(jì)算圖片位置
    const rect = img.getBoundingClientRect()
    
    // 如果圖片進(jìn)入可視區(qū)
    if (rect.top < window.innerHeight) {
      // 加載圖片
      img.src = img.dataset.src
      img.removeAttribute('data-src')
    }
  })
})

問(wèn)題

  • 滾動(dòng)事件頻繁觸發(fā),影響性能(需配合節(jié)流)
  • 需要手動(dòng)計(jì)算位置,代碼復(fù)雜
  • 無(wú)法處理元素在可視區(qū)內(nèi)但不滾動(dòng)的情況

現(xiàn)代方案:IntersectionObserver

IntersectionObserver 是現(xiàn)代瀏覽器提供的 API,用于異步觀(guān)察元素與其祖先或視口的交叉狀態(tài)。

// 創(chuàng)建觀(guān)察器
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    // 如果圖片進(jìn)入可視區(qū)
    if (entry.isIntersecting) {
      const img = entry.target
      const src = img.dataset.src
      // 加載圖片
      img.src = src
      // 加載完就不再觀(guān)察了
      observer.unobserve(img)
    }
  })
})
// 觀(guān)察所有圖片
const images = document.querySelectorAll('img[data-src]')
images.forEach(img => observer.observe(img))

優(yōu)勢(shì)

  • 無(wú)需監(jiān)聽(tīng)滾動(dòng),性能更好
  • 自動(dòng)處理元素在可視區(qū)的判斷
  • 提供精細(xì)的閾值配置

閾值設(shè)置的藝術(shù)

閾值(threshold)決定元素進(jìn)入可視區(qū)多少時(shí)觸發(fā)回調(diào):

const observer = new IntersectionObserver(callback, {
  // 閾值數(shù)組:觸發(fā)回調(diào)的交叉比例
  threshold: [0, 0.25, 0.5, 0.75, 1]
  
  // 或者單個(gè)值
  // threshold: 0.5 // 50% 可見(jiàn)時(shí)觸發(fā)
})

不同閾值的適用場(chǎng)景

閾值觸發(fā)時(shí)機(jī)適用場(chǎng)景
0元素剛進(jìn)入可視區(qū)普通圖片懶加載
0.1-0.3元素部分可見(jiàn)預(yù)加載、提前加載
0.5元素半可見(jiàn)視頻自動(dòng)播放
1元素完全可見(jiàn)廣告曝光統(tǒng)計(jì)

自定義指令 v-lazy:讓任何圖片都能懶加載

指令的生命周期

在 Vue3 中,自定義指令的生命周期鉤子包括:

const vLazy = {
  // 在綁定元素的 attribute 或事件監(jiān)聽(tīng)器被應(yīng)用之前調(diào)用
  created(el, binding, vnode) {},
  
  // 在元素被插入到 DOM 前調(diào)用
  beforeMount(el, binding, vnode) {},
  
  // 在綁定元素的父組件及他自己的所有子節(jié)點(diǎn)都掛載完成后調(diào)用
  mounted(el, binding, vnode) {
    // 通常在這里初始化懶加載
  },
  
  // 在包含組件的 VNode 更新之前調(diào)用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  
  // 在包含組件的 VNode 及其子組件的 VNode 更新之后調(diào)用
  updated(el, binding, vnode, prevVnode) {},
  
  // 在綁定元素的父組件卸載前調(diào)用
  beforeUnmount(el, binding, vnode) {},
  
  // 在綁定元素的父組件卸載后調(diào)用
  unmounted(el, binding, vnode) {
    // 清理工作
  }
}

基礎(chǔ) v-lazy 指令實(shí)現(xiàn)

// directives/vLazy.ts
import { Directive } from 'vue'

interface LazyOptions {
  loading?: string      // 加載中占位圖
  error?: string        // 加載失敗占位圖
  threshold?: number    // 閾值
  rootMargin?: string   // 擴(kuò)展區(qū)域
}

class LazyManager {
  private observer: IntersectionObserver | null = null
  private cache = new Set<string>() // 已加載圖片緩存
  
  constructor(options: LazyOptions = {}) {
    this.observer = new IntersectionObserver(
      this.onIntersection.bind(this),
      {
        root: null,
        rootMargin: options.rootMargin || '50px',
        threshold: options.threshold || 0
      }
    )
  }
  
  private onIntersection(entries: IntersectionObserverEntry[]) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target as HTMLImageElement
        const src = img.dataset.src
        if (src && !this.cache.has(src)) {
          this.loadImage(img, src)
        }
      }
    })
  }
  
  private loadImage(img: HTMLImageElement, src: string) {
    const tempImg = new Image()
    
    tempImg.onload = () => {
      img.src = src
      img.classList.add('loaded')
      this.cache.add(src)
      this.observer?.unobserve(img)
    }
    
    tempImg.onerror = () => {
      img.src = img.dataset.error || ''
      img.classList.add('error')
      this.observer?.unobserve(img)
    }
    
    tempImg.src = src
  }
  
  add(img: HTMLImageElement, src: string, errorSrc?: string) {
    if (this.cache.has(src)) {
      // 已緩存,直接顯示
      img.src = src
    } else {
      // 設(shè)置占位圖并開(kāi)始觀(guān)察
      img.src = img.dataset.loading || ''
      img.dataset.src = src
      if (errorSrc) {
        img.dataset.error = errorSrc
      }
      this.observer?.observe(img)
    }
  }
  
  remove(img: HTMLImageElement) {
    this.observer?.unobserve(img)
  }
}

const lazyManager = new LazyManager()

export const vLazy: Directive<HTMLImageElement, string> = {
  mounted(el, binding) {
    const { value, modifiers } = binding
    
    // 處理修飾符
    const options = {
      loading: modifiers.loading ? binding.instance?.loadingSrc : undefined,
      error: modifiers.error ? binding.instance?.errorSrc : undefined
    }
    
    lazyManager.add(el, value, options.error)
  },
  
  updated(el, binding) {
    if (binding.value !== binding.oldValue) {
      lazyManager.add(el, binding.value)
    }
  },
  
  unmounted(el) {
    lazyManager.remove(el)
  }
}

使用 v-lazy 指令

<template>
  <div>
    <!-- 直接使用,圖片會(huì)自動(dòng)懶加載 -->
    <img v-lazy="imageUrl" alt="圖片">
    <!-- 也可以自定義占位圖 -->
    <img 
      v-lazy="imageUrl" 
      :loading-src="'/images/my-loading.gif'"
      :error-src="'/images/my-error.png'"
      alt="圖片"
    >
  </div>
</template>
<script setup>
import { vLazy } from './directives/vLazy'
import { ref } from 'vue'
const imageUrl = ref('https://example.com/large-image.jpg')
</script>

漸進(jìn)式加載(LQIP):先模糊后清晰

什么是漸進(jìn)式加載

先看到模糊的占位圖(很?。?br />    ↓
等高清圖加載完成
    ↓
平滑過(guò)渡到高清圖

LQIP 原理

LQIP (Low Quality Image Placeholders) 的核心思想:先展示一個(gè)極低質(zhì)量的模糊圖,等原圖加載完成后,平滑過(guò)渡到高清圖

如何生成縮略圖

方案一:構(gòu)建時(shí)生成(推薦)

// vite.config.js 配合 imagemin 生成縮略圖
import imagemin from 'imagemin'
import imageminJpegtran from 'imagemin-jpegtran'

// 構(gòu)建時(shí)自動(dòng)生成縮略圖
await imagemin(['src/assets/**/*.{jpg,png}'], {
  destination: 'dist/thumbnails',
  plugins: [
    imageminJpegtran({ progressive: true })
  ]
})

方案二:運(yùn)行時(shí)動(dòng)態(tài)生成(性能差,不適合大量圖片)

function generateThumbnail(file, maxSize = 20) {
  return new Promise((resolve) => {
    const reader = new FileReader()
    reader.onload = (e) => {
      const img = new Image()
      img.onload = () => {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')
        
        // 縮小圖片
        canvas.width = maxSize
        canvas.height = (img.height / img.width) * maxSize
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
        
        resolve(canvas.toDataURL('image/jpeg', 0.5))
      }
      img.src = e.target.result
    }
    reader.readAsDataURL(file)
  })
}

方案三:使用現(xiàn)成的 CDN 服務(wù)

const thumbnailUrl = `https://images.example.com/w=20,q=30/${originalPath}`

使用漸進(jìn)式圖片組件

<template>
  <div class="gallery">
    <ProgressiveImage
      v-for="item in images"
      :key="item.id"
      :src="item.src"
      :thumbnail="item.thumbnail"
      :alt="item.alt"
    />
  </div>
</template>
<script setup>
import ProgressiveImage from './ProgressiveImage.vue'
const images = ref([
  {
    id: 1,
    src: 'https://example.com/high-quality.jpg',
    thumbnail: 'https://example.com/low-quality.jpg',
    alt: '風(fēng)景圖'
  }
])
</script>

加載失敗兜底:完善的錯(cuò)誤處理機(jī)制

基礎(chǔ)錯(cuò)誤處理

<template>
  <div class="image-wrapper">
    <img
      ref="imgRef"
      :src="currentSrc"
      :alt="alt"
      @error="handleError"
      @load="handleLoad"
    />
    <div v-if="loadingFailed" class="error-overlay">
      <span>?? 圖片加載失敗</span>
      <button @click="retry">重試</button>
    </div>
  </div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
  src: String,
  alt: String,
  fallbackSrc: { type: String, default: '/images/fallback.png' },
  retryCount: { type: Number, default: 3 }
})
const imgRef = ref()
const currentSrc = ref(props.src)
const loadingFailed = ref(false)
const retryAttempts = ref(0)
const handleError = () => {
  if (retryAttempts.value < props.retryCount) {
    // 重試
    retryAttempts.value++
    setTimeout(() => {
      currentSrc.value = props.src + `?retry=${retryAttempts.value}`
    }, 1000 * retryAttempts.value) // 指數(shù)退避
  } else {
    // 使用兜底圖
    currentSrc.value = props.fallbackSrc
    loadingFailed.value = true
  }
}
const handleLoad = () => {
  loadingFailed.value = false
  retryAttempts.value = 0
}
// 監(jiān)聽(tīng) src 變化,重置狀態(tài)
watch(() => props.src, (newSrc) => {
  currentSrc.value = newSrc
  loadingFailed.value = false
  retryAttempts.value = 0
})
</script>

指數(shù)退避重試算法

class RetryStrategy {
  private attempts = 0
  private maxAttempts: number
  private baseDelay: number
  
  constructor(maxAttempts = 3, baseDelay = 1000) {
    this.maxAttempts = maxAttempts
    this.baseDelay = baseDelay
  }
  
  getDelay(): number {
    this.attempts++
    if (this.attempts > this.maxAttempts) {
      return -1 // 不再重試
    }
    // 指數(shù)退避:baseDelay * 2^(attempts-1)
    return this.baseDelay * Math.pow(2, this.attempts - 1)
  }
  
  reset(): void {
    this.attempts = 0
  }
}

// 使用
const strategy = new RetryStrategy(5, 500)
const delay = strategy.getDelay()
if (delay > 0) {
  setTimeout(() => retry(), delay)
}

多種占位圖策略

const placeholders = {
  // 純色背景 + 文字
  color: (color = '#f0f2f5', text = '暫無(wú)圖片') => {
    const canvas = document.createElement('canvas')
    canvas.width = 200
    canvas.height = 200
    const ctx = canvas.getContext('2d')
    ctx.fillStyle = color
    ctx.fillRect(0, 0, 200, 200)
    ctx.fillStyle = '#999'
    ctx.font = '14px sans-serif'
    ctx.textAlign = 'center'
    ctx.fillText(text, 100, 100)
    return canvas.toDataURL()
  },
  
  // 內(nèi)置圖標(biāo)
  icon: (type = 'image') => {
    // 返回內(nèi)置圖標(biāo) URL
    return `/icons/placeholder-${type}.svg`
  },
  
  // 純 Base64 透明圖
  transparent: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
}

虛擬列表 + 懶加載:處理大量圖片

問(wèn)題分析

在虛擬列表中,大量圖片同時(shí)存在,比如 1000 張。如果全部進(jìn)行 IntersectionObserver 觀(guān)察,仍會(huì)造成性能問(wèn)題。最佳策略是:只觀(guān)察可視區(qū)附近的元素。

虛擬列表 + 懶加載的實(shí)現(xiàn)

<template>
  <div
    ref="containerRef"
    class="virtual-list"
    @scroll="onScroll"
  >
    <div
      class="list-phantom"
      :style="{ height: totalHeight + 'px' }"
    ></div>
    <div
      class="list-content"
      :style="{ transform: `translateY(${offsetY}px)` }"
    >
      <div
        v-for="item in visibleItems"
        :key="item.id"
        class="list-item"
        :style="{ height: itemHeight + 'px' }"
      >
        <!-- 只在可視區(qū)內(nèi)的圖片才加載 -->
        <ProgressiveImage
          v-if="shouldLoadImage(item)"
          :src="item.src"
          :thumbnail="item.thumbnail"
        />
        <div v-else class="placeholder"></div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import ProgressiveImage from './ProgressiveImage.vue'
const props = defineProps({
  items: Array,
  itemHeight: { type: Number, default: 200 },
  buffer: { type: Number, default: 5 } // 緩沖區(qū)大小
})
const containerRef = ref()
const scrollTop = ref(0)
// 計(jì)算可視區(qū)域
const visibleCount = computed(() => 
  Math.ceil(containerRef.value?.clientHeight / props.itemHeight)
)
// 計(jì)算起始索引(帶上緩沖區(qū))
const startIndex = computed(() => {
  let index = Math.floor(scrollTop.value / props.itemHeight)
  return Math.max(0, index - props.buffer)
})
// 計(jì)算結(jié)束索引
const endIndex = computed(() => {
  let index = startIndex.value + visibleCount.value + props.buffer * 2
  return Math.min(index, props.items.length)
})
// 可視區(qū)域內(nèi)的項(xiàng)目
const visibleItems = computed(() => 
  props.items.slice(startIndex.value, endIndex.value)
)
// 總高度
const totalHeight = computed(() => 
  props.items.length * props.itemHeight
)
// 偏移量
const offsetY = computed(() => 
  startIndex.value * props.itemHeight
)
// 判斷圖片是否應(yīng)該加載
const shouldLoadImage = (item) => {
  const index = props.items.indexOf(item)
  // 只加載可視區(qū)及前后 buffer 范圍內(nèi)的圖片
  return index >= startIndex.value && index < endIndex.value
}
const onScroll = () => {
  scrollTop.value = containerRef.value.scrollTop
}
</script>

性能對(duì)比

方案DOM 節(jié)點(diǎn)數(shù)內(nèi)存占用滾動(dòng)幀率
直接渲染1000080MB5-10fps
懶加載1000080MB15-20fps
虛擬列表 + 懶加載205MB60fps

預(yù)加載 - 提前加載重要圖片

什么情況需要預(yù)加載

用戶(hù)即將看到的圖片,都需要進(jìn)行預(yù)加載:

  • 輪播圖的下一張
  • 鼠標(biāo)懸停的圖片
  • 首屏的關(guān)鍵圖片
  • 預(yù)計(jì)用戶(hù)會(huì)看的圖片

帶進(jìn)度條的預(yù)加載組件

<template>
  <div class="preload-container">
    <!-- 預(yù)加載進(jìn)度條 -->
    <div v-if="loading" class="progress-wrapper">
      <div class="progress-bar">
        <div class="progress-fill" :style="{ width: progress + '%' }"></div>
      </div>
      <div class="progress-text">{{ Math.round(progress) }}%</div>
    </div>
    <!-- 加載完成后顯示圖片 -->
    <img v-else :src="currentSrc" :alt="alt" />
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const props = defineProps({
  src: String,
  alt: String,
  priority: { type: Boolean, default: false }  // 是否高優(yōu)先級(jí)
})
const currentSrc = ref('')
const loading = ref(true)
const progress = ref(0)
// 使用 XMLHttpRequest 實(shí)現(xiàn)進(jìn)度跟蹤
const loadImage = () => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', props.src, true)
    xhr.responseType = 'blob'
    xhr.onload = () => {
      if (xhr.status === 200) {
        const blob = xhr.response
        const url = URL.createObjectURL(blob)
        resolve(url)
      } else {
        reject(new Error('加載失敗'))
      }
    }
    xhr.onerror = reject
    xhr.onprogress = (e) => {
      if (e.lengthComputable) {
        progress.value = (e.loaded / e.total) * 100
      }
    }
    xhr.send()
  })
}
onMounted(async () => {
  try {
    const url = await loadImage()
    currentSrc.value = url
    loading.value = false
  } catch (error) {
    console.error('圖片加載失敗', error)
    loading.value = false
  }
})
onUnmounted(() => {
  if (currentSrc.value) {
    URL.revokeObjectURL(currentSrc.value)
  }
})
</script>
<style scoped>
.progress-wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.progress-bar {
  width: 200px;
  height: 4px;
  background: #f0f0f0;
  border-radius: 2px;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background: #1890ff;
  transition: width 0.1s ease;
}
.progress-text {
  font-size: 12px;
  color: #666;
}
</style>

最佳實(shí)踐清單

配置清單

  • 使用 IntersectionObserver 實(shí)現(xiàn)懶加載
  • 封裝 v-lazy 指令,方便復(fù)用
  • 大圖使用漸進(jìn)式加載(先模糊后清晰)
  • 配置錯(cuò)誤重試機(jī)制(指數(shù)退避)
  • 大量圖片使用虛擬列表
  • 重要圖片使用預(yù)加載
  • 添加 loading 動(dòng)畫(huà)或骨架屏

不同場(chǎng)景的選擇

場(chǎng)景技術(shù)方案性能收益
普通圖片v-lazy 指令 + IntersectionObserver減少 80% 首屏請(qǐng)求
高質(zhì)量圖片LQIP + 平滑過(guò)渡提升 60% 感知性能
圖片墻/畫(huà)廊虛擬列表 + 按需加載內(nèi)存占用減少 90%
關(guān)鍵圖片預(yù)加載 + 進(jìn)度條用戶(hù)體驗(yàn)提升
輪播圖預(yù)加載下一張流暢切換

實(shí)施清單

  • 懶加載基礎(chǔ):使用 IntersectionObserver 實(shí)現(xiàn) v-lazy 指令
  • 漸進(jìn)式增強(qiáng):LQIP 模糊占位 + 高清圖過(guò)渡
  • 錯(cuò)誤處理:重試機(jī)制 + 兜底圖
  • 性能優(yōu)化:虛擬列表 + 緩沖區(qū)控制
  • 用戶(hù)體驗(yàn):進(jìn)度反饋 + 平滑動(dòng)畫(huà)

最后的建議

圖片加載優(yōu)化不是單一技術(shù),而是一個(gè)系統(tǒng)工程:

  • 網(wǎng)絡(luò)層面:使用 HTTP/2、CDN 加速
  • 構(gòu)建層面:壓縮、格式轉(zhuǎn)換、雪碧圖
  • 運(yùn)行時(shí)層面:懶加載、預(yù)加載、緩存
  • 體驗(yàn)層面:進(jìn)度反饋、平滑過(guò)渡

結(jié)語(yǔ)

好的圖片加載策略應(yīng)該是無(wú)感知的。用戶(hù)不會(huì)注意到圖片是懶加載的,不會(huì)注意到有進(jìn)度條,他們只會(huì)感覺(jué)頁(yè)面"很快很流暢"。這才是優(yōu)化的最高境界。

以上就是深入探討Vue3組件中的圖片懶加載的實(shí)現(xiàn)原理與應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于Vue3圖片懶加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于Vue3實(shí)現(xiàn)動(dòng)態(tài)表格的完整指南

    基于Vue3實(shí)現(xiàn)動(dòng)態(tài)表格的完整指南

    動(dòng)態(tài)表格是一種在運(yùn)行時(shí)可生成或修改的表格,持排序、篩選、分頁(yè)和編輯等復(fù)雜交互,下面我們就來(lái)看看如何在Vue3中具體實(shí)現(xiàn)動(dòng)態(tài)表格的完整步驟吧
    2026-05-05
  • 解決VUE打包后與nginx代理出現(xiàn)加載速度超級(jí)慢的問(wèn)題

    解決VUE打包后與nginx代理出現(xiàn)加載速度超級(jí)慢的問(wèn)題

    這篇文章主要介紹了解決VUE打包后與nginx代理出現(xiàn)加載速度超級(jí)慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • vue3?中使用?jsx?開(kāi)發(fā)的詳細(xì)過(guò)程

    vue3?中使用?jsx?開(kāi)發(fā)的詳細(xì)過(guò)程

    這篇文章主要介紹了vue3?中使用?jsx?開(kāi)發(fā),本文著重說(shuō)一下在使用 .vue 文件和 .jsx 文件在使用語(yǔ)法上的差異,需要的朋友可以參考下
    2022-09-09
  • 最新評(píng)論

    安阳县| 张家口市| 陆川县| 贵州省| 佳木斯市| 淄博市| 金堂县| 当阳市| 淄博市| 武隆县| 烟台市| 西安市| 镇平县| 察雅县| 米泉市| 莒南县| 威信县| 怀柔区| 武陟县| 高台县| 青浦区| 寿宁县| 中山市| 安化县| 海阳市| 文登市| 富蕴县| 南涧| 齐河县| 鱼台县| 井研县| 乌苏市| 广南县| 漯河市| 锡林郭勒盟| 武威市| 南岸区| 东阳市| 琼结县| 淮安市| 新营市|