整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          去哪網(wǎng)開發(fā)實戰(zhàn)記錄(9):城市選擇頁(中)

          去哪網(wǎng)開發(fā)實戰(zhàn)記錄(9):城市選擇頁(中)

          弟組件之間的聯(lián)動

          所謂的兄弟組件之間的聯(lián)動,其實就是實現(xiàn)點擊右側(cè)的字母就能跳轉(zhuǎn)至對應(yīng)的首字母城市,因此列表組件需要知道右側(cè)的字母列表的點擊事件所對應(yīng)的元素字母,這就需要兄弟組件間的數(shù)據(jù)傳遞了(Alphabet組件與List組件之間的通信),可以使用到中央數(shù)據(jù)總線Bus,但是由于這里的業(yè)務(wù)邏輯不是很復(fù)雜,因此可以將Alphabet組件內(nèi)的信息傳遞到City組件(子組件向父組件傳遞信息,發(fā)布訂閱模式),然后City組件向List組件傳遞信息(父組件向子組件傳遞信息,屬性傳值方式)。

          在gitee的分支欄點擊新建分支city-components,然后記得將本地master分支切換到city-components分支。

          字母點擊跳轉(zhuǎn)

          打開Alphabet.vue文件,給字母表中的字母添加一個click事件,然后嘗試將點擊的字母在控制臺上進行輸出顯示:

          <template>
          <ul class="list">
            <li class="item"
                v-for="(item,key) of cities"
                :key="key"
                @click="handleLetterClick"
            >
              {{key}}
            </li>
          </ul>
          </template>
          

          然后在script標簽添加這個handleLetterClick方法:

          methods: {
              handleLetterClick (e) {
                console.log(e.target.innerText)
              }
            }
          

          可以測試一下當你點擊右側(cè)列表中的字母,控制臺是否真的輸出了對應(yīng)的字母:

          前面說了由于這里的業(yè)務(wù)邏輯不是很復(fù)雜,因此可以將Alphabet組件內(nèi)的信息傳遞到City組件(子組件向父組件傳遞信息,發(fā)布訂閱模式),然后City組件向List組件傳遞信息(父組件向子組件傳遞信息,屬性傳值方式)。

          那就開始編寫使用發(fā)布訂閱模式實現(xiàn)Alphabet組件內(nèi)的信息傳遞到City組件的代碼。修改子組件Alphabet.vue中handleLetterClick函數(shù)代碼為:

          methods: {
              handleLetterClick (e) {
              /** 注意此處必須使用innerText而不是innerHTML **/
                this.$emit('change', e.target.innerText)
              }
            }
          

          接著去父組件City.vue中監(jiān)聽該chang事件,請定義相應(yīng)的handleLetterChange去接收子組件傳遞過來的信息:

          <template>
            <div>
              <city-header></city-header>
              <city-search></city-search>
              <city-list :cities="cities" :hotCities="hotCities"></city-list>
              <city-alphabet :cities="cities" @change="handleLetterChange"></city-alphabet>
            </div>
          </template>
          
          handleLetterChange (letter) {
                console.log(letter)
              }
          

          控制臺測試發(fā)現(xiàn)當你點擊右側(cè)列表中的字母,控制臺仍然輸出了對應(yīng)的字母。接下來就是父組件City.vue通過屬性傳值的方式將數(shù)據(jù)傳遞給子組件List.vue。修改父組件City.vue中handleLetterChange函數(shù)代碼為:

             handleLetterChange (letter) {
                this.letter=letter
              }
          

          然后在父組件中的data中返回letter,并將其通過屬性傳值給city-list組件:

          <template>
            <div>
             ...
              <city-list :cities="cities" :hotCities="hotCities" :letter="letter"></city-list>
          ...
            </div>
          </template>
          
          
          data () {
              return {
                ...
                letter: ''
              }
          

          然后在子組件List.vue中通過props來接收數(shù)據(jù):

            props: {
              cities: Object,
              hotCities: Array,
              letter: String
            },
          

          當List.vue發(fā)現(xiàn)letter有變化的時候,就顯示跟letter首字母相同的城市列表,這種功能可以通過偵聽器來實現(xiàn):

          watch: {
              letter () {
                console.log(this.letter)
              }
            }
          

          控制臺測試發(fā)現(xiàn)當你點擊右側(cè)列表中的字母,控制臺仍然輸出了對應(yīng)的字母。

          還記得之前推薦的那篇關(guān)于better-scroll的文章:當 better-scroll 遇見 Vue么,里面介紹了如何滾動至某個元素。既然是滾動至某個元素,那么肯定需要選擇某個DOM節(jié)點了,Vue提供了ref來選擇節(jié)點(List.vue):

          <template>
             <div class="area"
                     v-for="(item, key) of cities"
                     :key="key"
                     :ref="key"
                >
          </template>
          

          然后使用偵聽器來監(jiān)聽letter的變化:

            watch: {
              letter () {
                if (this.letter) {
                  console.log(this.$refs[this.letter])
                }
              }
            }
          

          當你點擊某個字母時,控制臺輸出:

          而這個索引為0的div.area區(qū)域中包含了我們所需要的的城市列表信息:

          既然這樣就可以直接獲取到對應(yīng)的DOM節(jié)點,并將該節(jié)點傳遞給better-scroll,里面有一個scrollToElement方法,之后就能實現(xiàn)點擊某個字母,城市列表頁就會顯示對應(yīng)的城市信息:

            watch: {
              letter () {
                if (this.letter) {
                  const element=this.$refs[this.letter][0]
                  this.scroll.scrollToElement(element)
                }
              }
            }
          

          拇指滑動跳轉(zhuǎn)

          前面介紹的是點擊右側(cè)的字母就能跳轉(zhuǎn)至對應(yīng)的首字母城市,其實比這個更普通的 就是當你拇指按住字母表上下滑動時,左邊List組件也會相應(yīng)的上下跳動。

          實現(xiàn)這個需要,首先我們要監(jiān)聽使用者的手指,記錄時候開始點擊字母列表(touchstart),什么時候開始滾動(touchmove),以及什么時候離開字母列表(touchend)等等,自然而然地想到使用事件監(jiān)聽。注意我們還需要定義一個標志狀態(tài)(touchStatus),默認為flase,只有當你手指觸摸的時候才會變成true,其實就是指定這三個函數(shù)的執(zhí)行順序:

          <template>
          <ul class="list">
            <li class="item"
                v-for="(item,key) of cities"
                :key="key"
                @touchstart="handleTouchStart"
                @touchmove="handleTouchMove"
                @touchend="handleTouchEnd"
                @click="handleLetterClick"
            >
              {{key}}
            </li>
          </ul>
          </template>
          
          
          <script>
          ...
            methods: {
              handleLetterClick (e) {
               ...
              handleTouchStart () {
                this.touchStatus=true
              },
              handleTouchMove () {
                if (this.touchStatus) {
          
                }
              },
              handleTouchEnd () {
                this.touchStatus=false
              }
            }
          }
          </script>
          

          現(xiàn)在我們需要知道當你拇指在滑動的時候,你的拇指停留在哪個字母上,這件事情其實是較為復(fù)雜的,可以提供一種思路僅供參考:先獲取A字母距離頂部的高度,然后當你滑動的時候獲取你當前字母距離頂部的高度,接著將后者減去前者得到一個差值,最后用這個差值除于每個字母的長度就能得到這是第幾個字母。然后讓該字母觸發(fā)對應(yīng)的事件即可,那么這樣你需要新建一個數(shù)組用于存放字母,然后根據(jù)索引來獲取字母,可以使用計算屬性:

            computed: {
              letters () {
                const letters=[]
                for (let i in this.cities) {
                  letters.push(i)
                }
                return letters
              }
            },
          

          上面的計算屬性其實就是得到一個類似于['A','B','C','D']的數(shù)組。然后你城市字母表遍歷的對象就不再是cities,而是letters了(那就不需要使用key了,直接遍歷輸出item就可以):

          <template>
          <ul class="list">
            <li class="item"
                v-for="item of letters"
                :key="item"
                @touchstart="handleTouchStart"
                @touchmove="handleTouchMove"
                @touchend="handleTouchEnd"
                @click="handleLetterClick"
            >
              {{item}}
            </li>
          </ul>
          </template>
          

          接下來繼續(xù)編寫handleTouchMove函數(shù)的內(nèi)容,這個函數(shù)就是用于計算當你拇指在滑動的時候,你的拇指停留在哪個字母上。實現(xiàn)的邏輯是先獲取A字母距離頂部的高度,然后當你滑動的時候獲取你當前字母距離頂部的高度,接著將后者減去前者得到一個差值,最后用這個差值除于每個字母的長度就能得到這是第幾個字母。

          第一步在template中給DOM節(jié)點添加ref屬性,用于獲取某個DOM節(jié)點:ref="item";

          第二步,修改handleTouchMove函數(shù)為:

             handleTouchMove () {
                if (this.touchStatus) {
                  const startY=this.$refs['A'][0].offsetTop
                  console.log(startY)
                }
              },
          

          通過測試發(fā)現(xiàn)當你拇指在滑動的時候,控制臺始終輸出61,這個61就是A字母距離頁面頂部(注意是藍色區(qū)域底部)的高度:

          然后就可以獲取每個字母距離整個頁面的高度,里面有一個最小的值就是拇指距離整個頁面最小的高度:

          如果你想要獲取某個字母距離藍色區(qū)域底部的高度,可以將其高度減去藍色區(qū)域底部的高度79(43(Header組件高度)+36(Search組件告高度)=79),然后除以20(每個字母高度)并向下取整就能得到每個字母的索引:

            handleTouchMove (e) {
                if (this.touchStatus) {
                  const startY=this.$refs['A'][0].offsetTop
                  const touchY=e.touches[0].clientY - 79
                  const index=Math.floor((touchY - startY) / 20)
                  console.log(index)
                }
              },
          

          測試發(fā)現(xiàn)拇指移動到字母M處,右側(cè)顯示正常:

          最后使用子組件Alphabet使用發(fā)布訂閱模式向City組件傳遞數(shù)據(jù):

          handleTouchMove (e) {
                if (this.touchStatus) {
                  const startY=this.$refs['A'][0].offsetTop
                  const touchY=e.touches[0].clientY - 79
                  const index=Math.floor((touchY - startY) / 20)
                  if (index >=0 && index < this.letters.length) {
                    this.$emit('change', this.letters[index])
                  }
                }
              },
          

          測試發(fā)現(xiàn)功能顯示正常。

          城市列表頁新能優(yōu)化

          接下來就是對上面的代碼進行優(yōu)化,因為startY是一個定值,而按照目前寫的代碼則是每次都需要去執(zhí)行,這會造成性能低下。可以在data中return一個startY(初始值為0),然后定義一個生命周期函數(shù)update,只有頁面的數(shù)據(jù)被更新同時頁面完成了渲染時,該方法才會被執(zhí)行:

            data () {
              return {
                touchStatus: false,
                startY: 0
              }
            },
            updated () {
              this.startY=this.$refs['A'][0].offsetTop
            },
          
          handleTouchMove (e) {
                if (this.touchStatus) {
                  const touchY=e.touches[0].clientY - 79
                  const index=Math.floor((touchY - this.startY) / 20)
                  if (index >=0 && index < this.letters.length) {
                    this.$emit('change', this.letters[index])
                  }
                }
              },
          

          我們知道頁面一開始的時候cities變量是空值,也就是Alphabet組件內(nèi)不會顯示任何信息,然后通過ajax獲取到數(shù)據(jù)時Alphabet組才會被重新渲染,之后會觸發(fā)生命周期函數(shù)updated,這時候開始計算藍色區(qū)域底部與字母A標簽的距離。

          還有一個優(yōu)化就是函數(shù)節(jié)流。函數(shù)節(jié)流就是限制一個函數(shù)在一定時間內(nèi)只能執(zhí)行一次,這里就是當你拇指在字母表中上下移動時,touchmove函數(shù)執(zhí)行的頻率非常高會造成性能低下,因此可以借助于函數(shù)節(jié)流來優(yōu)化該代碼。具體的函數(shù)節(jié)流介紹可以參看這篇文章JS進階篇1---函數(shù)節(jié)流(throttle),此處采用計時器規(guī)定在一定時間內(nèi)函數(shù)才允許執(zhí)行。打開Alphabet.vue文件,先return一個timer對象,初始值是null,其次修改handleTouchMove函數(shù)代碼為:

          handleTouchMove (e) {
                if (this.touchStatus) {
                  if (this.timer) {
                    clearTimeout(this.timer)
                  }
                  this.timer=setInterval(()=> {
                    const touchY=e.touches[0].clientY - 79
                    const index=Math.floor((touchY - this.startY) / 20)
                    if (index >=0 && index < this.letters.length) {
                      this.$emit('change', this.letters[index])
                    }
                  }, 16)
                }
              },
          

          原理非常簡單,先判斷timer計時器對象是否存在,存在就清空該計時器避免緩存,否則就定義一個計時器并設(shè)置計時器時間為16毫秒,即每16毫秒才計算一次,這樣可避免不必要的計算工作。

          搜索功能實現(xiàn)

          在gitee的分支欄點擊新建分支city-search-logic,然后記得將本地master分支切換到city-search-logic分支,接下來開始進行搜索框的業(yè)務(wù)邏輯開發(fā)。

          打開city文件夾中的Search.vue組件,修改其中的template代碼為:

          <template>
            <div>
              <div class="search">
                <input class="search-input" type="text" placeholder="輸入城市名稱或者拼音"/>
              </div>
          
              <div class="search-content">
                <ul>
                  <li>123</li>
                </ul>
              </div>
            </div>
          </template>
          

          這里面其實就是新增了一個搜索結(jié)果展示的區(qū)域,也就是.search-content類所占區(qū)域,接著在style標簽中新增.search-content類所對應(yīng)的樣式(注意它應(yīng)該和.search類是平級關(guān)系):

          .search-content
            z-index: 1
            overflow: hidden
            position: absolute
            top: 1.58rem
            left: 0
            right: 0
            bottom: 0
            background: green
          

          然后需要實現(xiàn)搜索信息與結(jié)果展示區(qū)域的聯(lián)動,就需要使用到數(shù)據(jù)的雙向綁定:

          <script>
          export default {
            name: 'CitySearch',
            data () {
              return {
                keyword: ''
              }
            }
          }
          </script>
          
          
          <template>
            <div>
              <div class="search">
                <input v-model="keyword" class="search-input" type="text" placeholder="輸入城市名稱或者拼音"/>
              </div>
          ...
            </div>
          </template>
          

          最后結(jié)果肯定是顯示城市,那么需要從父組件City中接收cities,修改City.vue組件中的template代碼為:

          <template>
            <div>
              <city-header></city-header>
              <city-search :cities="cities"></city-search>
          ....
            </div>
          </template>
          

          然后在子組件Search.vue中通過props來接收cities:

            props: {
              cities: Object
            },
          

          然后我們在子組件Search.vue中return一個數(shù)組list(該數(shù)組用于存儲搜索結(jié)果)和計時器timer(函數(shù)節(jié)流使用):

            data () {
              return {
                keyword: '',
                list: [],
                timer: null
              }
            }
          

          接著我們定義一個偵聽器用于監(jiān)聽keyword的變化:

            watch: {
              keyword () {
                if (this.timer) {
                  clearTimeout(this.timer)
                }
                this.timer=setInterval(()=> {
                  // 書寫在cities對象中查找某個元素的邏輯
                  const result=[]
                  for (let i in this.cities) {
                    // i就是字母A,B...,而this.city[i]就是數(shù)組,value就是數(shù)組中的每一項
                    this.cities[i].forEach((value)=> {
                      if (value.spell.indexOf(this.keyword) > -1 ||
                        value.name.indexOf(this.keyword) > -1
                      ) {
                        result.push(value)
                      }
                    })
                  }
                  this.list=result
                }, 100)
              }
            }
          

          在city.json文件中,我們定義的cities結(jié)構(gòu)為:

          "cities": {
                      "A": [{
                          "id": 56,
                          "spell": "aba",
                          "name": "阿壩"
                      }, {
                          "id": 57,
                          "spell": "akesu",
                          "name": "阿克蘇"
                      }...
          

          也就是cities本身是一個對象,里面又包含了一個數(shù)組作為值,而數(shù)組中包含的則是一個個對象。注意if語句中的判斷條件為value.spell.indexOf(this.keyword) > -1 ||value.name.indexOf(this.keyword) > -1`也就是通過拼音或中文都可以查找是否能找到匹配的數(shù)據(jù)。

          接下來就是對搜索結(jié)果的布局進行優(yōu)化,給搜索結(jié)果添加一個.search-item類和一個邊框類.border-bottom:

          <div class="search-content">
                <ul>
                  <li class="search-item" v-for="item of list" :key="item.id">{{ item.name }}</li>
                </ul>
          </div>
          

          然后給.search-item類添加樣式,并修改.search-content類的背景顏色為#eee:

          .search-content
            z-index: 1
            overflow: hidden
            position: absolute
            top: 1.58rem
            left: 0
            right: 0
            bottom: 0
            background: #eee
            .search-item
              line-height: .62rem
              padding-left: .2rem
              background: #fff
              color: #666
          

          測試發(fā)現(xiàn)頁面顯示正常,但是搜索結(jié)果是無法滾動的,此時可以借助于bertter-scroll來實現(xiàn)。

          第一步獲取到search-content類節(jié)點DOM:

            <div class="search-content" ref="search">
                <ul>
                  <li class="search-item border-bottom" v-for="item of list" :key="item.id">{{ item.name }}</li>
                </ul>
              </div>
          

          第二步導(dǎo)入Bscroll類及創(chuàng)建該對象:

          <script>
          import BScroll from 'better-scroll'
          export default {
             name: 'CitySearch',
            props: {
              cities: Object
            },
            mounted () {
              this.scroll=new BScroll(this.$refs.search)
            }
          }
          </script>
          

          只需這兩步就完成了頁面滾動的效果。還有一個問題就是當你輸入完并清空搜索框的時候,搜索結(jié)果依舊還是存在:

          其實只需要當你的keyword為空的時候,你將這個list設(shè)置為[]即可,在偵聽器中添加實現(xiàn)上述功能的邏輯:

            watch: {
              keyword () {
                if (this.timer) {
                  clearTimeout(this.timer)
                }
                if (!this.keyword) {
                  this.list=[]
                  return
                }
               ...
            }
          

          這樣就解決了這個問題。新的問題又來了就是當你輸入一串非常長的字母或者說是輸入的關(guān)鍵詞不能匹配任何城市時,前面我們是什么也不顯示,其實這個是有一點問題的,我們最好是在頁面上添加諸如“找不到對應(yīng)的城市”等信息。最簡單的方式就是使用v-show來進行顯示:

           <div class="search-content" ref="search">
                <ul>
                  <li class="search-item border-bottom" v-for="item of list" :key="item.id">{{ item.name }}</li>
                </ul>
                <ul>
                  <li class="search-item border-bottom" v-show="!list.length">沒有找到匹配的城市</li>
                </ul>
              </div>
          

          這樣我們就實現(xiàn)了當list的長度為0的時候才顯示“沒有找到匹配的城市”字眼,但是這樣會造成一個問題,就是剛開始你不輸入關(guān)鍵詞的時候也會出現(xiàn)這個“沒有找到匹配的城市”字眼,把熱門城市和城市列表給遮住了。這個問題還是可以通過v-show來解決(只不過這次將v-show 標簽加到search-content類上):

              <div class="search-content" v-show="keyword" ref="search">
                <ul>
                  <li class="search-item border-bottom" v-for="item of list" :key="item.id">{{ item.name }}</li>
                </ul>
                <ul>
                  <li class="search-item border-bottom" v-show="!list.length">沒有找到匹配的城市</li>
                </ul>
              </div>
          

          這樣就完美地解決了上述問題。前面介紹過html中最好不需要包含計算邏輯(在“沒有找到匹配的城市”標簽上使用了取反運算):

          <li class="search-item border-bottom" v-show="!list.length">沒有找到匹配的城市</li>
          

          因此需要將這個取反的邏輯使用計算屬性來代替:

            computed: {
              hasNoData () {
                return !this.list.length
              }
            },
          

          自然而然就需要修改上面的template中的取反運算代碼為:

          <li class="search-item border-bottom" v-show="hasNoData">沒有找到匹配的城市</li>
          

          這樣就完成了搜索框的業(yè)務(wù)邏輯。最后就是將我們的代碼上傳到city-search-logic分支,并且將其與master分支進行合并,相應(yīng)的步驟如下:

          于企業(yè)多地點業(yè)務(wù)而言,SEO 策略與業(yè)務(wù)策略的協(xié)調(diào)對于成功至關(guān)重要。

          無論企業(yè)是經(jīng)營特許經(jīng)營模式、零售連鎖店,還是以服務(wù)區(qū)業(yè)務(wù)的形式運營多個中心,您的本地 SEO方法都需要量身定制,以滿足您的特定目標。它還需要具有足夠的可擴展性和效率,以便在獲得長期投資回報的同時進行維護。

          另一個關(guān)鍵要求是,您的內(nèi)容方法要為用戶和 Google 創(chuàng)造足夠的價值,以使其高于索引質(zhì)量閾值。

          這意味著超越本地 SEO 的標準最佳實踐 ,并創(chuàng)建可持續(xù)提高品牌知名度和轉(zhuǎn)化率的本地 SEO 活動。

          協(xié)調(diào) SEO 與業(yè)務(wù)戰(zhàn)略

          多地點經(jīng)營的企業(yè)有不同的目標。

          雖然多地點管理的基礎(chǔ)相同,但您的方法需要與整體戰(zhàn)略相結(jié)合并與整體業(yè)務(wù)目標保持一致。

          例如,在多個城鎮(zhèn)、城市和州經(jīng)營服務(wù)業(yè)務(wù)的多家運營商的戰(zhàn)略特許經(jīng)營業(yè)務(wù)與在多個州擁有數(shù)百家分店的大型倉儲式商店有所不同。

          成功指標也各不相同。通常,企業(yè)本地 SEO 活動的 KPI 屬于以下類別之一:

          • 提高各個地點的知名度和客流量。
          • 將本地意向搜索引導(dǎo)至在線商店進行直接投放,或?qū)砼c本地商店進行互動。
          • 以上兩者的結(jié)合。

          企業(yè)對“成功”的定義將極大地影響您為用戶創(chuàng)建選擇架構(gòu)的方法以及報告成功的方式。

          批量創(chuàng)建本地頁面的方法

          多年來,我們描述和制作多區(qū)域服務(wù)頁面的方法發(fā)生了變化。

          十年前,我們會將質(zhì)量低下的版本(只有細微修改且內(nèi)容基本相同)稱為門頁,而谷歌隨著時間的推移逐漸降低了門頁的價值。

          近年來,隨著程序化 SEO(pSEO)的日益普及,這種方法已經(jīng)成為大規(guī)模創(chuàng)建這些頁面的流行方法。

          本地服務(wù)頁面的程序化內(nèi)容創(chuàng)建

          對于運營數(shù)百或數(shù)千個門店的企業(yè)來說,程序化或部分程序化內(nèi)容創(chuàng)建可能是一個有吸引力的選擇。

          程序化 SEO(簡稱 pSEO)可讓您大規(guī)模生成大量內(nèi)容。這種方法已幫助許多企業(yè)擴大規(guī)模,但如果所創(chuàng)建的頁面無法提供足夠獨特的價值主張,讓 Google 不愿投入資源,那么這種方法也會帶來問題。

          如果我們看一下本地服務(wù)頁面的兩個常見網(wǎng)站架構(gòu),我們通常有一個中央服務(wù)頁面,然后是本地服務(wù)頁面,或者有一個充當區(qū)域設(shè)置服務(wù)頁面網(wǎng)關(guān)的中央頁面 - 例如商店定位器。

          圖片來自作者,2024 年 7 月

          根據(jù)您的業(yè)務(wù)類型,您可能會默認選擇一種結(jié)構(gòu),但兩者都可能帶來挑戰(zhàn)。

          使用中央服務(wù)頁面結(jié)構(gòu),您可能會遇到創(chuàng)建獨特價值主張的問題,并確保每個頁面都具有足夠的差異化并高于 Google 索引的質(zhì)量閾值。

          商店定位器頁面方法可能會導(dǎo)致 PageRank 分布以及內(nèi)部鏈接到不同位置的方式出現(xiàn)問題。大多數(shù)用戶友好的商店位置應(yīng)用程序不會加載 HTML 鏈接,因此雖然可以直觀地鏈接到所有商店,但 Google 無法抓取這些鏈接。

          然而,這兩種方法的共同問題是如何捕獲位置周圍“更廣泛”的搜索。

          本地內(nèi)容價值主張

          當本地頁面最適合位置時,它們會發(fā)揮最大的幫助。

          從歷史上看,我看到一些公司通過在頁面上“夸大”該地區(qū)的額外信息來做到這一點,比如一兩段關(guān)于當?shù)鼗A(chǔ)設(shè)施、學校和運動隊的段落——如果你想讓人們訪問你的五金店或詢問你的上門安全裝配服務(wù),這些信息都與你無關(guān)。

          僅僅更改 URL、H1、標題標簽和整個正文中的位置名稱也是不夠的。

          當這種情況發(fā)生時,谷歌實際上會看到近似重復(fù)的頁面,這些頁面在與用戶查詢相關(guān)的價值主張上幾乎沒有區(qū)別。

          這種情況的癥狀是,當頁面在 Search Console 中顯示為未編入索引時,Google 要么選擇覆蓋用戶聲明的規(guī)范,要么停留在“已發(fā)現(xiàn)”或“已抓取”的階段,而當前尚未編入索引。

          本地服務(wù)和位置頁面之間總會存在一定程度的重復(fù)。Google 對此并不介意。某些內(nèi)容在多個頁面上重復(fù)并不意味著其質(zhì)量低下。

          創(chuàng)造價值主張差異化

          這是我傾向于采用部分程序化方法的地方。

          程序化可以滿足 70% (+) 的頁面內(nèi)容;它可以涵蓋您針對特定位置的服務(wù)產(chǎn)品、定價和公司信息。

          該頁面的剩余百分比是手動的,但允許您創(chuàng)建與其他頁面的價值主張差異化。

          假設(shè)您是一家跨州快遞服務(wù)公司,擁有多條市場路線,您在德克薩斯州的主要配送中心位于奧斯汀、圣安東尼奧和達拉斯,而您想要瞄準尤利斯的潛在客戶。

          您為尤利斯提供的服務(wù)與您為普弗拉格維爾、凱爾和利安德的客戶提供的服務(wù)相同 - 因此每個位置頁面的這些部分在所有位置上都是相同的。

          但是尤利斯由達拉斯樞紐提供服務(wù),而其他機場則由奧斯汀樞紐提供服務(wù)——這是您要強調(diào)的第一個內(nèi)容差異點。

          然后,您可以使用來自企業(yè)內(nèi)部的數(shù)據(jù)和關(guān)鍵字研究,用旅行時間數(shù)據(jù)充實這些頁面。

          在尤利斯尋找快遞服務(wù)的客戶可能正在尋找從尤利斯到奧斯汀或從尤利斯到休斯頓的服務(wù)——因此,將此構(gòu)建到本地頁面并提供從目的地到熱門地點的時間估算,可以顯示出本地的專業(yè)性并幫助客戶更好地了解服務(wù)和計劃。

          您的業(yè)?務(wù)數(shù)據(jù)還將幫助您識別客戶類型。例如,在尤利斯預(yù)訂的許多工作可能針對的是搬出校園居住的大學生,因此這又是針對可以包含在頁面上的客戶群的更具本地化的定位。

          內(nèi)部鏈接

          當涉及到內(nèi)部鏈接時,使用偽 HTML 站點地圖可以幫助實現(xiàn)這一點,它不僅可以充當通過頁面的干凈內(nèi)部鏈接,而且還對用戶有益,并允許您創(chuàng)建其他登錄頁面來定位縣或地區(qū)級搜索。

          十年前,在一個房產(chǎn)查找頁面上,我所在的團隊構(gòu)建了“縣 > 鎮(zhèn)/市”的頁面結(jié)構(gòu)模式,同時將相關(guān)位置拉入登錄頁面。

          作者截圖,2024 年 7 月

          從視覺上看,這只是一種更“手動”的方法,讓用戶可以從非特定位置的頁面篩選到他們所在的當?shù)貐^(qū)域。

          Google 商業(yè)資料鏈接

          另一個經(jīng)常被忽視的關(guān)鍵組件是將 Google 商業(yè)資料(GBP) 直接鏈接到網(wǎng)站上的相關(guān)位置頁面。

          我遇到過許多跨國公司和國內(nèi)公司,他們鏈接回自己的公司主頁,有時還帶有一個參數(shù)來突出顯示用戶點擊的是哪個 GBP——但這既是糟糕的網(wǎng)絡(luò)架構(gòu),也是糟糕的用戶選擇架構(gòu)。

          如果用戶正在尋找 XYZ 中的服務(wù)/商店,他們不希望在點擊網(wǎng)站鏈接時看到主頁或通用信息頁面。

          就用戶選擇架構(gòu)而言,從這里,用戶可以導(dǎo)航到不同的商店或頁面,并錯過與他們相關(guān)的關(guān)鍵信息,否則這些信息可能會推動銷售或詢問。

          谷歌的本地算法

          除了 Google 的核心算法和更通用的搜索排名信號外,Google 還發(fā)布了專門針對本地查詢的更新。主要有兩個:

          • Pigeon 2014:此次更新旨在通過將本地搜索結(jié)果與一般搜索排名信號更緊密地結(jié)合起來,提供更相關(guān)、更準確的本地搜索結(jié)果。用戶接近度(作為信號)也得到了提升。
          • Possum 2016:此次更新旨在提高位于城市邊界外的商家的排名,使搜索結(jié)果更符合用戶與商家的距離。還引入了基于地址的過濾,以避免重復(fù)列出共享同一地址的商家(例如虛擬辦公室)。

          這些更新使企業(yè)更難以欺騙自己在當?shù)厥袌龅拇嬖冢⑶铱赡軣o法提供符合或滿足搜索者需求的價值主張。

          據(jù)傳,谷歌似乎優(yōu)先對提供最全面信息的企業(yè)進行排名。

          這包括開業(yè)日期、現(xiàn)場餐飲選擇(如果適用)、特殊營業(yè)時間、業(yè)務(wù)類別、服務(wù)列表以及定義服務(wù)區(qū)域和服務(wù)類型。

          Google 商業(yè)檔案的重要性

          遵循指南是必須的,但即便如此,你仍可能會違反 Google 的自動檢測檢查。

          與一家在亞洲擁有多個辦事處的國際軟件公司合作,在共享辦公室中租用了許多樓層。

          我們假設(shè),Google 偶爾會檢測到共享地址并誤認為它們是虛擬辦公室/虛假地址,而Possum 算法更新旨在減少這種情況。

          當您與擁有大量實際位置的企業(yè)組織合作時,通過內(nèi)部利益相關(guān)者管理以及了解 GBP 如何適應(yīng)并貢獻于總體目標和生態(tài)系統(tǒng),Google 商家資料管理方法可能會變得更加復(fù)雜。

          報告英鎊數(shù)據(jù)

          根據(jù)您的目標,報告成功的方式將因活動而異。

          通過 Google API,您可以訪問展示次數(shù)的列表級數(shù)據(jù),以及不同用戶互動的細分(從 GSC 鏡像指標推斷展示次數(shù)和點擊次數(shù))。

          非典型的 Google Business Profile 報告儀表板。(作者截圖,2024 年 7 月)

          在我看來,任何在多個城鎮(zhèn)、城市、縣或州運營的企業(yè)都需要擁有某種形式的 GBP 監(jiān)控和報告可見性,而不僅僅是在 Google Search Console 和其他分析平臺中跟蹤參數(shù)化 URL(假設(shè)您在 GBP 網(wǎng)站鏈接上使用參數(shù))。

          備工作

          省市區(qū)三級聯(lián)動所需文件:評論回復(fù)區(qū)

          文件說明:

          整個文件為一個大的對象(非數(shù)組類型)

          其中對象的屬性名 100000 中包含所有省份,它也是一個對象格式的;

          對象名為100000的每個屬性中的屬性名都為數(shù)字類型(其實是該地區(qū)的行政代碼)。數(shù)字屬性名對應(yīng)的屬性值是該地區(qū)的名稱,同時也可以通過該屬性名訪問到它下級的市列表。

          eg:

          			import districts from './js/districts.js'
          			console.log(districts[100000])
          

          比如當我們想獲取到行政代碼130000河北省下面的市級列表時:

          			import districts from './js/districts.js'
          			console.log(districts[130000])
          

          然后當我們想獲得行政代碼130100石家莊市下的區(qū)級列表時:

          			import districts from './js/districts.js'
          			console.log(districts[130100])
          

          沒錯,省市中的每一個行政代碼屬性名都對應(yīng)著它下一級(省行政代碼對應(yīng)市列表,市行政代碼對應(yīng)區(qū)列表)的對象列表。不存在數(shù)組對象嵌套。及其簡單方便。

          創(chuàng)建 option 標簽簡便API

          new Option('innerText', 'value屬性對應(yīng)的值')

          類似于傳統(tǒng)寫法:

          let option_1=document.createElement('option')

          option_1.text='innerText'

          option_1.value='value屬性對應(yīng)的值'

          嗯,沒什么區(qū)別,但是第一種簡單很多:

          清空 select標簽中的option標簽極簡方法:

          selectDOM.options.length=0

          .html文件中使用模塊化

          script 標簽改為 type 屬性改為 module <script type="module">

          創(chuàng)建三個 select標簽,分別用來存放 省級列表,市級列表,區(qū)級列表,并定義好變量:

          HTML:

          		<!-- 省級列表 -->
          		<select id="province"></select>
          		<!-- 市級列表 -->
          		<select id="city"></select>
          		<!-- 區(qū)級列表 -->
          		<select id="area"></select>
          

          JS:

          			// 獲取省市區(qū)selectDOM節(jié)點
          			let oProvince=document.getElementById('province')
          			let oCity=document.getElementById('city')
          			let oArea=document.getElementById('area')
          

          定義一個特定對應(yīng),用戶將用戶選中的結(jié)果獲取到:

          			let data={
          				province: {},
          				city: {},
          				area: {}
          			}
          

          第一步,渲染省份列表

          1,引入省市區(qū)文件 import districts from './js/districts.js'

          直接一個for in 循環(huán) districts[100000] 即可;

          然后在循環(huán)中創(chuàng)建option標簽,并插入到省級select節(jié)點中:

          			let provinceList=districts[100000] 
          			for (let province in provinceList) {
          				oProvince.options.add( new Option(provinceList[province], province) )
          			}
          

          效果:

          這樣我們的第一個select標簽就完成了

          第二步,渲染市級列表

          ps: 市級列表市基于省級之后才開始渲染的,所以應(yīng)該在省級select改變之后觸發(fā)(onchange事件)

          1,首先拿到用戶選中的是第幾個option;

          通過selectDOM的selectedIndex可拿到對應(yīng)索引值:

          然后通過children獲取option子元素列表,之后直接拿對應(yīng)索引即可!

          eg: this.children[this.selectedIndex], 因為是onChange事件,this指代selectDOM。

          在操作之前,可以先把用戶選中的省份數(shù)據(jù)存放在我們提前定義好的data對象中的province對象中:

          data.province.code=this.children[this.selectedIndex].value
          data.province.value=this.children[this.selectedIndex].text
          

          即此時數(shù)據(jù)應(yīng)為是:

          在數(shù)據(jù)存完之后我們就可以著手渲染我們的市級select標簽了。

          1,首先不管市級select中有沒有option標簽,我們先給它清除一遍(因為每個省份對應(yīng)的市都不一樣,如果不清除則下面渲染的市級option列表中含有上一個省份的市級option列表

          oCity.options.length=0
          

          2,清除完成之后我們用省級行政代碼(code)去獲取對應(yīng)的市級列表即可:

          districts['省級行政代碼'] 等于對應(yīng)市級列表

          districts[data.province.code] // 獲取用戶選中的省份的市級列表
          

          eg:

          拿到該省份的對應(yīng)市級列表之后,我們只需要向渲染省級列表一樣渲染市級列表即可:

          第三步,渲染區(qū)級列表

          第三步同第二步基本完全一樣,我們完全可以讓它兩共用一個函數(shù):

          在市級列表的onChange事件觸發(fā)之后執(zhí)行,

          存儲用戶選中的市的code以及市名。

          用獲取到的市級code去換對應(yīng)的區(qū)級列表即可(因為不存在數(shù)組對象嵌套,所以同省級code換市列表一樣);

          districts[data.province.code] // 獲取用戶選中的市級的區(qū)級列表
          

          在渲染區(qū)級之前先清空一直區(qū)級select下的option標簽列表

          eg:

          代碼同上基本一樣,除了需要注意此時我們的存值的對應(yīng)是我們事先定義好的市級對應(yīng)city而不是province即可。

          當區(qū)級select標簽onChange事件觸發(fā)時

          區(qū)級的 onChange 事件觸發(fā),此時我們已經(jīng)不需要再給渲染新的 select 標簽了(因為不是四級聯(lián)動,其實都一樣啦,大不了再復(fù)制一層唄)。所以我們只需要把用戶選中的結(jié)果存放在我們事先定義好的 data 中的 area 區(qū)級對象即可。

          這樣,當用戶選擇完畢之后,我們就會得到一個很清晰的對象了。

          至此基本完工。

          完善

          初步渲染完成時應(yīng)該給它下級一個默認值,這樣如果用戶不選了不至于獲取到的數(shù)據(jù)不完整:

          1,渲染完省級列表時默認給它一個選中的市,并存值。

          使用element.dispatchEvent().。實現(xiàn)onChange事件初始化觸發(fā)

          參考資料:https://www.jianshu.com/p/5f9027722204

          此時瀏覽器剛刷新數(shù)據(jù)為:

          源碼(不含省市區(qū)文件,省市區(qū)文件將開頭):

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8" />
          		<title></title>
          		<style type="text/css">
          			select {
          				height: 30px;
          				margin-right: 10px;
          			}
          		</style>
          	</head>
          	<body>
          		<!-- 省級列表 -->
          		<select id="province"></select>
          		<!-- 市級列表 -->
          		<select id="city"></select>
          		<!-- 區(qū)級列表 -->
          		<select id="area"></select>
          		
          		<script type="module">
          			// 引入省市區(qū)文件
          			import districts from './js/districts.js'
          			
          			// 獲取省市區(qū)selectDOM節(jié)點
          			let oProvince=document.getElementById('province')
          			let oCity=document.getElementById('city')
          			let oArea=document.getElementById('area')
          			
          			// 存儲用戶選中的值
          			let data={
          				province: {},
          				city: {},
          				area: {}
          			}
          			
          			// 渲染省級select標簽
          			let provinceList=districts[100000] 
          			for (let province in provinceList) {
          				oProvince.options.add( new Option(provinceList[province], province) )
          			}
          			
          			
          			// 當用戶選中某個省份時觸發(fā)
          			oProvince.addEventListener('change', function() {
          				// 先清空之前的市級<select>標簽下的option列表
          				oCity.options.length=0
          				// 保存省級數(shù)據(jù)
          				data.province.code=this.children[this.selectedIndex].value
          				data.province.value=this.children[this.selectedIndex].text
          				// 渲染市級列表
          				for(let city in districts[data.province.code]) {
          					oCity.options.add( new Option(districts[data.province.code][city], city) )
          				}
          			}, false)
          			
          			
          			// 當用戶選中某個市時觸發(fā)
          			oCity.addEventListener('change', function() {
          				oArea.options.length=0
          				// 保存市級數(shù)據(jù)
          				data.city.code=this.children[this.selectedIndex].value
          				data.city.value=this.children[this.selectedIndex].text
          				// 渲染區(qū)級列表
          				for(let city in districts[data.city.code]) {
          					oArea.options.add( new Option(districts[data.city.code][city], city) )
          				}
          			}, false)
          			
          			// 當用戶選中每個區(qū)時觸發(fā)
          			oArea.onchange=function () {
          				// 保存區(qū)級數(shù)據(jù)
          				data.area.code=this.children[this.selectedIndex].value
          				data.area.value=this.children[this.selectedIndex].text
          			}
          			
          			/**
          			 * 渲染完成初始化
          			 * @params dom selectDOM 
          			 */
          			function initSelect (dom) {
          				let createE=document.createEvent('HTMLEvents')
          				createE.initEvent("change", true, true)
          				dom.dispatchEvent(createE)
          			} 
          			initSelect(oProvince)
          			initSelect(oCity)
          			initSelect(oArea)
          			console.log(data)
          		</script>
          	</body>
          </html>
          

          至此基本完成啦。


          主站蜘蛛池模板: 国模大胆一区二区三区| 国产福利电影一区二区三区,日韩伦理电影在线福 | AV无码精品一区二区三区宅噜噜| 一区二区三区午夜| 一区二区三区在线观看| 成人无码一区二区三区| 国产suv精品一区二区6| 久久久无码精品人妻一区| 中文字幕亚洲综合精品一区| 国产一区二区三区不卡观| 红杏亚洲影院一区二区三区| 日韩经典精品无码一区| 国产一区二区三区精品久久呦| 精品无码一区二区三区电影| 久久一区二区明星换脸| 99精品国产高清一区二区三区 | 高清一区二区三区免费视频| 丰满岳妇乱一区二区三区| 在线一区二区三区| 老熟妇高潮一区二区三区| 美女视频一区三区网站在线观看| 日韩人妻无码一区二区三区久久 | 在线观看国产一区二区三区| 亚洲乱色熟女一区二区三区蜜臀| 人妻av无码一区二区三区| 日韩精品视频一区二区三区| 久久无码AV一区二区三区| 呦系列视频一区二区三区| 国产精品乱码一区二区三| 在线观看国产一区亚洲bd| 一区二区视频在线免费观看| 性色AV 一区二区三区| 果冻传媒一区二区天美传媒| 伊人久久大香线蕉av一区| 国产另类TS人妖一区二区| 无码国产精成人午夜视频一区二区 | 国产成人久久精品一区二区三区| 亚洲国产综合精品中文第一区| 精品无码一区二区三区爱欲九九| 亚洲综合av一区二区三区不卡| 色综合视频一区中文字幕|