去哪了網(wǎng)項目總結(jié)(移動端)

一、啟動項目

1. 安裝腳手架和依賴

首先保證你的電腦上有node和npm,版本越新越好

  1. npm install -g vue-cli
    全局安裝腳手架工具,安裝的時候可以指定版本
  2. vue init webpack myProject
    用webpack工具初始化vue項目,myProject是項目名稱,可以自己命名
  3. cd myProject
    進(jìn)入創(chuàng)建的vue項目目錄
  4. npm install
    安裝項目所需要的依賴,也就是安裝一些必要的插件
  5. npm run dev
    開始運行項目

2. 項目代碼介紹

  1. README.md----項目的介紹說明文件
  2. package.json----一些第三方模塊的依賴
  3. package-lock.json----確定第三方包的版本,保證團(tuán)隊編程的統(tǒng)一
  4. LICENSE----開源協(xié)議的說明(就是你的代碼別人可以怎么用,是否可用于商業(yè)化等)
  5. index.html----首頁的模板文件
  6. .postcssrc.js----對postcss的一個配置文件,postcss會處理我們的css
  7. .gitignore----把不想提交到線上的文件放在這里
  8. eslintrc.js----寫代碼的規(guī)范
  9. .editorconfig----配置了一些編輯器的語法,可以自行添加修改
  10. .babelrc----語法解析器,把我們寫的vue單文件組件寫法的代碼解析成瀏覽器能夠識別的代碼
  11. static----靜態(tài)資源(靜態(tài)圖片或者我們模擬的json數(shù)據(jù))
  12. node_modules----我們依賴的第三方包
  13. src---我們項目的源代碼
  14. config----我們項目的配置文件

index.js 基礎(chǔ)的配置信息
dev.nav.js 開發(fā)時的配置信息
prod.nev.js 線上的配置信息

  1. 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)點:

  1. 首屏?xí)r間快(請求了第一個HTML頁面,就立即展示)
  2. 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. 項目代碼初始化(移動端)

  1. 設(shè)置meta標(biāo)簽
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
  2. 引入 reset.css---把不同手機(jī)的瀏覽器的初始化樣式做一個統(tǒng)一
  3. 引入border.css---解決移動端1像素邊框的問題(因為手機(jī)分辨率不同)
  4. 引入fastclick庫---解決某些機(jī)型點擊后延遲300ms的問題。

    怎么引入?

    1. npm install fastclick --save(save是指不管開發(fā)和線上都使用)
    2. main.js中引入,import fastClick from "fastclick",然后
      fastClick.attach(document.body),綁定到body上
  5. iconfont矢量圖標(biāo)庫建立一個圖標(biāo)倉庫

使用方法:

  • 本地引用:
  1. 打開官網(wǎng)->圖標(biāo)管理->我的項目->新建項目->選擇所需圖標(biāo)到購物車->添加至新建項目
  2. 將選擇好的圖標(biāo)倉庫下載到本地
  3. 把所有的字體文件(以svg,eot,ttf,woff等結(jié)尾的文件)和iconfont.css文件拿出來,其他的文件沒用
  4. 把所有的字體文件放在一起iconfont文件夾(本項目將iconfont文件夾放在了assets里面styles文件夾下)
  5. 修改iconfont.css中的字體路徑
  6. main.js中全局引入iconfont.css文件或者在需要字體圖標(biāo)的文件里局部引入iconfont.css文件
  7. 在官網(wǎng)字體所在倉庫里把iconfont字體的地址粘貼到代碼對應(yīng)位置,OK

優(yōu)點:在沒有網(wǎng)絡(luò)的時候,字體圖標(biāo)可以正常顯示
缺點:如果想要在倉庫里新加圖標(biāo)應(yīng)用到項目,那么必須重新引入

  • 在線引用:
  1. 在字體倉庫下有生成在線鏈接,點擊,生成在線鏈接,放入項目的css文件夾下面,不要忘了字體的樣式也要引入
  2. 在官網(wǎng)字體所在倉庫里把iconfont字體的地址粘貼到代碼對應(yīng)位置,OK

優(yōu)點:添加新的圖標(biāo)的時候,只需要在重新生成一次在線鏈接,把鏈接放入項目中 就可以使用新添加的圖標(biāo)啦
缺點:沒網(wǎng)的時候不會顯示,以file協(xié)議打開也不會顯示。

二、Home頁面

1. Home頁面----Header.vue組件

  1. 安裝stylus工具(寫css)

npm install stylus --save
npm install stylus-loader --save
npm run dev(重新啟動項目)

  1. 代碼優(yōu)化

公用樣式

  1. 把公用樣式放到一個文件里

本項目在assets下styles建了varibles.styl文件,存放公共樣式,比如全局背景色$bgColor = ##00bcd4定義在這個文件里

  1. 使用

在要公用樣式的組件里面的style里面引入這個文件@import '../../../assets/styles/varibles.styl',然后background: $bgColor

  1. 優(yōu)點:便于維護(hù)和修改

自定義路徑
在項目中,我們都知道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組件

  1. 建立一個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(提交到線上倉庫)
  1. 使用 Vue-Awesome-Swiper輪播插件
    使用方法:參照官網(wǎng)或者 vue-awesome-swiper的使用以及API整理

問題:怎么防止頁面抖動?
描述:網(wǎng)速較慢時,輪播圖還沒加載出來,輪播圖所在位置高度為零,下面的元素占據(jù)了輪播圖的位置,等到輪播圖加載出來,就會把其他元素擠下去,頁面抖動
注意:輪播圖中圖片也不要給固定長寬,要用百分比
解決辦法:

  1. 在輪播圖外面包裹一層固定寬高的盒子(不推薦)

更換圖片或者改變圖片大小的時候,頁面可能會發(fā)生錯亂

  1. 在輪播圖外包裹一層長寬比例以圖片一致的盒子
    width: 100%;height: 33%vw

這種方法可以,但是兼容性不太好

  1. 在外面包裹一個盒子,盒子設(shè)置如下:(這是普遍的用法BFC)
.wrapper
     overflow hidden
     width 100%
     height 0
     padding-bottom 26.67%
     background #eee
  1. 在vue項目組件中使用子組件(外部組件),怎么去修改子組件的樣式?
  1. 去掉scoped,但會污染全局組件

去掉scoped,style樣式就不再局限與本組件,變成全局樣式,自然會影響到子組件的樣式

  1. 混用本地和全局樣式
<style>
/* 全局樣式 */
</style>
<style scoped>
/* 本地樣式 */
</style>
  1. 使用深度作用選擇器
    如果你希望 scoped 樣式中的一個選擇器能夠作用得“更深”,例如影響子組件,你可>以使用 >>> 操作符:
<style scoped>
.a >>> .b {
 /* ... */
}
</style>

有些像 SASS 之類的預(yù)處理器無法正確解析 >>>。這種情況下你可以用 /deep/操作符取而代之 —— 這是一個 >>> 的別名,同樣可以正常工作。

3. Home頁面----Icons.vue組件

  1. 輪播時分頁

這就需要兩次循環(huán),第一次循環(huán)把所有的頁找出來,然后在循環(huán)每一頁中的元素

4. Home頁面----Recommend.vue組件

  1. 省略號的使用

有時候一句話太長,我們又不想讓它換行,但是又得提醒用戶后面還有內(nèi)容,這就需要省略號啦
怎么添加?

  1. 單行文本的溢出
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;

注意:有的時候在部分機(jī)型上沒有效果,那么就換成多行文本的溢出形式就行,把多行的行數(shù)調(diào)成1行

  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)核的)瀏覽器

  1. 多行文本的溢出 (利用定位和偽類元素)
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)容,所以要添加標(biāo)簽兼容ie6-7(如:<span>…<span/>);兼容ie8需要將::after替換成:after。

可以結(jié)合js代碼優(yōu)化:

$(function(){
//獲取文本的行高,并獲取文本的高度,假設(shè)我們規(guī)定的行數(shù)是五行,那么對超>>>過行數(shù)的部分進(jìn)行限制高度,并加上省略號
  $('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請求

  1. npm install axios --save; 在Home組件中引入import axios from 'axios'
  2. Home頁面共有5個組件都需要Ajax獲取數(shù)據(jù),如果每個組件都發(fā)一個請求,那么就會降低頁面的性能。
    解決辦法:可以在Home頁面請求數(shù)據(jù)一次,然后把請求的數(shù)據(jù)通過父子組件傳值(props)傳遞給子組件。Ajax請求數(shù)據(jù)的操作本項目是在mounted鉤子函數(shù)中完成的,其實在created中也可以,相當(dāng)于數(shù)據(jù)初始化。
  3. 本地mock數(shù)據(jù)

注意:

  • 本地mock的數(shù)據(jù)要寫在static目錄下,因為只有static目錄下的文件才可以被外部訪問到,其他目錄下的文件不能被訪問,訪問時會自動跳轉(zhuǎn)到首頁的
  • mock文件夾是本地數(shù)據(jù),不想提交到線上,那么就在.gitignore中寫入
    static/mock
  • json文件內(nèi)的元素以,分割,不過最后一個元素不要加,,容易解析錯誤
  1. 那么我們訪問數(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ù)器端口地址,默認(rèn)的,后期如果修改服務(wù)器地址,直接改這個
       pathRewrite: {//路徑替換
         '^/api': '/static/mock'
       }
     }
   },

三、City頁面

1. better-scroll 滾動組件的使用

當(dāng) better-scroll 遇見 Vue

  1. 安裝使用

npm install better-scroll --save安裝
import BScroll from 'better-scroll'在需要滾動的組件中引入

使用
mounted () {
    this.scroll = new BScroll(this.$refs.wrapper)
}
  1. html格式(需要包裹兩層)
<div class="wrapper">
 <div class="content">
   <div>...</div>
   <div>...</div>
     ...(需要滾動的元素)
 </div>
</div>

注意:wrapper(父元素)的高度一定要小于content(子元素)的高度才會滾動,否則無法滾動


image.png

2. offsetTop的理解

注意:

  • offsetTop的參考點是:與當(dāng)前元素相距最近的經(jīng)過定位(position不為static)的父級元素(offsetParent),并不是我以前認(rèn)為的body元素
    具體情況如下:
  1. position為fixed時,offsetParent為null,offsettop的值和top相等。此時元素是以視口來定位的。
  2. position非fixed,父級元素?zé)o定位(static)時,offsetParent為body。
  3. position非fixed,父級元素有定位時,offsetParent為最近的有定位的父級元素。
  4. body元素,offsetParent為null,offsettop為0 。

3. 通過函數(shù)節(jié)流可以減少部分操作的執(zhí)行頻率,提高網(wǎng)頁的性能

以下代碼就是通過一個計時器進(jìn)行節(jié)流,意思是若你剛開始進(jìn)行這個操作的時候,會延遲16ms在執(zhí)行;若是在16ms之間你有進(jìn)行手指滾動操作,會把你當(dāng)前執(zhí)行的取消,在開始執(zhí)行新的操作

 if(this.touchStatus){
                //通過添加計時器對函數(shù)進(jìn)行節(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事件和鼠標(biāo)事件

移動端的touch click事件的理解+點透
觸屏事件與鼠標(biāo)事件

說明: 在很多情況下,觸摸事件和鼠標(biāo)事件會同時觸發(fā),目的是讓沒有對觸摸設(shè)備做優(yōu)化的代碼也可以在觸摸設(shè)備上正常運行。當(dāng)然,如果你使用了觸摸事件,那么可以通過e.preventDefault()來阻止鼠標(biāo)事件被觸發(fā)

1. 事件說明

觸屏事件:

  • touchstart 觸摸開始(手指放在觸摸屏上)
  • touchmove 拖動(手指在觸摸屏上移動)
  • touchend 觸摸結(jié)束(手指從觸摸屏上移開)
  • touchcancel,是在拖動中斷時候觸發(fā)。

鼠標(biāo)事件:

  • mouseover 鼠標(biāo)進(jìn)入
  • mousemove 鼠標(biāo)移動
  • mousedown 鼠標(biāo)按下觸發(fā)
  • mouseup 鼠標(biāo)抬起觸發(fā)
  • click 鼠標(biāo)點擊事件,包括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元素上面了.

點透消除的方法:

  1. e.preventDefault()取消點擊事件
level10.addEventListener('touchend', function(e) {
   e.preventDefault();
});
  1. 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)
},

問題:

  1. 問什么第一次觸發(fā)Home頁面的on事件沒有執(zhí)行?
  2. 后面再次觸發(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的引入和使用

  1. 引入:
  • 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中引入,然后注冊到實例對象中
  1. 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

  1. 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是一種錯誤處理機(jī)制,try...catch語句將能引發(fā)錯誤的代碼放在try塊中,并且對應(yīng)一個響應(yīng),然后有異常被拋出
try語句允許我們定義在執(zhí)行時進(jìn)行錯誤測試的代碼塊。
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)存里面去取就行

laalllll.gif

問題:如果說城市不變,頁面確實不必再次加載,但是如果城市改變了,頁面內(nèi)容不變就出問題啦,怎么解決呢?
keep-alive提供了一個生命周期鉤子函數(shù)activated,將這個鉤子寫在頁面里,只要這個頁面顯示,不管是不是從內(nèi)存中取得,它都會執(zhí)行,所以我們可以在這個鉤子里進(jìn)行當(dāng)前城市與上一個城市進(jìn)行對比,若城市不同,就發(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標(biāo)簽,所以會改變router-link內(nèi)包裹內(nèi)容的顏色,怎么解決?

  1. 直接給router-link包裹的內(nèi)容重新定義顏色,不要在使用繼承來的顏色
  2. 直接用router-link替換掉內(nèi)部包裹的標(biāo)簽,用tag指向原來的標(biāo)簽
        <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目錄存放公用組件,而且在buildwebpack.base.conj.jsresolve里面修改了common的引用路徑,以后可以直接引用common
    'common': resolve('src/common'),
  • 圖片畫廊公用組件其實就是一組圖片的拖動輪播組件,所以要使用vue-awesome-swiper插件來實現(xiàn),而該插件又是基于Swiper3的,所以插件的一些配置項可以在Swiper3中找答案
  • 一個注意點:如果要修改輪播圖的樣式或者位置,最好在外層在包裹一層div,不要直接對組件直接進(jìn)行修改,要不很容易出現(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)
    },

1234.gif

所以我們要進(jìn)行解綁:
利用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請求

  1. 請求時傳參
    拼接的形式,如果參數(shù)比較多,就不適用啦
axios.get('/api/index.json?city=' +this.city )

第二種:

 axios.get('/api/detail.json',{
           params: {
           id: this.$route.params.id
      }
 })
  1. keep-alive可以本地緩存,如果我們不想要一個頁面或組件使用緩存,那么可以:
<keep-alive exclude="Detail">
   <router-view/>
</keep-alive>

現(xiàn)在name=Detail的這個組件就不再使用緩存啦

注意:因為這個頁面不使用緩存啦,相當(dāng)與keep-alive沒有作用啦,那么它提供的兩個鉤子activated,deactivated就沒有作用啦,因為不使用緩存,頁面每次都要重新加載,那么mounted,unmounted這兩個鉤子開始每次執(zhí)行都要加載,可以代替上面的兩個鉤子

  1. 組件中的name有什么用
  1. 使用遞歸組件的時候可以通過名字對組件自身進(jìn)行引用
  2. 對某個頁面取消緩存的時候也要用到name
  3. chrome瀏覽器中的vue 插件中的dom結(jié)構(gòu)列表也要用到name
    image.png
  1. 拖動行為是相互影響的

比如第一個頁面你滾動到底部,那么進(jìn)入第二個頁面你會發(fā)現(xiàn),頁面頁面滾動位置與第一個頁面位置同步,我們想要進(jì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ù)器

  1. 根據(jù)網(wǎng)上地址安裝XMAPP,https://www.apachefriends.org/zh_cn/download.html
  2. XMAPP默認(rèn)的項目地址是放在 C:\xampp\htdocs 下,里面默認(rèn)有很多個文件(可以都刪除掉),創(chuàng)建一個api文件夾,里面放\static\mock里的3個json文件(老師并沒有在視頻里演示創(chuàng)建這個文件夾)。
  3. 瀏覽器訪問http://localhost/api/index.json 如果有json數(shù)據(jù),就表示服務(wù)器搭建成功了。
  4. 按照視頻里的操作修改\config\index.js 文件,修改代理接口
  5. 運行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可以省略,因為默認(rèn)端口就是80)
//
    },

本項目是前臺后臺數(shù)據(jù)都在本地的一個項目,較為簡單,服務(wù)器地址常見的內(nèi)網(wǎng)IP,外網(wǎng)域名都可以

2. 項目的真機(jī)測試

  1. 獲取本機(jī)的IP地址,在終端中輸入
    ipconfig(windows); ifconfig(ios)
  2. 通過ip地址啟動項目,但是你會發(fā)現(xiàn)啟動不了,因為vue項目是通過webpack dev server啟動項目的,而webpack dev server默認(rèn)不支持IP地址訪問
  3. 修改package.jsondev配置項,就可以用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",
  1. 讓手機(jī)和電腦在同一個局域網(wǎng)下(連的是同一個WiFi),手機(jī)就可以通過IP地址訪問這個項目
  2. 手機(jī)上的bug1(手機(jī)拖動右側(cè)的字母表的時候,整個屏幕跟著滑動)
    image.png

    解決:添加事件修飾符prevent,阻止該事件的默認(rèn)行為
    @touchstart.prevent = 'handleTouchStart'
  3. 手機(jī)上的bug2
    一些低版本的安卓機(jī)會出現(xiàn)白屏的問題
    原因一:手機(jī)不支持promise等一些ES6的語法

解決: 安裝一個包,這個包會自動判斷手機(jī)是否支持ES6的新特性,如不支持,會自動下載
npm install babel-polyfill --save
import 'babel-polyfill'(在main.js中引入)

原因二:webpack-dev-server的問題,打包上線后就會解決

3. 項目的打包上線

  1. npm run build生成一個dist目錄,這個目錄的內(nèi)容是我們代碼的一個封裝和壓縮,是我們要上線的內(nèi)容
  2. 把這個包交給后端,放到后端服務(wù)器的根路徑下
  3. 就可以直接訪問后端服務(wù)器的地址訪問網(wǎng)站啦 http://192.168.43.229/#/
  4. 如果說我們不想把打包的代碼直接扔到服務(wù)器的根目錄下,想放在根目錄的一個project目錄下,該怎么做呢
  • 首先,修改config/index.js下的build配置項里的打包路徑,把'/'->'/project'
  • 再次打包,運行npm run build
  • 可以把打包生成的dist目錄下的內(nèi)容放入服務(wù)器根目錄下的project目錄
  • 訪問http://192.168.43.229/project/#/

4. 異步組件實現(xiàn)按需加載

  1. 首先看一下打包生成的dist目錄


    image.png

    以map結(jié)尾的文件,是開始的時候調(diào)試的時候使用,意義并不是特別大
    css文件,所有頁面用到的css文件全部打包到這里
    manifest.js:webpack打包生成的所有配置文件
    vendor.js:各個頁面,組件公用的代碼
    app.js:各個頁面的業(yè)務(wù)邏輯代碼

  2. 我們?yōu)槭裁匆褂卯惒浇M件?


    image.png

    三個js文件,前兩個幾乎沒法修改
    每次加載頁面,就要把所有的業(yè)務(wù)邏輯代碼加載一下,若是項目比較大,勢必會大大影響頁面的初始加載速度,若是app.js小于1mb以下,就沒有必要使用異步組件啦

  3. 異步組件不僅可以在路由中使用,在頁面組件中中只要引入組件的地方都可以使用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • vue概述 在官方文檔中,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,624評論 0 25
  • 基于Vue的一些資料 內(nèi)容 UI組件 開發(fā)框架 實用庫 服務(wù)端 輔助工具 應(yīng)用實例 Demo示例 element★...
    嘗了又嘗閱讀 1,284評論 0 1
  • 33、JS中的本地存儲 把一些信息存儲在當(dāng)前瀏覽器指定域下的某一個地方(存儲到物理硬盤中)1、不能跨瀏覽器傳輸:在...
    萌妹撒閱讀 2,246評論 0 2
  • 元代景德鎮(zhèn)瓶、罐之類器型,采用分段制胎,然后再用胎泥粘合而成,粘接處器表往往突起,給人以不平之感,外壁接痕經(jīng)打磨...
    骨玩閱讀 264評論 0 0
  • 晚上睡前跟女兒說我小時候爬山的故事,小家伙非常感興趣,時不時插上兩句:“如果我看到那種陡的地方我會嗖直接滑下來或者...
    幸福_娟閱讀 255評論 2 2

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