饿了么vue-cli3.0+cube-ui笔记

时间:2023-03-09 07:52:13
饿了么vue-cli3.0+cube-ui笔记

1、目录结构

饿了么vue-cli3.0+cube-ui笔记

模板文件是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-cli3.0+cube-ui笔记

引入的一些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 就可以安装

是否使用后编译

饿了么vue-cli3.0+cube-ui笔记

是用部分引入还是全部引入,上面的选择是部分引入

饿了么vue-cli3.0+cube-ui笔记

自定义主题是否需要(选择是,因为我们的项目的颜色一般都与插件的不一样)

饿了么vue-cli3.0+cube-ui笔记

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

饿了么vue-cli3.0+cube-ui笔记

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

theme.styl 管理cube-ui的颜色(修改颜色可以在这里面进行修改)

饿了么vue-cli3.0+cube-ui笔记

表示可以直接引入cube-ui的源码,把cube-ui的组件直接引入项目中,不是用的编译后的代码

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

2、2-3 api接口mock

现在的项目都是前后端分离,我们这个项目,现在就进行数据接口模拟

饿了么vue-cli3.0+cube-ui笔记

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: ,
data: seller
})
})
app.get('/api/goods', function (req, res) {
res.json({
errno: ,
data: goods
})
})
app.get('/api/ratings', function (req, res) {
res.json({
errno: ,
data: ratings
})
})
}
}

这里有一个devServer,表示本地服务器,里面有个before方法,参数是app,可以在这里面定义接口

例如里面定义的app.get('/api/seller',。启动服务后,在url输入http://localhost:8080/api/seller,可以看到下面的效果

饿了么vue-cli3.0+cube-ui笔记

2、

饿了么vue-cli3.0+cube-ui笔记

~表示绝对路径,要使用这个,先要在vue.config.js里面进行配置

饿了么vue-cli3.0+cube-ui笔记

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:
}
},
data () {
return {
index: this.initialIndex,
slideOptions: {
listenScroll: true, // 是否监控scroll事件
probeType: , // 0 不派发scroll事件,1:非实时;2:滑动过程中;3:不仅在屏幕滑动的过程中,而且momentum 滚动动画运行过程中实时派发
directionLockThreshold:
}
}
},
created () {
console.log(this.tabs)
},
mounted () {
console.log(this.tabs)
this.onChange(this.index)
},

发现输出的值里面,sellel不是真实的数据

饿了么vue-cli3.0+cube-ui笔记

经过研究,是因为selle在App.vue里面,是异步获取的,所以是这个样子

饿了么vue-cli3.0+cube-ui笔记

如果我们在App.vue里面写死selle,那在tab组件的mounted,created里面就可以输出我们想要的值

如果在watch里监控,那就正确

饿了么vue-cli3.0+cube-ui笔记

饿了么vue-cli3.0+cube-ui笔记

综上所述:如果我们把请求到的数据封装到一个对象里面(或者是复杂的数据里面),然后传到子组件,在created和mounted里面输出这个对象的话,请求的这部分会显示的不对

但是在template里面使用或者输出,那就没问题

如果直接把请求的数据传递到子组件,在created和mounted里输出,也有可能不对,最好的方法是在watch监控这个数据,这样就正确

3、

     <!--注意这个写法,先判断seller.supports有没有,如果没有的话,seller.supports[]会报错-->
<div v-if="seller.supports" class="support">
<support-ico :size= :type="seller.supports[0].type"></support-ico>
<span class="text">{{seller.supports[].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: %
>>> .cube-tab
padding: 10px
.slide-wrapper
flex:
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>

在这个项目中

饿了么vue-cli3.0+cube-ui笔记

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(, , , )
$color-background-s = rgba(, , , 0.8)
$color-background-ss = rgba(, , , 0.5)
$color-background-sss = rgba(, , , 0.2)
$color-background-ssss = #f3f5f7 $color-red = rgb(, , )
$color-blue = rgb(, , )
$color-light-blue = rgba(, , , 0.2)
$color-green = #00b43c $color-col-line = #d9dde1
$color-row-line = rgba(, , , 0.1)

然后呢,在theme.styl中引入这个文件,然后替换定义好的颜色

引入这个文件

饿了么vue-cli3.0+cube-ui笔记

替换颜色

饿了么vue-cli3.0+cube-ui笔记

备注:修改的这些样式,要使用=,不要使用:=,不然不起作用

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: , // 0 不派发scroll事件,1:非实时;2:滑动过程中;3:不仅在屏幕滑动的过程中,而且momentum 滚动动画运行过程中实时派发
directionLockThreshold:
}
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: ,
tabs: [{
label: '商品'
}, {
label: '评价'
}, {
label: '商家'
}],
slideOptions: {
listenScroll: true, // 是否监控scroll事件
probeType: , // 0 不派发scroll事件,1:非实时;2:滑动过程中;3:不仅在屏幕滑动的过程中,而且momentum 滚动动画运行过程中实时派发
directionLockThreshold:
}
}
},
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: %
>>> .cube-tab
padding: 10px
.slide-wrapper
flex:
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组件

饿了么vue-cli3.0+cube-ui笔记

使用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="" height="" :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:
}
}
},
methods: {
fetch () {
getGoods().then((goods) => {
this.goods = goods
})
}
}
}
</script> <style lang="stylus" rel="stylesheet/stylus" scoped>
.goods
position: relative
text-align: left
height: %
.scroll-nav-wrapper
position: absolute
width: %
top:
left:
bottom: 48px
>>> .cube-scroll-nav-bar
width: 80px
white-space: normal
overflow: hidden
>>> .cube-scroll-nav-bar-item
padding: 10px
display: flex
align-items: center
height: 56px
line-height: 14px
font-size: $fontsize-small
background: $color-background-ssss
.text
flex:
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:
.icon
flex: 57px
margin-right: 10px
img
height: auto
.content
flex:
.name
margin: 2px 8px
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:
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:
bottom: 12px
.shop-cart-wrapper
position: absolute
left:
bottom:
z-index:
width: %
height: 48px
</style>

知识点有俩个:

(1)、options参数配置

scrollOptions: {
click: false, // 会点击俩次,底层用的是scroll,所以设置click为false
directionLockThreshold:
}

(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', )
} else {
this.food.count++
}this.$emit(EVENT_ADD, event.target)
},

这里面的this,food是父组件传到子组件里面的,可以用this.$set(this.food, 'count', 1)添加新的属性,并且赋值

而且可以修改里面值

修改完以后,在父组件里面的值也会随之改变

13、

饿了么vue-cli3.0+cube-ui笔记

14、ScrollNav 组件

在使用ScrollNav 组件的时候,不要用v-show,会出现有点地方没有加载进数据,可以使用v-if或者router跳转到新页面

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

饿了么vue-cli3.0+cube-ui笔记

可以自定义传入的内容