# 混入

vue mixins 官方文档 (opens new window),在阅读本文档前,请对该方法的使用有了解。

# 目录结构

└─ src
   └─ mixins                    # 混入
      ├─ getInfiniteData.js     # 无限滚动加载
      └─ preventScroll.js       # 页面禁止滚动

# getInfiniteData.js 无限滚动加载

export const getInfiniteData = {
  // 初始化一些数据
  data() {
    return {
      loading: false,     // 页面接口请求loading
      noData: false,      // 无数据
      noMoreData: false,  // 无更多数据
      isError: false,     // 接口报错
      list: [],           // 接口返回 data 中的列表数据
      total: 0,           // 列表数据总条数
      page: 1,            // 当前页码
      per_page: 20,       // 每页返回数量
      form: {},           // 查询条件
    };
  },

  computed: {
    //  是否显示加载骨架屏动画
    isShowSkeleton() {
      // 只有接口请求中并且当前为第一页时才显示骨架屏,否则显示页底的 <m-loadmore /> 组件
      return this.loading && this.page === 1;
    },
  },

  mounted() {
    // 调用加载方法,该方法定义在 `v-infinite-scroll="loadMore"` 指令中
    this.loadMore();
  },

  methods: {
    /**
     * @desc 分页获取更多数据
     * @param Function fn 接口请求方法,在 api 文件夹在定义的方法
     * @param Object ...args 接口请求参数,可以未多个,使用...运算符对参数进行分解
     * @return 无返回结果,相关数据已挂载在当前组件的 vue 实例上
    */
    async getData(fn, ...args) {
      // 接口请求失败时阻止继续请求,不然滚动加载会一直触发接口请求
      if (this.isError) return;

      // 设置无更多数据时标志,当前为第一页且无数据时,可以再次触发请求
      if (this.noMoreData && this.page === 1 && this.list.length === 0) {
        this.noMoreData = false;
      }

      // 接口请求中或者无更多数据时,拦截请求
      if (this.loading || this.noMoreData) return;

      // 接口请求标志
      this.loading = true;

      // 请求参数
      let params = {
        page: this.page,
        per_page: this.per_page,
        ...this.form
      };

      /**
       * @desc 接口请求
       * @param ...args 多个请求参数,需要与 api 接口定义一致
       * @param params 请求query中携带参数
       * @return code 状态码 200 为成功
       * @return data 数据列表
       * @return total 数据总条数
      */
      const { code, data, total } = await fn(...args, params);

      // 成功
      if (code === 200) {
        // 是否无数据,当前为第一页且返回数据为空
        if (this.page === 1 && data.length === 0) {
          this.noData = true;
        } else {
          this.noData = false;
        }

        this.isError = false;

        // 返回成功后当前页码+1
        this.page++;

        // 返回的数据追加到原有列表中
        this.list.push(...data);
        this.total = total || 0

        // 返回数据条数小于设置的每页请求条数时,则没有更多数据了
        if (data.length < this.per_page) {
          this.noMoreData = true;
        }
      } else {
        // 失败时设置失败标志位为 true,阻止滚动继续发起请求
        this.isError = true;
      }

      // 渲染完成后隐藏加载动画
      this.$nextTick(() => {
        this.loading = false;
      });
    },
  },
}

# 使用示例

<!-- 页面滚动触底时加载指令 -->
<div v-infinite-scroll="loadMore">
  <!-- 省略列表内容 -->
  <list-item v-for="item in list">列表内容</list-item>
  
  <!-- 加载更多组件 -->
  <m-loadmore :loading="loading" v-if="!isShowSkeleton" />
</div>
// 引入混入内容
import { getInfiniteData } from "@/mixins/getInfiniteData";
// 
export default {
  // 注入混入内容
  mixins: [getInfiniteData],

  methods: {
    // 页面滚动时会调用 loadMore 方法,getData 方法在上面的 mixins 里面定义了
    loadMore() {
      this.getData(request, ...params);
    },
  }
}

# preventScroll.js 解决滚动穿透

该混入是为了解决弹窗页面滚动穿透问题,所谓滚动穿透,即弹窗页面滚动时,弹窗下层的页面也会随着滚动。 如项目中影视评分、评论等弹出层页面。 解决方案是在弹窗展示时, body 元素增加 overflow: hidden 样式,弹窗隐藏时,删除 overflow: hidden 样式。 以下的 overflow-hidden 类名即为该样式的全局名称。

// 解决滚动穿透
export const preventScroll = {
  mounted() {
    this.$preventScroll(true);
  },

  beforeDestroy() {
    this.$preventScroll(false);
  },
}
// 给body元素添加或移除 overflow: hidden 样式
export const preventScroll = function (prevent = true) {
  if (prevent) {
    document.body.classList.add("overflow-hidden");
  } else {
    document.body.classList.remove("overflow-hidden");
  }
}
上次更新: 6/27/2022, 5:48:02 PM
示例展示,因PC端无touch事件,请在手机上进行浏览