Vue踩坑實(shí)錄(二)

在上一篇中說了一下踩過的前三個(gè)坑,剩下的坑就在這篇中全部搞定吧。
Vue踩坑實(shí)錄(一)


  1. Vue-cli .js?.Vue?
  2. 父組件向子組件傳值
  3. eslint format
  4. Vue中使用SCSS
  5. class綁定與順序
  6. v-for 子組件DOM的取得
  7. 子組件向父組件的傳遞
  8. 數(shù)組的更新方法

Vue中使用SCSS

目前流行的CSS預(yù)出處理大大減少了書寫CSS的不便性,自然這些在Vue中也是可以使用的。
Vue中使用SCSS的方法十分簡單,只要在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綁定與順序

在畫廊應(yīng)用中對(duì)于圖片翻轉(zhuǎn)的樣式是通過CSS來控制,這一點(diǎn)通過使用Vue的class綁定可以很方便的完成。與React不同,連判斷語句都不需要。

<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,通過判斷class開控制圖片樣式的顯示
          @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>

因此,在這里就需要注意。如果上面例子中的isCenterisInverse同時(shí)都為true的時(shí)候,此時(shí)添加在標(biāo)簽中class的順序是與書寫順序相同的。
當(dāng)兩個(gè)class中有重復(fù)或者沖突的屬性的時(shí)候,就要根據(jù)CSS的后書寫的屬性生效的規(guī)則來設(shè)計(jì)class的書寫順序。
如果在這邊順序?qū)φ{(diào)的話,那么is-inverse是不會(huì)生效的。即頁面上的圖片不會(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)航條都是通過循環(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>

雖然說Vue主要通過數(shù)據(jù)驅(qū)動(dòng)的方式來進(jìn)行操作,不過還是會(huì)遇上要直接操作DOM的時(shí)候。
這個(gè)時(shí)候可以通過this.$el來獲取DOM的實(shí)體元素。
對(duì)于子組件來說,可以在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 圖片畫廊 踩坑筆記)對(duì)于圖片組件大小的設(shè)定,實(shí)際上是通過element.scrollWidthelement.scrollHeight的方式來設(shè)定的。由于React中,這些是寫在render函數(shù)中的,因此可以通過ReactDOM.find()的方法來獲取。
而在Vue中,如果采用類似的方法,在mounted或者updated$nextTick方法中來獲取DOM元素來進(jìn)行設(shè)定的話,會(huì)造成死循環(huán)。
對(duì)此的情況,應(yīng)該是Vue在對(duì)子組件進(jìn)行渲染前需要獲取到實(shí)體DOM,而此時(shí)實(shí)體DOM并沒有生成,因此造成死循環(huán)。(個(gè)人的理解,有錯(cuò)誤的地方還望指正)
所以最后的解決方法是修改數(shù)據(jù)結(jié)構(gòu),添加了圖片組件的寬高信息,使得Vue在渲染前就能獲取到從而成功渲染。(其實(shí)本來在CSS中也是寫死的寬高)


子組件向父組件的傳遞

在上一篇中提到了Vue通過props屬性來向子組件傳值。自然子組件也需要通過一定的方法向父組件進(jìn)行通信。在React中,通過向子組件傳入一個(gè)回調(diào)函數(shù)的方法(即子組件實(shí)際調(diào)用的仍是父組件的方法)來解決這個(gè)問題。

    // 省略代碼
    // 利用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} />);
    // 省略代碼

在子組件中通過this.props.center()來調(diào)用。

    // 省略代碼
        // 點(diǎn)擊函數(shù)
    handleClick = (event) => {
        if (this.props.imgPos.isCenter) {
            this.props.inverse();
        } else {
            this.props.center();
        }
        event.stopPropagation();
        event.preventDefault();
    }
    // 省略代碼

而在Vue中通過使用EventBus來進(jìn)行事件的傳遞,類似于設(shè)計(jì)模式中的觀察者模式。其中EventBus就是一個(gè)全局的Vue實(shí)例,在子組件中通過$emit方法進(jìn)行事件的發(fā)射,而在父組件中通過v-on進(jìn)行事件綁定。
子組件中通過emit進(jìn)行的事件設(shè)定。

// 省略代碼
pictureAction: function () {
      if (this.$props.imgPos.isCenter) {
        // 通過$emit 來設(shè)置事件,可以傳入對(duì)應(yīng)的參數(shù)
        bus.$emit('refresh-pic', this.$props.index);
      } else {
        bus.$emit('do-rearrange', this.$props.index);
      }
    }
// 省略代碼

父組件通過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)聽子組件傳來的事件
      bus.$on('do-rearrange', this.doRearrange);
      bus.$on('refresh-pic', this.refreshPic);
    });
  },
// 省略代碼

可以看出,無論Vue還是React,子組件實(shí)際上最后調(diào)用的還是父組件中的方法。
只不過在閱讀層面上,Vue的觀察者模式更容易讀懂一點(diǎn)。不過這種通過EventBus的方法,只適用與小型的程序,當(dāng)項(xiàng)目比較復(fù)雜時(shí),還是應(yīng)該使用Vuex來進(jìn)行狀態(tài)管理。


數(shù)組的更新方法

Vue最大的特色是數(shù)據(jù)的雙重綁定。其中對(duì)于數(shù)組,在官方文檔中給出了相關(guān)的注意事項(xiàng)。特別要注意下面的情況,因?yàn)樵跀?shù)組操作中,這樣的用法是最方便也是最容易想到的。

由于 JavaScript 的限制, Vue 不能檢測以下變動(dòng)的數(shù)組:
當(dāng)你利用索引直接設(shè)置一個(gè)項(xiàng)時(shí),例如: vm.items[indexOfItem] = newValue
當(dāng)你修改數(shù)組的長度時(shí),例如: vm.items.length = newLengt

自然,這一次從React轉(zhuǎn)到Vue的過程中也遇到了這個(gè)問題。并且還是在最關(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); // 取上測區(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;
        }
        // 將原來的方法用Vue中對(duì)應(yīng)的數(shù)組更新方法替代,為此需要設(shè)置一個(gè)臨時(shí)對(duì)象
        // 然后通過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);
      }

      //省略代碼
    },
// 省略代碼

至于上部和中間的圖片由于本來就是通過splice方法進(jìn)行設(shè)定的,因此可以在Vue中進(jìn)行正常的更新。同時(shí)也是因此發(fā)現(xiàn)了上面的問題的。


后記

對(duì)于這個(gè)項(xiàng)目,從代碼量上來說,Vue和React差距并不大。不過由于.Vue文件分布相對(duì)清晰一點(diǎn),在代碼閱讀方面還是比較容易讀懂的。
此外,React和Vue在設(shè)計(jì)思考的角度上,其實(shí)差別還是蠻大的。React在感覺上更貼合我們一直以來的開發(fā)思路,上手React還是一件比較輕松容易的事情。而Vue則是通過數(shù)據(jù)來進(jìn)行驅(qū)動(dòng),一切都要以數(shù)據(jù)為重。剛開始的時(shí)候,還是挺不習(xí)慣的。不過在踩過幾個(gè)坑之后也漸漸開始接受起了這種思考方式。

這一次Vue的踩坑開始很愉快的。之后大概會(huì)學(xué)習(xí)一下全家桶中的Vuex和Vue-router吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 苗青有點(diǎn)不相信自己的耳朵,無論從身體的高度還是寬度,春生都是略輸一籌,但就是在這并不出眾的體內(nèi),竟然蘊(yùn)藏著如此巨大...
    西嶺布衣閱讀 305評(píng)論 0 3
  • 幾分鐘前,母親發(fā)來視頻,說是想看寶寶,平常說寶寶睡了便不再堅(jiān)持,今日卻是還想看看,后來才說鄰居陳叔叔過世了,說這話...
    瀟瀟成謎閱讀 121評(píng)論 0 0
  • 據(jù)說,現(xiàn)代鐵路兩條鐵軌之間的標(biāo)準(zhǔn)距離是四英尺又八點(diǎn)五英寸。 那依據(jù)是什么呢?來由絕對(duì)顛覆你的認(rèn)知。 因?yàn)樵缙诘蔫F路...
    周愚閱讀 1,525評(píng)論 0 0

友情鏈接更多精彩內(nèi)容