最近要求使用vue進(jìn)行前后端分離開發(fā)微信公眾號,不斷摸索踩坑之后无虚,總結(jié)出如下幾點vue項目開發(fā)中常見的問題及解決辦法踢俄。如果你是vue大佬咪橙,請忽略小弟的愚見
列表進(jìn)入詳情頁的傳參問題聂宾。
本地開發(fā)環(huán)境請求服務(wù)器接口跨域的問題
API接口的統(tǒng)一管理
UI庫的按需加載
定時器問題
rem文件的導(dǎo)入問題
Vue-Awesome-Swiper基本能解決你所有的輪播需求
打包后生成很大的.map文件的問題
fastClick的300ms延遲解決方案
組件中寫選項的順序
路由懶加載(也叫延遲加載)
開啟gzip壓縮代碼
詳情頁返回列表頁緩存數(shù)據(jù)和瀏覽位置果善、其他頁面進(jìn)入列表頁刷洗數(shù)據(jù)的實踐
css的scoped私有作用域和深度選擇器
===========================這是華麗麗的分割線~~=========================
列表進(jìn)入詳情頁的傳參問題。
例如商品列表頁面前往商品詳情頁面亏吝,需要傳一個商品id;
<router-link :to="{path: 'detail', query: {id: 1}}">前往detail頁面</router-link>
c頁面的路徑為http://localhost:8080/#/detail?id=1
岭埠,可以看到傳了一個參數(shù)id=1,并且就算刷新頁面id也還會存在蔚鸥。此時在c頁面可以通過id來獲取對應(yīng)的詳情數(shù)據(jù),獲取id的方式是this.$route.query.id
vue傳參方式有:query许赃、params+動態(tài)路由傳參止喷。
說下兩者的區(qū)別:
1.query通過path切換路由,params通過name切換路由
// query通過path切換路由
<router-link :to="{path: 'Detail', query: { id: 1 }}">前往Detail頁面</router-link>
// params通過name切換路由
<router-link :to="{name: 'Detail', params: { id: 1 }}">前往Detail頁面</router-link>
2.query通過`this.$route.query`來接收參數(shù)混聊,params通過this.$route.params來接收參數(shù)弹谁。
// query通過this.$route.query接收參數(shù)
created () {
const id = this.$route.query.id;
}
// params通過this.$route.params來接收參數(shù)
created () {
const id = this.$route.params.id;
}
3.query傳參的url展現(xiàn)方式:/detail?id=1&user=123&identity=1&更多參數(shù)
params+動態(tài)路由的url方式:/detail/123
4.params動態(tài)路由傳參,一定要在路由中定義參數(shù)句喜,然后在路由跳轉(zhuǎn)的時候必須要加上參數(shù)预愤,否則就是空白頁面:
{
path: '/detail/:id',
name: 'Detail',
component: Detail
},
注意,params傳參時咳胃,如果沒有在路由中定義參數(shù)植康,也是可以傳過去的,同時也能接收到展懈,但是一旦刷新頁面销睁,這個參數(shù)就不存在了供璧。這對于需要依賴參數(shù)進(jìn)行某些操作的行為是行不通的,因為你總不可能要求用戶不能刷新頁面吧冻记。 例如:
// 定義的路由中睡毒,只定義一個id參數(shù)
{
path: 'detail/:id',
name: 'Detail',
components: Detail
}
// template中的路由傳參,
// 傳了一個id參數(shù)和一個token參數(shù)
// id是在路由中已經(jīng)定義的參數(shù)冗栗,而token沒有定義
<router-link :to="{name: 'Detail', params: { id: 1, token: '123456' }}">前往Detail頁面</router-link>
// 在詳情頁接收
created () {
// 以下都可以正常獲取到
// 但是頁面刷新后演顾,id依然可以獲取,而token此時就不存在了
const id = this.$route.params.id;
const token = this.$route.params.token;
}
個人角度來說隅居,我是更喜歡query來傳參的偶房,更靈活自由。PS:生命誠可貴军浆,愛情價更高棕洋。若為自由故,兩者皆可拋呀~~~
本地開發(fā)環(huán)境請求服務(wù)器接口跨域的問題
[圖片上傳中...(image-e71731-1530834775408-3)]
上面的這個報錯大家都不會陌生乒融,報錯是說沒有訪問權(quán)限(跨域問題)掰盘。本地開發(fā)項目請求服務(wù)器接口的時候,因為客戶端的同源策略赞季,導(dǎo)致了跨域的問題愧捕。
vue-cli初始化的項目,在配置文件中提供了proxyTable來解決本地開發(fā)的跨域問題申钩。config文件的index.js文件中次绘,找到proxyTable選項,進(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
這個時候就可以在本地環(huán)境請求后臺接口了邮偎。
axios的封裝和API接口的統(tǒng)一管理:
axios的封裝,主要是用來幫我們進(jìn)行請求的攔截和響應(yīng)的攔截义黎。
在請求的攔截中我們可以攜帶userToken禾进,post請求頭、qs對post提交數(shù)據(jù)的序列化等廉涕。
在響應(yīng)的攔截中泻云,我們可以進(jìn)行根據(jù)狀態(tài)碼來進(jìn)行錯誤的統(tǒng)一處理等等。
axios接口的統(tǒng)一管理狐蜕,是做項目時必須的流程宠纯。這樣可以方便我們管理我們的接口,在接口更新時我們不必再返回到我們的業(yè)務(wù)代碼中去修改接口层释。
由于這里內(nèi)容稍微多一些婆瓜,日后放在另一篇文章,更新后這里會送上鏈接湃累。
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的如出一轍,可以去用一下勤婚。
定時器問題:
我在a頁面寫一個定時摹量,讓他每秒鐘打印一個1,然后跳轉(zhuǎn)到b頁面馒胆,此時可以看到缨称,定時器依然在執(zhí)行。這樣是非常消耗性能的祝迂。如下圖所示:
[圖片上傳中...(image-bbb27b-1530834775407-2)]
[圖片上傳中...(image-22274c-1530834775407-1)]
解決方法1:
首先我在data函數(shù)里面進(jìn)行定義定時器名稱:
data() {
return {
timer: null // 定時器名稱
}
},
然后這樣使用定時器:
this.timer = (() => {
// 某些操作
}, 1000)
最后在beforeDestroy()生命周期內(nèi)清除定時器:
beforeDestroy() {
clearInterval(this.timer);
this.timer = null;
}
方案1有兩點不好的地方睦尽,引用尤大的話來說就是:
- 它需要在這個組件實例中保存這個
timer
,如果可以的話最好只有生命周期鉤子可以訪問到它型雳。這并不算嚴(yán)重的問題当凡,但是它可以被視為雜物。 - 我們的建立代碼獨立于我們的清理代碼纠俭,這使得我們比較難于程序化的清理我們建立的所有東西沿量。
解決方案2:
該方法是通過$once這個事件偵聽器器在定義完定時器之后的位置來清除定時器。以下是完整代碼:
const timer = setInterval(() =>{
// 某些定時器操作
}, 500);
// 通過$once來監(jiān)聽定時器柑晒,在beforeDestroy鉤子可以被清除欧瘪。
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
方案2要感謝@zzx18023在評論區(qū)提供出的解決方案。類似于其他需要在當(dāng)前頁面使用匙赞,離開需要銷毀的組件(例如一些第三方庫的picker組件等等),都可以使用此方式來解決離開后以后在背后運行的問題妖碉。
綜合來說涌庭,我們更推薦使用方案2,使得代碼可讀性更強(qiáng)欧宜,一目了然坐榆。如果不清楚$once、$on冗茸、$off
的使用席镀,這里送上官網(wǎng)的地址教程匹中,在程序化的事件偵聽器那里。
rem文件的導(dǎo)入問題:
我們在做手機(jī)端時豪诲,適配是必須要處理的一個問題顶捷。例如,我們處理適配的方案就是通過寫一個rem.js屎篱,原理很簡單服赎,就是根據(jù)網(wǎng)頁尺寸計算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ù)你的文件路徑去填寫提针。
Vue-Awesome-Swiper基本能解決你所有的輪播需求
在我們使用的很多ui庫(vant、antiUi次洼、elementUi等)中关贵,都有輪播組件,對于普通的輪播效果足夠了卖毁。但是揖曾,某些時候,我們的輪播效果可能比較炫亥啦,這時候ui庫中的輪播可能就有些力不從心了炭剪。當(dāng)然,如果技術(shù)和時間上都還可以的話翔脱,可以自己造個比較炫的輪子奴拦。
這里我說一下vue-awesome-swiper這個輪播組件,真的非常強(qiáng)大届吁,基本可以滿足我們的輪播需求错妖。swiper相信很多人都用過,很好用疚沐,也很方便我們二次開發(fā)暂氯,定制我們需要的輪播效果。vue-awesome-swiper組件實質(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>
<!-- 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: {
// 滾動條
scrollbar: {
el: '.swiper-scrollbar',
},
// 上一張辣吃,下一張
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// 其他參數(shù)…………
}
}
},
swiper需要配置哪些功能需求动遭,自己根據(jù)文檔進(jìn)行增加或者刪減。附上文檔:npm文檔神得,swiper3.0/4.0文檔厘惦,更多用法,請參考文檔說明循头。
打包后生成很大的.map文件的問題
項目打包后绵估,代碼都是經(jīng)過壓縮加密的,如果運行時報錯卡骂,輸出的錯誤信息無法準(zhǔn)確得知是哪里的代碼報錯国裳。 而生成的.map后綴的文件,就可以像未加密的代碼一樣全跨,準(zhǔn)確的輸出是哪一行哪一列有錯可以通過設(shè)置來不生成該類文件缝左。但是我們在生成環(huán)境是不需要.map文件的,所以可以在打包時不生成這些文件:
在config/index.js文件中浓若,設(shè)置productionSourceMap: false
,就可以不生成.map文件
[圖片上傳中...(image-c1ad26-1530834775405-0)]
fastClick的300ms延遲解決方案
開發(fā)移動端項目渺杉,點擊事件會有300ms延遲的問題。至于為什么會有這個問題挪钓,請自行百度即可是越。這里只說下常見的解決思路,不管vue項目還是jq項目碌上,都可以使用fastClick
解決倚评。
安裝 fastClick
:
cnpm install fastclick -S
在main.js中引入fastClick
和初始化:
import FastClick from 'fastclick'; // 引入插件
FastClick.attach(document.body); // 使用 fastclick
組件中寫選項的順序
為什么選項要有統(tǒng)一的書寫順序呢?很簡單馏予,就是要將選擇和認(rèn)知成本最小化天梧。
-
副作用 (觸發(fā)組件外的影響)
el
-
全局感知 (要求組件以外的知識)
name
parent
-
組件類型 (更改組件的類型)
functional
-
模板修改器 (改變模板的編譯方式)
delimiters
comments
-
模板依賴 (模板內(nèi)使用的資源)
components
directives
filters
-
組合 (向選項里合并屬性)
extends
mixins
-
接口 (組件的接口)
inheritAttrs
model
-
props
/propsData
-
本地狀態(tài) (本地的響應(yīng)式屬性)
data
computed
-
事件 (通過響應(yīng)式事件觸發(fā)的回調(diào))
watch
- 生命周期鉤子 (按照它們被調(diào)用的順序)
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
非響應(yīng)式的屬性 (不依賴響應(yīng)系統(tǒng)的實例屬性)
* `methods`
- 渲染 (組件輸出的聲明式描述)
* `template`/`render`
* `renderError`
查看打包后各文件的體積,把你快速定位大文件
如果你是vue-cli初始化的項目霞丧,會默認(rèn)安裝webpack-bundle-analyzer
插件呢岗,該插件可以幫助我們查看項目的體積結(jié)構(gòu)對比和項目中用到的所有依賴。也可以直觀看到各個模塊體積在整個項目中的占比蛹尝。很霸道有木有~~
[圖片上傳中...(image-f5c75e-1530834775415-4)]
npm run build --report // 直接運行后豫,然后在瀏覽器打開http://127.0.0.1:8888/即可查看
記得運行的時候先把之前npm run dev
開啟的本地關(guān)掉
路由懶加載(也叫延遲加載)
路由懶加載可以幫我們在進(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)
}
]
})
開啟gzip壓縮代碼
spa這種單頁應(yīng)用,首屏由于一次性加載所有資源陨收,所有首屏加載速度很慢。解決這個問題非常有效的手段之一就是前后端開啟gizp(其他還有緩存、路由懶加載等等)务漩。gizp其實就是幫我們減少文件體積拄衰,能壓縮到30%左右,即100k的文件gizp后大約只有30k饵骨。
vue-cli初始化的項目中翘悉,是默認(rèn)有此配置的,只需要開啟即可居触。但是需要先安裝插件:
cnpm i compression-webpack-plugin
然后在config/index.js中開啟即可:
build: {
………………
productionGzip: true, // false不開啟gizp妖混,true開啟
………………
}
現(xiàn)在打包的時候,除了會生成之前的文件轮洋,還是生成.gz結(jié)束的gzip過后的文件制市。具體實現(xiàn)就是如果客戶端支持gzip,那么后臺后返回gzip后的文件弊予,如果不支持就返回正常沒有g(shù)zip的文件祥楣。
****注意**:這里前端進(jìn)行的打包時的gzip,但是還需要后臺服務(wù)器的配置汉柒。配置是比較簡單的误褪,配置幾行代碼就可以了,一般這個操作可以叫運維小哥哥小姐姐去搞一下碾褂,沒有運維的讓后臺去幫忙配置兽间。
詳情頁返回列表頁緩存數(shù)據(jù)和瀏覽位置、其他頁面進(jìn)入列表頁刷新數(shù)據(jù)的實踐
這樣一個場景:有三個頁面正塌,首頁/或者搜索頁嘀略,商品分類頁面,商品詳情頁传货。我們希望從首頁進(jìn)入分類頁面時屎鳍,分類頁面要刷新數(shù)據(jù),從分類進(jìn)入詳情頁再返回到分類頁面時问裕,我們不希望刷新逮壁,我們希望此時的分類頁面能夠緩存已加載的數(shù)據(jù)和自動保存用戶上次瀏覽的位置。之前在百度搜索的基本都是keep-alive處理的粮宛,但是總有那么一些不完善窥淆,所以自己在總結(jié)了之后進(jìn)行了如下的實踐。
解決這種場景需求我們可以通過vue提供的keepAlive屬性巍杈。這里直接送上另一篇處理這個問題的傳送門吧
CSS的coped私有作用域和深度選擇器
大家都知道當(dāng) <style>
標(biāo)簽有 scoped
屬性時忧饭,它的 CSS 只作用于當(dāng)前組件中的元素。那么他是怎么實現(xiàn)的呢筷畦,大家看一下編譯前后的代碼就明白了:
編譯前:
<style scoped>
.example {
color: red;
}
</style>
編譯后:
<style>
.example[data-v-f3f3eg9] {
color: red;
}
看完你肯定就會明白了词裤,其實是在你寫的組件的樣式刺洒,添加了一個屬性而已,這樣就實現(xiàn)了所謂的私有作用域吼砂。但是也會有弊端逆航,考慮到瀏覽器渲染各種 CSS 選擇器的方式,當(dāng) p { color: red }
設(shè)置了作用域時 (即與特性選擇器組合使用時) 會慢很多倍渔肩。如果你使用 class 或者 id 取而代之因俐,比如 .example { color: red }
,性能影響就會消除周偎。所以抹剩,在你的樣式里,進(jìn)來避免直接使用標(biāo)簽蓉坎,取而代之的你可以給標(biāo)簽起個class名澳眷。
如果你希望 scoped
樣式中的一個選擇器能夠作用得“更深”,例如影響子組件袍嬉,你可以使用 >>>
操作符:
<style scoped>
.parent >>> .child { /* ... */ }
</style>
上述代碼將會編譯成:
.parent[data-v-f3f3eg9] .child {
/* ... */
}
而對于less或者sass等預(yù)編譯境蔼,是不支持>>>
操作符的,可以使用/deep/
來替換>>>
操作符伺通,例如:.parent /deep/ .child { /* ... */ }
==================================
后面會繼續(xù)更新:
- restful路由設(shè)計
- axios封裝和api接口的統(tǒng)一管理
- hiper打開速度測試
- postcss在vue中的使用和相關(guān)插件
- porp雙向數(shù)據(jù)流的實踐
- vue插件的開發(fā)
- vue開發(fā)公眾號時接入微信jssdk箍土,jssdk接口項目的坑和封裝
- vue不同需求的登錄流程的實踐
- vue骨架屏的實現(xiàn)
- vue頁面切換動畫相關(guān)的實踐
- vue中css、js代碼的提取和去除冗余