Vue 項(xiàng)目里戳中你痛點(diǎn)的問題及解決辦法

一、先總結(jié)出如下幾點(diǎn)vue項(xiàng)目開發(fā)中常見的問題及解決辦法。

  • ? 列表進(jìn)入詳情頁的傳參問題。
  • ? 本地開發(fā)環(huán)境請求服務(wù)器接口跨域的問題
  • ? API接口的統(tǒng)一管理
  • ? UI庫的按需加載
  • ? 定時(shí)器問題
  • ? rem文件的導(dǎo)入問題
  • ? Vue-Awesome-Swiper基本能解決你所有的輪播需求
  • ? 打包后生成很大的.map文件的問題
  • ? fastClick的300ms延遲解決方案
  • ? 組件中寫選項(xiàng)的順序
  • ? 路由懶加載(也叫延遲加載)
  • ? 開啟gzip壓縮代碼
  • ? 詳情頁返回列表頁緩存數(shù)據(jù)和瀏覽位置、其他頁面進(jìn)入列表頁刷洗數(shù)據(jù)的實(shí)踐
  • ? css的scoped私有作用域和深度選擇器
    ==========================================

(1)列表進(jìn)入詳情頁的傳參問題。

??例如商品列表頁面前往商品詳情頁面,需要傳一個(gè)商品id;

<router-link :to="{path: 'detail', query: {id: 1}}">
      前往detail頁面
</router-link>

??c頁面的路徑為http://localhost:8080/#/detail?id=1,可以看到傳了一個(gè)參數(shù)id=1,并且就算刷新頁面id也還會存在。此時(shí)在c頁面可以通過id來獲取對應(yīng)的詳情數(shù)據(jù),獲取id的方式是this.$route.query.id

補(bǔ)充一下其他兩種傳參:
  • ?如果傳參通過<router-link :to="{name: 'Detail', params: {id:1}}">前往Detail頁面</router-link>的話,這種傳參方式,params中的參數(shù)不會出現(xiàn)在url中,所以一旦刷新頁面,id就不存在了,很懊惱有木有?。。傞_始我就在這里糾結(jié)了好一陣~~實(shí)際是因?yàn)闆]有發(fā)現(xiàn)更好的辦法,被自己蠢到了?。?!
  • ? 還有一種傳參的方式:動(dòng)態(tài)路由傳參。動(dòng)態(tài)路由通過在路由文件中定義路由:{ path: '/detail/:id', name: 'Detail', component: Detail },就是這里/detail/:id,然后在頁面進(jìn)行傳參<router-link to="/detail/123">前往d頁面</router-link>,在詳情頁可以通過this.$route.params.id獲取傳來的參數(shù),url會顯示為http://localhost:8080/#/detail/123,且刷新頁面參數(shù)也會存在。對于傳單個(gè)參數(shù),這種方式實(shí)現(xiàn)了我們的需求,但是沒法傳多個(gè)參數(shù)。除非定義路由的時(shí)候,定義為/detail/:id/:param1/param2/param3,這樣限制死了,必須要這樣傳參數(shù)。且個(gè)人角度看來,這種/detail/1/123/1d的url不如detail?id=1&user=123&identity=1來的優(yōu)雅。

(2)本地開發(fā)環(huán)境請求服務(wù)器接口跨域的問題


???上面的這個(gè)報(bào)錯(cuò)大家都不會陌生,報(bào)錯(cuò)是說沒有訪問權(quán)限(跨域問題)。本地開發(fā)項(xiàng)目請求服務(wù)器接口的時(shí)候,因?yàn)榭蛻舳说耐床呗裕瑢?dǎo)致了跨域的問題。
???vue-cli初始化的項(xiàng)目,在配置文件中提供了proxyTable來解決本地開發(fā)的跨域問題。config文件的index.js文件中,找到proxyTable選項(xiàng),進(jìn)行如下配置:

proxyTable: {
      // 用‘/api’開頭,代理所有請求到目標(biāo)服務(wù)器
      '/api': {
        target: 'http://jsonplaceholder.typicode.com', // 接口域名
        changeOrigin: true, // 是否啟用跨域
        pathRewrite: { //
          '^/api': ''
        }
      }
}

?例如請求接口:
?/api/posts/1 ==>http://jsonplaceholder.typicode.com/posts/1
?這個(gè)時(shí)候就可以在本地環(huán)境請求后臺接口了。

(3)axios的封裝和API接口的統(tǒng)一管理:

???axios的封裝,主要是用來幫我們進(jìn)行請求的攔截和響應(yīng)的攔截。
???在請求的攔截中我們可以攜帶userToken,post請求頭、qs對post提交數(shù)據(jù)的序列化等。
???在響應(yīng)的攔截中,我們可以進(jìn)行根據(jù)狀態(tài)碼來進(jìn)行錯(cuò)誤的統(tǒng)一處理等等。
???axios接口的統(tǒng)一管理,是做項(xiàng)目時(shí)必須的流程。這樣可以方便我們管理我們的接口,在接口更新時(shí)我們不必再返回到我們的業(yè)務(wù)代碼中去修改接口。
???由于這里內(nèi)容稍微多一些,日后放在另一篇文章,更新后這里會送上鏈接。

(4)UI庫的按需加載:

???為什么要使用按需加載的方式而不是一次性全部引入,原因就不多說了。這里以vant的按需加載為例,演示vue中ui庫怎樣進(jìn)行按需加載:

  • 安裝: cnpm i vant -S
  • 安裝babel-plugin-import插件使其按需加載: cnpm i babel-plugin-import -D
  • 在 .babelrc文件中中添加插件配置 :
libraryDirectory { 
    "plugins": [ 
        // 這里是原來的代碼部分
        // …………
        // 這里是要我們配置的代碼
        ["import", 
            { 
                "libraryName": "vant", 
                "libraryDirectory": "es", 
                "style": true 
            }
        ] 
    ] 
}
  • 在main.js中按需加載你需要的插件:
// 按需引入vant組件
import {   
    DatetimePicker,   
    Button,   
    List 
} from 'vant';
  • 使用組件:
// 使用vant組件
Vue.use(DatetimePicker)  
    .use(Button)  
    .use(List);
  • 最后在在頁面中使用:
<van-button type="primary">按鈕</van-button>

??補(bǔ)充:出來vant庫外,像antiUi、elementUi等,很多ui庫都支持按需加載,可以去看文檔,上面都會有提到。基本都是通過安裝babel-plugin-import插件來支持按需加載的,使用方式與vant的如出一轍,可以去用一下。

(5) 定時(shí)器問題:

??我在a頁面寫一個(gè)定時(shí),讓他每秒鐘打印一個(gè)1,然后跳轉(zhuǎn)到b頁面,此時(shí)可以看到,定時(shí)器依然在執(zhí)行。這樣是非常消耗性能的。如下圖所示:




解決思路很簡單:
首先我在data函數(shù)里面進(jìn)行定義定時(shí)器名稱:

data() {            
    return {                              
        timer: null  // 定時(shí)器名稱          
    }        
},

然后這樣使用定時(shí)器this.timer = setInterval(……………………)
最后在beforeDestroy()生命周期內(nèi)清除定時(shí)器:

beforeDestroy() {
    clearInterval(this.timer);        
    this.timer = null;
}

(6) rem文件的導(dǎo)入問題:

???我們在做手機(jī)端時(shí),適配是必須要處理的一個(gè)問題。例如,我們處理適配的方案就是通過寫一個(gè)rem.js,原理很簡單,就是根據(jù)網(wǎng)頁尺寸計(jì)算html的font-size大小,基本上小伙伴們都知道,這里直接附上代碼,不多做介紹。

(function(c,d){
    var e=document.documentElement||document.body,
    a="orientationchange" in window?"orientationchange":"resize",
    b=function(){
      var f=e.clientWidth;
      e.style.fontSize=(f>=750)?"100px":100*(f/750)+"px"
     };
   b();
c.addEventListener(a,b,false)})(window);

?這里說下怎么引入的問題,很簡單。在main.js中,直接import './config/rem'導(dǎo)入即可。import的路徑根據(jù)你的文件路徑去填寫。

(7) Vue-Awesome-Swiper基本能解決你所有的輪播需求

??在我們使用的很多ui庫(vant、antiUi、elementUi等)中,都有輪播組件,對于普通的輪播效果足夠了。但是,某些時(shí)候,我們的輪播效果可能比較炫,這時(shí)候ui庫中的輪播可能就有些力不從心了。當(dāng)然,如果技術(shù)和時(shí)間上都還可以的話,可以自己造個(gè)比較炫的輪子。
??這里我說一下vue-awesome-swiper這個(gè)輪播組件,真的非常強(qiáng)大,基本可以滿足我們的輪播需求。swiper相信很多人都用過,很好用,也很方便我們二次開發(fā),定制我們需要的輪播效果。vue-awesome-swiper組件實(shí)質(zhì)上給予swiper的,或者說就是能在vue中跑的swiper。下面說下怎么使用:

  • 安裝 cnpm install vue-awesome-swiper --save
  • 在組件中使用的方法,全局使用意義不大:
// 引入組件
import 'swiper/dist/css/swiper.css' 
import { swiper, swiperSlide } from 'vue-awesome-swiper'

// 在components中注冊組件
components: {
    swiper,
    swiperSlide
}
// template中使用輪播
// ref是當(dāng)前輪播
// callback是回調(diào)
// 更多參數(shù)用法,請參考文檔
<swiper :options="swiperOption" ref="mySwiper" @someSwiperEvent="callback">            
    <!-- slides -->            
    <swiper-slide><div class="item">1</div></swiper-slide>            
    <swiper-slide><div class="item">2</div></swiper-slide>            
    <swiper-slide><div class="item">3</div></swiper-slide>            
    <swiper-slide><div class="item">4</div></swiper-slide>            
    <swiper-slide><div class="item">5</div></swiper-slide>            
    <swiper-slide><div class="item">6</div></swiper-slide>            
    <swiper-slide><div class="item">7</div></swiper-slide>            
    <!-- Optional controls -->            
    <div class="swiper-pagination"  slot="pagination"></div>            
    <div class="swiper-button-prev" slot="button-prev"></div>            
    <div class="swiper-button-next" slot="button-next"></div>            
    <div class="swiper-scrollbar"   slot="scrollbar"></div>
</swiper>
// 參數(shù)要寫在data中
data() {            
    return {     
        // swiper輪播的參數(shù)           
        swiperOption: { 
            // 滾動(dòng)條                   
            scrollbar: {                        
                el: '.swiper-scrollbar',                    
            }, 
            // 上一張,下一張                   
            navigation: {                        
                nextEl: '.swiper-button-next',                        
                prevEl: '.swiper-button-prev',                    
            },
            // 其他參數(shù)…………   
        }            
    }                    
},

?附上文檔:npm文檔swiper3.0/4.0文檔,更多用法,請參考文檔說明。

(8)打包后生成很大的.map文件的問題

??項(xiàng)目打包后,代碼都是經(jīng)過壓縮加密的,如果運(yùn)行時(shí)報(bào)錯(cuò),輸出的錯(cuò)誤信息無法準(zhǔn)確得知是哪里的代碼報(bào)錯(cuò)。 而生成的.map后綴的文件,就可以像未加密的代碼一樣,準(zhǔn)確的輸出是哪一行哪一列有錯(cuò)可以通過設(shè)置來不生成該類文件。但是我們在生成環(huán)境是不需要.map文件的,所以可以在打包時(shí)不生成這些文件:在config/index.js文件中,設(shè)置productionSourceMap: false,就可以不生成.map文件

(9)fastClick的300ms延遲解決方案

??開發(fā)移動(dòng)端項(xiàng)目,點(diǎn)擊事件會有300ms延遲的問題。至于為什么會有這個(gè)問題,請自行百度即可。這里只說下常見的解決思路,不管vue項(xiàng)目還是jq項(xiàng)目,都可以使用fastClick解決。

  • 安裝:
cnpm install fastclick -S
  • 在main.js中引入和初始化:
import FastClick from 'fastclick'; // 引入插件
FastClick.attach(document.body); // 使用 fastclick

(10)組件中寫選項(xiàng)的順序

export default {
  name: '',
  mixins: [],
  components: {},
  props: {},
  data() {},
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  destroyed() {},
  methods: {}
};

查看打包后各文件的體積,把你快速定位大文件

??如果你是vue-cli初始化的項(xiàng)目,會默認(rèn)安裝webpack-bundle-analyzer插件,該插件可以幫助我們查看項(xiàng)目的體積結(jié)構(gòu)對比和項(xiàng)目中用到的所有依賴。也可以直觀看到各個(gè)模塊體積在整個(gè)項(xiàng)目中的占比。很霸道有木有~~

npm run build --report 
// 直接運(yùn)行,然后在瀏覽器打開http://127.0.0.1:8888/即可查看

?記得運(yùn)行的時(shí)候先把之前npm run dev開啟的本地關(guān)掉

(11)路由懶加載(也叫延遲加載)

??路由懶加載可以幫我們在進(jìn)入首屏?xí)r不用加載過度的資源,從而減少首屏加載速度。

  • 路由文件中,非懶加載寫法:
import Index from '@/page/index/index';
export default new Router({  
    routes: [    
        { 
            path: '/', 
            name: 'Index',     
            component: Index 
        }
    ]
})
  • 路由懶加載寫法:
export default new Router({
  routes: [    
        { 
            path: '/', 
            name: 'Index', 
            component: resolve => require(['@/view/index/index'], resolve) 
        }
   ]
})

(12)開啟gzip壓縮代碼

??spa這種單頁應(yīng)用,首屏由于一次性加載所有資源,所有首屏加載速度很慢。解決這個(gè)問題非常有效的手段之一就是前后端開啟gizp(其他還有緩存、路由懶加載等等)。gizp其實(shí)就是幫我們減少文件體積,能壓縮到30%左右,即100k的文件gizp后大約只有30k。
??vue-cli初始化的項(xiàng)目中,是默認(rèn)有此配置的,只需要開啟即可。但是需要先安裝插件:

cnpm i compression-webpack-plugin
  • 然后在config/index.js中開啟即可:
build: {
    ………………
    productionGzip: true, // false不開啟gizp,true開啟
    ………………
}

??現(xiàn)在打包的時(shí)候,除了會生成之前的文件,還是生成.gz結(jié)束的gzip過后的文件。具體實(shí)現(xiàn)就是如果客戶端支持gzip,那么后臺后返回gzip后的文件,如果不支持就返回正常沒有g(shù)zip的文件。

注意:這里前端進(jìn)行的打包時(shí)的gzip,但是還需要后臺服務(wù)器的配置。配置是比較簡單的,配置幾行代碼就可以了,一般這個(gè)操作可以叫運(yùn)維小哥哥小姐姐去搞一下,沒有運(yùn)維的讓后臺去幫忙配置。

(13) 詳情頁返回列表頁緩存數(shù)據(jù)和瀏覽位置、其他頁面進(jìn)入列表頁刷新數(shù)據(jù)的實(shí)踐

??這樣一個(gè)場景:有三個(gè)頁面,首頁/或者搜索頁,商品分類頁面,商品詳情頁。我們希望從首頁進(jìn)入分類頁面時(shí),分類頁面要刷新數(shù)據(jù),從分類進(jìn)入詳情頁再返回到分類頁面時(shí),我們不希望刷新,我們希望此時(shí)的分類頁面能夠緩存已加載的數(shù)據(jù)和自動(dòng)保存用戶上次瀏覽的位置。之前在百度搜索的基本都是keep-alive處理的,但是總有那么一些不完善,所以自己在總結(jié)了之后進(jìn)行了如下的實(shí)踐。

??解決這種場景需求我們可以通過vue提供的keepAlive屬性。這里直接送上另一篇處理這個(gè)問題的傳送門

(14)CSS的coped私有作用域和深度選擇器

大家都知道當(dāng) <style> 標(biāo)簽有 scoped 屬性時(shí),它的 CSS 只作用于當(dāng)前組件中的元素。那么他是怎么實(shí)現(xiàn)的呢,大家看一下編譯前后的代碼就明白了:

  • 編譯前:
<style scoped>
.example {
  color: red;
}
</style>
  • 編譯后:
<style>
.example[data-v-f3f3eg9] {
  color: red;
}

??看完你肯定就會明白了,其實(shí)是在你寫的組件的樣式,添加了一個(gè)屬性而已,這樣就實(shí)現(xiàn)了所謂的私有作用域。但是也會有弊端,考慮到瀏覽器渲染各種 CSS 選擇器的方式,當(dāng) p { color: red }設(shè)置了作用域時(shí) (即與特性選擇器組合使用時(shí)) 會慢很多倍。如果你使用 class 或者 id 取而代之,比如 .example { color: red },性能影響就會消除。所以,在你的樣式里,進(jìn)來避免直接使用標(biāo)簽,取而代之的你可以給標(biāo)簽起個(gè)class名。

??如果你希望 scoped 樣式中的一個(gè)選擇器能夠作用得“更深”,例如影響子組件,你可以使用 >>> 操作符:

<style scoped>
    .parent >>> .child { /* ... */ }
</style>
  • 上述代碼將會編譯成:
.parent[data-v-f3f3eg9] .child { 
    /* ... */ 
}

??而對于less或者sass等預(yù)編譯,是不支持>>>操作符的,可以使用/deep/來替換>>>操作符,例如:.parent /deep/ .child { /* ... */ }

文章轉(zhuǎn)載自https://juejin.im/post/5b174de8f265da6e410e0b4e

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容