39-StorageSync读写缓存渲染浏览记录页面

153 阅读4分钟

将浏览记录缓存并渲染到个人页面

在详情页中进行存储,只要用户进入过详情页,就表示用户观看过这条新闻

代码逻辑是:只要用户进入详情页,就把详情页本身在进行网络数据获取时拿到的缩略图、标题、作者、cid、sid等信息保存到一个提前写好的函数saveHistory中,保存这些信息就是通过在这个函数中调用StorageSync方法完成的

在网络请求方法中调用saveHistory方法,这样,在一进入页面,进行网络请求的同时,相关信息就被保存在内存中了

    methods:{
      getDetail(){
        uni.request({
          url:"https://ku.qingnian8.com/dataApi/news/detail.php",
          // 这里url携带的参数就是cid和id,而且data的参数就是一个对象,options也是一个对象
          // options直接放到这里来,就是这里要携带的参数
          data:this.options,
          success: res=>{
            console.log(res);
            // 解决微信小程序图片显示不全的问题
            // res.data.content = res.data.content.replace(/<img/gi,'<img style="max-width: 100%"')
            // 将接口获取到的数据data赋值给自定义变量detail,在页面中就可以拿到值了
            this.detail = res.data;
            // 调用数据缓存方法,一进入页面,就将信息保存到缓存中
            this.saveHistory();
            uni.setNavigationBarTitle({
              title:this.detail.title
            })
          }
        })
      },
      saveHistory(){
        //先判断内存中有没有这个值再保存,否则旧值会被新值覆盖,使用追加值保存
        // 先获取缓存中是否存在historyArr
        // 或运算:一个为真就为真,全假则为假,遇真则停
        // 如果存在historyArr,就向里面追加一个值,没有historyArr就保存一个空数组
        let historyArr = uni.getStorageSync("historyArr") || []
        
        let item={
          id:this.detail.id,
          classid:this.detail.classid,
          picurl:this.detail.picurl,
          title: this.detail.title,
          looktime:Date.now()
        }
        
        historyArr.unshift(item)
        
        uni.setStorageSync("historyArr", historyArr)
      }
    }
  }

随意点击一条信息进入详情页,数据已经被保存好了 image.png

在数据缓存的页面可以看到已经有两条缓存了,现在还有一个问题,重复点击同一条数据会多次保存,所以需要做去重 image.png

视频里没有解决这个问题,接下来就是渲染数据到user页面 user页面的逻辑:写一个方法读取缓存数据,如果有数据,就返回数据,没有就返回一个空数组,然后把读取到的值保存到data,在onload中调用这个方法,一进入页面就调用这个方法,最后是在标签中拿到data里的数据,渲染在页面中

<template>
  <view class="user">
    <view class="top">
      <image src="../../static/images/history.png" mode=""></image>
      <text>浏览历史</text>
    </view>
    <view class="content">
      <view class="row" v-for="item in listArr">
        <newsStyle :item="item" @click.native="goDetail"></newsStyle>
      </view>
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        listArr:[]
      };
    },
    onLoad() {
      this.getData()
    },
    methods:{
      goDetail(){
        uni.navigateTo({
          url:"/pages/detail/detail"
        })
      },
      // 获取缓存浏览记录
      getData(){
        // 如果缓存中有数据就读取出来,如果没有就返回一个空数组
        let hisArr = uni.getStorageSync("historyArr") || []
        this.listArr = hisArr
      }
    }
  }
</script>

<style lang="scss">
.user{
  .top{
    padding: 50rpx 0;
    background: #f8f8f8;
    color: #555;
    display: flex;
    // 使图标和文字垂直显示
    flex-direction: column;
    // 下面两句使图标和文字居中显示
    justify-content: center;
    align-items: center;
    image{
      width: 150rpx;
      height: 150rpx;
    }
    .text{
      font-size: 38rpx;
      color: #555;
      padding: 20rpx;
    }
  }
}
</style>

image.png

现在点击新闻是不能跳转的,因为还没有传参,在点击事件中传参,然后在跳转方法里接收参数

<template>
  <view class="user">
    <view class="top">
      <image src="../../static/images/history.png" mode=""></image>
      <text>浏览历史</text>
    </view>
    <view class="content">
      <view class="row" v-for="item in listArr">
        <newsStyle :item="item" @click.native="goDetail(item)"></newsStyle>
      </view>
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        listArr:[]
      };
    },
    onLoad() {
      this.getData()
    },
    methods:{
      goDetail(item){
        uni.navigateTo({
          url:`/pages/detail/detail?cid=${item.classid}&id=${item.id}`
        })
      },
      // 获取缓存浏览记录
      getData(){
        // 如果缓存中有数据就读取出来,如果没有就返回一个空数组
        let hisArr = uni.getStorageSync("historyArr") || []
        this.listArr = hisArr
      }
    }
  }
</script>

现在在个人中心点击浏览记录,就又能进入详情页了

处理findindex索引数据去重和优化细节

无论在首页点的新闻,还是在个人中心点的新闻,只要点进去详情页,就会被保存,这样就会重复多次的保存同一条新闻

处理逻辑:详情页里的文章保存了id,这是这篇文章的唯一标识符,保存在storage里的数据也保存了id,可以通过对比id来判断这一篇文章有没有被保存,如果比对出来了,证明已经被保存过了,那就把原本保存的那一条新闻删掉,然后再把新的数据追加进去,还有观看时间也不一样,也可以通过比对观看时间来判断

        // 判断获取到的值有没有被保存过,如果有,就删掉之前的,保存新的
        // 通过findIndex来找是否数组中包含某个值
        // findindex返回值:比对成功就返回比对值在数组中的索引,没有比对成功就返回-1
        let index=historyArr.findIndex(i=>{
          return i.id==this.detail.id
        })
        // 开始判断:如果这个值之前被保存过,就splice删掉他
        // 大于0表示有值,等于0,是因为数组的第一个元素索引值是0
        //如果这一条新闻之前保存过,就删掉他
        // 接下来就是按代码顺序正常在内存中插入这条数据进行保存
        //如果这一条新闻之前没有保存过,findIndex返回值是-1,不走这个判断
        // 会自动按正常代码流程保存这一条数据
        if(index>=0){
          historyArr.splice(index, 1)
        }

现在就实现了去重detail页面全部代码如下:

<template>
  <view class="detail">
    <view class="title">{{detail.title}}</view>
    <view class="info">
      <view class="author">编辑: {{detail.author}}</view>
      <!-- <view class="time">发布日期: {{detail.posttime}}</view> -->
      <uni-dateformat :date="detail.posttime*1000" format="yyyy/MM/dd hh:mm:ss"></uni-dateformat>
    </view>
    <view class="content">
      <!-- <view v-html="detail.content"></view> -->
      <rich-text :nodes="detail.content"></rich-text>
    </view>
    <view class="description">
      声明:本站的内容均采集自腾讯新闻,如果侵权请联系xxx进行整改删除,本站进行了内容采集不代表本站及作者观点,若有侵权请及时联系管理员,谢谢支持
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        options: null,
        detail:{}
      };
    },
    onLoad(e) {
      this.options = e;
      // 在这里调用获取网络数据的方法,一进去页面就能执行
      this.getDetail();
    },
    methods:{
      getDetail(){
        uni.request({
          url:"https://ku.qingnian8.com/dataApi/news/detail.php",
          // 这里url携带的参数就是cid和id,而且data的参数就是一个对象,options也是一个对象
          // options直接放到这里来,就是这里要携带的参数
          data:this.options,
          success: res=>{
            console.log(res);
            // 解决微信小程序图片显示不全的问题
            // res.data.content = res.data.content.replace(/<img/gi,'<img style="max-width: 100%"')
            // 将接口获取到的数据data赋值给自定义变量detail,在页面中就可以拿到值了
            this.detail = res.data;
            // 调用数据缓存方法,一进入页面,就将信息保存到缓存中
            this.saveHistory();
            uni.setNavigationBarTitle({
              title:this.detail.title
            })
          }
        })
      },
      saveHistory(){
        //先判断内存中有没有这个值再保存,否则旧值会被新值覆盖,使用追加值保存
        // 先获取缓存中是否存在historyArr
        // 或运算:一个为真就为真,全假则为假,遇真则停
        // 如果存在historyArr,就向里面追加一个值,没有historyArr就保存一个空数组
        let historyArr = uni.getStorageSync("historyArr") || []
        
        let item={
          id:this.detail.id,
          classid:this.detail.classid,
          picurl:this.detail.picurl,
          title: this.detail.title,
          looktime:Date.now()
        }
        // 判断获取到的值有没有被保存过,如果有,就删掉之前的,保存新的
        // 通过findIndex来找是否数组中包含某个值
        // findindex返回值:比对成功就返回比对值在数组中的索引,没有比对成功就返回-1
        let index=historyArr.findIndex(i=>{
          return i.id==this.detail.id
        })
        // 开始判断:如果这个值之前被保存过,就删掉他
        // 之所以要等于0,是因为数组的第一个元素索引值是0
        //如果这一条新闻之前保存过,就删掉他
        // 剩下的,就是按代码顺序正常在内存中插入这条数据进行保存
        //如果这一条新闻之前没有保存过,findIndex返回值是-1,不走这个判断
        // 会自动按正常代码流程保存
        if(index>=0){
          historyArr.splice(index, 1)
        }
        
        // 将值保存到item中
        historyArr.unshift(item)
        
        uni.setStorageSync("historyArr", historyArr)
      }
    }
  }
</script>

<style lang="scss">
.detail {
  padding: 30rpx;
  .title {
    font-size: 46rpx;
    color: #333;
  }
  .info {
    background: #f6f6f6;
    padding: 20rpx 20rpx;
    font-size: 25rpx;
    color: #666;
    // 加盒模型,使作者和时间部分左右对齐
    display: flex;
    justify-content: space-between;
    margin: 40rpx 0;
  }
  // 内容部分
  .content {
    padding-bottom: 50rpx;
    [alt]{
      max-width: 100%;
    }
  }
  // 声明部分
  .description {
    background: #fef0f0;
    font-size: 26rpx;
    padding: 20rpx;
    color: #f89898;
    line-height: 1.8em;
  }
}
</style>

但是还有一个问题,个人中心显示的数据之前是在onload中进行加载的,onload这个方法只有在页面刷新时才加载数据,这就出现一个问题,每次点击观看的新闻,如果不刷新页面,直接点到个人中心去看,是没有数据的,要重新在个人中心刷新页面,观看过的数据才会被加载出来

在首页点了其他新闻,进入详情页之后,退出来,点个人,发现还是原来的数据,就是因为onload的原因,在user页面把onload方法改成onshow即可,这样每次一进入个人页面,浏览过的新闻就会在onshow中被加载出来 image.png

<template>
  <view class="user">
    <view class="top">
      <image src="../../static/images/history.png" mode=""></image>
      <text>浏览历史</text>
    </view>
    <view class="content">
      <view class="row" v-for="item in listArr">
        <newsStyle :item="item" @click.native="goDetail(item)"></newsStyle>
      </view>
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        listArr:[]
      };
    },
    onShow() {
      this.getData()
    },
    methods:{
      goDetail(item){
        uni.navigateTo({
          url:`/pages/detail/detail?cid=${item.classid}&id=${item.id}`
        })
      },
      // 获取缓存浏览记录
      getData(){
        // 如果缓存中有数据就读取出来,如果没有就返回一个空数组
        let hisArr = uni.getStorageSync("historyArr") || []
        this.listArr = hisArr
      }
    }
  }
</script>

还有一个小问题,没有必要把用户浏览的新闻无限制的全部存到缓存中,存个10条就够了,在detail页面中,把存储的数据截取前10条出来保存就行了

//用户浏览记录没有必要全部保存,只保存最近的10条数据就够了
historyArr=historyArr.splice(0,10)

detail页面的全部代码:

<template>
  <view class="detail">
    <view class="title">{{detail.title}}</view>
    <view class="info">
      <view class="author">编辑: {{detail.author}}</view>
      <!-- <view class="time">发布日期: {{detail.posttime}}</view> -->
      <uni-dateformat :date="detail.posttime*1000" format="yyyy/MM/dd hh:mm:ss"></uni-dateformat>
    </view>
    <view class="content">
      <!-- <view v-html="detail.content"></view> -->
      <rich-text :nodes="detail.content"></rich-text>
    </view>
    <view class="description">
      声明:本站的内容均采集自腾讯新闻,如果侵权请联系xxx进行整改删除,本站进行了内容采集不代表本站及作者观点,若有侵权请及时联系管理员,谢谢支持
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        options: null,
        detail:{}
      };
    },
    onLoad(e) {
      this.options = e;
      // 在这里调用获取网络数据的方法,一进去页面就能执行
      this.getDetail();
    },
    methods:{
      getDetail(){
        uni.request({
          url:"https://ku.qingnian8.com/dataApi/news/detail.php",
          // 这里url携带的参数就是cid和id,而且data的参数就是一个对象,options也是一个对象
          // options直接放到这里来,就是这里要携带的参数
          data:this.options,
          success: res=>{
            console.log(res);
            // 解决微信小程序图片显示不全的问题
            // res.data.content = res.data.content.replace(/<img/gi,'<img style="max-width: 100%"')
            // 将接口获取到的数据data赋值给自定义变量detail,在页面中就可以拿到值了
            this.detail = res.data;
            // 调用数据缓存方法,一进入页面,就将信息保存到缓存中
            this.saveHistory();
            uni.setNavigationBarTitle({
              title:this.detail.title
            })
          }
        })
      },
      saveHistory(){
        //先判断内存中有没有这个值再保存,否则旧值会被新值覆盖,使用追加值保存
        // 先获取缓存中是否存在historyArr
        // 或运算:一个为真就为真,全假则为假,遇真则停
        // 如果存在historyArr,就向里面追加一个值,没有historyArr就保存一个空数组
        let historyArr = uni.getStorageSync("historyArr") || []
        
        let item={
          id:this.detail.id,
          classid:this.detail.classid,
          picurl:this.detail.picurl,
          title: this.detail.title,
          looktime:Date.now()
        }
        // 判断获取到的值有没有被保存过,如果有,就删掉之前的,保存新的
        // 通过findIndex来找是否数组中包含某个值
        // findindex返回值:比对成功就返回比对值在数组中的索引,没有比对成功就返回-1
        let index=historyArr.findIndex(i=>{
          return i.id==this.detail.id
        })
        // 开始判断:如果这个值之前被保存过,就删掉他
        // 之所以要等于0,是因为数组的第一个元素索引值是0
        //如果这一条新闻之前保存过,就删掉他
        // 剩下的,就是按代码顺序正常在内存中插入这条数据进行保存
        //如果这一条新闻之前没有保存过,findIndex返回值是-1,不走这个判断
        // 会自动按正常代码流程保存
        if(index>=0){
          historyArr.splice(index, 1)
        }
        
        // 将值保存到item中
        historyArr.unshift(item)
        //用户浏览记录没有必要全部保存,只保存最近的10条数据就够了
        historyArr=historyArr.splice(0,10)
        uni.setStorageSync("historyArr", historyArr)
      }
    }
  }
</script>

<style lang="scss">
.detail {
  padding: 30rpx;
  .title {
    font-size: 46rpx;
    color: #333;
  }
  .info {
    background: #f6f6f6;
    padding: 20rpx 20rpx;
    font-size: 25rpx;
    color: #666;
    // 加盒模型,使作者和时间部分左右对齐
    display: flex;
    justify-content: space-between;
    margin: 40rpx 0;
  }
  // 内容部分
  .content {
    padding-bottom: 50rpx;
    [alt]{
      max-width: 100%;
    }
  }
  // 声明部分
  .description {
    background: #fef0f0;
    font-size: 26rpx;
    padding: 20rpx;
    color: #f89898;
    line-height: 1.8em;
  }
}
</style>

优化:个人页面没有数据时不太好看,在user页面写一个提示文字样式: 再写一个盒子nohistory,装一个图片和暂时没有浏览记录的文字提示,然后要写一个判断,没有数据才让这个盒子显示出来,否则有没有数据他都会显示

<template>
  <view class="user">
    <view class="top">
      <image src="../../static/images/history.png" mode=""></image>
      <text>浏览历史</text>
    </view>
    <view class="content">
      <view class="row" v-for="item in listArr">
        <newsStyle :item="item" @click.native="goDetail(item)"></newsStyle>
      </view>
    </view>
    
    <view class="nohistory" v-if="!listArr.length">
      <image src="../../static/images/nohis.png" mode="widthFix"></image>
      <view class="text">暂无浏览记录</view>
    </view>
  </view>
</template>

给盒子写一个css样式

  .nohistory {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    image{
      width: 400rpx;
    }
    .text {
      font-size: 26rpx;
      color: #888;
    }
  }

image.png