Vue咖啡app項目總結

1、構建javascript

Vue有本身的腳手架構建工具vue-cli,使用起來很是方便,使用webpack來集成各類開發便捷工具,好比:css

  • Hot-reload Vue的熱更新,修改代碼以後無需手動刷新網頁,對前端開發來講很是方便
  • PostCss,不再用去管兼容性的問題了,只針對chrome這樣的現代瀏覽器寫css代碼,會自動編譯生成兼容多款瀏覽器的css代碼
  • ESlint,統一代碼風格,規避低級錯誤,對於有代碼潔癖的人來講是不可或缺的
  • Bable,ES2015出來已經有一段時間了,可是很多瀏覽器尚未兼容ES6,有了bable,放心使用ES6語法,它會自動轉義成ES5語法
  • SCSS,一款 CSS預處理器,編譯後成正常的CSS文件。爲CSS增長一些編程的特性

html

除此以外,vue-cli已經使用node配置了一套本地服務器和安裝命令等,本地運行和打包只須要一個命令就能夠搞定,很是的方便前端

2、實現功能vue

 

  • Goods、Ratings、Seller 組件視圖都可上下滾動
  • 商品頁 點擊左側menu,右側list對應跳轉到相應位置
  • 點擊list查看商品詳情頁,父子組件的通訊
  • 評論內容能夠篩選查看
  • 購物車組件,包括添加刪除商品及動效,購物控件與購物車組件之間爲兄弟組件通訊,點擊購物車圖標,展現已選擇的商品列表
  • 商家實景圖片能夠左右滑動
  • loaclStorage 緩存商家信息(id、name)

3、組件關係
java

 

├──app.vue ├──header.vue--頭部組件 ├──star.vue--星星評分組件 ├──goods.vue--商品組件 ├──shopcart.vue--購物車組件,包括小球飛入購物車動畫 ├──cartcontrol.vue--購買加減圖標控件--選中數量返回給父組件goods,goods響應後,從新計算選中數量,將數據發送給購物車組件, ├──food.vue--商品詳情頁 ├──ratingselect.vue--評價內容篩選組件 ├──ratings.vue--評論組件 ├──ratingselect.vue--評價內容篩選組件 ├──seller.vue--商家組件 獨立組件 ├──split.vue--關於分割線組件

4、項目結構node

common/---- 文件夾存放的是通用的css和fonts
components/---- 文件夾用來存放 Vue 組件
router/---- 文件夾存放的是vue-router相關配置(linkActiveClass,routes註冊組件路由)
build/---- 文件是 webpack 的打包編譯配置文件
config/---- 文件夾存放的是一些配置項,好比咱們服務器訪問的端口配置等
dist/---- 該文件夾一開始是不存在,在項目通過 build 以後纔會生成
prod.server.js---- 該文件是測試是模擬的服務器配置,用來運行dist裏面的文件,在config/index.js中,build對象中添加一條端口設置port:9000, App.vue---- 根組件,全部的子組件都將在這裏被引用 index.html---- 整個項目的入口文件,將會引用咱們的根組件 App.vue main.js---- 入口文件的 js 邏輯,在 webpack 打包以後將被注入到 index.html 中


5、開發過程問題彙總:

一、better-scroll 插件在移動端使用時須要設置 click:true,不然移動端滑動無效

二、分開設置css樣式:

  • 圖標icon.css--文字圖標樣式,經過icommon.io網站 將svg圖片轉成文字圖標樣式
  • 公共base.css--處理設備像素比的一些樣式,針對border-1px問題,不一樣設備像素比,顯示的線條粗細不一樣
  • 工具mixin.css--設置border-1px樣式和背景樣式

移動端 border-1px 實現原理

當樣式像素必定時,因手機有320px,640px等.各自的縮放比差別,因此設備顯示像素就會有1Npx,2Npx。android

公式:設備上像素 = 樣式像素 * 設備像素比webpack

爲了保證設計稿高度還原,採用 media + scale 的方法解決ios

屏幕寬度: 320px 480px 640px 設備像素比: 1 1.5 2 經過查詢它的設備像素比 devicePixelRatio 在設備像素比爲1.5倍時, round(1px 1.5 / 0.7) = 1px 在設備像素比爲2倍時, round(1px 2 / 0.5) = 1px

實現代碼

// SCSS 語法 @mixin border-1px($color) { position: relative; &::after { display: block; position: absolute; left: 0; bottom: 0; width: 100%; border-top: 1px solid $color; content: ''; } } @mixin border-none() { &::after{ display: none; } } @media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5) { .border-1px { &::after { -webkit-transform: scaleY(0.7); transform: scaleY(0.7); } } } @media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) { .border-1px { &::after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } } }

三、sticky-footer佈局

在 header 組件的詳情頁採用 sticky-footer 佈局,主要特色是若是頁面內容不夠長的時候,頁腳塊粘貼在視窗底部;若是內容足夠長時,頁腳塊會被內容向下推送

實現:

父級 position:fixed,內容設 爲padding-bottom:64px,頁腳相對定位,margin-top:-64px,clear:both
爲了保證兼容性,父級要清除浮動

參考:
https://www.cnblogs.com/shico... 
https://www.w3cplus.com/css3/...

四、要求自適應的佈局

一、左側寬度固定,右側寬度自適應

// 左側固定width:80px,右側自適應 parent: display:fiexd; child-left: flex:0 0 80px child-right: flex:1

二、元素寬度自適應設備寬度,且元素要求等寬高樣式

例如:商品詳情頁面的商品圖片展現樣式

// stylus語法 .img_header {  position:relative  width:100% // width是 設備寬度  height:0 padding-top:100% // 高度設爲0,使用padding撐開 .img {  position:absolute //定位佈局  top:0  left:0  width:100%  height:100% } }

五、背景模糊效果

filter:blur(10px),注意,全部在內的子元素也會模糊,包括文字,因此採用定位佈局,背景單獨佔用一個層,ios有一個設置backdrop-filter:blur(10px),只會模糊背景,但不支持android

六、transition過渡

在購買控件中使用transition過渡效果,實現添加減小按鈕的動效,和小球飛入購物車的動效(模仿貝塞爾曲線的效果)

vue2.x裏面定義了transition過渡狀態,
name - string, 用於自動生成 CSS 過渡類名。

例如:name: 'fade' 將自動拓展爲.fade-enter,.fade-enter-active等。默認類名爲 "v" fade-enter fade-enter-active fade-leave fade-leave-active

包括transition過渡的鉤子函數

before-enter before-leave before-appear enter leave appear after-enter after-leave after-appear enter-cancelled leave-cancelled (v-show only) appear-cancelled

七、seller組件:

問題一:seller頁面中商品商家實景圖片橫向滾動

解決方案:每一個 li 要 display:inline-block,由於width不會自動撐開父級ul,因此須要將計算後的寬度賦值給ul的width,(每一張圖片的width+margin)*圖片數量-一個margin,由於最後一張圖片沒有margin
同時new BScroll裏面要設置scrollX: true,eventPassthrough: 'vertical', // 滾動方向橫向

問題二:打開seller頁面,沒法滾動

問題分析:出現這種現象是由於better-scroll插件是嚴格基於DOM的,數據是採用異步傳輸的,頁面剛打開,DOM並無被渲染,因此,要確保DOM渲染了,才能使用 better-scroll,
解決方案:用到mounted鉤子函數,同時必須搭配this.$nextTick()

問題三:在seller頁面,刷新後,沒法滾動

問題分析:出現這種狀況是由於mounted函數在整個生命週期中只會只行一次
解決方案:使用watch方法監控數據變化,並執行滾動函數 this._initScroll();this._initPicScroll();

八、緩存數據

使用window.localStorage保存和設置緩存信息,封裝在store.js文件內

//將頁面信息保存到localStorage裏 export function saveToLocal(id, key, value) { let store = window.localStorage._store_; // 新定義一個key值_store_,存放要保存的數據對象 // _store_ { // store[id]: { // key: value // } // } if (!store) { store = {}; store[id] = {}; } else { store = JSON.parse(store); // String格式--> json格式 if (!store[id]) { store[id] = {}; } } store[id][key] = value; window.localStorage._store_ = JSON.stringify(store); // 將json格式轉成String格式,存放到window.localStorage._store中 } //將localStorage信息設置到頁面中 export function loadFromLocal(id, key, defaults) { let store = window.localStorage._store_; if (!store) { // 一開始是沒有的,由於沒有點擊事件,因此顯示默認數據 return defaults; } store = JSON.parse(store)[id]; // 將json格式-->String格式 // console.log(store); // {"isFavorite":true} if (!store) { return defaults; } let ret = store[key]; return ret || defaults; }

九、解析url,獲得商家信息,包括id,name,在獲取數據時,直接賦值,商家的id或name會被丟掉

使用window.localStorage.search獲取url地址,並進行解析 
封裝在util.js文件內

/** * 解析URL參數 * @example ?id=12345&a=b * @return Object {id:12345, a:b} **/ export function urlParse() { let url = window.location.search; let obj = {}; let reg = /[?&][^?&]+=[^?&]+/g; let arr = url.match(reg); // ['?id=12345', '&a=b'] if (arr) { arr.forEach((item) => { let temArr = item.substring(1).split('='); let key = decodeURIComponent(temArr[0]); let value = decodeURIComponent(temArr[1]); obj[key] = value; }); } return obj; };

咱們須要將獲得的 id 和 name 帶到數據中,實際上在獲取數據的時候,並無帶着id和name,這時就要用到 es6 語法中Object.assign(),官方解釋爲:能夠把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象。

this.seller = Object.assign({}, this.seller, response.data); //即將vm.seller屬性和請求返回數據對象合併到空對象,而後賦值給vm.seller,這裏加上this.seller即提供了一種可擴展的機制,假若原來的屬性中有預約義的其餘屬性。

十、goods,ratings,seller組件之間切換時會從新渲染

解決方案:在 app.vu 內使用 keep-alive,保留各組件狀態,避免從新渲染

<keep-alive>
    <router-view :seller="seller"></router-view> </keep-alive>

Vue 使用技巧

一、vue-router

使用<router-link>組件完成導航,<router-link> 默認會被渲染成一個 <a> 標籤,但必須使用 to屬性,指定鏈接

// app.vue
 <!-- 導航 --> <router-link to="/home">home</router-link> <router-link to="/about">about</router-link> <!-- 路由出口 組件渲染容器 --> <router-view></router-view>
// router: index.js import Vue from 'vue'; import Router from 'vue-router'; import goods from 'components/goods/goods.vue'; import ratings from 'components/ratings/ratings.vue'; import seller from 'components/seller/seller.vue'; Vue.use(Router); const routes = [{ path: '/', redirect: '/goods' }, { path: '/goods', component: goods }, { path: '/ratings', component: ratings }, { path: '/seller', component: seller }]; export default new Router({ routes, linkActiveClass: 'active' });

二、axios

在vue1.x的時候,vue的官方推薦HTTP請求工具是vue-resource,可是在vue2.0的時候將推薦工具改爲了axios。

若是想像之前使用 vue-resource 那樣 this.$http.get 調用,要這樣定義:

Vue.prototype.$http = axios;

經過 this.$http.get 來定義經過vue實例來發送get請求,而後經過then後面的回調函數將請求成功的數據接收,經過狀態碼來判斷是否成功以及複製給vue的數據對象。因爲這裏是用的mock數據(模擬後臺數據),因此用的模擬狀態碼。

const ERR_OK = 0;//表示沒有錯誤信息,即獲取數據成功 this.$http.get('/api/seller').then((response) => { response = response.data; if (response.errno === ERR_OK) { this.seller = Object.assign({}, this.seller, response.data); } });

三、組件間通信

vue是組件式開發,因此組件間通信是必不可少的

  • 父傳子: props
  • 子傳父: $emit
  • 兄弟通信: 1. event bus: 利用一箇中間組件來做爲信息傳遞中介;2. vuex: 信息樹

父傳子: props

vue提供了一種方式,即在子組件定義 props 來接受父組件傳遞來的數據對象。

// 父組件 <v-header :seller="seller"></v-header> // 子組件 header.vue props: {  seller: {  type: Object } }

子傳父: $emit

若是是子組件想傳遞數據給父組件,須要派發自定義事件,使用 $emit 派發,
父組件使用v-on接收監控(v-on能夠簡寫成@)

// 子組件 RatingSelect.vue,派發自定義事件isContent,將this.onlyContent數據傳給父級 this.$emit('isContent', this.onlyContent); this.$emit('selRatings', this.selectType); // 父組件 foodInfo.vue 在子組件的模板標籤裏,使用v-on監控isContent傳過來的數據 <v-ratingselect @selRatings="filterRatings" @isContent="iscontent"></v-ratingselect> 

非父子組件之間通訊

  1. 大型項目能夠用 Vue官方推薦的vuex
  2. EventBus :https://n-y.io/vue-eventbus/
  3. 子組件A $emit 派發具體事件,由父組件 @ 監聽獲得數據

父組件再利用 $refs 直接訪問子組件B的方法,間接實現數據從子組件A傳遞至子組件B

四、組件提取管理

將相一樣式或功能的區塊單獨提出來,做爲一個組件。
另外組件中用到的圖片等資源就近維護,便可以考慮在組件文件夾中新建images文件夾。

抽離組件遵循原則:
要儘可能遵循單一職責原則,複用性更高,不要設置額外的margin等影響佈局的東西

五、打開app應用,默認顯示 goods 頁面內容

想要達到這種目的,有兩種方法,一種是利用重定向,另外一種是利用vue-router的導航式編程。

一、重定向

//在router的index.js文件中設置,要多寫一個對象,指向目標組件 Vue.use(Router); const routes = [{ path: '/', redirect: '/goods' // 重定向 }, { path: '/goods', component: goods }, { path: '/ratings', component: ratings }, { path: '/seller', component: seller }]; export default new Router({ routes, linkActiveClass: 'active' });

二、導航式編程

router.push('/Goods');

項目難點

一、關於購物車添加按鈕的動畫

html代碼

  • 生成一個動畫小球的div,而且生成五個小球,五個是爲了生成必定數量的小球來做爲操做使用,按照小球動畫的速度,通常來講五個也能夠保證有足夠的小球數量來運行動畫
  • 動畫的內容分別是外層和內層,外層控制動畫小球的軌道和方向,內層控制動畫小球自身的運行狀態
  • 動畫使用vue的js鉤子實現
  • 由於小球動畫只有一個方向(只執行單方向從上到下滾落),因此只用了before-enter,enter,after-enter
  • 用v-show控制小球的可見性,在動畫執行期間可見,其他時候隱藏
<div class="ball-container"> <div v-for="ball in balls">  //用了兩種方式的動畫,css和js鉤子 <transition name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop">  //外層動畫 <div class="ball" v-show="ball.show">  //內層動畫 <div class="inner inner-hook"></div> </div> </transition> </div> </div>

js代碼

  • 設置了balls數組來表明五個小球
  • 設置了dropBalls數組正在運行的小球
data(){ return {  balls: [ {show: false}, {show: false}, {show: false}, {show: false}, {show: false} ],  dropBalls: [] } },
  • 只要觸發了drop事件,不止是drop事件裏面的代碼會執行,另外幾個vue的js監聽鉤子也會一塊兒按順序執行

    • 觸發了 drop 事件
    • beforeDrop 開始執行
    • dropping 開始執行
    • afterDrop 開始執行
  • drop 事件的觸發能夠經過點擊 cartcontrol 組件的添加小球按鈕 addCart 事件觸發使用 $emit ,也能夠父組件 this.$refs.shopcart.drop(target); 直接觸發

    • 這麼作的目的是實現,在子組件 cartcontrol 點擊以後,能夠將具體點擊的 dom 傳給父組件 goods 而後再傳給子組件 shopcart,(由於目前他們之間的通道就是這樣,shopcart子組件並無導入cartcontrol子組件,因此沒有直接通信)這樣就實現了多個組件之間的通信(也可使用 EventBus 和 vuex),從而能夠實現需求,例如這裏就是實現點擊子組件 cartcontrol 後添加一個動畫,將小球滑落到另一個組件shopcart
  • $emit 是觸發當前實例上的事件。附加參數都會傳給監聽器回調。
methods: { drop(el) { //觸發一次事件就會將全部小球進行遍歷 for (let i = 0; i < this.balls.length; i++) { let ball = this.balls[i]; if (!ball.show) { //將false的小球放到dropBalls ball.show = true; ball.el = el; //設置小球的el屬性爲一個dom對象 this.dropBalls.push(ball); return; } } }, beforeDrop(el){ //這個方法的執行是由於這是一個vue的監聽事件 let count = this.balls.length; while (count--) { let ball = this.balls[count]; if (ball.show) { let rect = ball.el.getBoundingClientRect(); //獲取小球的相對於視口的位移(小球高度) let x = rect.left - 32; let y = -(window.innerHeight - rect.top - 22); //負數,由於是從左上角往下的的方向 el.style.display = ''; //清空display el.style.webkitTransform = `translate3d(0,${y}px,0)`; el.style.transform = `translate3d(0,${y}px,0)`; //處理內層動畫 let inner = el.getElementsByClassName('inner-hook')[0]; //使用inner-hook類來單純被js操做 inner.style.webkitTransform = `translate3d(${x}px,0,0)`; inner.style.transform = `translate3d(${x}px,0,0)`; } } }, dropping(el, done) { //這個方法的執行是由於這是一個vue的監聽事件 /* eslint-disable no-unused-vars */ let rf = el.offsetHeight; //觸發重繪html this.$nextTick(() => { //讓動畫效果異步執行,提升性能 el.style.webkitTransform = 'translate3d(0,0,0)'; el.style.transform = 'translate3d(0,0,0)'; //處理內層動畫 let inner = el.getElementsByClassName('inner-hook')[0]; //使用inner-hook類來單純被js操做 inner.style.webkitTransform = 'translate3d(0,0,0)'; inner.style.transform = 'translate3d(0,0,0)'; el.addEventListener('transitionend', done); //Vue爲了知道過渡的完成,必須設置相應的事件監聽器。 }); }, afterDrop(el) { //這個方法的執行是由於這是一個vue的監聽事件 let ball = this.dropBalls.shift(); //完成一次動畫就刪除一個dropBalls的小球 if (ball) { ball.show = false; el.style.display = 'none'; //隱藏小球 } } }
  • 關於 transitionend
  • 關於drop方法,是實現每個ball的show屬性和el屬性處理,而且點擊一次會自動將一個小球放到 dropBalls 數組裏面,放到裏面就表明的是一個小球已經被開始執行動畫,可是因爲動畫是異步的,因此先主動設置.
  • 關於 getBoundingClientRect (位移的計算是從左上角開始)

    • 使用 getBoundingClientRect 獲取到當前元素的座標,而後須要位移的left減去元素的寬獲取真正的最終位移x座標
    • 使用 getBoundingClientRect 獲取到當前元素的座標,而後須要當前屏幕的高度減去元素的 top 再減去元素自己的高度獲取到真正的最終位移 y 座標,而且這個是負數,由於是從左上角往下的方向
  • 關於html重繪

    • 由於瀏覽器對於重繪是有要求而且是有隊列完成的,這是主要爲了性能,雖然動畫隱藏了小球display none,但沒有觸發html重繪,或者說沒有當即觸發html重繪,因此須要手動
    • let rf = el.offsetHeight; 這是一個手動觸發html重繪的方法
    • 網頁性能管理詳解
    • 高性能JavaScript 重排與重繪

css代碼

.ball-container .ball position: fixed //小球動畫必須脫離html佈局流 left: 32px bottom: 22px z-index: 200 transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41) .inner width: 16px height: 16px border-radius: 50% background: rgb(0, 160, 220) transition: all 0.4s linear
  • 關於cubic-bezier(0.49, -0.29, 0.75, 0.41),是動畫拋物曲線(貝塞爾曲線)的配置,基於css3實現,參考貝塞爾曲線與CSS3動畫、SVG和canvas的基情 ,至於拋物線放在外層就是爲了控制內層的元素的軌道和方向的.

二、星星組件star.vue

整個流程是:

  1. 綁定星星類型的class(48,36,24尺寸),使用starType
  2. 使用class來顯示星星,有3種類型,全星,半星,無星,使用star-item表明星星自己,而後分別使用on,off,half表明三種不一樣類型的星星
  3. 一個span表明一個星星項目,而且使用v-for循環將星星項目輸出

最後造成的星星html就相似這樣

<div class="star star-48"> <span class="star-item on"></span> <span class="star-item on"></span> <span class="star-item on"></span> <span class="star-item on"></span> <span class="star-item half"></span> </div>

html部分

<template>
  <div class="star" :class="starType"> <span v-for="itemClass in itemClasses" :class="itemClass" class="star-item"></span> </div> </template>

js部分

  • 設置常量是爲了方便解耦
  • 星星計算比較巧妙(根據分數轉換爲星星數)

    • 對於分數score進行乘以2而後向下取整,而後再除以2,是爲了獲取全部星星的數量,而且這個數量是0.5倍數的,例如4.6 2就是9.2,而後向下取整是9,而後再除以2就是4.5,那麼就能夠獲得一個0.5倍數的星星數,能夠轉換爲4個全星+一個半星
    • 對於非整數的星星算做是半個星星,須要知道是否有存在這種狀況,因此分數score%1 ,例如 8 % 1是0,8.5 % 1就不是0,而且這個半星只會出現一次,由於半星狀態就只要一個
    • 沒有星星的部分是要補全的,這裏使用while循環來處理這種狀況
<script> //設置常量 const LENGTH = 5; const CLS_ON = 'on'; const CLS_HALF = 'half'; const CLS_OFF = 'off'; export default{ props: { size: { //傳入的size變量 type: Number //設置變量類型 }, score: { //傳入的score變量 type: Number } }, computed: { starType(){ //經過計算屬性,返回組裝過的類型,用來對應class類型 return 'star-' + this.size; }, itemClasses(){ let result = []; //返回的是一個數組,用來遍歷輸出星星 let score = Math.floor(this.score * 2) / 2; //計算全部星星的數量 let hasDecimal = score % 1 !== 0; //非整數星星判斷 let integer = Math.floor(score); //整數星星判斷 for (let i = 0; i < integer; i++) { //整數星星使用on result.push(CLS_ON);//一個整數星星就push一個CLS_ON到數組 } if (hasDecimal) { //非整數星星使用half result.push(CLS_HALF);//相似 } while (result.length < LENGTH) { //餘下的用無星星補全,使用off result.push(CLS_OFF);//相似 } return result; } } } </script>

css部分

  • 引入mixin.styl是爲了使用bg-image的mixin,由於以前作了一個mixin是專門處理2x和3x圖片的轉換
  • 由於這裏有3種類型的星星圖片,分別是48尺寸,36尺寸,24尺寸,因此對於每個類別的圖片分別使用一種class作對應
  • 每一種星星的尺寸都是有一種相對應的圖片的,例如48尺寸的星星就會有,而且圖片放在相對應的vue文件目錄下
star48_half@2x.png star48_half@3x.png star48_off@2x.png star48_off@3x.png star48_on@2x.png star48_on@3x.png
<style lang="scss" rel="stylesheet/scss"> @import "../../common/css/mixin"; .star { font-size: 0; .star-item { display: inline-block; background-repeat: no-repeat; } &.star-48 { //48尺寸的星星 .star-item { //每個星星的基本css信息 width: 20px; height: 20px; margin-right: 22px; //每個星星dom都有外邊距 background-size: 20px 20px; &:last-child { //最後一個的外邊距就是0 margin-right: 0; } &.on { //全星狀態的class @include bg-img('star48_on') } &.half { //半星狀態的class @include bg-img('star48_half') } &.off { //無星狀態的class @include bg-img('star48_off') } } } &.star-36 { .star-item { width: 15px; height: 15px; margin-right: 6px; background-size: 15px 15px; &:last-child { margin-right: 0; } &.on { @include bg-img('star36_on') } &.half { @include bg-img('star36_half') } &.off { @include bg-img('star36_off') } } } &.star-24 { .star-item { width: 10px; height: 10px; margin-right: 3px; background-size: 10px 10px; &:last-child { margin-right: 0; } &.on { @include bg-img('star24_on') } &.half { @include bg-img('star24_half') } &.off { @include bg-img('star24_off') } } } } </style>

三、ratingselect組件

html代碼

備註:父組件food.vue傳入的數據
<ratingselect @select="selectRating" @toggle="toggleContent" :selectType="selectType" :onlyContent="onlyContent" :desc="desc" :ratings="food.ratings"></ratingselect>
  • 方法有:@select="selectRating" @toggle="toggleContent",經過將字組件的方法和父組件的方法進行關聯,這樣就可以實現跨組件通信和操做
  • 屬性有::selectType="selectType":onlyContent="onlyContent" :desc="desc":ratings="food.ratings",這是經過pros傳入到子組件的屬性,將父組件的數據傳到子組件裏面,也帶有一種經過父組件來初始化子組件屬性的意思.
 <div class="ratingselect"> <!--有使用一個border-1px的mixin--> <div class="rating-type border-1px"> <!--綁定一個select方法控制切換,綁定class控制切換以後的按鈕樣式顯示--> <span @click="select(2,$event)" class="block positive" :class="{'active':selectType ===2}">{{desc.all}}<span class="count">{{ratings.length}}</span></span> <span @click="select(0,$event)" class="block positive" :class="{'active':selectType ===0}">{{desc.positive}}<span class="count">{{positives.length}}</span></span> <span @click="select(1,$event)" class="block negative" :class="{'active':selectType ===1}">{{desc.negative}}<span class="count">{{negatives.length}}</span></span> </div> <!--綁定一個toggleContent方法來控制有內容和無內容的顯示--> <div @click="toggleContent" class="switch" :class="{'on':onlyContent}"> <span class="icon-check_circle"></span> <span class="text">只看有內容的評價</span> </div> </div>
  • @click="select(2,$event)" select方法傳入類型和事件,而後在methods裏面調用父組件的方法,實現子組件控制父組件的目的
  • :class="{'active':selectType ===2}" 根據類型來肯定顯示的class,實現不一樣類型顯示不一樣樣式的目的
  • positives.length 使用計算屬性自動計算類型數組的長度,用來顯示不一樣類型的數量
  • @click="toggleContent" :class="{'on':onlyContent}"

    • toggleContent 控制是否展現有內容的rate,也是在methods裏面調用父組件的方法,實現子組件控制父組件的目的
    • 綁定on這個class來控制該按鈕的樣式

JS代碼

const POSITIVE = 0; //設置顯示常量 const NEGATIVE = 1; const ALL = 2; export default{ props: { ratings: { //傳入ratings數組,跟food.ratings關聯 type: Array, default(){ return []; } }, selectType: { //跟selectType關聯,經過在父組件裏面設置這3個值來實現控制子組件的操做 type: Number, default: ALL }, onlyContent: { //跟onlyContent關聯 type: Boolean, default: true }, desc: { //跟desc關聯 type: Object, default(){ return { all: '所有', positive: '滿意', negative: '不滿意' } } } }, computed: { positives(){ //自動過濾rateType(正面的rate) return this.ratings.filter((rating) => { //js的filter函數會返回一個處理後的(爲true)結果的結果數組 return rating.rateType === POSITIVE; }) }, negatives(){ //自動過濾rateType(反面的rate) return this.ratings.filter((rating) => { return rating.rateType === NEGATIVE; }) } }, methods: { select(type, event) { // 選擇rateType而且通知父組件 if (!event._constructed) { return; } this.$emit('select', type); // 派發事件,父組件監聽此事件 }, toggleContent(event) { // 選擇是否顯示有內容的rate,而且通知父組件 if (!event._constructed) { return; } this.$emit('toggle'); } } }

四、商品區域goods.vue

HTML

  1. v-for使用已經很常見了,不過這裏須要瞭解,vue1和2有區別,如今是用vue2,因此index變量傳遞會變成如今這種模式 (item,index) in goods
  2. vue傳遞原生事件使用$event
  3. :class="{'current':currentIndex === index}" 是vue的綁定class的使用方法,經過綁定一個class變量來直接操做,而且這裏的邏輯會跟js代碼裏面對應

    1. 經過currentIndex和index作對比,來確認是否添加current類,他們之間的對比關係也就是 menu 區域和 foods 區域的顯示區域的對比關係
    2. 經過添加current類來實現當前頁面的區域的樣式變化
    3. currentIndex是一個計算屬性,能夠隨時變化而且直接反應到dom上(看js裏面邏輯)
  4. v-show 和 v-if 的區別官網已經說過

    1. v-if 是「真正的」條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
    2. v-if 也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。

通常來講, v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不太可能改變,則使用 v-if 較好。

  1. $refs 的使用是vue操做dom的一種方式:

    1. ref 被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs 對象上。
    2. 若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 若是用在子組件上,引用就指向組件實例:
  2. hook鉤子類的使用,須要結合js裏面的語法來看,這個類只是用來操做,不會產生dom的渲染,方便js控制清晰.

JS

  • <food @add="addFood" :food="selectedFood" ref="food"> 是經過selectFood方法寫入到vue實例裏面,而後傳給子組件food
  • <shopcart ref="shopcart" :selectFoods="selectFoods" 這裏selectFoods被自動添加了count屬性,是爲了讓購物車更加簡單的計算已選擇的food
  • 這裏最關鍵的是menu和food兩個區域的對應處理:

    • 在vue實例生命週期的開始created分別加載_initScroll_calculateHeight
    • 經過 _calculateHeight 計算foods內部每個塊的高度,組成一個數組listHeight
    • 在 _initScroll 裏面,設置了bscroll插件的一個監聽事件scroll,將food區域當前的滾動到的位置的y座標設置到一個vue實例屬性 scrollY this.scrollY = Math.abs(Math.round(pos.y));
    • 經過計算屬性currentIndex,獲取到food滾動區域對應的menu區域的子塊的索引,而後經過設置一個class來作樣式切換變化:class="{'current':currentIndex === index},實現聯動
    • 另外當點擊menu 區域的時候,會觸發selectMenu事件,也會根據點擊到的menu子塊的索引而後去觸發food區域滾動到對應的高度區塊區間this.foodsScroll.scrollToElement(el, 300);
    • 這樣完成整個對應.
  • 拋物線小球動畫橫跨多個vue組件(也能夠說是橫跨了多個DOM)
  • better-scroll須要安裝,npm安裝,具體參看better-scroll官網
  • 關於在selectMenu中點擊,在pc界面會出現兩次事件,在移動端就只出現一次事件的問題:

    • 緣由:

      • bsScrooler會監聽事件(例如touchmove,click之類),而且阻止默認事件(prevent stop),而且他只會監聽移動端的,pc端的沒有監聽
      • 在pc頁面上 bsScroller也派發了一次click事件,原生也派發了一次click事件
    • 解決:

      • 針對bsScroole的事件,有_constructed: true,因此作處理,return 掉非bsScroll的事件

SCSS 預處理器

css預處理器

index.scss是SCSS文件的入口文件,裏面使用 @import 引入各類SCSS文件

@import "./base"; @import "./mixin"; @import "./icon.css";

在入口文件main.js中全局引用index.scss

import 'common/css/index.scss';

關於ESlint

eslint 是一個js代碼風格檢查器,配合vue-cli腳手架中的熱更新,能夠很方便的定位和提示錯誤。在公司多人協做開發時能夠確保代碼風格保持一致,能夠很方便的閱讀他人的代碼。

手機測試網頁技巧

將 localhost 換成本身的ip (Windows在命令行執行ipconfig查看,mac執行ifconfig查看)

而後複製地址欄地址,進入草料二維碼,而後生成二維碼,而後用手機掃一掃就能夠查看了,前提是,你手機和電腦必須在同一個局域網。

項目運行

克隆項目到本地
git clone git@github.com:nanyang24/eleme-vue.git 安裝依賴 npm install 本地開發,開啓服務器,瀏覽器訪問http://localhost:8080 npm run dev 構建生產 npm run build 運行打包文件 node prod.server.js 會看到 Listening at http://localhost:9000 在瀏覽器中打開便可
相關文章
相關標籤/搜索