package.json 配置完全解读

发表于 2年以前  | 总阅读数:529 次

package.json 是前端每个项目都有的 json 文件,位于项目的根目录。许多脚手架在搭建项目时也会自动帮我们自动初始化好 package.json。

package.json 里面有许许多多的配置,与项目息息相关,了解它们有助于了解项目,提效开发,规范代码。

今天主要介绍一些常见配置,我把它们分为了 7 大类:

  • 描述配置

  • 文件配置

  • 脚本配置

  • 依赖配置

  • 发布配置

  • 系统配置

  • 第三方配置

1 . 描述配置

主要是项目的基本信息,包括名称,版本,描述,仓库,作者等,部分会展示在 npm 官网上。

name

项目的名称,如果是第三方包的话,其他人可以通过该名称使用 npm install 进行安装。

"name": "react"

version

项目的版本号,开源项目的版本号通常遵循 semver 语义化规范,具体规则如下图所示:

简单介绍一下:

  • 1 代表主版本号 Major,通常在涉及重大功能更新,产生了破坏性变更时会更新此版本号

  • 2 代表次版本号 Minor,在引入了新功能,但未产生破坏性变更,依然向下兼容时会更新此版本号

  • 3 代表修订号 Patch,在修复了一些问题,也未产生破坏性变更时会更新此版本号

除了 X.Y.Z 这样的标准版本号,还有 Pre-release 和 Metadata 来描述项目的测试版本,关于 semver 规范更多的内容,可以参考https://juejin.cn/post/7122240572491825160 。

回到 package.json 的 version 字段,name + version 能共同构成一个完全唯一的项目标识符,所以它两是最重要的两个字段。

"version": "18.2.0"

repository

项目的仓库地址以及版本控制信息。

"repository": {
  "type": "git",
  "url": "https://github.com/facebook/react.git",
  "directory": "packages/react"
}

description

项目的描述,会展示在 npm 官网,让别人能快速了解该项目。

"description": "React is a JavaScript library for building user interfaces."

keywords

一组项目的技术关键词,比如 Ant Design 组件库的 keywords 如下:

"keywords": [
  "ant",
  "component",
  "components",
  "design",
  "framework",
  "frontend",
  "react",
  "react-component",
  "ui"
 ],

好的关键词可以帮助别人在 npm 官网上更好地检索到此项目,增加曝光率。

homepage

项目主页的链接,通常是项目 github 链接,项目官网或文档首页。

"homepage": "https://reactjs.org/"

bugs

项目 bug 反馈地址,通常是 github issue 页面的链接。

"bugs": "https://github.com/vuejs/core/issues"

license

项目的开源许可证。项目的版权拥有人可以使用开源许可证来限制源码的使用、复制、修改和再发布等行为。常见的开源许可证有 BSD、MIT、Apache 等,它们的区别可以参考:如何选择开源许可证?https://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html

"license": "MIT"

author

项目作者。

"author": "Li jiaxun",

2 . 文件配置

包括项目所包含的文件,以及入口等信息。

files

项目在进行 npm 发布时,可以通过 files 指定需要跟随一起发布的内容来控制 npm 包的大小,避免安装时间太长。

发布时默认会包括 package.json,license,README 和main 字段里指定的文件。忽略 node_modules,lockfile 等文件。

在此基础上,我们可以指定更多需要一起发布的内容。可以是单独的文件,整个文件夹,或者使用通配符匹配到的文件。

"files": [
  "filename.js",
  "directory/",
  "glob/*.{js,json}"
 ]

一般情况下,files 里会指定构建出来的产物以及类型文件,而 src,test 等目录下的文件不需要跟随发布。

type

在 node 支持 ES 模块后,要求 ES 模块采用 .mjs 后缀文件名。只要遇到 .mjs 文件,就认为它是 ES 模块。如果不想修改文件后缀,就可以在 package.json文件中,指定 type 字段为 module。

"type": "module"

这样所有 .js 后缀的文件,node 都会用 ES 模块解释。

# 使用 ES 模块规范
$ node index.js

如果还要使用 CommonJS 模块规范,那么需要将 CommonJS 脚本的后缀名都改成.cjs,不过两种模块规范最好不要混用,会产生异常报错。

main

项目发布时,默认会包括 package.json,license,README 和main 字段里指定的文件,因为 main 字段里指定的是项目的入口文件,在 browser 和 Node 环境中都可以使用。

如果不设置 main 字段,那么入口文件就是根目录下的 index.js

比如 packageA 的 main 字段指定为 index.js。

"main": "./index.js"

我们引入 packageA 时,实际上引入的就是 node_modules/packageA/index.js

这是早期只有 CommonJS 模块规范时,指定项目入口的唯一属性。

browser

main 字段里指定的入口文件在 browser 和 Node 环境中都可以使用。如果只想在 web 端使用,不允许在 server 端使用,可以通过 browser 字段指定入口。

"browser": "./browser/index.js"

module

同样,项目也可以指定 ES 模块的入口文件,这就是 module 字段的作用。

"module": "./index.mjs"

当一个项目同时定义了 main,browser 和 module,像 webpack,rollup 等构建工具会感知这些字段,并会根据环境以及不同的模块规范来进行不同的入口文件查找。

"main": "./index.js", 
"browser": "./browser/index.js",
"module": "./index.mjs"

比如 webpack 构建项目时默认的 target 为 'web',也就是 Web 构建。它的 resolve.mainFeilds 字段默认为 ['browser', 'module', 'main']。

module.exports = {
  //...
  resolve: {
    mainFields: ['browser', 'module', 'main'],
  },
};

此时会按照 browser -> module -> main 的顺序来查找入口文件。

exports

node 在 14.13 支持在 package.json 里定义 exports 字段,拥有了条件导出的功能。

exports 字段可以配置不同环境对应的模块入口文件,并且当它存在时,它的优先级最高。

比如使用 requireimport 字段根据模块规范分别定义入口:

"exports": {
  "require": "./index.js",
  "import": "./index.mjs"
 }
}

这样的配置在使用 import 'xxx'require('xxx') 时会从不同的入口引入文件,exports 也支持使用 browsernode 字段定义 browser 和 Node 环境中的入口。

上方的写法其实等同于:

"exports": {
  ".": {
    "require": "./index.js",
    "import": "./index.mjs"
  }
 }
}

为什么要加一个层级,把 require 和 import 放在 "." 下面呢?

因为 exports 除了支持配置包的默认导出,还支持配置包的子路径。

比如一些第三方 UI 包需要引入对应的样式文件才能正常使用。

import `packageA/dist/css/index.css`;

我们可以使用 exports 来封装文件路径:

"exports": {
  "./style": "./dist/css/index.css'
},

用户引入时只需:

import `packageA/style`;

除了对导出的文件路径进行封装,exports 还限制了使用者不可以访问未在 "exports" 中定义的任何其他路径。

比如发布的 dist 文件里有一些内部模块 dist/internal/module ,被用户单独引入使用的话可能会导致主模块不可用。为了限制外部的使用,我们可以不在 exports 定义这些模块的路径,这样外部引入 packageA/dist/internal/module 模块的话就会报错。

结合上面入口文件配置的知识,再来看看下方 vite 官网推荐的第三方库入口文件的定义,就很容易理解了。

workspaces

项目的工作区配置,用于在本地的根目录下管理多个子项目。可以自动地在 npm install 时将 workspaces 下面的包,软链到根目录的 node_modules 中,不用手动执行 npm link 操作。

workspaces 字段接收一个数组,数组里可以是文件夹名称或者通配符。比如:

"workspaces": [
  "workspace-a"
]

表示在 workspace-a 目录下还有一个项目,它也有自己的 package.json。

package.json
workspace-a
  └── package.json

通常子项目都会平铺管理在 packages 目录下,所以根目录下 workspaces 通常配置为:

"workspaces": [
  "packages/*"
]

3 . 脚本配置

scripts

指定项目的一些内置脚本命令,这些命令可以通过 npm run 来执行。通常包含项目开发,构建 等 CI 命令,比如:

"scripts": {
  "build": "webpack"
}

我们可以使用命令 npm run build / yarn build 来执行项目构建。

除了指定基础命令,还可以配合 pre 和 post 完成命令的前置和后续操作,比如:

"scripts": {
  "build": "webpack",
  "prebuild": "xxx", // build 执行之前的钩子
  "postbuild": "xxx" // build 执行之后的钩子
}

当执行 npm run build 命令时,会按照 prebuild -> build -> postbuild 的顺序依次执行上方的命令。

但是这样的隐式逻辑很可能会造成执行工作流的混乱,所以 pnpm 和 yarn2 都已经废弃掉了这种 pre/post 自动执行的逻辑,参考 pnpm issue 讨论 https://github.com/pnpm/pnpm/issues/2891

如果需要手动开启,pnpm 项目可以设置 .npmrc enable-pre-post-scripts=true。

enable-pre-post-scripts=true

config

config 用于设置 scripts 里的脚本在运行时的参数。比如设置 port 为 3001:

"config": {
  "port": "3001"
}

在执行脚本时,我们可以通过 npm_package_config_port 这个变量访问到 3001。

console.log(process.env.npm_package_config_port); // 3001

4 . 依赖配置

项目可能会依赖其他包,需要在 package.json 里配置这些依赖的信息。

dependencies

运行依赖,也就是项目生产环境下需要用到的依赖。比如 react,vue,状态管理库以及组件库等。

使用 npm install xxx 或则 npm install xxx --save 时,会被自动插入到该字段中。

"dependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}

devDependencies

开发依赖,项目开发环境需要用到而运行时不需要的依赖,用于辅助开发,通常包括项目工程化工具比如 webpack,vite,eslint 等。

使用 npm install xxx -D 或者 npm install xxx --save-dev 时,会被自动插入到该字段中。

"devDependencies": {
  "webpack": "^5.69.0"
}

peerDependencies

同伴依赖,一种特殊的依赖,不会被自动安装,通常用于表示与另一个包的依赖与兼容性关系来警示使用者。

比如我们安装 A,A 的正常使用依赖 B@2.x 版本,那么 B@2.x 就应该被列在 A 的 peerDependencies 下,表示“如果你使用我,那么你也需要安装 B,并且至少是 2.x 版本”。

比如 React 组件库 Ant Design,它的 package.json 里 peerDependencies 为

"peerDependencies": {
  "react": ">=16.9.0",
  "react-dom": ">=16.9.0"
}

表示如果你使用 Ant Design,那么你的项目也应该安装 react 和 react-dom,并且版本需要大于等于 16.9.0。

optionalDependencies

可选依赖,顾名思义,表示依赖是可选的,它不会阻塞主功能的使用,安装或者引入失败也无妨。这类依赖如果安装失败,那么 npm 的整个安装过程也是成功的。

比如我们使用 colors 这个包来对 console.log 打印的信息进行着色来增强和区分提示,但它并不是必需的,所以可以将其加入到 optionalDependencies,并且在运行时处理引入失败的逻辑。

使用 npm install xxx -O 或者 npm install xxx --save-optional 时,依赖会被自动插入到该字段中。

"optionalDependencies": {
  "colors": "^1.4.0"
}

peerDependenciesMeta

同伴依赖也可以使用 peerDependenciesMeta 将其指定为可选的。

"peerDependencies": {
  "colors": "^1.4.0"
},
"peerDependenciesMeta": {
  "colors": {
    "optional": true
   }
 }

bundleDependencies

打包依赖。它的值是一个数组,在发布包时,bundleDependencies 里面的依赖都会被一起打包。

比如指定 react 和 react-dom 为打包依赖:

"bundleDependencies": [
  "react",
  "react-dom"
]

在执行 npm pack 打包生成 tgz 压缩包中,将出现 node_modules 并包含 react 和 react-dom。

需要注意的是,这个字段数组中的值必须是在 dependencies,devDependencies 两个里面声明过的依赖才行。

普通依赖通常从 npm registry 安装,但当你想用一个不在 npm registry 里的包,或者一个被修改过的第三方包时,打包依赖会比普通依赖更好用。

overrides

overrides 可以重写项目依赖的依赖,及其依赖树下某个依赖的版本号,进行包的替换。

比如某个依赖 A,由于一些原因它依赖的包 foo@1.0.0 需要替换,我们可以使用 overrides 修改 foo 的版本号:

"overrides": {
  "foo": "1.1.0-patch"
}

当然这样会更改整个依赖树里的 foo,我们可以只对 A 下的 foo 进行版本号重写:

"overrides": {
  "A": {
    "foo": "1.1.0-patch",
  }
}

overrides 支持任意深度的嵌套。

如果在 yarn 里也想复写依赖版本号,需要使用 resolution 字段,而在 pnpm 里复写版本号需要使用 pnpm.overrides 字段。

5 . 发布配置

主要是和项目发布相关的配置。

private

如果是私有项目,不希望发布到公共 npm 仓库上,可以将 private 设为 true。

"private": true

publishConfig

顾名思义,publishConfig 就是 npm 包发布时使用的配置。

比如在安装依赖时指定了 registry 为 taobao 镜像源,但发布时希望在公网发布,就可以指定 publishConfig.registry。

"publishConfig": {
  "registry": "https://registry.npmjs.org/"
}

6 . 系统配置

和项目关联的系统配置,比如 node 版本或操作系统兼容性之类。这些要求只会起到提示警告的作用,即使用户的环境不符合要求,也不影响安装依赖包。

engines

一些项目由于兼容性问题会对 node 或者包管理器有特定的版本号要求,比如:

"engines": {
  "node": ">=14 <16",
  "pnpm": ">7"
}

要求 node 版本大于等于 14 且小于 16,同时 pnpm 版本号需要大于 7。

os

在 linux 上能正常运行的项目可能在 windows 上会出现异常,使用 os 字段可以指定项目对操作系统的兼容性要求。

"os": ["darwin", "linux"]

cpu

指定项目只能在特定的 CPU 体系上运行。

"cpu": ["x64", "ia32"]

7 . 第三方配置

一些第三方库或应用在进行某些内部处理时会依赖这些字段,使用它们时需要安装对应的第三方库。

types 或者 typings

指定 TypeScript 的类型定义的入口文件

"types": "./index.d.ts",

unpkg

可以让 npm 上所有的文件都开启 CDN 服务。

比如 vue package.json 的 unpkg 定义为 dist/vue.global.js

"unpkg": "dist/vue.global.js",

当我们想通过 CDN 的方式使用链接引入 vue 时。

访问 https://unpkg.com/vue 会重定向到 https://unpkg.com/vue@3.2.37/dist/vue.global.js,其中 3.2.27 是 Vue 的最新版本。

jsdelivr

与 unpkg 类似,vue 通过如下的配置

"jsdelivr": "dist/vue.global.js",

访问 https://cdn.jsdelivr.net/npm/vue 实际上获取到的是 jsdelivr 字段里配置的文件地址。

browserslist

设置项目的浏览器兼容情况。babel 和 autoprefixer 等工具会使用该配置对代码进行转换。当然你也可以使用 .browserslistrc 单文件配置。

"browserslist": [
  "> 1%",
  "last 2 versions"
]

sideEffects

显示设置某些模块具有副作用,用于 webpack 的 tree-shaking 优化。

比如在项目中整体引入 Ant Design 组件库的 css 文件。

import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'

如果 Ant Design 的 package.json 里不设置 sideEffects,那么 webapck 构建打包时会认为这段代码只是引入了但并没有使用,可以 tree-shaking 剔除掉,最终导致产物缺少样式。

所以 Ant Design 在 package.json 里设置了如下的 sideEffects,来告知 webpack,这些文件具有副作用,引入后不能被删除。

"sideEffects": [
  "dist/*",
  "es/**/style/*",
  "lib/**/style/*",
  "*.less"
]

lint-staged

lint-staged 是用于对 git 的暂存区的文件进行操作的工具,比如可以在代码提交前执行 lint 校验,类型检查,图片优化等操作。

"lint-staged": {
  "src/**/*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "git add -A"
  ]
}

lint-staged 通常配合 husky 这样的 git-hooks 工具一起使用。git-hooks 用来定义一个钩子,这些钩子方法会在 git 工作流程中比如 pre-commit,commit-msg 时触发,可以把 lint-staged 放到这些钩子方法中。

8 . 结语

今天我们简单了解了 package.json 的常见配置。有了这些知识,我敢说绝大多数项目的 package.json 你都能毫无压力的阅读。

但 package.json 里的内容远不止如此,比如 semver 规范,入口文件,项目依赖等都还有很多值得深入的内容,认识他们只是第一步。

9 . 参考

https://docs.npmjs.com/cli/v8/configuring-npm/package-json

https://zhuanlan.zhihu.com/p/548202395 by WangHaoyu

https://juejin.cn/post/7095903278084390948 by 摸鱼的春哥

https://juejin.cn/post/7122240572491825160 by 摸鱼的春哥

https://zhuanlan.zhihu.com/p/29855253 by 风清洋

https://juejin.cn/post/7023539063424548872 by CUGGZ

https://segmentfault.com/a/1190000016365409 by senntyou

https://github.com/SunshowerC/blog/issues/8 by SunshowerC

https://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html by 阮一峰

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

 相关推荐

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

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

发布于: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年以前  |  237227次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录