一勺届、啟動項目
1. 安裝腳手架和依賴
首先保證你的電腦上有node和npm主到,版本越新越好
-
npm install -g vue-cli
全局安裝腳手架工具派桩,安裝的時候可以指定版本 -
vue init webpack myProject
用webpack工具初始化vue項目,myProject是項目名稱徘层,可以自己命名 -
cd myProject
進入創(chuàng)建的vue項目目錄 -
npm install
安裝項目所需要的依賴,也就是安裝一些必要的插件 -
npm run dev
開始運行項目
2. 項目代碼介紹
- README.md----項目的介紹說明文件
- package.json----一些第三方模塊的依賴
- package-lock.json----確定第三方包的版本利职,保證團隊編程的統(tǒng)一
- LICENSE----開源協(xié)議的說明(就是你的代碼別人可以怎么用趣效,是否可用于商業(yè)化等)
- index.html----首頁的模板文件
- .postcssrc.js----對postcss的一個配置文件,postcss會處理我們的css
- .gitignore----把不想提交到線上的文件放在這里
- eslintrc.js----寫代碼的規(guī)范
- .editorconfig----配置了一些編輯器的語法猪贪,可以自行添加修改
- .babelrc----語法解析器跷敬,把我們寫的vue單文件組件寫法的代碼解析成瀏覽器能夠識別的代碼
- static----靜態(tài)資源(靜態(tài)圖片或者我們模擬的json數(shù)據(jù))
- node_modules----我們依賴的第三方包
- src---我們項目的源代碼
- config----我們項目的配置文件
index.js 基礎(chǔ)的配置信息
dev.nav.js 開發(fā)時的配置信息
prod.nev.js 線上的配置信息
- build----我們項目打包的一些webpack的配置內(nèi)容
3. 多頁應(yīng)用 vs 單頁應(yīng)用
3.1 多頁應(yīng)用
- 什么是多頁應(yīng)用?
頁面每次跳轉(zhuǎn)热押,后臺都會返回一個新的HTML頁面
- 執(zhí)行步驟:
頁面跳轉(zhuǎn)----->返回HTML
- 優(yōu)缺點:
優(yōu)點:
- 首屏?xí)r間快(請求了第一個HTML頁面西傀,就立即展示)
- SEO效果好(搜索引擎可以識別HTML中的內(nèi)容,而我們每個頁面都放在HTML中桶癣,搜索效果好)
缺點:
頁面切換慢(每次跳轉(zhuǎn)都要發(fā)一個HTTP請求)
3.2 單頁應(yīng)用
- 什么是單頁應(yīng)用拥褂?
js感知到url的變化,js會動態(tài)的清除掉當(dāng)前頁面的內(nèi)容饺鹃,把下個頁面的內(nèi)容掛載到當(dāng)前頁面上,頁面始終就有一個,路由是由我們前端做了悔详。
- 執(zhí)行步驟:
頁面跳轉(zhuǎn)----->js渲染
- 優(yōu)缺點:
優(yōu)點:
頁面切換快
缺點:
首屏?xí)r間稍慢镊屎,SEO差(現(xiàn)在可以解決)
4. 項目代碼初始化(移動端)
- 設(shè)置meta標簽
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
- 引入
reset.css
---把不同手機的瀏覽器的初始化樣式做一個統(tǒng)一 - 引入
border.css
---解決移動端1像素邊框的問題(因為手機分辨率不同) - 引入
fastclick
庫---解決某些機型點擊后延遲300ms的問題。怎么引入伟端?
- npm install fastclick --save(save是指不管開發(fā)和線上都使用)
- 在
main.js
中引入杯道,import fastClick from "fastclick"
,然后
fastClick.attach(document.body)
责蝠,綁定到body上
- 在
iconfont
矢量圖標庫建立一個圖標倉庫
使用方法:
- 本地引用:
- 打開官網(wǎng)->圖標管理->我的項目->新建項目->選擇所需圖標到購物車->添加至新建項目
- 將選擇好的圖標倉庫下載到本地
- 把所有的字體文件(以
svg,eot,ttf,woff
等結(jié)尾的文件)和iconfont.css
文件拿出來党巾,其他的文件沒用- 把所有的字體文件放在一起iconfont文件夾(本項目將iconfont文件夾放在了assets里面styles文件夾下)
- 修改
iconfont.css
中的字體路徑- 在
main.js
中全局引入iconfont.css
文件或者在需要字體圖標的文件里局部引入iconfont.css
文件- 在官網(wǎng)字體所在倉庫里把
iconfont
字體的地址粘貼到代碼對應(yīng)位置,OK優(yōu)點:在沒有網(wǎng)絡(luò)的時候霜医,字體圖標可以正常顯示
缺點:如果想要在倉庫里新加圖標應(yīng)用到項目齿拂,那么必須重新引入
- 在線引用:
- 在字體倉庫下有生成在線鏈接,點擊肴敛,生成在線鏈接署海,放入項目的css文件夾下面,不要忘了字體的樣式也要引入
- 在官網(wǎng)字體所在倉庫里把
iconfont
字體的地址粘貼到代碼對應(yīng)位置,OK優(yōu)點:添加新的圖標的時候医男,只需要在重新生成一次在線鏈接砸狞,把鏈接放入項目中 就可以使用新添加的圖標啦
缺點:沒網(wǎng)的時候不會顯示,以file協(xié)議打開也不會顯示镀梭。
二刀森、Home頁面
1. Home頁面----Header.vue組件
- 安裝stylus工具(寫css)
npm install stylus --save
npm install stylus-loader --save
npm run dev(重新啟動項目)
- 代碼優(yōu)化
公用樣式
- 把公用樣式放到一個文件里
本項目在assets下styles建了
varibles.styl
文件,存放公共樣式报账,比如全局背景色$bgColor = ##00bcd4
定義在這個文件里
- 使用
在要公用樣式的組件里面的style里面引入這個文件
@import '../../../assets/styles/varibles.styl'
研底,然后background: $bgColor
- 優(yōu)點:便于維護和修改
自定義路徑
在項目中,我們都知道src
可以用@
代替透罢,我們也可以自定義一些路徑
步驟:
在build->webpack.base.conf.js
找到resolve
配置項榜晦,可以自定義'@': resolve('src'), 'styles': resolve('src/assets/styles'),
現(xiàn)在上面那個引用就可以改成
@import '~styles/varibles.styl'
注意:在一個css中引用另一個css文件,如果想使用自定義路徑羽圃,路徑前面必須加上~
~ 是 stylus-loader 到東東乾胶,參考 https://github.com/shama/stylus-loader
~styles 表示相對 styles,然后我們在 webpack 配置了 styles 的 alias朽寞,就能找到了它的路徑了
2. Home頁面----Swiper.vue組件
- 建立一個
index-swiper
分支(在真實的開發(fā)環(huán)境下胚吁,每一個獨立功能都有一個獨立分支)
- 在githup下建立分支
- 在終端
git pull
---把線上的改變拉到本地git checkout 'index-swiper'
---切換分支- 新分支開發(fā)完成
git add . ; git commit -m ; git push (index-swiper)
- 將新分支的代碼合并到主分支
git checkout master
(切換到主分支)git merge index-swiper
(將新分支代碼合并到當(dāng)前分支)git push
(提交到線上倉庫)
- 使用
Vue-Awesome-Swiper
輪播插件
使用方法:參照官網(wǎng)或者 vue-awesome-swiper的使用以及API整理
問題:怎么防止頁面抖動?
描述:網(wǎng)速較慢時愁憔,輪播圖還沒加載出來腕扶,輪播圖所在位置高度為零,下面的元素占據(jù)了輪播圖的位置吨掌,等到輪播圖加載出來半抱,就會把其他元素擠下去脓恕,頁面抖動
注意:輪播圖中圖片也不要給固定長寬,要用百分比
解決辦法:
- 在輪播圖外面包裹一層固定寬高的盒子(不推薦)
更換圖片或者改變圖片大小的時候窿侈,頁面可能會發(fā)生錯亂
- 在輪播圖外包裹一層長寬比例以圖片一致的盒子
width: 100%;height: 33%vw
這種方法可以炼幔,但是兼容性不太好
- 在外面包裹一個盒子,盒子設(shè)置如下:(這是普遍的用法BFC)
.wrapper overflow hidden width 100% height 0 padding-bottom 26.67% background #eee
- 在vue項目組件中使用子組件(外部組件)史简,怎么去修改子組件的樣式乃秀?
- 去掉
scoped
,但會污染全局組件去掉
scoped
,style樣式就不再局限與本組件圆兵,變成全局樣式跺讯,自然會影響到子組件的樣式
- 混用本地和全局樣式
<style> /* 全局樣式 */ </style> <style scoped> /* 本地樣式 */ </style>
- 使用深度作用選擇器
如果你希望 scoped 樣式中的一個選擇器能夠作用得“更深”,例如影響子組件殉农,你可>以使用 >>> 操作符:<style scoped> .a >>> .b { /* ... */ } </style>
有些像 SASS 之類的預(yù)處理器無法正確解析
>>>
刀脏。這種情況下你可以用/deep/
操作符取而代之 —— 這是一個>>>
的別名,同樣可以正常工作超凳。
3. Home頁面----Icons.vue組件
- 輪播時分頁
這就需要兩次循環(huán)愈污,第一次循環(huán)把所有的頁找出來,然后在循環(huán)每一頁中的元素
4. Home頁面----Recommend.vue組件
- 省略號的使用
有時候一句話太長轮傍,我們又不想讓它換行暂雹,但是又得提醒用戶后面還有內(nèi)容,這就需要省略號啦
怎么添加创夜?
- 單行文本的溢出
overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
注意:有的時候在部分機型上沒有效果杭跪,那么就換成多行文本的溢出形式就行,把多行的行數(shù)調(diào)成1行
- 多行文本的溢出 (WebKit)
overflow hidden; text-overflow: ellipsis; display: -webkit-box; /*將對象作為彈性伸縮盒子模型顯示*/ -webkit-line-clamp: 1; /* 限制在一個塊元素顯示的文本的行數(shù) */ -webkit-box-orient: vertical; /* 垂直排列 */ word-break: break-all; /* 內(nèi)容自動換行 */
適用場景:這個屬性只合適WebKit瀏覽器或移動端(絕大部分是WebKit內(nèi)核的)瀏覽器
- 多行文本的溢出 (利用定位和偽類元素)
p{ position: relative; width:400px; line-height: 20px; max-height: 60px; overflow: hidden; } p::after{ content: "..."; position: absolute; bottom: 0; right: 0; padding-left: 40px; background: -webkit-linear-gradient(left, transparent, #fff 55%); background: -o-linear-gradient(right, transparent, #fff 55%); background: -moz-linear-gradient(right, transparent, #fff 55%); background: linear-gradient(to right, transparent, #fff 55%); }
適用場景:文字內(nèi)容較多挥下,確定文字內(nèi)容一定會超過容器的揍魂,那么選擇這種方式不錯桨醋。但文字未超出行的情況下也會出現(xiàn)省略號,可結(jié)合js優(yōu)化該方法棚瘟。 效果圖
注:
- 將height設(shè)置為line-height的整數(shù)倍,防止超出的文字露出喜最。
- 給p::after添加漸變背景可避免文字只顯示一半偎蘸。
- 由于ie6-7不顯示content內(nèi)容,所以要添加標簽兼容ie6-7(如:<span>…<span/>)瞬内;兼容ie8需要將::after替換成:after迷雪。
可以結(jié)合js代碼優(yōu)化:
$(function(){ //獲取文本的行高,并獲取文本的高度蜒程,假設(shè)我們規(guī)定的行數(shù)是五行蜂怎,那么對超>>>過行數(shù)的部分進行限制高度劲弦,并加上省略號 $('p').each(function(i, obj){ var lineHeight = parseInt($(this).css("line-height")); var height = parseInt($(this).height()); if((height / lineHeight) >3 ){ $(this).addClass("p-after") $(this).css("height","60px"); }else{ $(this).removeClass("p-after"); } }); })
5. 使用axios發(fā)送Ajax請求
-
npm install axios --save
; 在Home組件中引入import axios from 'axios'
- Home頁面共有5個組件都需要Ajax獲取數(shù)據(jù),如果每個組件都發(fā)一個請求赁严,那么就會降低頁面的性能扰柠。
解決辦法:可以在Home頁面請求數(shù)據(jù)一次,然后把請求的數(shù)據(jù)通過父子組件傳值(props)傳遞給子組件疼约。Ajax請求數(shù)據(jù)的操作本項目是在mounted鉤子函數(shù)中完成的卤档,其實在created中也可以,相當(dāng)于數(shù)據(jù)初始化程剥。 - 本地mock數(shù)據(jù)
注意:
- 本地mock的數(shù)據(jù)要寫在
static
目錄下劝枣,因為只有static目錄下的文件才可以被外部訪問到,其他目錄下的文件不能被訪問织鲸,訪問時會自動跳轉(zhuǎn)到首頁的- mock文件夾是本地數(shù)據(jù)舔腾,不想提交到線上,那么就在
.gitignore
中寫入
static/mock
- json文件內(nèi)的元素以
,
分割昙沦,不過最后一個元素不要加,
琢唾,容易解析錯誤
- 那么我們訪問數(shù)據(jù)時就可以
axios.get('/static/mock/index.json')
問題:我們是本地模擬數(shù)據(jù),上線前還要修改路徑盾饮,上線前修改代碼是有風(fēng)險的采桃,不推薦這么做,怎么辦丘损?
解決:
在config
目錄下index.js
文件中的proxyTable
中修改配置(這是由webpack-dev-server工具提供的)proxyTable: { '/api':{ target: 'http://localhost:8080',//訪問的服務(wù)器端口地址普办,默認的,后期如果修改服務(wù)器地址徘钥,直接改這個 pathRewrite: {//路徑替換 '^/api': '/static/mock' } } },
三衔蹲、City頁面
1. better-scroll 滾動組件的使用
- 安裝使用
npm install better-scroll --save
安裝
import BScroll from 'better-scroll'
在需要滾動的組件中引入使用 mounted () { this.scroll = new BScroll(this.$refs.wrapper) }
- html格式(需要包裹兩層)
<div class="wrapper"> <div class="content"> <div>...</div> <div>...</div> ...(需要滾動的元素) </div> </div>
注意:wrapper(父元素)的高度一定要小于content(子元素)的高度才會滾動,否則無法滾動
2. offsetTop的理解
注意:
offsetTop
的參考點是:與當(dāng)前元素相距最近的經(jīng)過定位(position不為static)的父級元素(offsetParent
)呈础,并不是我以前認為的body元素
具體情況如下:
- position為fixed時舆驶,offsetParent為null,offsettop的值和top相等而钞。此時元素是以視口來定位的沙廉。
- position非fixed,父級元素?zé)o定位(static)時臼节,offsetParent為body撬陵。
- position非fixed,父級元素有定位時网缝,offsetParent為最近的有定位的父級元素巨税。
- body元素,offsetParent為null粉臊,offsettop為0 草添。
3. 通過函數(shù)節(jié)流可以減少部分操作的執(zhí)行頻率,提高網(wǎng)頁的性能
以下代碼就是通過一個計時器進行節(jié)流扼仲,意思是若你剛開始進行這個操作的時候远寸,會延遲16ms在執(zhí)行促王;若是在16ms之間你有進行手指滾動操作,會把你當(dāng)前執(zhí)行的取消而晒,在開始執(zhí)行新的操作
if(this.touchStatus){
//通過添加計時器對函數(shù)進行節(jié)流蝇狼,提高性能
if(this.timer){
clearTimeout(this.timer)
}
this.timer = setTimeout(()=>{
//const startY = this.$refs['A'][0].offsetTop
const touchY = e.touches[0].clientY - 74
const index = Math.floor((touchY - this.startY) / 23)
if(index >= 0 && index <= this.letters.length){
this.bus.$emit('change',this.letters[index])
}
},16)
}
4. 移動端touch事件和鼠標事件
移動端的touch click事件的理解+點透
觸屏事件與鼠標事件
說明: 在很多情況下,觸摸事件和鼠標事件會同時觸發(fā)倡怎,目的是讓沒有對觸摸設(shè)備做優(yōu)化的代碼也可以在觸摸設(shè)備上正常運行迅耘。當(dāng)然,如果你使用了觸摸事件监署,那么可以通過
e.preventDefault()
來阻止鼠標事件被觸發(fā)
1. 事件說明
觸屏事件:
- touchstart 觸摸開始(手指放在觸摸屏上)
- touchmove 拖動(手指在觸摸屏上移動)
- touchend 觸摸結(jié)束(手指從觸摸屏上移開)
- touchcancel颤专,是在拖動中斷時候觸發(fā)。
鼠標事件:
- mouseover 鼠標進入
- mousemove 鼠標移動
- mousedown 鼠標按下觸發(fā)
- mouseup 鼠標抬起觸發(fā)
- click 鼠標點擊事件钠乏,包括mousedown和mouseup2個過程
觸發(fā)規(guī)則:
在觸屏操作后栖秕,手指提起的一剎那(即發(fā)生touchend后),系統(tǒng)會判斷接收到事件的element的內(nèi)容是否被改變:
- 如果內(nèi)容被改變晓避,會解析為touch事件簇捍,接下來的click事件都不會觸發(fā),
- 如果內(nèi)容沒有改變俏拱,則會解析為click事件暑塑,按照mousedown,mouseup锅必,click的順序觸發(fā)事件事格。
特別需要提到的是,在解析為click事件時搞隐,只有再觸發(fā)一個觸屏事件時驹愚,才會觸發(fā)上一個事件的mouseout事件。
通常click事件官網(wǎng)文檔是說會延時200~300ms,不過項目通過引入fastclick已經(jīng)解決
觸發(fā)順序:
- 移動端劣纲,點擊一下會觸發(fā):
touchstart->touchend->mouseover->mousedown-> mouseup->click;
- 移動端逢捺,滑動,觸發(fā):
touchstart->touchmove->touchend;
- pc端味廊,點擊:
mouseover->mousemove->mousedown-> mouseup->click;
- pc端蒸甜,移動:
mouseover->mousemove
執(zhí)行順序代碼演示:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#level0 {
/* width: 500px;
height: 500px; */
}
#level1-0 {
background: red;
width: 500px;
height: 500px;
}
#level1-1 {
background: green;
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div id="level0">
<div id="level1-0">
</div>
<div id="level1-1">
</div>
</div>
</body>
<script type="text/javascript">
var level10 = document.getElementById("level1-0");
level10.addEventListener('touchstart', function(e) {
console.log(1);
e.preventDefault()
});
level10.addEventListener('touchmove', function(e) {
console.log(2);
});
level10.addEventListener('touchend', function(e) {
console.log(3);
});
level10.onclick = function() {
console.log(5);
}
document.body.onclick = function() {
console.log('6');
}
</script>
</html>
//
在紅色區(qū)域點擊會出現(xiàn)什么效果呢棠耕? 出現(xiàn)的是 1 3 5 6余佛, 奇怪了 touchmove 為何不執(zhí)行,因為我們并沒有移動窍荧,也就是說辉巡,必須觸碰到屏幕上面,而且發(fā)生了移動動作蕊退,touchmove才執(zhí)行郊楣,現(xiàn)在我們觸碰到憔恳,而且手指稍微動一下,發(fā)現(xiàn)輸出的效果是净蚤, 1 2(+) 3, 其中touchmove 可能觸發(fā)多次钥组,又奇怪了, click為何不執(zhí)行今瀑, 因為 click執(zhí)行的條件是 點擊程梦, 而且不移動 所以一般情況下,我們可以理解成 touchmove和click是相斥的橘荠。
我們知道屿附,當(dāng)一個用戶在點擊屏幕的時候,系統(tǒng)會觸發(fā)touch事件和click事件哥童,touch事件優(yōu)先處理挺份,touch事件經(jīng)過 捕獲,處理, 冒泡 一系列流程處理完成后贮懈, 才回去觸發(fā)click事件
2. 點透事件
- 點透發(fā)生的條件匀泊,
- A 和 B不是后代繼承關(guān)系(如果是后代繼承關(guān)系的話,就直接是冒泡子類的話題了)
- A發(fā)生touch朵你, A touch后立即消失探赫, B事件綁定click
- A z-index大于B,即A顯示在B浮層之上
點透發(fā)生的理由:
當(dāng)手指觸摸到屏幕的時候撬呢,系統(tǒng)生成兩個事件伦吠,一個是touch 一個是click,touch先執(zhí)行魂拦,touch執(zhí)行完成后毛仪,A從文檔樹上面消失了,而且由于移動端click還有延遲200-300ms的關(guān)系芯勘,當(dāng)系統(tǒng)要觸發(fā)click的時候箱靴,發(fā)現(xiàn)在用戶點擊的位置上面,目前離用戶最近的元素是B荷愕,所以就直接把click事件作用在B元素上面了.
點透消除的方法:
e.preventDefault()
取消點擊事件level10.addEventListener('touchend', function(e) { e.preventDefault(); });
- 將
touch
操作延遲3s后關(guān)閉setTimeout(() => { level10.style.display = 'none'; }, 300);
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#level0 {
/* width: 500px;
height: 500px; */
position: relative;
}
#level1-0 {
position: absolute;
z-index: 1;
background: red;
width: 500px;
height: 500px;
}
#level1-1 {
background: green;
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div id="level0">
<div id="level1-0">
</div>
<div id="level1-1">
</div>
</div>
</body>
<script type="text/javascript">
var level10 = document.getElementById("level1-0");
var level11 = document.getElementById("level1-1");
level10.addEventListener('touchstart', function(e) {
level10.style.display = 'none';
e.preventDefault()
});
level11.onclick = function() {
console.log('level11莫名被點擊了');
}
</script>
</html>
5. vue中eventbus
使用的那些坑
參考:vue中使用eventbus踩過的坑
Vue的鉤子函數(shù)
聲明:在已經(jīng)用到vue-router
并且跨頁面(跨路由)傳值的情景衡怀,最好用vuex
,不要用eventbus
,因為在頁面跳轉(zhuǎn)時,新頁面組件的加載和舊頁面組件的銷毀順序不好把握安疗。
本項目我就是Home頁面和City頁面使用的bus總線的方式傳值抛杨,出現(xiàn)的問題和解決方案:
最初代碼:
這些代碼在同一個頁面沒有路由跳轉(zhuǎn)的組件間傳值是沒有問題的,但是荐类,不同頁面間傳值就有問題啦
//City頁面
methods: {
handleClickChange (city) {
this.bus.$emit('cityChange',city)
localStorage.city = city
this.currentCity = city
}
}
//Home頁面
methods: {
handleCity (city) {
this.city = city
console.log(city)
localStorage.city = city
}
},
mounted () {
this.bus.$on('cityChange',this.handleCity)
},
問題:
- 問什么第一次觸發(fā)Home頁面的on事件沒有執(zhí)行怖现?
- 后面再次觸發(fā)時Home頁面的on事件就開始執(zhí)行,并且on事件的執(zhí)行次數(shù)一次次的增加,就像沒有撤銷一樣屈嗤?
問題一解決:
因為
vue-router
切換時潘拨,先加載新組件,但是在新組件渲染完成還沒有掛載前饶号,銷毀舊組件铁追,在掛載新組件
切換時順序:
新組件: beforeCreate
新組件: created
新組件: beforeMount
舊組件: beforeDestroy
舊組件: destroy
新組件: mounted
解決方案:
在City頁面(舊組件)的$emit
事件寫在beforeDestroy,destroy
這兩個生命周期鉤子函數(shù)中,Home頁面(新組件)的$on
事件寫在beforeCreate,created,beforeMount
中茫船,才能夠正確傳值
改正后代碼:
//City頁面
destroyed () {
this.bus.$emit('cityChange',this.currentCity)
}
//Home頁面
methods: {
handleCity (city) {
this.city = city
console.log(city)
localStorage.city = city
}
},
created () {
this.bus.$on('cityChange',this.handleCity)
},
問題二解決:
兩個頁面都要寫上
beforeDestroy () {
bus.$off('get', this.handleCity)
},
就是說脂信,這個$on事件是不會自動清楚銷毀的,需要我們手動來銷毀
6. vuex的引入和使用
- 引入:
npm install vuex --save
安裝src->store->index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { city: '北京' }, actions: { cityChange (ctx,city) { // console.log(city) ctx.commit('change',city) } }, mutations: { change (state,city) { state.city = city } } }) ``
import store from './store'
在main.js
中引入透硝,然后注冊到實例對象中
- vuex中
mapState
的使用
mapState
的作用就是把vuex的state里面的數(shù)據(jù)映射到組件中狰闪,可以讓組件直接使用,不用再去store中調(diào)用濒生,減少代碼的復(fù)雜度
本項目的使用:
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['city']) //把state中的city屬性值映射到該組件計算屬性的city上
}
}
</script>
所以埋泵,現(xiàn)在代碼中
this.$store.state.city === this.city
- vuex中
mapMutations
的使用
import { mapState, mapMutations } from 'vuex'
methods: {
handleCityChange (city) {
// this.$store.commit('cityChange',city)
this.cityChange(city)
this.$router.push('/')
},
...mapMutations(['cityChange'])//把mutations中的cityChange方法映射到該組件cityChange方法上
},
7. localStorage的使用
本項目使用vuex之后可以改變狀態(tài),但是刷新之后數(shù)據(jù)又變成初始值罪治,怎樣才能讓數(shù)據(jù)保持改變后的狀態(tài)呢丽声?
使用localStorage
,將傳過來的值用localStorage
本地緩存起來觉义,與cookie的效果有點像雁社,但cookie是服務(wù)器端的
把上面的代碼這樣寫,就能夠保留狀態(tài)
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: localStorage.city || '北京'
},
mutations: {
cityChange (state,city) {
state.city = city
localStorage.city= city
}
}
})
注意:有些瀏覽器因為用戶關(guān)閉了本地存儲或使用了隱身模式晒骇,使用
localStorage
瀏覽器可能會拋出異常霉撵,解決辦法如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let defaultCity = '北京'
try {
if(localStorage.city){
defaultCity = localStorage.city
}
}catch (e) {}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
cityChange (state,city) {
state.city = city
try{
localStorage.city = city
}catch (e) {}
}
}
})
就是把所有的
localStorage.city
都放在try catch
中
但是try catch
是什么東西?
try catch
是一種錯誤處理機制洪囤,try...catch
語句將能引發(fā)錯誤的代碼放在try塊中徒坡,并且對應(yīng)一個響應(yīng),然后有異常被拋出
try語句允許我們定義在執(zhí)行時進行錯誤測試的代碼塊瘤缩。
catch 語句允許我們定義當(dāng) try 代碼塊發(fā)生錯誤時喇完,所執(zhí)行的代碼塊。
finally 語句在 try 和 catch 之后無論有無異常都會執(zhí)行剥啤。
注意: catch 和 finally 語句都是可選的锦溪,但你在使用 try 語句時必須至少使用一個。
8. keep-alive
的使用
頁面不使用keep-alive
府怯,頁面每次跳轉(zhuǎn)都要重新加載一次頁面刻诊,非常影響性能
如下圖所示:
解決辦法:在頁面顯示位置外面包裹一層
keep-alive
如下:
<keep-alive>
<router-view/>
</keep-alive>
效果如下圖:
頁面只加載了一次,通過keep-alive
富腊,把頁面保存到內(nèi)存里面坏逢,再次加載只需要去內(nèi)存里面去取就行
問題:如果說城市不變域帐,頁面確實不必再次加載赘被,但是如果城市改變了是整,頁面內(nèi)容不變就出問題啦,怎么解決呢民假?
keep-alive
提供了一個生命周期鉤子函數(shù)activated
,將這個鉤子寫在頁面里浮入,只要這個頁面顯示,不管是不是從內(nèi)存中取得羊异,它都會執(zhí)行事秀,所以我們可以在這個鉤子里進行當(dāng)前城市與上一個城市進行對比,若城市不同野舶,就發(fā)一個Ajax請求
代碼如下:
methods: {
getHomeInfo(){
axios.get('/api/index.json?city=' +this.city )//請求數(shù)據(jù)時帶上城市信息參數(shù)
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc(res){
console.log(res)
res = res.data
if(res.ret && res.data){
this.swiperList = res.data.swiperList
this.iconList = res.data.iconList
this.recommendList = res.data.recommendList
this.weekendList = res.data.weekendList
}
}
},
mounted () {
this.lastCity = this.city//將最初的城市信息保存下來
this.getHomeInfo()
},
activated () {
if(this.lastCity !== this.city){
this.lastCity = this.city//為上一個城市重新賦值
this.getHomeInfo()
}
}
四易迹、Detail頁面
1. Detail頁面---banner組件
1.1 <router-link>問題
router-link
在頁面中會被渲染成a
標簽,所以會改變router-link
內(nèi)包裹內(nèi)容的顏色平道,怎么解決睹欲?
- 直接給
router-link
包裹的內(nèi)容重新定義顏色,不要在使用繼承來的顏色- 直接用
router-link
替換掉內(nèi)部包裹的標簽一屋,用tag
指向原來的標簽
<ul>
<router-link to="/detail">
<li>
點我
</li>
</router-link>
</ul>
<ul>
<router-link to="/detail" tag='li'>
點我
</router-link>
</ul>
1.2 動態(tài)路由的使用
配置路由時:
{
path: '/detail/:id',
name: 'detail',
component: Detail
}
頁面中要把信息傳過來
<ul>
<router-link
:to='"/detail" + item.id'
tag='li'>
點我
</router-link>
</ul>
2. Detail頁面---公用Gallery組件
- 如果一個組件會被多個頁面或組件引用窘疮,就應(yīng)該設(shè)為公用組件,放在一起
本項目在src
目錄下設(shè)了common
目錄存放公用組件冀墨,而且在build
中webpack.base.conj.js
中resolve
里面修改了common
的引用路徑,以后可以直接引用common
'common': resolve('src/common'),
- 圖片畫廊公用組件其實就是一組圖片的拖動輪播組件闸衫,所以要使用
vue-awesome-swiper
插件來實現(xiàn),而該插件又是基于Swiper3
的诽嘉,所以插件的一些配置項可以在Swiper3
中找答案 - 一個注意點:如果要修改輪播圖的樣式或者位置蔚出,最好在外層在包裹一層div,不要直接對組件直接進行修改虫腋,要不很容易出現(xiàn)一些莫名其妙的錯誤
3. Detail頁面---Header組件
對全局事件的解綁:
本組件監(jiān)聽了一個全局滾動事件身冬,在任何一個頁面或組件滾動都會觸發(fā)本操作,這明顯是不利的岔乔,我們要的是只在本頁面滾動觸發(fā)本操作而已
methods: {
handleScroll () {
console.log('scroll')
const top = document.documentElement.scrollTop
if(top >= 45){
this.headerShow = true
const opacity = top / 120
this.styleOpacity = {opacity: opacity}
}else{
this.headerShow = false
}
}
},
activated () {
window.addEventListener('scroll',this.handleScroll)
},
所以我們要進行解綁:
利用
keep-alive
提供的另一個生命周期鉤子deactivated
,就是頁面消失的時候該函數(shù)執(zhí)行
deactivated () {
window.removeEventListener('scroll',this.handleScroll)
}
4. Detail頁面---List組件
遞歸組件的使用:我們經(jīng)乘煮荩看到的列表分為一級,二級等幾個級別的列表雏门,這就是遞歸組件做出來的嘿歌。
組件的名字(name)用處:
在遞歸組件中,遞歸組件要引用自己的時候茁影,組件的名字就是代表的這個組件宙帝,直接用名字就可以啦
list: [{
title: '成人票',
children: [{
title: '成人三館聯(lián)票',
children: [{
title: '成人三館聯(lián)票 - 清明上河園'
}]
},{
title: '成人五館聯(lián)票'
},]
},]
<div v-for="(item,index) of list" :key="index">
<div class="list-item">
<span class="mp-ticketype-ticket"></span>
{{item.title}}
</div>
<div v-if="item.children" class="item-children">
<detail-list :list="item.children"></detail-list>//遞歸組件使用自己,這是組件名字的用處
</div>
</div>
5. Detail頁面---ajax請求
- 請求時傳參
拼接的形式募闲,如果參數(shù)比較多步脓,就不適用啦
axios.get('/api/index.json?city=' +this.city )
第二種:
axios.get('/api/detail.json',{
params: {
id: this.$route.params.id
}
})
-
keep-alive
可以本地緩存,如果我們不想要一個頁面或組件使用緩存,那么可以:
<keep-alive exclude="Detail">
<router-view/>
</keep-alive>
現(xiàn)在name=Detail
的這個組件就不再使用緩存啦
注意:因為這個頁面不使用緩存啦靴患,相當(dāng)與
keep-alive
沒有作用啦仍侥,那么它提供的兩個鉤子activated,deactivated
就沒有作用啦,因為不使用緩存鸳君,頁面每次都要重新加載农渊,那么mounted,unmounted
這兩個鉤子開始每次執(zhí)行都要加載,可以代替上面的兩個鉤子
- 組件中的
name
有什么用
- 使用遞歸組件的時候可以通過名字對組件自身進行引用
- 對某個頁面取消緩存的時候也要用到
name
- chrome瀏覽器中的vue 插件中的dom結(jié)構(gòu)列表也要用到
name
- 拖動行為是相互影響的
比如第一個頁面你滾動到底部或颊,那么進入第二個頁面你會發(fā)現(xiàn)砸紊,頁面頁面滾動位置與第一個頁面位置同步,我們想要進入一個新頁面囱挑,頁面位置在頂端醉顽,該怎么辦?
在
vue-router
中的滾動行為中有一個scrollBehavior
平挑,在路由routes
下面加上這段代碼就可以解決問題徽鼎,使頁面切換的時候始終回到最頂部
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
5. Detail頁面---在banner
中加入FadeAnimation.vue
動畫組件
新建一個公用的FadeAnimation.vue
動畫組件
<template>
<transition>
<slot></slot>
</transition>
</template>
<script>
export default {
name: 'FadeAnimation'
}
</script>
<style lang="stylus" scoped>
.v-enter, .v-leave-to {
opacity: 0
}
.v-enter-active, .v-leave-active {
transition opacity 1s
}
</style>
使用:
<fade-animation>
<CommonGallary
:imgs = 'this.detailData.gallaryImgs'
v-show="showGallary"
@close="close"
></CommonGallary>
</fade-animation>
<script>
import FadeAnimation from 'common/fade/FadeAnimation'
export default {
name: 'DetailBanner',
components: {
FadeAnimation
},
}
</script>
五、項目的聯(lián)調(diào)弹惦、測試否淤、與發(fā)布上線
1. 項目的聯(lián)調(diào)
本次服務(wù)器是使用的小型本地PHP服務(wù)器
- 根據(jù)網(wǎng)上地址安裝XMAPP,https://www.apachefriends.org/zh_cn/download.html
- XMAPP默認的項目地址是放在 C:\xampp\htdocs 下棠隐,里面默認有很多個文件(可以都刪除掉)石抡,創(chuàng)建一個api文件夾,里面放\static\mock里的3個json文件(老師并沒有在視頻里演示創(chuàng)建這個文件夾)助泽。
- 瀏覽器訪問http://localhost/api/index.json 如果有json數(shù)據(jù)啰扛,就表示服務(wù)器搭建成功了。
- 按照視頻里的操作修改\config\index.js 文件嗡贺,修改代理接口
- 運行npm run dev隐解,然后訪問http://localhost:8080,數(shù)據(jù)就能正常訪問了诫睬。
proxyTable: {
'/api':{
target: 'http://localhost:8080',
pathRewrite: {
'^/api': '/static/mock'
}
}
},
//修改后
proxyTable: {
'/api':{
target: 'http://localhost:80',//這是本地服務(wù)器地址(:80可以省略煞茫,因為默認端口就是80)
//
},
本項目是前臺后臺數(shù)據(jù)都在本地的一個項目,較為簡單摄凡,服務(wù)器地址常見的內(nèi)網(wǎng)IP续徽,外網(wǎng)域名都可以
2. 項目的真機測試
- 獲取本機的IP地址,在終端中輸入
ipconfig
(windows);ifconfig
(ios) - 通過ip地址啟動項目亲澡,但是你會發(fā)現(xiàn)啟動不了钦扭,因為vue項目是通過
webpack dev server
啟動項目的,而webpack dev server
默認不支持IP地址訪問 - 修改
package.json
的dev
配置項床绪,就可以用IP地址來訪問啦
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
//修改后
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
- 讓手機和電腦在同一個局域網(wǎng)下(連的是同一個WiFi)客情,手機就可以通過IP地址訪問這個項目
- 手機上的bug1(手機拖動右側(cè)的字母表的時候其弊,整個屏幕跟著滑動)
解決:添加事件修飾符prevent
,阻止該事件的默認行為
@touchstart.prevent = 'handleTouchStart'
- 手機上的bug2
一些低版本的安卓機會出現(xiàn)白屏的問題
原因一:手機不支持promise
等一些ES6的語法
解決: 安裝一個包膀斋,這個包會自動判斷手機是否支持ES6的新特性梭伐,如不支持,會自動下載
npm install babel-polyfill --save
import 'babel-polyfill'
(在main.js中引入)
原因二:webpack-dev-server
的問題概页,打包上線后就會解決
3. 項目的打包上線
-
npm run build
生成一個dist目錄籽御,這個目錄的內(nèi)容是我們代碼的一個封裝和壓縮练慕,是我們要上線的內(nèi)容 - 把這個包交給后端惰匙,放到后端服務(wù)器的根路徑下
- 就可以直接訪問后端服務(wù)器的地址訪問網(wǎng)站啦
http://192.168.43.229/#/
- 如果說我們不想把打包的代碼直接扔到服務(wù)器的根目錄下,想放在根目錄的一個
project
目錄下铃将,該怎么做呢
- 首先项鬼,修改
config/index.js
下的build
配置項里的打包路徑,把'/'->'/project'
- 再次打包劲阎,運行
npm run build
- 可以把打包生成的dist目錄下的內(nèi)容放入服務(wù)器根目錄下的project目錄
- 訪問
http://192.168.43.229/project/#/
4. 異步組件實現(xiàn)按需加載
-
首先看一下打包生成的dist目錄
以map結(jié)尾的文件绘盟,是開始的時候調(diào)試的時候使用,意義并不是特別大
css文件悯仙,所有頁面用到的css文件全部打包到這里
manifest.js:webpack打包生成的所有配置文件
vendor.js:各個頁面龄毡,組件公用的代碼
app.js:各個頁面的業(yè)務(wù)邏輯代碼 -
我們?yōu)槭裁匆褂卯惒浇M件?
三個js文件锡垄,前兩個幾乎沒法修改
每次加載頁面沦零,就要把所有的業(yè)務(wù)邏輯代碼加載一下,若是項目比較大货岭,勢必會大大影響頁面的初始加載速度路操,若是app.js小于1mb以下,就沒有必要使用異步組件啦 - 異步組件不僅可以在路由中使用千贯,在頁面組件中中只要引入組件的地方都可以使用