困擾99%前端程序員的Vue問題,全在這了
1.前言
最近要求使用vue進行前后端分離開發(fā)微信公眾號,不斷摸索踩坑之后,總結出如下幾點vue項目開發(fā)中常見的問題及解決辦法。如果你是vue大佬,請忽略小弟的愚見
- 列表進入詳情頁的傳參問題。
- 本地開發(fā)環(huán)境請求服務器接口跨域的問題
- axios封裝和api接口的統(tǒng)一管理
- UI庫的按需加載
- 如何優(yōu)雅的只在當前頁面中覆蓋ui庫中組件的樣式
- 定時器問題
- rem文件的導入問題
- Vue-Awesome-Swiper基本能解決你所有的輪播需求
- 打包后生成很大的.map文件的問題
- fastClick的300ms延遲解決方案
- 組件中寫選項的順序
2.列表進入詳情頁傳參問題
例如商品列表頁面前往商品詳情頁面,需要傳一個商品id;
- <router-link :to="{path: 'detail', query: {id: 1}}">前往detail頁面</router-link>
c頁面的路徑為 http://localhost:8080/#/detail?id=1
,可以看到傳了一個參數 id=1
,并且就算刷新頁面id也還會存在。此時在c頁面可以通過id來獲取對應的詳情數據,獲取id的方式是 this.$route.query.id
vue傳參方式有:query、params+動態(tài)路由傳參。
vue傳參方式有:query、params+動態(tài)路由傳參。說下兩者的區(qū)別:
- 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>
- query通過
this.$route.query
來接收參數,params通過this.$route.params
來接收參數。
- // query通過this.$route.query接收參數
- created () {
- const id = this.$route.query.id;
- }
- // params通過this.$route.params來接收參數
- created () {
- const id = this.$route.params.id;
- }
- query傳參的url展現方式:
/detail?id=1&user=123&identity=1&更多參數
params+動態(tài)路由的url方式:/detail/123
- params動態(tài)路由傳參,一定要在路由中定義參數,然后在路由跳轉的時候必須要加上參數,否則就是空白頁面
- {
- path: '/detail/:id',
- name: 'Detail',
- component: Detail
- },
注意,params傳參時,如果沒有在路由中定義參數,也是可以傳過去的,同時也能接收到,但是一旦刷新頁面,這個參數就不存在了。這對于需要依賴參數進行某些操作的行為是行不通的,因為你總不可能要求用戶不能刷新頁面吧。例如:
- // 定義的路由中,只定義一個id參數
- {
- path: 'detail/:id',
- name: 'Detail',
- components: Detail
- }
- // template中的路由傳參,
- // 傳了一個id參數和一個token參數
- // id是在路由中已經定義的參數,而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;
- }
3.本地開發(fā)環(huán)境請求服務器接口跨域的問題
上面的這個報錯大家都不會陌生,報錯是說沒有訪問權限(跨域問題)。本地開發(fā)項目請求服務器接口的時候,因為客戶端的同源策略,導致了跨域的問題。下面先演示一個沒有配置允許本地跨域的的情況:
可以看到,此時我們點擊獲取數據,瀏覽器提示我們跨域了。所以我們訪問不到數據。那么接下來我們演示設置允許跨域后的數據獲取情況:
我們在1出設置了允許本地跨域,在2處,要注意我們訪問接口時,寫的是 /api
,此處的 /api
指代的就是我們要請求的接口域名。如果我們不想每次接口都帶上 /api
,可以更改axios的默認配置 axios.defaults.baseURL = '/api';
這樣,我們請求接口就可以直接 this.$axios.get('app.php?m=App&c=Index&a=index')
,很簡單有木有。此時如果你在 network
中查看 xhr
請求,你會發(fā)現顯示的是 localhost:8080/api
的請求地址。這樣沒什么大驚小怪的,代理而已:
好了,最后附上proxyTable的代碼:
- proxyTable: {
- // 用‘/api’開頭,代理所有請求到目標服務器
- '/api': {
- target: 'http://jsonplaceholder.typicode.com', // 接口域名
- changeOrigin: true, // 是否啟用跨域
- pathRewrite: { //
- '^/api': ''
- }
- }
- }
注意:配置好后一定要關閉原來的server,重新 npm run dev
啟動項目。不然無效。
axios的封裝,主要是用來幫我們進行請求的攔截和響應的攔截。在請求的攔截中我們可以攜帶userToken,post請求頭、qs對post提交數據的序列化等。在響應的攔截中,我們可以進行根據狀態(tài)碼來進行錯誤的統(tǒng)一處理等等。axios接口的統(tǒng)一管理,是做項目時必須的流程。這樣可以方便我們管理我們的接口,在接口更新時我們不必再返回到我們的業(yè)務代碼中去修改接口。
4.UI庫的按需加載
為什么要使用按需加載的方式而不是一次性全部引入,原因就不多說了。這里以vant的按需加載為例,演示vue中ui庫怎樣進行按需加載:
- 安裝:
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>
ps:出來 vant
庫外,像 antiUi
、 elementUi
等,很多ui庫都支持按需加載,可以去看文檔,上面都會有提到?;径际峭ㄟ^安裝babel-plugin-import插件來支持按需加載的,使用方式與vant的如出一轍,可以去用一下。
5.如何優(yōu)雅的只在當前頁面中覆蓋ui庫中組件的樣式
首先我們vue文件的樣式都是寫在 <style lang="less" scoped></style>
標簽中的,加scoped是為了使得樣式只在當前頁面有效。那么問題來了,看圖:
我們正常寫的所有樣式,都會被加上[data-v-23d425f8]這個屬性(如1所示),但是第三方組件內部的標簽并沒有編譯為附帶[data-v-23d425f8]這個屬性。所以,我們想修改組件的樣式,就沒轍了。怎么辦呢,有些小伙伴給第三方組件寫個class,然后在一個公共的css文件中或者在當前頁面再寫一個沒有socped屬性的style標簽,然后直接在里面修改第三方組件的樣式。這樣不失為一個方法,但是存在全局污染和命名沖突的問題。約定特定的命名方式,可以避免命名沖突。但是還是不夠優(yōu)雅。作為一名優(yōu)( 強 )秀( 迫 )的( 癥 )前( 患 )端( 者 ),怎么能允許這種情況出現呢?好了,下面說下優(yōu)雅的解決方式:通過深度選擇器解決。例如修改上圖中組件里的van-ellipsis類的樣式,可以這樣做:
- .van-tabs /deep/ .van-ellipsis { color: blue};
編譯后的結果就是:
這樣就不會給 van-ellipsis
也添加 [data-v-23d425f8]
屬性了。至此你可以愉快的修改第三方組件的樣式了。當然了這里的深度選擇器 /deep/
是因為我用的 less
語言,如果你沒有使用 less/sass
等,可以用 >>>
符號。更多的關于深度選擇器的內容,在文章后面有介紹。
6.定時器問題
我在a頁面寫一個定時,讓他每秒鐘打印一個1,然后跳轉到b頁面,此時可以看到,定時器依然在執(zhí)行。這樣是非常消耗性能的。如下圖所示:
解決方法1
首先我在data函數里面進行定義定時器名稱:
- data() {
- return {
- timer: null // 定時器名稱
- }
- },
然后這樣使用定時器:
- this.timer = (() => {
- // 某些操作
- }, 1000)
最后在beforeDestroy()生命周期內清除定時器:
- beforeDestroy() {
- clearInterval(this.timer);
- this.timer = null;
- }
方案1有兩點不好的地方,引用尤大的話來說就是:
- timer
解決方案2
該方法是通過$once這個事件偵聽器器在定義完定時器之后的位置來清除定時器。以下是完整代碼:
- const timer = setInterval(() =>{
- // 某些定時器操作
- }, 500);
- // 通過$once來監(jiān)聽定時器,在beforeDestroy鉤子可以被清除。
- this.$once('hook:beforeDestroy', () => {
- clearInterval(timer);
- })
方案2要感謝@zzx18023在評論區(qū)提供出的解決方案。類似于其他需要在當前頁面使用,離開需要銷毀的組件(例如一些第三方庫的picker組件等等),都可以使用此方式來解決離開后以后在背后運行的問題。綜合來說,我們更推薦使用 方案2,使得代碼可讀性更強,一目了然 。如果不清楚once、on、$off的使用,這里送上官網的地址教程,在程序化的事件偵聽器那里: https:// cn.vuejs.org/v2/guide/c omponents-edge-cases.html#%E7%A8%8B%E5%BA%8F%E5%8C%96%E7%9A%84%E4%BA%8B%E4%BB%B6%E4%BE%A6%E5%90%AC%E5%99%A8 。
7.rem文件的導入問題
我們在做手機端時,適配是必須要處理的一個問題。例如,我們處理適配的方案就是通過寫一個rem.js,原理很簡單,就是根據網頁尺寸計算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'
導入即可。import的路徑根據你的文件路徑去填寫。
8.Vue-Awesome-Swiper基本能解決你所有的輪播需求
在我們使用的很多ui庫(vant、antiUi、elementUi等)中,都有輪播組件,對于普通的輪播效果足夠了。但是,某些時候,我們的輪播效果可能比較炫,這時候ui庫中的輪播可能就有些力不從心了。當然,如果技術和時間上都還可以的話,可以自己造個比較炫的輪子.這里我說一下 vue-awesome-swiper
這個輪播組件,真的非常強大,基本可以滿足我們的輪播需求。swiper相信很多人都用過,很好用,也很方便我們二次開發(fā),定制我們需要的輪播效果。vue-awesome-swiper組件實質上基于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是當前輪播
- // callback是回調
- // 更多參數用法,請參考文檔
- <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>
- // 參數要寫在data中
- data() {
- return {
- // swiper輪播的參數
- swiperOption: {
- // 滾動條
- scrollbar: {
- el: '.swiper-scrollbar',
- },
- // 上一張,下一張
- navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev',
- },
- // 其他參數…………
- }
- }
- },
swiper需要配置哪些功能需求,自己根據文檔進行增加或者刪減。附上文檔:npm文檔,swiper3.0/4.0文檔,更多用法,請參考文檔說明。
9.打包后生成很大的.map文件的問題
項目打包后,代碼都是經過壓縮加密的,如果運行時報錯,輸出的錯誤信息無法準確得知是哪里的代碼報錯。 而生成的.map后綴的文件,就可以像未加密的代碼一樣,準確的輸出是哪一行哪一列有錯可以通過設置來不生成該類文件。但是我們在生成環(huán)境是不需要.map文件的,所以可以在打包時不生成這些文件:在 config/index.js
文件中,設置 productionSourceMap: false,
就可以不生成 .map
文件
10.fastClick的300ms延遲解決方案
開發(fā)移動端項目,點擊事件會有300ms延遲的問題。至于為什么會有這個問題,請自行百度即可。這里只說下常見的解決思路,不管vue項目還是jq項目,都可以使用 fastClick
解決。安裝 fastClick
:
- cnpm install fastclick -S
在main.js中引入fastClick和初始化:
- import FastClick from 'fastclick'; // 引入插件
- FastClick.attach(document.body); // 使用 fastclick
11.組件中寫選項的順序
為什么選項要有統(tǒng)一的書寫順序呢?很簡單,就是要將選擇和認知成本最小化。
-
- 副作用 (觸發(fā)組件外的影響)
el
- 全局感知 (要求組件以外的知識)
- name
- parent
- 組件類型 (更改組件的類型)
functional
- 模板修改器 (改變模板的編譯方式)
- 副作用 (觸發(fā)組件外的影響)
- delimiters
- comments
- 模板依賴 (模板內使用的資源)
- components
- directives
- filters
-
- 組合 (向選項里合并屬性)
- extends
- mixins
-
- 接口 (組件的接口)
inheritAttrs
model
props
/propsData
- 本地狀態(tài) (本地的響應式屬性)
- 接口 (組件的接口)
- data
- computed
-
- 事件 (通過響應式事件觸發(fā)的回調)
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- watch
- 非響應式的屬性 (不依賴響應系統(tǒng)的實例屬性)
methods
- 渲染 (組件輸出的聲明式描述)
template
/render
renderError