",
6 | "homepage": "https://github.com/alvin0216/awsome-demo#readme",
7 | "license": "ISC",
8 | "scripts": {
9 | "test": "promises-aplus-tests test8.js"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/alvin0216/awsome-demo/issues"
13 | },
14 | "dependencies": {
15 | "promises-aplus-tests": "^2.1.2"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/code/my-promise/test1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 new Promise(executor) 的参数校验
3 | *
4 | * new Promise()
5 | * TypeError: Promise resolver undefined is not a function
6 | */
7 | const Promise = require('./promise');
8 |
9 | new Promise();
10 |
--------------------------------------------------------------------------------
/code/my-promise/test2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 1. 测试 new Promise(executor).then() 方法
3 | * 2. 测试链式调用
4 | */
5 | const Promise = require('./promise');
6 |
7 | new Promise((resolve, reject) => {
8 | resolve('A');
9 | }).then(
10 | (value) => {
11 | console.log('执行成功态回调,value:', value);
12 | },
13 | (error) => {
14 | console.log('执行拒绝态回调,error:', error);
15 | },
16 | );
17 |
18 | new Promise((resolve, reject) => {
19 | reject('B');
20 | }).then(
21 | (value) => {
22 | console.log('执行成功态回调,value:', value);
23 | },
24 | (error) => {
25 | console.log('执行拒绝态回调,error:', error);
26 | },
27 | );
28 |
29 | new Promise((resolve, reject) => {
30 | resolve('C');
31 | })
32 | .then()
33 | .then((value) => {
34 | console.log('链式调用测试,value:', value);
35 | });
36 |
37 | /**
38 | * ? 即使支持了 then 方法。执行顺序也是不对的。Promise.reslove 方法是一个微任务,需要在上一个宏任务执行完后才执行
39 | * * 代码:
40 | console.log(1)
41 | new Promise((resolve, reject) => {
42 | console.log(2)
43 | resolve('A')
44 | }).then(
45 | value => {
46 | console.log(4)
47 | }
48 | )
49 | console.log(3)
50 | * * 结果为 1 2 4 3
51 | * * 正确结果为:1 2 3 4
52 | *
53 | */
54 |
--------------------------------------------------------------------------------
/code/my-promise/test3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 then 方法 在异步下执行
3 | */
4 | const Promise = require('./promise');
5 |
6 | console.log(1);
7 | new Promise((resolve, reject) => {
8 | console.log(2);
9 | resolve('A');
10 | }).then((value) => {
11 | console.log(4);
12 | });
13 | console.log(3);
14 |
15 | /**
16 | * ? 如果在 executor 函数中异步 reslove 呢?
17 | * * 代码:
18 | console.log(1)
19 | new Promise((resolve, reject) => {
20 | console.log(2)
21 | setTimeout(() => {
22 | resolve('A')
23 | })
24 | }).then(value => {
25 | console.log(4)
26 | })
27 | console.log(3)
28 | * * 结果为 1 2 3
29 | * * 正确结果为 1 2 3 4
30 | */
31 |
--------------------------------------------------------------------------------
/code/my-promise/test4.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 在 executor 函数中异步 reslove
3 | */
4 |
5 | const Promise = require('./promise');
6 |
7 | console.log(1);
8 | new Promise((resolve, reject) => {
9 | console.log(2);
10 | setTimeout(() => {
11 | resolve('A');
12 | });
13 | }).then((value) => {
14 | console.log(4);
15 | });
16 | console.log(3);
17 |
18 | // 1 2 3 4
19 |
20 | /**
21 | * ? 如果在 then 函数中 return string
22 | * * 代码:
23 | *
24 | new Promise((resolve, reject) => {
25 | setTimeout(() => {
26 | resolve('A')
27 | })
28 | })
29 | .then(
30 | value => {
31 | return 'then1 return string'
32 | },
33 | reason => {
34 | console.log('then1 reason', reason)
35 | }
36 | )
37 | .then(
38 | value => {
39 | console.log(value)
40 | },
41 | reason => {
42 | console.log('then2 reason', reason)
43 | }
44 | )
45 | * * 结果 A
46 | * * 正确结果 then1 return string
47 | *
48 | */
49 |
--------------------------------------------------------------------------------
/code/my-promise/test5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 onFulfilled 方法返回一个字符串后的结果
3 | */
4 |
5 | const Promise = require('./promise');
6 |
7 | new Promise((resolve, reject) => {
8 | setTimeout(() => {
9 | resolve('A');
10 | });
11 | })
12 | .then(
13 | (value) => {
14 | return 'then1 return string';
15 | },
16 | (reason) => {
17 | console.log('then1 reason', reason);
18 | },
19 | )
20 | .then(
21 | (value) => {
22 | console.log('then2 value', value);
23 | },
24 | (reason) => {
25 | console.log('then2 reason', reason);
26 | },
27 | );
28 |
29 | // * 结果 then1 return string
30 |
31 | /**
32 | * ? 如果 onFulfilled 或 onRejected 返回值不为字符串而是 Promise 呢?
33 | * * 代码:
34 | new Promise((resolve, reject) => {
35 | setTimeout(() => {
36 | resolve('A')
37 | })
38 | })
39 | .then(
40 | value => {
41 | return new Promise(resolve => resolve('B'))
42 | },
43 | reason => {
44 | console.log('then1 reason', reason)
45 | }
46 | )
47 | .then(
48 | value => {
49 | console.log('then2 value', value)
50 | },
51 | reason => {
52 | console.log('then2 reason', reason)
53 | }
54 | )
55 | * * 结果 then2 value MyPromise { status: 'fulfilled', value: 'B'...}
56 | * * 正确结果 then2 value B
57 | */
58 |
--------------------------------------------------------------------------------
/code/my-promise/test6.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 返回值 x 为 Promise 时值需要的操作
3 | */
4 | const Promise = require('./promise');
5 |
6 | new Promise((resolve, reject) => {
7 | resolve(1);
8 | })
9 | .then(
10 | (value) => {
11 | return new Promise((reslove) => {
12 | reslove(2);
13 | });
14 | },
15 | (reason) => {},
16 | )
17 | .then(
18 | (value) => {
19 | console.log(value);
20 | },
21 | (reason) => {},
22 | );
23 |
24 | // * 答案 2
25 |
--------------------------------------------------------------------------------
/code/my-promise/test7.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 测试 返回值 x 为 Promise 时值需要的操作
3 | */
4 | const Promise = require('./promise');
5 |
6 | new Promise((resolve, reject) => {
7 | resolve(1);
8 | })
9 | .then(
10 | (value) => {
11 | return new Promise((reslove) => {
12 | reslove(
13 | new Promise((resolve, reject) => {
14 | reslove(2);
15 | }),
16 | );
17 | });
18 | },
19 | (reason) => {},
20 | )
21 | .then(
22 | (value) => {
23 | console.log(value);
24 | },
25 | (reason) => {},
26 | )
27 | .then((value) => {
28 | console.log('then2 value', value);
29 | });
30 |
31 | // 2
32 | // then2 value undefined
33 |
--------------------------------------------------------------------------------
/code/my-promise/test8.js:
--------------------------------------------------------------------------------
1 | const MyPromise = require('./promise');
2 |
3 | // 跑 promises-aplus-tests 验证是否符合 promise A+ 规范
4 | MyPromise.deferred = function () {
5 | var result = {};
6 | result.promise = new MyPromise(function (resolve, reject) {
7 | result.resolve = resolve;
8 | result.reject = reject;
9 | });
10 |
11 | return result;
12 | };
13 |
14 | module.exports = MyPromise;
15 |
--------------------------------------------------------------------------------
/code/my-react-router/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
--------------------------------------------------------------------------------
/code/my-react-router/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/code/my-react-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-react-router",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build",
7 | "serve": "vite preview"
8 | },
9 | "dependencies": {
10 | "path-to-regexp": "^6.2.0",
11 | "react": "^17.0.0",
12 | "react-dom": "^17.0.0"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-react-refresh": "^1.3.1",
16 | "vite": "^2.1.5"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/code/my-react-router/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { HashRouter, Route, Link, Redirect, Switch } from './react-router-dom';
4 |
5 | const Home = props => {
6 | console.log(props);
7 | return home
;
8 | };
9 |
10 | const User = () => user
;
11 | const Profile = () => profile
;
12 | const NotFound = () => NotFound
;
13 | const List = props => List {JSON.stringify(props.match.params)}
;
14 |
15 | const App = props => {
16 | return (
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/code/my-react-router/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root'),
10 | );
11 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/HashRouter.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, { useEffect, useState } from 'react';
3 | import Context from './context';
4 |
5 | const HashRouter = props => {
6 | const [location, setLocation] = useState({
7 | hash: '',
8 | pathname: window.location.hash.slice(1) || '/', // 删除 #
9 | search: '',
10 | state: undefined,
11 | });
12 |
13 | useEffect(() => {
14 | window.location.hash = window.location.hash || '/'; // 默认添加 hash
15 |
16 | // 监听 hash 值变化
17 | window.addEventListener('hashchange', onHashChange);
18 | return () => {
19 | window.removeEventListener('hashchange', onHashChange);
20 | };
21 | }, []);
22 |
23 | function onHashChange() {
24 | setLocation({
25 | ...location,
26 | pathname: window.location.hash.slice(1) || '/',
27 | });
28 | }
29 |
30 | const value = {
31 | location,
32 | history: {
33 | push(to) {
34 | window.location.hash = to;
35 | },
36 | },
37 | };
38 |
39 | return (
40 |
41 | {/* */}
42 | {props.children}
43 |
44 | );
45 | };
46 |
47 | export default HashRouter;
48 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/Link.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import Context from './context';
3 |
4 | const Link = props => {
5 | const { to } = props;
6 |
7 | return (
8 |
9 | {state => {
10 | return state.history.push(to)}>{props.children};
11 | }}
12 |
13 | );
14 | };
15 |
16 | export default Link;
17 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/Redirect.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import Context from './context';
3 |
4 | const Redirect = props => {
5 | const { history } = useContext(Context);
6 | history.push(props.to);
7 | return null;
8 | };
9 |
10 | export default Redirect;
11 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/Route.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Context from './context';
3 | import { pathToRegexp } from 'path-to-regexp';
4 |
5 | const Route = props => {
6 | const { path, component: Component, extract = false } = props;
7 |
8 | return (
9 |
10 | {state => {
11 | // 也可以使用 useContext
12 | const { pathname } = state.location;
13 | let keys = [];
14 | // extract 严格匹配
15 | let reg = pathToRegexp(path, keys, { end: extract });
16 | let result = pathname.match(reg);
17 | let [url, ...values] = result || [];
18 |
19 | let comProps = {
20 | location: state.location,
21 | history: state.history,
22 | match: {
23 | params: keys.reduce((obj, item, idx) => {
24 | const key = item.name;
25 | obj[key] = values[idx];
26 | return obj;
27 | }, {}),
28 | },
29 | };
30 |
31 | return result ? : null;
32 | }}
33 |
34 | );
35 | };
36 |
37 | export default Route;
38 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/Switch.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import Context from './context';
3 | import { pathToRegexp } from 'path-to-regexp';
4 |
5 | const Switch = props => {
6 | const { location } = useContext(Context);
7 | const pathname = location.pathname;
8 |
9 | for (let i = 0; i < props.children.length; i++) {
10 | const child = props.children[i];
11 | const path = child.props.path || '';
12 | const reg = pathToRegexp(path, [], { end: false });
13 |
14 | if (reg.test(pathname)) {
15 | return child;
16 | }
17 | }
18 |
19 | return null;
20 | };
21 |
22 | export default Switch;
23 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | let Context = React.createContext();
4 |
5 | export default Context;
6 |
--------------------------------------------------------------------------------
/code/my-react-router/src/react-router-dom/index.js:
--------------------------------------------------------------------------------
1 | import HashRouter from './HashRouter';
2 | import Route from './Route';
3 | import Link from './Link';
4 | import Redirect from './Redirect';
5 | import Switch from './Switch';
6 |
7 | export { HashRouter, Route, Link, Redirect, Switch };
8 |
--------------------------------------------------------------------------------
/code/my-react-router/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import reactRefresh from '@vitejs/plugin-react-refresh';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [reactRefresh()],
7 | });
8 |
--------------------------------------------------------------------------------
/code/my-redux/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
--------------------------------------------------------------------------------
/code/my-redux/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/code/my-redux/middleware/exception.js:
--------------------------------------------------------------------------------
1 | const exceptionMiddleware = store => next => action => {
2 | try {
3 | next(action);
4 | } catch (err) {
5 | console.error('错误报告: ', err);
6 | }
7 | };
8 |
9 | export default exceptionMiddleware;
10 |
--------------------------------------------------------------------------------
/code/my-redux/middleware/logger.js:
--------------------------------------------------------------------------------
1 | const loggerMiddleware = store => next => action => {
2 | console.log('this state', store.getState());
3 | console.log('action', action);
4 | next(action);
5 | console.log('next state', store.getState());
6 | };
7 |
8 | export default loggerMiddleware;
9 |
--------------------------------------------------------------------------------
/code/my-redux/middleware/time.js:
--------------------------------------------------------------------------------
1 | const timeMiddleware = store => next => action => {
2 | console.log('time', new Date().getTime());
3 | next(action);
4 | };
5 |
6 | export default timeMiddleware;
7 |
--------------------------------------------------------------------------------
/code/my-redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-redux",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build",
7 | "serve": "vite preview"
8 | },
9 | "dependencies": {
10 | "react": "^17.0.0",
11 | "react-dom": "^17.0.0"
12 | },
13 | "devDependencies": {
14 | "@vitejs/plugin-react-refresh": "^1.3.1",
15 | "vite": "^2.1.5"
16 | }
17 | }
--------------------------------------------------------------------------------
/code/my-redux/react-redux/connect.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import bindActionCreators from '../redux/bindActionCreators';
4 |
5 | export default function connect(mapStateToProps, mapDispatchToProps) {
6 | return function(Component) {
7 | class Connect extends React.Component {
8 | componentDidMount() {
9 | // 从context获取store并订阅更新
10 | this.context.store.subscribe(this.handleStoreChange.bind(this));
11 | }
12 | handleStoreChange() {
13 | // 触发的方法有多种,这里为了简洁起见,直接forceUpdate强制更新,读者也可以通过setState来触发子组件更新
14 | this.forceUpdate();
15 | }
16 |
17 | render() {
18 | const dispathProps =
19 | typeof mapDispatchToProps &&
20 | bindActionCreators(mapDispatchToProps, this.context.store.dispatch);
21 |
22 | return (
23 |
31 | );
32 | }
33 | }
34 | // 接收context的固定写法
35 | Connect.contextTypes = {
36 | store: PropTypes.object,
37 | };
38 | return Connect;
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/code/my-redux/react-redux/index.js:
--------------------------------------------------------------------------------
1 | import Provider from './provider';
2 | import connect from './connect';
3 |
4 | export { Provider, connect };
5 |
--------------------------------------------------------------------------------
/code/my-redux/react-redux/provider.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class Provider extends React.Component {
5 | // 需要声明静态属性childContextTypes来指定context对象的属性,是context的固定写法
6 | static childContextTypes = {
7 | store: PropTypes.object,
8 | };
9 |
10 | // 实现getChildContext方法,返回context对象,也是固定写法
11 | getChildContext() {
12 | return { store: this.store };
13 | }
14 |
15 | constructor(props, context) {
16 | super(props, context);
17 | this.store = props.store;
18 | }
19 |
20 | // 渲染被Provider包裹的组件
21 | render() {
22 | return this.props.children;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/code/my-redux/redux/applyMiddleware.js:
--------------------------------------------------------------------------------
1 | import compose from './compose';
2 |
3 | export default function applyMiddleware(...middlewares) {
4 | /* 返回一个重写createStore的方法*/
5 | return function rewriteCreateStoreFunc(oldCreateStore) {
6 | /* 返回重写后新的 createStore*/
7 | return function newCreateStore(reducer, initState) {
8 | /* 1. 生成store*/
9 | const store = oldCreateStore(reducer, initState);
10 | /* 给每个 middleware 传下store,相当于 const logger = loggerMiddleware(store);*/
11 | /* const chain = [exception, time, logger]*/
12 | const chain = middlewares.map(middleware => middleware(store));
13 | let dispatch = store.dispatch;
14 | dispatch = compose(...chain)(store.dispatch);
15 |
16 | /* 2. 重写 dispatch*/
17 | store.dispatch = dispatch;
18 | return store;
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/code/my-redux/redux/bindActionCreators.js:
--------------------------------------------------------------------------------
1 | /* 核心的代码在这里,通过闭包隐藏了 actionCreator 和 dispatch*/
2 | function bindActionCreator(actionCreator, dispatch) {
3 | return function() {
4 | return dispatch(actionCreator.apply(this, arguments));
5 | };
6 | }
7 |
8 | /* actionCreators 必须是 function 或者 object */
9 | export default function bindActionCreators(actionCreators, dispatch) {
10 | if (typeof actionCreators === 'function') {
11 | return bindActionCreator(actionCreators, dispatch);
12 | }
13 |
14 | if (typeof actionCreators !== 'object' || actionCreators === null) {
15 | throw new Error();
16 | }
17 |
18 | const keys = Object.keys(actionCreators);
19 | const boundActionCreators = {};
20 | for (let i = 0; i < keys.length; i++) {
21 | const key = keys[i];
22 | const actionCreator = actionCreators[key];
23 | if (typeof actionCreator === 'function') {
24 | boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
25 | }
26 | }
27 | return boundActionCreators;
28 | }
29 |
--------------------------------------------------------------------------------
/code/my-redux/redux/combineReducers.js:
--------------------------------------------------------------------------------
1 | export default function combineReducers(reducers) {
2 | /* reducerKeys = ['counter', 'info']*/
3 | const reducerKeys = Object.keys(reducers);
4 |
5 | /* 返回合并后的新的reducer函数*/
6 | return function combination(state = {}, action) {
7 | /* 生成的新的state*/
8 | const nextState = {};
9 |
10 | /* 遍历执行所有的reducers,整合成为一个新的state*/
11 | for (let i = 0; i < reducerKeys.length; i++) {
12 | const key = reducerKeys[i];
13 | const reducer = reducers[key];
14 | /* 之前的 key 的 state*/
15 | const previousStateForKey = state[key];
16 | /* 执行 分 reducer,获得新的state*/
17 | const nextStateForKey = reducer(previousStateForKey, action);
18 |
19 | nextState[key] = nextStateForKey;
20 | }
21 | return nextState;
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/code/my-redux/redux/compose.js:
--------------------------------------------------------------------------------
1 | export default function compose(...funcs) {
2 | if (funcs.length === 1) {
3 | return funcs[0];
4 | }
5 | return funcs.reduce((a, b) => (...args) => a(b(...args)));
6 | }
7 |
--------------------------------------------------------------------------------
/code/my-redux/redux/createStore.js:
--------------------------------------------------------------------------------
1 | export default function createStore(
2 | reducer,
3 | initState,
4 | rewriteCreateStoreFunc,
5 | ) {
6 | let state = initState;
7 | const listeners = [];
8 |
9 | if (typeof initState === 'function') {
10 | rewriteCreateStoreFunc = initState;
11 | initState = undefined;
12 | }
13 |
14 | /* 如果有 rewriteCreateStoreFunc,那就采用新的 createStore */
15 | if (rewriteCreateStoreFunc) {
16 | const newCreateStore = rewriteCreateStoreFunc(createStore);
17 | return newCreateStore(reducer, initState);
18 | }
19 |
20 | /* 否则按照正常的流程走*/
21 | /* 订阅函数 */
22 | function subscribe(listener) {
23 | listeners.push(listener);
24 | }
25 |
26 | /* state 值的修改 */
27 | function dispatch(action) {
28 | state = reducer(state, action);
29 | /* 执行通知 */
30 | for (let i = 0; i < listeners.length; i++) {
31 | const listener = listeners[i];
32 | listener();
33 | }
34 | }
35 |
36 | /* 注意!!!只修改了这里,用一个不匹配任何计划的 type,来获取初始值 */
37 | dispatch({ type: Symbol() });
38 |
39 | function getState() {
40 | return state;
41 | }
42 |
43 | return { subscribe, dispatch, getState };
44 | }
45 |
--------------------------------------------------------------------------------
/code/my-redux/redux/index.js:
--------------------------------------------------------------------------------
1 | import createStore from './createStore';
2 | import combineReducers from './combineReducers';
3 | import applyMiddleware from './applyMiddleware';
4 | import compose from './compose';
5 |
6 | export { createStore, combineReducers, applyMiddleware, compose };
7 |
--------------------------------------------------------------------------------
/code/my-redux/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from '../react-redux';
3 | import { addCount } from '../store/couter/actions';
4 |
5 | class App extends Component {
6 | render() {
7 | console.log(this.props);
8 | return (
9 |
10 |
{this.props.count}
11 |
12 |
13 | );
14 | }
15 | }
16 |
17 | export default connect(state => ({ count: state.counter.count }), {
18 | addCount,
19 | })(App);
20 |
--------------------------------------------------------------------------------
/code/my-redux/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | import { Provider } from '../react-redux';
6 | import store from '../store';
7 |
8 | ReactDOM.render(
9 |
10 |
11 |
12 |
13 | ,
14 | document.getElementById('root'),
15 | );
16 |
--------------------------------------------------------------------------------
/code/my-redux/store/couter/actions.js:
--------------------------------------------------------------------------------
1 | export const addCount = () => ({ type: 'INCREMENT' });
2 |
--------------------------------------------------------------------------------
/code/my-redux/store/couter/reducer.js:
--------------------------------------------------------------------------------
1 | // ====== state
2 | const defaultState = {
3 | count: 0,
4 | };
5 |
6 | /**
7 | * UserReducer
8 | */
9 | export default function countReducer(state = defaultState, action) {
10 | switch (action.type) {
11 | case 'INCREMENT':
12 | return { count: state.count + 1 };
13 | default:
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/code/my-redux/store/index.js:
--------------------------------------------------------------------------------
1 | import { compose, createStore, applyMiddleware } from '../redux';
2 | import logger from '../middleware/logger';
3 | import exception from '../middleware/exception';
4 | import time from '../middleware/time';
5 |
6 | import rootReducer from './rootReducers';
7 |
8 | const storeEnhancers = compose(applyMiddleware(exception, time, logger));
9 |
10 | export default createStore(rootReducer, storeEnhancers);
11 |
--------------------------------------------------------------------------------
/code/my-redux/store/rootReducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from '../redux';
2 | import counter from './couter/reducer';
3 |
4 | export default combineReducers({ counter });
5 |
--------------------------------------------------------------------------------
/code/my-redux/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import reactRefresh from '@vitejs/plugin-react-refresh';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [reactRefresh()],
7 | });
8 |
--------------------------------------------------------------------------------
/code/react-context/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: react-context demo
3 | ---
4 |
5 | ```tsx | pure
6 | import React from 'react';
7 | import { useImmerReducer } from 'use-immer';
8 |
9 | export const initialState: FRInitialState = {
10 | widgetList: [], // 数据列表
11 | template: null, // 组件模板数据
12 | channelPort1: null,
13 | validationList: [],
14 | };
15 |
16 | type ACTIONTYPE =
17 | | { type: 'setFR'; payload: Partial }
18 | | { type: 'setWipWidgetIndex'; payload: any } // for example number
19 | | { type: 'setWidgetList'; payload: WidgetItem[] };
20 |
21 | export const FRContext = React.createContext<
22 | [FRInitialState, React.Dispatch]
23 | >([initialState, action => action]);
24 |
25 | export const reducer = (state: FRInitialState, action: ACTIONTYPE) => {
26 | const { type, payload } = action;
27 | switch (type) {
28 | // 更新 widgetList
29 | case 'setWidgetList':
30 | state.widgetList = payload;
31 | break;
32 |
33 | default:
34 | }
35 |
36 | return state;
37 | };
38 |
39 | export const FRProvider = ({ children }: any) => {
40 | const contextValue = useImmerReducer(reducer, initialState); // 初始化数据
41 | return (
42 | {children}
43 | );
44 | };
45 | ```
46 |
--------------------------------------------------------------------------------
/code/react-context/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-context",
3 | "version": "1.0.0",
4 | "description": "> TODO: description",
5 | "author": "alvin ",
6 | "homepage": "https://github.com/alvin0216/awsome-demo#readme",
7 | "license": "ISC",
8 | "main": "lib/context.js",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "publishConfig": {
17 | "registry": "http://registry.cnpm.3weijia.com/"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/alvin0216/awsome-demo.git"
22 | },
23 | "scripts": {
24 | "test": "echo \"Error: run tests from root\" && exit 1"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/alvin0216/awsome-demo/issues"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ssr-demo",
3 | "version": "1.0.0",
4 | "description": "> TODO: description",
5 | "author": "alvin ",
6 | "homepage": "https://github.com/alvin0216/awsome-demo#readme",
7 | "license": "ISC",
8 | "main": "lib/react-ssr-demo.js",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/alvin0216/awsome-demo.git"
12 | },
13 | "scripts": {
14 | "dev": "npm-run-all --parallel dev:**",
15 | "dev:start": "nodemon --watch build --exec node \"./build/bundle.js\"",
16 | "dev:build:server": "webpack --config webpack.server.js --watch",
17 | "dev:build:client": "webpack --config webpack.client.js --watch"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/alvin0216/awsome-demo/issues"
21 | },
22 | "devDependencies": {
23 | "@babel/cli": "^7.10.5",
24 | "@babel/core": "^7.11.0",
25 | "@babel/plugin-transform-runtime": "^7.11.0",
26 | "@babel/preset-env": "^7.11.0",
27 | "@babel/preset-react": "^7.10.4",
28 | "@babel/runtime-corejs2": "^7.11.0",
29 | "babel-loader": "^8.1.0",
30 | "nodemon": "^2.0.4",
31 | "npm-run-all": "^4.1.5",
32 | "webpack": "^4.44.1",
33 | "webpack-cli": "^3.3.12",
34 | "webpack-merge": "^5.0.9",
35 | "webpack-node-externals": "^2.5.0"
36 | },
37 | "dependencies": {
38 | "axios": ">=0.21.2",
39 | "express": "^4.17.1",
40 | "express-http-proxy": "^1.6.2",
41 | "react": "^16.13.1",
42 | "react-dom": "^16.13.1",
43 | "react-redux": "^7.2.1",
44 | "react-router-config": "^5.1.1",
45 | "react-router-dom": "^5.2.0",
46 | "redux": "^4.0.5",
47 | "redux-thunk": "^2.3.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { renderRoutes } from 'react-router-config';
3 |
4 | const App = (props) => {
5 | return (
6 | <>
7 | App...
8 | {renderRoutes(props.route.routes)}
9 | >
10 | );
11 | };
12 |
13 | export default App;
14 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/client/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 | import { BrowserRouter, Route } from 'react-router-dom';
4 | import { Provider } from 'react-redux';
5 | import { getClientStore } from '../redux';
6 | import { renderRoutes } from 'react-router-config';
7 | import routes from '../routes';
8 |
9 | const Root = (
10 |
11 | {renderRoutes(routes)}
12 |
13 | );
14 |
15 | ReactDom.hydrate(Root, document.getElementById('root'));
16 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/client/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const instance = axios.create({
4 | baseURL: 'https://mock.yonyoucloud.com/mock/13592',
5 | });
6 |
7 | export default instance;
8 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/containers/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { useSelector, useDispatch } from 'react-redux';
3 | import { fetchName } from '../redux/user/actions';
4 |
5 | const Home = (props) => {
6 | const dispatch = useDispatch();
7 | const user = useSelector((state) => state.user);
8 |
9 | // useEffect(() => {
10 | // dispatch(fetchName()).then(res => {
11 | // console.log(res)
12 | // })
13 | // }, [])
14 |
15 | return (
16 | <>
17 | Home, {user.name}
18 |
19 | >
20 | );
21 | };
22 |
23 | Home.loadData = (store) => {
24 | return store.dispatch(fetchName());
25 | };
26 |
27 | export default Home;
28 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/containers/Login.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Login = (props) => Login
;
4 |
5 | export default Login;
6 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/redux/contants.js:
--------------------------------------------------------------------------------
1 | export const SET_NAME = Symbol.for('SET_NAME');
2 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/redux/index.js:
--------------------------------------------------------------------------------
1 | import thunk from 'redux-thunk';
2 | import { createStore, applyMiddleware, combineReducers } from 'redux';
3 | import clientAxios from '../client/request';
4 | import serverAxios from '../server/request';
5 |
6 | import UserReducer from './user/reducer';
7 |
8 | const reducer = combineReducers({
9 | user: UserReducer,
10 | });
11 |
12 | //服务端的store创建函数
13 | export const getStore = (req) => {
14 | return createStore(
15 | reducer,
16 | applyMiddleware(thunk.withExtraArgument(serverAxios(req))),
17 | );
18 | };
19 | //客户端的store创建函数
20 | export const getClientStore = () => {
21 | const defaultState = window.context ? window.context.state : {};
22 | return createStore(
23 | reducer,
24 | defaultState,
25 | applyMiddleware(thunk.withExtraArgument(clientAxios)),
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/redux/user/actions.js:
--------------------------------------------------------------------------------
1 | import { SET_NAME } from '../contants';
2 |
3 | //异步操作的action(采用thunk中间件)
4 | export const fetchName = () => {
5 | return (dispatch, getState, axiosInstance) => {
6 | return axiosInstance.get('/random/name').then((res) => {
7 | console.log(res.data);
8 | return dispatch({ type: SET_NAME, payload: res.data.name });
9 | });
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/redux/user/reducer.js:
--------------------------------------------------------------------------------
1 | import { SET_NAME } from '../contants';
2 |
3 | let defaultState = {
4 | name: 'alvin',
5 | };
6 |
7 | export default function UserReducer(state = defaultState, action) {
8 | const { type, payload } = action;
9 | switch (type) {
10 | case SET_NAME:
11 | return { ...state, name: payload };
12 |
13 | default:
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/routes.js:
--------------------------------------------------------------------------------
1 | import Home from './containers/Home';
2 | import Login from './containers/Login';
3 | import App from './App';
4 |
5 | //这里出现了多级路由
6 | export default [
7 | {
8 | path: '/',
9 | component: App,
10 | routes: [
11 | {
12 | path: '/',
13 | component: Home,
14 | exact: true,
15 | loadData: Home.loadData,
16 | key: 'home',
17 | },
18 | {
19 | path: '/login',
20 | component: Login,
21 | exact: true,
22 | key: 'login',
23 | },
24 | ],
25 | },
26 | ];
27 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/src/server/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const createInstance = (req) =>
4 | axios.create({
5 | baseURL: 'https://mock.yonyoucloud.com/mock/13592',
6 | headers: {
7 | cookie: req.get('cookie') || '',
8 | },
9 | });
10 |
11 | export default createInstance;
12 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/webpack.base.js:
--------------------------------------------------------------------------------
1 | // base config
2 | module.exports = {
3 | mode: 'development', // 开发模式
4 |
5 | resolve: {
6 | extensions: ['.js', '.json', '.jsx'],
7 | },
8 |
9 | module: {
10 | rules: [
11 | {
12 | test: /\.jsx?$/,
13 | loader: 'babel-loader', // .babelrc 单独配置 babel
14 | exclude: /node_modules/,
15 | options: {
16 | presets: [
17 | [
18 | '@babel/preset-env',
19 | {
20 | targets: {
21 | browsers: ['>1%', 'last 2 versions', 'not ie <= 8'],
22 | },
23 | },
24 | ],
25 | '@babel/preset-react',
26 | ],
27 | plugins: [['@babel/plugin-transform-runtime', { corejs: 2 }]],
28 | cacheDirectory: true, // 构建优化 第二次构建时,会读取之前的缓存
29 | },
30 | },
31 | ],
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/webpack.client.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const { merge } = require('webpack-merge');
3 | const config = require('./webpack.base.js');
4 |
5 | module.exports = merge(config, {
6 | entry: resolve(__dirname, './src/client/index.js'),
7 | output: {
8 | filename: 'index.js',
9 | path: resolve(__dirname, 'public'),
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/code/react-ssr-demo/webpack.server.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const nodeExternals = require('webpack-node-externals');
3 | const { merge } = require('webpack-merge');
4 | const config = require('./webpack.base.js');
5 |
6 | module.exports = merge(config, {
7 | entry: resolve(__dirname, './src/server/index.js'),
8 | output: {
9 | filename: 'bundle.js',
10 | path: resolve(__dirname, 'build'),
11 | },
12 | // target: node 不会将 node 的核心模块打包进来
13 | target: 'node',
14 | externals: [nodeExternals()], // 排除 node_modules 目录中所有模块
15 | });
16 |
--------------------------------------------------------------------------------
/code/systemjs-demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/code/systemjs-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "systemjs-demo",
3 | "scripts": {
4 | "dev": "webpack serve",
5 | "build": "webpack build"
6 | },
7 | "dependencies": {
8 | "@babel/cli": "^7.14.8",
9 | "@babel/core": "^7.14.8",
10 | "@babel/preset-env": "^7.14.8",
11 | "@babel/preset-react": "^7.14.5",
12 | "babel-loader": "^8.2.2",
13 | "html-webpack-plugin": "^5.3.2",
14 | "webpack": "^5.46.0",
15 | "webpack-cli": "^4.7.2",
16 | "webpack-dev-server": "^3.11.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/code/systemjs-demo/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => App...
;
4 |
--------------------------------------------------------------------------------
/code/systemjs-demo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 | systemjs-react
3 |
4 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
--------------------------------------------------------------------------------
/code/systemjs-demo/src/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDom.render(, document.querySelector('#root'));
6 |
--------------------------------------------------------------------------------
/code/systemjs-demo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | mode: 'development',
6 | entry: './src/main.js',
7 | output: {
8 | path: path.join(__dirname, 'build'),
9 | filename: 'main.js',
10 | libraryTarget: 'system', // 指定构建时所需要的库
11 | },
12 | devtool: 'source-map',
13 | // 服务器运行配置
14 | devServer: {
15 | port: 9000, // 端口
16 | contentBase: path.join(__dirname, 'build'),
17 | historyApiFallback: true,
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js$/,
23 | exclude: /node_modules/,
24 | use: {
25 | loader: 'babel-loader',
26 | options: { presets: ['@babel/preset-env', '@babel/react'] },
27 | },
28 | },
29 | ],
30 | },
31 |
32 | plugins: [
33 | // 插件
34 | new HtmlWebpackPlugin({
35 | /* 打包时,不需要自动引入JS文件(
12 |