Skip to content

Commit

Permalink
fix(runtime): 修复、迭代生命周期 (#6996)
Browse files Browse the repository at this point in the history
* 固定 lodash 版本
* 生命周期 hooks 在组件销毁时,同时销毁监听回调,fix # 6598
* 修复页面为 Class 组件,子组件使用生命周期 hooks 时不触发回调的问题。
* 增加 onAddToFavorites、onShareTimeline 页面事件 hooks
* 【Breaking】使用 onShareAppMessage,useShareAppMessage 时,必须设置页面的 enableShareAppMessage 配置为 true,#6825
  • Loading branch information
Chen-jj authored Jul 13, 2020
1 parent fea55af commit b2398fc
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 32 deletions.
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) {
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])) {
(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

0 comments on commit b2398fc

Please sign in to comment.