移动端的vant学习2

348 阅读3分钟

开篇首先感谢我的师傅,谆谆教诲,工作能遇到这么好的师傅,真的很幸运~🙏

1.对象拷贝问题

接口数据的要求,要求传一个数组,数组只有id,seq
并且过滤掉数组中推荐的对象 截屏2022-08-16 下午6.24.27.png 进行数组对象的转换

async addChannel(obj) {
      // 过滤掉推荐对象
      const newarry = this.userChannelList.filter((item) => item.id !== 0);
      // 接口规范
      newarry.forEach((item, index) => {
      // 删除对象
        delete item.name;
        item.seq = index;
      });
      const data = {
        channels: this.newarry,
      };
      const res = await updateChannelsAPI(data);
      if (res.status >= 200 && res.status < 300) {
        this.userChannelList.push(obj);
        Notify({ type: "success", message: "新增频道成功~" });
      }
    },

截屏2022-08-16 下午6.28.38.png

为什么点击新增名字没有了?

- 上面的代码出了问题,新增时名字被删除了
- 原因:所有数组里的对象,都是同一个内存地址,影响到channelEdit.vue中的对象
- 解决方案:需要拷贝出来一份不使用原数组的对象

// 过滤掉推荐对象
const newarry = this.allChannelList.filter((obj) => obj.id !== 0);
      // 浅拷贝
      const theNewarry = newarry.map((obj, index) => {
        const newObj = { ...obj };
        delete newObj.name; //删除名称
        newObj.seq = index;
        return newObj;
      });
      const data = {
        channels: theNewarry,
      };

2.编辑状态初始化

<ChannelPopup
        :userList="userChannelList"
        :unCheckList="uncheckChannelList"
        @addChannelEv="addChannel"
        @deleteChannelEv="deleteChannel"
        @closeEv="closePopup"
        ref="editRef"
      ></ChannelPopup>
// 关闭popup页面
    closePopup() {
      this.showPopup = false;
      // 初始化编辑状态
      this.$refs.editRef.isEdit = false;
    },

截屏2022-08-17 上午10.30.29.png

v-moudle的知识点

v-moudle="channelId"
等同于
props:{value:channelId}
自动触发@input事件

3. 输入框自动获取焦点

全局创建自定义指令

<!-- 搜索组件 -->
      <van-search
        placeholder="请输入搜索关键词"
        background="#007BFF"
        shape="round"
        v-fofo
      />
// 全局自定义指令 main.js
const directiveObj = {
  install(Vue) {
    // 全局自定义指令 main.js
    Vue.directive("fofo", {
      inserted(el) {
        const theInput = el.querySelector("input");
        theInput.focus();
      },
    });
  },
};

Vue.use(directiveObj);
  1. 实现输入框自动获取焦点 截屏2022-08-17 下午2.57.23.png

  2. 注意自定义函数绑定的位置应该在合适的标签中

<textarea placeholder="友善评论、理性发言、阳光心灵" v-fofo></textarea>

出现如下问题 截屏2022-08-22 下午6.09.49.png

解决,注意自定义指令的位置是否获取正确

//知识点:原生DOM.nodeName 拿到标签名(注意:大写的字符串)

const directiveObj = {
  install(Vue) {
    // 全局自定义指令 main.js
    Vue.directive("fofo", {
      inserted(el) {
        if (el.nodeName === "TEXTARES" || el.nodeName === "INPUT") {
          el.focus();
        } else {
          // 如果是在自身绑定的话就尝试往里获取一下
          const theInput = el.querySelector("input");
          const theTextare = el.querySelector("textarea");
          if (theInput) theInput.focus();
          if (theTextare) theTextare.focus();
        }
      },
    });
  },
};

4.输入框防抖

  • 什么是防抖 --- 当一个动作连续触发,只执行最后一次请求

  • 什么是节流 --- 一个动作在一段时间内只执行一次请求

防抖:

data() {
    return {
      keyword: "", // 搜索关键字
      timer: null, // 防抖定时器
    };
  },
inputFn() {
      // 输入框内容实时改变触发事件的方法
      // 防抖:延时执行逻辑代码,事件再次触发时,清除上一个定时器
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        if (this.keyword.length === 0) return;
        console.log(this.keyword);
      }, 1000);
    },

5.关键字高亮

关键字高亮如何做?

用replace方法+正则+文字替换span带样式的标签

截屏2022-08-17 下午6.02.04.png

截屏2022-08-17 下午6.02.53.png

结果:

截屏2022-08-17 下午6.05.11.png

修改为v-html后即可获得正确的样式,解析成标签才能成功

截屏2022-08-17 下午6.07.44.png

截屏2022-08-17 下午6.07.22.png

而正确的做法其实是如下所示,在获取到替换的值只改变颜色,创建一个正则对象进行全局替换和大小写不区分

keywordColor(searchMsg, keyword) {
      /**
       * 使用正则表达式进行全文匹配关键词
       * ig : 表示 全文查找 ,忽略大小写
       *  i : 忽略大小写
       *  g : 全文查找
       *
       * 使用字符串的replace方法进行替换
       * stringObject.replace('被替换的值',替换的值)
       **/
      if (!searchMsg) return;
      const reg = new RegExp(keyword, "ig");
      return searchMsg.replace(reg, (match) => {
        return `<span style="color:red;">${match}</span>`;
      });
    },

基本的就已经完成了,如果你想更好的复用的话可以封装起来使用~

截屏2022-08-18 上午9.32.37.png

好啦,最终就实现完成了~

6.本地存储 (保存历史记录)

因为浏览器本地只能存储字符串,所以存储的时候应该转换为字符串,取出来的时候需要转换为数组,配合监听器使用

坑:路由跳转在watch之前执行,所以应该给路由跳转一个setTimeOut包裹,eventLoop,异步任务的问题

数组去重,使用Set类数组转换实现(如果是对象就是比较对象的内存地址)

 data() {
    return {
      keywordColor,
      keyword: "", // 搜索关键字
      timer: null, // 防抖定时器
      searchList: [], // 关键字列表
      history: JSON.parse(localStorage.getItem("his")) || [], // 搜索历史
    };
  },
// 监听历史记录这个数组的变化
 watch: {
    history: {
      deep: true,
      handler() {
        // 数组去重 类数组转为真实数组
        const theSet = new Set(this.history);
        const arry = Array.from(theSet);
        // 保存在本地
        localStorage.setItem("his", JSON.stringify(arry));
      },
    },
  },
// 点击跳转路由
toSearchResult(keyword) {
      // 防止点击历史记录而将历史记录保存到localstorage
      const index = this.history.findIndex((item) => item === keyword);
      if (index < 0 && keyword.length > 0) {
        // 不在历史记录里面就数组第一个元素前存储
        this.history.unshift(keyword);
      }
      // 路由跳转
      setTimeout(() => {
        this.$router.push(`/searchResult/${keyword}`);
      }, 0);
    },
// 清空localstorage中指定的数据项
 localStorage.removeItem("his");

7.路由传参

// router.js
{
    path: "/searchResult/:keyword", // 动态路由传参
    component: SearchResult,
  },
  

获取路由参数keyword

// searchResult.vue
this.keyword = this.$route.params.keyword;

7.图片防盗链

image.png

  • 了解什么是图片防盗链
  • 怎么解决该问题

分析

有时候报错403状态码, 发现都是图片路径请求问题

原因

  • http请求头中有一个referrer字段,用来表示发起http请求的源地址信息

image.png

  • 服务器端在拿到这个referrer值后判断请求是否来自本站

    • 若不是则返回403,从而实现图片的防盗链。上面出现403就是因为,请求的是别人服务器上的资源,但把自己的referrer信息带过去了,被对方服务器拦截返回了403

解决

在前端可以通过meta来设置referrer policy(来源策略),referrer设置成no-referrer,发送请求不会带上referrer信息,对方服务器也就无法拦截了

<!-- 解决图片403防盗链问题 -->
<meta name="referrer" content="no-referrer" />

但是如果他们做了其他判断, 我们依旧拿不到此图片

小结

  1. 图片为何会403?

    • 在自己的网页里, img的src地址是别人服务器的, 他们做了限制
  2. 如何解决图片403?

    • 在index.html中, 添加meta信息, 不携带referrer给第三方服务器
    • 但是如果他们做了其他判断, 我们依旧拿不到此图片

8.点击跳转到详情页

@click.native()

一丶@click.native是给组件绑定原生事件 image.png 我的标签 ‘ListCell’ 是子组件引到当前父组件
因为当父组件中引入子组件的时候,当要触发子组件点击事件的时候@click 不生效。
有两种解决方式
1.@click.native
2.在子组件中添加this.$emit ( “事件名” ,value )方法 将子组件的值传到父组件。

怪异盒模型和普通盒模型

content-box:指定盒模型为 W3C 标准模型,设置 border、padding 会增加元素 width与 height 的尺寸,即 border 与 padding 相当于是元素的“殖民地”,元素的“土地”、尺寸会增加,为向外延伸。

border-box:指定盒模型为 IE模型(怪异模式),设置 border、padding 不会影响元素 width 与 height 的尺寸,即 border 与 padding 由元素已设空间转变。即空间还是这个空间,只是将部分空余的地方,转变成了其他空间用法而已,为内部转变。

moment函数

// 封装一个函数 判断时间到现在的天数
import moment from "moment";

export const longTime = (startDay) => {
  return moment().diff(moment(startDay), "day");
};
// html
<van-cell center :title="dateSouce.aut_name" :label="diffDay + `天前`">

// js
import { longTime } from "@/utils/moment";
data() {
    return {
      dateSouce: {}, // 文章详情
    };
  },
  computed: {
    diffDay() {
      return longTime(this.dateSouce.pubdate);
    },

9.滚动页面位置到相应的位置

点击评论按钮滚动到评论列表的第一个,第一条评论到首部

有四种情况

  • 内容很高,评论很少
  • 内容很少,评论很高
  • 内容很少,没有评论
  • 内容很多,没有评论

实现步骤:
点击获取内容的高度,并且点击后滚动scroll-y那么多距离

 toCommentList() {
      // scrollTo里面应该放一个对象,实现动画滚动
      const articleHeight =
        document.querySelector(".article-container").scrollHeight;
      const scrollHeight = articleHeight;
     window.scrollTo({ top: scrollHeight, behavior: "smooth" });
    },

截屏2022-08-23 下午7.15.11.png

截屏2022-08-23 下午7.15.24.png

10.异步事件执行

当点击输入框时输入框获取焦点,当失去焦点后当前输入框组件销毁,点击发布也会失去焦点。

解决:异步事件,点击发布后再销毁组件

截屏2022-08-24 上午10.34.07.png

<!-- 底部添加评论区域 - 2 -->
      <div class="cmt-box van-hairline--top" v-else>
        <textarea
          placeholder="友善评论、理性发言、阳光心灵"
          v-fofo
          @blur="blurFn"
          v-model.trim="commentText"
        ></textarea>
        <van-button
          type="default"
          :disabled="commentText.length === 0"
          @click="sendComment"
          >发布</van-button
        >
      </div>
blurFn() {
      //销毁dom延迟,点击事件先执行后再执行,异步代码
      setTimeout(() => {
        this.isShowComment = true;
      }, 0);
    },