张延森 4 yıl önce
ebeveyn
işleme
32ff0b1679

+ 3
- 1
config/dev.js Dosyayı Görüntüle

@@ -4,6 +4,8 @@ module.exports = {
4 4
   },
5 5
   defineConstants: {
6 6
   },
7
-  mini: {},
7
+  mini: {
8
+    debugReact: true
9
+  },
8 10
   h5: {}
9 11
 }

+ 5
- 0
config/index.js Dosyayı Görüntüle

@@ -1,3 +1,5 @@
1
+const path = require('path')
2
+
1 3
 const config = {
2 4
   projectName: 'miniapp',
3 5
   date: '2020-11-24',
@@ -59,6 +61,9 @@ const config = {
59 61
         }
60 62
       }
61 63
     }
64
+  },  
65
+  alias: {
66
+    '@/store': path.resolve(__dirname, '..', 'src/store'),
62 67
   }
63 68
 }
64 69
 

+ 9
- 8
package.json Dosyayı Görüntüle

@@ -37,23 +37,24 @@
37 37
   "dependencies": {
38 38
     "@babel/runtime": "^7.7.7",
39 39
     "@tarojs/components": "3.0.14",
40
+    "@tarojs/react": "3.0.14",
40 41
     "@tarojs/runtime": "3.0.14",
41 42
     "@tarojs/taro": "3.0.14",
42
-    "@tarojs/react": "3.0.14",
43
-    "react-dom": "^16.10.0",
44
-    "react": "^16.10.0"
43
+    "fast-deep-equal": "^3.1.3",
44
+    "react": "^16.10.0",
45
+    "react-dom": "^16.10.0"
45 46
   },
46 47
   "devDependencies": {
47
-    "@types/webpack-env": "^1.13.6",
48
-    "@types/react": "^16.0.0",
49
-    "@tarojs/mini-runner": "3.0.14",
50 48
     "@babel/core": "^7.8.0",
49
+    "@tarojs/mini-runner": "3.0.14",
51 50
     "@tarojs/webpack-runner": "3.0.14",
51
+    "@types/react": "^16.0.0",
52
+    "@types/webpack-env": "^1.13.6",
52 53
     "babel-preset-taro": "3.0.14",
53
-    "eslint-config-taro": "3.0.14",
54 54
     "eslint": "^6.8.0",
55
-    "eslint-plugin-react": "^7.8.2",
55
+    "eslint-config-taro": "3.0.14",
56 56
     "eslint-plugin-import": "^2.12.0",
57
+    "eslint-plugin-react": "^7.8.2",
57 58
     "eslint-plugin-react-hooks": "^1.6.1",
58 59
     "stylelint": "9.3.0"
59 60
   }

+ 3
- 1
src/app.config.js Dosyayı Görüntüle

@@ -1,6 +1,8 @@
1 1
 export default {
2 2
   pages: [
3
-    'pages/index/index'
3
+    'pages/index/index',
4
+    'pages/foo/index',
5
+    'pages/bar/index'
4 6
   ],
5 7
   window: {
6 8
     backgroundTextStyle: 'light',

+ 7
- 2
src/app.js Dosyayı Görüntüle

@@ -1,4 +1,5 @@
1
-import { Component } from 'react'
1
+import React, { Component } from 'react'
2
+import { StoreRoot } from './store'
2 3
 import './app.less'
3 4
 
4 5
 class App extends Component {
@@ -13,7 +14,11 @@ class App extends Component {
13 14
 
14 15
   // this.props.children 是将要会渲染的页面
15 16
   render () {
16
-    return this.props.children
17
+    return (
18
+      <StoreRoot>
19
+        {this.props.children}
20
+      </StoreRoot>
21
+    )
17 22
   }
18 23
 }
19 24
 

+ 3
- 0
src/pages/bar/index.config.js Dosyayı Görüntüle

@@ -0,0 +1,3 @@
1
+export default {
2
+  navigationBarTitleText: '首页'
3
+}

+ 26
- 0
src/pages/bar/index.jsx Dosyayı Görüntüle

@@ -0,0 +1,26 @@
1
+import React, { Component, useEffect } from 'react'
2
+import { View, Text, Navigator } from '@tarojs/components'
3
+import './index.less'
4
+
5
+import { useModel } from '@/store'
6
+
7
+export default (props) => {
8
+  const { foo, setFoo } = useModel('foo')
9
+  
10
+  console.log('-----bar-------')
11
+
12
+  useEffect(() => {
13
+    let i = 1;
14
+    console.log('-----bar-------')
15
+
16
+    const t = setInterval(() => setFoo(i++), 1000)
17
+    return () => clearInterval(t)
18
+  }, [])
19
+
20
+  return (
21
+    <View className='foo'>
22
+      <Text>{foo}</Text>
23
+      <Navigator url="/pages/foo/index">foo</Navigator>
24
+    </View>
25
+  )
26
+}

+ 0
- 0
src/pages/bar/index.less Dosyayı Görüntüle


+ 3
- 0
src/pages/foo/index.config.js Dosyayı Görüntüle

@@ -0,0 +1,3 @@
1
+export default {
2
+  navigationBarTitleText: '首页'
3
+}

+ 27
- 0
src/pages/foo/index.jsx Dosyayı Görüntüle

@@ -0,0 +1,27 @@
1
+import React, { Component, useEffect } from 'react'
2
+import { View, Text, Navigator } from '@tarojs/components'
3
+import './index.less'
4
+
5
+import { useModel } from '@/store'
6
+
7
+export default (props) => {
8
+  // const { foo, setFoo } = useModel('foo')
9
+  const { foo } = useModel('foo')
10
+  
11
+  console.log('-----foo-------')
12
+
13
+  // useEffect(() => {
14
+  //   let i = 1;
15
+  //   console.log('-----setfoo-------')
16
+
17
+  //   const t = setInterval(() => setFoo(i++), 1000)
18
+  //   return () => console.log('descard foo') || clearInterval(t) 
19
+  // }, [])
20
+
21
+  return (
22
+    <View className='foo'>
23
+      <Text>{foo}</Text>
24
+      <Navigator url="/pages/bar/index">bar</Navigator>
25
+    </View>
26
+  )
27
+}

+ 0
- 0
src/pages/foo/index.less Dosyayı Görüntüle


+ 7
- 0
src/store/index.js Dosyayı Görüntüle

@@ -0,0 +1,7 @@
1
+// import { createStoreRoot, createStore, createModelHook } from '@zjxpcyc/react-tiny-store'
2
+import { createStoreRoot, createStore, createModelHook } from '../utils/store'
3
+import models from './models'
4
+
5
+export const store = createStore()
6
+export const StoreRoot = createStoreRoot(store, models)
7
+export const useModel = createModelHook(store)

+ 7
- 0
src/store/models/index.js Dosyayı Görüntüle

@@ -0,0 +1,7 @@
1
+import useUser from './useUser'
2
+
3
+const models = {
4
+  'user': useUser
5
+}
6
+
7
+export default models

+ 14
- 0
src/store/models/useUser.js Dosyayı Görüntüle

@@ -0,0 +1,14 @@
1
+import React, { useState } from 'react'
2
+
3
+export default function useBar() {
4
+  const [user, setUser] = useState({})
5
+
6
+  const signIn = () => {
7
+    // xxx
8
+  }
9
+
10
+  return {
11
+    user,
12
+    signIn
13
+  }
14
+}

+ 65
- 0
src/utils/store/executor.js Dosyayı Görüntüle

@@ -0,0 +1,65 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+import React, { useEffect, useMemo } from 'react'
18
+import useUpdate from './utils/useUpdate'
19
+
20
+const noop = () => undefined
21
+
22
+// 所有的 Model 会分别被绑定到各自 Executor 上
23
+// 借用 Executor onUpdate 把 Model 的变化传递到各订阅的组件上
24
+export function Executor(props) {
25
+  if (!props.store || !props.namespace) {
26
+    throw new Error("[Executor] props.store 与 props.namespace 不能为空")
27
+  }
28
+
29
+  const { store, namespace, model = noop } = props
30
+  const data = model()
31
+
32
+  // 首次加载立即执行
33
+  const deModel = useMemo(() => {
34
+    return store.model(namespace, data)
35
+  }, [])
36
+
37
+  // 如果组件卸载, 则卸载 model
38
+  useEffect(() => {
39
+    store.getModel(namespace).publish()
40
+    return deModel
41
+  }, [])
42
+
43
+  // 传递 model 变化到订阅组件
44
+  useUpdate(() => {
45
+    store.getModel(namespace).publish(data)
46
+  }, [data])
47
+
48
+  return <></>
49
+}
50
+
51
+export function createExecutors(store, models) {
52
+  return function Executors(props) {
53
+    return (
54
+      <>
55
+      {
56
+        Object.entries(models).map(pair => {
57
+          const [namespace, hook] = pair
58
+          return <Executor key={namespace} namespace={namespace} model={hook} store={store} />
59
+        })
60
+      }
61
+      </>
62
+    )
63
+  }
64
+}
65
+

+ 41
- 0
src/utils/store/index.js Dosyayı Görüntüle

@@ -0,0 +1,41 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+import React from 'react'
18
+import { createExecutors, Executor } from './executor'
19
+import { createModelHook } from './model'
20
+import { createStore } from './store'
21
+
22
+function createStoreRoot(store, models) {
23
+  const Executors = createExecutors(store, models)
24
+
25
+  return function StoreRoot(props) {
26
+    return (
27
+      <>
28
+        <Executors />
29
+        {props.children}
30
+      </>
31
+    )
32
+  }
33
+}
34
+
35
+export {
36
+  createStore,
37
+  createExecutors,
38
+  createStoreRoot,
39
+  createModelHook,
40
+  Executor
41
+}

+ 60
- 0
src/utils/store/model.js Dosyayı Görüntüle

@@ -0,0 +1,60 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+import React, { useRef, useEffect, useCallback, useState } from 'react'
18
+
19
+const isEqual = require('fast-deep-equal/react')
20
+const getBySelectorOrNot = (data, selector) => selector ? selector(data) : data
21
+
22
+export function createModelHook(store) {
23
+  return function useModel(namespace, selector) {
24
+    if (!namespace) {
25
+      throw new Error('[useModel] namespace 不能为空')
26
+    }
27
+  
28
+    const selectorRef = useRef()
29
+    selectorRef.current = selector
30
+  
31
+    const model = store.getModel(namespace)
32
+    const initialData = getBySelectorOrNot(model.getState(), selectorRef.current)
33
+    const [state, setState] = useState(initialData)
34
+    const stateRef = useRef()
35
+    stateRef.current = state
36
+  
37
+    const renderTrigger = useCallback((e) => {
38
+      const preState = stateRef.current
39
+      const curState = getBySelectorOrNot(e, selectorRef.current)
40
+  
41
+      if (!isEqual(preState, curState)) {
42
+        setState(curState)
43
+      } else {
44
+        setState(e)
45
+      }
46
+    },[])
47
+  
48
+    useEffect(() => {
49
+      // 如果需要触发修改则触发 renderTrigger
50
+      // subscribe 返回取消订阅的函数
51
+      const model = store.getModel(namespace)
52
+      const deSubscribe = model.subscribe(renderTrigger)
53
+      // 通知组件更新
54
+      model.publish()
55
+      return deSubscribe
56
+    }, [namespace])
57
+  
58
+    return state
59
+  }  
60
+}

+ 36
- 0
src/utils/store/store.js Dosyayı Görüntüle

@@ -0,0 +1,36 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+import observe from './utils/observe'
18
+
19
+export function createStore() {
20
+  let data = {}
21
+
22
+  const model = (namespace, state) => {
23
+    if (!data[namespace]) {
24
+      data[namespace] = observe(state)
25
+    }
26
+
27
+    return () => delete(data[namespace])
28
+  }
29
+
30
+  const getModel = namespace => data[namespace]
31
+
32
+  return {
33
+    model,
34
+    getModel
35
+  }
36
+}

+ 40
- 0
src/utils/store/utils/observe.js Dosyayı Görüntüle

@@ -0,0 +1,40 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+export default function(initialState) {
18
+  let state = initialState
19
+  const listeners = []
20
+
21
+  const subscribe = f => {
22
+    const len = listeners.length
23
+    listeners.push(f)
24
+    return () => listeners.splice(len, 1)
25
+  }
26
+
27
+  const publish = (s) => {
28
+    if (s) state = s
29
+
30
+    listeners.forEach(f => f(state))
31
+  }
32
+
33
+  const getState = () => state
34
+
35
+  return {
36
+    subscribe,
37
+    publish,
38
+    getState
39
+  }
40
+}

+ 29
- 0
src/utils/store/utils/useUpdate.js Dosyayı Görüntüle

@@ -0,0 +1,29 @@
1
+/*
2
+ * Copyright (c) [2020] Zhang Yansen.All rights reserved.
3
+ *
4
+ * use-async is licensed under the Mulan PSL v2.
5
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ * You may obtain a copy of Mulan PSL v2 at:
7
+ *
8
+ *     http://license.coscl.org.cn/MulanPSL2
9
+ *
10
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13
+ * See the Mulan PSL v2 for more details.
14
+ */
15
+
16
+
17
+import React, { useEffect, useRef } from 'react'
18
+
19
+export default function useUpdate(effect, deps) {
20
+  const mounted = useRef(false)
21
+
22
+  useEffect(() => {
23
+    if (mounted.current) {
24
+      effect()
25
+    } else {
26
+      mounted.current = true
27
+    }
28
+  }, deps)
29
+}

+ 1
- 1
yarn.lock Dosyayı Görüntüle

@@ -4146,7 +4146,7 @@ extsprintf@^1.2.0:
4146 4146
   resolved "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
4147 4147
   integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
4148 4148
 
4149
-fast-deep-equal@^3.1.1:
4149
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
4150 4150
   version "3.1.3"
4151 4151
   resolved "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
4152 4152
   integrity sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=