Новички строят простые строительные леса Express-React-Redux

внешний интерфейс Express React.js Redux

написать впереди

Я обнаружил, что многие учебники для начинающих в Интернете не полностью ориентированы на новичков, и новички могут использовать его. Монахи не могут понять это. В последнее время я хочу использовать узел в качестве бэкэнда и реагировать в качестве фронтенда, чтобы построить небольшой system, и я хочу заснять процесс.Сборка строительных лесов с нуля однозначно подойдет для новичков (я новичок во фронтенде). Поскольку он предназначен для новичков, то удалите этот сложный контент и не рассматривайте все, например, рендеринг на стороне сервера.Это абсолютно мейнстрим: Node + Express используется в качестве бэкэнда, MongoDB используется для базы данных, а React + React - Router используется для внешнего интерфейса, а затем используется Redux для управления состоянием, а затем используется инфраструктура пользовательского интерфейса и т.д. Другие могут быть добавлены непосредственно на этой основе.Новички могут самостоятельно построить скелет проекта, обратившись к следующим шагам, и усвоить некоторые знания в статье. Ниже приведен скриншот строительных лесов:

титульная страница:


Страница со списком пользователей:

Я также безумно настроил для вас 404 страницы (не могу не поставить себе большой палец вверх):

Хоть страниц всего три, но воробей маленький и полный: включая настройку front-end и back-end роутинга, ссылки на базу данных, получение данных, использование react и redux и т.д. говорят, что это реактивное семейное ведро.

адрес проектакликните сюда, ваши любимые друзья могут сниматься!

Первый шаг создать-реагировать-приложение

Официальный скаффолдинг Facebook, базовой конфигурации вполне достаточно, ББ для инициализации проекта создания не так много, на официальный сайт можно зайти самостоятельно. Вот только одно предложение, потому что вы хотите настроить antd для загрузки по запросу, вы можете установить его шаг за шагом в соответствии с официальным сайтом antd, но я столкнулся с некоторыми проблемами при его установке в соответствии с официальным сайтом, и, наконец, следуйте моему собственному. установка.
Сначала установите зависимости:

yarn add react-app-rewired react-app-rewire-less antd babel-plugin-import 
// react-app-rewired 是用来修改create-react-app的默认配置的
// babel-plugin-import 按需加载antd组件必须的插件
// react-app-wire-less antd是依赖less的

Во-вторых, настроить:

  • Измените файл package.json и измените режим запуска на перезапуск.
     "start": "react-app-rewired start",
     "build": "react-app-rewired build",
    
  • Добавьте файл config-overrides.js в корневой каталог, чтобы настроить antd для загрузки по запросу.
    /* config-overrides.js */
      const { injectBabelPlugin } = require('react-app-rewired');
      const rewireLess = require('react-app-rewire-less');
      
      module.exports = function override(config, env) {
         config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config);
         config = rewireLess.withLoaderOptions({
           modifyVars: { "@primary-color": "#ADFF2F" }, // 可以在这里修改antd的默认配置
         })(config, env);
          return config;
      };
    

На этом этапе вы можете ссылаться на antd по мере необходимости в компоненте.

Второй шаг – настройка роутера

// 首先,16之后react-router和react-router-dom安装一个即可
yarn add react-router-dom 
// 其次,使用BrowserRouter作为路由,同时需要history配合
yarn add history
// 最后,router的配置
...
import { Router, Switch, Route, Redirect} from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
... 
const router = (
  <Router history={history}>
    <Switch>
      <Route exact path="/home" component={App}/> // 首页路由
      <Route path="/userList" component={UserList} /> //用户列表页
      <Redirect from='' to="/home" />
    </Switch>
  </Router>
);
ReactDOM.render(router, document.getElementById('root'));
registerServiceWorker();

Узел третьего шага + экспресс

Следующим шагом является добавление в проект бэкенда, экспресса.

  • Создайте новый сервер папок в корневом каталоге, а затем создайте новый package.json со следующим содержимым:
        {
            "name": "server",
            "version": "1.0.0",
            "description": "server config",
            "main": "server.js",
            "author": "luffy",
            "license": "MIT",
            "dependencies": {
              "babel-cli": "^6.26.0",
              "babel-preset-es2015": "^6.24.1",
              "body-parser": "^1.18.2",
              "express": "^4.16.3",
              "mongoose": "^5.0.16"
             },
            "scripts": {
              "start": "nodemon ./server.js",
              "build": "babel ./server.js --out-file server-compiled.js",
              "serve": "node server-compiled.js"
            }
      }
    
    

    Обратите внимание, что исходная команда запуска должна быть node, но для того, чтобы серверная часть также достигла эффекта автоматического обновления измененного кода, nodemon необходимо установить глобально.npm install nodemon -g.

  • Создайте новый файл server.js в папке сервера со следующим содержимым:
      const express = require('express');
      const bodyParser = require('body-parser');
    
      const app = express();
      // 给app配置bodyParser中间件
      // 通过如下配置再路由种处理request时,可以直接获得post请求的body部分
      app.use(bodyParser.urlencoded({ extended: true }));
      app.use(bodyParser.json());
      // 注册路由
      const router = express.Router();
      // 路由中间件
      router.use((req, res, next) => {
        // 任何路由信息都会执行这里面的语句
        console.log('this is a api request!');
        // 把它交给下一个中间件,注意中间件的注册顺序是按序执行
        next();
      })
      // 获取用户列表信息的路由
      router.get('/user/list', (req, res) => {
        const userList = [
          {
            name: 'luffy',
            age: 24,
            gender: '男'
          },{
            name: 'lfy',
            age: 23,
            gender: '女'
          }
        ];
        res.json(userList);
      });
      // 所有的路由会加上“/api”前缀
      app.use('/api', router); //添加router中间件
      
      // express 自动帮我们创建一个server,封装的node底层http
      app.listen(3003, () => {
        console.log('node server is listening 3003');
      });
    

На данный момент здесь нет части маршрутизации, только тестирование.

Четвертый шаг, фронтенд и бэкэнд тестирование

  • Запускайте внутренний и внешний код отдельно
    // 后端运行
    cd server && yarn start
    // 前端运行
    yarn start
    
  • Посетите http://localhost:3003/api/user/list в браузере.

Изображение выше показывает, что серверная часть работает правильно.

  • Добавьте UserList страницы во внешний интерфейс, получите данные из внутреннего интерфейса и отобразите их в компоненте.
      import React, { Component } from 'react';
      import axios from 'axios';
      import { Table } from 'antd';
      
      class UserList extends Component {
        constructor(props) {
          super(props);
          this.state = { userList:[] };
        }
      
        componentDidMount() {
          // 获取用户列表
          axios.get('/api/user/list')
          .then((res) => {
            console.log(res);
            this.setState({ userList: res.data })
          })
          .catch(function (error) {
            console.log(error);
          });
        }
      
        render() {
          const columns = [{
            title: '姓名',
            dataIndex: 'name',
            key: 'name',
          }, {
            title: '年龄',
            dataIndex: 'age',
            key: 'age',
          }, {
            title: '性别',
            dataIndex: 'gender',
            key: 'gender',
          }];
          return (
            <div>
              <h1 style={{ textAlign: 'center' }}>用户列表页</h1>
              <div style={{ width: '50%', margin: '10px auto' }}>
                <Table dataSource={this.state.userList} columns={columns} />
              </div>
            </div>
          )
        }
      }
      export default UserList;
    

httpRequest использует axios, можно использовать fetch, но он идет в ногу со временем, ведь vue2.0 рекомендует axios, да и документация хорошая.yarn add axios.

  • Начинайте одновременно, спереди и сзади. Чтобы запустить проект выше, вам нужно сначала запустить бэкэнд, а затем запустить фронтенд.Нужно запустить как минимум два инструмента командной строки.Две командные строки для одного проекта выглядят очень недружелюбно, хотя это было сделано раньше , О(∩_∩)О ха-ха~.

    Здесь мы используем одновременно, чтобы помочь нам выполнить две команды одновременно.

    yarn add concurrentlyИзмените код скриптов в package.json следующим образом:

    "scripts": {
      "react-start": "react-app-rewired start",
      "react-build": "react-app-rewired build",
      "start": "concurrently \"react-app-rewired start\" \"cd server && yarn start\"",
      "build": "concurrently \"react-app-rewired build\" \"cd server && yarn build\"",
      "test": "react-scripts test --env=jsdom",
      "eject": "react-scripts eject"
    },
    

    Затем выполните запуск пряжи, чтобы одновременно запустить интерфейс и сервер.

  • разрешать междоменные

    Первый прокси-атрибут приложения create-реагировать (рекомендуется)

    Просто добавьте в package.json следующий код, чтобы получить данные по доменам. Внешний интерфейс этого проекта — порт 3000, а серверный — порт 3003. Конфигурация выглядит следующим образом:

    "proxy": "http://127.0.0.1:3003"

    Сторона второго узла решает междоменные
    //allow custom header and CORS
      app.all('*',function (req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
        res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
      
        if (req.method == 'OPTIONS') {
          res.send(200); /让options请求快速返回/
        }
        else {
          next();
        }
      });
    

Наконец, следует отметить, что проект использует node+express в качестве бэкенда для предоставления сервисов API, поэтому бэкенд не имеет никаких страниц рендеринга, что принципиально отличается от использования node+express для построения системы блогов, поэтому мы не иметь бэкэнд-рендеринг Страница, то есть представление, поэтому все маршруты должны использовать res.json() в качестве возврата и не могут использовать res.render() в качестве возврата, иначе будет сообщено об ошибке Экспресс-ошибка: Нет по умолчанию был указан двигатель, и расширение не было предоставлено.

Четвертый шаг — подключение к базе данных

База данных использует MongoDB, поэтому mongoose необходимо установить на стороне узла.yarn add mongoose.

// 下面是关于mongoose的几个概念:
Schema: 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力
Model: 由Schema发布生成的模型,具有抽象属性和行为的数据库操作对象
Entity: 由Model创建的实体,它的操作也会影响数据库
Schema、Model、Entity的关系请牢记,Schema生成Model,Model创造Entity,Model和Entity都可对数据库操作造成影响,但Model比Entity更具操作性。 

Здесь особо нечего сказать об установке MongoDB, вы можете напрямую спросить Du Niang о различных конфигурациях после установки. После установки вы можете использовать различные инструменты визуализации для просмотра базы данных. Здесь я установил robo и создал базу данных luffy_blog,Да, я могу использовать эти леса для создания блога в будущем, потому что я обнаружил, что как новичок некоторые учебники действительно не очень дружелюбны..

Как показано выше, я установил MongoDB, создал новую базу данных luffy_blog и добавил новые пользовательские данные.Далее мы будем использовать экспресс для взаимодействия с MongoDB для получения данных и передачи их во внешний интерфейс:

  • Создайте новую папку db в каталоге сервера для обработки связанных с базой данных

    目录结构如下:
    - db
      - config // MongoDB的配置文件
      - models // 数据模型model
      - schemas // 模型骨架schema
    
  • Настроить MongoDB

    папка конфигурации

    • config.js
      // 数据库地址: 'mongodb://用户名:密码@ip地址:端口号/数据库';
      // 一般如果没设置用户名和密码直接写IP地址就可以,数据库你可以新建一个
      module.exports = {
        mongodb : 'mongodb://127.0.0.1:27017/luffy_blog'
      };
      
    • mongoose.js
      // 用于连接数据库并且定义Schema和Model
      const mongoose = require('mongoose');
      const config = require('./config');
      module.exports = ()=>{
          mongoose.connect(config.mongodb);//连接mongodb数据库
          // 实例化连接对象
          var db = mongoose.connection;
          db.on('error', console.error.bind(console, '连接错误:'));
          db.once('open', (callback) => {
              console.log('MongoDB连接成功!!');
          });
          return db;
      };
      
      Операция подключения к БД завершена выше, далее добавляем в server.js следующий код:
      // 连接mongodb数据库
      const mongoose = require('./db/config/mongoose');
      const db = mongoose();
      

    Следующим шагом будет создание скелета данных и модели, что можно сделать в полном соответствии с шаблоном мангуста Далее возьмем в качестве примера пользовательскую модель пользователя.

    папка схемы

    // UserSchema.js
      const mongoose = require('mongoose');
      const Schema = mongoose.Schema;
      //创建Schema
      const UserSchema = new Schema({
          name: String,
          password: String,
          email: String
      });
      module.exports = UserSchema;
    

    папка моделей

      // UserModel.js
      const mongoose = require('mongoose');
      const UserSchema = require('../schemas/UserSchema');
      //创建model,这个地方的user对应mongodb数据库中users的conllection。
      const User = mongoose.model('user',UserSchema);
      module.exports = User;
    

    Все готово, кроме возможности. База у нас есть, данные у нас есть, подключение экспресса к базе выполнено, дальше осталось только взять данные из базы и вернуть на фронтенд в виде API. Например: мы определяем интерфейс следующим образом:

    接口名称: /api/user/list
    后端路由:
      const express = require('express');
      const User = require('../db/models/UserModel');// 引入模型
      
      const router = express.Router();
      
      router.get('/list', (req, res) => {
        User.find({}, (err, data) => {
          if (err) 
            next(err);
          res.json(data);
        });
      });
    浏览器访问:
        浏览器可以登录http://localhost:3003/api/user/list访问数据
    前端页面获取:
      axios.get('/api/user/list')
            .then(res => {
              return res.data;
            })
            .catch((error) => {
              console.log(error);
            }); 
    

    Наконец, мы отображаем данные на странице, и эффект выглядит следующим образом:

Выше введение части базы данных завершено, и подробный код можно просмотреть в проекте клонирования.

Шаг 5: Добавьте избыточность для управления состоянием

Вот и точка, вот и точка, вот и точка (важные вещи говорятся три раза). Когда дело доходит до реактивных проектов, как он может не использовать избыточность для управления состоянием? Конечно, многие люди в текущей экосистеме могут сказать вам, используйте mobx, он достаточно прост для общих проектов, но я думаю, что раз он проще, то изучайте redux, а потом переходите на mobx, как вы думаете?

Чтобы объявить, это не для того, чтобы объяснить, что такое редукс, здесь нужно объяснить, как использовать редукс в проекте. Для получения подробных статей с пояснениями вы можете перейти к онлайн-учебникам. Если вы начинаете, я рекомендую Руан Дашен, редукс входная серия.Нажмите здесь, чтобы перейти.

Далее я буду предполагать, что вы уже понимаете, что делает redux, но не очень понятно, как внедрить его в проект, потому что статьи в интернете либо слишком глубоки, и вы сдадитесь, не прочитав первый абзац; иначе , это слишком мелко, и это всегда встречный экземпляр.Прочитав его, я не знаю, как управлять состоянием в проекте.Если это похоже на вышеизложенное, поздравляю, вы наконец-то встретили меня, и следующее определенно научит вас использовать redux.

  • Установите зависимости, связанные с избыточностью

    yarn add redux react-redux redux-logger redux-thunkКак мы все знаем (я буду считать, что вы знаете), редукс опирается на различное промежуточное программное обеспечение.Мы используем здесь только redux-logger и redux-thunk для простоты.Один из них предназначен для вывода журналов редукса, а другой - позволяет нам упростить асинхронные операции. Чтобы быть более конкретным, см. официальную документацию и различные учебные пособия.

  • Добавьте каталог redux в интерфейс

    // 目录结构,ok就是redux三剑客
    - redux
      - actions
      - reducers
      - store
      - middleware
    
  • Завершите различные базовые конфигурации

    Далее вам просто нужно выполнить следующие шаги, чтобы внедрить redux в свой проект.

    • Настроить магазин
    // configureStore.js
      import { createStore, applyMiddleware } from 'redux';
      import thunkMiddleware from 'redux-thunk';
      import { logger } from '../middleware';
      import rootReducer from '../reducers';
      
      const nextReducer = require('../reducers');
      
      function configure(initialState) {
        const create = window.devToolsExtension
          ? window.devToolsExtension()(createStore)
          : createStore;
      
        const createStoreWithMiddleware = applyMiddleware(
          thunkMiddleware,
          logger,
        )(create);
      
        const store = createStoreWithMiddleware(rootReducer, initialState);
      
        if (module.hot) {
          module.hot.accept('../reducers', () => {
            store.replaceReducer(nextReducer);
          });
        }
        return store;
      }
      export default configure;
    

    После настройки вышеуказанных файлов должна быть куча ошибок, не беда, давайте по крупицам. Промежуточное ПО регистратора и редуктор используются выше, и эти два настраиваются далее.

    • Настройка редуктора и промежуточного ПО
      /middleware/index.js
      // 你没有看错,中间件就是人家已经造好的轮子,我们直接拿来用就行。
      import logger from 'redux-logger';
      export {
        logger,
      };
      
      /reducers/index.js
      import { combineReducers } from 'redux';
      import user from './user/index'; // 一般会配置多个reducer然后使用combineReducers将他们合并起来
      const rootReducer = combineReducers({
        user
      });
      
      export default rootReducer;
      

Результат настройки логгера следующий:

  • настроить действие

    Говорят настроить действие.На самом деле действие не настраивается, а мы передаем состояние всего приложения в редукс для управления, поэтому если мы хотим обновить данные, то должны делать это через редукс, а редукс делает это для us Предоставленный способ обновления данных — действие отправки. Давайте возьмем пример получения данных списка пользователей и действительно используем избыточность.

    /actions/User.js
    import {
      FETCH_ALL_USER_LIST,
      FETCH_ALL_USER_LIST_SUCCESS,
      FETCH_ALL_USER_LIST_FAIL
    } from '../../constants/ActionTypes';
    import axios from 'axios';
    
    // 获取用户列表
    const getAllUserList = () => ({
      type: FETCH_ALL_USER_LIST,
    });
    const getAllUserListSuccess = (payload) => ({
      type: FETCH_ALL_USER_LIST_SUCCESS,
      payload
    });
    const getAllUserListFail = () => ({
      type: FETCH_ALL_USER_LIST_FAIL
    });
    export const fetchAllUserList = () => (dispatch) => {
      dispatch(getAllUserList());
      // 获取用户列表
      // 因为设置了proxy的缘故,所以不需要写http://localhost:3003
      // 会自动定向到后端服务器
      return axios.get('/api/user/list')
              .then(res => {
                return dispatch(getAllUserListSuccess(res.data));
              })
              .catch((error) => {
                console.log(error);
                dispatch(getAllUserListFail());
              }); 
    };
    
    

    Выше приведен полный процесс инициирования действия для получения данных, который обычно включает три этапа: запрос данных, успешный запрос данных и сбой запроса данных.

  • Разделите компоненты на компоненты-контейнеры и компоненты представления.

    Таким же образом, разница между ними состоит в том, чтобы увидеть объяснения больших коров. Они очень подробные. Я расскажу здесь только немного. Поскольку введен редукс, данные не должны быть получены через ajax в компонентеDidMount( ) на странице Вышеупомянутое, как уже упоминалось, запускается действием, поэтому компонент состояния требуется для передачи состояния и API обработки данных, требуемых страницей, компоненту отображения.

    // /containers/UserList.js容器组件
      import { connect } from 'react-redux';
      import UserList from '../components/UserList';
      import {
        fetchAllUserList
      } from '../redux/actions/User';
      
      const mapStateToProps = state => ({
        list: state.user.userList.list,
      });
      
      const mapDispatchToProps = dispatch => ({
        fetchAllUserList() {
          dispatch(fetchAllUserList());
        }
      });
      
      export default connect(
        mapStateToProps,
        mapDispatchToProps
      )(UserList);
      
    // /components/UserList.js 展示组件
      import React, { Component } from 'react';
      import { Table } from 'antd';
      
      class UserList extends Component {
        constructor(props) {
          super(props);
          this.state = { userList: this.props.list };
        }
      
        componentDidMount() {
          this.props.fetchAllUserList(); //获取数据渲染页面
        }
      
       ...
      }
      export default UserList;
    

напиши в конце

Я наконец-то закончил писать свою первую чисто техническую статью.Хоть она и не техническая, но я думаю, что она все же очень содержательная.По крайней мере, я думаю, что написанное мною должно быть понятно новичкам или студентам.Надеюсь, вы меня поправите! Наконец, ставьте код, если он вам нравится, не жалейте звездочки! Затем я могу использовать эти леса для обучения системе, основная цель - повысить способность.

GitHub: https://github.com/luffyZh/express-react-scaffold.gitАвтострада