├── favicon.ico ├── jsconfig.json ├── public ├── favicon.ico ├── manifest.json └── index.html ├── src ├── assist │ ├── images │ │ └── loading.gif │ └── styles │ │ ├── layout.scss │ │ ├── about.scss │ │ ├── items.scss │ │ ├── variables.scss │ │ ├── reset.scss │ │ └── main.scss ├── store │ ├── index.js │ ├── home.js │ ├── detail.js │ └── items.js ├── utils │ ├── objectEmpty.js │ └── getDate.js ├── compontens │ ├── common │ │ ├── footer.js │ │ └── header.js │ ├── App.js │ ├── home.js │ ├── detail.js │ ├── about.js │ └── itemsList.js ├── config │ └── config.js ├── index.js ├── routers │ └── index.js └── registerServiceWorker.js ├── .travis.yml ├── .idea ├── vcs.xml ├── vagrant.xml └── codeStyleSettings.xml ├── config ├── jest │ ├── fileTransform.js │ └── cssTransform.js ├── polyfills.js ├── paths.js ├── env.js ├── webpackDevServer.config.js ├── webpack.config.dev.js └── webpack.config.prod.js ├── manifest.json ├── .gitignore ├── scripts ├── test.js ├── start.js └── build.js ├── LICENSE ├── package.json ├── index.html ├── README-zh.md └── README.md /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hancoson/react-mobx-demo/HEAD/favicon.ico -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hancoson/react-mobx-demo/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assist/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hancoson/react-mobx-demo/HEAD/src/assist/images/loading.gif -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | after_success: 5 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vagrant.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-07 15:43:32 4 | * @version 0.0.1 5 | */ 6 | import {homeStore} from './home' 7 | import {itemsStore} from './items' 8 | import {detailStore} from './detail' 9 | 10 | export {homeStore, itemsStore, detailStore} -------------------------------------------------------------------------------- /src/utils/objectEmpty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:27:43 4 | * @version 0.0.1 5 | */ 6 | 7 | export const isEmptyObject = obj => { 8 | for (var key in obj) { 9 | if (obj.hasOwnProperty(key)) { 10 | return false; 11 | } 12 | } 13 | return true; 14 | } -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/compontens/common/footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:24:24 4 | * @version 0.0.1 5 | */ 6 | 7 | import React, { Component } from 'react' 8 | export default class Footer extends Component { 9 | 10 | render() { 11 | return ( 12 | 16 | ) 17 | } 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | # /build 11 | /build/manifest.json 12 | 13 | # misc 14 | .idea 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | #styles 26 | /src/assist/styles/*.css 27 | -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-11 16:53:56 4 | * @version 0.0.1 5 | */ 6 | export default { 7 | URL_PREFIX : 'http://daily.zhihu.com/story/', 8 | API : 'http://news-at.zhihu.com/api/4/news/before/',//消息列表 9 | NEWS : 'http://news-at.zhihu.com/api/4/news/',//消息内容获取 10 | YAHOO : 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20json%20where%20url=%22', 11 | YAHOO_SUFFIX: '%22&format=json' 12 | }; -------------------------------------------------------------------------------- /src/store/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-07 15:46:16 4 | * @version 0.0.1 5 | */ 6 | import {observable, action} from 'mobx'; 7 | 8 | class HomeStore { 9 | @observable text; 10 | @observable num; 11 | 12 | constructor() { 13 | this.num = 0 14 | this.text = 'Hello Word!' 15 | } 16 | 17 | @action plus = () => { 18 | this.num = ++this.num 19 | } 20 | @action minus = () => { 21 | this.num = --this.num 22 | } 23 | } 24 | 25 | const homeStore = new HomeStore() 26 | 27 | export {homeStore} -------------------------------------------------------------------------------- /src/assist/styles/layout.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @author Guoxing.Han(hancoson#163.com) 4 | * @time 2016/11/14. 5 | */ 6 | @import 'variables.scss'; 7 | body{ 8 | } 9 | .main{ 10 | width: 1200px; 11 | position: relative; 12 | margin: 0 auto; 13 | } 14 | section{ 15 | @extend .border-box; 16 | width: 100%; 17 | margin-top: 40px; 18 | } 19 | 20 | 21 | .index-warp{ 22 | text-align: center; 23 | padding: 100px 0 24 | } 25 | 26 | 27 | footer{ 28 | width: 100%; 29 | text-align: center; 30 | padding: 20px 0; 31 | color: #999; 32 | font-size: 13px; 33 | background: #fff; 34 | border-top:#f0f0f0 1px solid; 35 | margin-top: 20px; 36 | } 37 | 38 | .loading{ 39 | margin: 100px auto; 40 | text-align: center; 41 | } 42 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-25 17:23:43 4 | * @version 0.0.1 5 | */ 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import {useStrict} from 'mobx'; 9 | import {HashRouter as Router} from 'react-router-dom' 10 | import {Provider} from 'mobx-react'; 11 | import * as stores from './store'; 12 | 13 | import App from './compontens/App' 14 | 15 | import './assist/styles/main.css'; 16 | import registerServiceWorker from './registerServiceWorker'; 17 | 18 | useStrict(true) // 不允许在动作之外进行状态修改 19 | 20 | ReactDOM.render( 21 | 22 | 23 | 24 | 25 | , document.getElementById('root')); 26 | registerServiceWorker(); 27 | -------------------------------------------------------------------------------- /src/compontens/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:24:43 4 | * @version 0.0.1 5 | */ 6 | import React, {Component} from 'react' 7 | import {observer, inject} from 'mobx-react' 8 | import {withRouter} from 'react-router-dom' 9 | //import DevTools from 'mobx-react-devtools'; 10 | 11 | import Main from './../routers'; 12 | 13 | import Header from './common/header' 14 | import Footer from './common/footer' 15 | 16 | @withRouter @inject('store') @observer 17 | class App extends Component { 18 | 19 | render() { 20 | return ( 21 |
22 |
23 | 24 |
25 | 26 |
29 | ) 30 | } 31 | } 32 | 33 | export default App -------------------------------------------------------------------------------- /src/routers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:26:17 4 | * @version 0.0.1 5 | */ 6 | import React from 'react' 7 | import {Route, Switch} from 'react-router-dom' 8 | 9 | import Index from './../compontens/home' 10 | import About from './../compontens/about' 11 | import Items from './../compontens/itemsList' 12 | import Detail from './../compontens/detail' 13 | 14 | const Main = () => ( 15 | 16 | ()}/> 17 | 18 | ()}/> 19 | ()}/> 20 |

找不到此页面

}/> 21 |
22 | ) 23 | 24 | export default Main; -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /src/store/detail.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Guoxing.Han(hancoson#163.com) 3 | * @time 2017/12/13 4 | */ 5 | import {observable, action} from 'mobx' 6 | import Config from './../config/config' 7 | 8 | class DetailStore { 9 | @observable detail 10 | 11 | constructor() { 12 | this.detail = {} 13 | } 14 | 15 | @action emptyData=()=>{ 16 | this.detail = {} 17 | } 18 | 19 | @action fetchDetailData = (id) => { 20 | fetch(Config.YAHOO + Config.NEWS + id + Config.YAHOO_SUFFIX).then( 21 | action('fetchRes', res => { 22 | return res.json() 23 | })).then( 24 | action('fetchSuccess', data => { 25 | this.detail = data.query.results.json; 26 | })).catch( 27 | action('fetchError', e => { 28 | console.log(e.message) 29 | })) 30 | } 31 | 32 | } 33 | 34 | const detailStore = new DetailStore() 35 | export {detailStore} 36 | -------------------------------------------------------------------------------- /src/utils/getDate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:27:28 4 | * @version 0.0.1 5 | */ 6 | 7 | //获取当天时间并格式化 8 | export const getDate = () => { 9 | let d = new Date(); 10 | let m = d.getMonth() + 1; 11 | let dd = d.getDate(); 12 | if (m < 10) { 13 | m = '0' + m.toString() 14 | } 15 | if (dd < 10) { 16 | dd = '0' + dd.toString() 17 | } 18 | let str = d 19 | .getFullYear() 20 | .toString() + m + dd.toString(); 21 | return str 22 | } 23 | 24 | //格式化时间 25 | export const subString = str => { 26 | let _y = '', 27 | _m = '', 28 | _d = ''; 29 | _y = str.substring(0, 4); 30 | _m = str.substring(4, 6); 31 | _d = str.substring(6, 8); 32 | return (_y + '/' + _m + '/' + _d) 33 | 34 | } 35 | //格式化时间 36 | export const timeClear = str => { 37 | let _t = ''; 38 | str 39 | .split('-') 40 | .forEach((i) => { 41 | _t = _t + i.toString() 42 | }) 43 | return _t 44 | 45 | } -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /src/assist/styles/about.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Guoxing.Han(hancoson#163.com) 3 | * @time 2017/1/13. 4 | */ 5 | .about-warp { 6 | h1 { 7 | padding-bottom: 5px; 8 | border-bottom: 1px #f0f0f0 solid; 9 | font-size: 32px; 10 | margin-bottom: 20px; 11 | } 12 | ul { 13 | padding-left: 20px; 14 | li { 15 | line-height: 1.6; 16 | list-style: circle; 17 | } 18 | } 19 | h2 { 20 | font-size: 26px; 21 | } 22 | code{ 23 | padding: 2px 4px; 24 | font-size: 90%; 25 | color: #c7254e; 26 | background-color: #f9f2f4; 27 | border-radius: 4px; 28 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 29 | } 30 | pre{ 31 | display: block; 32 | padding: 9.5px; 33 | margin: 0 0 10px; 34 | font-size: 13px; 35 | line-height: 1.42857143; 36 | color: #333; 37 | word-break: break-all; 38 | word-wrap: break-word; 39 | background-color: #f5f5f5; 40 | border: 1px solid #ccc; 41 | border-radius: 4px; 42 | } 43 | } -------------------------------------------------------------------------------- /src/assist/styles/items.scss: -------------------------------------------------------------------------------- 1 | .items-warp { 2 | width: 100%; 3 | margin-top: 10px; 4 | .bar { 5 | width: 100%; 6 | background: #f7f7f7; 7 | padding: 10px; 8 | } 9 | .gutter-example .ant-row > div { 10 | border: 0; 11 | } 12 | .gutter-box { 13 | background: #00A0E9; 14 | padding: 5px 0; 15 | } 16 | .item { 17 | display: block; 18 | padding: 10px; 19 | height: 100px; 20 | position: relative; 21 | background: #f5f5f5; 22 | border-radius: 10px; 23 | margin: 10px 0 10px; 24 | transition: all .2s linear 0s; 25 | &:hover{ 26 | box-shadow: 0 5px 10px rgba(0,0,0,.1); 27 | } 28 | img{ 29 | display: block; 30 | width: 80px; 31 | height: 80px; 32 | float: left; 33 | margin-right: 10px; 34 | } 35 | p{ 36 | font-size: 14px; 37 | line-height: 1.4; 38 | color: #31b9ff; 39 | } 40 | sub{ 41 | font-size: 13px; 42 | position: absolute; 43 | bottom: 13px; 44 | right: 13px; 45 | color: #999; 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/compontens/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:25:00 4 | * @version 0.0.1 5 | */ 6 | import React, {Component} from 'react' 7 | import {inject, observer} from "mobx-react"; 8 | 9 | @inject("store") @observer 10 | export default class Index extends Component { 11 | constructor(props) { 12 | super(props) 13 | this.store = props.store.homeStore 14 | this.plus = this.store.plus 15 | this.minus = this.store.minus 16 | } 17 | 18 | render() { 19 | const {text,num} = this.store 20 | return ( 21 |
22 |
{text}
23 |
24 | - 25 | {num} 26 | + 27 |
28 |
29 | ) 30 | } 31 | 32 | plusHandle() { 33 | this.plus() 34 | } 35 | minusHandle() { 36 | this.minus() 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hancoson(hancoson#163.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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/compontens/detail.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:24:52 4 | * @version 0.0.1 5 | */ 6 | import React, { Component } from 'react' 7 | import { Spin } from 'antd'; 8 | import { isEmptyObject } from './../utils/objectEmpty' 9 | import {inject, observer} from "mobx-react"; 10 | 11 | @inject("store") @observer 12 | export default class Detail extends Component { 13 | constructor(props){ 14 | super(props) 15 | this._id = props.match.params.id 16 | this.store = props.store.detailStore 17 | } 18 | componentWillMount() { 19 | const { fetchDetailData, emptyData } = this.store; 20 | //调取数据 21 | fetchDetailData(this._id) 22 | //清空原有的数据 23 | emptyData() 24 | } 25 | 26 | createMarkup() { 27 | var _fonts = this.store.detail.body; 28 | return { __html: _fonts }; 29 | } 30 | 31 | render() { 32 | const _o = this.store.detail; 33 | 34 | return ( 35 |
36 | { 37 | isEmptyObject(_o) ? 38 |
39 | 40 |
: 41 |
42 | } 43 |
44 | 45 | ) 46 | } 47 | } -------------------------------------------------------------------------------- /src/store/items.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-11 16:48:57 4 | * @version 0.0.1 5 | */ 6 | import {observable, action, computed, autorun} from 'mobx'; 7 | 8 | import Config from './../config/config' 9 | import {getDate, subString, timeClear} from './../utils/getDate' 10 | 11 | class ItemsStore { 12 | @observable items; 13 | @observable dataTime; 14 | 15 | constructor() { 16 | this.items = [] 17 | this.dataTime = '' 18 | } 19 | 20 | @computed get total() { 21 | return this.items.length; 22 | } 23 | 24 | @action 25 | fetchData = (time) => { 26 | fetch(Config.YAHOO + Config.API + time + Config.YAHOO_SUFFIX).then( 27 | 28 | action('fetchRes', res => { 29 | return res.json() 30 | })).then( 31 | 32 | action('fetchSuccess', data => { 33 | this.items = data.query.results.json.stories; 34 | this.dataTime = data.query.results.json.date; 35 | 36 | })).catch( 37 | 38 | action('fetchError', e => { 39 | console.log(e.message) 40 | })) 41 | } 42 | 43 | @action 44 | emptyData = () => { 45 | this.items = [] 46 | } 47 | 48 | defaultDate = autorun((e) => this.fetchData(getDate())) 49 | } 50 | 51 | const itemsStore = new ItemsStore() 52 | 53 | export {itemsStore} -------------------------------------------------------------------------------- /src/compontens/common/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:24:11 4 | * @version 0.0.1 5 | */ 6 | import React, {Component} from 'react' 7 | import {Link} from 'react-router-dom' 8 | import {Menu} from 'antd'; 9 | 10 | export default class Header extends Component { 11 | constructor(porps) { 12 | super(porps); 13 | 14 | this.state = { 15 | current: this.props.pathname === '/' 16 | ? '/index' 17 | : this.props.pathname 18 | } 19 | } 20 | 21 | handleClick(e) { 22 | if(e.key !== this.state.current) 23 | this.setState({current: e.key}); 24 | } 25 | 26 | render() { 27 | return ( 28 | 34 | 35 | 36 | 首页 37 | 38 | 39 | 40 | 41 | 文章 42 | 43 | 44 | 45 | 46 | 关于 47 | 48 | 49 | 50 | ) 51 | } 52 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-mobx-demo", 3 | "version": "1.0.0", 4 | "description": "Get started with React, Mobx, and React-Router to achieve a Zhihu Daily App.", 5 | "author": "Hancoson (http://github.com/hancoson)", 6 | "license": "MIT", 7 | "private": true, 8 | "dependencies": { 9 | "antd": "^2.0.0", 10 | "mobx": "^2.5.0", 11 | "mobx-react": "^3.5.5", 12 | "mobx-react-devtools": "^4.2.5", 13 | "mobx-react-router": "^4.0.1", 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1", 16 | "react-router-dom": "^4.2.2" 17 | }, 18 | "scripts": { 19 | "build-css": "node-sass-chokidar src/ -o src/", 20 | "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive", 21 | "start-js": "react-scripts start", 22 | "start": "npm-run-all -p watch-css start-js", 23 | "build-js": "react-scripts build", 24 | "build": "npm-run-all build-css build-js", 25 | "test": "react-scripts test --env=jsdom", 26 | "eject": "react-scripts eject", 27 | "clean": "yarn cache clean && rm -rf node_modules && rm -rf yarn.lock && yarn" 28 | }, 29 | "devDependencies": { 30 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 31 | "babel-preset-stage-2": "^6.24.1", 32 | "node-sass-chokidar": "^0.0.3", 33 | "npm-run-all": "^4.1.2", 34 | "react-scripts": "0.2.3" 35 | } 36 | } -------------------------------------------------------------------------------- /src/assist/styles/variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 变量 3 | * @author 墨萧 4 | * @time 2016/06/06 5 | * @version 0.1.0 6 | */ 7 | 8 | 9 | /** 10 | * 继承代码 11 | **/ 12 | 13 | //border-box 14 | .border-box { 15 | -moz-box-sizing: border-box; 16 | -webkit-box-sizing: border-box; 17 | -o-box-sizing: border-box; 18 | -ms-box-sizing: border-box; 19 | box-sizing: border-box; 20 | } 21 | 22 | //旋转360 23 | .transition { 24 | -moz-transition: -moz-transform 0.3s ease; 25 | -webkit-transition: -webkit-transform 0.3s ease; 26 | transition: transform 0.3s ease; 27 | } 28 | 29 | //截取长度显示省略号 30 | .ellipsis { 31 | overflow: hidden; 32 | text-overflow: ellipsis; 33 | white-space: nowrap; 34 | } 35 | 36 | //滚动条 37 | .scrollbar::-webkit-scrollbar-track { 38 | -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.2); 39 | border-radius: 6px; 40 | background-color: #F5F5F5; 41 | } 42 | 43 | .scrollbar::-webkit-scrollbar { 44 | width: 3px; 45 | background-color: #fff; 46 | } 47 | 48 | .scrollbar::-webkit-scrollbar-thumb { 49 | border-radius: 6px; 50 | -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, .2); 51 | background-color: #07a1d6; 52 | } 53 | 54 | 55 | /** 56 | * flex 57 | **/ 58 | 59 | .flex { 60 | display: -webkit-box; 61 | display: -webkit-flex; 62 | display: -ms-flexbox; 63 | display: flex; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/compontens/about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Guoxing.han 3 | * @Date: 2017-12-08 17:24:32 4 | * @version 0.0.1 5 | */ 6 | import React, { Component } from 'react' 7 | 8 | export default class About extends Component { 9 | 10 | render() { 11 | 12 | return ( 13 |
14 |
15 |

React-Mobx Zhihu Daily

16 |
    17 |
  • 一个React+Mobx版知乎日报 所有API均来自网络(若涉及侵权,请及时联系我删除)
  • 18 |
  • Github[https://github.com/Hancoson/react-mobx-demo]
  • 19 |
20 |
21 |
22 |

技术实现

23 |
    24 |
  • 采用creat-react-app构建生成脚手架
  • 25 |
  • 采用React+mobx+React-router(v4)+antd等实现
  • 26 |
27 |
28 |
29 |

运行

30 |
    31 |
  • 安装 npm install(yarn)
  • 32 |
  • 启动 npm start
  • 33 |
  • 构建 npm build
  • 34 |
35 |
36 |
37 |

目录结构

38 |
39 |             ├─src
40 | │ ├─assets //静态资源
41 | │ │ ├─img
42 | │ │ └─scss
43 | │ ├─compontens //组建
44 | │ ├─config
45 | │ ├─routes //路由
46 | │ ├─store //store
47 | │ └─utils //工具函数
48 | └─build //发布目录
49 |
50 |
51 |
52 | ) 53 | } 54 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | react-mobx|知乎日报 8 | 9 | 10 | 14 | 15 | 16 | 25 | 26 | 27 | 30 |
31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | react-mobx|知乎日报 8 | 9 | 10 | 14 | 15 | 16 | 25 | React App 26 | 27 | 28 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/assist/styles/reset.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * reset 3 | * @author 墨萧 4 | * @time 2016/06/06 5 | * @version 0.1.0 6 | */ 7 | html, body, div, span, applet, object, iframe, 8 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 9 | a, abbr, acronym, address, big, cite, code, 10 | del, dfn, em, img, ins, kbd, q, s, samp, 11 | small, strike, strong, sub, sup, tt, var, 12 | b, u, i, center, 13 | dl, dt, dd, ol, ul, li, 14 | fieldset, form, label, legend, 15 | table, caption, tbody, tfoot, thead, tr, th, td, 16 | article, aside, canvas, details, embed, 17 | figure, figcaption, footer, header, 18 | menu, nav, output, ruby, section, summary, 19 | time, mark, audio, video { 20 | margin: 0; 21 | padding: 0; 22 | border: 0; 23 | font: inherit; 24 | } 25 | 26 | * { 27 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 28 | } 29 | 30 | /* HTML5 display-role reset for older browsers */ 31 | article, aside, details, figcaption, figure, 32 | footer, header, menu, nav, section { 33 | display: block; 34 | } 35 | 36 | body { 37 | line-height: 1; 38 | font-family: sans-serif; 39 | } 40 | 41 | ol, ul, ul li { 42 | list-style: none; 43 | } 44 | 45 | blockquote, q { 46 | quotes: none; 47 | } 48 | 49 | blockquote:before, blockquote:after, 50 | q:before, q:after { 51 | content: ''; 52 | content: none; 53 | } 54 | 55 | table { 56 | border-collapse: collapse; 57 | border-spacing: 0; 58 | } 59 | 60 | input { 61 | outline: 0; 62 | } 63 | 64 | input, textarea, keygen, select, button { 65 | font: inherit; 66 | &:focus { 67 | outline: none; 68 | } 69 | } 70 | 71 | button, a { 72 | border: 0; 73 | cursor: pointer; 74 | } 75 | 76 | a, a:hover { 77 | text-decoration: none; 78 | } 79 | 80 | .clearfix:before, 81 | .clearfix:after { 82 | display: table; 83 | content: " "; 84 | } 85 | 86 | .clearfix:after { 87 | clear: both; 88 | } 89 | 90 | /* End hide from IE-mac */ 91 | .no-scroll { 92 | overflow: hidden; 93 | position: relative; 94 | } 95 | 96 | .fl { 97 | float: left; 98 | } 99 | 100 | .fr { 101 | float: right; 102 | } -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right