阅读视图

发现新文章,点击刷新页面。

零依赖!教你用原生 JS 把 JSON 数组秒变 CSV 文件

一、核心思路

  1. 把 JS 数组拼成「逗号分隔 + 换行」的字符串 → 这就是 CSV 的“文本协议”。
  2. 利用 Blob 把字符串变成文件流。
  3. 创建一个看不见的 <a> 标签,给它一个 download 属性,再自动点一下,浏览器就会弹出保存框。

二、核心代码

1. 准备原始数据

原始数据可以是接口返回,也可以是 mock。

const posts = [
  { id:1, title:'用 Vite 搭建 React 18 项目', link:'...', img:'...', views:12034 },
  // ...
];

2. 定义表头

顺序随意,只要和下面 map 对应即可。

const headers = ['id','名称','链接','图片','阅读'];

3. 拼接数据

const csvContent = [
  headers.join(','), // 第一行:表头
  ...posts.map(item => [ // 剩余行:数据
    `"${item.id}"`,
    `"${item.title}"`,
    `"${item.link}"`,
    `"${item.img}"`,
    `"${item.views}"`
  ].join(','))
].join('\n');

4. 生成文件并下载

const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.href = url;
link.download = `文章信息_${new Date().toISOString()}.csv`;
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);

三、完整代码

// 1. 造点假数据
const posts = [
  {
    id: 1,
    title: '用 Vite 搭建 React 18 项目',
    link: 'https://example.com/vite-react18',
    img: 'https://example.com/cover/vite-react.jpg',
    views: 12034
  },
  {
    id: 2,
    title: 'Tailwind CSS 3 响应式布局技巧',
    link: 'https://example.com/tailwind-layout',
    img: 'https://example.com/cover/tailwind.jpg',
    views: 8721
  },
  {
    id: 3,
    title: '深入浅出浏览器事件循环',
    link: 'https://example.com/event-loop',
    img: 'https://example.com/cover/event-loop.jpg',
    views: 15003
  },
  {
    id: 4,
    title: 'Webpack 5 性能优化清单',
    link: 'https://example.com/webpack5-optimize',
    img: 'https://example.com/cover/webpack.jpg',
    views: 9855
  },
  {
    id: 5,
    title: '前端图片懒加载完整方案',
    link: 'https://example.com/lazy-load',
    img: 'https://example.com/cover/lazy-load.jpg',
    views: 6542
  }
];

// 2. 组装 CSV
const headers = ['id', '名称', '链接', '图片', '阅读'];
const csvContent = [
  headers.join(','),
  ...posts.map(item => [
    `"${item.id}"`,
    `"${item.title}"`,
    `"${item.link}"`,
    `"${item.img}"`,
    `"${item.views}"`
  ].join(','))
].join('\n');

// 3. 下载
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `文章信息_${new Date().toISOString()}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);   // 释放内存

快速体验

  • 打开任意网页,F12 进控制台
  • 把完整代码全部粘进去,回车

uni-app 广告弹窗最佳实践:不扰民、可控制频次、含完整源码

需求背景

在很多移动端应用中,广告弹窗是常见的变现手段。但频繁弹出、无法关闭、重复打扰的广告往往会适得其反,导致用户流失。

本文将带你用 uni-app 实现一个 “智能不扰民” 的广告弹窗组件,支持24小时只弹一次点击跳转优雅关闭,可直接复制使用。

效果预览

image-20251023154222615

功能亮点

  • 24小时内只弹一次,避免骚扰
  • 点击蒙层或关闭按钮均可关闭
  • 图片点击支持跳转活动页
  • 不依赖第三方库,开箱即用

核心思路拆解

模板

  • .ad-mask 作为半透明遮罩层,点击可关闭广告
  • .ad-container 包含广告图片和关闭按钮
  • .ad-image 显示广告图片,点击可跳转
  • .ad-close-btn 提供关闭按钮

样式

  • .ad-mask: 遮罩层样式,居中显示内容
  • .ad-container: 广告容器尺寸和定位
  • .ad-image: 图片自适应显示
  • .ad-close-btn: 关闭按钮样式,圆形设计

逻辑部分

  • visible 驱动渲染:控制广告显示状态。
  • 事件冒泡“双保险”:蒙层绑定 @click="closeAd",内部容器加 @click.stop 阻止冒泡。
  • 频次控制:利用 uni.setStorageSync('lastAdTime', Date.now()) 记录本次弹出时间,下次 onLoad 时对比 24 h 间隔,不到时间绝不骚扰。

使用建议

  • 广告图建议尺寸:600x800rpx,体积 < 100KB
  • 弹窗频率可升级为后端控制,避免前端被篡改
  • 可扩展 uni.request 动态获取广告图和跳转链接
  • 若需支持“不再提示”,可新增 uni.setStorageSync('neverShowAd', true)

完整代码

<template>
  <view class="content">
    <!-- 其他元素 -->
    <!-- 广告弹窗 -->
    <view v-if="visible" class="ad-mask" @click="closeAd">
      <view class="ad-container" @click.stop>
        <image class="ad-image" :src="adImage" mode="aspectFit" @click="onAdClick" />
        <view class="ad-close-btn" @click="closeAd"></view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      adImage: '/static/ad.png',
      adLink: '/pages/activity/index'
    }
  },
  onLoad() {
    const last = uni.getStorageSync('lastAdTime');
    const now = Date.now();
    const oneDay = 24 * 60 * 60 * 1000;

    if (!last || now - last > oneDay) {
      this.showAd()
    }
  },
  methods: {
    showAd() {
      this.visible = true;
    },
    closeAd() {
      this.visible = false;
      uni.setStorageSync('lastAdTime', Date.now());
    },
    onAdClick() {
      uni.navigateTo({ url: adLink });
      this.closeAd();
    }
  }
}
</script>

<style>
.ad-mask {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}
.ad-container {
  position: relative;
  width: 600rpx;
  height: 800rpx;
}
.ad-image {
  width: 100%;
  height: 100%;
  border-radius: 16rpx;
}
.ad-close-btn {
  position: absolute;
  top: 16rpx;
  right: 16rpx;
  color: #fff;
  font-size: 36rpx;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  width: 60rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
}
</style>
❌