記一次實(shí)戰(zhàn)經(jīng)歷<去哪兒網(wǎng)> vue-cli 2
預(yù)備知識(shí)
1.Git命令:
git add .? 放到緩沖區(qū)
git commit -m 把緩沖區(qū)代碼提交到本地倉(cāng)庫(kù)中
git push 推到遠(yuǎn)程倉(cāng)庫(kù)
git checkout branchName 切換到某分支
git merge origin/index-swiper 將分支合并到主分支
2.在style中引入css文件
如果需要在style中引入其他的css 文件,則需要使用@import 'path'
如果要用到@(也就是src),則需要在@前面加~,也就是~@
3.起別名
在build/webpack.base.config.js中resolve的alias修改,修改以后需要重啟服務(wù)器
4.開(kāi)發(fā)者工具可以模擬3G網(wǎng)絡(luò)
network -> no throtting
5.寬高比一定
兩種辦法? 1.height:0? padding-bottom: 50%? 表示占寬度的50% 標(biāo)準(zhǔn)寫(xiě)法
width:100%? height:50%vw? 視口寬度的50%? 存在兼容性問(wèn)題
6.使用axios
【補(bǔ)充】:cdn引入axios <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
import axios from 'axios'
在聲明周期鉤子函數(shù)中調(diào)用函數(shù)
methods: {
getCityInfo() {
axios.get('/api/city.json')
.then(this.handleGetCityInfoSucc)
?? },
handleGetCityInfoSucc(res) {
res=res.data
if(res.ret&&res.data){
constdata=res.data
this.cities=data.cities
this.hotCities=data.hotCities
? ?? }
?? }
}
7.監(jiān)聽(tīng)器watch
watch和data、methods、components是并列的,對(duì)應(yīng)的是一個(gè)對(duì)象類型,對(duì)象中是函數(shù),這些函數(shù)用于監(jiān)聽(tīng)數(shù)據(jù)的改變。函數(shù)名就是變量名
watch:{
question([newValue,oldValue]){
//code
?? }
}
8.indexOf()
string.indexOf(searchvalue[,formindex])該方法用來(lái)判斷字符串中是否含有特定子字符串。第一個(gè)參數(shù)是要查找的內(nèi)容,第二個(gè)字符串是要開(kāi)始查找的索引號(hào),如果沒(méi)有第二個(gè)參數(shù)默認(rèn)從0開(kāi)始。如果沒(méi)有找到,返回-1
<scripttype="text/javascript">
varstr="Hello world!"
document.write(str.indexOf("Hello")+"<br />")
document.write(str.indexOf("World")+"<br />")
document.write(str.indexOf("world"))
</script>
以上代碼的輸出:
0
-1
6
9.vuex
需要進(jìn)一步學(xué)習(xí),了解的比較少
state mutations mapState...
10.遍歷對(duì)象和遍歷數(shù)組
用v-for遍歷數(shù)組,第一個(gè)參數(shù)是元素,第二個(gè)參數(shù)是索引。
遍歷對(duì)象中的對(duì)象,第一個(gè)是對(duì)象本身,第二個(gè)參數(shù)是對(duì)象名,用let in 參數(shù)是對(duì)象名
項(xiàng)目預(yù)熱
安裝node.js -> LTS? 長(zhǎng)期維護(hù)版,穩(wěn)定
github
安裝Vue CLI腳手架,創(chuàng)建項(xiàng)目 vue init webpack my-project
項(xiàng)目概述
項(xiàng)目結(jié)構(gòu)
糾正:static可以存放靜態(tài)文件,是外界可以直接訪問(wèn)的目錄。
重點(diǎn)介紹src目錄,我們寫(xiě)的代碼都保存在這個(gè)目錄下assets是一些共用的靜態(tài)資源,common放的是公共組件,pages放組件,router下面是路由配置文件,store下面是公共數(shù)據(jù)文件(vuex相關(guān)),App.vue是跟實(shí)例,main.js是整個(gè)項(xiàng)目的入口。
項(xiàng)目初始化
修改meta標(biāo)簽,禁止用戶通過(guò)手指縮放(不報(bào)錯(cuò)表明引用正確)
index.html
<metaname="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
引入reset.css(重置樣式表)? normalize修改程度比較小? reset.css對(duì)默認(rèn)樣式破壞性比較多main.js
引入border.css? 解決在某些手機(jī)上border寬度不一的情況
解決某些手機(jī)存在300毫秒延遲問(wèn)題
npm install fastclick --save? //安裝
import fastClick from 'fastclick' //導(dǎo)入
fastClick.attach(document.body)? //使用
刪除無(wú)用代碼,提交本地倉(cāng)庫(kù)
首頁(yè)開(kāi)發(fā)
header區(qū)域開(kāi)發(fā)
1.安裝stylus(可選)
npm install stylus --save
npm install stylus-loader --save
2.創(chuàng)建header組件
3.安裝less(好用)
npm install less less-loader --save-dev
修改build/webpack.base.config.js
{
test:/\.less$/i,
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
? ? ?? ],
? ?? }
報(bào)錯(cuò)是版本原因,要安裝低版本
npm uninstall less-loader? //安裝less-loader
npm install less-loader@4.1.0? //指定版本
解決header區(qū)域城市自動(dòng)切換到指定城市
在Home組件中創(chuàng)建一個(gè)變量lastCity表示最新當(dāng)前城市,當(dāng)路由跳轉(zhuǎn)到當(dāng)前頁(yè)面時(shí),讓lastCity等于vuex中的city,lastCIty在結(jié)構(gòu)中并沒(méi)有被用到,他的作用即使監(jiān)測(cè)當(dāng)前頁(yè)面被激活時(shí),檢查城市是否發(fā)生變化,從而決定是否重新發(fā)送請(qǐng)求。
在組件Header.vue中我們直接使用vuex中的city。
在computed中
computed: {
...mapState(['city'])
}
就可以直接在當(dāng)前頁(yè)面中使用this.city(vuex中的數(shù)據(jù))。
當(dāng)從其他頁(yè)面跳轉(zhuǎn)到當(dāng)前頁(yè)面時(shí),激發(fā)activated()鉤子函數(shù),檢查lastCity是否和vuex中的city一樣,如果不一樣就改變,并重新發(fā)送網(wǎng)絡(luò)請(qǐng)求。
activated() {
if(this.lastCity !== this.city){
this.lastCity = this.city
this.getHomeInfo()
}
}
methods: {
getHomeInfo() {
axios.get('./api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc(res) {
res = res.data
const data = res.data
if(res.ret && res.data){
this.swiperList = data.swiperList
this.iconList = data.iconList
this.recommendList = data.recommendList
this.weekendList = data.weekendList
}
}
}
由于本項(xiàng)目只含有北京的數(shù)據(jù),我們默認(rèn)請(qǐng)求數(shù)據(jù)的時(shí)候不傳參。
Q:說(shuō)了這么多?我們?cè)谑裁磿r(shí)候需要用到上述功能呢?
A:當(dāng)我們點(diǎn)擊城市選擇頁(yè)面城市的時(shí)候
@click='handleCityClick(innerItem.name)
methods: {
handleCityClick(city){
this.$store.commit('changeCity',city)
this.$router.push('/')
}
}
觸發(fā)mutations.js中的changeCity()函數(shù),city作為第二個(gè)參數(shù)
mutations.js:
localStorage是為了解決關(guān)閉頁(yè)面以后再次打開(kāi)頁(yè)面的問(wèn)題,這樣可以直接打開(kāi)直接的頁(yè)面。
changeCity(state, city) {
state.city = city;
try {
localStorage.city = city;
} catch (e) {}
}
state.js
let defaultCity = "北京";
try {
if (localStorage.city) {
defaultCity = localStorage.city;
}
} catch (e) {}
export default {
city: defaultCity
};
首頁(yè)輪播圖開(kāi)發(fā)
線上建立分支index-swiper
拉取到本地? git pull
進(jìn)入分支? git checkout index-swiper
安裝swiper npm install vue-awesome-swiper@2.6.7 --save這里使用v2.6.7老版本,可以在GitHub上找到具體使用的代碼
輪播圖的核心結(jié)構(gòu)就是swiper和swiper-slide
解決圖片區(qū)域抖動(dòng)問(wèn)題
給圖片區(qū)域高度寫(xiě)死
vue中css穿透
在使用vue-awesome-swiper時(shí),swiper組件并不屬于當(dāng)前組件,而我們又寫(xiě)了scoped,因此需要用到css穿透。
less和sass無(wú)法識(shí)別>>>,因此要用到/deep/或者::v-deep? stylus可以識(shí)別>>>
.swiper /deep/ .swiper-pagination-bullet-active
//.wrapper >>> .swiper-pagination-bullet-active
{
background-color: #fff !important;
}
從百度到CSDN,最后到Vue.js官網(wǎng)。
不得不說(shuō),官方文檔
實(shí)現(xiàn)文字省略功能
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
實(shí)現(xiàn)圖標(biāo)分頁(yè)效果
我們可以再次借助輪播圖,這次關(guān)閉自動(dòng)播放功能。
因?yàn)槲覀兪莿?dòng)態(tài)渲染從后端請(qǐng)求過(guò)來(lái)的數(shù)據(jù),因此要實(shí)現(xiàn)自動(dòng)分頁(yè),要用到二維數(shù)組。
computed: {
pages() {
const pages = []
this.list.forEach((item, index) => {
const page = Math.floor(index / 8)
if(!pages[page])
pages[page] = []
pages[page].push(item)? //二維數(shù)組,自動(dòng)分頁(yè)
})
return pages
}
}
放到計(jì)算屬性中,計(jì)算屬性的成員都是函數(shù)(返回一個(gè)數(shù)據(jù)),但是在使用的時(shí)候直接我們當(dāng)成屬性來(lái)使用,因此命名一般是名詞。
動(dòng)態(tài)渲染
v-for="item of page" :key="item.id">
:src="item.imgUrl"
/>
{{item.desc}}
城市頁(yè)面開(kāi)發(fā)
創(chuàng)建分支city-router
在路由文件router/index.js中配置路由
router-link把內(nèi)容包裹起來(lái),router-link實(shí)質(zhì)就是a標(biāo)簽。? to="path"表示跳轉(zhuǎn)的路徑
a標(biāo)簽優(yōu)先級(jí)高,因?yàn)闉g覽器對(duì)a標(biāo)簽有默認(rèn)樣式,所以a標(biāo)簽不會(huì)繼承父類的樣式
頭部開(kāi)發(fā)
position: absolute;
top: 0;
left: 0;
頭部搜索框
input輸入框雙向綁定keyword來(lái)決定是否顯示搜索結(jié)果,通過(guò)搜索結(jié)果的長(zhǎng)度來(lái)決定沒(méi)有匹配到數(shù)據(jù)是否顯示。計(jì)算屬性:hasNoData
局部導(dǎo)入Bscroll
import Bscroll from 'better-scroll'
mounted() {
this.scroll = new Bscroll(this.$refs.search)
}
通過(guò)ref來(lái)綁定DOM
樣式
position: absolute;
top: 1.58rem;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
搜索結(jié)果的算法(用到了節(jié)流)
watch: {
keyword() {
if(this.timer){
clearTimeout(this.timer)
}
if(!this.keyword){
this.list = []
return
} //這里解決的是刪除搜索框中的內(nèi)容以后,使搜索結(jié)果消失
this.timer = setTimeout(() => {
const result = []
for (let i in this.cities){
//從A到Z,再逐個(gè)遍歷
this.cities[i].forEach( value => {
//英文名和漢字都檢查,只要有一個(gè)符合就加入result
if(value.spell.indexOf(this.keyword) > -1 ||
value.name.indexOf(this.keyword) > -1){
result.push(value)
}
});
}
this.list = result
},100)
}
}
列表布局
分支city-list
最外層容器的樣式:top,left,right,bottom都為0? overflow:hidden
better-scroll
npm install better-scroll --save
按照固定的結(jié)構(gòu)來(lái)書(shū)寫(xiě)
用ref來(lái)獲取DOM
import Bscroll from 'better-scroll'
this.scroll = new Bscroll(this.$refs.wrapper)
頁(yè)面可以絲滑地滾動(dòng)
頁(yè)面的動(dòng)態(tài)渲染
分支city-ajax
兄弟組件間聯(lián)動(dòng)
點(diǎn)擊Alphabet組件中的字母,list組件通過(guò)事件對(duì)象將字母?jìng)鹘o父組件city
handleLetterClick(e) {
//e是事件對(duì)象
this.$emit('change',e.target.innerText)
}
city再傳給list,LIst組件定位到對(duì)應(yīng)的DOM
利用better-scroll的功能自動(dòng)滾到某個(gè)區(qū)域中,通過(guò)href獲取dom
watch: {
letter() {
if (this.letter) {
const element = this.$refs[this.letter][0]? //為什么獲取到的是數(shù)組?因?yàn)閞ef為該字母的可能有很多
//如果屬性名是一個(gè)變量,可以這樣寫(xiě)obj[variable]
this.scroll.scrollToElement(element);
//定位到該DOM
}
},
}
實(shí)現(xiàn)字母?jìng)?cè)邊欄連續(xù)觸摸時(shí)滾動(dòng)到對(duì)應(yīng)區(qū)域
給字母綁定觸摸事件
v-for="item of letters"
:key="item"
:ref="item"
@touchstart.prevent = 'handleTouchStart'
@touchmove = 'handleTouchMove'
@touchend = 'handleTouchEnd'
@click = handleLetterClick>
{{item}}
methods: {
handleLetterClick(e) {
//e是事件對(duì)象
this.$emit('change',e.target.innerText)
},
handleTouchStart(){
this.touchStatus = true
},
handleTouchMove(e){
if(this.touchStatus){
if(this.timer){
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
console.log(e.touches[0].clientY);
const touchY = e.touches[0].clientY - 79;? ? //touch到的字母距離當(dāng)前盒子頂部的距離
const index = Math.floor((touchY - this.startY)/20);
if(index >= 0 && index < this.letters.length){
this.$emit('change',this.letters[index]);
}
},16)
}
},
handleTouchEnd(){
this.touchStatus = false
}
}
性能優(yōu)化
startY放到updated()鉤子函數(shù)中
當(dāng)頁(yè)面數(shù)據(jù)更新完成
節(jié)流
添加定時(shí)器
詳情頁(yè)面開(kāi)發(fā)
實(shí)現(xiàn)點(diǎn)擊banner畫(huà)廊出現(xiàn),點(diǎn)擊畫(huà)廊banner出現(xiàn)
畫(huà)廊作為banner的一個(gè)組件,點(diǎn)擊banner時(shí)showGalleay變?yōu)閠rue,點(diǎn)擊畫(huà)廊時(shí)觸發(fā)自定義事件改變showGallary的值。
為banner添加底部陰影
漸變
background-image: linear-gradient(top,rgba(0,0,0,0),rgba(0,0.0,.8));
Header漸變
通過(guò)動(dòng)態(tài)綁定style實(shí)現(xiàn)漸變
class="header-fixed"
v-show="!showAbs"
:style="opacityStyle">
景點(diǎn)詳情
methods: {
handleScroll() {
const top = document.documentElement.scrollTop
if(top > 60){
let opacity = top / 140
opacity = opacity > 1 ? 1 : opacity
this.opacityStyle = {
opacity
}
this.showAbs = false
}
else this.showAbs = true
}
},
// activated() {
//? window.addEventListener('scroll',this.handleScroll)
// },
// deactivated() {
//? window.removeEventListener('scroll',this.handleScroll)
// }
mounted () {
window.addEventListener('scroll', this.handleScroll)
},
unmounted () {
window.removeEventListener('scroll', this.handleScroll)
}
}
開(kāi)啟全局監(jiān)聽(tīng)以后,同時(shí)要開(kāi)啟unmounted,否則會(huì)在其他頁(yè)面一直保持監(jiān)聽(tīng)。
疑點(diǎn):為什么activated不行
結(jié)語(yǔ):這個(gè)項(xiàng)目是我完整做的第一個(gè)Vue項(xiàng)目,做下來(lái)真的收獲很大,對(duì)一個(gè)完整的項(xiàng)目有了大概的了解。
項(xiàng)目使用vue + vue-router + vuex全家桶,涉及到邏輯方面的內(nèi)容也有很多。包括但不限于使用vue,還有vue-cli,Git,其他插件的使用,接觸到以前聽(tīng)說(shuō)過(guò)但是不知道是什么的名詞(例如節(jié)流和防抖),在遇到不懂的地方的時(shí)候通過(guò)各個(gè)社區(qū)尋求答案,學(xué)會(huì)使用專業(yè)文檔(利于vue官網(wǎng))。
感謝老師