十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Expo JavaScript 风格指南:在 expo 仓库中如何写出可读、可维护、可自动化的 JS/React 代码

Expo JavaScript 风格指南:在 expo 仓库中如何写出可读、可维护、可自动化的 JS/React 代码 Expo JavaScript 风格指南在 expo 仓库中如何写出可读、可维护、可自动化的 JS/React 代码【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo导读本文是对 guides/Expo JavaScript Style Guide.md 的完整展开与仓库级深化。该指南是 Expo 团队面向本仓库一个跨 React Native、React Web 与 Node 三种运行环境的 monorepo制定的 JavaScript 写作规范覆盖代码格式化、注释与 import 排序、命名法则、const/let取舍以及 Babel 工具链取舍。读完本文你将掌握一套可以立刻在 React Native / Expo 项目中落地执行的编码约定并理解它们如何通过 ESLint 与 Prettier 被自动化为团队级别的纪律。一、这份指南的背景与适用边界Expo 是一个同时驱动 Android、iOS 与 Web 的应用框架代码库横跨三类运行环境React 组件、纯 Web 逻辑与 Node.js 工具链。风格指南开篇即强调一个前提大部分约定在整个仓库通用但某些写法在不同环境之间确实存在差异因此规则应在“广泛适用”与“分环境微调”之间取得平衡。本指南的目标清晰而克制优先保证团队阅读代码的体验并简化写代码时的大量琐碎决策。它不追求发明一套宏大教条而是用尽量少、尽量容易记忆、尽量能被 Linter 自动强制执行的规则把人力从格式争论中解放出来。站在“领先但不冒进”的现代 JavaScript 上指南对语言能力的使用给出了一条原则性描述使用ES20xx 规范中已经稳定的特性加上少量扩展如 JSX整体站位是“靠近前沿但不踩在流血边缘”near the leading edge but away from the bleeding edge。这一条原则可以在仓库的解析配置中得到印证在 packages/eslint-config-universe/shared/core.js 中解析器选项配置为ecmaVersion: 2022、sourceType: module、并启用jsx: true与impliedStrict: true——即代码按 ES2022 模块语义解析、默认启用严格模式同时允许 JSX。env中同时启用了es2022与jest说明这套解析基线既服务于应用代码也服务于测试代码。二、ESLint 与 Prettier把风格纪律交给工具Expo 把“错误分级”作为 ESLint 配置的设计哲学error错误只要检测到会导致代码无法工作的隐患就报 errorwarning警告只用于风格或格式上的吹毛求疵整体配置写得宽松lenient因此你几乎永远不需要写/* eslint-disable */。指南甚至建议如果发现非用 disable 不可的场合就反馈给维护者调整配置本身让规则“始终可用”而不是靠局部豁免。仓库中 eslint-config-universe 的 README 把这一哲学总结得很直白严重问题如语法错误标记为 error风格问题标记为 warning这样团队可以制定类似“提交不允许引入新 errorwarning 只要不是你这次引入的就可以容忍”的策略。同时它刻意“宽松”把更大的判断自由度留给有经验的团队靠代码评审氛围来沉淀规范而不是让规则越收越死。在实际规则清单shared/core.js里你可以清楚地看到这条“分级”边界判为 error 的导致代码必然出错no-const-assign、no-delete-var、no-dupe-args、no-dupe-class-members、no-dupe-keys、no-duplicate-case、no-func-assign、no-invalid-regexp、no-new-symbol、no-undef、use-isnan、valid-typeof、constructor-super等判为 warn 的风格与卫生curlyall所有分支强制大括号、eqeqeqsmart、no-var、prefer-const、no-debugger、no-alert被关闭React Native 的Alert是合法 API不能一刀切禁止等关于语法能力的关键规则no-unused-vars配置为args: none不检查函数形参便于保留某些签名一致性、caughtErrors: all且caughtErrorsIgnorePattern: ^_catch 到的变量若以下划线开头则豁免——这与下文“私有命名用下划线”的约定呼应。为什么“跑 ESLint 就等于跑 Prettier”指南明确指出ESLint 依赖 Prettier 检查代码格式并支持自动重排在 Expo 的配置下运行 ESLint 即同时执行了 Prettier 检查。背后的机制是 ESLint 的 prettier 插件shared/prettier.js 中extends: [prettier]用来关闭所有与 Prettier 冲突的格式类规则然后通过plugins: [prettier]注册prettier/prettier检查面向用户应用的新一代配置 eslint-config-expo 同样把eslint-plugin-prettier作为开发依赖让 Prettier 的排版差异直接以 ESLint 诊断形式暴露。ESLint 提供--fix开关自动修复可修复的诊断。并非所有问题都能自动修但相当一部分可以其中就包括 Prettier 报出的格式问题——这正是本仓库根目录 package.json将 lint 接入turbo lint工作流的前提机器能修的先修完人工只看真正需要判断的点。编辑器集成文字清单由于配置入口在 ESLint 层编辑器只要集成 ESLint 就自动获得了 Prettier 能力。常用方案VS Code安装 ESLint 扩展dbaeumer.vscode-eslint即可Atom / Nuclidelinter-eslint包Sublime TextSublimeLinter-eslintEmacsFlycheck 的javascript-eslint检查器建议配合“向上查找最近 node_modules 中的 ESLint”的辅助如add-node-modules-path保证用项目本地版本而非全局版本VimSyntastic 的eslint语法检查器同样配置为使用node_modules中最近的 ESLint。三、格式化Formatting3.1 Prettier 与 Expo 的具体配置Expo 使用带 Expo 定制项的 Prettier处理绝大部分代码排版。仓库中实际存在的配置文件给出了确切的参数值packages/eslint-config-universe/.prettierrc 与 packages/eslint-config-expo/.prettierrc{ printWidth: 100, tabWidth: 2, singleQuote: true, bracketSameLine: true, trailingComma: es5 }各项含义括号内为仓库配置采用的默认取向printWidth: 100每行宽度上限 100 列兼顾代码密度与横向阅读舒适度Prettier 默认 80tabWidth: 2缩进 2 空格统一用空格不用 Tab 制表singleQuote: true字符串优先使用单引号bracketSameLine: trueJSX 多行属性时结束的与最后一个属性同行对多行 JSX 树更友好trailingComma: es5仅在 ES5 合法的地方数组、对象、函数参数列表除外启用尾随逗号——这也是面向“稳定 ES 语法”立场的体现。仓库根 package.json 声明的 Prettier 版本为~3.8.3各子包配置跟随同一基线tools/.prettierrc 也保持完全一致的参数确保工具链代码与应用代码排版同源。官方指南建议如果你想覆盖 Expo 默认值只需在项目根新建.prettierrc即可自定义。3.2 局部豁免与整体豁免局部豁免在希望原样保留的表达式上方加一行// prettier-ignore文件其余部分仍由 Prettier 接管。指南给出的典型场景是手工对齐的矩阵/表格型数组——一旦交给 Prettier 自动排版精心对齐的列会被全部打散// prettier-ignore let matrix [ -c, 1, 1, 1, -c, 1, 1, 1, -c, ];整体豁免希望整个文件不参与格式化时把文件路径加入仓库的.prettierignore仓库内如 packages/expo/.prettierignore 这类按包维护的忽略文件即是该机制的应用。3.3 “保持文件总是 pretty”的隐性纪律指南提醒一个经常被忽视的连锁效应Prettier 是按整个文件重新排版的。因此日常提交时必须保持文件整体始终处于已格式化状态——否则某天任何人对该文件任意改动一行并保存diff 里会混入大量与他本次改动无关的全文件重排噪音。这正是仓库接入lint-staged见根 package.json的原因之一提交前只对暂存文件跑格式化与 lint把“全文件漂移”挡在 commit 之外。3.4 注释Comments绝大多数场景用//行注释类、方法等结构体上方用/** block */文档式块注释行中内嵌的短注释用/* inline block */提交到 GitHub 之前必须删除被注释掉的死代码。// CORRECT /** * Gets the latest version of Android thats been released. This is a version * string like 7.1 instead of the code name Nougat. */ function getLatestAndroidVersion() { // Keep this logic in sync with Googles versioning scheme return maxBy(getAndroidVersions(/* includePrereleases */ false), linearizeSemver); }注意这个片段同时示范了三类注释各归其位函数头上的块注释负责“为什么、语义约束”函数体内行注释负责“实现注意点”参数位置的/* includePrereleases */则给一个原本难以读懂的false做了就地标注。3.5 Imports排序约定与真实规则现状原文档特别加了免责声明目前并没有一个成熟的 linter 插件能可靠地对 import 排序做程序化检查因此本节被定位为“轻量指引”不要求评审者为此耗费精力。有趣的对照是在 eslint-config-universe 的 core.js 中面向内部包的import/order规则已经启用分组为builtinexternal→internal→parent/index/sibling组间强制空行并开启字母序说明仓库内部工具链层面正逐步把这份“人工排序”上升为可自动校验的规则。对贡献者而言最稳妥的策略仍是按下面的顺序手写整齐因为它同时也符合解析器与人的直觉。推荐的排序规则import语句整体排在require()调用之前JS 会提升import代码书写顺序应体现这一点无赋值的副作用导入import side-effect放在有赋值的导入import React from react之前——副作用通常希望尽早执行分组顺序外部模块与 Node 内置模块path、react→别名内部模块→相对路径模块../a/b、./c各组内部按被导入的模块名排序而非变量名使用 ASCII 顺序大写在前、小写在后、scoped 包scope/...最后单条语句内按default → namespace → named的顺序书写。// CORRECT import side-effect; import invariant from invariant; import Expo, { Audio } from expo; import path from path; import HomeScreen from ../screens/HomeScreen; import Colors from ../style/Colors; import calculateViewport from ../style/calculateViewport; import LoginButton from ./LoginButton; const assert require(assert);// CORRECT —— 组内按模块名 ASCII 排序大写 小写 scope import Z from Z; import b from x; import a from y; import p from scope/package;// CORRECT —— default 在 namespace 前namespace 在 named 前 import a, * as b, { c } from module;3.6 React 与 JSX 的组件书写顺序指南建议一个固定“自上而下”的类组件结构降低读者扫读成本顶部放置Props/State 类型声明与静态方法之后是constructor 与生命周期方法最后是render 及 render 调用的方法、其余方法。JSX 排版一律交给 Prettier。指南给出示范细节可直接复用为规范模板——注意它同时演示了 3.5 的导入分组、命名的下划线私有方法约定、以及“methods near the top”的次序// CORRECT type Props { title: string, onPress?: event void, }; type State { isPressed: boolean, }; class Button extends React.Component { props: Props; state: State { isPressed: true, }; constructor(props, context) { super(props, context); this.state { ...this.state, bounce: new Animated.Value(1), }; } componentWillUnmount() { if (this.state.animation) { this.state.animation.stop(); } } render() { return ( Animated.View onPress{this._handlePress} style{{ transform: [{ scale: this.state.bounce }] }} Text {this.props.title} /Text /Animated.View ); } _handlePress event { this._bounce(); if (this.props.onPress) { this.props.onPress(event); } }; _bounce() { this.setState(state { state.bounce.setValue(0); let animation Animated.spring(state.bounce, { toValue: 1 }); animation.start(({ finished }) { if (finished) { this.setState(() ({ animation: null })); } }); return { animation }; }); } }四、命名Naming总原则优先照顾读者。一个容易被 grep 的名字收益极高——更容易找到它的所有使用点、更容易重命名与重构、对上下文更不敏感class TestPipeline { // PREFERRED runTests() { ... } // DISFAVORED run() { ... } } // runTests 比 run 更容易被 grep 到且不多啰嗦就讲清了自己的职责4.1 类、函数与变量camelCase 首字母规则统一使用 camelCase类名与构造函数名大写开头其余一律小写开头// CORRECT class Aquarium { filterWater() {...} } function Fish() {...} Object.assign(Fish.prototype, ...); function populateAquarium(aquarium, school) {...}// INCORRECT class house { CloseWindows() {...} } function EstimatePrice(house) {...}4.2 异步函数以Async结尾凡是可能异步完成的 async 函数、以及其它返回 Promise 的函数命名统一加Async后缀——这是一种“看名字即知需await”的信号常对应 I/O 操作// CORRECT async function fetchAccountAsync(accountId: ID): PromiseAccount { ... }后缀取决于调用点观感而非实现关键字即便函数没有写async只要它在调用处表现为异步就用同一约定// CORRECT —— 手写 Promise 的“异步形”函数同样带 Async function readSettingsFileAsync(): Promisestring { return Promise((resolve, reject) { fs.readFile(settings.txt, utf8, ...); }); }唯一的例外是“函数自身只做同步工作、仅仅返回 Promise”的纯组合型函数——此时省略后缀更准确因为读者看到的是它对 Promise 的纯操作// OK function multiplexPromises(promises: Promise*[]): PromiseArray* | Error { // 接收 Promise 数组、返回结果或错误的 Promise 数组。语义上这个函数 // 自身不做异步工作真正异步的是被操作的这些 Promise。 }4.3 私有变量下划线前缀用_前缀标记设计上属于私有的实例变量。指南给出的取舍是下划线既传达了“这里存的是私有数据”又保持了简单可访问性——便于调试、测试以及极少量场景下的补丁式访问不强制用#硬私有// CORRECT class Counter { _currentNumber 0; getNextNumber() { ... } }同一约定可扩展到模块内私有函数/变量帮助读者一眼区分“仅当前模块使用”的成员// CORRECT export default function prettyPrintAll(values) { for (let value of values) { _prettyPrint(value); } } function _prettyPrint(value) { ... }在 ESLint 层core.js 的no-unused-vars也以下划线豁免catch参数^_与这套下划线语义保持一致。4.4 布尔变量动词前缀消歧义布尔命名容易产生“描述对象/状态”与“引用对象”之间的歧义。用is、was、did等动词开头能显著提升清晰度// AMBIGUOUS console.log(history.deleted); // CLEAR console.log(history.isDeleted);五、声明const优先let按需指南明确要求能写const就写const需要重新赋值时才用let。文档甚至坦诚地给出了这个取舍背后的成本核算只看代码质量更严谨的语义是用const表达“这个变量确实是个常量”用let表达“未来会被重赋值”——即便某些const只是“暂时还没被重赋值”但把“写代码 评审代码”的注意力成本一起算进来const where possible规则简单到无需逐处斟酌语义且能被 Linter 稳定强制并自动修复core.js 中prefer-const即设为 warndestructuring 也纳入检查最终取舍用可接受的少量“语义精度”换取团队整体注意力的显著下降。这是整篇指南“务实主义”的缩影。六、综合示例把整套规范串起来指南用一个自包含的GreetingText组件演示规范的“合奏”效果——导入分组、const默认、下划线私有方法、StyleSheet.create集中定义样式import Expo from expo; import PropTypes from prop-types; import React from react; import { StyleSheet, Text } from react-native; import Log from ../log/Log; import Colors from ../style/Colors; export default class GreetingText extends React.PureComponent { static propTypes { greeting: PropTypes.string.isRequired, ...Text.propTypes, }; componentDidUpdate() { Log.info(The greeting was re-rendered); } render() { let { greeting, style, ...props } this.props; return ( Text {...props} onPress{this._handlePress} style{[styles.greeting, style]} {greeting} /Text ); } _handlePress event { alert(Congratulations!); }; } const styles StyleSheet.create({ greeting: { color: Colors.energetic, fontSize: 30, }, });七、Babel让 ESLint 与“稳定新语法”共存为使用“足够稳定”的新语法Expo 依赖 Babel 做转译范围基本限定在已定稿的 ECMAScript 标准特性不盲目追 stage 提案。关键在于解析链路Expo 使用babel-eslint让 ESLint 走 Babel 解析器。指南点破了真实痛点较新的语法扩展会使 Babel 产生 ESLint 原生无法消费的 AST 节点因此“Linter 兼容性稳定”也是团队挑选 Babel 插件的硬性筛选条件——一个语法特性如果会让 lint 全线崩溃就不值得引入。这与本文开头“领先但远离出血边缘”的原则互为表里语法基线由 Babel 解析能力决定而解析能力又反过来约束团队敢不敢用某个新语法。八、如何在你的 Expo 项目里落地这套规范仓库内已经沉淀出可直接复用的工具链成果按需取用即可纯 Expo 应用在项目中安装 eslint-config-expo其 peer 依赖要求eslint 8.10并内建eslint-plugin-prettier、react hooks 等能力继承其default/flat配置内部 monorepo 多环境项目参考 eslint-config-universe 的 README 的用法——universe通用、universe/nativeReact Native Expo、universe/web浏览器 React/JSX、universe/nodeNode 工具链多平台项目可通过数组叠加多个配置格式化基线直接复用仓库的 .prettierrcprintWidth: 100、2 空格缩进、单引号、bracketSameLine、trailingComma: es5如要覆盖则在项目根新建.prettierrcCI 层面Expo 仓库自身通过 package.json 的lint脚本turbo lint在 monorepo 内逐包执行 ESLint并配合lint-staged只对暂存文件做检查——这套“整仓 lint 提交前 staged lint”的组合可以直接复刻到任意规模的项目中。阅读建议若你是第一次向 Expo 仓库提交代码建议把本文与仓库内 Expo JavaScript Style Guide、以及 eslint-config-universe/shared/core.js 中的规则清单对照阅读写完后运行一次 ESLint 的--fix剩下的 warning 再逐条人工判断——这正是 Expo 团队自己每天的工作方式。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表