轻量化AppDelegate,针对项目后期越来越臃肿的AppDelegate的方案 集成umeng分享、umeng统计、个推、 bugly等功能
请自行选择集成
TODO:
融云
微信、支付宝支付
使用
Vant提供的 NavBar 导航栏 组件,同时在 NavBar 导航栏 组件标签中使用 Button 按钮 组件,最后进行样式调整即可
-
使用导航栏组件
-
在导航栏组件中插入按钮
- 文本
- 图标
-
样式调整
- 宽高
- 背景色
- 边框
- 文本大小
- 图标大小
<template>
<div class="home-container">
<!-- 顶部导航栏组件 -->
<van-nav-bar class="page-nav-bar">
<van-button class="search-btn" slot="title" type="info" size="small" round icon="search">搜索</van-button>
</van-nav-bar>
</div>
</template>
<script>
export default {
name: 'HomeIndex'
}
</script>
<style scoped lang="less">
.home-container {
/deep/ .van-nav-bar__title {
max-width: unset;
}
.search-btn {
width: 555px;
height: 64px;
background-color: #5babfb;
border: none;
font-size: 28px;
.van-icon {
font-size: 32px;
}
}
}
</style>使用
Vant提供的 Tab 标签页组件 实现区域的开发
-
分析 Tab 标签页组件 的滑动切换使用方式,并应用到页面中
- 通过
v-model绑定当前激活标签对应的索引值,默认情况下启用第一个标签 - 通过
animated属性可以开启切换标签内容时的转场动画 - 通过
swipeable属性可以开启滑动切换标签页
<!-- 频道列表 --> <van-tabs v-model="active" swipeable> <van-tab title="标签 1">内容 1</van-tab> <van-tab title="标签 2">内容 2</van-tab> <van-tab title="标签 3">内容 3</van-tab> <van-tab title="标签 4">内容 4</van-tab> <van-tab title="标签 5">内容 5</van-tab> <van-tab title="标签 6">内容 6</van-tab> </van-tabs>
export default { name: 'HomeIndex', data() { return { active: 0 } } }
- 通过
-
给组件添加
animated属性,开启切换标签内容时的转场动画<!-- 频道列表 --> <van-tabs v-model="active" swipeable animated> <!-- 略 --> </van-tabs>
-
基础样式调整
- 标签项
- 右边框
- 下边框
- 宽高
- 文字大小
- 文字颜色
- 底部条
- 宽高
- 颜色
- 位置
- 标签项
-
添加类名
<van-tabs class="channel-tabs" v-model="active" swipeable animated> <!-- 略 --> </van-tabs>
-
样式调整
/deep/ .channel-tabs { .van-tabs__wrap { height: 82px; } .van-tab { min-width: 200px; border-right: 1px solid #edeff3; font-size: 30px; color: #777777; } .van-tab--active { color: #333333; } .van-tabs__nav { padding-bottom: 0; } .van-tabs__line { bottom: 8px; width: 31px !important; height: 6px; background-color: #3296fa; } }
-
汉堡菜单样式调整
-
使用插槽插入内容
-
样式调整
- 定位
- 内容居中
- 宽高
- 背景色、透明度
- 字体图标大小
-
使用伪元素设置渐变边框
- 定位
- 宽高
- 背景图
- 背景图填充模式
-
-
使用插槽插入内容
<!-- 频道列表 --> <van-tabs class="channel-tabs" v-model="active" swipeable animated> <van-tab title="标签 1">内容 1</van-tab> <van-tab title="标签 2">内容 2</van-tab> <van-tab title="标签 3">内容 3</van-tab> <van-tab title="标签 4">内容 4</van-tab> <van-tab title="标签 5">内容 5</van-tab> <van-tab title="标签 6">内容 6</van-tab> <!-- 汉堡按钮 --> <div slot="nav-right" class="hamburger-btn"> <i class="toutiao toutiao-gengduo"></i> </div> </van-tabs>
-
样式调整
.hamburger-btn { position: fixed; right: 0; display: flex; justify-content: center; align-items: center; width: 66px; height: 82px; background-color: #fff; opacity: 0.902; i.toutiao { font-size: 33px; } }
-
使用伪元素设置渐变边框
.hamburger-btn { position: fixed; right: 0; display: flex; justify-content: center; align-items: center; width: 66px; height: 82px; background-color: #fff; opacity: 0.902; i.toutiao { font-size: 33px; } &:before { content: ''; position: absolute; left: 0; width: 1px; height: 100%; background-image: url(~@/assets/gradient-gray-line.png); background-size: contain; } }
-
添加占位符充当内容区域,解决最后一个标签被覆盖遮挡的问题
<!-- 频道列表 --> <van-tabs class="channel-tabs" v-model="active" swipeable animated> <van-tab title="标签 1">内容 1</van-tab> <van-tab title="标签 2">内容 2</van-tab> <van-tab title="标签 3">内容 3</van-tab> <van-tab title="标签 4">内容 4</van-tab> <van-tab title="标签 5">内容 5</van-tab> <van-tab title="标签 6">内容 6</van-tab> <!-- 汉堡按钮 --> <div slot="nav-right" class="placeholder"></div> <div slot="nav-right" class="hamburger-btn"> <i class="toutiao toutiao-gengduo"></i> </div> </van-tabs>
.placeholder { flex-shrink: 0; width: 66px; height: 82px; }
-
完整代码
.placeholder { flex-shrink: 0; width: 66px; height: 82px; } .hamburger-btn { position: fixed; right: 0; display: flex; justify-content: center; align-items: center; width: 66px; height: 82px; background-color: #fff; opacity: 0.902; i.toutiao { font-size: 33px; } &:before { content: ''; position: absolute; left: 0; width: 1px; height: 100%; background-image: url(~@/assets/gradient-gray-line.png); background-size: contain; } }
- 找数据接口
- 把接口封装为请求方法
- 在组件中请求获取数据
- 模板绑定
-
在
src\api\user.js中封装数据请求接口// 获取用户自己的信息 export const getUserChannels = () => { return request({ method: 'GET', url: '/app/v1_0/user/channels' }) }
-
在
src\views\home\index.vue导入封装的getUserChannels方法import { getUserChannels } from '@/api/user'
-
调用
getUserChannels方法获取到频道列表的数据export default { name: 'HomeIndex', data() { return { active: 0, channels: [] // 频道列表 } }, created() { this.loadChannels() }, methods: { async loadChannels() { try { const { data } = await getUserChannels() this.channels = data.data.channels } catch (err) { this.$toast('获取频道数据失败') } } } }
-
模板绑定,渲染请求回来的数据
<!-- 频道列表 --> <van-tabs class="channel-tabs" v-model="active" swipeable animated> <van-tab :title="channel.name" v-for="channel in channels" :key="channel.id">{{ channel.name }}的内容</van-tab> <!-- 汉堡按钮 --> <div slot="nav-right" class="placeholder"></div> <div slot="nav-right" class="hamburger-btn"> <i class="toutiao toutiao-gengduo"></i> </div> </van-tabs>
你的思路可能是这样的:
1、找到数据接口
2、封装请求方法
3、在组件中请求获取数据,将数据存储到 data 中
4、模板绑定展示
根据不同的频道加载不同的文章列表,你的思路可能是这样的:
- 有一个
list数组,用来存储文章列表 - 查看
a频道:请求获取数据,让list = a频道文章 - 查看
b频道:请求获取数据,让list = b频道文章 - 查看
c频道:请求获取数据,让list = c频道文章 - ...
思路没有问题,但是并不是我们想要的效果。
我们想要的效果是:加载过的数据列表不要重新加载。
list: [
a: [{}, {}],
b: [{}, {}]
]实现思路也非常简单,就是我们准备多个 list 数组,每个频道对应一个,查看哪个频道就把数据往哪个频道的列表数组中存放,这样的话就不会导致覆盖问题。
可是有多少频道就得有多少频道文章数组,我们都一个一个声明的话会非常麻烦,所以这里的建议是利用组件来处理。
具体做法就是:
- 封装一个文章列表组件
- 然后在频道列表中把文章列表遍历出来
因为文章列表组件中请求获取文章列表数据需要频道 id,所以 频道 id 应该作为 props 参数传递给文章列表组件,为了方便,我们直接把频道对象传递给文章列表组件就可以了。
在文章列表中请求获取对应的列表数据,展示到列表中。
最后把组件在频道列表中遍历出来,就像下面这样。
-
创建
src/views/home/components/article-list.vue<template> <div class="article-list">文章列表</div> </template> <script> export default { name: 'ArticleList' } </script> <style scoped lang="less"></style>
-
在
home/index.vue中注册并使用article-list.vue组件<template> <div class="home-container"> <!-- 顶部导航栏组件 --> <!-- 代码略 --> <!-- 频道列表 --> <van-tabs class="channel-tabs" v-model="active" swipeable animated> <van-tab :title="channel.name" v-for="channel in channels" :key="channel.id"> <!-- 文章列表 --> <article-list ref="article-list" :channel="channel" /> </van-tab> <!-- 汉堡按钮 --> <!-- 代码略 --> </van-tabs> </div> </template> <script> import { getUserChannels } from '@/api/user' import ArticleList from './components/article-list' export default { name: 'HomeIndex', components: { ArticleList } // 其他节点略 } </script>
-
在
article-list.vue组件内部使用props接收到父组件传递的数据<template> <div class="article-list">文章列表</div> </template> <script> export default { name: 'ArticleList', props: { channel: { type: Object, required: true } } } </script> <style scoped lang="less"></style>
-
测试代码是否完成
-
答疑:
- 为什么标签内容是懒渲染的?
- 因为这是 Tab 标签页组件本身支持的默认功能,如果不需要可以通过配置
:lazy-render="false"来关闭这个效果。
- 因为这是 Tab 标签页组件本身支持的默认功能,如果不需要可以通过配置
- 为什么标签内容是懒渲染的?
使用
Vant组件的List 列表组件 完成页面的渲染工作
List 列表组件:瀑布流滚动加载,用于展示长列表。
List组件通过loading和finished两个变量控制加载状态- 当组件初始化或滚动到到底部时,会触发 load 事件并将
loading设置成true,此时可以发起异步操作并更新数据,数据更新完毕后,将loading设置成false即可,若数据已全部加载完毕,则直接将finished设置成true即可。 load事件- List 初始化后会触发一次 load 事件,用于加载第一屏的数据。
- 如果一次请求加载的数据条数较少,导致列表内容无法铺满当前屏幕,List 会继续触发 load 事件,直到内容铺满屏幕或数据全部加载完成。
loading 属性:控制加载中的 loading 状态- 非加载中,loading 为 false,此时会根据列表滚动位置判断是否触发 load 事件(列表内容不足一屏幕时,会直接触发)
- 加载中,loading 为 true,表示正在发送异步请求,此时不会触发 load 事件
finished 属性:控制加载结束的状态- 在每次请求完毕后,需要手动将 finished 设置为 false,表示本次加载结束
- 所有数据加载结束,finished 为 true,此时不会触发 load 事件
-
初步使用
List组件,并分析属性的含义<template> <div class="article-list"> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" > <van-cell v-for="item in list" :key="item" :title="item" /> </van-list> </div> </template> <script> export default { name: 'ArticleList', props: { channel: { type: Object, required: true } }, data () { return { list: [], // 存储列表数据的数组 loading: false, // 控制加载中 loading 状态 finished: false // 控制数据加载结束的状态 } }, methods: { // 初始化或滚动到底部的时候会触发调用 onLoad onLoad () { console.log('onLoad') // 1. 请求获取数据 // setTimeout 仅做示例,真实场景中一般为 ajax 请求 setTimeout(() => { // 2. 把请求结果数据放到 list 数组中 for (let i = 0; i < 10; i++) { // 0 + 1 = 1 // 1 + 1 = 2 // 2 + 1 = 3 this.list.push(this.list.length + 1) } // 3. 本次数据加载结束之后要把加载状态设置为结束 // loading 关闭以后才能触发下一次的加载更多 this.loading = false // 4. 判断数据是否全部加载完成 if (this.list.length >= 40) { // 如果没有数据了,把 finished 设置为 true,之后不再触发加载更多 this.finished = true } }, 1000) } } } </script> <style scoped lang="less"></style>
-
加载到底部以后数据提示可能看不到,需要在
src\views\home\index.vue设置home-container样式进行设置.home-container { padding-bottom: 100px; }
- 实现思路
- 找到数据接口
- 封装请求方法
- 请求获取数据
- 模板绑定
注意:使用接口文档中最下面的 频道新闻推荐
/V1_1
-
创建
src/api/article.js封装获取文章列表数据的接口getArticles/** * 文章接口模块 */ import request from '@/utils/request' // 获取频道的文章列表 export const getArticles = params => { return request({ method: 'GET', url: '/app/v1_1/articles', params }) }
-
在
article-list.vue中导入getArticles方法import { getArticles } from '@/api/article'
-
在
onload方法使用getArticles方法- 往方法中传入即可文档需要的参数
- 获取到文章列表的数据
methods: { // 当触发上拉加载更多的时候调用该函数 async onLoad() { try { const { data } = await getArticles({ channel_id: this.channel.id, // 频道 id timestamp: this.timestamp || Date.now(), // 时间戳,请求新的推荐数据传当前的时间戳,请求历史推荐传指定的时间戳 with_top: 1 // 是否包含置顶,进入页面第一次请求时要包含置顶文章,1-包含置顶,0-不包含 }) console.log(data) } catch (err) { console.log(err) } } }
-
处理响应的数据结果
data () { return { list: [], // 文章列表数据 loading: false, // 上拉加载更多的 loading 状态 finished: false, // 是否加载结束 error: false, // 是否加载失败 timestamp: null // 请求下一页数据的时间戳 } }, methods: { // 当触发上拉加载更多的时候调用该函数 async onLoad () { try { // 1. 请求获取数据 const { data } = await getArticles({ channel_id: this.channel.id, // 频道 id timestamp: this.timestamp || Date.now(), // 时间戳,请求新的推荐数据传当前的时间戳,请求历史推荐传指定的时间戳 with_top: 1 // 是否包含置顶,进入页面第一次请求时要包含置顶文章,1-包含置顶,0-不包含 }) // 2. 把数据添加到 list 数组中 const { results } = data.data this.list.push(...results) // 3. 设置本次加载中 loading 状态结束 this.loading = false // 4. 判断数据是否加载结束 if (results.length) { // 更新获取下一页数据的时间戳 this.timestamp = data.data.pre_timestamp } else { // 没有数据了,设置加载状态结束,不再触发上拉加载更多了 this.finished = true } } catch (err) { console.log(err) } } } }
-
渲染页面
<template> <div class="article-list"> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> <van-cell v-for="(article, index) in list" :key="index" :title="article.title" /> </van-list> </div> </template>
-
返回页面查看渲染结果
错误处理的提示参考 List 列表 组件错误提示用法
-
若列表数据加载失败,将
error设置成true即可显示错误提示,用户点击错误提示后会重新触发 load 事件- 给
List组件添加error.sync属性,表示是否加载失败 - 给
List组件添加error-text属性,代表加载失败后的提示文案
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" :error.sync="error" error-text="请求失败,点击重新加载" @load="onLoad" > <van-cell v-for="(article, index) in list" :key="index" :title="article.title" /> </van-list>
- 给
-
在
data中声明error属性data() { return { list: [], // 存储列表数据的数组 loading: false, // 控制加载中 loading 状态 finished: false, // 控制数据加载结束的状态 error: false // 控制列表加载失败的提示状态 } }
-
在请求错误以后将
error设置为true开启提示,同时将loading也进行关闭methods: { // 当触发上拉加载更多的时候调用该函数 async onLoad () { try { // 请求成功的逻辑略…… } catch (err) { this.loading = false // 关闭 loading 效果 this.error = true // 开启错误提示 } } } }
这里主要使用到
Vant中的 PullRefresh 下拉刷新 组件
- 注册下拉刷新事件(组件)的处理函数
- 发送请求获取文章列表数据
- 把获取到的数据添加到当前频道的文章列表的顶部
- 提示用户刷新成功
-
使用
PullRefresh 下拉刷新组件将需要下拉刷新的组件进行包裹- 复制
PullRefresh 下拉刷新组件标签,在src\views\home\components\article-list.vue组件中对van-list包裹 - 声明组件标签中需要的属性
v-model和refresh事件
<van-pull-refresh v-model="isreFreshLoading" @refresh="onRefresh"> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" :error.sync="error" error-text="请求失败,点击重新加载" @load="onLoad" > <van-cell v-for="(article, index) in list" :key="index" :title="article.title" /> </van-list> </van-pull-refresh>
data() { return { isreFreshLoading: false // 控制下拉刷新的 loading 状态 } }
methods: { // 当下拉刷新的时候会触发调用该函数 onRefresh() { console.log('onRefresh') } }
- 复制
-
在
refresh事件中,发起请求,获取下拉刷新的数据,同时下拉刷新的loading状态// 当下拉刷新的时候会触发调用该函数 async onRefresh() { try { // 请求获取数据 // 1. 请求获取数据 const { data } = await getArticles({ channel_id: this.channel.id, // 频道 id timestamp: Date.now(), // 下拉刷新,每次请求获取最新数据,所以传递当前最新时间戳 with_top: 1 // 是否包含置顶,进入页面第一次请求时要包含置顶文章,1-包含置顶,0-不包含 }) // 2. 将数据追加到列表的顶部 const { results } = data.data this.list.unshift(...results) // 3. 关闭下拉刷新的 loading 状态 this.isreFreshLoading = false } catch (error) { this.isreFreshLoading = false } }
-
对下拉刷新细节进行优化,添加成功提示属性
success-text,以及刷新成功提示展示时长属性success-duration<van-pull-refresh v-model="isreFreshLoading" @refresh="onRefresh" :success-text="refreshSuccessText" :success-duration="1500" > <!-- 略 --> </van-pull-refresh>
data() { return { refreshSuccessText: '刷新成功' // 下拉刷新成功提示文本 } }
async onRefresh() { try { // 请求获取数据 // 1. 请求获取数据 // 略…… // 2. 将数据追加到列表的顶部 // 略…… // 3. 关闭下拉刷新的 loading 状态 // 略…… // 更新下拉刷新成功提示的文本 this.refreshSuccessText = `刷新成功,更新了${results.length}条数据` } catch (error) { this.refreshSuccessText = '刷新失败' this.isreFreshLoading = false } }
-
在
src\views\home\index.vue中,给van-nav-bar导航栏添加fixed属性,实现导航栏固定<!-- 顶部导航栏组件 --> <van-nav-bar class="page-nav-bar" fixed> <van-button class="search-btn" slot="title" type="info" size="small" round icon="search">搜索</van-button> </van-nav-bar>
-
导航栏固定以后,发现
Tab标签栏被覆盖,使用样式进行调整/deep/ .channel-tabs { .van-tabs__wrap { position: fixed; top: 92px; z-index: 1; left: 0; right: 0; height: 82px; }
-
Tab标签栏被覆盖问题解决以后,发现列表前两个被遮盖,给home-container添加样式进行调整.home-container { padding-top: 174px; }
-
为什么列表滚动会相互影响 ?
- 因为他们并不是再自己内部滚动,而是整个
body页面在滚动,无论你是在a频道还是在b频道,其实滚动的都是body元素
- 因为他们并不是再自己内部滚动,而是整个
-
如何快速找到是哪个元素产生的滚动
- 使用下面的代码粘贴到调试工具中运行一下,然后滚动页面,就可以看到是哪个元素产生的滚动了
function findscroller(element) { element.onscroll = function () { console.log(element) } Array.from(element.children).forEach(findscroller) } findscroller(document.body)
-
让每一个标签内容文章列表产生自己的滚动容器,这样就不会相互影响了
-
如何让标签内容文章列表产生自己的滚动容器?
- 固定高度:
height: xxx; - 溢出滚动:
overflow-y: auto;
- 固定高度:
-
我们给文章列表组件的根节点样式设置如下,
<style scoped lang="less"> .article-list { height: 100%; overflow-y: auto; } </style>
-
但是发现设置高
100%的话没有作用,这是为什么?- 因为百分比是相对于父元素,而我们通过审查元素发现它所有的父元素都没有高
- 那肯定没有作用了
-
css3中新增了一种视口单位vw和vh,何谓视口,就是根据你浏览器窗口的大小的单位,不受父元素的影响- 在移动端,视口单位相对于布局视口
1vw= 可视窗口宽度的百分之一,比如窗口宽度是750,则1vw=7.5px1vh= 可视窗口高度的百分之一,比如窗口宽度是667,则1vw=6.67px- 使用它唯一需要注意的就是它的兼容性:
- 在
PC端已兼容到IE6 - 在移动端
IOS8以上以及Android 4.4以上获得支持,并且在微信x5内核中也得到了完美的全面支持 Vue本身就已经兼容到IE9,所以可以使用这个视口单位
- 在
<style scoped lang="less"> .article-list { height: 79vh; overflow-y: auto; } </style>
-
在我们项目中有好几个页面中都有这个文章列表项内容,如果我们在每个页面中都写一次的话不仅效率低而且维护起来也麻烦。 所以最好的办法就是我们把它封装为一个组件,然后在需要使用的组件中加载使用即可
-
创建
src/components/article-item/index.vue组件<template> <div class="article-item">文章列表项</div> </template> <script> export default { name: 'ArticleItem', props: { article: { type: Object, required: true } } } </script> <style lang="less" scoped> </style>
-
在文章列表组件中注册使用文章列表项组件
<script> import { getArticles } from '@/api/article' import ArticleItem from '@/components/article-item' export default { name: 'ArticleList', components: { ArticleItem } } </script>
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" :error.sync="error" error-text="请求失败,点击重新加载" @load="onLoad" > <article-item v-for="(article, index) in list" :key="index" :article="article"></article-item> </van-list>
2、
使用
Vant提供的 Cell 单元格组件 完成列表项内容的开发,展示标题以及底部信息
-
使用
cell单元格组件以及image组件完成页面功能的开发<template> <div class="article-item"> <van-cell class="article-item"> <div slot="title" class="title">{{ article.title }}</div> <div slot="label"> <div v-if="article.cover.type === 3" class="cover-wrap"> <div class="cover-item" v-for="(img, index) in article.cover.images" :key="index"> <van-image width="100" height="100" :src="img" /> </div> </div> <div> <span>{{ article.aut_name }}</span> <span>{{ article.comm_count }}评论</span> <span>{{ article.pubdate }}</span> </div> </div> <van-image v-if="article.cover.type === 1" slot="default" width="100" height="100" :src="article.cover.images[0]" /> </van-cell> </div> </template> <script> export default { name: 'ArticleItem', props: { article: { type: Object, required: true } } } </script> <style lang="less" scoped> </style>
-
文章标题
- 字号
- 颜色
- 多行文字省略
-
单图封面
- 封面容器
- 去除 flex: 1,固定宽高
- 左内边距
- 封面图
- 宽高
- 填充模式:cover
- 封面容器
-
底部文本信息
- 字号
- 颜色
- 间距
-
多图封面
- 外层容器
- flex 容器
- 上下外边距
- 图片容器
- 平均分配容器空间:flex: 1;
- 固定高度
- 容器项间距
- 图片
- 宽高
- 填充模式
- 外层容器
-
完成列表项代码的样式布局
<template> <van-cell class="article-item"> <div slot="title" class="title van-multi-ellipsis--l2">{{ article.title }}</div> <div slot="label"> <div v-if="article.cover.type === 3" class="cover-wrap"> <div class="cover-item" v-for="(img, index) in article.cover.images" :key="index"> <van-image class="cover-item-img" fit="cover" :src="img" /> </div> </div> <div class="label-info-wrap"> <span>{{ article.aut_name }}</span> <span>{{ article.comm_count }}评论</span> <span>{{ article.pubdate }}</span> </div> </div> <van-image v-if="article.cover.type === 1" slot="default" class="right-cover" fit="cover" :src="article.cover.images[0]" /> </van-cell> </template> <script> export default { name: 'ArticleItem', props: { article: { type: Object, required: true } } } </script> <style scoped lang="less"> .article-item { .title { font-size: 32px; color: #3a3a3a; } .van-cell__value { flex: unset; width: 232px; height: 146px; padding-left: 25px; } .right-cover { width: 232px; height: 146px; } .label-info-wrap span { font-size: 22px; color: #b4b4b4; margin-right: 25px; } .cover-wrap { display: flex; padding: 30px 0; .cover-item { flex: 1; height: 146px; &:not(:last-child) { padding-right: 4px; } .cover-item-img { width: 100%; height: 146px; } } } } </style>
-
为什么文章列表数据中的好多图片资源请求失败返回 403?
- 这是因为我们项目的接口数据是后端通过爬虫抓取的第三方平台内容,而第三方平台对图片资源做了防盗链保护处理
-
第三方平台怎么处理图片资源保护的?
-
Referer是什么东西?扩展参考:http://www.ruanyifeng.com/blog/2019/06/http-referer.html
注意:
referer实际上是referrer误拼写。参见 HTTP referer on Wikipedia (HTTP referer在维基百科上的条目)来获取更详细的信息。Referer是HTTP请求头的一部分,当浏览器向Web服务器发送请求的时候,一般会带上Referer,它包含了当前请求资源的来源页面的地址。服务端一般使用Referer请求头识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等
-
怎么解决
- 不要发送
referrer,对方服务端就不知道你从哪来的了,姑且认为是你是自己人吧
- 不要发送
-
如何设置不发送
referrer?- 用
<a>、<area>、<img>、<iframe>、<script>或者<link>元素上的referrerpolicy属性为其设置独立的请求策略,例如:
<img src="proxy.php?url=http%3A%2F%2F%E2%80%A6%E2%80%A6" referrerPolicy="no-referrer">- 或者直接在
HTMl页面头中通过meta属性全局配置:
<meta name="referrer" content="no-referrer" /> - 用
-
推荐两个第三方库:
两者都是专门用于处理时间的
JavaScript库,功能差不多,因为Day.js的设计就是参考的Moment.js。但是Day.js相比Moment.js的包体积要更小一些,因为它采用了插件化的处理方式。Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的
API设计保持完全一样,如果您曾经用过Moment.js, 那么您已经知道如何使用Day.js。Day.js可以运行在浏览器和Node.js中。- 🕒 和
Moment.js相同的API和用法 - 💪 不可变数据 (
Immutable) - 🔥 支持链式操作 (
Chainable) - 🌐 国际化
I18n - 📦 仅
2kb大小的微型库 - 👫 全浏览器兼容
-
安装
npm i dayjs
-
创建
utils/dayjs.jsimport Vue from 'vue' import dayjs from 'dayjs' // 加载中文语言包 import 'dayjs/locale/zh-cn' import relativeTime from 'dayjs/plugin/relativeTime' // 配置使用处理相对时间的插件 dayjs.extend(relativeTime) // 配置使用中文语言包 dayjs.locale('zh-cn') // 全局过滤器:处理相对时间 Vue.filter('relativeTime', value => { return dayjs().to(dayjs(value)) })
-
在
main.js中加载初始化import './utils/dayjs'
-
使用过滤器
<p>{{ 日期数据 | relativeTime }}</p>









