2021-05-29

記一次實(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

?? }

}

vue官網(wǎng)偵聽(tīng)器

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

js indexOf()用法

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)。

vue官網(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))。

    感謝老師

    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
    【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
    平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

    相關(guān)閱讀更多精彩內(nèi)容

    友情鏈接更多精彩內(nèi)容