• vue分类页开发--axios数据获取,localstorage数据缓存


    效果图

    1、首先在app.vue中,给路由添加keep-alive,使其能够被缓存

    2、配置路由 router/index.js

    3、书写各部分组件

    src/pages/category/header.vue

    <template>
        <div class="header">
            <i class="iconfont icon-scan header-left"></i>
            <div class="header-center">搜索框</div>
            <i class="iconfont icon-msg header-right"></i>
        </div>
    </template>
    
    <script>
    export default {
        name:'CategoryHeader'
    }
    </script>
    
    <style lang="scss" scoped>
        .header{
            background-color:rgba(222, 24, 27, 0.9);
            transition:background-color 0.5s;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding:5px 20px;
    
            .iconfont{
                font-size:24px;
                color:#fff;
            }
    
            .header-center{
                flex:1;
            }
        } 
    </style>

    src/pages/category/tab.vue

    <template>
        <div class="tab-container">
            <ul class="tab">
                <li class="tab-item" :class="{'tab-item-active':item.id===curId}" v-for="(item,index) in items" :key="index" @click="switchTab(item.id)">
                    {{item.name}}
                </li>
            </ul>
        </div>
    </template>
    
    <script>
    import {categoryNames} from './config';
    
    export default {
        name:'CategoryTab',
        data(){
            return{
                items:categoryNames,
                curId:""
            }
        },
        created(){
            this.switchTab(this.items[0].id);
        },
        methods:{
            switchTab(id){
                this.curId=id;
                this.$emit("switch-tab",id);//派发事件,传递id
            }
        }
    }
    </script>
    
    <style lang="scss" scoped>
        .tab-container{
            100%;
            height:100%;
            overflow:auto;
        }
      .tab {
         100%;
        height:auto;
    
        &-item {
          height: 46px;
          background-color: #fff;
          border-right: 1px solid #e5e5e5;
          border-bottom: 1px solid #e5e5e5;
          color: #080808;
          font-size: 14px;
          font-weight: bold;
          text-align: center;
          line-height: 46px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
    
          &:last-child {
            border-bottom: none;
          }
        }
    
        &-item-active {
          background: none;
          border-right: none;
          color: #f23030;
        }
      }
    </style>

    src/pages/category/content.vue

    <template>
    <div class="content-wrapper">
        <div class="loading-container" v-if="isLoading">
          <div class="loading-wrapper">
            <me-loading/>
          </div>
        </div>
        <scrollbar ref="scroll" @pull-up="pullUp" @pull-down="pullRefresh">
          <div class="content">
            <div class="pic" v-if="content.banner">
              <a :href="content.banner.linkUrl" class="pic-link">
                <img @load="updateScroll" :src="content.banner.picUrl" class="pic-img">
              </a>
            </div>
            <div class="section" v-for="(section, index) in content.data" :key="index" >
              <h4 class="section-title">{{section.name}}</h4>
              <ul class="section-list">
                <li class="section-item" v-for="(item, i) in section.itemList" :key="i" >
                  <a :href="item.linkUrl" class="section-link">
                    <p class="section-pic" v-if="item.picUrl">
                      <img v-lazy="item.picUrl" class="section-img" />
                    </p>
                    <p class="section-name">{{item.name}}</p>
                  </a>
                </li>
              </ul>
            </div>
          </div>
        </scrollbar>
        <div class="g-backtop-container">
            <me-backtop :visible="backtopVisible" @backtop="backtop" />
        </div>
      </div>
    </template>
    
    <script>
    import {getCategorys} from 'api/category';
    import MeLoading from 'components/loading';
    import Scrollbar from 'components/scroll';
    import MeBacktop from 'components/backtop';
    import storage from 'assets/js/storage.js';
    import {CATEGORY_CONTENT_KEY,CATEGORY_CONTENT_UPDATE_TIME} from './config';
    
    export default {
        name:'CategoryContent',
        components:{
            MeLoading,
            Scrollbar,
            MeBacktop,
        },
        props:{
            curId:{
                type:String,
                default:''
            }
        },
        data(){
            return{
                content:{},
                isLoading:false,
                backtopVisible:false,
            }
        },
        watch:{
            curId(id){
                this.isLoading=true;
                this.getContent(id).then(()=>{
                    this.isLoading=false;
                    this.backtop();//回到顶部
                });
            }
        },
        methods:{
            getContent(id){
              let content=storage.get(CATEGORY_CONTENT_KEY);
              let updateTime;
              const curTime=new Date().getTime();
    
              if(content && content[id]){
                updateTime=content[id].updateTime||0;
                
                if(curTime-updateTime<CATEGORY_CONTENT_UPDATE_TIME){//未超时
                  console.log('从缓存获取');
                  return this.getContentByStorage(content[id]);//从缓存获取
                }else{
                  console.log('从服务器获取');
                  return this.getContentByHTTP(id).then(()=>{//从服务器获取
                    this.updateStorage(content,id,updateTime);//更新缓存
                  });
                }
              }else{
                console.log('从服务器获取');
                return this.getContentByHTTP(id).then(()=>{//从服务器获取
                  this.updateStorage(content,id,curTime);//更新缓存
                });
              }           
            },
            getContentByStorage(content){
              this.content=content.data;
              return Promise.resolve();//返回一个成功的promise对象
            },
            getContentByHTTP(id){
              return getCategorys(id).then(data=>{
                return new Promise(resolve=>{
                  if(data){
                    this.content=data.content;
                    resolve();
                  }
                })              
              })
            },
            updateStorage(content,id,curTime){
              //content=content || {};
              content[id]={};
              content[id].data=this.content;
              content[id].updateTime=curTime;
              storage.set(CATEGORY_CONTENT_KEY,content);
            },
            updateScroll(){
                this.$refs.scroll && this.$refs.scroll.update();
            },
            scrollEnd(translate,swiper,pulling){//下拉刷新结束
                //显示回到顶部按钮
                this.backtopVisible=translate<0 && -translate>swiper.height;//向下拉并且距离大于一屏          
            },
            backtop(){
                this.$refs.scroll && this.$refs.scroll.scrollTop();//回到顶部
            },
            pullUp(end){
              end();
            },
            pullRefresh(end){
              this.getContent(this.curId).then(()=>{
                    this.isLoading=false;
                    this.backtop();//回到顶部
                    end();
              });
            }
        }
    }
    </script>
    
    <style lang="scss" scoped>
        .g-backtop-container{
            position: absolute;
            z-index:1100;
            right:10px;
            bottom:60px;
        }
     .content-wrapper {
        position: relative;
        height: 100%;
      }
    
      .loading-container {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 1190;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: row;
         100%;
        height: 100%;
    
        .mine-loading {
          color: #fff;
          font-size: 14px;
        }
      }
      .loading-wrapper {
         50%;
        padding: 30px 0;
        background-color: rgba(0, 0, 0, 0.4);
        border-radius: 4px;
      }
    
      .content {
        padding: 10px;
      }
    
      .pic {
        margin-bottom: 12px;
    
        &-link {
          display: block;
        }
    
        &-img {
           100%;
        }
      }
    
      .section {
        margin-bottom: 12px;
    
        &:last-child {
          margin-bottom: 0;
        }
    
        &-title {
          height: 28px;
          line-height: 28px;
          color: #080808;
          font-weight: bold;
        }
    
        &-list {
          display: flex;
          flex-wrap: wrap;
          background-color: #fff;
          padding: 10px 10px 0;
        }
    
        &-item {
           (100% / 3);
        }
    
        &-link {
          display: block;
        }
    
        &-pic {
          position: relative;
           80%;
          padding-bottom: 80%;
          margin: 0 auto;
        }
    
        &-img {
          position: absolute;
          top: 0;
          left: 0;
           100%;
          height: 100%;
        }
    
        &-name {
          height: 36px;
          line-height: 36px;
          text-align: center;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
      }
      .g-backtop-container {
        bottom: 10px;
      }
    </style>

    src/pages/category/index.vue

    <template>
        <div class="category">
            <div class="g-header-container">
                <category-header />
            </div>
            <div class="g-content-container">
                <div class="sidebar">
                    <category-tab @switch-tab="getCurId" />
                </div>
                <div class="main">
                    <category-content :curId="curId" />
                </div>
            </div>
        </div>
    </template>
    
    <script>
    import CategoryHeader from './header';
    import CategoryTab from './tab';
    import CategoryContent from './content';
    
    export default {
        name:'Category',
        components:{
            CategoryHeader,
            CategoryTab,
            CategoryContent
        },
        data(){
            return{
                curId:''
            }
        },
        methods:{
            getCurId(id){
                this.curId=id;
            }
        }
    }
    </script>
    
    <style lang="scss" scoped>
        html,body{
            100%;
            height:100%;
        }
        .category {
            overflow:hidden;
            100%;
            height:100%;
            background-color: '#f5f5f5';
        }
    
        .g-content-container {
            100%;
            height:100%;
            display: flex;
        }
        .sidebar {
             80px;
            height: 100%;
        }
        .main {
            flex: 1;
            height: 100%;
        }
    </style>

    4、储存常量和服务器获取来的数据

    src/pages/category/config.js

    export const CATEGORY_CONTENT_KEY='cyymall-category-content-key';
    export const CATEGORY_CONTENT_UPDATE_TIME=1*24*60*60*1000;//1天
    
    export const categoryNames = [
        {
          'name': '热门推荐',
          'id': 'WQR2006'
        },
        {
          'name': '慕淘超市',
          'id': 'WQ1968'
        },
        {
          'name': '国际名牌',
          'id': 'WQ1969'
        },
        {
          'name': '奢侈品',
          'id': 'WQ1970'
        },
        {
          'name': '全球购',
          'id': 'WQ1971'
        },
        {
          'name': '男装',
          'id': 'WQ1972'
        },
        {
          'name': '女装',
          'id': 'WQ1973'
        },
        {
          'name': '男鞋',
          'id': 'WQ1974'
        },
        {
          'name': '女鞋',
          'id': 'WQ1975'
        },
        {
          'name': '内衣配饰',
          'id': 'WQ1976'
        },
        {
          'name': '箱包手袋',
          'id': 'WQ1977'
        },
        {
          'name': '美妆个护',
          'id': 'WQ1978'
        },
        {
          'name': '钟表珠宝',
          'id': 'WQ1979'
        },
        {
          'name': '手机数码',
          'id': 'WQ1980'
        },
        {
          'name': '电脑办公',
          'id': 'WQ1981'
        },
        {
          'name': '家用电器',
          'id': 'WQ1982'
        },
        {
          'name': '食品生鲜',
          'id': 'WQ1983'
        },
        {
          'name': '酒水饮料',
          'id': 'WQ1984'
        },
        {
          'name': '母婴童鞋',
          'id': 'WQ1985'
        },
        {
          'name': '玩具乐器',
          'id': 'WQ1986'
        },
        {
          'name': '医药保健',
          'id': 'WQ1987'
        },
        {
          'name': '计生情趣',
          'id': 'WQ1988'
        },
        {
          'name': '运动户外',
          'id': 'WQ1989'
        },
        {
          'name': '汽车用品',
          'id': 'WQ1990'
        },
        {
          'name': '家居厨具',
          'id': 'WQ1991'
        },
        {
          'name': '家具家装',
          'id': 'WQ1992'
        },
        {
          'name': '礼品鲜花',
          'id': 'WQ1993'
        },
        {
          'name': '宠物生活',
          'id': 'WQ1994'
        },
        {
          'name': '生活旅行',
          'id': 'WQ1995'
        },
        {
          'name': '图书音像',
          'id': 'WQ1996'
        },
        {
          'name': '邮币',
          'id': 'WQ1997'
        },
        {
          'name': '农资绿植',
          'id': 'WQ1998'
        },
        {
          'name': '特产馆',
          'id': 'WQ1999'
        },
        {
          'name': '慕淘金融',
          'id': 'WQ2000'
        },
        {
          'name': '拍卖',
          'id': 'WQ2001'
        },
        {
          'name': '房产',
          'id': 'WQ2008'
        },
        {
          'name': '二手商品',
          'id': 'WQ2002'
        }
      ];

    5、axios获取数据

    src/api/category.js

    import axios from 'axios';
    
    // CancelToken:快速发送多个请求时,取消前面的请求,以最后一个为准
    const CancelToken=axios.CancelToken;
    let cancel;
    
    //获取数据 ajax
    export const getCategorys=(id)=>{
        cancel && cancel("取消了前一次请求");
        cancel=null;
    
        let url='http://www.imooc.com/api/category/content/'+id;
    
        return axios.get(url,{
            cancelToken:new CancelToken(function executor(c){
                cancel=c;
            })
        }).then(res=>{
            
            if(res.data.code===0){
                console.log("获取category成功");
                return res.data;
            }
    
            throw new Error('没有成功获取到数据');
        }).catch(err=>{
            console.log(err);
        });
    }

    6、localstorage操作缓存

    const storage = window.localStorage;
    
    export default {
      set(key, val) {
        if (val === undefined) {
          return;
        }
        storage.setItem(key, serialize(val));
      },
      get(key, def) {
        const val = deserialize(storage.getItem(key));
        return val === undefined ? def : val;
      },
      remove(key) {
        storage.removeItem(key);
      },
      clear() {
        storage.clear();
      }
    };
    
    function serialize(val) {
      return JSON.stringify(val);
    }
    
    function deserialize(val) {
      if (typeof val !== 'string') {
        return undefined;
      }
      try {
        return JSON.parse(val);
      } catch (e) {
        return val || undefined;
      }
    }

    最后补充一下,关于 $nextTick

  • 相关阅读:
    php redis 的使用方法
    各文件系统对单个文件大小的限制
    MySQL优化之COUNT(*)效率
    理解数据库设计范式【转】
    Mysql re-set password, mysql set encode utf8 mysql重置密码,mysql设置存储编码格式
    MBR,boot loader, partition table, backup, recovery, clean 硬盘引导记录,分区表备份,恢复,清空
    LAMP on ubuntu12.04 PHP, Apache2, MySQL, Linux ( with phpmyadmin installed)
    python alembic which comes from SQLalchemy
    Arduino live weather broadcasting 实时天气站
    python schedule processor
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12651154.html
Copyright © 2020-2023  润新知