├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── config ├── webpack.config.dev.js └── webpack.config.pro.js ├── dev-server ├── index.html └── server.js ├── index.html ├── package.json └── src ├── App.js ├── components └── ErrorBoundary.js ├── constants └── RouteWithSubRoutes.js ├── containers └── Main.js ├── index.js ├── reducers └── index.js ├── routes └── index.js ├── sagas └── index.js ├── store └── index.js └── styles └── less └── index.less /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "development": { 4 | "presets" : ["es2015", "stage-0", "react"], 5 | "plugins" : ["react-hot-loader/babel", "transform-decorators-legacy", ["transform-runtime", { "polyfill": false }]] 6 | }, 7 | "production": { 8 | "presets" : [["es2015", { "modules": false, "loose": true }], "stage-0", "react"], 9 | "plugins" : ["transform-decorators-legacy", ["transform-runtime", { "polyfill": false }]] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | // eslint 推荐规则 5 | "eslint:recommended", 6 | // eslint-react 推荐规则 7 | "plugin:react/recommended" 8 | ], 9 | "env": { 10 | "browser": true, // browser 全局变量 11 | "node": true, // Node.js 全局变量和 Node.js 作用域 12 | "es6": true // 支持除模块外所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6) 13 | // "mocha": true, // 添加所有的 Mocha 测试全局变量 14 | // "jest": true, // Jest 全局变量 15 | // "jasmine": true // 添加所有的 Jasmine 版本 1.3 和 2.0 的测试全局变量 16 | }, 17 | "rules": { 18 | // 不允许未使用的变量,不检测参数 19 | "no-unused-vars": ["error", { "args": "none" }], 20 | // 换行符必须为unix风格 21 | "linebreak-style": ["error", "unix"], 22 | // 缩进必须为2个空格 23 | "indent": ["error", 2, { 24 | // switch case子句缩进2个空格 25 | "SwitchCase": 1 26 | }], 27 | // 强制所有不包含双引号的 JSX 属性值使用双引号 28 | "jsx-quotes": ["error", "prefer-double"], 29 | // 不检测 props中的属性在 prop-type 是否存在 30 | "react/prop-types": [0], 31 | // 不检测不安全的target=「blank」 32 | "react/jsx-no-target-blank": [0], 33 | // 可以直接对state赋值 34 | "react/no-direct-mutation-state": [0], 35 | // 允许直接返回reactdom render方法的内容 36 | "react/no-render-return-value": [0], 37 | // 不允许出现分号 38 | "semi": ["warn", "never"], 39 | // 建议使用 let 或 const 而不是 var 40 | "no-var": "warn" 41 | }, 42 | "parserOptions": { 43 | "ecmaVersion": 6, 44 | "ecmaFeatures": { 45 | "jsx": true, 46 | "experimentalObjectRestSpread": true 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | */.DS_Store 4 | package-lock.json 5 | dist 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 陈泽韦 2 | 3 | e-mail: chanzewail@gmail.com 549226266@qq.com 4 | website: http://www.zewail.me/ 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React 开发环境 2 | 3 | 整合React全家桶,简化开发前期准备(这并不是一个框架),适用于对react技术栈有一定了解得开发人员使用 4 | 5 | 使用webpack 4 + react 16 6 | 7 | React 15 + webpack 3 请查看V1版本: https://github.com/czewail/zewail-react/tree/v1 8 | 9 | #### 整合包含react全家桶包含 10 | 11 | - redux:状态管理 12 | - redux-saga:redux中间件,用来处理异步方法 13 | - react-router 4:路由管理 14 | - redux-persist:redux中间件,用来处理本地存储数据 15 | - less: css 预处理 16 | 17 | ## 使用 18 | 19 | 使用 [zewail-react-cli](https://github.com/czewail/zewail-react-cli)工具来管理你的项目 20 | 21 | ## 目录结构 22 | ```txt 23 | . 24 | ├── config // webpack配置目录 25 | │   ├── webpack.config.dev.js // 用于开发环境的webpack配置文件 26 | │   └── webpack.config.pro.js // 用于生产环境的webpack配置文件 27 | ├── package.json 28 | ├── server // 本地开发web服务器目录 29 | │   ├── index.html // 主文件 30 | │   └── server.js // 本地开发web服务器node启动文件,也用于热更新 31 | └── src // 源文件目录 32 | ├── index.js // 入口文件 33 | ├── App.js // 应用主文件 34 | ├── components // 组件库目录,存放项目独立组件 35 | ├── constants // 常用其他文件目录 36 | │   └── RouteWithSubRoutes.js // 子路由包装方法 37 | ├── containers // 页面容器目录,一般存放用来做数据交互的页面 38 | │   └── Main.js // 主页面示例 39 | ├── reducers // reducer 目录 40 | │   └── index.js // reducer 整合文件 41 | ├── routes // 路由配置目录 42 | │   └── index.js // 主路由配置示例 43 | ├── sagas // saga 目录 44 | │   └── index.js // saga 整合文件 45 | ├── store // store 目录 46 | │   └── index.js // 生成store的文件 47 | └── styles // 样式目录 48 | └── less // less 文件目录 49 | └── index.less // 示例文件 50 | 51 | ``` 52 | 53 | ### 其他说明 54 | 55 | - 代码检测使用eslint工具,相关规则使用:https://github.com/czewail/zewail-eslint 56 | - reducers与sagas的存放目录已做自动加载文件处理,reducer的状态键名为文件名,如果实二级目录,以下划线分隔,如src/reducers/main/menus.js文件,获取redux的state的键值为main_menus 57 | 58 | -------------------------------------------------------------------------------- /config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | const ROOT_PATH = path.resolve(__dirname) 5 | const APP_PATH = path.resolve(ROOT_PATH, '../src') // __dirname 中的src目录,以此类推 6 | const APP_FILE = path.resolve(APP_PATH, 'index.js') // 根目录文件app.jsx地址 7 | const BUILD_PATH = path.resolve(ROOT_PATH, '../dist') // 发布文件所存放的目录 8 | 9 | module.exports = { 10 | devtool: 'cheap-module-source-map', 11 | mode: 'development', 12 | entry: { 13 | app: [ 14 | // 'react-hot-loader/patch', 15 | // 这里reload=true的意思是,如果碰到不能hot reload的情况,就整页刷新。 16 | 'webpack-hot-middleware/client?reload=true', 17 | APP_FILE 18 | ], 19 | }, 20 | output: { 21 | // 输出目录的配置,模板、样式、脚本、图片等资源的路径配置都相对于它 22 | // “path”仅仅告诉Webpack结果存储在哪里 23 | path: BUILD_PATH, 24 | filename: '[name].js', 25 | chunkFilename: "[name].js", 26 | //模板、样式、脚本、图片等资源对应的server上的路径 27 | publicPath: "/assets/", 28 | }, 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.js$/, 33 | exclude: /node_modules/, 34 | loader: "babel-loader" 35 | }, 36 | { 37 | test: /\.css$/, 38 | use: [ 39 | 'style-loader', 40 | 'css-loader', 41 | { 42 | loader: 'postcss-loader', 43 | options: { 44 | plugins: (loader) => [ 45 | require('autoprefixer')(), 46 | // require('cssnano')() 47 | ] 48 | } 49 | } 50 | ] 51 | }, 52 | { 53 | test: /\.less$/, 54 | use: [ 55 | 'style-loader', 56 | 'css-loader', 57 | { 58 | loader: 'postcss-loader', 59 | options: { 60 | plugins: (loader) => [ 61 | require('autoprefixer')(), 62 | // require('cssnano')() 63 | ] 64 | } 65 | }, 66 | 'less-loader' 67 | ] 68 | }, 69 | { 70 | test: /\.(eot|woff|svg|ttf|woff2|gif)(\?|$)/, 71 | loader: 'file-loader?name=[hash].[ext]' 72 | }, 73 | { 74 | test: /\.(png|jpg)$/, 75 | loader: 'url-loader?limit=1200&name=[hash].[ext]' 76 | } 77 | ] 78 | }, 79 | plugins: [ 80 | new webpack.DefinePlugin({ 81 | 'process.env': { 82 | // NODE_ENV: JSON.stringify('development') //定义编译环境 83 | } 84 | }), 85 | new webpack.HashedModuleIdsPlugin(), 86 | new webpack.HotModuleReplacementPlugin(), 87 | ], 88 | resolve: { 89 | extensions: ['.js', '.jsx', '.less', '.scss', '.css'], //后缀名自动补全 90 | alias: { 91 | '@': `${APP_PATH}/`, 92 | }, 93 | modules: [ 94 | 'node_modules', 95 | 'src', 96 | ] 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /config/webpack.config.pro.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") 5 | const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") 6 | 7 | const ROOT_PATH = path.resolve(__dirname) 8 | const APP_PATH = path.resolve(ROOT_PATH, '../src') // __dirname 中的src目录,以此类推 9 | const APP_FILE = path.resolve(APP_PATH, 'index.js') // 根目录文件地址 10 | const BUILD_PATH = path.resolve(ROOT_PATH, '../dist/assets') // 发布文件所存放的目录 11 | 12 | module.exports = { 13 | mode: 'production', 14 | entry: { 15 | app: APP_FILE 16 | }, 17 | output: { 18 | // 输出目录的配置,模板、样式、脚本、图片等资源的路径配置都相对于它 19 | // “path”仅仅告诉Webpack结果存储在哪里 20 | path: BUILD_PATH, 21 | filename: '[name].[hash].js', 22 | chunkFilename: "[name].[hash].js", 23 | //模板、样式、脚本、图片等资源对应的server上的路径 24 | // “publicPath”项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。 25 | publicPath: "assets/", 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | test: /\.js$/, 31 | exclude: /node_modules/, 32 | loader: "babel-loader" 33 | }, 34 | { 35 | test: /\.css$/, 36 | use: [ 37 | 'style-loader', 38 | 'css-loader', 39 | { 40 | loader: 'postcss-loader', 41 | options: { 42 | plugins: (loader) => [ 43 | require('autoprefixer')(), 44 | // require('cssnano')() 45 | ] 46 | } 47 | } 48 | ] 49 | }, 50 | { 51 | test: /\.less$/, 52 | use: [ 53 | 'style-loader', 54 | 'css-loader', 55 | { 56 | loader: 'postcss-loader', 57 | options: { 58 | plugins: (loader) => [ 59 | require('autoprefixer')(), 60 | // require('cssnano')() 61 | ] 62 | } 63 | }, 64 | 'less-loader' 65 | ] 66 | }, 67 | { 68 | test: /\.(eot|woff|svg|ttf|woff2|gif)(\?|$)/, 69 | loader: 'file-loader?name=[hash].[ext]' 70 | }, 71 | { 72 | test: /\.(png|jpg)$/, 73 | loader: 'url-loader?limit=1200&name=[hash].[ext]' 74 | } 75 | ] 76 | }, 77 | plugins: [ 78 | new webpack.DefinePlugin({ 79 | 'process.env': { 80 | } 81 | }), 82 | new MiniCssExtractPlugin({ 83 | filename: "[name].[hash].css", 84 | chunkFilename: "[id].[hash].css" 85 | }), 86 | new HtmlWebpackPlugin({ 87 | template: path.resolve(ROOT_PATH, '../dev-server/index.html'), 88 | filename: '../../index.html', 89 | }) 90 | ], 91 | resolve: { 92 | extensions: ['.js', '.jsx', '.less', '.scss', '.css'], //后缀名自动补全 93 | alias: { 94 | '@': `${APP_PATH}/`, 95 | } 96 | }, 97 | optimization: { 98 | runtimeChunk: { 99 | name: "manifest" 100 | }, 101 | splitChunks: { 102 | cacheGroups: { 103 | vendor: { 104 | test: /[\\/]node_modules[\\/]/, 105 | name: "vendors", 106 | priority: -20, 107 | chunks: "all" 108 | } 109 | } 110 | }, 111 | minimizer: [ 112 | new OptimizeCSSAssetsPlugin({}) 113 | ] 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /dev-server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /dev-server/server.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const fs = require('fs') 3 | const webpackDevMiddleware = require('webpack-dev-middleware') 4 | const webpackHotMiddleware = require('webpack-hot-middleware') 5 | const config = require('../config/webpack.config.dev.js') 6 | const cheerio = require('cheerio') 7 | 8 | const app = new (require('express'))() 9 | const port = 3333 10 | 11 | const compiler = webpack(config) 12 | 13 | 14 | const htmlString = fs.readFileSync(__dirname + '/index.html') 15 | const $ = cheerio.load(htmlString) 16 | 17 | $('body').append('') 18 | 19 | app.use(webpackDevMiddleware(compiler, { 20 | noInfo: true, 21 | // 如果false,将会给你列出一大堆无聊的信息。 22 | publicPath: config.output.publicPath, 23 | stats: { 24 | colors: true 25 | } 26 | })) 27 | 28 | app.use(webpackHotMiddleware(compiler)) 29 | 30 | app.get('*', function(req, res) { 31 | res.sendFile($.html()) 32 | }) 33 | 34 | app.listen(port, function(error) { 35 | if (error) { 36 | /*eslint no-console: 0*/ 37 | console.error(error) 38 | } else { 39 | /*eslint no-console: 0*/ 40 | console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zewail-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "dev": "node dev-server/server.js", 8 | "start": "npm run dev", 9 | "clean": "rimraf dist/*", 10 | "build": "npm run clean && webpack --config config/webpack.config.pro.js" 11 | }, 12 | "author": "Zewail", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "autoprefixer": "^8.2.0", 16 | "babel-cli": "^6.26.0", 17 | "babel-eslint": "^8.2.2", 18 | "babel-loader": "^7.1.4", 19 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 20 | "babel-plugin-transform-runtime": "^6.23.0", 21 | "babel-preset-es2015": "^6.24.1", 22 | "babel-preset-react": "^6.24.1", 23 | "babel-preset-stage-0": "^6.24.1", 24 | "cheerio": "^1.0.0-rc.2", 25 | "clean-webpack-plugin": "^0.1.19", 26 | "copyfiles": "^2.0.0", 27 | "css-loader": "^0.28.11", 28 | "eslint": "^4.19.1", 29 | "eslint-plugin-react": "^7.7.0", 30 | "express": "^4.16.3", 31 | "file-loader": "^1.1.11", 32 | "html-webpack-plugin": "^3.2.0", 33 | "less": "^3.0.1", 34 | "less-loader": "^4.1.0", 35 | "mini-css-extract-plugin": "^0.4.0", 36 | "optimize-css-assets-webpack-plugin": "^4.0.2", 37 | "postcss-loader": "^2.1.3", 38 | "react-hot-loader": "^4.0.1", 39 | "redux-logger": "^3.0.6", 40 | "rimraf": "^2.6.2", 41 | "style-loader": "^0.20.3", 42 | "url-loader": "^1.0.1", 43 | "webpack": "^4.5.0", 44 | "webpack-cli": "^2.0.14", 45 | "webpack-dev-middleware": "^3.1.2", 46 | "webpack-hot-middleware": "^2.21.2" 47 | }, 48 | "dependencies": { 49 | "axios": "^0.18.0", 50 | "babel-runtime": "^6.26.0", 51 | "classnames": "^2.2.5", 52 | "prop-types": "^15.6.1", 53 | "react": "^16.3.1", 54 | "react-dom": "^16.3.1", 55 | "react-redux": "^5.0.7", 56 | "react-router": "^4.2.0", 57 | "react-router-dom": "^4.2.2", 58 | "react-router-redux": "^5.0.0-alpha.9", 59 | "redux": "^3.7.2", 60 | "redux-persist": "^5.9.1", 61 | "redux-saga": "^0.16.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Provider } from 'react-redux' 3 | import { history } from '@/store' 4 | import { ConnectedRouter } from 'react-router-redux' 5 | import { PersistGate } from 'redux-persist/es/integration/react' 6 | import { hot } from 'react-hot-loader' 7 | import { Redirect, Switch } from 'react-router-dom' 8 | import RouteWithSubRoutes from '@/constants/RouteWithSubRoutes' 9 | import routes from '@/routes' 10 | 11 | 12 | const onBeforeLift = () => { 13 | // console.log('before action') 14 | } 15 | 16 | class App extends React.Component { 17 | constructor(props) { 18 | super(props) 19 | } 20 | 21 | render() { 22 | const { persistor, store } = this.props.stores 23 | // 主页面 24 | return ( 25 | 26 | } onBeforeLift={onBeforeLift} persistor={persistor}> 27 | {/* ConnectedRouter 会自动使用Provider里的store */} 28 | 29 | 30 | { 31 | routes && routes.map((route, index) => ()) 32 | } 33 | 34 | 35 | 36 | 37 | 38 | ) 39 | } 40 | } 41 | 42 | export default hot(module)(App) 43 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react' 2 | 3 | export default class ErrorBoundary extends Component { 4 | constructor(props) { 5 | super(props) 6 | } 7 | 8 | componentDidCatch(error, info) { 9 | /* eslint no-console: 0 */ 10 | console.log(error, info) 11 | } 12 | 13 | render() { 14 | return this.props.children 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/constants/RouteWithSubRoutes.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route } from 'react-router-dom' 3 | 4 | // 把 组件像这样包一层,然后在需要使用 的地方使用 5 | // 子路由可以加到任意路由组件上。 6 | const RouteWithSubRoutes = (route) => ( 7 | ( 8 | 9 | )} /> 10 | ) 11 | 12 | export default RouteWithSubRoutes 13 | -------------------------------------------------------------------------------- /src/containers/Main.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { connect } from 'react-redux' 3 | 4 | @connect() 5 | export default class Main extends Component { 6 | render() { 7 | return ( 8 |
9 | Main Container 10 |
11 | ) 12 | } 13 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import configureStore from '@/store' 5 | // 主页面 6 | import App from './App' 7 | import ErrorBoundary from '@/components/ErrorBoundary' 8 | 9 | const stores = configureStore() 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | , document.getElementById('app')) 16 | 17 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { routerReducer } from 'react-router-redux' 2 | 3 | import { persistCombineReducers } from 'redux-persist' 4 | import storage from 'redux-persist/lib/storage' 5 | 6 | const context = require.context('./', true, /\.js$/) 7 | const keys = context.keys().filter(item => item !== './index.js') 8 | const reducers = {} 9 | for (let i = 0; i < keys.length; i += 1) { 10 | reducers[keys[i].replace(/^\.\/(.*)\.js$/, "$1").replace(/(\/)/g, "_")] = context(keys[i]).default 11 | } 12 | 13 | export const config = { 14 | key: 'root', 15 | storage, 16 | whitelist: [ 17 | 'routing' 18 | ] 19 | } 20 | 21 | // 合并reducer 22 | export default persistCombineReducers(config, { 23 | routing: routerReducer, // 默认引入路由reducer 24 | ...reducers 25 | }) 26 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | 2 | // 你也可以在第一层路由加载主布局容器,然后在子路由加载菜单和内容路由 3 | 4 | import MainContainer from '@/containers/Main' 5 | 6 | /** 7 | * title 链接名称 8 | * path 路由地址 9 | * exact 路由渲染完全匹配 10 | * component 路由组件 11 | * routes 子路由 12 | */ 13 | const routes = [ 14 | { 15 | title: '首页', 16 | path: '/', 17 | exact: true, 18 | component: MainContainer, 19 | // routes: 子路由 格式和本文件一致 20 | } 21 | ] 22 | 23 | export default routes 24 | -------------------------------------------------------------------------------- /src/sagas/index.js: -------------------------------------------------------------------------------- 1 | import { fork, all } from 'redux-saga/effects' 2 | 3 | const context = require.context('./', true, /\.js$/) 4 | const keys = context.keys().filter(item => item !== './index.js') 5 | const sagas = [] 6 | for (let i = 0; i < keys.length; i += 1) { 7 | sagas.push(context(keys[i]).default) 8 | } 9 | 10 | const sagasForks = sagas.map(saga => fork(saga)) 11 | 12 | // 根saga 13 | export default function* rootSaga () { 14 | yield all(sagasForks) 15 | } 16 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux' 2 | import { persistStore } from 'redux-persist' 3 | import reducers from '@/reducers' 4 | import createHistory from 'history/createHashHistory' 5 | import { routerMiddleware } from 'react-router-redux' 6 | import createSagaMiddleware from 'redux-saga' 7 | import sagas from '@/sagas' 8 | import logger from 'redux-logger' 9 | 10 | // 创建history 11 | export const history = createHistory() 12 | 13 | //创建saga中间件 14 | const sagaMiddleware = createSagaMiddleware() 15 | 16 | // 需要调用的中间件 17 | const middleWares = [ 18 | sagaMiddleware, 19 | routerMiddleware(history), 20 | logger 21 | ] 22 | 23 | // 生成最终的store函数 24 | export default function configureStore( onComplete = () => {} ){ 25 | // 生成store 26 | const store = createStore(reducers, undefined, compose( 27 | applyMiddleware(...middleWares), 28 | )) 29 | // 将store数据保存到缓存 30 | const persistor = persistStore(store, null, onComplete) 31 | // 运行saga 32 | sagaMiddleware.run(sagas) 33 | 34 | return { persistor, store } 35 | } 36 | -------------------------------------------------------------------------------- /src/styles/less/index.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/czewail/zewail-react/d19ca88734bd374857d0d1c1e3a7d0fbf8233f67/src/styles/less/index.less --------------------------------------------------------------------------------