kuan1 / kuan-vue-waterfall

vue3.0瀑布流插件(该仓库没有经过细心测试,建议选择其他更稳定的库)
https://kuan1.github.io/kuan-vue-waterfall/demo
33 stars 13 forks source link

打包后报错 TypeError: Cannot read property '$options' of undefined #1

Closed DarkPage closed 5 years ago

DarkPage commented 5 years ago

开发时没有大问题,build后运行就报错,项目用的Nuxt 2.x版本脚手架

TypeError: Cannot read property '$options' of undefined
    at 92c5a5ba63eff5e6e7f8.js:2
    at prepatch (92c5a5ba63eff5e6e7f8.js:2)
    at S (92c5a5ba63eff5e6e7f8.js:2)
    at 92c5a5ba63eff5e6e7f8.js:2
    at S (92c5a5ba63eff5e6e7f8.js:2)
    at a.__patch__ (92c5a5ba63eff5e6e7f8.js:2)
    at a.t._update (92c5a5ba63eff5e6e7f8.js:2)
    at a.r (92c5a5ba63eff5e6e7f8.js:2)
    at $e.get (92c5a5ba63eff5e6e7f8.js:2)
    at $e.run (92c5a5ba63eff5e6e7f8.js:2)
6403f7553cae77551255.js:1 

[nuxt] Error while initializing app DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method.
    at Object.appendChild (http://localhost:3000/_nuxt/92c5a5ba63eff5e6e7f8.js:2:38643)
    at h (http://localhost:3000/_nuxt/92c5a5ba63eff5e6e7f8.js:2:50778)
kuan1 commented 5 years ago

nuxt服务端渲染的时候不能操作demo,这里有一个同原理的nuxt的demo http://collect.luzhongkuan.cn/uploadList

<template>
  <div>
    <message-form @refresh="fetchData"></message-form>

    <div :style="{height: `${height}px`}" ref="container" class="container message-container">
      <div
        v-for="item in data"
        :key="item.id"
        ref="wrap"
        :style="{opacity: isReady ? 1 : 0.1}"
        class="message-item">
        <div :style="{background: (item.color || 'white')}" class="paper">
          <time class="time">{{ item.create_time }}</time>
          <img class="message-image" v-if="item.url" :src="item.url">
          <p>{{ item.message }}</p>
          <div class="address">{{ item.address }}</div>
          <span class="name">{{ item.name }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import request from '~/assets/request'

  import MessageForm from '~/components/messageForm'

  export default {
    name: 'message',
    head() {
      return {
        title: '卢忠宽的留言'
      }
    },
    data() {
      return {
        height: 0,
        isReady: false,
        itemWidth: 0
      }
    },
    async asyncData() {
      const { data } = await request({
        url: `/api/message`,
        params: { size: 1000 }
      }, {isCache: false})
      return { data: data.content }
    },
    methods: {
      color() {
        const colors = ['#ccf', '#cfc', '#fcc', '#ffc', '#cff', '#fcf']
        return colors[Math.floor(Math.random() * colors.length)]
      },
      setColor() {
        this.data.forEach(item => item.color = this.color())
        this.data = [...this.data]
      },
      allReady() {
        const { wrap } = this.$refs
        const len = wrap.length
        const promiseMap = []
        for (let i = 0; i < len; i++) {
          const el = wrap[i]
          promiseMap.push(isReady(el))
        }
        return Promise.all(promiseMap)
      },
      getColNum() {
        // 获取列数,设置容器宽度
        const { container, wrap } = this.$refs
        const conW = container.clientWidth
        const itemW = wrap[0].clientWidth
        const colNum = ~~(conW / itemW)
        container.style.width = `${(colNum * itemW) || itemW}px`
        this.itemWidth = itemW
        return colNum
      },
      setPos(hMap) {
        const { wrap } = this.$refs
        const len = wrap.length
        for (let i = 0; i < len; i++) {
          // 获取最小高度对应组下标
          const minHeight = Math.min(...hMap)
          let minIndex = hMap.findIndex((item) => item === minHeight)
          if (minIndex === -1) minIndex = 0

          // 设置item位置
          const el = wrap[i]
          hMap[minIndex] += el.clientHeight
          el.style.left = `${this.itemWidth * minIndex}px`
          el.style.top = `${minHeight}px`

          // 设置最外层容器高度
          this.height = Math.max(...hMap)
        }
      },
      initWaterFall() {
        this.$nextTick(async () => {
          // 没有数据直接返回
          if (!this.data.length) return
          // 再次获取宽度的时候,可以获取自适应宽度
          this.$refs.container.removeAttribute('style')
          // 等待图片加载
          await this.allReady()
          this.isReady = true
          // 设置容器宽度
          const colNum = this.getColNum() || 1
          // 获取默认各组高度
          const hMap = getDefaultHeightMap(colNum)
          this.setPos(hMap)
        })
      },
      async fetchData() {
        const { data } = await request({
          url: `/api/message`
        }, {isCache: false})
        this.data = []
        this.$nextTick(() => {
          this.data = data.content
          this.setColor()
          this.initWaterFall()
        })
      },
      resize() {
        debounce(() => {
          this.initWaterFall()
        })()
      }
    },
    mounted() {
      // 设置默认随机颜色
      this.setColor()
      // 初始化瀑布流
      this.initWaterFall()
      if (typeof window !== 'undefined') {
        window.addEventListener('resize', this.resize)
      }
    },
    beforeDestroy() {
      if (typeof window !== 'undefined') {
        window.removeEventListener('resize', this.resize)
      }
    },
    components: {
      MessageForm
    }
  }

  // 等待图片加载完成,获取高度(在resize的时候等待图片加载),只会判断每个waterFallItem组件中,只能放一张图片
  function isReady(el) {
    return new Promise(resolve => {
      let img
      if (el.tagName === 'IMG') {
        img = el
      } else {
        img = el.querySelector('img')
      }
      if (!img) {
        resolve()
      } else if (img.complete) {
        resolve()
        return
      }
      img.onload = resolve
      img.onerror = resolve
    })
  }

  // 获取各组默认高度 [0, 0, 0 ...]
  function getDefaultHeightMap(colNum = 1, arr = []) {
    if (arr.length < colNum) {
      arr.push(0)
      return getDefaultHeightMap(colNum, arr)
    }
    return arr
  }

  // 防抖函数
  function debounce(fn, time = 300) {
    if (typeof fn !== 'function') {
      throw new Error('必须传入一个函数作为参数')
    }
    let timer
    return () => {
      timer && clearTimeout(timer)

      timer = setTimeout(() => {
        fn()
      }, time)
    }
  }

</script>

<style lang="scss" scoped>
  .message-container {
    line-height: 1.3;
    max-width: 1300px;
  }

  .paper {
    width: 320px;
    max-width: 100%;
    min-height: 160px;
    margin: 0 auto;
    box-sizing: border-box;
    padding: 20px 20px 36px;
    box-shadow: 2px 2px 20px #ccc;
  }

  .paper:hover {
    box-shadow: 2px 2px 40px #bbb;
  }

  .time {
    font-size: 14px;
    line-height: 24px;
  }

  p {
    font-size: 16px;
    line-height: 22px;
    margin: 20px 0;
    min-height: 20px;
    text-indent: 1em;
  }

  .address {
    font-size: 14px;
    color: #999;
    line-height: 14px;
  }

  .name {
    float: right;
    font-size: 14px;
  }

  .message-item {
    left: 0;
    top: 0;
    position: absolute;
    transition: left .3s, right .3s, opacity .3s;
    padding: 20px;
    box-sizing: border-box;
  }

  .message-image {
    width: 100%;
  }

  @media (max-width: 350px) {
    .message-item {
      max-width: 320px;
    }
  }
</style>