├── 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 |
--------------------------------------------------------------------------------