Open yleo77 opened 6 years ago
数十年以前, Sun 公司为了推广 Java 相关技术, 提出了一句 Slogon:
Write once, run anywhere
但是被 Web & Native 开发人员熟知却是在近年, 源于移动开发的兴起. Javascript 作为运行于浏览器端天然具有跨平台能力的语言, 自然被推到了前沿, 坊间一时兴起 Native 程序借助于 Webview 的形式完成 APP 的主要业务逻辑, 也就是上面那句 slogon 的那句含义了, coding 一次, 多端运行, 这期间也诞生了很多非常不错的工具库, 例如 cordova.
开发是省事了, 但用户体验实在是无法保证, 尤其是在五六年之前.
Learn once, write anywhere
这是 React Native 自诞生一来, 一直没变的 slogon. Facebook 重新出发, 和上面稍有区别, 没有变的是 Javascript, 变的是结合了实际情况(大多数情况是指 iOS 及 Android 俩个平台的差异), 在 React 基础上, 打造出 React Native, 利用 Javascript 来完成核心业务逻辑, 依赖于 React 和实际渲染层的解耦做到了利用 Native 来完成 UI 层绘制这一特性, 一定程度上保证了开发效率, 又兼顾了用户体验.
本文包含两部分:
本文适合以下人看
本文 不 包括以下话题:
对于前端开发人员来说, 可能对于 div 这个标签再熟悉不过了. 借助于 React 这个框架, 我们可以像写 HTML 标签 以及他们的嵌套一样使用已经写好的组件, 这一点也类似于 Vue. 在 React Native 中, 也有非常非常基础的一个 tag 就是 view (更准确得说这些都应该叫做UI 层组件, 这里为了和非常熟悉的知识建立链接, 先理解为 tag)
div
view
在普通的网页中, 我们可以通过 div 结合一些其他常用标签, 搭建出一个页面. 作为 React Native 的开始, 我们看看在 React Native 中怎么用这些 tag 搭建一个 Native 页面.
FYI, 和 WEB 稍有不同的是, 在 React Native 中想要显示文字片段, 必须用 Text 包裹, 这点需要注意.
Text
OK, 先来看看环境配置相关.
环境配置分为两部分. iOS 的配置 和 Android的配置.
iOS 的配置延续了苹果家的一贯风格, 如果装好了 xcode 的话, 理论上不需要做任何配置.
Android 的配置较为麻烦些, 包括 模拟器的安装和 Android SDK 的安装配置.
Android SDK 的安装是通过在 Android Studio 中完成的, 安装完之后需要配置 SDK 的相关环境变量. 虚拟机推荐使用 Genymotion, 无它, 简单易用, 安装好之后只需要修改下 ADB 的路径.
关于 Android 更详细的配置, 可以查看官方文档: Getting Started
和开发网页较为类似, 需要熟知一些常用的 tag (先这么理解). 除了上面提到的 View, Text, 还有 Image, Button, 这几个对 Web 开发人员来说就再熟悉不过了. 再增加一个: StyleSheet, ScrollView.
View
Image
Button
StyleSheet
ScrollView
最后这俩个标签如其名, 也都能猜到它们的作用, 一个是为了控制页面样式, 一个是创建了一个有滚动区域的视图, 这俩点 稍有不同于网页, 当内容多余既定宽高时可以自动出现滚动条.
# 安装 create-react-native-app npm install -g create-react-native-app # 创建项目 create-react-native-app react-native-simple-example # 少不了的安装依赖 cd react-native-simple-example && npm i # 依赖安装完毕后, 启动项目 npm start
# 在开始前, 不妨将示例 git 库检出到本地, 该库包含了示例文件. git clone https://github.com/jd-smart-fe/react-native-simple-example.git
根据提示, 按 i 在 iOS 模拟器中预览. 这个过程如果不出意外是自动的. 工具包会自动在 iOS 模拟器中安装Expo Client APP, 安装完毕后便可以预览我们的 APP 了. 接下来做一些较简单的修改.
打开 App.js, 将文件内容修改为和这个文件保持一致. 如果不出意外, 当保存文件时, iOS 模拟器会 自动刷新, 这是我们使用 React Native 的带来的一大好处, 免去了编译的麻烦. 当然了, 对前端工程师来说可能觉得这没什么, 但对于 Native 工程师来说可就完全不是这样了.
第一步的完成效果如下图所示, 用到了几个非常非常基础的标签, 同时点击 Enter Button 会在控制台打出 Clicked 类似的日志输出.
这一步完成后的代码示例: react-native-simple-example:v1.0.0
接下来, 为这个简单的页面增加类似 Native APP 常见的场景跳转功能. 这一步, 稍微麻烦一些, 因为我们调整了目录结构使之更合理化, 引入了新的包 react-navigation 来完成这一功能, 最终的源码文件可以通过这里看到. 也可以借助 git diff v2.0.0 v1.0.0 来查看本次的修改.
git diff v2.0.0 v1.0.0
看 List.js 源码文件也会发现, 我们使用了一个变量存储了想要显示在 ui 中的数据. 在真实的世界中, 数据往往都会来源于服务器端, 而不是客户端, 在下面这一步我们就给这个 APP 增加 网络请求 这个功能.
这一步完成后的代码示例: react-native-simple-example:v2.0.0
完成后的代码可以通过这里看到, 得益于 React Native 的良好封装, Web 开发者不用再学习新的 API 就可以搞定. 代码中我们通过调用前端开发人员非常熟悉的 fetch API 来完成. 当然如果想使用 XMLHttpRequest 也 OK, 甚至于如果想使用更抽象的上层封装例如 axios 也没有任何问题. 此外, 这一步骤用到了新的模块 ActivityIndicator 来展示数据加载过程中的 Loading 状态.
fetch
XMLHttpRequest
ActivityIndicator
这一步完成后的代码示例: react-native-simple-example:v3.0.0
再怎么强调 如何调试 的重要性都不为过. 就我自己的实际经验来看, 绝大多数人都将性能看的太过于重要, 总是经常提起这个这个影响性能, 那个提高性能等等, 反而忽视了如何调试应用程序的重要性.
打开菜单的快捷键, 必须熟知. iOS 为 CMD-D, Android 为 CMD-M.
按上面提到的菜单快捷键, 可以看到菜单中提供了一些较为常见的调试功能, 好奇哪个点哪个.
例如选择 Debug Remote JS 菜单项可以打开一个 Chrome Tab, 打开该 tab 的控制台可以看到一些它的基本信息, 包括应用的 log 输出, 网络请求等, 注意 Element Inspect 可不是在这里查看.
先看怎么查看这些最基本的 tag.
用 React 开发过网页的同学应该都知道 React Developer Tools 对开发调试的重要性, 这是一款 Chrome 插件, 能够查看网页组件树的层级结构, 每个组件持有的状态以及从上层传递过来的 props, 也包括了一些小 trick, 例如在插件提供的组件树面板(类似Chrome Devtool 的 Elements 面板), 当选中一个组件时, 此时在控制台按 $r 可以快速访问到该实例.
$r
在 React Native 中同样有办法做到. 首先需要安装 react-devtools , 这是一个标准的 npm 包. npm install -g react-devtools 就可以了. 它提供了和上述 chrome 插件近乎一样的功能. 因为 chrome 插件只能运行在 chrome 浏览器中, 而该工具可不受这样的限制, 所以它的适用面就更光一些了, 比如移动端浏览器, webview 等.
npm install -g react-devtools
使用方法呢, 安装好了, 直接命令行运行即可, 一般情况下会自动连接到所要调试的 React APP. 二般情况下呢, 根据提示在所要调试的页面中插入一段 js 代码即可.
效果如下:
默认情况下, 上面提到的Chrome 控制台中网络请求面板是看不到代码中发起的网络请求的. 但在开发过程中, 抓包查看 HTTP 返回的数据是经常需要做的事情, 这一点可以通过修改代码来满足这一调试需求.
// 将以下代码添加到程序入口处, 引入 react native 之后的地方 GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
补充, 做这样的设置之后, 可能需要在某些情况下, 服务器需要做 CORS 的配置, 如图中划线所示.
延伸讨论: Show network requests such as fetch, WebSocket etc. in chrome dev tools
待补充吧
以上通过一个简单例子, 来说明了如何开始使用 React Native 开发一款 APP, 涉及到了
距离最基本的一个 APP 还差很远, 例如 动画, 异步存储, 手势操作 等等等等, 也还差最后的一个闭环: 发布到应用商店, 后续把这部分加入进来.
首先回答为什么要有这一部分内容. 因为这篇文章的主旨是快速实际上手 React Native, 所以用到了create-react-native-app. 它和 create-react-app 的定位极其类似, 提供了最方便的方式来使我们快速启动一个 React Native 项目, 而不用关心它的配置, 当你不依赖 Native 自定义模块时甚至都不需要 xcode 或者 Android Studio.
create-react-native-app 某些功能依赖于 Expo, 所以这里也对 Expo 做一个简单的介绍.
Expo 是一个开发工具集合, 它囊括了开发原生 APP 过程中需要的 Tools, Library, Services.
用一句话概括,就是使用 Expo, 能让 开始开发 App 这一路径变得更短一些. 同时, 也可以利用 Expo 来 build 出可以直接发布到 Apple Store 或者 Google Play 商店的应用(不过有一定的限制).
Expo 之于 React-Native 就像 Rails 之于 Ruby, Sails 之于 Node. 能够让你快速着眼于业务开发.
具体一点, 它都提供了以下几点能力
等等.
via: frequently asked questions
换句话说, 如果在开发过程中, 确定不会用到自定义的 Native 模块, 那 Expo 完全可以覆盖到整个 APP 的开发生命周期, 直到上线.
这部分只涉及 Expo 相关的安装, 其他的一些和 react native 开发相关的安装依赖项, 例如 Node.js, watchman 等这里不做重复.
分两部分的安装, 桌面端的 Expo XDE 和手机端的 Expo APP.
XDE 是 Expo Development Environment 的缩写, 看名字也大概就知道它的作用了, 为开发者提供了一个桌面端的集成开发环境.
那 APP 又是什么鬼, 起初我看到它时的第一反映是我就是为了开发 APP, 怎么这里还有个 APP, 不禁好奇它的作用. 结合 Expo 的开发过程也就很容易理解了, 这个 APP 的作用可以理解为一个容器, 一个浏览器, 目的在于快速预览开发成果.
这个预览是如何做到的? 是这样的, 当我们在借助 Expo 开发 APP 时, 它会为应用提供一个类似 URL 可以实时预览效果的地址, 形如 exp://192.168.X.X:19000 , 通过手机端的 Expo APP 能够打开这样的地址并提供预效果, 和浏览器打开 URL 的过程非常类似. 因为 Expo 的预览功能是基于 ngrok 来完成的, 所以 Expo APP 和开发机即便不在一个局域网内, 也不会影响到它的预览功能.
exp://192.168.X.X:19000
其他的一些依赖, 在 react native 的安装中都有介绍, 不重复了.
和 Expo XDE 对应的有一个 cli 版本的工具 exp, 标准的 npm 包, 这里略过, 感兴趣可以看这里: exp Command-Line Interface.
先以一图作为这一小节的开端.
一图胜千言, 从图中基本上可以看到 expo 为开发者提供的功能了. 即可以通过它创建出新的项目, 也可以将已有的项目通过它 run 起来, 但是这有一定的条件.
如果正在开发的 APP 是以 create-react-native-app 创建出来的, 在项目目录下执行 npm start 之后, 根据对应的提示按 a (对应在 Android 模拟器中打开项目) 或者 i (在 iOS 模拟器中打开项目) 就可以在对应的模拟器中看到实际效果了. 打开 Expo XDE 选择刚才创建出来的 APP, 会看到这样的界面:
npm start
如提示, 左边为脚本 build 结果, 右边为设备 log 所输出的信息.
根据我这边的实际使用情况来看, 当希望在 iOS 模拟器中查看项目时, 直接按 i 即可; 但是在 Android 模拟器中查看时, 需要先将 Android 模拟器打开, 同时将 Expo APP 安装进模拟器才可以, 否则会报错, 这点和文档描述稍有出入, 需要注意.
这里先补充一个上面提到但用户可能感兴趣的话题: 如果想用 Expo, 但项目又依赖到 Native 自定义模块,怎么办? 两种办法:
以上, 对 Expo 做一个简单的介绍, 这里也只是就我的初步使用做一个简单入手介绍. 更多的介绍请移步官方文档描述: Expo - Quick Start.
这篇文章对 React Native 做了一个简单的介绍. 也是我第一次用 org-mode 来写文档, 真正感触到了 org-mode 的强大, 愈发觉得大纲模式对之于对写文档这件事情来说所提供的不可或缺的重要性, 犹如用过 vi 之后, 就再也接受不了不支持 类似 vi 移动编辑操作的编辑器了.
背景
数十年以前, Sun 公司为了推广 Java 相关技术, 提出了一句 Slogon:
但是被 Web & Native 开发人员熟知却是在近年, 源于移动开发的兴起. Javascript 作为运行于浏览器端天然具有跨平台能力的语言, 自然被推到了前沿, 坊间一时兴起 Native 程序借助于 Webview 的形式完成 APP 的主要业务逻辑, 也就是上面那句 slogon 的那句含义了, coding 一次, 多端运行, 这期间也诞生了很多非常不错的工具库, 例如 cordova.
开发是省事了, 但用户体验实在是无法保证, 尤其是在五六年之前.
这是 React Native 自诞生一来, 一直没变的 slogon. Facebook 重新出发, 和上面稍有区别, 没有变的是 Javascript, 变的是结合了实际情况(大多数情况是指 iOS 及 Android 俩个平台的差异), 在 React 基础上, 打造出 React Native, 利用 Javascript 来完成核心业务逻辑, 依赖于 React 和实际渲染层的解耦做到了利用 Native 来完成 UI 层绘制这一特性, 一定程度上保证了开发效率, 又兼顾了用户体验.
本文包含两部分:
本文适合以下人看
本文 不 包括以下话题:
上手篇
对于前端开发人员来说, 可能对于
div
这个标签再熟悉不过了. 借助于 React 这个框架, 我们可以像写 HTML 标签 以及他们的嵌套一样使用已经写好的组件, 这一点也类似于 Vue. 在 React Native 中, 也有非常非常基础的一个 tag 就是view
(更准确得说这些都应该叫做UI 层组件, 这里为了和非常熟悉的知识建立链接, 先理解为 tag)在普通的网页中, 我们可以通过
div
结合一些其他常用标签, 搭建出一个页面. 作为 React Native 的开始, 我们看看在 React Native 中怎么用这些 tag 搭建一个 Native 页面.FYI, 和 WEB 稍有不同的是, 在 React Native 中想要显示文字片段, 必须用
Text
包裹, 这点需要注意.OK, 先来看看环境配置相关.
环境配置
环境配置分为两部分. iOS 的配置 和 Android的配置.
iOS 配置
iOS 的配置延续了苹果家的一贯风格, 如果装好了 xcode 的话, 理论上不需要做任何配置.
Android 配置
Android 的配置较为麻烦些, 包括 模拟器的安装和 Android SDK 的安装配置.
Android SDK 的安装是通过在 Android Studio 中完成的, 安装完之后需要配置 SDK 的相关环境变量. 虚拟机推荐使用 Genymotion, 无它, 简单易用, 安装好之后只需要修改下 ADB 的路径.
关于 Android 更详细的配置, 可以查看官方文档: Getting Started
开发
和开发网页较为类似, 需要熟知一些常用的 tag (先这么理解). 除了上面提到的
View
,Text
, 还有Image
,Button
, 这几个对 Web 开发人员来说就再熟悉不过了. 再增加一个:StyleSheet
,ScrollView
.最后这俩个标签如其名, 也都能猜到它们的作用, 一个是为了控制页面样式, 一个是创建了一个有滚动区域的视图, 这俩点 稍有不同于网页, 当内容多余既定宽高时可以自动出现滚动条.
根据提示, 按 i 在 iOS 模拟器中预览. 这个过程如果不出意外是自动的. 工具包会自动在 iOS 模拟器中安装Expo Client APP, 安装完毕后便可以预览我们的 APP 了. 接下来做一些较简单的修改.
第一步 最基本的标签使用
打开 App.js, 将文件内容修改为和这个文件保持一致. 如果不出意外, 当保存文件时, iOS 模拟器会 自动刷新, 这是我们使用 React Native 的带来的一大好处, 免去了编译的麻烦. 当然了, 对前端工程师来说可能觉得这没什么, 但对于 Native 工程师来说可就完全不是这样了.
第一步的完成效果如下图所示, 用到了几个非常非常基础的标签, 同时点击 Enter Button 会在控制台打出 Clicked 类似的日志输出.
这一步完成后的代码示例: react-native-simple-example:v1.0.0
TODO 第二步 增加场景切换功能
接下来, 为这个简单的页面增加类似 Native APP 常见的场景跳转功能. 这一步, 稍微麻烦一些, 因为我们调整了目录结构使之更合理化, 引入了新的包 react-navigation 来完成这一功能, 最终的源码文件可以通过这里看到. 也可以借助
git diff v2.0.0 v1.0.0
来查看本次的修改.看 List.js 源码文件也会发现, 我们使用了一个变量存储了想要显示在 ui 中的数据. 在真实的世界中, 数据往往都会来源于服务器端, 而不是客户端, 在下面这一步我们就给这个 APP 增加 网络请求 这个功能.
这一步完成后的代码示例: react-native-simple-example:v2.0.0
第三步 通过网络请求获取真实数据
完成后的代码可以通过这里看到, 得益于 React Native 的良好封装, Web 开发者不用再学习新的 API 就可以搞定. 代码中我们通过调用前端开发人员非常熟悉的
fetch
API 来完成. 当然如果想使用XMLHttpRequest
也 OK, 甚至于如果想使用更抽象的上层封装例如 axios 也没有任何问题. 此外, 这一步骤用到了新的模块ActivityIndicator
来展示数据加载过程中的 Loading 状态.这一步完成后的代码示例: react-native-simple-example:v3.0.0
调试
再怎么强调 如何调试 的重要性都不为过. 就我自己的实际经验来看, 绝大多数人都将性能看的太过于重要, 总是经常提起这个这个影响性能, 那个提高性能等等, 反而忽视了如何调试应用程序的重要性.
打开菜单的快捷键, 必须熟知. iOS 为 CMD-D, Android 为 CMD-M.
默认的调试功能
按上面提到的菜单快捷键, 可以看到菜单中提供了一些较为常见的调试功能, 好奇哪个点哪个.
例如选择 Debug Remote JS 菜单项可以打开一个 Chrome Tab, 打开该 tab 的控制台可以看到一些它的基本信息, 包括应用的 log 输出, 网络请求等, 注意 Element Inspect 可不是在这里查看.
组件树的调试
先看怎么查看这些最基本的 tag.
用 React 开发过网页的同学应该都知道 React Developer Tools 对开发调试的重要性, 这是一款 Chrome 插件, 能够查看网页组件树的层级结构, 每个组件持有的状态以及从上层传递过来的 props, 也包括了一些小 trick, 例如在插件提供的组件树面板(类似Chrome Devtool 的 Elements 面板), 当选中一个组件时, 此时在控制台按
$r
可以快速访问到该实例.在 React Native 中同样有办法做到. 首先需要安装 react-devtools , 这是一个标准的 npm 包.
npm install -g react-devtools
就可以了. 它提供了和上述 chrome 插件近乎一样的功能. 因为 chrome 插件只能运行在 chrome 浏览器中, 而该工具可不受这样的限制, 所以它的适用面就更光一些了, 比如移动端浏览器, webview 等.使用方法呢, 安装好了, 直接命令行运行即可, 一般情况下会自动连接到所要调试的 React APP. 二般情况下呢, 根据提示在所要调试的页面中插入一段 js 代码即可.
效果如下:
网络请求的调试
默认情况下, 上面提到的Chrome 控制台中网络请求面板是看不到代码中发起的网络请求的. 但在开发过程中, 抓包查看 HTTP 返回的数据是经常需要做的事情, 这一点可以通过修改代码来满足这一调试需求.
效果如下:
补充, 做这样的设置之后, 可能需要在某些情况下, 服务器需要做 CORS 的配置, 如图中划线所示.
延伸讨论: Show network requests such as fetch, WebSocket etc. in chrome dev tools
其他
待补充吧
TODO 与第三方 Native 模块的集成
TODO 打包发布
小小结
以上通过一个简单例子, 来说明了如何开始使用 React Native 开发一款 APP, 涉及到了
距离最基本的一个 APP 还差很远, 例如 动画, 异步存储, 手势操作 等等等等, 也还差最后的一个闭环: 发布到应用商店, 后续把这部分加入进来.
Expo 部分
这是什么
首先回答为什么要有这一部分内容. 因为这篇文章的主旨是快速实际上手 React Native, 所以用到了create-react-native-app. 它和 create-react-app 的定位极其类似, 提供了最方便的方式来使我们快速启动一个 React Native 项目, 而不用关心它的配置, 当你不依赖 Native 自定义模块时甚至都不需要 xcode 或者 Android Studio.
create-react-native-app 某些功能依赖于 Expo, 所以这里也对 Expo 做一个简单的介绍.
Expo 是一个开发工具集合, 它囊括了开发原生 APP 过程中需要的 Tools, Library, Services.
用一句话概括,就是使用 Expo, 能让 开始开发 App 这一路径变得更短一些. 同时, 也可以利用 Expo 来 build 出可以直接发布到 Apple Store 或者 Google Play 商店的应用(不过有一定的限制).
Expo 之于 React-Native 就像 Rails 之于 Ruby, Sails 之于 Node. 能够让你快速着眼于业务开发.
具体一点, 它都提供了以下几点能力
等等.
via: frequently asked questions
换句话说, 如果在开发过程中, 确定不会用到自定义的 Native 模块, 那 Expo 完全可以覆盖到整个 APP 的开发生命周期, 直到上线.
如何使用
安装
这部分只涉及 Expo 相关的安装, 其他的一些和 react native 开发相关的安装依赖项, 例如 Node.js, watchman 等这里不做重复.
分两部分的安装, 桌面端的 Expo XDE 和手机端的 Expo APP.
XDE 是 Expo Development Environment 的缩写, 看名字也大概就知道它的作用了, 为开发者提供了一个桌面端的集成开发环境.
那 APP 又是什么鬼, 起初我看到它时的第一反映是我就是为了开发 APP, 怎么这里还有个 APP, 不禁好奇它的作用. 结合 Expo 的开发过程也就很容易理解了, 这个 APP 的作用可以理解为一个容器, 一个浏览器, 目的在于快速预览开发成果.
这个预览是如何做到的? 是这样的, 当我们在借助 Expo 开发 APP 时, 它会为应用提供一个类似 URL 可以实时预览效果的地址, 形如
exp://192.168.X.X:19000
, 通过手机端的 Expo APP 能够打开这样的地址并提供预效果, 和浏览器打开 URL 的过程非常类似. 因为 Expo 的预览功能是基于 ngrok 来完成的, 所以 Expo APP 和开发机即便不在一个局域网内, 也不会影响到它的预览功能.其他的一些依赖, 在 react native 的安装中都有介绍, 不重复了.
和 Expo XDE 对应的有一个 cli 版本的工具 exp, 标准的 npm 包, 这里略过, 感兴趣可以看这里: exp Command-Line Interface.
使用
先以一图作为这一小节的开端.
一图胜千言, 从图中基本上可以看到 expo 为开发者提供的功能了. 即可以通过它创建出新的项目, 也可以将已有的项目通过它 run 起来, 但是这有一定的条件.
如果正在开发的 APP 是以 create-react-native-app 创建出来的, 在项目目录下执行
npm start
之后, 根据对应的提示按 a (对应在 Android 模拟器中打开项目) 或者 i (在 iOS 模拟器中打开项目) 就可以在对应的模拟器中看到实际效果了. 打开 Expo XDE 选择刚才创建出来的 APP, 会看到这样的界面:如提示, 左边为脚本 build 结果, 右边为设备 log 所输出的信息.
根据我这边的实际使用情况来看, 当希望在 iOS 模拟器中查看项目时, 直接按 i 即可; 但是在 Android 模拟器中查看时, 需要先将 Android 模拟器打开, 同时将 Expo APP 安装进模拟器才可以, 否则会报错, 这点和文档描述稍有出入, 需要注意.
小小结
这里先补充一个上面提到但用户可能感兴趣的话题: 如果想用 Expo, 但项目又依赖到 Native 自定义模块,怎么办? 两种办法:
以上, 对 Expo 做一个简单的介绍, 这里也只是就我的初步使用做一个简单入手介绍. 更多的介绍请移步官方文档描述: Expo - Quick Start.
总结
这篇文章对 React Native 做了一个简单的介绍. 也是我第一次用 org-mode 来写文档, 真正感触到了 org-mode 的强大, 愈发觉得大纲模式对之于对写文档这件事情来说所提供的不可或缺的重要性, 犹如用过 vi 之后, 就再也接受不了不支持 类似 vi 移动编辑操作的编辑器了.
参考资料