• 写于2018-03-27 参考Demo:https://github.com/javaLuo/react-app...react@16.x react-router@4.x redux@3.x webpack@4.x 将要用到以下开发工具 node.js webstorm yarn (npm install yarn -g) 一、这就开...

    写于2018-03-27
    参考Demo:https://github.com/javaLuo/react-app

    基本

    最基本的将包含以下框架及插件

    • react@16.x
    • react-router@4.x
    • redux@3.x
    • webpack@4.x

    将要用到以下开发工具

    • node.js
    • webstorm
    • yarn (npm install yarn -g)

    一、这就开始了

    你可以使用官方维护的 create-react-app 生成一个新项目,但生成出来仅包含了最基本的react。
    本文不使用create-react-app,但目录结构会和官方保持一致。

    1、创建新项目

    • 打开node.js控制台,进入某个目录
    • 运行以下命令,生成一个全新的package.json
    yarn init

    2、引入react、react-router、redux相关插件

    yarn add react              // react核心
    yarn add react-dom          // 新版本react单独提取了渲染相关函数
    yarn add react-router-dom   // 前端路由器
    yarn add redux              // redux核心
    yarn add react-redux        // 为了把react组件挂载到redux
    yarn add react-router-redux // 为了保持状态与路由同步
    yarn add react-loadable     // 代码分割按需加载
    yarn add prop-types         // 检查传入子组件的props参数类型,有效防止忘记子组件有哪些props参数
    yarn add history            // 第3方的history,比较好用。也可以用react-router自带的

    除此之外还需要选择一种处理异步action的redux中间件
    redux-thunkredux-sagaredux-promise

    yarn add redux-thunk

    然后是webpack

    yarn add webpack -D                 // webpack核心
    yarn add webpack-cli -D             // 4.0以后需要这个来进行build
    yarn add webpack-dev-middleware -D  // 小型服务器
    yarn add webpack-hot-middleware -D  // HMR热替换插件
    yarn add clean-webpack-plugin -D    // 打包时自动删除上一次打包的旧数据
    yarn add extract-text-webpack-plugin@next -D    // 提取CSS,生成单独的.css文件
    yarn add html-webpack-plugin -D     // 通过模板生成index.html,自动加入script/style等标签

    也可以使用webpack内置的webpack-dev-server配合react-hot-loader实现HMR


    接下来需要相关的webpack解析器(虽然应该先安装这些解析器的依赖项,不过等会儿再装)

    yarn add babel-loader -D    // 解析js文件中的ES6+/JSX语法
    yarn add css-loader -D      // 解析css模块(import的css文件)
    yarn add eslint-loader -D   // 打包前检测语法规范要用
    yarn add file-loader -D     // 解析所有的文件(字体、视频、音频等)
    yarn add url-loader -D      // 与file-loader类似,但可以把小图片编码为base64
    yarn add postcss-loader -D  // 自动为css添加-webkit-前缀等功能
    yarn add less-loader -D     // 解析.less文件
    yarn add sass-loader -D     // 解析.scss/.sass文件
    yarn add style-loader -D    // 自动将最终css代码嵌入html文件(<style>标签)
    yarn add csv-loader -D      // 解析office的表格excel文件
    yarn add xml-loader -D      // 解析xml文件

    以上的部分解析器需要相关依赖,也需要额外的插件优化项目

    yarn add babel-core -D          // babel核心,babel-loader依赖
    yarn add less -D                // less-loader依赖
    yarn add node-sass -D           // sass-loader依赖
    yarn add autoprefixer -D        // postcss的插件
    yarn add eslint -D              // eslint代码规范检测器
    yarn add babel-eslint -D        // 让eslint支持一些新语法
    yarn add babel-plugin-transform-class-properties -D  // 支持类中直接定义箭头函数
    yarn add babel-plugin-transform-decorators-legacy -D // 支持ES8修饰器
    yarn add babel-plugin-syntax-dynamic-import -D       // 支持异步import语法
    yarn add babel-runtime -D       // 各种浏览器兼容性垫片函数
    yarn add babel-plugin-transform-runtime -D  // 避免重复编译babel-runtime中的代码
    yarn add babel-preset-env -D    // 自动识别浏览器环境运用对应的垫片库兼容ES6+语法
    yarn add babel-preset-react -D  // 让babel支持解析JSX语法
    yarn add eslint-plugin-react -D // 让eslint支持检测JSX语法
    

    还需要一个node.js的后端框架,为了启动一个服务。选择expresskoa
    你也可以用webpack自带的webpack-dev-server命令行模式来启动本地服务。
    但自己配置会有更高的自由度,比如之后可以配置mock.js模拟数据等。

    yarn add express -D

    3、开始手动新建项目结构

    关于结构:最佳实践是按照业务模块来分store/action/router,
    可以按照自己的喜好创建不同的文件夹,
    本文为了简单是按照功能来划分的。你可以在构建大型系统时按照最佳实践的方式划分。

    1

    二、开始各项配置

    1、配置/.babelrc文件

    {
      "presets": [
        "babel-preset-env",    支持ES6+新语法
        "babel-preset-react"   支持react相关语法(JSX)
      ],
      "plugins": [
        "transform-runtime",            使用垫片库兼容各种浏览器
        "transform-decorators-legacy",  支持修饰器语法
        "transform-class-properties",   支持class类中直接定义箭头函数
        "syntax-dynamic-import",        支持异步import语法
        "react-loadable/babel"          这个在服务端渲染中使用代码分割有用,虽然现在没用,但还是留着吧
      ]
    }
    

    2、配置/eslint.json文件

    {
        "env": {
            "browser": true,    默认已声明浏览器端所有全局对象
            "commonjs": true,   默认已声明commonjs所有全局对象
            "es6": true,        默认已声明ES6+所有全局对象
            "jquery": true      默认已声明$符号
        },
        "parser": "babel-eslint", 使用babel-eslint插件定义的语法(支持ES6+)
        "extends": "plugin:react/recommended", 默认的语法规则,必须用这个,其他的要报错
        "parserOptions": {      更精细的语言配置
            "ecmaVersion": 8,   支持到ES8的所有新特性
            "ecmaFeatures": {   额外的规则
                "impliedStrict": true,                启动严格模式
                "experimentalObjectRestSpread": true, 启用实验性的 object rest/spread properties 支持
                "jsx": true                           jsx语法支持
            },
            "sourceType": "module"                    按照Ecma模块语法对代码进行检测
        },
        "plugins": [    插件
            "react",    eslint-plugin-react插件,支持react语法
        ],
        "rules": {     自定义的规则
            "semi": "warn",                         语句结尾要用分号,否则警告
            "no-cond-assign": "error",              禁止条件表达式中出现赋值操作符,否则报错
            "no-debugger": "error",                 禁用 debugger,否则报错
            "no-dupe-args": "error",                禁止 function 定义中出现重名参数
            "no-caller": "error",                   禁用 arguments.caller 或 arguments.callee
            "no-unmodified-loop-condition": "error",禁用一成不变的循环条件
            "no-with": "error",                     禁用with语句
            "no-catch-shadow": "error"              禁止 catch 子句的参数与外层作用域中的变量同名
        }
    }

    3、/postcss.config.js

    module.exports = {
      plugins: [require("autoprefixer")()]
    };
    

    三、配置webpack

    1、/webpack.dev.config.js 开发环境使用的配置

    /** 这是用于开发环境的webpack配置文件 **/
    
    const path = require("path");       // 获取绝对路径用
    const webpack = require("webpack"); // webpack核心
    const HtmlWebpackPlugin = require("html-webpack-plugin"); // 动态生成html插件
    
    module.exports = {
      mode: "development",             // 使用webpack推荐的开发环境配置
      entry: [
        "webpack-hot-middleware/client?reload=true&path=/__webpack_hmr", // webpack热更新插件配置
        "./src/index.js"              // 指向项目入口
      ],
      output: {
        path: "/",            // 将打包好的文件放在此路径下,dev模式中,只会在内存中存在,不会真正的打包到此路径
        publicPath: "/",      // 文件解析路径,index.html中引用的路径会被设置为相对于此路径
        filename: "bundle.js" // 编译后的文件名字
      },
      devtool: "inline-source-map", // 报错的时候在控制台输出哪一行报错
      context: __dirname,           // entry 和 module.rules.loader 选项相对于此目录开始解析
      module: {                     // 各种解析器配置
        rules: [
          {
            // 编译前通过eslint检查代码规范
            test: /\.js?$/,                          // 检查.js结尾的文件
            enforce: "pre",                          // 在编译之前执行
            use: ["eslint-loader"],                  // 使用哪些解析器
            include: path.resolve(__dirname, "src")  // 只解析这个目录下的文件
          },
          {
            // .js .jsx用babel解析
            test: /\.js?$/,
            use: ["babel-loader"],
            include: path.resolve(__dirname, "src")
          },
          {
            // .css 解析
            test: /\.css$/,
            use: [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  modules: true,  // 配置为true的话,代码需要按模块的形式使用css,最终编译后class会带有一串hash码
                  localIdentName: "[local]_[hash:base64:5]" // 定义最终编译class命名规则
                }
              },
              "postcss-loader"
            ]
          },
          {
            // .less 解析
            test: /\.less$/,
            use: [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  modules: true,
                  localIdentName: "[local]_[hash:base64:5]"
                }
              },
              "postcss-loader",
              "less-loader"
            ],
            include: path.resolve(__dirname, "src")
          },
          {
            // .scss 解析
            test: /\.scss$/,
            use: [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  modules: true,
                  localIdentName: "[local]_[hash:base64:5]"
                }
              },
              "postcss-loader",
              "sass-loader"
            ]
          },
          {
            // 文件解析
            test: /\.(eot|woff|otf|svg|ttf|woff2|appcache|mp3|mp4|pdf)(\?|$)/,
            include: path.resolve(__dirname, "src"),
              use: [
                  "file-loader?name=assets/[name].[ext]"
              ]
          },
          {
            // 图片解析
            test: /\.(png|jpg|gif)(\?|$)/,
            include: path.resolve(__dirname, "src"),
              use: [
                  "url-loader?limit=8192&name=assets/[name].[ext]" // 小于8KB的图片将被编译为base64
              ]
          },
          {
            // CSV/TSV文件解析
            test: /\.(csv|tsv)$/,
            use: [
               'csv-loader'
            ]
          },
          {
            // xml文件解析
            test: /\.xml$/,
            use: [
              'xml-loader'
             ]
          }
        ]
      },
      plugins: [
        //根据模板插入css/js等生成最终HTML
        new HtmlWebpackPlugin({
          filename: "index.html",          //生成的html存放路径,相对于 output.path
          favicon: "./public/favicon.ico", // 自动把favicon.ico图片加入html
          template: "./public/index.html", // html模板路径
          inject: true                     // 是否自动创建script标签,设为false则不会自动引入js
        }),
        new webpack.HotModuleReplacementPlugin() // 热更新插件
      ],
      resolve: {
        extensions: [".js", ".jsx", ".less", ".css", ".scss"] //后缀名自动补全
      }
    };
    

    2、配置/webpack.production.config.js

    略。跟开发环境类似,只有几个参数不同。
    参考:https://github.com/javaLuo/react-app/blob/master/webpack.production.config.js

    3、接下来需要配置一个本地服务用于启动开发环境:/server.js

    /** 用于开发环境的服务启动 **/
    const path = require("path");       // 获取绝对路径有用
    const express = require("express"); // express服务器端框架
    const bodyParser = require("body-parser"); // 解析post请求时body中带的参数
    const env = process.env.NODE_ENV;   // 模式(dev开发环境,production生产环境)
    const webpack = require("webpack"); // webpack核心
    const webpackDevMiddleware = require("webpack-dev-middleware"); // webpack服务器
    const webpackHotMiddleware = require("webpack-hot-middleware"); // HMR热更新中间件
    const webpackConfig = require("./webpack.dev.config.js");       // webpack开发环境的配置文件
    
    const app = express();                      // 实例化express服务
    const DIST_DIR = webpackConfig.output.path; // webpack配置中设置的文件输出路径,所有文件存放在内存中
    const PORT = 8888;                          // 服务启动端口号
    
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
    
    if (env === "production") {
      // 如果是生产环境,则运行build文件夹中最终正式打包后的代码
      app.use(express.static("build"));
      app.get("*", function(req, res) {
        res.sendFile(path.join(__dirname, "build", "index.html"));
      });
    } else {
      // 否则就利用webpack配置启动开发环境
      const compiler = webpack(webpackConfig); // 实例化webpack
      app.use(
        webpackDevMiddleware(compiler, {
          // 挂载webpack小型服务器
          publicPath: webpackConfig.output.publicPath, // 对应webpack配置中的publicPath
          quiet: true, // 是否不输出启动时的相关信息
          stats: {
            colors: true, // 不同信息不同颜色
            timings: true // 输出各步骤消耗的时间
          }
        })
      );
      // 挂载HMR热更新中间件
      app.use(webpackHotMiddleware(compiler));
      // 所有请求都返回index.html
      app.get("*", (req, res, next) => {
        // 由于index.html是由html-webpack-plugin生成到内存中的,所以使用下面的方式获取
        const filename = path.join(DIST_DIR, "index.html");
        compiler.outputFileSystem.readFile(filename, (err, result) => {
          if (err) {
            return next(err);
          }
          res.set("content-type", "text/html");
          res.send(result);
          res.end();
        });
      });
    }
    
    /** 启动服务 **/
    app.listen(PORT, () => {
      console.log("本地服务启动地址: http://localhost:%s", PORT);
    });
    

    4、最终的/package.json文件

    (自己新建scripts, 或者也可以使用npx命令)
    start 启动开发环境
    build 生产环境打包
    dist 运行生产环境下的代码(注意&&前面千万不要有空格)

    {
      "name": "react-app",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "scripts": {
        "start": "node server.js",
        "build": "webpack -p --config webpack.production.config.js --progress --profile --colors --display errors-only",
        "dist": "set NODE_ENV=production&& node server.js",
      },
      "dependencies": {
        "history": "^4.7.2",
        "prop-types": "^15.6.1",
        "react": "^16.2.0",
        "react-dom": "^16.2.0",
        "react-loadable": "^5.3.1",
        "react-redux": "^5.0.7",
        "react-router-dom": "^4.2.2",
        "react-router-redux": "^5.0.0-alpha.6",
        "redux": "^3.7.2",
        "redux-thunk": "^2.2.0"
      },
      "devDependencies": {
        ...
      }
    }
    

    四、开始写代码(简单模拟登录功能)

    redux最重要的几个概念:

    • store 数据中心
    • action 行为动作
    • dispatch 分发
    • reducer 改变store数据的唯一方法

    一般流程是:

    ①、用户点击按钮
    ②、按钮被绑定了事件,事件触发action
    ③、action中发送请求获取后台数据
    ④、用dispatch把数据分发给reducer(redux自动触发对应的reducer)
    ⑤、reducer中把得到的新数据存入store
    ⑥、组件(页面)中获取store最新的数据,展现出来


    下面要做:

    • 从登录页登录
    • 登录成功跳转到主页
    • 主页有个按钮,每点一下,数字自动+1

    1、 /public/index.html 主页

    代码略,参考Demo

    2、创建 /src/actions/app-action.js

    /**
     * action只是一些纯函数
     * 一些公共的action可以写在这里,比如用户登录、退出登录、权限查询等
     * 其他的action可以按模块不同,创建不同的js文件
     * */
    
    import FetchApi from "../util/fetch-api"; // 自己写的工具函数,只是简单的封装了请求数据的通用接口
    
    /** 测试:数字+1,普通的分发触发reducer **/
    export const onTestAdd = (params) => async dispatch => {
      dispatch({
        type: "APP::add",
        payload: params
      });
    };
    
    /** 测试:用户登录 **/
    export const serverLogin = (params = {}) => async dispatch => {
      try {
        // const res = await FetchApi.newFetch("login.ajax", params);
        // 为了简便,这里直接使用假数据返回
        const res = { status: 200, data: { username: params.username, password: params.password }, message: '登录成功' };
        dispatch({              // 内容分发
          type: "APP::LOGIN",   // 会自动触发/src/reducers/app-reducer.js中对应的方法
          payload: res.data     // 传递到reducer中的数据
        });
          console.log('到这里了吗', res);
        return res;       // 同时也将数据直接return到页面组件中
      } catch (err) {
        console.error("网络错误,请重试");
      }
    };
    

    3、创建 src/util/fetch-api.js

    为了发送请求,可以选择一种异步请求库
    jquery 或 reqwest 或 axios

    yarn add axios
    /**
     * 自己封装的异步请求函数
     * APP中的所有请求都将汇聚于此
     * **/
    
    import axios from "axios"; // 封装了fetch请求的库
    
    export default class ApiService {
    
        /** fetch请求(用的axios.js)
         * @param url 请求的地址
         * @param bodyObj 请求的参数对象
         */
      static newFetch(url, bodyObj = {}) {
        return axios({
          url,
          method: "post",
          headers: {
            "Content-Type": "application/json;charset=utf-8"
          },
          withCredentials: true,
          data: JSON.stringify(bodyObj)
        });
      }
    }
    

    4、创建 src/reducers/app-reducer.js

    /** 初始值 **/
    const initState = {
      num: 0,       // 页面测试数据 初始值0
      userinfo: {}, // 存放登录后的用户信息
    };
    
    /** 对应的reducer处理函数,改变store中的值 **/
    const actDefault = state => state;
    
    const add = (state, { payload }) => {
      return Object.assign({}, state, {
        num: payload
      });
    };
    
    const login = (state, { payload }) => {
      return Object.assign({}, state, {
        userinfo: payload
      });
    };
    
    /** 接收action触发的dispatch, 执行对应的reducer处理函数 **/
    const reducerFn = (state = initState, action) => {
      switch (action.type) {
        case "APP::add":   // 用户点击按钮数字+1
          return add(state, action);
        case "APP::LOGIN": // 用户登录
          return login(state, action);
        default:
          return actDefault(state, action);
      }
    };
    
    export default reducerFn;
    

    5、创建 src/reducers/index.js

    /**
     * 根reducer
     * 用于结合 App 中所有的 reducer
     * 使用 combineReducers 来把多个 reducer 合并成一个根 reducer
     */
    
    import { combineReducers } from "redux";
    import { routerReducer } from "react-router-redux";
    
    import appReducer from "./app-reducer"; // 引入之前创建的reducer
    
    const RootReducer = combineReducers({
      // 注意一定要加上routing: routerReducer 这是用于redux和react-router的连接
      routing: routerReducer,
      // 其他自定义的reducer
      app: appReducer // 这里的命名,会成为store命名空间,组件中根据命名来获取对应reducer中的数据
    });
    
    export default RootReducer;
    

    6、创建 src/containers/root/index.js 根组件

    /** 根页 - 包含了根级路由 **/
    
    import React from "react";
    import { connect } from "react-redux";
    import { bindActionCreators } from "redux";
    import { Router, Route, Switch, Redirect } from "react-router-dom";
    import P from "prop-types";
    // import createHistory from 'history/createBrowserHistory';   // URL模式的history
    import createHistory from "history/createHashHistory";         // 锚点模式的history
    import Loadable from "react-loadable";                         // 用于代码分割时动态加载模块
    
    /** 普通组件 **/
    import Loading from "../../components/loading"; // loading动画组件
    
    /** 下面是代码分割异步加载的方式引入各页面 **/
    const Home = Loadable({ // 主页
        loader: () => import("../home"),
        loading: Loading,   // 自定义的Loading动画组件
        timeout: 10000      // 可以设置一个超时时间来应对网络慢的情况(在Loading动画组件中可以配置error信息)
    });
    const Login = Loadable({// 登录页
        loader: () => import("../login"),
        loading: Loading
    });
    
    const history = createHistory();    // 实例化history对象
    
    @connect(
        state => ({}),
        dispatch => ({
            actions: bindActionCreators({}, dispatch)
        })
    )
    export default class RootContainer extends React.Component {
        static propTypes = {
            dispatch: P.func,
            children: P.any
        };
    
        constructor(props) {
            super(props);
        }
    
        componentDidMount() {
            // 可以手动在此预加载指定的模块:
            //Home.preload(); // 预加载Features页面
            //Login.preload(); // 预加载Test页面
            // 也可以直接预加载所有的异步模块
            Loadable.preloadAll();
        }
    
        /** 权限控制 **/
        onEnter(Component, props) {
            // 例子:如果没有登录,直接跳转至login页
            if (sessionStorage.getItem('userInfo')) {
              return <Component {...props} />;
            }
            return <Redirect to='/login' />;
        }
        // 下面配置了根级路由
        render() {
            return [
                <Router history={history}>
                    <Route
                        render={() => {
                            return (
                                <Switch>
                                    <Redirect exact from="/" to="/home" />
                                    <Route
                                        path="/home"
                                        render={props => this.onEnter(Home, props)}
                                    />
                                    <Route
                                        path="/login"
                                        render={props => this.onEnter(Login, props)}
                                    />
                                </Switch>
                            );
                        }}
                    />
                </Router>
            ];
        }
    }

    7、创建 src/store/index.js 数据中心

    /** 全局唯一数据中心 **/
    import { createStore, applyMiddleware } from "redux";
    import ReduxThunk from "redux-thunk"; // 管理异步action的插件,为了使action中能够使用异步请求
    import RootReducer from "../reducers";
    
    // 创建所需的所有中间件
    const middlewares = [];
    // 加入需要的中间件
    middlewares.push(ReduxThunk);
    
    // 实例化store
    const store = createStore(RootReducer, applyMiddleware(...middlewares));
    
    // REDUX 2.x 中,HMR检测不到reducer的变化,所以在创建store的文件中加入下面代码
    if (module.hot) {
      module.hot.accept("../reducers", () => {
        const nextRootReducer = require("../reducers/index");
        store.replaceReducer(nextRootReducer);
      });
    }
    export default store;
    

    至此,所有项目中必要的文件都创建完毕了
    剩下的便是添加所需业务模块和代码

    以上示例中还需要创建:

    • src/containers/home/index.js 作为主页
    • src/containers/login/index.js 作为登录页
    • src/components/loading/index.js 作为按需加载时显示的loading动画, 代码略,参考Demo

    8、创建 src/containers/home/index.js 主页

    /** 主页 **/
    
    import React from "react";
    import { connect } from "react-redux";
    import { bindActionCreators } from "redux";
    import { Link, } from "react-router-dom";
    import P from "prop-types";
    
    import { onTestAdd } from '../../actions/app-action';
    @connect(
      state => ({
          userinfo: state.app.userinfo, // 从store中获取userinfo
          num: state.app.num,
      }),
      dispatch => ({
        actions: bindActionCreators({ onTestAdd }, dispatch)
      })
    )
    export default class HomePageContainer extends React.Component {
      static propTypes = {
        userinfo: P.any,
        num: P.number,
        location: P.any,
        history: P.any,
        actions: P.any
      };
    
      constructor(props) {
        super(props);
        this.state = {};
      }
    
      onAdd = () => {
          const n = this.props.num+1;
          this.props.actions.onTestAdd(n);
      };
    
      render() {
        return (
            <div>
                <h2>Hello, {this.props.userinfo.username}</h2>
                <div>
                    <span>{this.props.num}</span><br/>
                    <button onClick={this.onAdd}>+1</button>
                </div>
                <Link to="/login">去登录页</Link>
            </div>
        );
      }
    }
    

    9、创建 src/containers/login/index.js 登录页

    /** 登录页 **/
    
    // ==================
    // 所需的各种插件
    // ==================
    import React from "react";
    import { connect } from "react-redux";
    import { bindActionCreators } from "redux";
    import P from "prop-types";
    import css from './index.scss';
    
    import { serverLogin } from '../../actions/app-action'; // 引入需要用到的action
    
    @connect(
      state => ({}),
      dispatch => ({
        actions: bindActionCreators({ serverLogin }, dispatch)  // 将需要用到的action挂载到redux中
      })
    )
    export default class LoginPageContainer extends React.Component {
      static propTypes = {
        location: P.any,
        history: P.any,
        actions: P.any
      };
    
      constructor(props) {
        super(props);
        this.state = {
            username: '',   // 用户名
            password: '',   // 密码
        };
      }
    
      onUserName = (v) => {
          this.setState({
              username: v.target.value,
          });
      };
    
      onPassword = (v) => {
          this.setState({
              password: v.target.value,
          });
      };
    
      onSubmit = () => {
          const params = {
              username: this.state.username,
              password: this.state.password,
          };
          console.log('触发:', params);
        this.props.actions.serverLogin(params).then((res) => {
            console.log('返回:', res);
            if(res.status === 200) {
                // 登录成功,跳转到主页
                sessionStorage.setItem("userInfo", true);
                this.props.history.push('/home');
            }
        });
      };
    
      render() {
        return (
            <div className={css.login}>
                <div>
                    <h2>登录</h2>
                    <input type="text" value={this.state.username} onInput={this.onUserName}/>
                    <br/>
                    <input type="password" value={this.state.password} onInput={this.onPassword}/>
                    <br/>
                    <button onClick={this.onSubmit}>提交</button>
                </div>
            </div>
        );
      }
    }
    

    五、运行项目

    yarn run start

    a

    b

    六、额外配置

    • 可以配置antd UI库,功能齐全,使用很方便

    • 可以配置prettier,一键自动代码格式化,再也不必担心eslint报错


    文章源码: https://github.com/javaLuo/react-app

    展开全文
  • 环境变量: 在根目录直接新建文件: .env.production ...必须是 REACT_APP_ 开头的 REACT_APP_ROOT='/api' 读取不变还是: let root = process.env.REACT_APP_ROOT 设置代理 在src 根目录新建:set...

    环境变量:
    在根目录直接新建文件:
    .env.production
    就是生产环境的变量
    当然 .env.development .env.test就是开发和测试环境了

    变量规则:
    必须是 REACT_APP_ 开头的

    REACT_APP_ROOT='/api'
    

    读取不变还是:

    let root = process.env.REACT_APP_ROOT
    

    设置代理

    在src 根目录新建:setupProxy.js
    配置还是一样的

    const proxy = require('http-proxy-middleware')
    
    module.exports = function (app) {
      app.use(proxy('/api', {
        target: 'http://192.168.152.55:6666',
        changeOrigin: true,
        pathRewrite: {
          "^/api": ""
        }
      }))
    }
    

    react会自动加载这个文件

    展开全文
  • react配置导入根路径

    2019-12-31 17:27:57
    react配置导入根路径需要在webpack中配置,但使用脚手架创建的项目,默认将babel、webpack等内容全部封装到react-scripts中,所以在这种状态下没有办法更改webpack中的内容 需要执行npm run eject解开react-scripts...

    前提

    react配置导入根路径需要在webpack中配置,但使用脚手架创建的项目,默认将babelwebpack等内容全部封装到react-scripts中,所以在这种状态下没有办法更改webpack中的内容

    需要执行npm run eject解开react-scripts

    • npm run eject 命令无法撤销
    • 执行后,会出现configscripts 两个文件夹

    方法

    在新出现的config 文件夹中,找到 webpack.config.js 文件,在alias 中插入'@': path.resolve('src'),就可以了

    alias: {
            '@': path.resolve('src'),
            // Support React Native Web
            // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
            'react-native': 'react-native-web',
            // Allows for better profiling with ReactDevTools
            ...(isEnvProductionProfile && {
              'react-dom$': 'react-dom/profiling',
              'scheduler/tracing': 'scheduler/tracing-profiling',
            }),
            ...(modules.webpackAliases || {}),
          },
    

    在以后引入时,#代表的就是 根目录/src

    个人博客

    展开全文
  • react配置绝对路径

    2019-09-25 15:14:15
    react中 如果想使用例如img的东西 它不像vue里面直接拿过来使用 react则需要手动引入 但是当项目目录嵌套太深的时候 很容易出现错误 有没有什么简单的方式呢 那肯定会有的了 react官网里面其实也有告诉配置的 只是...

    在react中  如果想使用例如img的东西 它不像vue里面直接拿过来使用 react则需要手动引入 但是当项目目录嵌套太深的时候 很容易出现错误 有没有什么简单的方式呢 那肯定会有的了 react官网里面其实也有告诉配置的 只是不细心的人可能找不到 说白点 就是配置成绝对路径 看看到底该怎么配置吧

    1. 在根目录下创建一个jsconfig.json文件(是根目录 而非src 相当于是和src同级的

    2.在里面添加配置(添加完成后 一定要记得重新启动项目 这就相当于修改了配置文件 不重启会报错的

    {
        "compilerOptions":{
            "baseUrl":"src"
        },
        "include":["src"]
    }

    3.页面引入 (它默认就是src 所以 只需要写绝对路径就行了

    展开全文
  • react router 配置404页面

    2019-07-18 01:37:46
    react router 配置404页面 使用Vue相关的技术栈2年左右了,在路由管理上一直用的比较得心应手,现在的项目使用react开发,路由自然也就切换到了react router,所用版本为react router 4 在Vue router配置中,我们...

    react router 配置404页面

    使用Vue相关的技术栈2年左右了,在路由管理上一直用的比较得心应手,现在的项目使用react开发,路由自然也就切换到了react router,所用版本为react router 4

    在Vue router配置中,我们可以很简单的配置出404页面
    使用通配符 * 号匹配所有路由,并将此配置放在数组的最末端,当前面的路由都匹配不上时,就会匹配到 * 号,然后我们使用redirect 配置将路由重定向到404component
    此配置方法不管是对于单级路由还是嵌套路由都适用,只要匹配不到,最终都可以通过 * 来redirect

    export default new Router({
      routes: [
        {
          path: '/',
          name: 'home',
          component: Home
        },
        {
          path: '/404',
          name: 'home',
          component: Home
        },
        {
          path: '/about',
          name: 'about',
          component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
          children: [
            {
              path: 'page1',
              name: 'page1',
              component: page1
            },
            {
              path: 'page2',
              name: 'page2',
              component: page2
            },
          ]
        },
        {
          path: '*',
          redirect: '/404'
        }
      ]
    })
    

    那么 react router 4 有什么不同的地方呢?

    首先是配置子路由的位置不同

    react router 4配置嵌套路由是在所在组件里面进行配置

    // Parent component
    	<Switch>
          <Route path="/Page" component={Page} />
          <Route path="/Sub" component={Childs} />
        </Switch>
    
    // Child component
    const Childs = ({ match }) => (
      <div>
        <Switch>
          <Route path={`/Sub/page1`} component={Page1} />
          <Route path={`/Sub/page2`} component={Page2} />
        </Switch>
      </div>
    );
    

    在父组件里定义好子组件
    这样就可以通过 '/Sub/page1’访问到该路由页面了

    其次,react router 4还在路由匹配上有些不同,R4没有通配符* ,取而代之的是在路由最后可以添加

    <Switch>
      <Route path="/Page" component={Page} />
      <Route path="/Sub" component={Childs} />
      <Route component={NoMatch} />  // 匹配未找到的路由
    </Switch>
    

    参考链接:react router官方文档

    这时候,我们添加404页面在根组件,it works!

    但是,我们的需求往往不是这么简单,通常情况下,我们希望在所有路由情况下他都可以校验路由是否不存在


    problem:

    在嵌套路由下,我们发现:访问 /Sub/page3’’ 仍然可以看到不完整的路由页面,虽然没有匹配到page3,但是router匹配到了/Sub,这时候问题来了,子路由匹配不到404页面啊。

    我查看了当前比较火的react前端解决方案 <react-admin>和<ant-design-pro>,发现他们都没有全局404页面的解决方案。

    查了很久文档和他人的文章,最终找到一个解决方案,借鉴出处:一个神奇的网站

    1. 既然R4是在父组件内部配置路由的,那么内部也要添加404匹配路由
    // 在子组件中添加404路由
     <Switch>
      <Route path={`/Sub/page1`} component={Page1} />
      <Route path={`/Sub/page2`} component={Page2} />
      <Route component={NoMatch} />
    </Switch>
    
    // NoMatch(404)组件
    
    class NoMatch extends React.Component {
      componentDidMount() {
      }
      render() {
        return (
          <div className="404">
            404
          </div>
        );
      }
    }
    
    1. 直接匹配404依然会渲染父组件,所以我们需要重定向
    // RedirectAs404
    const RedirectAs404 = ({ location }) => <Redirect to='/404' />
    
    // 通过Redirect将当前路由重定向到
     <Switch>
      <Route path={`/Sub/page1`} component={Page1} />
      <Route path={`/Sub/page2`} component={Page2} />
      <Route component={RedirectAs404} />
    </Switch>
    
    1. 发现直接redirect到404不太友好,页面路径也变成了404
      在这里插入图片描述
    2. 在根组件添加一个state,来修改总路由配置
    // RedirectAs404
    const RedirectAs404 = ({ location }) => <Redirect to='{Object.assign({}, location, { state: { is404: true } })}' />
    
    // App.js 这里指根组件
    <Route render={({ location }) => (
      location.state && location.state.is404
        ? <NoMatch />
        : <Switch>
          	<Route path="/Page" component={Page} />
    		<Route path="/Sub" component={Childs} />
            <Route component={RedirectAs404} />
        </Switch>
    )} />
    

    这样,不管是第几级的组件,当只要匹配到RedirectAs404组件,都会重定向到404页面,也就做到了全局404显示

    展开全文
  • React 配置全局变量

    2019-07-08 14:09:34
    React 配置全局变量 使用webpack,您可以将env特定的配置放在webpack.config.js中的externals字段中 externals: { 'Config': JSON.stringify(process.env.ENV === 'production' ? { serverUrl: ...
  • react配置less或者sass

    2020-06-19 10:32:48
    react如何配置less(sass与less配置的方法基本一致,如果还是不会的朋友可以私信我) 接下来笔者会一步一步的跟朋友们一起去做配置 生成react项目, create-react-app <你的项目名> 可以将src目录中的文件删...
  • react配置scss的方法。

    2017-12-01 14:14:21
    留一个牛逼的地址:大漠讲解scss只说在react项目中的配置。 创建完项目后,要执行npm eject 也就是打开全部配置项。 找到config里面的两个配置文档。 配置的内容都是一样的。 代码是:{ test: /\.scss$/, ...
  • create-react-app 生成的项目看不到 webpack 相关的配置文件,需要先暴露出来,使用如下命令即可 npm run eject 修改配置文件 webpack.config.js resolve: { ... alias: { ... // 路径引用 @ '@': p...
  • react配置之绝对路径

    2018-08-17 10:39:29
    绝对路径不多bb,简单的说就是,我们不再使用 ../ ../../之类的来表示路径位置了 直接上代码 ... react-create-app my-react cd my-react npm install npm run ejecj 得到想要的了 再config 中的...
  • react项目配置less

    2019-04-05 19:18:38
    1.配置less前,先暴露出react项目的配置文件,在命令行输入: npm run eject 2.安装less,less-loader: npm install less-loader less --save-dev 3.修改配置文件: 修改webpack.config....
  • react配置全局scss变量

    2019-04-08 15:28:18
    自己在搭一个react的移动端demo,考虑到以后可能回做样式整体调整,就进行了scss变量全局的配置配置过程如下: 1.安装sass-resources-loader npm i -S sass-resources-loader 2.创建公共配置文件 我创建了两...
  • React 配置装饰器

    2018-04-17 21:51:23
    React文件做到对装饰器的编译,只需要简单几步弹出webpack配置 npm run eject安装相关依赖 npm install transform-decorators-legacy --save-dev编写.babelrc文件 ​ { "presets": [ "react-app...
  • React 脚手架 create-react-app 新版使用说明 重点是配置代理 近期更新了一下 create-react-app 工具,然后发现,和原来的老版本使用出现了略微的差异。比如原先想要处理 sass 还需要去手动配置 webpack 但是新版...
  • webstorm快速配置react

    2019-04-18 17:33:49
    因为链接多,所以懒得...ps:顺便给个官网配置react的链接,谷歌打开翻译吧 链接 然后创建react工程,文件名只能是字母或者中划线‘-’ ps:反正不支持中文和下划线 等待安装完成就能用了 ↓顺便,我是在这...
  • 首先可以参考一下官方文档 添加自定义环境变量, 怎么创建环境 接下来进入正文 1,创建 .env, .env.test, .env.production文件 # 默认测试环境 .env REACT_APP_ENV=development ...REACT_APP_ENV=p...
  • 大家好,我是凯文,本篇文章将介绍React前端框架的环境配置以及项目搭建方法,其中涉及到了node.js(js运行平台)、npm(依赖包管理工具)等内容。网上已经有许多类似的教程,这篇文章可以给各位做个参考,同时给我...
  • react配置postcss-pxtorem

    2019-08-02 15:49:46
    1.通过 eject 命令暴露出react 全部配置 npm run eject 2.安装 npm i postcss-pxtorem -D 3.配置 webpack.config.js require('postcss-pxtorem')({ rootValue : 100, selectorBlackList : [], //过滤 ...
  • vscode React编程配置

    2018-09-17 17:50:10
    React Native Tools:微软官方出的ReactNative插件,非常好用 Reactjs code snippets:react的代码提示,如componentWillMount方法可以通过cwm直接获得 Auto Close Tag:自动闭合标签 Auto R...
  • 快速搭建react-antd开发环境 下载node使用npm指令 按照antd react-create-app安装 安装yarn 提交所有文件 暴露配置文件 yarn eject 删除node module重新安装 yarn install 根据提示是否需要安装最新版babel-loader@...
1 2 3 4 5 ... 20
收藏数 63,376
精华内容 25,350
关键字:

新版react配置