一、先總結(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