Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产精品成人一区二区,亚洲精品一区二区电影,www.日本高清.com

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          去哪網開發實戰記錄(9):城市選擇頁(中)

          去哪網開發實戰記錄(9):城市選擇頁(中)

          弟組件之間的聯動

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

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

          字母點擊跳轉

          打開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)
              }
            }
          

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

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

          那就開始編寫使用發布訂閱模式實現Alphabet組件內的信息傳遞到City組件的代碼。修改子組件Alphabet.vue中handleLetterClick函數代碼為:

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

          接著去父組件City.vue中監聽該chang事件,請定義相應的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)
              }
          

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

             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來接收數據:

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

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

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

          控制臺測試發現當你點擊右側列表中的字母,控制臺仍然輸出了對應的字母。

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

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

          然后使用偵聽器來監聽letter的變化:

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

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

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

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

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

          拇指滑動跳轉

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

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

          <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>
          

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

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

          上面的計算屬性其實就是得到一個類似于['A','B','C','D']的數組。然后你城市字母表遍歷的對象就不再是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>
          

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

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

          第二步,修改handleTouchMove函數為:

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

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

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

          如果你想要獲取某個字母距離藍色區域底部的高度,可以將其高度減去藍色區域底部的高度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)
                }
              },
          

          測試發現拇指移動到字母M處,右側顯示正常:

          最后使用子組件Alphabet使用發布訂閱模式向City組件傳遞數據:

          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])
                  }
                }
              },
          

          測試發現功能顯示正常。

          城市列表頁新能優化

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

            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組件內不會顯示任何信息,然后通過ajax獲取到數據時Alphabet組才會被重新渲染,之后會觸發生命周期函數updated,這時候開始計算藍色區域底部與字母A標簽的距離。

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

          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計時器對象是否存在,存在就清空該計時器避免緩存,否則就定義一個計時器并設置計時器時間為16毫秒,即每16毫秒才計算一次,這樣可避免不必要的計算工作。

          搜索功能實現

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

          打開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>
          

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

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

          然后需要實現搜索信息與結果展示區域的聯動,就需要使用到數據的雙向綁定:

          <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>
          

          最后結果肯定是顯示城市,那么需要從父組件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一個數組list(該數組用于存儲搜索結果)和計時器timer(函數節流使用):

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

          接著我們定義一個偵聽器用于監聽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]就是數組,value就是數組中的每一項
                    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結構為:

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

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

          接下來就是對搜索結果的布局進行優化,給搜索結果添加一個.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
          

          測試發現頁面顯示正常,但是搜索結果是無法滾動的,此時可以借助于bertter-scroll來實現。

          第一步獲取到search-content類節點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>
          

          第二步導入Bscroll類及創建該對象:

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

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

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

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

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

          這樣我們就實現了當list的長度為0的時候才顯示“沒有找到匹配的城市”字眼,但是這樣會造成一個問題,就是剛開始你不輸入關鍵詞的時候也會出現這個“沒有找到匹配的城市”字眼,把熱門城市和城市列表給遮住了。這個問題還是可以通過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>
          

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

          于企業多地點業務而言,SEO 策略與業務策略的協調對于成功至關重要。

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

          另一個關鍵要求是,您的內容方法要為用戶和 Google 創造足夠的價值,以使其高于索引質量閾值。

          這意味著超越本地 SEO 的標準最佳實踐 ,并創建可持續提高品牌知名度和轉化率的本地 SEO 活動。

          協調 SEO 與業務戰略

          多地點經營的企業有不同的目標。

          雖然多地點管理的基礎相同,但您的方法需要與整體戰略相結合并與整體業務目標保持一致。

          例如,在多個城鎮、城市和州經營服務業務的多家運營商的戰略特許經營業務與在多個州擁有數百家分店的大型倉儲式商店有所不同。

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

          • 提高各個地點的知名度和客流量。
          • 將本地意向搜索引導至在線商店進行直接投放,或將來與本地商店進行互動。
          • 以上兩者的結合。

          企業對“成功”的定義將極大地影響您為用戶創建選擇架構的方法以及報告成功的方式。

          批量創建本地頁面的方法

          多年來,我們描述和制作多區域服務頁面的方法發生了變化。

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

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

          本地服務頁面的程序化內容創建

          對于運營數百或數千個門店的企業來說,程序化或部分程序化內容創建可能是一個有吸引力的選擇。

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

          如果我們看一下本地服務頁面的兩個常見網站架構,我們通常有一個中央服務頁面,然后是本地服務頁面,或者有一個充當區域設置服務頁面網關的中央頁面 - 例如商店定位器。

          圖片來自作者,2024 年 7 月

          根據您的業務類型,您可能會默認選擇一種結構,但兩者都可能帶來挑戰。

          使用中央服務頁面結構,您可能會遇到創建獨特價值主張的問題,并確保每個頁面都具有足夠的差異化并高于 Google 索引的質量閾值。

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

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

          本地內容價值主張

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

          從歷史上看,我看到一些公司通過在頁面上“夸大”該地區的額外信息來做到這一點,比如一兩段關于當地基礎設施、學校和運動隊的段落——如果你想讓人們訪問你的五金店或詢問你的上門安全裝配服務,這些信息都與你無關。

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

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

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

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

          創造價值主張差異化

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

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

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

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

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

          但是尤利斯由達拉斯樞紐提供服務,而其他機場則由奧斯汀樞紐提供服務——這是您要強調的第一個內容差異點。

          然后,您可以使用來自企業內部的數據和關鍵字研究,用旅行時間數據充實這些頁面。

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

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

          內部鏈接

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

          十年前,在一個房產查找頁面上,我所在的團隊構建了“縣 > 鎮/市”的頁面結構模式,同時將相關位置拉入登錄頁面。

          作者截圖,2024 年 7 月

          從視覺上看,這只是一種更“手動”的方法,讓用戶可以從非特定位置的頁面篩選到他們所在的當地區域。

          Google 商業資料鏈接

          另一個經常被忽視的關鍵組件是將 Google 商業資料(GBP) 直接鏈接到網站上的相關位置頁面。

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

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

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

          谷歌的本地算法

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

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

          這些更新使企業更難以欺騙自己在當地市場的存在,并且可能無法提供符合或滿足搜索者需求的價值主張。

          據傳,谷歌似乎優先對提供最全面信息的企業進行排名。

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

          Google 商業檔案的重要性

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

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

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

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

          報告英鎊數據

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

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

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

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

          備工作

          省市區三級聯動所需文件:評論回復區

          文件說明:

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

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

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

          eg:

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

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

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

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

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

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

          創建 option 標簽簡便API

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

          類似于傳統寫法:

          let option_1=document.createElement('option')

          option_1.text='innerText'

          option_1.value='value屬性對應的值'

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

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

          selectDOM.options.length=0

          .html文件中使用模塊化

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

          創建三個 select標簽,分別用來存放 省級列表,市級列表,區級列表,并定義好變量:

          HTML:

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

          JS:

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

          定義一個特定對應,用戶將用戶選中的結果獲取到:

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

          第一步,渲染省份列表

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

          直接一個for in 循環 districts[100000] 即可;

          然后在循環中創建option標簽,并插入到省級select節點中:

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

          效果:

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

          第二步,渲染市級列表

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

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

          通過selectDOM的selectedIndex可拿到對應索引值:

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

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

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

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

          即此時數據應為是:

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

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

          oCity.options.length=0
          

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

          districts['省級行政代碼'] 等于對應市級列表

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

          eg:

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

          第三步,渲染區級列表

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

          在市級列表的onChange事件觸發之后執行,

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

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

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

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

          eg:

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

          當區級select標簽onChange事件觸發時

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

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

          至此基本完工。

          完善

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

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

          使用element.dispatchEvent().。實現onChange事件初始化觸發

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

          此時瀏覽器剛刷新數據為:

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

          <!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>
          		<!-- 區級列表 -->
          		<select id="area"></select>
          		
          		<script type="module">
          			// 引入省市區文件
          			import districts from './js/districts.js'
          			
          			// 獲取省市區selectDOM節點
          			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) )
          			}
          			
          			
          			// 當用戶選中某個省份時觸發
          			oProvince.addEventListener('change', function() {
          				// 先清空之前的市級<select>標簽下的option列表
          				oCity.options.length=0
          				// 保存省級數據
          				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)
          			
          			
          			// 當用戶選中某個市時觸發
          			oCity.addEventListener('change', function() {
          				oArea.options.length=0
          				// 保存市級數據
          				data.city.code=this.children[this.selectedIndex].value
          				data.city.value=this.children[this.selectedIndex].text
          				// 渲染區級列表
          				for(let city in districts[data.city.code]) {
          					oArea.options.add( new Option(districts[data.city.code][city], city) )
          				}
          			}, false)
          			
          			// 當用戶選中每個區時觸發
          			oArea.onchange=function () {
          				// 保存區級數據
          				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本道一区二区三区四区| 免费萌白酱国产一区二区 | 亚洲AⅤ视频一区二区三区 | 国产一区二区三区免费观看在线 | 久久久久久人妻一区二区三区| 无码人妻精品一区二区三区东京热 | 91午夜精品亚洲一区二区三区| 亚洲AV成人一区二区三区AV| 国产丝袜无码一区二区视频| 久久国产视频一区| 人妻无码一区二区三区AV| 日韩av无码一区二区三区| 亚洲天堂一区二区三区| 日本免费一区二区在线观看| 国产一区三区二区中文在线| 日本不卡一区二区视频a| 2021国产精品视频一区| 日本免费精品一区二区三区| 日本中文字幕一区二区有码在线| 亚洲Av永久无码精品一区二区| 日本高清成本人视频一区| 国产精品视频一区麻豆| 日本道免费精品一区二区| 亚洲高清日韩精品第一区| 国产一区高清视频| 极品尤物一区二区三区| 国产福利电影一区二区三区,日韩伦理电影在线福 | 美女免费视频一区二区| 精品无码一区二区三区电影| 久久久久人妻一区精品| 中文字幕色AV一区二区三区| 亚洲国产精品一区二区第四页| 亚洲熟妇AV一区二区三区浪潮| 亚洲综合色一区二区三区| 免费无码一区二区三区蜜桃大| 国产91精品一区| 日韩精品无码一区二区三区四区| 国产一区二区三区久久| 78成人精品电影在线播放日韩精品电影一区亚洲 | 中文字幕一区二区三区5566| 亚洲一区精品视频在线|