在上一篇中說(shuō)了一下踩過(guò)的前三個(gè)坑杭抠,剩下的坑就在這篇中全部搞定吧蚌父。
Vue踩坑實(shí)錄(一)
- Vue-cli .js?.Vue?
- 父組件向子組件傳值
- eslint format
- Vue中使用SCSS
- class綁定與順序
- v-for 子組件DOM的取得
- 子組件向父組件的傳遞
- 數(shù)組的更新方法
Vue中使用SCSS
目前流行的CSS預(yù)出處理大大減少了書(shū)寫(xiě)CSS的不便性姿现,自然這些在Vue中也是可以使用的冰单。
Vue中使用SCSS的方法十分簡(jiǎn)單纹腌,只要在style
標(biāo)簽添加lang="scss"
即可霎终。
<style lang="scss">
.picture-continer {
width: 320px;
height: 360px;
margin: auto;
padding: 40px 40px 0px 40px;
box-sizing: border-box;
border-radius: 1px;
background-color: #fff;
position: absolute;
cursor: pointer;
transform-style: preserve-3d;
transform-origin: 0 50% 0; // 改變變化的原點(diǎn)為x軸原點(diǎn)
transition: left .8s ease-in-out, top .8s ease-in-out, transform .5s;
perspective: 1800px;
h2 {
height: 80px;
margin: 0;
line-height: 80px;
color: #727272;
text-align: center;
font-size: 16px;
}
}
</style>
class綁定與順序
在畫(huà)廊應(yīng)用中對(duì)于圖片翻轉(zhuǎn)的樣式是通過(guò)CSS來(lái)控制,這一點(diǎn)通過(guò)使用Vue的class綁定可以很方便的完成升薯。與React不同莱褒,連判斷語(yǔ)句都不需要。
<template>
<figure class="picture-continer"
:style="{top: imgPos.position.top + 'px', left: imgPos.position.left + 'px', transform:'rotate(' + this.imgPos.rotate + 'deg)'}"
:class="{'is-center':imgPos.isCenter,'is-inverse':imgPos.isInverse}" // 綁定class涎劈,通過(guò)判斷class開(kāi)控制圖片樣式的顯示
@click.stop="pictureAction">
<img :src="picture.src"
:alt="picture.title" />
<h2 class="img-title">{{picture.title}}</h2>
<div class="img-back">
<p>{{picture.desc}}</p>
</div>
</figure>
</template>
因此广凸,在這里就需要注意。如果上面例子中的isCenter
和isInverse
同時(shí)都為true
的時(shí)候蛛枚,此時(shí)添加在標(biāo)簽中class的順序是與書(shū)寫(xiě)順序相同的谅海。
當(dāng)兩個(gè)class中有重復(fù)或者沖突的屬性的時(shí)候,就要根據(jù)CSS的后書(shū)寫(xiě)的屬性生效的規(guī)則來(lái)設(shè)計(jì)class的書(shū)寫(xiě)順序蹦浦。
如果在這邊順序?qū)φ{(diào)的話扭吁,那么is-inverse
是不會(huì)生效的。即頁(yè)面上的圖片不會(huì)翻轉(zhuǎn)盲镶。
.is-center {
transform: rotate(0deg);
z-index: 11;
}
.is-inverse {
transform: translate(320px) rotateY(180deg);
}
v-for 子組件DOM的取得
Vue中提供了v-for
指令能很方便的循環(huán)標(biāo)簽智末。在這次的應(yīng)用中,圖片和導(dǎo)航條都是通過(guò)循環(huán)單個(gè)組件生成的徒河。
<template>
<div id="container"
class="stage">
<!-- 圖片區(qū)域 -->
<section class="img-sec">
<picture v-for="(picture, index) in pictureList"
:key="index"
:index="index"
:picture="picture"
:img-pos="imgArrangeList[index]"
ref="pictureList"></picture>
</section>
<!-- 導(dǎo)航條區(qū)域 -->
<section class="nav-sec">
<navbar v-for="(picture, index) in pictureList"
:key="index"
:index="index"
:img-pos="imgArrangeList[index]"></navbar>
</section>
</div>
</template>
雖然說(shuō)Vue主要通過(guò)數(shù)據(jù)驅(qū)動(dòng)的方式來(lái)進(jìn)行操作,不過(guò)還是會(huì)遇上要直接操作DOM的時(shí)候送漠。
這個(gè)時(shí)候可以通過(guò)this.$el
來(lái)獲取DOM的實(shí)體元素顽照。
對(duì)于子組件來(lái)說(shuō),可以在mounted
方法中獲取闽寡。
methods: {
setStageSize: function (sizeObj) {
var stage = this.$el; // 這里獲取實(shí)體DOM
stage.style.width = sizeObj.width + 'px';
stage.style.height = sizeObj.height + 'px';
},
// 省略一些代碼
mounted() {
this.pictureList = imgList;
this.initImgArrangeList(this.pictureList);
this.setStageSize(stageOpt); // 這個(gè)方法用到了實(shí)體DOM元素
// 省略一些代碼
},
// 省略一些代碼
}
注意點(diǎn):
其實(shí)如果按照React那一版(請(qǐng)參照React 圖片畫(huà)廊 踩坑筆記)對(duì)于圖片組件大小的設(shè)定代兵,實(shí)際上是通過(guò)element.scrollWidth
和element.scrollHeight
的方式來(lái)設(shè)定的。由于React中爷狈,這些是寫(xiě)在render函數(shù)中的植影,因此可以通過(guò)ReactDOM.find()
的方法來(lái)獲取。
而在Vue中涎永,如果采用類似的方法思币,在mounted
或者updated
的$nextTick
方法中來(lái)獲取DOM元素來(lái)進(jìn)行設(shè)定的話鹿响,會(huì)造成死循環(huán)。
對(duì)此的情況谷饿,應(yīng)該是Vue在對(duì)子組件進(jìn)行渲染前需要獲取到實(shí)體DOM惶我,而此時(shí)實(shí)體DOM并沒(méi)有生成,因此造成死循環(huán)博投。(個(gè)人的理解绸贡,有錯(cuò)誤的地方還望指正)
所以最后的解決方法是修改數(shù)據(jù)結(jié)構(gòu),添加了圖片組件的寬高信息毅哗,使得Vue在渲染前就能獲取到從而成功渲染听怕。(其實(shí)本來(lái)在CSS中也是寫(xiě)死的寬高)
子組件向父組件的傳遞
在上一篇中提到了Vue通過(guò)props
屬性來(lái)向子組件傳值。自然子組件也需要通過(guò)一定的方法向父組件進(jìn)行通信虑绵。在React中尿瞭,通過(guò)向子組件傳入一個(gè)回調(diào)函數(shù)的方法(即子組件實(shí)際調(diào)用的仍是父組件的方法)來(lái)解決這個(gè)問(wèn)題。
// 省略代碼
// 利用rearrange函數(shù)
// 讓被點(diǎn)擊的圖片劇中
center(index) {
return function() {
this.rearrange(index);
}.bind(this);
}
// 省略代碼
// 向子組件傳遞事件
// 省略代碼
PictureList.push( < Picture imgData = {imgData} key = {index} imgPos = {this.state.imgArrangeList[index]}
inverse = {this.inverse(index)}
center = {this.center(index)} // 往子組件中傳遞 center 事件
ref = {'imgData' + index} />);
// 省略代碼
在子組件中通過(guò)this.props.center()
來(lái)調(diào)用蒸殿。
// 省略代碼
// 點(diǎn)擊函數(shù)
handleClick = (event) => {
if (this.props.imgPos.isCenter) {
this.props.inverse();
} else {
this.props.center();
}
event.stopPropagation();
event.preventDefault();
}
// 省略代碼
而在Vue中通過(guò)使用EventBus來(lái)進(jìn)行事件的傳遞筷厘,類似于設(shè)計(jì)模式中的觀察者模式。其中EventBus就是一個(gè)全局的Vue實(shí)例宏所,在子組件中通過(guò)$emit
方法進(jìn)行事件的發(fā)射酥艳,而在父組件中通過(guò)v-on
進(jìn)行事件綁定。
子組件中通過(guò)emit
進(jìn)行的事件設(shè)定爬骤。
// 省略代碼
pictureAction: function () {
if (this.$props.imgPos.isCenter) {
// 通過(guò)$emit 來(lái)設(shè)置事件充石,可以傳入對(duì)應(yīng)的參數(shù)
bus.$emit('refresh-pic', this.$props.index);
} else {
bus.$emit('do-rearrange', this.$props.index);
}
}
// 省略代碼
父組件通過(guò)v-on
進(jìn)行事件的響應(yīng)。
// 省略代碼
mounted() {
this.pictureList = imgList;
this.initImgArrangeList(this.pictureList);
this.setStageSize(stageOpt);
this.setAreaPosition();
this.rearrange(0);
this.$nextTick(function () {
// 監(jiān)聽(tīng)子組件傳來(lái)的事件
bus.$on('do-rearrange', this.doRearrange);
bus.$on('refresh-pic', this.refreshPic);
});
},
// 省略代碼
可以看出霞玄,無(wú)論Vue還是React骤铃,子組件實(shí)際上最后調(diào)用的還是父組件中的方法。
只不過(guò)在閱讀層面上坷剧,Vue的觀察者模式更容易讀懂一點(diǎn)惰爬。不過(guò)這種通過(guò)EventBus的方法,只適用與小型的程序惫企,當(dāng)項(xiàng)目比較復(fù)雜時(shí)撕瞧,還是應(yīng)該使用Vuex來(lái)進(jìn)行狀態(tài)管理。
數(shù)組的更新方法
Vue最大的特色是數(shù)據(jù)的雙重綁定狞尔。其中對(duì)于數(shù)組丛版,在官方文檔中給出了相關(guān)的注意事項(xiàng)。特別要注意下面的情況偏序,因?yàn)樵跀?shù)組操作中页畦,這樣的用法是最方便也是最容易想到的。
由于 JavaScript 的限制研儒, Vue 不能檢測(cè)以下變動(dòng)的數(shù)組:
當(dāng)你利用索引直接設(shè)置一個(gè)項(xiàng)時(shí)豫缨,例如: vm.items[indexOfItem] = newValue
當(dāng)你修改數(shù)組的長(zhǎng)度時(shí)独令,例如: vm.items.length = newLengt
自然,這一次從React轉(zhuǎn)到Vue的過(guò)程中也遇到了這個(gè)問(wèn)題州胳。并且還是在最關(guān)鍵的圖片重排函數(shù)中记焊。
React版本
// 省略代碼
// 重新布局所有圖片 centerIndex 是居中圖片的索引
rearrange(centerIndex) {
let imgArrangeList = this.state.imgArrangeList;
let AreaPos = this.AreaPos;
let center = AreaPos.center;
let hPosRangeLeftRange = AreaPos.hPosRange.leftRange;
let hPosRangeRightRange = AreaPos.hPosRange.rightRange;
let hPosRangeTop = AreaPos.hPosRange.top;
let vPosRangeX = AreaPos.vPosRange.x;
let vPosRangeTopRange = AreaPos.vPosRange.topRange;
let imgTopArray = [];
let topImgNumber = Math.floor(Math.random() * 2); // 取上測(cè)區(qū)域的圖片數(shù)量
// 取得居中圖片
let centerImgArray = imgArrangeList.splice(centerIndex, 1);
centerImgArray[0].position = center;
centerImgArray[0].rotate = 0; // 中間圖片不需要旋轉(zhuǎn)
centerImgArray[0].isCenter = true;
// 取出上側(cè)圖片的信息
let topImgSpliceIndex = Math.floor(Math.random() * (imgArrangeList.length - topImgNumber));
imgTopArray = imgArrangeList.splice(topImgSpliceIndex, topImgNumber);
// 布局上側(cè)圖片
imgTopArray.forEach(function(img) {
img.position = {
left: getRandomValue(vPosRangeX[0], vPosRangeX[1]),
top: getRandomValue(vPosRangeTopRange[0], vPosRangeTopRange[1])
}
img.rotate = getRandomRange();
img.isCenter = false;
});
// 布局左右兩側(cè)圖片
for(let i = 0, len = imgArrangeList.length, k = len / 2; i < len; i++) {
let hPosRangeTmp = null;
// 取布局的隨機(jī)值
if(i < k) {
hPosRangeTmp = hPosRangeLeftRange;
} else {
hPosRangeTmp = hPosRangeRightRange;
}
// 這一部分在Vue中是不會(huì)進(jìn)行數(shù)據(jù)更新的因此需要做轉(zhuǎn)換
imgArrangeList[i].position = {
left: getRandomValue(hPosRangeTmp[0], hPosRangeTmp[1]),
top: getRandomValue(hPosRangeTop[0], hPosRangeTop[1])
}
imgArrangeList[i].rotate = getRandomRange();
imgArrangeList[i].isCenter = false;
}
// 重新合成數(shù)組
if(imgArrangeList && imgTopArray[0]) {
imgArrangeList.splice(topImgSpliceIndex, 0, imgTopArray[0]);
}
imgArrangeList.splice(centerIndex, 0, centerImgArray[0]);
this.setState({
imgArrangeList: imgArrangeList
});
}
// 省略代碼
Vue版本
// 省略代碼
rearrange: function (centerIndex) {
// 省略代碼
// 布局左右兩側(cè)圖片
for (let i = 0, len = imgArrangeList.length, k = len / 2; i < len; i++) {
// 設(shè)置一個(gè)臨時(shí)變量,其結(jié)構(gòu)與每一個(gè) imgArrangeList[i]相同
let tmpItem = {};
let hPosRangeTmp = null;
// 取布局的隨機(jī)值
if (i < k) {
hPosRangeTmp = hPosRangeLeftRange;
} else {
hPosRangeTmp = hPosRangeRightRange;
}
// 將原來(lái)的方法用Vue中對(duì)應(yīng)的數(shù)組更新方法替代栓撞,為此需要設(shè)置一個(gè)臨時(shí)對(duì)象
// 然后通過(guò)Vue給出的方法進(jìn)行實(shí)現(xiàn)
// imgArrangeList[i].position = {
// left: getRandomValue(hPosRangeTmp[0], hPosRangeTmp[1]),
// top: getRandomValue(hPosRangeTop[0], hPosRangeTop[1])
// }
// imgArrangeList[i].rotate = getRandomRange();
// imgArrangeList[i].isCenter = false;
tmpItem = {
position: {
left: getRandomValue(hPosRangeTmp[0], hPosRangeTmp[1]),
top: getRandomValue(hPosRangeTop[0], hPosRangeTop[1])
},
rotate: getRandomRange(),
isCenter: false,
isInverse: imgArrangeList[i].isInverse
};
this.$set(imgArrangeList, i, tmpItem);
}
//省略代碼
},
// 省略代碼
至于上部和中間的圖片由于本來(lái)就是通過(guò)splice
方法進(jìn)行設(shè)定的遍膜,因此可以在Vue中進(jìn)行正常的更新。同時(shí)也是因此發(fā)現(xiàn)了上面的問(wèn)題的瓤湘。
后記
對(duì)于這個(gè)項(xiàng)目瓢颅,從代碼量上來(lái)說(shuō),Vue和React差距并不大弛说。不過(guò)由于.Vue文件分布相對(duì)清晰一點(diǎn)挽懦,在代碼閱讀方面還是比較容易讀懂的。
此外木人,React和Vue在設(shè)計(jì)思考的角度上信柿,其實(shí)差別還是蠻大的。React在感覺(jué)上更貼合我們一直以來(lái)的開(kāi)發(fā)思路醒第,上手React還是一件比較輕松容易的事情渔嚷。而Vue則是通過(guò)數(shù)據(jù)來(lái)進(jìn)行驅(qū)動(dòng),一切都要以數(shù)據(jù)為重稠曼。剛開(kāi)始的時(shí)候形病,還是挺不習(xí)慣的。不過(guò)在踩過(guò)幾個(gè)坑之后也漸漸開(kāi)始接受起了這種思考方式霞幅。
這一次Vue的踩坑開(kāi)始很愉快的漠吻。之后大概會(huì)學(xué)習(xí)一下全家桶中的Vuex和Vue-router吧。