餓了么vue-cli3.0+cube-ui筆記


1、目錄結構

模板文件是public里的index.html,運行項目的時候,會引用src/main.js(入口文件)

詳細文檔在這里:https://cli.vuejs.org/zh/config/#pwa

public:放着html模板和靜態資源,public/index.html 文件是一個會被 html-webpack-plugin 處理的模板。在構建過程中,資源鏈接會被自動注入。

.browserslistrc 指定瀏覽器版本。不同的瀏覽器會有兼容性問題,比如css,我們會給它們加上前綴,這個文件是為postcss.config.js的Autoprefixer 插件使用的,

    Autoprefixer 插件依據browserslistrc 來添加前綴

postcss.config.js 里的autoprefixer就是依據.browserslistrc文件加前綴

.eslintrc.js  eslint的相關配置

  引入的一些vue插件,比如v-for模板沒有使用key,就會報錯

babel.config.js  預設

package.json 各種依賴

package-lock.json 鎖版本 管理版本的文件

 

cube-ui插件

https://github.com/didi/cube-ui

后編譯:就是我們做項目的時候使用的是 源代碼,只有打包運行后,才會進行編譯。好處是節省構建包的體積,做完項目后,可以把不用的引入刪掉,這樣打包的時候,

就只會打包那些我們使用的模塊

在vue-cli3.0的項目里,直接使用vue add cube-ui 就可以安裝

是否使用后編譯

   

是用部分引入還是全部引入,上面的選擇是部分引入

 

自定義主題是否需要(選擇是,因為我們的項目的顏色一般都與插件的不一樣)

安裝完以后,下面是修改和添加的文件

cube-ui.js 管理cube-ui模塊的引入

 theme.styl 管理cube-ui的顏色(修改顏色可以在這里面進行修改)

 

表示可以直接引入cube-ui的源碼,把cube-ui的組件直接引入項目中,不是用的編譯后的代碼

vue.config.js  類似以前的webpack.js文件,進行一些配置

 2、2-3 api接口mock

現在的項目都是前后端分離,我們這個項目,現在就進行數據接口模擬

data.json里保存的所有數據,類似於后端的數據庫

vue.config.js

// 引入data.json文件,獲取對應的數據
const appData = require('./data.json')
const seller = appData.seller
const goods = appData.goods
const ratings = appData.ratings

  devServer: {
    before (app) {
      app.get('/api/seller', function (req, res) {
        res.json({
          errno: 0,
          data: seller
        })
      })
      app.get('/api/goods', function (req, res) {
        res.json({
          errno: 0,
          data: goods
        })
      })
      app.get('/api/ratings', function (req, res) {
        res.json({
          errno: 0,
          data: ratings
        })
      })
    }
  }

這里有一個devServer,表示本地服務器,里面有個before方法,參數是app,可以在這里面定義接口

例如里面定義的app.get('/api/seller',。啟動服務后,在url輸入http://localhost:8080/api/seller,可以看到下面的效果

 

2、

~表示絕對路徑,要使用這個,先要在vue.config.js里面進行配置

 webpack里的DevServer的before方法

https://webpack.js.org/configuration/dev-server/#devserver-before

 

發現的一個問題:

在tab組件的mounted,created,watch里面輸出App.vue傳入的tabs

 

  export default {
    name: 'tab',
    props: {
      tabs: {
        type: Array,
        default() {
          return []
        }
      },
      initialIndex: {
        type: Number,
        default: 0
      }
    },
    data () {
      return {
        index: this.initialIndex,
        slideOptions: {
          listenScroll: true, // 是否監控scroll事件
          probeType: 3, // 0 不派發scroll事件,1:非實時;2:滑動過程中;3:不僅在屏幕滑動的過程中,而且momentum 滾動動畫運行過程中實時派發
          directionLockThreshold: 0
        }
      }
    },
    created () {
      console.log(this.tabs)
    },
    mounted () {
      console.log(this.tabs)
      this.onChange(this.index)
    },

發現輸出的值里面,sellel不是真實的數據

 

 經過研究,是因為selle在App.vue里面,是異步獲取的,所以是這個樣子

 

如果我們在App.vue里面寫死selle,那在tab組件的mounted,created里面就可以輸出我們想要的值

如果在watch里監控,那就正確

 

綜上所述:如果我們把請求到的數據封裝到一個對象里面(或者是復雜的數據里面),然后傳到子組件,在created和mounted里面輸出這個對象的話,請求的這部分會顯示的不對

但是在template里面使用或者輸出,那就沒問題

如果直接把請求的數據傳遞到子組件,在created和mounted里輸出,也有可能不對,最好的方法是在watch監控這個數據,這樣就正確

3、

     <!--注意這個寫法,先判斷seller.supports有沒有,如果沒有的話,seller.supports[0]會報錯-->
        <div v-if="seller.supports" class="support">
        <support-ico :size=1 :type="seller.supports[0].type"></support-ico>
        <span class="text">{{seller.supports[0].description}}</span>
     </div>

 

4、3-4  headerdetail組件交互

headerDetail組件是fixed,如果放在其他組件內部(有類似transition的樣式),會對樣式造成影響,所以我們可以直接把這種類型的組件放在body下

這里可以借助cube-ui的create-api 模塊  https://didi.github.io/cube-ui/#/zh-CN/docs/create-api

該模塊默認暴露出一個 createAPI 函數,可以實現以 API 的形式調用自定義組件

register.js里面

import { createAPI } from 'cube-ui'
import Vue from 'vue'
import HeaderDetail from 'components/header-detail/header-detail'

createAPI(Vue, HeaderDetail)

main.js

import Vue from 'vue'
import './cube-ui'
import App from './App.vue'
// 引入register.js
import './register'

import 'common/stylus/index.styl'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

組件里面使用

 showDetail() {
        // cube-ui的create-api把headerdetail組件變成了api實例,所以可以這樣當成一個實例調用this.$createHeaderDetail
        this.headerDetailComp = this.headerDetailComp || this.$createHeaderDetail({
          $props: {
            seller: 'seller'
          }
        })
        this.headerDetailComp.show()
}

5、4-1 tab組件基礎實現

https://didi.github.io/cube-ui/#/zh-CN/docs/tab-bar

使用了cube-ui的TabBar組件

6、cube-ui的輪播圖組件  https://didi.github.io/cube-ui/#/zh-CN/docs/slide

7、4-1 tab組件基礎實現

.tab
    display: flex
    flex-direction: column
    height: 100%
    >>> .cube-tab
      padding: 10px 0
    .slide-wrapper
      flex: 1
      overflow: hidden

>>>是什么?

這與vue-loader有關,vue-loader是webpack的一個Loader,專門為了編寫vue方便而出現的

參考地址:https://vue-loader.vuejs.org/zh/guide/scoped-css.html#子組件的根元素

深度作用選擇器

如果你希望 scoped 樣式中的一個選擇器能夠作用得“更深”,例如影響子組件,你可以使用 >>> 操作符:

<style scoped>
.a >>> .b { /* ... */ }
</style>

在這個項目中

cube-tab是tab子類中的子類,要想修改樣式,只能用>>>

8、項目用了cube-ui庫,如果要修改里面默認的顏色,可以這樣做

我們安裝cube-ui的時候,會出現一個文件:theme.styl,可以在這里面修改

項目中的做法是,我們新建common/stylus/variable.styl文件,在里面先定義好顏色

@import "~cube-ui/src/common/stylus/variable.styl"

$color-background = rgba(7, 17, 27, 1)
$color-background-s = rgba(7, 17, 27, 0.8)
$color-background-ss = rgba(7, 17, 27, 0.5)
$color-background-sss = rgba(7, 17, 27, 0.2)
$color-background-ssss = #f3f5f7

$color-red = rgb(240, 20, 20)
$color-blue = rgb(0, 160, 220)
$color-light-blue = rgba(0, 160, 220, 0.2)
$color-green = #00b43c

$color-col-line = #d9dde1
$color-row-line = rgba(7, 17, 27, 0.1)

然后呢,在theme.styl中引入這個文件,然后替換定義好的顏色

引入這個文件

替換顏色

備注:修改的這些樣式,要使用=,不要使用:=,不然不起作用 

9、4-2 tab組件上下聯動

利用cube-tab和cube-slide實現,當滾動slide的時候,對應的tab下划線也滾動

 (1)、在cube-slide加入option屬性

<cube-slide
        :loop=false
        :auto-play=false
        :show-dots=false
        :initial-index="index"
        ref="slide"
        :options="slideOptions"
        @scroll="onScroll"
        @change="onChange"
      >
slideOptions: {
          listenScroll: true, // 是否監控scroll事件
          probeType: 3, // 0 不派發scroll事件,1:非實時;2:滑動過程中;3:不僅在屏幕滑動的過程中,而且momentum 滾動動畫運行過程中實時派發
          directionLockThreshold: 0
        }
directionLockThreshold  https://ustbhuangyi.github.io/better-scroll/doc/zh-hans/options.html#directionlockthreshold
  • 類型:Number
  • 默認值:5(不建議修改)
  • 作用:當我們需要鎖定只滾動一個方向的時候,我們在初始滾動的時候根據橫軸和縱軸滾動的絕對值做差,當差值大於 directionLockThreshold 的時候來決定滾動鎖定的方向。
  • 備注:當設置 eventPassthrough 的時候,directionLockThreshold 設置無效,始終為 0。

如果項目中只是一個方向滾動,那就不用設置,現在這個項目是倆個方向滾動,所以要設置為0

(2)、cube-slide有scroll事件

 滾動中實時派發,返回一個對象,包含滾動的坐標值

https://didi.github.io/cube-ui/#/zh-CN/docs/slide

onScroll (pos) {
        // cube-slide的scroll事件,滾動中實時派發,獲取到滾動位置的坐標值
        // 滾動slide的時候,tab實時改變
        // 原理是:先獲取tabBar和slide的寬度,然后獲取到滾動位置的坐標值,坐標值/slideWidth得到滾動的比例,然后乘以tabBarWidth,實時得到
        // tab下划線的滾動位置
        // 調用cube-tab的setSliderTransform方法,參數就是上面得到的值
        const tabBarWidth = this.$refs.tabBar.$el.clientWidth
        const slideWidth = this.$refs.slide.slide.scrollerWidth
        const transform = -pos.x / slideWidth * tabBarWidth
        this.$refs.tabBar.setSliderTransform(transform)
}

 (3)、調用cube-tab的setSliderTransform方法,實時改變tab的下划線

   備注:tab里有useTransition參數,transition 過渡(默認為true),為了讓效果好看,這里我們要把這個值設為false

   

<cube-tab-bar
      :useTransition=false
      :showSlider="true"
      v-model="selectedLabel"
      :data="tabs"
      ref="tabBar"
      class="border-bottom-1px"
    >

 

完整代碼:

<template>
  <div class="tab">
    <cube-tab-bar
      :useTransition=false
      :showSlider="true"
      v-model="selectedLabel"
      :data="tabs"
      ref="tabBar"
      class="border-bottom-1px"
    >
    </cube-tab-bar>
    <div class="slide-wrapper">
      <cube-slide
        :loop=false
        :auto-play=false
        :show-dots=false
        :initial-index="index"
        ref="slide"
        :options="slideOptions"
        @scroll="onScroll"
        @change="onChange"
      >
        <cube-slide-item>
          <goods></goods>
        </cube-slide-item>
        <cube-slide-item>
          <ratings></ratings>
        </cube-slide-item>
        <cube-slide-item>
          <seller></seller>
        </cube-slide-item>
      </cube-slide>
    </div>
  </div>
</template>

<script>
  import Goods from 'components/goods/goods'
  import Ratings from 'components/ratings/ratings'
  import Seller from 'components/seller/seller'

  export default {
    name: 'tab',
    data () {
      return {
        index: 0,
        tabs: [{
          label: '商品'
        }, {
          label: '評價'
        }, {
          label: '商家'
        }],
        slideOptions: {
          listenScroll: true, // 是否監控scroll事件
          probeType: 3, // 0 不派發scroll事件,1:非實時;2:滑動過程中;3:不僅在屏幕滑動的過程中,而且momentum 滾動動畫運行過程中實時派發
          directionLockThreshold: 0
        }
      }
    },
    methods: {
      // silde 頁面切換時觸發change事件,返回當前的索引值,然后賦值給this.index
      // this.index改變的話,會觸發selectedLabel重新計算,然后cube-tab就會進行新的計算,就可以完成切換了
      onChange (current) {
        this.index = current
      },
      onScroll (pos) {
        // cube-slide的scroll事件,滾動中實時派發,獲取到滾動位置的坐標值
        // 滾動slide的時候,tab實時改變
        // 原理是:先獲取tabBar和slide的寬度,然后獲取到滾動位置的坐標值,坐標值/slideWidth得到滾動的比例,然后*tabBarWidth,實時得到
        // tab下划線的滾動位置
        // 調用cube-tab的setSliderTransform方法,參數就是上面得到的值
        const tabBarWidth = this.$refs.tabBar.$el.clientWidth
        const slideWidth = this.$refs.slide.slide.scrollerWidth
        const transform = -pos.x / slideWidth * tabBarWidth
        this.$refs.tabBar.setSliderTransform(transform)
      }
    },
    computed: {
      selectedLabel: {
        get() {
          return this.tabs[this.index].label
        },
        set(newVal) {
          this.index = this.tabs.findIndex((value) => {
            return value.label === newVal
          })
        }
      }
    },
    components: {
      Goods,
      Ratings,
      Seller
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus" scoped>
  @import "~common/stylus/variable"

  .tab
    display: flex
    flex-direction: column
    height: 100%
    >>> .cube-tab
      padding: 10px 0
    .slide-wrapper
      flex: 1
      overflow: hidden
</style>

10、4-3、tab組件的抽象和封裝

上一節是實現了tab組件的效果,所有的數據都是直接寫在了tab組件,寫在我們把數據抽離出來,只留下功能代碼,這樣我們以后新加/減少tabbar,都不用修改tab組件,只要修改

父組件傳入的數據就行

在父組件的計算屬性里,定義tabs,然后傳入到tab中

computed: {
      tabs() {
        return [
          {
            label: '商品',
            component: Goods,
            data: {
              seller: this.seller
            }
          },
          {
            label: '評論',
            component: Ratings,
            data: {
              seller: this.seller
            }
          },
          {
            label: '商家',
            component: Seller,
            data: {
              seller: this.seller
            }
          }
        ]
      }
    },
<tab :tabs="tabs"></tab>

在tba組件里

<cube-slide-item v-for="(tab, index) in tabs" :key="index">
  <component ref="component" :is="tab.component" :data="tabs.data"></component>
</cube-slide-item>

 11、5-1 scroll-nav組件

使用cube-ui的scrollNav組件

<template>
  <div class="goods">
    <div class="scroll-nav-wrapper">
      <cube-scroll-nav
        :side=true
        :data="goods"
        :options="scrollOptions"
        v-if="goods.length"
      >
        <cube-scroll-nav-panel
          v-for="good in goods"
          :key="good.name"
          :label="good.name"
          :title="good.name"
        >
          <ul>
            <li
              v-for="food in good.foods"
              :key="food.name"
              class="food-item"
            >
              <div class="icon">
                <img width="57" height="57" :src="food.icon">
              </div>
              <div class="content">
                <h2 class="name">{{food.name}}</h2>
                <p class="desc">{{food.description}}</p>
                <div class="extra">
                  <span class="count">月售{{food.sellCount}}份</span><span>好評率{{food.rating}}%</span>
                </div>
                <div class="price">
                  <span class="now">¥{{food.price}}</span>
                  <span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span>
                </div>
                <div class="cart-control-wrapper">
                </div>
              </div>
            </li>
          </ul>
        </cube-scroll-nav-panel>
      </cube-scroll-nav>
    </div>
  </div>
</template>

<script>
  import { getGoods } from 'api'
  export default {
    name: 'goods',
    props: {
      data: {
        type: Object,
        default() {
          return {}
        }
      }
    },
    data() {
      return {
        goods: [],
        selectedFood: {},
        scrollOptions: {
          click: false, // 會點擊倆次,底層用的是scroll,所以設置click為false
          directionLockThreshold: 0
        }
      }
    },
    methods: {
      fetch () {
        getGoods().then((goods) => {
          this.goods = goods
        })
      }
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus" scoped>
  .goods
    position: relative
    text-align: left
    height: 100%
    .scroll-nav-wrapper
      position: absolute
      width: 100%
      top: 0
      left: 0
      bottom: 48px
    >>> .cube-scroll-nav-bar
      width: 80px
      white-space: normal
      overflow: hidden
    >>> .cube-scroll-nav-bar-item
      padding: 0 10px
      display: flex
      align-items: center
      height: 56px
      line-height: 14px
      font-size: $fontsize-small
      background: $color-background-ssss
      .text
        flex: 1
        position: relative
      .num
        position: absolute
        right: -8px
        top: -10px
      .support-ico
        display: inline-block
        vertical-align: top
        margin-right: 4px
    >>> .cube-scroll-nav-bar-item_active
      background: $color-white
      color: $color-dark-grey
    >>> .cube-scroll-nav-panel-title
      padding-left: 14px
      height: 26px
      line-height: 26px
      border-left: 2px solid $color-col-line
      font-size: $fontsize-small
      color: $color-grey
      background: $color-background-ssss
    .food-item
      display: flex
      margin: 18px
      padding-bottom: 18px
      position: relative
      &:last-child
        border-none()
        margin-bottom: 0
      .icon
        flex: 0 0 57px
        margin-right: 10px
        img
          height: auto
      .content
        flex: 1
        .name
          margin: 2px 0 8px 0
          height: 14px
          line-height: 14px
          font-size: $fontsize-medium
          color: $color-dark-grey
        .desc, .extra
          line-height: 10px
          font-size: $fontsize-small-s
          color: $color-light-grey
        .desc
          line-height: 12px
          margin-bottom: 8px
        .extra
          .count
            margin-right: 12px
        .price
          font-weight: 700
          line-height: 24px
          .now
            margin-right: 8px
            font-size: $fontsize-medium
            color: $color-red
          .old
            text-decoration: line-through
            font-size: $fontsize-small-s
            color: $color-light-grey
      .cart-control-wrapper
        position: absolute
        right: 0
        bottom: 12px
    .shop-cart-wrapper
      position: absolute
      left: 0
      bottom: 0
      z-index: 50
      width: 100%
      height: 48px
</style>

知識點有倆個:

 (1)、options參數配置

scrollOptions: {
          click: false, // 會點擊倆次,底層用的是scroll,所以設置click為false
          directionLockThreshold: 0
}

(2)、獲取數據的方法為fetch

fetch () {
        getGoods().then((goods) => {
          this.goods = goods
        })
      }

什么是調用呢?我們一般是在組件的mounted里面調用,但是在這個項目中,如果我們在評論或者商家頁面,商品頁面有可能是在mounted,這時就會進行數據加載,這樣的話,

會影響當前頁面的顯示,所以,我們應該在切換組件的時候調用這個方法

可以在Tab組件的onChange方法里調用

 // 切換的時候,調用對應組件里面的fetch
      onChange (current) {
        this.index = current
        const instance = this.$refs.component[current]
        if (instance && instance.fetch) {
          instance.fetch()
        }

 12、5-3 cart-control組件

   

add (event) {
        if (!this.food.count) {
          // food這個數據時由父組件傳過來的,最開始里面是沒有count屬性的,我們要給里面添加,就需要是vue的$set
          this.$set(this.food, 'count', 1)
        } else {
          this.food.count++
        }this.$emit(EVENT_ADD, event.target)
},

這里面的this,food是父組件傳到子組件里面的,可以用this.$set(this.food, 'count', 1)添加新的屬性,並且賦值

而且可以修改里面值

修改完以后,在父組件里面的值也會隨之改變

13、

 14、ScrollNav 組件

在使用ScrollNav 組件的時候,不要用v-show,會出現有點地方沒有加載進數據,可以使用v-if或者router跳轉到新頁面

15、https://didi.github.io/cube-ui/#/zh-CN/docs/picker  picker選擇器

可以自定義傳入的內容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM