├── src ├── components │ └── .gitkeep ├── App.less ├── App.scss ├── pages │ ├── display │ │ ├── service │ │ │ └── index.js │ │ ├── model │ │ │ └── index.js │ │ └── index.js │ ├── exception │ │ └── index.js │ ├── home │ │ └── index.js │ └── settings │ │ └── index.js ├── GlobalModel.js ├── index.js ├── utils │ └── request.js └── App.js ├── public ├── favicon.ico └── index.html ├── .gitignore ├── README.md ├── babel.config.js └── package.json /src/components/.gitkeep: -------------------------------------------------------------------------------- 1 | 用来存放公共组件的目录 -------------------------------------------------------------------------------- /src/App.less: -------------------------------------------------------------------------------- 1 | @padding: 60px; 2 | 3 | .app { 4 | padding: @padding; 5 | } -------------------------------------------------------------------------------- /src/App.scss: -------------------------------------------------------------------------------- 1 | $padding: 60px; 2 | 3 | .app { 4 | padding: $padding; 5 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazyperson/react-quickstart-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # production 5 | /dist 6 | 7 | # misc 8 | .DS_Store 9 | .vscode 10 | .ideal 11 | 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | yarn.lock 16 | package-lock.json -------------------------------------------------------------------------------- /src/pages/display/service/index.js: -------------------------------------------------------------------------------- 1 | import { get } from '@utils/request'; 2 | 3 | /** 4 | * 获取 Mock 数据 5 | */ 6 | export const getMockData = () => { 7 | return get('https://easy-mock.com/mock/5c2dc9665cfaa5209116fa40/example/mock'); 8 | } -------------------------------------------------------------------------------- /src/GlobalModel.js: -------------------------------------------------------------------------------- 1 | import { observable, action } from 'mobx'; 2 | 3 | export default class GlobalModel { 4 | 5 | @observable username = '张三丰'; 6 | 7 | @action 8 | changeUserName = (name) => { 9 | this.username = name; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/exception/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from "antd"; 3 | 4 | export default ({ history }) => { 5 | return ( 6 |
7 |

404

8 | 9 |
10 | ) 11 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React开发 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-quickstart-template 2 | React 脚手架模板 3 | 4 | ## fe-start-kit 5 | 前端脚手架工具[fe-start-kit](https://github.com/lazyperson/fe-start-kit) 6 | 7 | ## 安装插件 8 | 9 | `npm install` 10 | 11 | ## 运行项目 12 | 13 | `npm run start` 14 | 15 | 打开浏览器,输入:`localhost:8000` 查看项目运行效果 16 | 17 | 18 | ## 项目打包 19 | 20 | `npm run build` 21 | 22 | 新增 `dist` 目录,里面是打包好的项目代码 23 | -------------------------------------------------------------------------------- /src/pages/home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'antd'; 3 | 4 | export default ({history}) => { 5 | 6 | const toSettingPage = () => { 7 | history.push('/settings'); 8 | } 9 | 10 | const toDisplayPage = () => { 11 | history.push('/display'); 12 | } 13 | 14 | return ( 15 |
16 |

当前是 Home 页面

17 | 18 | 19 |
20 | ) 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDom from 'react-dom'; 3 | import { Provider } from 'mobx-react' 4 | import { LocaleProvider } from 'antd'; 5 | import { HashRouter } from 'react-router-dom'; 6 | import zh_CN from 'antd/lib/locale-provider/zh_CN'; 7 | 8 | 9 | import GlobalModel from './GlobalModel'; 10 | import App from './App'; 11 | 12 | const globalModel = new GlobalModel(); 13 | 14 | ReactDom.render( 15 | 16 | 17 | 18 | 19 | 20 | 21 | , 22 | document.querySelector('#root') 23 | ); -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import 'whatwg-fetch'; 2 | import { stringify } from 'qs'; 3 | 4 | /** 5 | * 使用 Get 方式进行网络请求 6 | * @param {*} url 7 | * @param {*} data 8 | */ 9 | export const get = (url, data) => { 10 | const newUrl = url + '?' + stringify(data) + (stringify(data) === '' ? '' : '&') +'_random=' + Date.now(); 11 | return fetch(newUrl, { 12 | cache: 'no-cache', 13 | headers: { 14 | 'Accept': 'application/json', 15 | 'Content-Type': 'application/json; charset=utf-8' 16 | }, 17 | method: 'GET', 18 | }) 19 | .then(response => response.json()); 20 | } 21 | 22 | /** 23 | * 进行 Post 方式进行网络请求 24 | * @param {*} url 25 | * @param {*} data 26 | */ 27 | export const post = (url, data) => { 28 | return fetch(url, { 29 | body: JSON.stringify(data), 30 | cache: 'no-cache', 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json; charset=utf-8' 34 | }, 35 | method: 'POST', 36 | }) 37 | .then(response => response.json()) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/pages/display/model/index.js: -------------------------------------------------------------------------------- 1 | import { observable, action, runInAction } from "mobx"; 2 | 3 | import { getMockData } from '../service/index'; 4 | 5 | export default class DisplayModel { 6 | // count 计数器次数 7 | @observable count = 0; 8 | 9 | // Table 是否处于加载中 10 | @observable loading = false; 11 | 12 | // Table 数据源 13 | @observable listData = [] 14 | 15 | /** 16 | * 修改 count 计数器次数的方法 17 | */ 18 | @action 19 | changeCount = (type) => { 20 | switch(type) { 21 | case 'increment': 22 | this.count ++; 23 | break; 24 | case 'decrement': 25 | this.count --; 26 | break; 27 | } 28 | } 29 | 30 | /** 31 | * 获取 listData 数据的方法 32 | */ 33 | @action 34 | getListData = async () => { 35 | this.loading = true; 36 | const response = await getMockData(); 37 | runInAction(() => { 38 | this.loading = false; 39 | if(response && response.success) { 40 | this.listData = response.data.projects 41 | } 42 | }) 43 | } 44 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch, Route } from 'react-router-dom'; 3 | import Loadable from 'react-loadable'; 4 | 5 | // 使用 CSS Module 的方式引入样式文件 6 | import styles from './App.{{preCssType}}'; 7 | 8 | // Loading 提示 9 | const loadingComponent = () => Loading; 10 | 11 | // Home 组件 12 | const Home = Loadable({ 13 | loader: () => import('@pages/home'), 14 | loading: loadingComponent 15 | }) 16 | 17 | // Settings 组件 18 | const Settings = Loadable({ 19 | loader: () => import('@pages/settings'), 20 | loading: loadingComponent 21 | }) 22 | 23 | // Display 组件 24 | const Display = Loadable({ 25 | loader: () => import('@pages/display'), 26 | loading: loadingComponent 27 | }) 28 | 29 | // NotFound 组件 30 | const NotFound = Loadable({ 31 | loader: () => import('@pages/exception'), 32 | loading: loadingComponent 33 | }) 34 | 35 | export default (props) => { 36 | return ( 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /src/pages/display/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | import { Button, Table } from 'antd'; 4 | import DisplayModel from './model'; 5 | 6 | const model = new DisplayModel(); 7 | 8 | // Table 的 columns 值 9 | const columns = [ 10 | { 11 | title: '名称', 12 | dataIndex: 'name' 13 | }, 14 | { 15 | title: '地址', 16 | dataIndex: 'address' 17 | }, 18 | { 19 | title: '数量', 20 | dataIndex: 'number' 21 | }, 22 | { 23 | title: '星级', 24 | dataIndex: 'string' 25 | } 26 | ] 27 | 28 | @observer 29 | export default class Display extends Component { 30 | 31 | componentDidMount() { 32 | model.getListData(); 33 | } 34 | 35 | render() { 36 | const tableTitle = () => 展示 Mobx 异步操作获取数据的 Table; 37 | const { count, changeCount, loading, listData } = model; 38 | return ( 39 |
40 |

Display 界面:展示 Mobx 同步、异步修改数据

41 |
42 | counter 计数器次数:{ count } 43 |
44 | 45 | 46 |
47 | 48 | 49 | address + number } 56 | /> 57 | 58 | ) 59 | } 60 | } -------------------------------------------------------------------------------- /src/pages/settings/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { inject, observer } from 'mobx-react'; 3 | import { Button, Card } from 'antd'; 4 | import PropTypes from 'prop-types'; 5 | 6 | @inject('globalModel') 7 | @observer 8 | export default class Seetings extends Component { 9 | 10 | static propTypes = { 11 | globalModel: PropTypes.shape({ 12 | username: PropTypes.string.isRequired, 13 | changeUserName: PropTypes.func.isRequired 14 | }).isRequired, 15 | history: PropTypes.shape({ 16 | goBack: PropTypes.func.isRequired 17 | }).isRequired 18 | } 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | val: '' 24 | } 25 | } 26 | 27 | /** 28 | * input 值发生变化时触发该方法 29 | */ 30 | handleValueChange = (e) => { 31 | this.setState({ 32 | val: e.target.value 33 | }) 34 | } 35 | 36 | /** 37 | * 按钮点击时触发该方法 38 | */ 39 | handleClick = () => { 40 | const { val } = this.state; 41 | const { changeUserName } = this.props.globalModel; 42 | if (val) { 43 | changeUserName(val); 44 | this.setState({ 45 | val: '' 46 | }) 47 | } 48 | } 49 | 50 | render () { 51 | const { username } = this.props.globalModel; 52 | const { val } = this.state; 53 | const title = "Settings 界面:展示以及修改全局 Model 中的属性" 54 | const actions = [ 55 | 56 | ] 57 | return ( 58 | 59 |
60 | 61 | 62 |
63 | 可以获取到 GlobalModel 中的 username: 64 | { username } 65 | 66 |
67 | ) 68 | } 69 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | 4 | return { 5 | presets: [ 6 | "@babel/preset-env", 7 | "@babel/preset-react" 8 | ], 9 | plugins: [ 10 | [ 11 | "@babel/plugin-proposal-decorators", 12 | { 13 | "legacy": true 14 | } 15 | ], 16 | [ 17 | "import", 18 | { 19 | "libraryName": "antd", 20 | "style": true 21 | } 22 | ], 23 | [ 24 | "@babel/plugin-transform-runtime", 25 | { 26 | "corejs": 2 27 | } 28 | ], 29 | [ 30 | "@babel/plugin-proposal-class-properties", 31 | { 32 | "loose": true 33 | } 34 | ], 35 | "@babel/plugin-syntax-dynamic-import", 36 | // "@babel/plugin-syntax-import-meta", 37 | // 可以用 const ex = "before 38 | // after"; 这种方式编写字符串 39 | // "@babel/plugin-proposal-json-strings", 40 | // 可以使用 generate 语法 41 | // "@babel/plugin-proposal-function-sent", 42 | // 可以使用 export * 这种命名空间的方式导出模块 43 | "@babel/plugin-proposal-export-namespace-from", 44 | // 可以使用数字分离器书写数字 45 | // "@babel/plugin-proposal-numeric-separator" 46 | // 可以使用异常抛出表达式, 47 | "@babel/plugin-proposal-throw-expressions", 48 | // 默认导出 49 | "@babel/plugin-proposal-export-default-from", 50 | // 可以使用逻辑赋值运算符 51 | "@babel/plugin-proposal-logical-assignment-operators", 52 | // 可以使用可选链的方式访问深层嵌套的属性或者函数 ?. 53 | "@babel/plugin-proposal-optional-chaining", 54 | // 可以使用管道运算符 |> 55 | [ 56 | "@babel/plugin-proposal-pipeline-operator", 57 | { 58 | "proposal": "minimal" 59 | } 60 | ], 61 | // 可以使用空值合并语法 ?? 62 | "@babel/plugin-proposal-nullish-coalescing-operator", 63 | // 可以使用 do 表达式(可以认为是三元运算符的复杂版本) 64 | "@babel/plugin-proposal-do-expressions", 65 | // 可以使用功能绑定语法 obj::func 66 | "@babel/plugin-proposal-function-bind" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{name}}", 3 | "version": "{{version}}", 4 | "description": "{{description}}", 5 | "scripts": { 6 | "start": "webpack-dev-server --config build/webpack.config.dev.js", 7 | "build": "webpack --config build/webpack.config.prod.js" 8 | }, 9 | "keywords": "{{keywords}}", 10 | "author": "{{author}}", 11 | "license": "{{license}}", 12 | "devDependencies": { 13 | "@babel/core": "^7.0.0", 14 | "@babel/plugin-proposal-class-properties": "^7.0.0", 15 | "@babel/plugin-proposal-decorators": "^7.0.0", 16 | "@babel/plugin-proposal-do-expressions": "^7.0.0", 17 | "@babel/plugin-proposal-export-default-from": "^7.0.0", 18 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0", 19 | "@babel/plugin-proposal-function-bind": "^7.0.0", 20 | "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", 21 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", 22 | "@babel/plugin-proposal-optional-chaining": "^7.0.0", 23 | "@babel/plugin-proposal-pipeline-operator": "^7.0.0", 24 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 25 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 26 | "@babel/plugin-transform-runtime": "^7.0.0", 27 | "@babel/preset-env": "^7.0.0", 28 | "@babel/preset-react": "^7.0.0", 29 | "@babel/runtime-corejs2": "^7.0.0", 30 | "autoprefixer": "^9.5.1", 31 | "babel-loader": "^8.0.0", 32 | "babel-plugin-import": "^1.11.0", 33 | "clean-webpack-plugin": "^2.0.1", 34 | "css-loader": "^2.1.1", 35 | "file-loader": "^3.0.1", 36 | "friendly-errors-webpack-plugin": "^1.7.0", 37 | "html-loader": "^0.5.5", 38 | "html-webpack-plugin": "^3.2.0", 39 | "less": "^3.9.0", 40 | "less-loader": "^4.1.0", 41 | "markdown-loader": "^5.0.0", 42 | "mini-css-extract-plugin": "^0.5.0", 43 | "node-sass": "^4.12.0", 44 | "optimize-css-assets-webpack-plugin": "^5.0.1", 45 | "postcss-loader": "^3.0.0", 46 | "sass-loader": "^7.1.0", 47 | "style-loader": "^0.23.1", 48 | "uglifyjs-webpack-plugin": "^2.1.2", 49 | "webpack": "^4.29.6", 50 | "webpack-cli": "^3.3.0", 51 | "webpack-dev-server": "^3.1.4", 52 | "webpack-merge": "^4.2.1" 53 | }, 54 | "dependencies": { 55 | "antd": "^3.16.2", 56 | "mobx": "^5.9.4", 57 | "mobx-react": "^5.4.3", 58 | "moment": "^2.24.0", 59 | "prop-types": "^15.7.2", 60 | "qs": "^6.7.0", 61 | "react": "^16.8.6", 62 | "react-dom": "^16.8.6", 63 | "react-loadable": "^5.5.0", 64 | "react-router-dom": "^5.0.0", 65 | "terser-webpack-plugin": "^1.3.0", 66 | "whatwg-fetch": "^3.0.0" 67 | } 68 | } 69 | --------------------------------------------------------------------------------