首頁 >web前端 >js教程 >Vue框架中有關goods組件開發

Vue框架中有關goods組件開發

亚连
亚连原創
2018-06-09 16:08:081364瀏覽

這篇文章主要介紹了Vue框架之goodvs組件開發詳解,現在分享給大家,也給大家做個參考。

一、佈局Flex

Flex 佈局,可以簡單、完整、響應式地實現各種頁面佈局,Flex 是Flexible Box 的縮寫,意為"彈性版面",用來為盒狀模型提供最大的彈性。任何一個容器都可以指定為 Flex 佈局。

// 指定为 Flex 布局
 display: flex;
flex : 等分 内容缩放 展位空间;
flex : 0 0 80px

// 主要屬性
  flex: none | [ a7a0d5b646417f6594c8dc5f362a4c77 a0b15e1abe832ad4879244e7e7399768? || 4defc00a3dd612141d10914f04e0cfc9 ]
  flex屬性為flex-grow, flex-shrink 和flex-basis的簡寫,預設值為0 1 auto。後兩個屬性可選。
  flex-grow屬性定義項目的放大比例,預設為0,即如果存在剩餘空間,也不放大
  flex-shrink屬性定義項目的縮小比例,預設為1,即如果空間不足,該項目將縮小,flex-shrink屬性為0,其他項目都為1,則空間不足時,前者不縮小
  flex-basis屬性定義了在分配多餘空間之前,項目佔據的主軸空間(main size)。瀏覽器根據這個屬性,計算主軸是否有多餘空間。它的預設值為auto,即項目的本來大小,設為跟width或height屬性一樣的值(例如350px),則項目將佔據固定空間

##二、圖示元件

子元件iconMap

<template lang="html">
 <span class="iconMap" :class="iconClassMap[iconType]"></span>
</template>
export default {
 props: { // 图标类型
 iconType: Number
 },
 created() { // 数组类名
 this.iconClassMap = [&#39;decrease&#39;, &#39;discount&#39;, &#39;special&#39;, &#39;invoice&#39;, &#39;guarantee&#39;]
 }
}

父元件goods

import iconMap from &#39;../iconMap/iconMap&#39; // 注意路径写法
// 注册组件
 components: {
 iconMap
 }
<ul>
 <li v-for=&#39;(item,index) in goods&#39; class="menu-item">
 <span class="text"> // json 数据 根据 type 判断 是否有图标
 <iconMap v-show="item.type>0" :iconType="item.type"></iconMap>
 {{item.name}}
 </span>
 </li>
</ul>

三、better-scroll 應用

#類似iscroll 實現滾動效果

安裝

npm install better-scroll

引入

import BScroll from &#39;better-scroll&#39;

說明

(1)原理:父容器wrapper,它具有固定的高度,當它的第一個子元素content 的高度超出了wrapper的高度,我們就可以滾動內容區了,若沒有超出則不能滾動了。

(2)better-scroll 的初始化

better-scroll 的初始化時機很重要,因為它在初始化的時候,會計算父元素和子元素的高度和寬度,來決定是否可以縱向和橫向捲動。因此,我們在初始化它的時候,必須確保父元素和子元素的內容已經正確渲染了。如果子元素或父元素 DOM 結構改變的時候,必須重新呼叫 scroll.refresh() 方法重新計算來確保捲動效果的正常。所以 better-scroll 不能滾動的原因多半是初始化 better-scroll 的時機不對,或者是當 DOM 結構發送變化的時候並沒有重新計算 better-scroll。

(3)better-scroll 結合 Vue

Vue.js 提供了我們一個取得 DOM 物件的介面- vm.$refs。在這裡,我們透過了 this.$refs.wrapper 存取到了這個 DOM 對象,並且我們在 mounted 這個鉤子函數裡,this.$nextTick 的回調函數中初始化 better-scroll 。因為這個時候,wrapper 的 DOM 已經渲染了,我們可以正確計算它以及它內層 content 的高度,以確保滾動正常。

這裡的 this.$nextTick 是一個非同步函數,為了確保 DOM 已經渲染,底層用到了 MutationObserver 或是 setTimeout(fn, 0)。其實我們在這裡把 this.$nextTick 換成 setTimeout(fn, 20) 也是可以的(20 ms 是一個經驗值,每一個 Tick 約為 17 ms),對使用者體驗而言都是無感知的。

(4)非同步資料的處理

在我們的實際工作中,清單的資料往往都是非同步取得的,因此我們初始化better-scroll 的時機需要在資料取得後,程式碼如下:


<script>
 import BScroll from &amp;#39;better-scroll&amp;#39;
 export default {
 data() {
 return {
 data: []
 }
 },
 created() {
 requestData().then((res) => {
 this.data = res.data
 this.$nextTick(() => {
 this.scroll = new Bscroll(this.$refs.wrapper, {})
 })
 })
 }
 }
</script>

這裡的requestData 是偽代碼,作用就是發起一個http 請求從服務端取得數據,而這個函數傳回的是一個promise(實際專案中我們可能會用axios 或vue-resource )。我們取得到資料的後,需要透過非同步的方式再去初始化better-scroll,因為Vue 是資料驅動的, Vue 資料會改變(this.data = res.data)到頁面重新渲染是一個非同步的過程,我們的初始化時機是要在DOM 重新渲染後,所以這裡用到了this.$nextTick,當然替換成setTimeout(fn, 20) 也是可以的。

注意:這裡為什麼是在 created 這個鉤子函數裡請求資料而不是放到 mounted 的鉤子函數裡?因為requestData 是發送一個網路請求,這是一個非同步過程,當拿到回應資料的時候,Vue 的DOM 早就已經渲染好了,但是資料改變—> DOM 重新渲染仍然是一個非同步過程,所以即使在我們拿到資料後,也要異步初始化better-scroll。

使用

初始化需要捲動的dom結構

使用ref屬性用來綁定某個dom元素,或者來說來綁定某個元件,然後在函式內用this.$refs.menuwrapper取得到dom。

說明:如果在普通的DOM 元素上使用,引用指向的就是DOM 元素; 如果用在子元件上,參考就指向元件實例:

<p class="menu-wrapper" ref=&#39;menuWrapper&#39;> </p>
<p class="foods-wrapper" ref="foodsWrapper"></p>

在ajax內執行_initScroll () 函數

在此之前我們要做一些準備和注意事項

(1) dom结构完全加载完再调用_initScroll()方法才会生效

(2) 因为要监听内容区域的高度,所以初始化应在created过程中去监听dom结构是否完全加载,这里是在$nextTick对象中进行触发检测

ES6语法格式: this.$nextTick(() => {})

created (){ // 在实例创建完成后被立即调用 $el 属性目前不可见。
 axios.get(&#39;static/data.json&#39;).then((result) => {
 this.goods=result.data.goods
 //dom结构加载结束
 this.$nextTick(() => {
 this._initScroll(); // 初始化scroll
 })
 })
}

(3) 在methods方法里面定义一个_initScroll的函数,主要用来对左右两侧dom结构进行初始化

methods:{
 // 用来对左右两侧dom结构进行初始化
 _initScroll (){
 // 实例化 better-scroll 插件,传入要滚动的DOM 对象 
 this.meunScroll=new BScroll(this.$refs.menuWrapper,{
 click:true 
 });
 this.foodScroll=new BScroll(this.$refs.foodsWrapper,{
 click:true
 });
 
 }
 }

说明:vue中更改数据,DOM会跟着做映射,但vue更新DOM是异步的,用 $nextTick ()来确保Dom变化后能调用到_initScroll()方法。调用_initScroll()方法能计算内层ul的高度,当内层ul的高度大于外层wrapper的高度时,可以实现滚动。

此时俩侧可以分别滚动了!

(4) 实现左右联动

原理:我们计算出右侧实时变化的y值,落到哪一个区间,我们就显示那一个区间。首先我们要计算整体区间的一个高度,然后分别计算第一个区间的高度,第二个区间的高度,以此类推。然后将区间数存入一个定义好的数组。当我们在滚动的时候实时拿到y轴的高度,然后对比在哪一个区间,这样我们就会得到一个区间的索引值去对应左侧的菜品类别,最后我们用一个vue的class去绑定高亮文本。

1.定义一个方法在 _initScroll 下面,作为计算高度的方法叫做_calculateHeight () ,再定义一个listHeight:[]数组,存放获取到的每一块foods类的高度。然后通过给每个li 定义类名来供js 选择 从而计算出高度存放到listHeight数组里。

// 通过 方法 计算foods内部每一个块的高度,组成一个数组listHeight。
 // 每个li 定义一个类food-list-hook 通过获取该类 来计算 每一块的高度 存到数组listHeight里
 _calculateHeight (){
 // 获取 li 通过food-list-hook
 let foodList=this.$refs.foodsWrapper.querySelectorAll(".food-list-hook");
 let height=0;// 初始化高度
 this.listHeight.push(height) // 把第一个高度存入数组
 //通过循环foodList下的dom结构,将每一个li的高度依次送入数组
 for(let i = 0 ,l = foodList.length ; i < l ; i++){
 let item=foodList[i]; //每一个item都是刚才获取的food的每一个dom
 height += item.clientHeight; //获取每一个foods内部块的高度
 this.listHeight.push(height) // 将获取的值存放到数组里
 }
 
 }

2.我们获取到区间高度数组后,我们要实时获取到右侧的y值,和左侧的索引值做一个对比,定义一个scrollY变量用来存放实时获取的y值。bs插件为我们提供了一个实时获取y值的方法,我们在初始化this.foodScroll的时候加一个·属性probeType: 3,其作用就是实时获取y值,相当于探针的作用。

goods: [],// goods json 数组
listHeight: [],// 存放 foods 内部的每一块的高度
scrollY:0
this.foodScroll=new BScroll(this.$refs.foodsWrapper,{
 click:true,
 //探针作用,实时监测滚动位置
 probeType: 3
 });

3.我们再添加一个方法this.foodScroll.on('scroll',(pos) => {}),作用是实时滚动的时候把获取到的位置给暴露出来。代码如下。

//结合BScroll的接口使用,监听scroll事件(实时派发的),并获取鼠标坐标,当滚动时能实时暴露出scroll
 this.foodScroll.on("scroll",(pos) =>{ // 回调函数
 //scrollY接收变量 
 this.scrollY=Math.abs(Math.round(pos.y)) //滚动坐标会出现负的,并且是小数,所以需要处理一下,实时取得scrollY
 // console.log(pos.y)
 })

4.定义一个计算属性computed,获取到food滚动区域对应的menu区域的子块的索引i值,从而定位到左侧边栏的位置。

computed:{
 currentIndex (){ //计算到达哪个区域的区间的时候的对应的索引值
 // 利用 listHeight 存放 每一块 对应的高度
 for (let i=0,l=this.listHeight.length; i<l ; i++){
 let menuHeight_fir = this.listHeight[i] // 当前menu 子块区域的 高度
 let menuHeight_sec = this.listHeight[i + 1] // 下一个menu 子块区域的 高度
 // 当滑到底部时,menuHeight_sec 为 underfined,
 // 需要确定滑到俩个高度区间 
 if( !menuHeight_sec || (this.scrollY > menuHeight_fir && this.scrollY < menuHeight_sec) ){
 return i;
 }
 }
 },
 
 }

获取到i后,,然后通过设置一个class来做样式切换变化 :class="{'current':currentIndex === index}" ,当currentIndex和menu-item对应的index相等时,设置current的样式。这样就可以实现左右联动了。

<li v-for=&#39;(item,index) in goods&#39; class="menu-item" :class="index === currentIndex?&#39;menu-item-selected&#39;:&#39;menu-item&#39;">
...

在样式里提前设好 选中和正常的样式

5.最后实现左侧点击的功能。在左侧的li下绑定一个selectMenu的点击事件,并传入索引值,这样我们就可以知道点击的是哪一个li

<li v-for=&#39;(item,index) in goods&#39; class="menu-item" @click="selectMenu(index,$event)" :class="index === currentIndex?&#39;menu-item-selected&#39;:&#39;menu-item&#39;">
...
selectMenu (index, event){ // 点击左侧 ,右侧响应
 this.foodScroll.scrollTo(0, -this.listHeight[index], 300)
 }
scrollTo(x, y, time, easing)
//滚动到某个位置,x,y 代表坐标,time 表示动画时间,easing 表示缓动函数
scroll.scrollTo(0, 500)

参考: vue使用 better-scroll的参数和方法

6.关于在selectMenu中点击事件

在selectMenu中点击,在pc界面会出现两次事件,在移动端就只出现一次事件的问题

原因 : better-scroll 会监听事件(例如touchmove,click之类),并且阻止默认事件(prevent stop),并且他只会监听移动端的,pc端的没有监听

在pc页面上 better-scroll 也派发了一次click事件,原生也派发了一次click事件

// better-scroll 的事件,有_constructed: true
MouseEvent {isTrusted: false, _constructed: true, screenX: 0, screenY: 0, clientX: 0…}
//pc的事件
MouseEvent {isTrusted: true, screenX: -1867, screenY: 520, clientX: 53, clientY: 400…}

解决 : 针对better-scroll 的事件,有_constructed: true,所以做处理,return掉非better-scroll 的事件

selectMenu(index, event){
 if (!event._constructed) { //去掉自带的click事件点击,即pc端直接返回
 return;
 }
 let foodList=this.$refs.foodsWrapper.querySelectorAll(".food-list-hook"); // 获得监听元素
 let el = foodList[index]; // 获得 当前 监听元素的高度 
 this.foodScroll.scrollToElement(el, 300); //类似jump to的功能,通过这个方法,跳转到指定的dom
 }

goods 组件到此差不多了!

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用原生JavaScript实现放大镜效果

在nodejs中通过redis作为缓存实现的封装缓存类

Vue Socket.io源码详细分析

vue中调用methods的方法

Vue组件通信(详细教程)

以上是Vue框架中有關goods組件開發的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn