Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runtime): 修复、迭代生命周期 #6996

Merged
merged 7 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/taro-api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
useTitleClick,
useOptionMenuClick,
usePullIntercept,
useShareTimeline,
useAddToFavorites,
useReady,
useRouter,
options,
Expand Down Expand Up @@ -57,6 +59,8 @@ const Taro = {
useTitleClick,
useOptionMenuClick,
usePullIntercept,
useShareTimeline,
useAddToFavorites,
useReady,
useRouter,
options,
Expand Down
2 changes: 1 addition & 1 deletion packages/taro-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"inquirer": "^5.2.0",
"klaw": "^2.1.1",
"latest-version": "^5.1.0",
"lodash": "^4.17.5",
"lodash": "4.17.19",
"mem-fs": "^1.1.3",
"mem-fs-editor": "^4.0.0",
"minimatch": "^3.0.4",
Expand Down
23 changes: 23 additions & 0 deletions packages/taro-loader/src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import { getOptions, stringifyRequest } from 'loader-utils'
import * as path from 'path'
import { frameworkMeta } from './utils'

interface PageConfig {
content: any
path: string
}

export default function (this: webpack.loader.LoaderContext) {
const options = getOptions(this)
const config = getPageConfig(options.config, this.resourcePath)
const stringify = (s: string): string => stringifyRequest(this, s)
const { isNeedRawLoader } = frameworkMeta[options.framework]
// raw is a placeholder loader to locate changed .vue resource
Expand All @@ -18,7 +24,24 @@ if (typeof PRERENDER !== 'undefined') {
}`
return `import { createPageConfig } from '@tarojs/runtime'
import component from ${stringify(componentPath)}
${config.enableShareTimeline ? 'component.enableShareTimeline = true' : ''}
${config.enableShareAppMessage ? 'component.enableShareAppMessage = true' : ''}
var inst = Page(createPageConfig(component, '${options.name}'))
${options.prerender ? prerender : ''}
`
}

function getPageConfig (configs: Record<string, PageConfig>, resourcePath: string) {
const configPath = removeExt(resourcePath) + '.config'
for (const name in configs) {
const config = configs[name]
if (removeExt(configs[name].path) === configPath) {
return config.content
}
}
return {}
}

function removeExt (file: string) {
return path.join(path.dirname(file), path.basename(file, path.extname(file)))
}
7 changes: 6 additions & 1 deletion packages/taro-mini-runner/src/plugins/MiniPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ export default class TaroMiniPlugin {
options: {
framework,
name: module.name,
prerender: this.prerenderPages.has(module.name)
prerender: this.prerenderPages.has(module.name),
config: this.filesConfig
}
})
}
Expand Down Expand Up @@ -653,6 +654,10 @@ export default class TaroMiniPlugin {

generateConfigFile (compilation: webpack.compilation.Compilation, filePath: string, config: Config & { component?: boolean }) {
const fileConfigName = this.getConfigPath(this.getComponentName(filePath))
const unOfficalConfigs = ['enableShareAppMessage', 'enableShareTimeline']
unOfficalConfigs.forEach(item => {
delete config[item]
})
const fileConfigStr = JSON.stringify(config)
compilation.assets[fileConfigName] = {
size: () => fileConfigStr.length,
Expand Down
41 changes: 28 additions & 13 deletions packages/taro-runtime/src/dsl/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CurrentReconciler } from '../reconciler'
const instances = new Map<string, Instance>()

export function injectPageInstance (inst: Instance<PageProps>, id: string) {
CurrentReconciler.mergePageInstance?.(instances.get(id), inst)
instances.set(id, inst)
}

Expand Down Expand Up @@ -86,7 +87,7 @@ export function getOnHideEventKey (path: string) {
return path + '.' + 'onHide'
}

export function createPageConfig (component: React.ComponentClass, pageName?: string, data?: Record<string, unknown>) {
export function createPageConfig (component: any, pageName?: string, data?: Record<string, unknown>) {
const id = pageName ?? `taro_page_${pageId()}`
// 小程序 Page 构造器是一个傲娇小公主,不能把复杂的对象挂载到参数上
let pageElement: TaroRootElement | null = null
Expand Down Expand Up @@ -175,18 +176,6 @@ export function createPageConfig (component: React.ComponentClass, pageName?: st
const path = getPath(id, this.options)
return safeExecute(path, 'onPageScroll', options)
},
onShareAppMessage (options) {
const target = options.target
if (target != null) {
const id = target.id
const element = document.getElementById(id)
if (element != null) {
options.target!.dataset = element.dataset
}
}
const path = getPath(id, this.options)
return safeExecute(path, 'onShareAppMessage', options)
},
onResize (options) {
const path = getPath(id, this.options)
return safeExecute(path, 'onResize', options)
Expand All @@ -210,6 +199,32 @@ export function createPageConfig (component: React.ComponentClass, pageName?: st
onPullIntercept () {
const path = getPath(id, this.options)
return safeExecute(path, 'onPullIntercept')
},
onAddToFavorites () {
const path = getPath(id, this.options)
return safeExecute(path, 'onAddToFavorites')
}
}

// onShareAppMessage 和 onShareTimeline 一样,会影响小程序右上方按钮的选项,因此不能默认注册。
if (component.onShareAppMessage || component.enableShareAppMessage) {
Copy link
Contributor

@cncolder cncolder Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Chen-jj 这里不应该是 component.prototype.onShareAppMessage 吗?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

component.onShareAppMessage 是给 Vue 用的,根据 prototype 判断可以加下,那么 Class Component 就不用配置 enable 了。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

记录一下:这样还是不完美,HOC 如 connect 后的页面组件会跪,还是要编译时处理一下以自动兼容大部分情况。

config.onShareAppMessage = function (options) {
const target = options.target
if (target != null) {
const id = target.id
const element = document.getElementById(id)
if (element != null) {
options.target!.dataset = element.dataset
}
}
const path = getPath(id, this.options)
return safeExecute(path, 'onShareAppMessage', options)
}
}
if (component.onShareTimeline || component.enableShareTimeline) {
config.onShareTimeline = function () {
const path = getPath(id, this.options)
return safeExecute(path, 'onShareTimeline')
}
}

Expand Down
32 changes: 26 additions & 6 deletions packages/taro-runtime/src/dsl/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isFunction, isArray } from '@tarojs/shared'
import { PageContext, R as React } from './react'
import { getPageInstance, injectPageInstance } from './common'
import { PageLifeCycle } from './instance'
Expand All @@ -19,19 +20,34 @@ const taroHooks = (lifecycle: keyof PageLifeCycle) => {
inst = Object.create(null)
}

inst = inst!

// callback is immutable but inner function is up to date
const callback = (...args: any) => fnRef.current(...args)
if (lifecycle !== 'onShareAppMessage') {
(inst![lifecycle] as any) = [
...((inst![lifecycle] as any) || []),
callback
]
if (lifecycle === 'onShareAppMessage') {
inst[lifecycle] = callback
} else {
inst![lifecycle] = callback
if (isFunction(inst[lifecycle])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Chen-jj 如果上面 common.ts 里修复了, 页面实例就没有默认的 onShareAppMessage 回调了, 在函数式组件里 inst[lifecycle] 永远是 undefined, 我们还是应该用 lifecycle 这个字符串上来判断, 以实现页面第一次初始化时设置 onShareAppMessage/onShareTimeline/onAddToFavorites

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createPageConfig 时 inst 是 undefined,小程序 onLoad 时才会绑定。

(inst[lifecycle] as any) = [inst[lifecycle], callback]
} else {
(inst[lifecycle] as any) = [
...((inst[lifecycle] as any) || []),
callback
]
}
}
if (first) {
injectPageInstance(inst!, id)
}
return () => {
const inst = getPageInstance(id)
const list = inst![lifecycle]
if (list === callback) {
(inst![lifecycle] as any) = undefined
} else if (isArray(list)) {
(inst![lifecycle] as any) = list.filter(item => item !== callback)
}
}
}, [])
}
}
Expand All @@ -58,6 +74,10 @@ export const useOptionMenuClick = taroHooks('onOptionMenuClick')

export const usePullIntercept = taroHooks('onPullIntercept')

export const useShareTimeline = taroHooks('onShareTimeline')

export const useAddToFavorites = taroHooks('onAddToFavorites')

export const useReady = taroHooks('onReady')

export const useRouter = (dynamic = false) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/taro-runtime/src/dsl/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export interface PageLifeCycle extends Show {
onPopMenuClick?(): void
onReady?(): void
onPullIntercept?(): void
onShareTimeline?(): void
onAddToFavorites?(): void
eh?(event: MpEvent): void
onLoad(options: Record<string, unknown>): void
onUnload(): void
Expand Down
17 changes: 17 additions & 0 deletions packages/taro-runtime/src/dsl/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ function setReconciler () {
lifecycle = 'componentDidHide'
}
return instance[lifecycle] as Function
},
mergePageInstance (prev, next) {
if (!prev || !next) return

// 子组件使用 lifecycle hooks 注册了生命周期后,会存在 prev,里面是注册的生命周期回调。
Object.keys(prev).forEach(item => {
if (item === 'onShareAppMessage') {
if (!isFunction(next[item])) next[item] = prev[item]
return
}

if (isFunction(next[item])) {
next[item] = [next[item], ...prev[item]]
} else {
next[item] = [...(next[item] || []), ...prev[item]]
}
})
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/taro-runtime/src/reconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type { TaroElement } from './dom/element'
import type { TaroText } from './dom/text'
import type { DataTree, TaroNode } from './dom/node'
import type { TaroRootElement } from './dom/root'
import type { PageInstance } from './dsl/instance'
import type { Instance, PageInstance, PageProps } from './dsl/instance'

type Inst = Instance<PageProps>

export interface Reconciler<Instance, DOMElement = TaroElement, TextElement = TaroText, DOMNode = TaroNode> {
// mini apps
Expand All @@ -26,6 +28,8 @@ export interface Reconciler<Instance, DOMElement = TaroElement, TextElement = Ta
createPullDownComponent?(el: Instance, path: string, framework)

findDOMNode?(instance: Instance): DOMElement | undefined

mergePageInstance?(prev: Inst | undefined, next: Inst): void
}

export const CurrentReconciler: Reconciler<any> = {
Expand Down
10 changes: 10 additions & 0 deletions packages/taro/types/taro.hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ declare namespace Taro {
*/
function useTabItemTap(callback: (payload: TabItemTapObject) => any): void

/**
* 用户点击右上角菜单“收藏”按钮时的回调。
*/
function useAddToFavorites(callback: (paload: AddToFavoritesObject) => AddToFavoritesReturnObject): void

/**
* 用户点击右上角菜单“分享到朋友圈”按钮时的回调。
*/
function useShareTimeline(callback: () => any): void

/**
* 页面初次渲染完成的回调。
* 此时页面已经准备妥当,可以和视图层进行交互。
Expand Down
38 changes: 28 additions & 10 deletions packages/taro/types/taro.lifecycle.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ declare namespace Taro {
* 转发事件来源
* `button`:页面内转发按钮
* `menu`:右上角转发菜单
*
*
* @since 1.2.4
*/
from?: 'button' | 'menu' | string
/**
* 如果 `from` 值是 `button`,则 `target` 是触发这次转发事件的 `button`,否则为 `undefined`
*
*
* @since 1.2.4
*/
target?: object
/**
* 页面中包含 `<web-view>` 组件时,返回当前 `<web-view>` 的 url
*
*
* @since 1.6.4
*/
webViewUrl?: string
Expand All @@ -62,7 +62,7 @@ declare namespace Taro {
* 支持PNG及JPG
* 显示图片长宽比是 5:4
* 默认使用截图
*
*
* @since 1.5.0
*/
imageUrl?: string
Expand All @@ -71,26 +71,44 @@ declare namespace Taro {
interface TabItemTapObject {
/**
* 被点击tabItem的序号,从 0 开始
*
* @since 1.9.0
*/
index: string

/**
* 被点击tabItem的页面路径
*
* @since 1.9.0
*/
pagePath: string

/**
* 被点击tabItem的按钮文字
*
* @since 1.9.0
*/
text: string
}

interface AddToFavoritesObject {
/**
* 页面中包含web-view组件时,返回当前web-view的url
*/
webviewUrl: string
}

interface AddToFavoritesReturnObject {
/**
* 自定义标题
*/
title?: string

/**
* 自定义图片,显示图片长宽比为 1:1
*/
imageUrl?: string

/**
* 自定义query字段
*/
query?: string
}

type GetDerivedStateFromProps<P, S> =
/**
* Returns an update to a component's state based on its new props and old state.
Expand Down