Skip to content

Commit d8e9791

Browse files
committed
docs: 更新 taro native-apis 文章
1 parent 5ea1cfd commit d8e9791

File tree

2 files changed

+324
-45
lines changed

2 files changed

+324
-45
lines changed

docs/taro/navite-apis/README.md

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
---
2+
highlight: darcula
3+
theme: smartblue
4+
---
5+
6+
# Taro 4.0 已发布 - 6. 为啥 Taro.xxx 能访问 wx.xxx ?
7+
8+
## 1. 前言
9+
10+
大家好,我是[若川](https://juejin.cn/user/1415826704971918),欢迎关注我的[公众号:若川视野](https://mp.weixin.qq.com/s/MacNfeTPODNMLLFdzrULow)。我倾力持续组织了 3 年多[每周大家一起学习 200 行左右的源码共读活动](https://juejin.cn/post/7079706017579139102),感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。另外,想学源码,极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093),目前是掘金关注人数(6k+人)第一的专栏,写有几十篇源码文章。
11+
12+
截至目前(`2024-08-18`),[`taro 4.0` 正式版已经发布](https://github.com/NervJS/taro/releases/tag/v4.0.3),目前最新是 `4.0.4`,官方`4.0`正式版本的介绍文章暂未发布。官方之前发过[Taro 4.0 Beta 发布:支持开发鸿蒙应用、小程序编译模式、Vite 编译等](https://juejin.cn/post/7330792655125463067)
13+
14+
计划写一个 `taro` 源码揭秘系列,欢迎持续关注。
15+
16+
- [x] [Taro 源码揭秘 - 1. 揭开整个架构的入口 CLI => taro init 初始化项目的秘密](https://juejin.cn/post/7378363694939783178)
17+
- [x] [Taro 源码揭秘 - 2. 揭开整个架构的插件系统的秘密](https://juejin.cn/spost/7380195796208205824)
18+
- [x] [Taro 源码揭秘 - 3. 每次创建新的 taro 项目(taro init)的背后原理是什么](https://juejin.cn/post/7390335741586931738)
19+
- [x] [Taro 4.0 已正式发布 - 4. 每次 npm run dev:weapp 开发小程序,build 编译打包是如何实现的?](https://juejin.cn/post/7403193330271682612)
20+
- [x] [Taro 4.0 已发布 - 5.高手都在用的发布订阅机制 Events 在 Taro 中是如何实现的?](https://juejin.cn/post/7403915119448915977)
21+
- [ ] 等等
22+
23+
前面 4 篇文章都是讲述编译相关的,CLI、插件机制、初始化项目、编译构建流程。第 5 篇我们来讲些相对简单的,Taro 是如何实现发布订阅机制 Events 的。
24+
25+
学完本文,你将学到:
26+
27+
```bash
28+
等等
29+
```
30+
31+
## 2. tarojs/taro
32+
33+
```ts
34+
// packages/taro/index.js
35+
const { hooks } = require('@tarojs/runtime')
36+
const taro = require('@tarojs/api').default
37+
38+
if (hooks.isExist('initNativeApi')) {
39+
hooks.call('initNativeApi', taro)
40+
}
41+
42+
module.exports = taro
43+
module.exports.default = module.exports
44+
```
45+
46+
## src/program.ts
47+
48+
49+
```ts
50+
// packages/taro-platform-weapp/src/program.ts
51+
import { TaroPlatformBase } from '@tarojs/service'
52+
53+
// 省略若干代码
54+
55+
const PACKAGE_NAME = '@tarojs/plugin-platform-weapp'
56+
57+
export default class Weapp extends TaroPlatformBase {
58+
template: Template
59+
platform = 'weapp'
60+
globalObject = 'wx'
61+
projectConfigJson: string = this.config.projectConfigName || 'project.config.json'
62+
runtimePath = `${PACKAGE_NAME}/dist/runtime`
63+
// 省略若干代码
64+
}
65+
```
66+
67+
## src/runtime.ts
68+
69+
```ts
70+
// packages/taro-platform-weapp/src/runtime.ts
71+
import { mergeInternalComponents, mergeReconciler } from '@tarojs/shared'
72+
73+
import { components, hostConfig } from './runtime-utils'
74+
75+
mergeReconciler(hostConfig)
76+
mergeInternalComponents(components)
77+
```
78+
79+
## mergeReconciler
80+
81+
```ts
82+
// packages/shared/src/utils.ts
83+
import { hooks } from './runtime-hooks'
84+
export function mergeReconciler (hostConfig, hooksForTest?) {
85+
const obj = hooksForTest || hooks
86+
const keys = Object.keys(hostConfig)
87+
keys.forEach(key => {
88+
obj.tap(key, hostConfig[key])
89+
})
90+
}
91+
92+
```
93+
94+
```ts
95+
// packages/shared/src/utils.ts
96+
```
97+
98+
## hostConfig
99+
100+
```ts
101+
// packages/taro-platform-weapp/src/runtime-utils.ts
102+
import { Shortcuts, toCamelCase } from '@tarojs/shared'
103+
104+
import { initNativeApi } from './apis'
105+
106+
declare const getCurrentPages: any
107+
108+
export { initNativeApi }
109+
export * from './apis-list'
110+
export * from './components'
111+
export const hostConfig = {
112+
initNativeApi,
113+
getMiniLifecycle (config) {
114+
const methods = config.page[5]
115+
if (methods.indexOf('onSaveExitState') === -1) {
116+
methods.push('onSaveExitState')
117+
}
118+
return config
119+
},
120+
transferHydrateData (data, element, componentsAlias) {
121+
if (element.isTransferElement) {
122+
const pages = getCurrentPages()
123+
const page = pages[pages.length - 1]
124+
data[Shortcuts.NodeName] = element.dataName
125+
page.setData({
126+
[toCamelCase(data.nn)]: data
127+
})
128+
return {
129+
sid: element.sid,
130+
[Shortcuts.Text]: '',
131+
[Shortcuts.NodeName]: componentsAlias['#text']?._num || '8'
132+
}
133+
}
134+
},
135+
}
136+
137+
```
138+
139+
## initNativeApi
140+
141+
```ts
142+
// packages/taro-platform-weapp/src/apis.ts
143+
import { processApis } from '@tarojs/shared'
144+
145+
import { needPromiseApis } from './apis-list'
146+
147+
declare const wx: any
148+
149+
export function initNativeApi (taro) {
150+
processApis(taro, wx, {
151+
needPromiseApis,
152+
modifyApis (apis) {
153+
// fix https://github.com/NervJS/taro/issues/9899
154+
apis.delete('lanDebug')
155+
},
156+
transformMeta (api: string, options: Record<string, any>) {
157+
if (api === 'showShareMenu') {
158+
options.menus = options.showShareItems?.map(item => item === 'wechatFriends' ? 'shareAppMessage' : item === 'wechatMoment' ? 'shareTimeline' : item)
159+
}
160+
161+
return {
162+
key: api,
163+
options
164+
}
165+
}
166+
})
167+
taro.cloud = wx.cloud
168+
taro.getTabBar = function (pageCtx) {
169+
if (typeof pageCtx?.getTabBar === 'function') {
170+
return pageCtx.getTabBar()?.$taroInstances
171+
}
172+
}
173+
taro.getRenderer = function () {
174+
return taro.getCurrentInstance()?.page?.renderer ?? 'webview'
175+
}
176+
}
177+
178+
```
179+
180+
## processApis
181+
182+
```ts
183+
// packages/shared/src/native-apis.ts
184+
function processApis (taro, global, config: IProcessApisIOptions = {}) {
185+
const patchNeedPromiseApis = config.needPromiseApis || []
186+
const _needPromiseApis = new Set<string>([...patchNeedPromiseApis, ...needPromiseApis])
187+
const preserved = [
188+
'getEnv',
189+
'interceptors',
190+
'Current',
191+
'getCurrentInstance',
192+
'options',
193+
'nextTick',
194+
'eventCenter',
195+
'Events',
196+
'preload',
197+
'webpackJsonp'
198+
]
199+
200+
const apis = new Set(
201+
!config.isOnlyPromisify
202+
? Object.keys(global).filter(api => preserved.indexOf(api) === -1)
203+
: patchNeedPromiseApis
204+
)
205+
206+
if (config.modifyApis) {
207+
config.modifyApis(apis)
208+
}
209+
210+
apis.forEach(key => {
211+
if (_needPromiseApis.has(key)) {
212+
const originKey = key
213+
taro[originKey] = (options: Record<string, any> | string = {}, ...args) => {
214+
let key = originKey
215+
216+
// 第一个参数 options 为字符串,单独处理
217+
if (typeof options === 'string') {
218+
if (args.length) {
219+
return global[key](options, ...args)
220+
}
221+
return global[key](options)
222+
}
223+
224+
// 改变 key 或 option 字段,如需要把支付宝标准的字段对齐微信标准的字段
225+
if (config.transformMeta) {
226+
const transformResult = config.transformMeta(key, options)
227+
key = transformResult.key
228+
; (options as Record<string, any>) = transformResult.options
229+
// 新 key 可能不存在
230+
if (!global.hasOwnProperty(key)) {
231+
return nonsupport(key)()
232+
}
233+
}
234+
235+
let task: any = null
236+
const obj: Record<string, any> = Object.assign({}, options)
237+
238+
// 为页面跳转相关的 API 设置一个随机数作为路由参数。为了给 runtime 区分页面。
239+
setUniqueKeyToRoute(key, options)
240+
241+
// Promise 化
242+
const p: any = new Promise((resolve, reject) => {
243+
obj.success = res => {
244+
config.modifyAsyncResult?.(key, res)
245+
options.success?.(res)
246+
if (key === 'connectSocket') {
247+
resolve(
248+
Promise.resolve().then(() => task ? Object.assign(task, res) : res)
249+
)
250+
} else {
251+
resolve(res)
252+
}
253+
}
254+
obj.fail = res => {
255+
options.fail?.(res)
256+
reject(res)
257+
}
258+
obj.complete = res => {
259+
options.complete?.(res)
260+
}
261+
if (args.length) {
262+
task = global[key](obj, ...args)
263+
} else {
264+
task = global[key](obj)
265+
}
266+
})
267+
268+
// 给 promise 对象挂载属性
269+
if (['uploadFile', 'downloadFile'].includes(key)) {
270+
equipTaskMethodsIntoPromise(task, p)
271+
p.progress = cb => {
272+
task?.onProgressUpdate(cb)
273+
return p
274+
}
275+
p.abort = cb => {
276+
cb?.()
277+
task?.abort()
278+
return p
279+
}
280+
}
281+
return p
282+
}
283+
} else {
284+
let platformKey = key
285+
286+
// 改变 key 或 option 字段,如需要把支付宝标准的字段对齐微信标准的字段
287+
if (config.transformMeta) {
288+
platformKey = config.transformMeta(key, {}).key
289+
}
290+
291+
// API 不存在
292+
if (!global.hasOwnProperty(platformKey)) {
293+
taro[key] = nonsupport(key)
294+
return
295+
}
296+
if (isFunction(global[key])) {
297+
taro[key] = (...args) => {
298+
if (config.handleSyncApis) {
299+
return config.handleSyncApis(key, global, args)
300+
} else {
301+
return global[platformKey].apply(global, args)
302+
}
303+
}
304+
} else {
305+
taro[key] = global[platformKey]
306+
}
307+
}
308+
})
309+
310+
!config.isOnlyPromisify && equipCommonApis(taro, global, config)
311+
}
312+
313+
```
314+
315+
## 总结
316+
317+
318+
----
319+
320+
**如果看完有收获,欢迎点赞、评论、分享、收藏支持。你的支持和肯定,是我写作的动力。也欢迎提建议和交流讨论**
321+
322+
作者:常以**若川**为名混迹于江湖。所知甚少,唯善学。[若川的博客](https://ruochuan12.github.io)[github blog](https://github.com/ruochuan12/blog),可以点个 `star` 鼓励下持续创作。
323+
324+
最后可以持续关注我[@若川](https://juejin.cn/user/1415826704971918),欢迎关注我的[公众号:若川视野](https://mp.weixin.qq.com/s/MacNfeTPODNMLLFdzrULow)。我倾力持续组织了 3 年多[每周大家一起学习 200 行左右的源码共读活动](https://juejin.cn/post/7079706017579139102),感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。另外,想学源码,极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093),目前是掘金关注人数(6k+人)第一的专栏,写有几十篇源码文章。

docs/taro/naviteApis/README.md

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)