VUE常用問題hack修改


vue-router

router這里踩的坑主要是組件的重用。構建單頁面大型應用的話,肯定要開啟組件的緩存的,因為一般會要求后退的時候不要重新加載頁面,而且要記住原始的滾動位置。
首先,引入router-view的地方要加上keep-alive

<router-view keep-alive></router-view>

然后開啟html5 history模式,並開啟位置紀錄

const router = new Router({
  history: true, // use history=false when testing
  saveScrollPosition: true
})

開啟keep-alive以后,當要求一個組件的內容發生變化時,比如 漫畫詳情頁面是一個路由帶參數的組件,當參數變化時,router會重用這個組件,而不是重新請求數據,這顯然是不符合要求的,所以正確的姿勢是:
首先,用一個字段保存這個路由參數,
用router的鈎子函數data獲取路由變化參數,保存到字段里


route:{
  data: function(transition){
      this.bookId = transition.to.params.id;
  }
}

寫一個watcher來拉取數據並填充模版,因為在data鈎子函數中,我們已經修改了相應字段,所以當路由參數更改時會直接觸發這個watcher

watch: {
  'bookId' : function(val){
    //do something
  }
}

如果是多個參數的,可以把這些參數放到一個對象里,watcher采用深監測

route:{
  data: function(transition){
      this.watcher.type = transition.to.params.type;
      this.watcher.id = transition.to.query.id;
  }
},
watch : {
  'watcher' : {
    handler: function(val){
    //do something
    window.scrollTo(0,0);// 不使用緩存時,不使用記錄好的用戶位置,滑倒頂部
    },
    deep: true
  }
}

一開始沒有用這種方法出了很多的bug,改了以后,路由和緩存方面的邏輯瞬間就變得清晰了,組件的切換也更加流暢了。
第二個坑就是關於緩存頁面瀏覽位置的紀錄,router是通過html5 history的pushState來紀錄當前滾動位置的,切換路由的時候,把當前位置push進去,用戶后退時,會觸發onpopstate事件,這個時候再把位置取出來並滾動到指定位置,但是!某些瀏覽器本身也設置了一些奇怪的位置滾動,vue-router的滾動就失效了,所以需要延遲執行一下

   window.addEventListener("popstate",function(e){
     setTimeout(()=>{
       window.scrollTo(0,e.state.pos.y);//通過打log,發現了位置紀錄在這個變量里了
     },300)
    },false);

然而,瀏覽器只能記錄一個位置,所以會有這樣的情況: 從m.cm233.com 到 m.cm233.com/book,再返回到m.cm233.com,這時瀏覽器跳到了當時記錄的位置,但是再前進到/book時,瀏覽器還是會停在首頁的那個位置上,這個bug暫時還沒有解決,好在用戶場景不是很多。所以告訴我們,子頁面路由參數變化的時候,要把滾動條人工弄到最上面,要不然就會滾動到入口頁面的瀏覽位置。也就是watcher里還要加一句如上的滾動。

頁面標題也是要手動更改的,所以每個頁面要放一個專門的title變量存一下,然后在data鈎子函數(用於組件緩存時) 和 路由參數的watcher(用於組件更新時) 里 都改變title

route:{
  data: function(transition){
    this.title = 'hiahia';
    document.title = this.title;
  }
},
watch : {
  'id' : function(val){
    this.title = 'hiahia';
    document.title = this.title;
  }
}

通常頁面的標題不是固定的,用變量存儲title,主要是為了記住上一次組件被用的時候的title,以便於重用的時候更換。

然而,ios微信不會監測document.title的變化,所以要寫一個專門針對它的hack,通過創建iframe

//全局函數
window.isWeiXin = function(){
    var ua = window.navigator.userAgent.toLowerCase();
    if(ua.match(/MicroMessenger/i) == 'micromessenger'){
        return true;
    }else{
        return false;
    }
}

window.weiXinChange = function(title){
  if(window.isWeiXin()){
    document.title = title;
    var iframe = document.createElement('iframe');
    iframe.src = './favicon.ico';
    iframe.style.display = 'none';
    iframe.onload = function(){
      setTimeout(function() {
        document.body.removeChild(iframe);
      }, 0);
    }
    document.body.appendChild(iframe);
  }
}

//組件中

route:{
  data: function(transition){
    this.title = '墨瞳漫畫';
    document.title = this.title;
    window.weiXinChange(this.title);
  }
},
watch : {
  'id' : function(val){
    this.title = '墨瞳漫畫';
    document.title = this.title;
    window.weiXinChange(this.title);
  }
}

vue-infinite-scroll (directive)

(為什么不自己寫!)

組件地址 https://github.com/ElemeFE/vue-infinite-scroll 餓了么出品

使用方法
main.js

import Scroll from 'vue-infinite-scroll'
Vue.use(Scroll)

組件中

  <dl v-infinite-scroll="loadMore()" infinite-scroll-disabled="busy" infinite-scroll-distance="7">
    <template v-for="item in list">
      <dd class="page-item">
      </dd>
    </template>
  </dl>

其中busy這個變量比較重要,他控制着這個指令是否繼續執行,當沒有下一頁數據的時候,應該把busy置為true來關閉滾動加載。正在讀取下一頁數據時,要先把busy置為true,數據返回時在置為false

loadmore(){
  this.busy = true;
  someApi.someFunction().then((data) => {this.busy = false;})
}

但是這個組件在路由切換的時候會出問題,routerView被移除時,組件會觸發加載(大概是因為頁面高度突然塌陷),而且會一直加載到我們自己設置的停止條件(busy=true)。所以離開頁面的時候,需要在路由的deactivate鈎子函數里把滾動關掉,再次進入頁面的時候再開啟(路由無變化在data鈎子函數里開啟,有變化的話在watcher里開啟,如果不需要在路由改變時向子組件延時傳遞參數也可以都在data鈎子函數里開啟)

route:{
    deactivate: function(transition){
      this.busy = true;
      transition.next();
    },
  data: function(transition){
    if(){
      this.busy = false;
    }//這里輸入組件路由參數沒有變化的條件
  }
}

lazyload

(為什么不自己寫!)
網上找了幾個lazyload的組件,都不太好使,就自己改了一個,是改了一個,原組件叫vue-lazyload, 毛病還挺多的,寫這個組件的人估計沒有真正在大項目中用過就匆匆發布在npm了,es6版本也寫的不倫不類的 - -,不過還是很厲害,自己寫的話毛病肯定會更多。我改后的放在https://github.com/Ganother/blog/blob/master/lazyload.js了,是個較為穩定的版本。其中過渡動畫寫在img-loaded這個class里

/*簡單的透明度漸入,圖片加載完成后會刪掉這個class,以防router切換緩存頁面時再次引起動畫*/
.img-loaded {
  animation: loaded .2s ease-in-out;
 }
 @keyframes loaded {
   0%{
     opacity: 0;
   }
   100%{
     opacity: 1;
   }
 }
let loadingJpg = require('assets/loading.jpg');//這里引入一張loading圖,會被轉成base64
Vue.use(VueLazyload, {
 preLoad: 1.3, //圖片頂端距窗口頂端1.3個屏幕高度時開始加載
 loading: loadingJpg,
 error: loadingJpg
})

自適應的圖片:如果服務端傳過來的圖片帶了寬高信息,可以在img外層包一個class為img-bar的元素,圖片過來的時候先設置一個min-height為響應高,組件在圖片加載后會自動取掉這個min-height。這樣可以防止loading圖和圖片大小不一樣引起的頁面跳動繼而導致的加載圖片時機錯誤。

vue-resource

跨域時,會先發送一個空的options請求來查看接口是不是支持跨域,再發送一次真實請求。還不是很了解這種方式的好處,當接口較多時,請求數量多了一倍也是有點尷尬的,所以要設置一下。而且如果接口每次都打印空參數的log的話。。。嗯。

Vue.http.options.emulateJSON = true;
Vue.http.options.headers={
    'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
};

結構目錄

vue-cli直接構建的,src里的目錄如下

api 放一些ajax請求接口的函數
assets 放一些靜態資源,圖片,公共sass
directives 放一些指令js,比如改動后的lazyload
pages 頁面入口組件,用在router中
components 小組件們
vuex vuex
app.vue
main.js

另外,可以修改下生成的靜態文件,vue-cli默認聲稱的靜態文件時間戳是加在文件名上的,如app.fefdfd7s8f78sd7.js,這樣版本迭代很快后會使服務器上積壓過多無用文件,我們希望這樣加時間戳 app.js?t=32j32ih4u32h 所以改一下webpack.prod.conf.js就好了,如下

  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].js?t=[chunkhash]'),
    chunkFilename: utils.assetsPath('js/[id].js')
  }


    new ExtractTextPlugin(utils.assetsPath('css/[name].css?t=[contenthash]')),


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM