├── example
├── pages
│ ├── index.css
│ └── index.js
├── .umirc.js
├── package.json
├── tsconfig.json
└── yarn.lock
├── .fatherrc.js
├── src
├── index.ts.tpl
├── fixCustomizedRuntime.tsx.tpl
├── runtime.tsx.tpl
└── index.js
├── .gitignore
├── .prettierrc
├── package.json
├── LICENSE
├── .github
└── workflows
│ └── publish.yml
├── README.md
└── README_EN.md
/example/pages/index.css:
--------------------------------------------------------------------------------
1 |
2 | .normal {
3 | background: #79F2AA;
4 | }
5 |
--------------------------------------------------------------------------------
/.fatherrc.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | target: 'node',
4 | cjs: 'babel',
5 | };
6 |
--------------------------------------------------------------------------------
/example/.umirc.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | export default {
4 | plugins: [
5 | 'umi-plugin-keep-alive'
6 | ],
7 | }
8 |
--------------------------------------------------------------------------------
/src/index.ts.tpl:
--------------------------------------------------------------------------------
1 | export { KeepAlive, useActivate, useUnactivate, withActivation, withAliveScope, useAliveController, autoFixContext } from 'react-activation';
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /yarn.lock
3 | /package-lock.json
4 | /example/dist
5 | /example/node_modules
6 | /example/pages/.umi
7 | /example/pages/.umi-production
8 | /lib
9 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "semi": false,
5 | "printWidth": 100,
6 | "overrides": [
7 | {
8 | "files": ".prettierrc",
9 | "options": { "parser": "json" }
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/fixCustomizedRuntime.tsx.tpl:
--------------------------------------------------------------------------------
1 | import * as customizedRuntime from '@/app';
2 |
3 | const key = 'rootContainer';
4 |
5 | export function rootContainer(container, ...rest) {
6 | const customizedRootContainer = customizedRuntime[key];
7 | return typeof customizedRootContainer === 'function' ? customizedRootContainer(container, ...rest) : container;
8 | };
9 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": ".umirc.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "umi-plugin-keep-alive": "^0.0.1-beta.2"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { KeepAlive } from 'umi'
3 |
4 | function Counter() {
5 | const [count, setCount] = useState(0)
6 |
7 | return (
8 |
9 |
count: {count}
10 |
11 |
12 | )
13 | }
14 |
15 | export default function() {
16 | const [show, setShow] = useState(true)
17 |
18 | return (
19 |
20 |
Page index
21 | {show && (
22 |
23 |
24 |
25 | )}
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/runtime.tsx.tpl:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AliveScope, NodeKey } from 'react-activation';
3 |
4 | // @ts-ignore
5 | NodeKey.defaultProps.onHandleNode = (node, mark) => {
6 | // 因异步组件 loaded 后会去掉 LoadableComponent 层,导致 nodeKey 变化,缓存定位错误
7 | // 故排除对 LoadableComponent 组件的标记,兼容 dynamicImport
8 | if (node.type && node.type.displayName === 'LoadableComponent') {
9 | return undefined;
10 | }
11 |
12 | return mark;
13 | };
14 |
15 | // 兼容因使用 rootContainer 导致 access 权限无效问题 (传入 routes 带有 unaccessible 才能成功)
16 | const Wrapper = ({ children, ...props }) => (
17 | React.createElement(AliveScope, null, React.cloneElement(children, { ...children.props, ...props }))
18 | );
19 |
20 | export function rootContainer(container: React.ReactNode) {
21 | return React.createElement(Wrapper, null, container);
22 | }
23 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "build/dist",
6 | "sourceMap": true,
7 | "jsx": "react",
8 | "declaration": false,
9 | "module": "es2015",
10 | "moduleResolution": "node",
11 | "emitDecoratorMetadata": true,
12 | "experimentalDecorators": true,
13 | "importHelpers": true,
14 | "target": "es5",
15 | "typeRoots": ["node_modules/@types"],
16 | "lib": ["es2018", "dom"],
17 | "allowSyntheticDefaultImports": true,
18 | "rootDirs": ["/src", "/test", "/mock", "./typings"],
19 | "forceConsistentCasingInFileNames": true,
20 | "noImplicitReturns": true,
21 | "suppressImplicitAnyIndexErrors": true,
22 | "noUnusedLocals": true,
23 | "allowJs": true,
24 | "paths": {
25 | "@/*": ["./src/*"]
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "umi-plugin-keep-alive",
3 | "version": "0.0.1-beta.35",
4 | "description": " for umi base on react-activation",
5 | "authors": [
6 | "xiaohuoi <448627663@qq.com>",
7 | "CJY <375564567@qq.com>"
8 | ],
9 | "repository": "alitajs/umi-plugin-keep-alive",
10 | "peerDependencies": {
11 | "umi": "3.x || 4.x"
12 | },
13 | "main": "lib/index.js",
14 | "scripts": {
15 | "build": "father-build"
16 | },
17 | "keywords": [
18 | "React Keep Alive",
19 | "Keep Alive",
20 | "keep-alive",
21 | "KeepAlive",
22 | "react-activation",
23 | "activation",
24 | "react cache",
25 | "umi keep alive",
26 | "umijs",
27 | "cache"
28 | ],
29 | "devDependencies": {
30 | "father-build": "^1.8.0"
31 | },
32 | "files": [
33 | "lib",
34 | "src"
35 | ],
36 | "license": "MIT",
37 | "dependencies": {
38 | "react-activation": "^0.12.1"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018-present xiaohuoni (448627663@qq.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/js-md5@^0.4.2":
6 | version "0.4.2"
7 | resolved "https://registry.yarnpkg.com/@types/js-md5/-/js-md5-0.4.2.tgz#95b39911e2081bf2915436e61cc345e12459e5bb"
8 |
9 | hoist-non-react-statics@^3.3.0:
10 | version "3.3.0"
11 | resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
12 | dependencies:
13 | react-is "^16.7.0"
14 |
15 | js-md5@^0.7.3:
16 | version "0.7.3"
17 | resolved "https://registry.yarnpkg.com/js-md5/-/js-md5-0.7.3.tgz#b4f2fbb0b327455f598d6727e38ec272cd09c3f2"
18 |
19 | react-is@^16.7.0:
20 | version "16.9.0"
21 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb"
22 |
23 | react-keep-alive@^2.4.1:
24 | version "2.4.1"
25 | resolved "https://registry.yarnpkg.com/react-keep-alive/-/react-keep-alive-2.4.1.tgz#145b226f0680e6518b585dccea733b8987ec465e"
26 | dependencies:
27 | "@types/js-md5" "^0.4.2"
28 | hoist-non-react-statics "^3.3.0"
29 | js-md5 "^0.7.3"
30 |
31 | umi-plugin-keep-alive@^0.0.1-beta.2:
32 | version "0.0.1-beta.2"
33 | resolved "https://registry.yarnpkg.com/umi-plugin-keep-alive/-/umi-plugin-keep-alive-0.0.1-beta.2.tgz#c2cd47a80578f12e1fb42f5250474b2deeee11e4"
34 | dependencies:
35 | react-keep-alive "^2.4.1"
36 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: npm-publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | npm-publish:
10 | name: npm-publish
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@master
15 |
16 | - name: Check if version has been updated
17 | id: check
18 | uses: EndBug/version-check@v1
19 | with:
20 | file-url: https://unpkg.com/umi-plugin-keep-alive/package.json
21 | static-checking: localIsNew
22 |
23 | - name: Log if version has been updated
24 | if: steps.check.outputs.changed == 'true'
25 | run: 'echo "Version change found in commit ${{ steps.check.outputs.commit }}! New version: ${{ steps.check.outputs.version }} (${{ steps.check.outputs.type }})"'
26 |
27 | - name: Set up Node.js
28 | if: steps.check.outputs.changed == 'true'
29 | uses: actions/setup-node@master
30 | with:
31 | node-version: 13.11.0
32 |
33 | - name: Install Dependencies
34 | if: steps.check.outputs.changed == 'true'
35 | uses: bahmutov/npm-install@v1
36 | with:
37 | args: install
38 | useLockFile: false
39 |
40 | - name: Build
41 | if: steps.check.outputs.changed == 'true'
42 | run: 'sudo npm run build'
43 |
44 | - name: Publish
45 | if: steps.check.outputs.changed == 'true'
46 | uses: pascalgn/npm-publish-action@1.3.9
47 | with:
48 | commit_pattern: "^Release (\\S+)"
49 | env:
50 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # umi-plugin-keep-alive
2 |
3 | 中文说明 | [English](./README_EN.md)
4 |
5 | [](https://npmjs.org/package/umi-plugin-keep-alive)
6 | [](https://npmjs.org/package/umi-plugin-keep-alive)
7 |
8 | 此 `` 功能基于 [react-activation](https://github.com/CJY0208/react-activation/blob/master/README_CN.md)
9 |
10 | ## 在线示例
11 |
12 | umi 多 tabs 示例:https://codesandbox.io/s/umi-keep-alive-tabs-demo-knfxy
13 |
14 | ## 使用方法
15 |
16 | 1. 安装
17 |
18 | ```bash
19 | npm install umi-plugin-keep-alive --save
20 | # or
21 | yarn add umi-plugin-keep-alive
22 | ```
23 |
24 | 2. 从 `umi` 中导出 `KeepAlive`,包裹在需要被缓存的组件上
25 |
26 | ```javascript
27 | import { useState } from 'react'
28 | import { KeepAlive } from 'umi'
29 |
30 | function Counter() {
31 | const [count, setCount] = useState(0)
32 |
33 | return (
34 |
35 |
count: {count}
36 |
37 |
38 | )
39 | }
40 |
41 | export default function() {
42 | const [show, setShow] = useState(true)
43 |
44 | return (
45 |
46 |
Page index
47 | {show && (
48 |
49 |
50 |
51 | )}
52 |
53 |
54 | )
55 | }
56 | ```
57 |
58 | ## 文档
59 |
60 | 所有来自 [react-activation](https://github.com/CJY0208/react-activation/blob/master/README_CN.md) 都可以由 `umi` 导出
61 |
62 | ```javascript
63 | import {
64 | KeepAlive,
65 | useActivate,
66 | useUnactivate,
67 | withActivation,
68 | withAliveScope,
69 | useAliveController
70 | } from 'umi'
71 | ```
72 |
73 | 访问 [react-activation](https://github.com/CJY0208/react-activation/blob/master/README_CN.md) 查阅完整的文档
74 |
75 | ## LICENSE
76 |
77 | MIT
78 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | # umi-plugin-keep-alive
2 |
3 | [](https://npmjs.org/package/umi-plugin-keep-alive)
4 | [](https://npmjs.org/package/umi-plugin-keep-alive)
5 |
6 | `` for umijs base on [react-activation](https://github.com/CJY0208/react-activation)
7 |
8 | English | [中文说明](./README.md)
9 |
10 | ## Online Demo
11 |
12 | https://codesandbox.io/s/umi-keep-alive-tabs-demo-knfxy
13 |
14 | ## Usage
15 |
16 | 1. Install
17 |
18 | ```bash
19 | npm install umi-plugin-keep-alive --save
20 | # or
21 | yarn add umi-plugin-keep-alive
22 | ```
23 |
24 | 2. export `KeepAlive` from umi and wrap any component you want to be keeped
25 |
26 | ```javascript
27 | import { useState } from 'react'
28 | import { KeepAlive } from 'umi'
29 |
30 | function Counter() {
31 | const [count, setCount] = useState(0)
32 |
33 | return (
34 |
35 |
count: {count}
36 |
37 |
38 | )
39 | }
40 |
41 | export default function() {
42 | const [show, setShow] = useState(true)
43 |
44 | return (
45 |
46 |
Page index
47 | {show && (
48 |
49 |
50 |
51 | )}
52 |
53 |
54 | )
55 | }
56 | ```
57 |
58 | ## Options
59 |
60 | TODO
61 |
62 | ## Documentation
63 |
64 | All function of react-activation can be completely imported from umi
65 |
66 | ```javascript
67 | import {
68 | KeepAlive,
69 | useActivate,
70 | useUnactivate,
71 | withActivation,
72 | withAliveScope,
73 | useAliveController
74 | } from 'umi'
75 | ```
76 |
77 | Visit [react-activation](https://github.com/CJY0208/react-activation) for full documentation
78 |
79 | ## LICENSE
80 |
81 | MIT
82 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | join
3 | } from 'path'
4 | import {
5 | readFileSync,
6 | } from 'fs'
7 | import {
8 | Mustache
9 | } from '@umijs/utils';
10 |
11 | const Encoding = 'utf-8';
12 |
13 | export default (api) => {
14 | // 通过新增方法判断umi4
15 | const isUmi4 = typeof api.modifyAppData === 'function';
16 |
17 | if (isUmi4) {
18 | // umi@4 自动生成插件目录
19 | api.addRuntimePlugin({
20 | fn: () => '@@/plugin-keepAlive/runtime',
21 | stage: -1 * Number.MAX_SAFE_INTEGER,
22 | })
23 | } else {
24 | api.addRuntimePlugin({
25 | fn: () => '@@/plugin-keep-alive/runtime',
26 | stage: -1 * Number.MAX_SAFE_INTEGER,
27 | })
28 | }
29 |
30 | // // 因 keep-alive 的 runtime 部分选择不渲染其 children,可能会丢失默认的用户 rootContainer
31 | // // 此处修复自定义 rootContainer
32 | // // https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/generateFiles/core/plugin.ts#L23-L27
33 | // const customizedAppPath = getFile({
34 | // base: api.paths.absSrcPath,
35 | // fileNameWithoutExt: 'app',
36 | // type: 'javascript',
37 | // })?.path
38 |
39 | // if (customizedAppPath) {
40 | // api.addRuntimePlugin({
41 | // fn: () => '@@/plugin-keep-alive/fixCustomizedRuntime',
42 | // stage: -1 * Number.MAX_SAFE_INTEGER + 1,
43 | // })
44 |
45 | // api.onGenerateFiles(async () => {
46 | // api.writeTmpFile({
47 | // path: 'plugin-keep-alive/fixCustomizedRuntime.tsx',
48 | // content: Mustache.render(
49 | // readFileSync(join(__dirname, 'fixCustomizedRuntime.tsx.tpl'), Encoding),
50 | // {},
51 | // ),
52 | // })
53 | // })
54 | // }
55 |
56 | // Babel Plugin for react-activation
57 | const babelOpt = [require.resolve('react-activation/babel')]
58 | if (typeof api.modifyBabelOpts === 'function') {
59 | api.modifyBabelOpts((babelOpts) => {
60 | babelOpts.plugins.push(babelOpt)
61 | return babelOpts
62 | })
63 | }
64 |
65 | if (typeof api.addExtraBabelPlugins === 'function') {
66 | api.addExtraBabelPlugins(() => [babelOpt])
67 | }
68 | // 生成:export * from 'react-activation'
69 | // TODO: 指明支持的 api,使用上述方式存在 bug https://github.com/guybedford/es-module-lexer/issues/76
70 | // 业务中可 import { KeepAlive } from 'umi'
71 | if (isUmi4) {
72 | // umi@4 需要通过插件文件进行自动导出
73 | api.onGenerateFiles(async () => {
74 | api.writeTmpFile({
75 | path: 'index.ts',
76 | content: Mustache.render(readFileSync(join(__dirname, 'index.ts.tpl'), Encoding), {}),
77 | })
78 | api.writeTmpFile({
79 | path: 'runtime.tsx',
80 | content: Mustache.render(readFileSync(join(__dirname, 'runtime.tsx.tpl'), Encoding), {}),
81 | })
82 | })
83 | } else {
84 | api.addUmiExports(() => [{
85 | specifiers: [
86 | 'KeepAlive',
87 | 'useActivate',
88 | 'autoFixContext',
89 | 'useUnactivate',
90 | 'withActivation',
91 | 'withAliveScope',
92 | 'useAliveController',
93 | ],
94 | source: 'react-activation',
95 | }, ])
96 |
97 | api.onGenerateFiles(async () => {
98 | api.writeTmpFile({
99 | path: 'plugin-keep-alive/runtime.tsx',
100 | content: Mustache.render(readFileSync(join(__dirname, 'runtime.tsx.tpl'), Encoding), {}),
101 | })
102 | })
103 | }
104 | }
--------------------------------------------------------------------------------