使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信小程序、H5、App 端等)运行的代码。实现 一次编写,多端运行。 关于 Taro 的更多详细的信息可以看官方的介绍文章 Taro - 多端开发框架
Taro 项目实现的功能强大,项目复杂而庞大,涉及到的方方面面(多端代码转换、组件、路由、状态管理、生命周期、端能力的实现与兼容等等)多,对于大多数人来说,想要深入理解其实现机制及原理,还是比较困难的。
Taro 技术揭秘系列文章将为你逐步揭开 Taro 强大的功能之后的神秘面纱,带领你深入 Taro 内部,了解 Taro 是怎样一步一步实现 一次编写,多端运行 的宏伟目标,同时也希望借此机会抛砖引玉,促进前端圈涌现出更多的,能够解决大家痛点的开源项目。
首先,我们将从负责 Taro 脚手架初始化和项目构建的的命令行工具,也就是 Taro 的入口:@tarojs/cli 开始。
taro-cli 包
taro 命令
taro-cli 包位于 Taro 工程的 packages 目录下,通过 npm install -g @tarojs/cli 全局安装后,将会生成一个taro 命令。主要负责项目初始化、编译、构建等。直接在命令行输入 taro ,会看到如下提示:
➜ taro
👽 Taro v0.0.63
Usage: taro <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
init [projectName] Init a project with default templete
build Build a project with options
update Update packages of taro
help [cmd] display help for [cmd]
包管理与发布
首先,我们需要了解 taro-cli 包与 taro 工程的关系。
将 Taro 工程 clone 下来之后,我们可以看到工程的目录结构如下,整体还是比较简单明了的。
.
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build
├── docs
├── lerna-debug.log
├── lerna.json // Lerna 配置文件
├── package.json
├── packages
│ ├── eslint-config-taro
│ ├── eslint-plugin-taro
│ ├── postcss-plugin-constparse
│ ├── postcss-pxtransform
│ ├── taro
│ ├── taro-async-await
│ ├── taro-cli
│ ├── taro-components
│ ├── taro-components-rn
│ ├── taro-h5
│ ├── taro-plugin-babel
│ ├── taro-plugin-csso
│ ├── taro-plugin-sass
│ ├── taro-plugin-uglifyjs
│ ├── taro-redux
│ ├── taro-redux-h5
│ ├── taro-rn
│ ├── taro-rn-runner
│ ├── taro-router
│ ├── taro-transformer-wx
│ ├── taro-weapp
│ └── taro-webpack-runner
└── yarn.lock
复制代码
Taro 项目主要是由一系列 npm 包组成,位于工程的 packages 目录下。它的包管理方式和 Babel 项目一样,将整个项目作为一个 monorepo 来进行管理,并且同样使用了包管理工具 Lerna。
Lerna 是一个用来优化托管在 git/npm 上的多 package 代码库的工作流的一个管理工具,可以让你在主项目下管理多个子项目,从而解决了多个包互相依赖,且发布时需要手动维护多个包的问题。
关于 Lerna 的更多介绍可以看官方文档 Lerna:A tool for managing JavaScript projects with multiple packages。
packages 目录下十几个包中,最常用的项目初始化与构建的命令行工具 taro-cli 就是其中一个。在 Taro 工程根目录运行 lerna publish 命令之后,lerna.json 里面配置好的所有的包会被发布到 npm 上去。
目录结构
taro-cli 包的目录结构如下:
./
├── bin // 命令行
│ ├── taro // taro 命令
│ ├── taro-build // taro build 命令
│ ├── taro-update // taro update 命令
│ └── taro-init // taro init 命令
├── package.json
├── node_modules
├── src
│ ├── build.js // taro build 命令调用,根据 type 类型调用不同的脚本
│ ├── config
│ │ ├── babel.js // Babel 配置
│ │ ├── babylon.js // JavaScript 解析器 babylon 配置
│ │ ├── browser_list.js // autoprefixer browsers 配置
│ │ ├── index.js // 目录名及入口文件名相关配置
│ │ └── uglify.js
│ ├── creator.js
│ ├── h5.js // 构建h5 平台代码
│ ├── project.js // taro init 命令调用,初始化项目
│ ├── rn.js // 构建React Native 平台代码
│ ├── util // 一系列工具函数
│ │ ├── index.js
│ │ ├── npm.js
│ │ └── resolve_npm_files.js
│ └── weapp.js // 构建小程序代码转换
├── templates // 脚手架模版
│ └── default
│ ├── appjs
│ ├── config
│ │ ├── dev
│ │ ├── index
│ │ └── prod
│ ├── editorconfig
│ ├── eslintrc
│ ├── gitignore
│ ├── index.js // 初始化文件及目录,copy模版等
│ ├── indexhtml
│ ├── npmrc
│ ├── pagejs
│ ├── pkg
│ └── scss
└── yarn-error.log
复制代码其中关键文件的作用已添加注释说明,大家可以先大概看看,有个初步印象。
通过上面的目录树可以看出,taro-cli 工程的文件并不算多,主要目录有:/bin、/src、/template,我已经在上面详细标注了主要的目录和文件的作用,至于具体的流程,咱们接下来再分析。
用到的核心库
tj/commander.js Node.js 命令行接口全面的解决方案,灵感来自于 Ruby’s commander。可以自动的解析命令和参数,合并多选项,处理短参等等,功能强大,上手简单。
jprichardson/node-fs-extra 在nodejs的fs基础上增加了一些新的方法,更好用,还可以拷贝模板。
chalk/chalk 可以用于控制终端输出字符串的样式。
SBoudrias/Inquirer.js NodeJs 命令行交互工具,通用的命令行用户界面集合,用于和用户进行交互。
sindresorhus/ora 加载中状态表示的时候一个loading怎么够,再在前面