React строит фоновое управление (первый взгляд на реакцию)

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

предисловие

Раньше я использовал vue для разработки, поэтому решил перейти на React через год, так что я некоторое время возился с React после года, и я вижу результаты.

Я собирался написать что-то вродеVue имитирует сегодняшние заголовкиЯ начинал с такого проекта, а потом подумал, что может быть лучше написать фоновый менеджмент. Так и начал возиться.

Используйте модули экологической цепочки, связанные с реакцией:

  • react
  • react-dom
  • react-router-dom: После react-router4 вроде использует эту штуку
  • react-transition-group: для анимации
  • redux: используется для управления глобальным состоянием
  • react-redux: используется для управления глобальным состоянием
  • redux-actions: Используется для создания действий и не пишет switch/case или if/else при создании связанных редьюсеров, в основном для удобства.
  • redux-thunk: reduxпромежуточное ПО для обработки наших асинхронных действий
  • antd: более часто используемая библиотека реактивного пользовательского интерфейса, которую я нашел случайно.

Основные из них, связанные с реакцией, это Что касается конфигурации webpack, то она в принципе мало чем отличается от предыдущей конфигурации vue.

Объяснение каталога файлов:

图片描述

  • build: используется для размещения конфигурации о веб-пакете
  • config: конфигурация проекта
  • src: исходный код
  • static: статический ресурс
  • .babelrc: конфигурация бабеля
  • postcss.config.js: конфигурация css

Я не буду говорить о других каталогах, а в основном представлю одинsrcструктура каталогов под

图片描述

  • actions: Поместите место, связанное с действием, в редукцию.
  • reducers: Поместите место, связанное с редуктором, в редукцию.
  • assets: статические ресурсы проекта
  • components: часто используемые общедоступные компоненты.
  • router: конфигурация, связанная с маршрутизацией
  • store: редукционная конфигурация
  • styles: общедоступный файл стиля
  • utils: Инкапсуляция классов инструментов
  • view: Основная структура всех страниц
  • main.js: файл входа в проект
  • config.js: конфигурация публичного свойства

1. Несколько способов написания реакции

  • React.createClass
import React from 'react'
const MyComponent = React.createClass({
   render () {
       return (
           <h2>我是React.createClass生成的组件</h2>
       )
   }
})
  1. React.createClass будет самостоятельно связывать методы функций (в отличие от React.Component, который связывает только те функции, о которых нужно заботиться), вызывая ненужные накладные расходы на производительность и увеличивая вероятность устаревшего кода.
  2. Миксины React.createClass не являются естественными и интуитивно понятными;

  • React.Component
import React from 'react'
class MyComponent from React.Component {
    render () {
        return (
            <h2>我是React.Component生成的组件</h2>
        )
    }
}
  1. Необходимо вручную привязать этот указатель
  2. Форма React.Component очень подходит для компонентов более высокого порядка (компоненты более высокого порядка — HOC), она показывает более мощные функции, чем примеси, в более интуитивно понятной форме, а HOC — это чистый JavaScript, не беспокойтесь о том, что их забросят.

  • Функциональные компоненты без состояния
import React from 'react'
 const MyComponent = (props) => (
     <h2>我是无状态函数式组件</h2>
 )
 ReactDOM.render(<MyComponent name="Sebastian" />, mountNode)
  1. Создание компонентов без состояния делает код более читаемым, сокращает много избыточного кода и сводит его только к одному методу рендеринга, что значительно повышает удобство написания компонента.
  2. Компоненты не создаются, а общая производительность рендеринга повышается.
  3. Компонент не может получить доступ к этому объекту
  4. Компонент не имеет доступа к методам жизненного цикла
  5. Компоненты без состояния могут получить доступ только к входным реквизитам, те же реквизиты получат тот же результат рендеринга без побочных эффектов.

2. Перехват маршрута

Долго перехватывал роутинг, хотел найти хук-функцию, похожую на beforeRouter из vue, но не нашел.

а затем найтиhistoryмодуль, я обнаружил что у этой штуки есть метод для мониторинга маршрута.Я использовал его в начале, но когда я вдруг врубился в режим хеширования для разработки, я обнаружил, что черезhistory.push(path, [state])Возникла проблема при установке свойства состояния.Вроде эта штука может устанавливать свойство состояния только для режима истории, но некоторые мои вещи заходят с установкой свойства состояния, поэтому я отказался от этого метода и искал новый метод.

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

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

class MainComponents extends React.Component {
    componentWillMount () { // 第一次进来触发
        this.dataInit(this.props)
    }
    componentWillReceiveProps(nextProps){ // 以后每次变化props都会触发
        // 如果死循环了 可能是某个属性设置会更新props上属性,所以导致一直循环,这个时候记得做好判断
        this.dataInit(nextProps)
    }
    render () {
        // 404
        if (!isExistPath(allRoutes, pathname)) return <Redirect to='/error/404'/>
        
        //当前路径路由信息
        let currRoute = getRoute(allRoutes, pathname)

        // 非白名单验证
        if (!whiteList.some(path => path === pathname)) {

            // 登录验证
            if (!Cookie.get('Auth_Token')) {
                return <Redirect to={{ pathname: '/login' }} />
            }
            
            // 获取用户信息
            if (!user) {
                this.getUserInfo(() => {
                    this.setRoutesByRole(this.props.user.roles)
                })
            }
        }
        // 401
        if (user && currRoute) {
            if (!isAuth(currRoute.role, user)) return <Redirect to='/error/401'/>
        }

        // 网页title
        document.title = currRoute.name
    }
}

3. Централизованные настройки маршрутизации

Любой, кто использовал Vue, знает, что мы обычно передаемnew Router({routes})для централизованного управления таблицей маршрутизации. Но реагирующий маршрутизатор, похоже, не может установить его таким образом. Последняя версия, кажется, даже не работает с вложением. Поэтому я начал просто создавать централизованную версию. Но позже я увидел плагин, который кажется управляемым.react-router-config, но я еще не пробовал, и я не знаю, возможно ли это.

// 路由表
const allRoutes = [
  {
    path: '/auth',
    login: true,
    layout: true,
    icon: 'user',
    name: '权限管理',
    role: ['admin'],
    component: _import_views('Auth')
  },
  {
    path: '/error',
    login: true,
    layout: true,
    icon: 'user',
    name: 'ErrorPage',
    redirect: '/error/404',
    children: [
        { path: '/error/404', component: _import_views('Error/NotFound'), name: '404'},
        { path: '/error/401', component: _import_views('Error/NotAuth'), name: '401'}
    ]
  }
  ...
]


// 根目录
<BrowserRouter>
    <Route path="/" component={MainComponents}/>
</BrowserRouter>

// MainComponents
class MainComponents extends React.Component {
  render () {
    return (
      <Switch>
          {renderRouteComponent(allRoutes.filter(route => !route.layout))} //不需要侧边栏等公共部分的路由页面
          <Route path="/" component={ComponentByLayout}/>
      </Switch>
    )
  }
}

// ComponentByLayout
const ComponentByLayout = ({history}) => (
  <Layout history={history}>
      <Switch>
          {renderRouteComponent(allRoutes.filter(route => route.layout))}
      </Switch>
  </Layout>   
)


// 路由渲染
const RouteComponent = route => <Route key={route.path} exact={route.exact || false} path={route.path} component={route.component} /> 
const renderRouteComponent = routes => routes.map((route, index) => {
    return route.children ? route.children.map(route => RouteComponent(route)) : RouteComponent(route)
})

4. Динамически генерировать маршруты на основе разрешений пользователя

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

{
  path: '/auth',
  login: true,
  layout: true,
  icon: 'user',
  name: '权限管理',
  role: ['admin'],
  component: _import_views('Auth')
}

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

Таким образом, таблица маршрутизации и боковая панель, соответствующие этому пользователю, отфильтровываются (боковая панель создается на основе таблицы маршрутизации).

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

Но на тот момент маршрутизация была настроена.vueпредоставляется внутриrouter.addRoutesЭтот метод предназначен для динамической установки маршрутизации,reactНичего про этот апи я в нём не нашёл, поэтому прошёл все роуты, чтобы его прописать, но это вызвало проблему.

к/authНапример, я сам не имею доступа/authразрешения, поэтому моя боковая панель не генерирует/authэтот список вариантов. Но мы получаем доступ к нему в адресной строке/authНа эту страницу можно зайти (лучше вообще не генерировать этот маршрут). Так вот с этой настройкой на самом деле проблема.В данный момент я не знаю как динамически генерировать маршруты.Пока это только в根目录Выполнить обработку разрешений

5. Загрузка по требованию

Есть также много методов загрузки по требованию.В настоящее время я пробовал только первый, потому что я также использовал импорт для реализации загрузки по требованию, когда писал Vue, поэтому я не стал заморачиваться.

1. метод импорта

//asyncComponent.js
import React from 'react'
export default loadComponent => (
    class AsyncComponent extends React.Component {
        state = {
            Component: null,
        }
        async componentDidMount() {
            if (this.state.Component !== null) return

            try {
                const {default: Component} = await loadComponent()
                this.setState({ Component })
            }catch (err) {
                console.error('Cannot load component in <AsyncComponent />');
                throw err
            }
        }

        render() {
            const { Component } = this.state
            return (Component) ? <Component {...this.props} /> : null
        }
    }
)


// index.js
import asyncComponent from './asyncComponent.js'
const _import_ = file => asyncComponent(() => import(file))
_import_('components/Home/index.js')

Принцип прост:

  • import() принимает соответствующий модуль и возвращает объект Promise
  • asyncComponent получает функцию, и эта функция возвращает объект обещания.
  • В функции хука componentDidMount выполните полученный метод loadComponent через async/await, получите результат, возвращенный импортом, и назначьте его для state.Component,
  • Поскольку то, что мы импортируем, является компонентом React, то, что мы получаем, также является компонентом React, а затем нам нужно только отобразить компонент.

2. Связка компонент + импорт (аналогично первому ощущению)

3. react-loadable

4. bundle-loader

6. request

То, что я использую здесь, этоaxios, использоватьaxiosсделал простой перехватчик

import axios from 'axios'
import qs from 'qs'


axios.defaults.withCredentials = true 

// 发送时
axios.interceptors.request.use(config => {
    // 发起请求,可以进行动画啥的
    return config
}, err => {
    return Promise.reject(err)
})

// 响应时
axios.interceptors.response.use(response => response, err => Promise.resolve(err.response))

// 检查状态码
function checkStatus(res) { 
    // 得到返回结果,结束动画啥的
    if (res.status === 200 || res.status === 304) {
        return res.data
    }
    return {
        code: 0,
        msg: res.data.msg || res.statusText,
        data: res.statusText
    }
    return res
}


// 检查CODE值
function checkCode(res) {
    if (res.code === 0) {
        throw new Error(res.msg)
    }
    
    return res
}

export default {
    get(url, params) {
        if (!url) return
        return axios({
            method: 'get',
            url: url,
            params,
            timeout: 30000
        }).then(checkStatus).then(checkCode)
    },
    post(url, data) {
        if (!url) return
        return axios({
            method: 'post',
            url: url,
            data: qs.stringify(data),
            timeout: 30000
        }).then(checkStatus).then(checkCode)
    }
}

7. redux

В основном используется здесьredux-actionsсоздать действие,родное письмо

// action
const addTodo = text => ({
    type: 'ADD_TODO',
    payload: {
      text,
      completed: false
    }
})

// reducer
const todos = (state = [], action) => {
    switch(action.type) {
        case 'ADD_TODO':
            return [...state, action.payload]
        ...
        default:
            return state
    }
}

использовалredux-actionsнаписание

import { createAction, handleActions } from 'redux-actions'

// action
const addTodo = createAction('ADD_TODO')

// reducer
const todos = handleActions({
    ADD_TODO: (state, action) => {
        return [...state, action.payload]
    }
    ...
}, [])

// использоватьredux-actionsпросто и понятно

8. connect

С редуксом эта штука в принципе незаменима,connectВ основном используется для подключения组件а такжеredux store, чтобы позволить компоненту получить данные в хранилище избыточностиа также方法 connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])

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

  • mapStateToProps(state, ownProps): Получить данные указанного состояния в хранилище, а затем передать их указанному компоненту, ownProps Реквизит самого компонента
  • mapDispatchToProps: Это чтобы получить метод действия в хранилище, а затем передать указанный компонент

Применение

import toggleTodo from 'actions/todo'
const mapStateToProps = state => ({
    active: state.active
})
const mapDispatchToProps = {
    onTodoClick: toggleTodo
}
connect(mapStateToProps, mapDispatchToProps)(Component)
// 在Component组件中, 便能在 props 里面获取到 active 数据, 跟 onTodoClick 这个方法了

connectВ основном используется во многих местах Так еще и упаковано

// connect.js
import actions from 'src/actions' // 所有action
import {connect} from 'react-redux' 
import {bindActionCreators} from 'redux'
export default connect(
    state => ({state}), // 偷懒了, 每次把state里面所有的数据都返回了
    dispatch => bindActionCreators(actions, dispatch) //合并所有action,并且传入dispatch, 那样我们在组件里面调用action,就不在需要dispatch了
)

bindActionCreators

Затем мы кладемconnect.jsфайл черезwebpackАтрибут псевдонима для настройки

//配置别名映射
alias: {
    'src': resolve('src'),
    'connect': resolve('src/utils/connect')
}

Затем мы можем обратиться к файлу следующим образом

import React from 'react'
import connect from 'connect'

@connect // 通过装饰器调用
class Component extends React.Component {
  componentWillMount () {
    const {state, onTodoClick} = this.props
    console.log(state, onTodoClick)
  }
}

Чтобы избежать проблем, я поставилstoreВсе данные внутри иactionвсе вернулись.

9. cssModules

существуетvueОбычно мы устанавливаем тег стиля, устанавливаяscopedсвойства, чтобы сделать css модульным Но когдаreact, Я использовалcssModulesсделать модульность css

  1. пройти черезwebpackнастраиватьcss-loaderизmodulesчтобы открыть модуляризацию css
{
    loader: 'css-loader',
    options: {
      modules: true, //是否开启
      localIdentName: '[name]__[local]___[hash:base64:5]'  // 转化出来的class名字结构
    }
},
  1. Внедрите css и добавьте className через назначение объекта
import styles from './styles.css'

export default () => (
  <div className={styles.a}></div>
)

//styles.css
.a {
    color: #ff4747;
}

или может пройтиreact-css-modulesдля более удобного управленияclassимя класса

import styles from './styles.css'
import CSSModules from 'react-css-modules'

class Component extends React.Component {
  render () {
    return (
      <div styleName='a b'></div>
    )
  }
}
export default CSSModules(Component, styles, {
    allowMultiple: true //允许多个class一起使用
})


//styles.css
.a {
    color: #ff4747;
}
.b {
  background: #f00;
}

Таким образом, мы можем передать строкуclassимя класса. Примечание: не используется, когда мы добавилиclassName, но использоватьstyleNameохватывать

10. Реализация двухсторонней привязки

class Bingding extends React.Component {
  state = {
    value: ''
  }
  handleInput = value => {
    this.setState({
      value
    })
  }
  render () {
    return (
      <input type="text" value={this.state.value} onChange={e => {this.handleInput(e.target.value)}}/>
      <div>{this.state.value}</div>
    )
  }
}

черезonChangeсобытие для запускаthis.setStateПовторный рендеринг метода рендеринга

Еще немного знаний включать动画,生命周期так далее Не так много, чтобы представить. Эти проекты в основном более или менее вовлечены. Проблем, возникающих при разработке, довольно много, самая главная – этоreact-routerПроблема настройки, как настроить не очень. В то же время я надеюсь, что кто-то порекомендует несколько исчерпывающих, особенно последнюю версию.reactПроект с открытым исходным кодом.

Шаги запуска проекта

  1. npm/yarn run dll (DllPlugin запакован, достаточно одного пакета)
  2. npm/yarn run dev (режим разработки)
  3. npm/yarn run build (рабочий режим)

резюме

Два сравнительно популярных в Китае фреймворка практически не соприкасаются друг с другом.vueЯ использовал его все время,reactЭто новый контакт спустя год. С моей нынешней точки зрения,vueСравниватьreactЭто действительно намного удобнее в разработке (можно использовать больше). потому чтоvueМногие часто используемые встроены. а такжеreactПо сути, вы должны найти соответствующий модуль самостоятельно. Он предоставляет только сам пользовательский интерфейс, а остальные в основном самодостаточны. Основная причина в том, что вы часто находите несколько модулей, но не знаете, какой из них использовать, и вам приходится тестировать воду один за другим. Конечно,reactСообщество сильное, так что это не большая проблема.

Адрес онлайн-просмотра

адрес блога

github