使用 Next.js 和掘金 API 打造个性博客

发表于 1年以前  | 总阅读数:1411 次


阅读本文,你将收获:


  • 通过 chrome 调试工具获得掘金 api
  • 学会使用 Next.js 服务端渲染
  • 学会使用 Tailwindcss 来代替原生 css
  • 在几分钟内就可以部署一个自己的博客

背景

在开始之前,我想先问下各位,是否有自建博客?很多人选择在社区写博客,比如:掘金,因为在社区写博客能够第一时间被人看到,能够第一时间把知识分享出去,也可以在第一时间得到他们反馈和评论。 但在社区写博客也有劣势,比如掘金社区只能写技术文章,并不能完全展现你自己的个性。比如,我是一名前端开发者,在社区看我的文章,只能体现我是一名前端,但同时我又是一名摄影爱好者,这点就没办法体现了,所以这就是自建博客的优势,有非常高的灵活度,可以自己设计想要的风格和模块。但自建博客也有非常大的劣势,第一点就是部署到服务器,有一定花费,其次就是新建博客几乎没流量,所以我们需要在各大社区论坛发表文章,给自己的博客引流。这样一来,就迎来另一个问题,我需要在两个地方发表,这不就是重复劳动吗?

接下来,我就分享下我的方法,通过掘金 API 打造个性博客,只要在掘金发表文章,就会自动同步到自己的博客中。

本文涉及的代码都在这个 Github 仓库[1]中。

获取掘金 API

打开掘金主页,使用 chrome devtools 很容易可以找到获取文章列表的接口。

devtools 查看文章列表接口

可以看到接口返回了文章列表数据 data,文章总数 count,以及当前的分页游标 cursor

我们使用 axios 在 nodejs 中请求数据,封装成一个 getArticles 方法


export async function getArticles(uid: string, cursor: number = 0) {
  const res = await axios.post(
    "https://api.juejin.cn/content_api/v1/article/query_list",
    {
      cursor: cursor + "",
      sort_type: 2,
      user_id: uid + "",
    }
  );

  return res.data;
}

devtools 查看文章详情接口

通过查看文章详情页,我们可以复制出接口,使用 axios 修改下,封装成 getArticleDetail 方法


export async function getArticleDetail(article_id: string) {
  const res = await axios.post(
    "https://api.juejin.cn/content_api/v1/article/detail",
    {
      article_id,
    }
  );

  return res.data;
}

有了接口,我们就可以用它来搭建自己的博客了。

初始化项目

接下来,我们将从零开始创建一个 next 项目,并且选择 Typescript 模板

npx create-next-app --ts nextjs-juejin-blog
cd nextjs-juejin-blog
yarn dev

创建项目后,脚手架会帮我们自动执行 yarn install

打开 http://localhost:3000/ 你将看到如下页面

Next.js 默认页面

添加 Tailwind CSS

Tailwind CSS 是一个 CSS 原子类样式框架,我们可以使用现成的样式, 比如flextext-3xlmr-3 等等,并且这些 CSS 会在构建的时候,打包出最小的样式文件。没接触过的小伙伴,一开始可能会不习惯,但写完一个项目后,你会爱不释手,因为所有的 CSS 都在组件中,并且一目了然。如果你之前的项目中使用的是 CSS modules,当项目变得复杂后,若没维护好的话,到最后可能会面向 vscode 搜索编程。

在开始之前,你首先需要的 VSCODE 中安装 Tailwind CSS IntelliSense[2] 插件,这样在你写 class 的时候,就会有智能提示,鼠标移动到 class 上,也可以看到具体的 CSS 属性。

Tailwind CSS IntelliSense

接下来,我们可以在命令行中运行下面命令

  1. 安装 npm 包
yarn add -D tailwindcss postcss autoprefixer

Tailwindcss 的编译依赖 postcss,autoprefixer 会自动根据 Can I Use 标准给 CSS 属性添加浏览器适配前缀。

2 . 初始化 tailwind.config.js 配置文件

npx tailwindcss init -p

3 . 修改 tailwind.config.js 配置文件,修改下 content 字段构建,修改后,只会打包 content 中匹配文件使用到的 class


const colors = require("tailwindcss/colors");

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.tsx"],
  theme: {
    extend: {
      colors: {
        primary: colors.indigo,
        //@ts-ignore
        gray: colors.neutral, // TODO: Remove ts-ignore after tw types gets updated to v3
      },
    },
  },
  plugins: [],
};

扩展一个 primary color,主色调统一使用这个,方便后续不同的人使用这个模板,可以方便地修改主色

调整下目录,将主要的代码目录都移动到 src

mkdir src
mkdir src/components
mv styles src
mv pages src

这点是个人爱好,你可以遵循原来的目录。

4 . 修改 /src/styles/globals.css 中的 CSS


@tailwind base;
@tailwind components;
@tailwind utilities;

@tailwind 指令会在运行的时候生成默认样式。

约定式路由

pages 或者 src/pages 文件夹下建立文件或文件夹,Next.js 会帮我自动创建路由系统。

比如我们创建如下目录结构:

src/pages
├── _app.tsx
├── api
│   └── hello.ts
├── blog
│   ├── [...slug].tsx
│   └── page
│       └── [page].tsx
├── blog.tsx
└── index.tsx

就会创建如下路由


/api/hello
/blog/page/:page
/blog/:slug
/blog
  • 其中 [page] 是变量,可以匹配任意值,那么我们的路由就是: /blog/page/1;
  • [...slug] 是多层变量,可以匹配/blog/a/blog/a/b/blog/a/b/c 等等。

服务端渲染

在 Next.js 中,在 Page 页面中可以导出一个 getServerSideProps 方法,用于服务端获取数据。

下面我们来实现下博客列表页面,需要获取 url 上的翻页参数

import React from "react";
import { GetServerSidePropsContext } from "next";
import { getArticles } from "../lib/db";
import { InferGetServerSidePropsType } from "next";

export default function Page({
  data,
  count,
  page,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // Render data...
  console.log(data);
}

// 每次刷新页面都后执行这个函数
export async function getServerSideProps(context: GetServerSidePropsContext) {
  const page = (context.query?.page as string) || 1;
  // 通过 API 请求数据
  const uid = process.env.uid!;
  const { data, count } = await getArticles(uid, (+page - 1) * 10);

  // 将数据传递到页面上
  return { props: { data, count, page: +page } };
}

新建一个.env 文件,将掘金的 ID 设置为 uid,我们就可以在 nodejs 中通过process.env获取这个值。

此时的 data 就是文章列表数据,复制其中一条数据,使用工具[3]将 json 转为 typescript 类型,删除一些我们不需要的字段,我们就可以得到 Article 的 ts 类型定义。

export interface Article {
  article_id: string;
  article_info: ArticleInfo;
  category: Category;
  tags: Tag[];
}

export interface ArticleInfo {
  article_id: string;
  cover_image: string;
  title: string;
  brief_content: string;
  content: string;
  ctime: string;
  mtime: string;
  rtime: string;
  view_count: number;
  collect_count: number;
  digg_count: number;
  comment_count: number;
}

export interface Category {
  category_id: string;
  category_name: string;
}

export interface Tag {
  id: number;
  tag_id: string;
  tag_name: string;
}

文章列表页实现

下面我们用 Tailwind css 来实现下 ArticleList 组件

import React from "react";
import Link from "next/link";
import Pagination from "./Pagination";

export default function ArticleList({
  articles,
  totalPages,
  currentPage,
}: Props) {
  return (
    <div className="max-w-5xl mx-auto">
      <ul>
        {articles.map((article) => (
          <li key={article.article_id} className="py-4">
            <article className="xl:gap-2 xl:grid xl:grid-cols-4 xl:items-start">
              <dl>
                <dt>
                  <img
                    className="w-52"
                    src={article.article_info.cover_image}
                    alt={article.article_info.title}
                  />
                </dt>
                <dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
                  <span className="sr-only">Published on</span>
                  <time>
                    {new Date(
                      +article.article_info.ctime * 1000
                    ).toLocaleDateString("zh-CN", {
                      year: "numeric",
                      month: "long",
                      day: "numeric",
                    })}
                  </time>
                </dd>
              </dl>
              <div className="space-y-3 xl:col-span-3">
                <div>
                  <h3 className="text-2xl font-bold leading-8 tracking-tight">
                    <Link
                      className="text-gray-900 dark:text-gray-100"
                      href={`/blog/${article.article_id}`}
                    >
                      {article.article_info.title}
                    </Link>
                  </h3>
                  <div className="mt-3 flex flex-wrap">
                    {article.tags.map((tag) => (
                      <Link
                        key={tag.tag_id}
                        className="mr-3 text-sm font-medium uppercase text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
                        href={`/tags/${tag.tag_name}`}
                      >
                        {tag.tag_name}
                      </Link>
                    ))}
                  </div>
                </div>
                <div className="prose max-w-none text-gray-500 dark:text-gray-400">
                  {article.article_info.brief_content}
                </div>
              </div>
            </article>
          </li>
        ))}
      </ul>
      <Pagination totalPages={totalPages} currentPage={currentPage} />
    </div>
  );
}
  • 页面之间的跳转,我们应该使用 next/link ,而不是 <a>标签,跟 react-router 中的Link类似,用户点击链接,并不会全局刷新,而是动态替换网页中的内容。
  • Tailwindcss 默认是移动优先,若要适配其他屏幕,可以在样式前面加 md、lg、xl、2xl 等前缀, 上面代码中的 xl:代表屏幕宽度大于 1280px 应用的样式。

翻页组件

getArticles 方法中返回了 count 文章总数,我们可以根据它和当前的 currentPage 封装成一个分页组件,代码如下:

import Link from 'next/link'

interface Props {
  totalPages: number
  currentPage: number
}

export default function Pagination({ totalPages, currentPage }: Props) {
  const prevPage = currentPage - 1 > 0
  const nextPage = currentPage + 1 <= totalPages

  return (
    <div className="space-y-2 pt-6 pb-8 md:space-y-5">
      <nav className="flex justify-between">
        {!prevPage && (
          <button className="cursor-auto disabled:opacity-50" disabled={!prevPage}>
            上一页
          </button>
        )}
        {prevPage && (
          <Link href={currentPage - 1 === 1 ? `/blog/` : `/blog/page/${currentPage - 1}`}>
            <button>上一页</button>
          </Link>
        )}
        <span>
          {currentPage} of {totalPages}
        </span>
        {!nextPage && (
          <button className="cursor-auto disabled:opacity-50" disabled={!nextPage}>
            下一页
          </button>
        )}
        {nextPage && (
          <Link href={`/blog/page/${currentPage + 1}`}>
            <button>下一页</button>
          </Link>
        )}
      </nav>
    </div>
  )
}

一起来看下效果吧

文章列表页

路由重写

这里有一个疑问,其实我们的路由是 /blogblog/page/1 这 2 个页面应该使用同一个组件,而现在我们需要在 pages 下面定义 2 个页面,那么 Next.js 中有没有可以配置的地方,可以重写路由,使用同一个组件呢?

答案是当然可以的,在 next.config.js, 配置 rewrites 字段。

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  async rewrites() {
    return [
      {
        source: "/blog/:id/edit",
        destination: `/blog/create`,
      },
      {
        source: "/blog/page/:page",
        destination: `/blog`,
      },
    ];
  },
};

module.exports = nextConfig;

比如上面的配置中, 博客编辑页面 /blog/:id/edit,使用 /blog/create页面来实现,rewrites 字段也就是实现了 webpack devserver 的 proxy 功能,比如:后端有些接口使用 Java 实现,也可以使用 rewrites 实现代理联调。

文章详情

实现了文章列表页面,我们应该可以很快写出文章详情页面的页面代码,大致如下:

import { GetServerSidePropsContext } from "next";
import ErrorPage from "next/error";
import { getArticleDetail } from "../../lib/db";
import { InferGetServerSidePropsType } from "next";
import { Article } from "../../types/article";

export default function Page({
  data,
  statusCode,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  if (statusCode) {
    return <ErrorPage statusCode={statusCode} />;
  }
  console.log(data);
  //Render data...

  return <div className="prose"></div>;
}

// 每次刷新页面都后执行这个函数
export async function getServerSideProps(context: GetServerSidePropsContext) {
  const slug = context.query?.slug as string[];
  // 通过 API 请求数据
  const res = await getArticleDetail(slug[0]);
  if (res.err_msg === "success") {
    // 将数据传递到页面上
    return { props: { data: res.data as Article } };
  }

  // 将数据传递到页面上
  return { props: { statusCode: 404 } };
}

实现方式跟列表页相同

  • getServerSideProps 中通过接口获取文章详情;
  • 接口获取失败的时候返回状态码 404,并且使用next/error 显示成统一的错误页面;

接下来还有 3 个功能要实现:

  1. markdown 格式转为 html
  2. 文章详情页面的样式
  3. 代码高亮

markdown 转 html

请求接口后,得到的 markdown 内容结构如下

---
highlight: monokai-sublime
---

## 正文内容

所以在解析 markdown 内容之前,还得解析 markdown 的前缀, 在命令行中安装以下 2 个包来实现这个功能。

yarn add markdown-it gray-matter
yarn add @types/markdown-it --dev

那么我便可以写出编译 markdown 内容的代码了:


import MarkdownIt from "markdown-it";
import matter from "gray-matter";

const md = new MarkdownIt();

export default function Page({
  data,
  statusCode,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {

  if (statusCode || !data) {
    return <Error statusCode={statusCode} />;
  }

  const result = matter(data?.article_info.mark_content || "");

  return (
    <div className="mx-auto">
      <header className="pt-6">
        <h1>{data?.article_info.title}</h1>
        <dl>
          <dt className="sr-only">Published on</dt>
          <dd className="text-base font-medium leading-6 text-gray-500">
            <time>
              {new Date(+data.article_info.ctime * 1000).toLocaleDateString(
                "zh-CN",
                {
                  year: "numeric",
                  month: "long",
                  day: "numeric",
                }
              )}
            </time>
          </dd>
        </dl>
        {data.article_info.cover_image && (
          <img
            className="max-w-full"
            src={data.article_info.cover_image}
            alt={data.article_info.title}
          />
        )}
      </header>
      <div
        dangerouslySetInnerHTML={{
          __html: md.render(result.content),
        }}
      ></div>
    </div>
  );
}

文章详情页面的样式

关于文章详情页的样式,我第一个想到的是github-markdown-css[4], 但今天要推荐的还是 Tailwindcss,@tailwindcss/typography 是官方提供的插件,可以帮助我们排版美化文章类页面的样式。

首先让我们来安装这个插件

yarn add  @tailwindcss/typography

然后在 tailwind.config.js 配置文件中加入这个插件:

module.exports = {
  theme: {
    // ...
  },
  plugins: [
    require('@tailwindcss/typography'),
    // ...
  ],
}

最后我们在文章最外层就可以加入 prose 这个样式了,prose-indigo 将主色调配置成湛蓝色,当然你可以改为其他 Tailwind css 中提供的默认颜色变量。

<article class="prose prose-indigo">
  {{ markdown }}
</article>

代码高亮

最后一步,代码高亮,我选择使用更加轻量的 prismjs,在 react 使用也很简单,详情可以参考之前写的这篇文章《使用 Prism.js 对代码进行语法高亮》[5]。


import React, { useEffect } from "react";
import Prism from "prismjs";
import "prismjs/components/prism-jsx";
import "prismjs/components/prism-tsx";
import "prismjs/components/prism-typescript";
import "prismjs/components/prism-bash";
import "prismjs/components/prism-markdown";
...
useEffect(() => {
    Prism.highlightAll();
  }, [data]);
...

完成啦,一起来看下看下实现效果

文章详情页面效果

个性化首页

到此,我们实现了文章列表页面和文章详情页面,现在还缺一个首页,写到这里,正巧发现今年有个主题是“航天”,那么我们就来设计一个“航天主题“的博客。

  • 在爱给网等网站搜索主题相关的 png 免扣素材;
  • 使用 canvas 粒子制作星空背景;

我们先来看下效果,再看实现代码。

下面是星空代码的实现,主要是实现思路

  1. 随机在屏幕屏幕上初始化 800 个粒子
  2. 使用 requestAnimationFrame 在原坐标基础上增加一定速度的系数
  3. 粒子超出画布重新初始化粒子坐标
  4. 使用 ResizeObserver 监听容器大小,重新初始化画布
const COUNT = 800;
const SPEED = 0.1;
class Star {
  x: number;
  y: number;
  z: number;
  xPrev: number;
  yPrev: number;
  constructor(x = 0, y = 0, z = 0) {
    this.x = x;
    this.y = y;
    this.z = z;
    this.xPrev = x;
    this.yPrev = y;
  }
  update(width: number, height: number, speed: number) {
    this.xPrev = this.x;
    this.yPrev = this.y;
    this.z += speed * 0.0675;
    this.x += this.x * (speed * 0.0225) * this.z;
    this.y += this.y * (speed * 0.0225) * this.z;
    // 超出屏幕坐标,初始化为随机值
    if (
      this.x > width / 2 ||
      this.x < -width / 2 ||
      this.y > height / 2 ||
      this.y < -height / 2
    ) {
      this.x = Math.random() * width - width / 2;
      this.y = Math.random() * height - height / 2;
      this.xPrev = this.x;
      this.yPrev = this.y;
      this.z = 0;
    }
  }
  draw(ctx: CanvasRenderingContext2D) {
    ctx.lineWidth = this.z;
    ctx.beginPath();
    ctx.moveTo(this.x, this.y);
    ctx.lineTo(this.xPrev, this.yPrev);
    ctx.stroke();
  }
}
const stars = Array.from({ length: COUNT }, () => new Star(0, 0, 0));
let rafId = 0;
const canvas: HTMLCanvasElement = document.querySelector("#canvas")!;
const ctx = canvas.getContext("2d")!;
const container = ref.current!;
// 监听 container 容器的变化,设置canvas 画布的大小
const resizeObserver = new ResizeObserver(setup);
resizeObserver.observe(container);
function setup() {
  // 缩放屏幕后取消动画
  rafId > 0 && cancelAnimationFrame(rafId);
  const { clientWidth: width, clientHeight: height } = container;
  // 根据 dpi 缩放画布,保证高清屏显示
  const dpr = window.devicePixelRatio || 1;
  canvas.width = width * dpr;
  canvas.height = height * dpr;
  canvas.style.width = `${width}px`;
  canvas.style.height = `${height + 1}px`;
  ctx.scale(dpr, dpr);
  // 初始化坐标为随机 正负 1/2 width
  for (const star of stars) {
    star.x = Math.random() * width - width / 2;
    star.y = Math.random() * height - height / 2;
    star.z = 0;
  }
  // 中心点偏移到屏幕中心
  ctx.translate(width / 2, height / 2);
  ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
  ctx.strokeStyle = "white";
  rafId = requestAnimationFrame(frame);
}
function frame() {
  const { clientWidth: width, clientHeight: height } = container;
  for (const star of stars) {
    star.update(width, height, SPEED);
    star.draw(ctx);
  }
  ctx.fillRect(-width / 2, -height / 2, width, height);
  rafId = requestAnimationFrame(frame);
}

最后

我将该项目开源在 GitHub 中,你只需要:

  • Fork 该仓库[6]后,新建 .env 文件,写入 uid=2189882895384093, uid 值为掘金主页 url 上的 Id
  • 修改 src/config.js 里的配置为你自己的配置,
  • 使用 GitHub 账户登录 vercel 导入这个项目, 即可部署成功

当然这个项目还存在一些问题,比如:

  • 需要进行SEO优化等
  • Vercel 部署 Region 选择香港,Serverless 函数访问掘金接口的速度还是有些慢。

后续

接下来我将继续分享 Next.js 相关的实战文章,欢迎各位关注我的《Next.js 全栈开发实战》 专栏。

  • 使用 Strapi CSM系统进行 Next.js 应用全栈开发
  • 使用 Notion 数据库进行 Next.js 应用全栈开发
  • 使用 Prisma 和 PostgreSQL 进行 Next.js 应用全栈开发
  • 使用 NextAuth 实现 Next.js 应用的鉴权与认证
  • 使用 React query 给 Next.js 应用全局状态管理
  • 使用 i18next 实现 Next.js 应用国际化
  • 使用 Playwright 进行 Next.js 应用的端到端测试
  • 使用 Github actions 给 Next.js 应用创建 CI/CD
  • 使用 Docker 部署 Next.js 应用
  • 将 Next.js 应用部署到腾讯云 serverless

你对哪块内容比较感兴趣呢?欢迎在评论区留言,感谢您的阅读。

[1]nextjs-juejin-blog: https://github.com/maqi1520/nextjs-juejin-blog

[2]Tailwind CSS IntelliSense: https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss

[3]json-to-typescript: https://www.runjs.cool/json-to-typescript

[4]github-markdown-css: https://github.com/sindresorhus/github-markdown-css

[5]使用 Prism.js 对代码进行语法高亮: https://juejin.cn/post/7088920558598881293

[6]nextjs-juejin-blog: https://github.com/maqi1520/nextjs-juejin-blog

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/_oRb-YXJiBCJy8xLVJJn8w

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237224次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8059次阅读
 目录