<p>Introducción a dva</p>

Creado en 24 jun. 2016  ·  76Comentarios  ·  Fuente: dvajs/dva

No hay conceptos nuevos, todos son viejos.

¿Por qué dva?

Después de un período de autoaprendizaje o capacitación, todos deberían poder comprender el concepto de redux y reconocer que este control de flujo de datos puede hacer que la aplicación sea más controlable y la lógica más clara.

Pero luego suele surgir esa pregunta: hay demasiados conceptos, y el reductor, la saga y la acción están todos separados (subarchivos).

El problema con esto es:

  • El costo de edición es alto y necesitas alternar entre reductor, saga y acción.
  • No conviene organizar el modelo de negocio (o modelo de dominio). Por ejemplo, después de escribir una lista de usuarios, para escribir una lista de productos, necesitamos copiar muchos archivos.

Y algunos otros:

  • La escritura de la saga es demasiado complicada. Cada vez que escuchas una acción, debes pasar por el proceso de bifurcación -> observador -> trabajador
  • problema de escritura de entrada
  • ...

Y dva se utiliza para resolver estos problemas.

¿Qué es dva?

dva es un paquete ligero basado en la arquitectura de la aplicación existente (redux + react-router + redux-saga, etc.), sin introducir nuevos conceptos, y el código total es inferior a 100 líneas. (Inspirado en elm y choo.)

dva es un marco, no una biblioteca. Similar a emberjs, le dirá claramente cómo se debe escribir cada componente, lo que es más controlable para el equipo. Además, dva encapsula todas las demás dependencias, excepto react y react-dom, que son peerDependencies.

En la implementación de dva, intente no crear una nueva sintaxis, pero use la sintaxis de la propia biblioteca de dependencias, como la definición de enrutador o la sintaxis JSX de react-router (la configuración dinámica es una consideración de rendimiento, que será compatible luego).

El núcleo de esto es proporcionar el método app.model , que se usa para encapsular reducer, initialState, action y saga juntos, como:

app.model({
  namespace: 'products',
  state: {
    list: [],
    loading: false,
  },
  subscriptions: [
    function(dispatch) {
      dispatch({type: 'products/query'});
    },
  ],
  effects: {
    ['products/query']: function*() {
      yield call(delay(800));
      yield put({
        type: 'products/query/success',
        payload: ['ant-tool', 'roof'],
      });
    },
  },
  reducers: {
    ['products/query'](state) {
      return { ...state, loading: true, };
    },
    ['products/query/success'](state, { payload }) {
      return { ...state, loading: false, list: payload };
    },
  },
});

Antes de dva, normalmente creábamos sagas/products.js , reducers/products.js y actions/products.js y luego cambiábamos entre estos archivos.

Presente las claves de estos modelos: (asumiendo que ya está familiarizado con la arquitectura de aplicaciones de redux, redux-saga)

  • espacio de nombres: corresponde al valor clave del reductor cuando se combina con rootReducer
  • estado - corresponde al estado inicial del reductor
  • suscripción: un nuevo concepto de [email protected] , ejecutado después de dom ready, no hay explicación aquí, consulte: A Farewell to FRP
  • efectos - corresponde a saga y simplifica el uso
  • reductores

Cómo utilizar

Consulte los ejemplos:

Mapa vial

  • [x] devtool soporte de intercambio en caliente
  • [x] El enrutador admite configuración dinámica
  • [x] Los efectos deben ser compatibles con más modos de saga.
  • [ ] Effects considera extender el acceso a thunk, promise, observable y otras soluciones, el propósito básico es ser compatible con IE8
  • [ ] Es demasiado problemático pasar despacho entre componentes, considere el siguiente plan
  • [x] Solución de prueba unitaria
  • [x] Más ejemplos: todolist, usuarios en antd-init, productos populares

    Preguntas más frecuentes

¿Soporte de nivel de herramienta de desarrollo?

Además del reemplazo en caliente, que aún no se ha adaptado, otros como redux-devtool, css livereload, etc. son todos compatibles.

¿Ya está disponible para el entorno de compilación?

Poder.

¿Incluye todas las características de la arquitectura de aplicación redux + redux-saga anterior?

Si.

¿Compatibilidad con navegadores?

IE8 no lo admite porque se usa redux-saga. (Consideraremos admitir thunks, promesas, observables, etc. en la capa de efectos de manera extendida más adelante)

Comentario más útil

Ser hecho vivo por redux es simplemente evangelio. Es demasiado simple y elegante. ¡Gran elogio! ! !

por cierto, accidentalmente vi que un extranjero lo volvió a publicar en Twitter hoy, pensé que lo había escrito un extranjero, pero no esperaba que fuera un compañero de clase de Alipay, 👍

Todos 76 comentarios

Ser hecho vivo por redux es simplemente evangelio. Es demasiado simple y elegante. ¡Gran elogio! ! !

por cierto, accidentalmente vi que un extranjero lo volvió a publicar en Twitter hoy, pensé que lo había escrito un extranjero, pero no esperaba que fuera un compañero de clase de Alipay, 👍

Mirando hacia adelante a la expansión de los efectos

¿El entorno de producción de Alipay utiliza esta arquitectura?

@besteric dva acaba de salir y aún no se ha aplicado, pero la arquitectura de la aplicación detrás de él se ha utilizado durante un tiempo.

¿Se puede escribir reductor así:

const reducer = (state, { type, payload }) => {
  switch (type) {
    case 'products/query':
      return { ...state, loading: true, };
    case 'products/query/success':
      return { ...state, loading: false, list: payload };
    default
      return state;
  }
}

app.model({
  reducer
})

Esto hace posible aplicar algunos métodos de orden superior al reductor.

Alabanza, escribí algunas demostraciones y solo hay un problema, el modelo solo se puede usar
app.model(Model1); app.model(Model2);
Es este método para completar la combinación, de hecho, creo que el ideal es
app.model([Model1,Model2])
algún tipo de

Es demasiado problemático pasar el envío entre componentes, considere la siguiente solución

¿No usas bindActionCreators ?

¿El escenario específico del uso avanzado del reductor @yesmeck es solo redo/undo ? No quiero que dva sea demasiado flexible, y consideraré agregarlo a través de un complemento en el futuro.

Usamos mucho en nuestro proyecto. Por ejemplo, extraeremos las partes similares de múltiples reductores en un método de alto nivel para modificar el reductor original, y existen métodos de alto nivel que permiten que el reductor restablezca el estado cuando la ruta cambios. , y esto https://github.com/erikras/multireducer

@Tinker404 Siento que sería más claro declarar el modelo por separado, y sería más fácil agregarlo y eliminarlo. yo escribiria esto:

app.model(require('../models/a'));
app.model(require('../models/b'));

@JimmyLv personalmente prefiere no usar actionCreator, sino solo dispatch .

@yesmeck ok, lo pensaré de nuevo.

También hay métodos de orden superior que permiten que el reductor restablezca el estado cuando cambia la ruta

Siento que este escenario es más apropiado al suscribirse a los cambios de enrutamiento en subscriptions y luego restablecer el estado a través de la acción. ¿O hay alguna ventaja en usar el método potenciador reductor?

Siento que este escenario es más apropiado al suscribirse a los cambios de enrutamiento en las suscripciones y luego restablecer el estado a través de la acción.

En este caso, cada reductor que necesita ser reiniciado necesita escribir la lógica de reinicio.Si usamos un método de alto nivel, solo necesitamos hacer esto ahora:

combineReducers({
  products: composeReducers({  // composeReducers 的实现见下面
    recycle(LOCATION_CHANGE, initialState),  // recycle 用来在路由变化时重置状态
    products
  })
})

Otro escenario es la misma lógica que estoy hablando de extraer diferentes reductores. Por ejemplo, hay una lista de productos y una lista de usuarios, y sus reductores son así:

// reducers/products.js
const reducer = (state, { type, action}) => {
  switch (type) {
    case 'products/FETCH_SUCCESS':
      return {
        ...state,
        loading: false,
        list: payload
      }
    default:
      return state
  }
}
// reducers/users.js
const reducer = (state, { type, payload}) => {
  switch (type) {
    case 'users/FETCH_SUCCESS':
      return {
        ...state,
        loading: false,
        list: payload
      }
    default:
      return state
  }
}

Aquí los dos reductores son casi iguales, así que lo extraemos y escribimos un reductor de lista:

const list = (actionType) => {
  return (state, { type, payload }) => {
    switch (type) {
      case actionType:
        return {
          ...state,
          loading: false,
          list: payload
        }
        break;
      default:
        return state
    }
  }
}

Luego implementamos un composeReducers para combinar estos 3 reductores:

function composeReducers(...reducers) {
  return (state, action) => {
    if (reducers.length === 0) {
      return state
    }

    const last = reducers[reducers.length - 1]
    const rest = reducers.slice(0, -1)

    return rest.reduceRight((enhanced, reducer) => reducer(enhanced, action), last(state, action))
  }
}

De esta forma, el reductor para la lista de productos y la lista de usuarios se convierte en esto:

// reducers/products.js
const reducer = (state, { type, payload}) => {
  // 其他逻辑
}

export default composeReducer(reducer, list('products/FETCH_SUCCESS'))
// reducers/users.js
const reducer = (state, { type, payload}) => {
  // 其他逻辑
}

export default composeReducer(reducer, list('users/FETCH_SUCCESS'))

list es solo un ejemplo, de hecho, hay muchos reductores en el proyecto que tienen la misma lógica.

@yesmeck 👍, el papel de potenciador reductor ha sido subestimado antes.

@sorrycc ¿puedes decir por qué? Llamado explícitamente con dispatch comparación?

@Tinker404 Siento que sería más claro declarar el modelo por separado, y sería más fácil agregarlo y eliminarlo. yo escribiria esto:
app.modelo(requerir('../modelos/a'));
app.modelo(requerir('../modelos/b'));

También sugiero un método que pueda pasar varios modelos a la vez. Los proyectos grandes pueden tener muchos modelos. Ahora los requiero (importar) todos, y luego modelo cada modelo uno por uno, lo cual no es muy conveniente. Mi forma actual de escritura es:

// models是个文件夹,有很多model
import models from './models';

models.forEach((m)=>{
    app.model(m);
});

// models.js
const context = require.context('./', false, /\.js$/);
const keys = context.keys().filter(item => item !== './index.js');
const models = [];
for (let i = 0; i < keys.length; i++) {
  models.push(context(keys[i]));
}
export default models;

Es muy D.VA.

Encontré el uso del componente de formulario antd en el panel de usuario. Recuerdo que no se puede usar para el componente puro. ¿Es posible ahora?

@codering No recuerdo que haya restricciones, los problemas con antd se pueden consultar en https://github.com/ant-design/ant-design/issues .

Hola, quiero usar su dva. Actualmente, uso la estructura de directorios generada por el andamiaje React Webpack Redux. Cambié el código con referencia al ejemplo del panel de usuario en su ejemplo, pero no hay nada después del inicio. ¿Pueden ayudarme? averigüe dónde está Algo salió mal, la dirección de mi proyecto: https://github.com/baiyulong/lenovo_parts

@baiyulong, ¿por qué no hacerlo directamente en función de la estructura de directorios del panel de usuario?

@sorrycc Estoy usando la estructura de directorios del panel de usuario ahora. ¿Hay algún tratamiento o escritura especial para el enrutamiento dva?
export default function({ history }) {
return (
<Router history={history}>
<IndexRoute component={HomePage} />
<Route path='/' component={HomePage}>
<Route path='/create' component={CreateOrder} />
</Route>
</Router>
)
}
Esta ruta que escribí, HomePage puede, escribí un enlace <Link to='/create'>Create</Link> , no puedo ir al componente CreateOrder después de hacer clic en él

No hay una forma especial de escribir la ruta de @baiyulong dva , intente:

  1. hay algun error
  2. Intente acceder a /create ruta directamente

@nikogu muchas gracias, estaré bien después de anidar

Hola, ¿dva puede admitir la carga en caliente de modelos?

@kkkf1190 está considerando esto y lo apoyará.

👍

Solo quería decir gracias. . .

Siempre he pensado que el andamiaje de vue-cli de vuejs es muy bueno, después de leer esto mi pensamiento ha cambiado por completo.

Marco muy maravilloso! He estado investigando por un tiempo. @sorrycc Quiero hacerle dos preguntas a Yunda:

  1. ¿Se puede usar dva perfectamente en proyectos nativos de reacción?
  2. ¿Puede dva+reactjs admitir bien la representación del lado del servidor?

@miembrolibre007

  1. Admite react-native, ejemplo de referencia: https://github.com/sorrycc/dva-example-react-native
  2. No hay problema en la operación del servidor en teoría.Tanto redux como react-router detrás de él admiten SSR, pero llevará algún tiempo aplicarlo a dva, porque la lógica relevante debe ser enderezada y empaquetada bien.

@sorrycc ¿Existe ahora una solución para el soporte de reductor de orden superior? Nuestro proyecto utiliza muchos reductores de alto orden debido a la reutilización.

Compatible con @ancheel , puede ser global o local, caso de uso de referencia: https://github.com/dvajs/dva/blob/master/test/reducers-test.js

Después de modificar el estado del modelo, cómo modificarlo nuevamente, este problema siempre ocurre ahora
antd.js:32924 Advertencia: setState(...): No se puede actualizar durante una transición de estado existente (como dentro render o el constructor de otro componente). Los métodos de representación deben ser una función pura de accesorios y estado; constructor los efectos secundarios son un antipatrón, pero se pueden mover a componentWillMount .

Muy emocionante, trate de usarlo en el entorno de producción, espero seguir optimizando y mejorando

nerfear esto!

Buen trabajo。¡Gracias!

@sorrycc ¡ Estoy ansioso por admitir la representación del lado del servidor!

Con el respaldo de @mountainmoon , consulte https://github.com/sorrycc/dva-boilerplate-isomorphic .

Vino una ola de ruedas :+1:

Hola, me acabo de poner en contacto con el aprendizaje de este dva. Después de leer el código durante unos días, tengo algunas preguntas en mi corazón. Me gustaría preguntar:
Vi que sus demostraciones son todas aplicaciones de una sola página, pero todas son aplicaciones de varias páginas en desarrollo. Me gustaría preguntar, si el enrutamiento no se usa en el desarrollo de aplicaciones de varias páginas, cómo cargar componentes en su lugar, tal vez sí. preguntando a un idiota Es un poco confuso, porque no uso enrutamiento, por lo que el oyente configurado en los modelos no tiene claro dónde activar:
historia.escuchar( ubicación => {
if(ubicación.nombreruta === '/usuarios') {
envío({
tipo: 'consulta Exitosa',
carga útil:{}
})
}
})
PD: al cargar datos en el método querySuccess y usar export default connect(mapStateToProps)(Users); también se informa un error:
connect.js: 41 TypeError no detectado: no se puede llamar a una clase como una función
Me siento como un idiota en un instante, no sé si te puedo molestar para que me lo expliques, ¡gracias!

¿Por qué dva? en inglés por favor

No me gusta mucho esta forma de escribir.

@codering mencionó el uso de componentes de formulario antd en el panel de usuario.Recuerdo que no se puede usar para componentes puros.¿Es posible ahora?
También lo he encontrado más.Si es un componente de función pura, la función getFieldDecorator no se puede obtener a través de props.form.getFieldDecorator.Si usa extensiones para crear un componente, puede obtenerlo.
No sé si Dios tiene una solución @sorrycc

¿Puedes lanzar la misma página en inglés? No podemos entender esto, y por qué necesitamos dva.

Hola, si es un proyecto grande, su estado será muy grande y será muy engorroso de procesar. ¿Debe dividirse en varios modelos?

@ yazhou-zyz tengo el mismo problema que tú:
Advertencia: setState(...): No se puede actualizar durante una transición de estado existente (como dentro del renderizado o el constructor de otro componente). Los métodos de renderizado deben ser una función pura de accesorios y estado; los efectos secundarios del constructor son un antipatrón, pero se puede mover a componenteWillMount.
queria preguntarte como lo solucionaste

Aprender

sigue estudiando

dva es de gran valor de referencia para proyectos de construcción.

buen trabajo~

Donde puedo encontrar los docs en ingles??? Traducir el tema con motores de traducción es problemático y la comprensión no es suficiente. Con el inglés, ustedes pueden llegar al mundo. ¡Sigan con el buen trabajo! :cohete:

dva no se prueba en las versiones React-native 0.47.X y React16.0.0

@vecold siempre ha podido usarlo, diciendo que el código de invitación o el mensaje de error no se pueden usar

¿Hay alguna posibilidad de que podamos obtener una traducción al inglés de los documentos?
¡Gracias!

En el código comercial, este ejemplo es común. Una actualización de estado local puede afectar a todo el cuerpo. Muchos lugares que no necesitan volver a renderizarse también se vuelven a renderizar, lo que reduce en gran medida el rendimiento de la página. ¿Se puede agregar esta función para analizar automáticamente el estado del que depende redux connect para reducir los cálculos mapStateToProps innecesarios y volver a renderizar?

muy bien
pero construye toda la página cuando espero construir una sola página

_traducción no oficial_

¿Por qué Dva?

Redux es bueno. Pero hay demasiados conceptos, reductores separados, sagas y acciones (divididas en diferentes archivos)

  1. Debe cambiar entre reductores, sabios y acciones con frecuencia.
  2. Inconveniencia para organizar modelos de negocio (o modelos de dominio). Para exp., cuando ya tenemos user_list y se requiere product_list, entonces debe duplicar una copia de archivo
  3. La saga es difícil de escribir. Debes hacer un tenedor -> observador -> trabajador para cada acción.
  4. La entrada es tediosa y complicada.

¿Qué es Dva?

Es un contenedor ligero sobre el marco existente (redux + react-router + redux-saga ...). No hay ningún nuevo concepto involucrado. < 100 líneas de código. (Inspirado en olmo y choo.)

Es un marco, no una biblioteca. Al igual que Ember.js, restringe la forma en que escribe cada parte. Es más controlable para el trabajo en equipo. Dva encapsula todas las dependencias excepto react y react-dom como peerDependencies

Su implementación introduce lo menos posible nuevas sintaxis. Reutiliza las dependencias. Para exp., la definición del enrutador es exactamente igual que el JSX de react-router.

La funcionalidad principal es app.model . Encapsula reducer, initialState, action, saga por completo.

app.model({
  namespace: 'products',
  state: {
    list: [],
    loading: false,
  },
  subscriptions: [
    function(dispatch) {
      dispatch({type: 'products/query'});
    },
  ],
  effects: {
    ['products/query']: function*() {
      yield call(delay(800));
      yield put({
        type: 'products/query/success',
        payload: ['ant-tool', 'roof'],
      });
    },
  },
  reducers: {
    ['products/query'](state) {
      return { ...state, loading: true, };
    },
    ['products/query/success'](state, { payload }) {
      return { ...state, loading: false, list: payload };
    },
  },
});

Solíamos crear sagas/products.js, reducers/products.js actions/products.js y cambiar entre ellos.

punto clave:

  • espacio de nombres: el key del reducer en su objeto rootReducer
  • estado: initialState de reducer
  • suscripción: el nuevo concepto de [email protected] , ejecutado cuando dom está listo: Adiós a FRP
  • efectos: salvia más fácil
  • reductores

Cómo utilizar

Ver ejemplos

Mapa vial

  • devtool recarga en caliente
  • Configuración dinámica para enrutador
  • Effects admite más modelos saga
  • Prueba de unidad
  • Más ejemplos: todolist, usuarios en antd-init, productos populares

Preguntas más frecuentes

¿Soporta herramientas de desarrollo?

Compatible con redux-devtool, css livereload. Necesita más trabajo para la recarga en caliente

Bueno para prod env?

Por supuesto

¿Incluyendo todas las funcionalidades de redux + redux-saga?

¿Compatibilidad de navegadores?

Sin IE8 debido a redux-saga. (Más tarde puede aplicar thunk, promise, observable como extensiones en la capa de efectos)

por favor similar

['products/query']: function*() {}
['products/query'](state) {}

¿Cuál es la sintaxis? ¿Se pueden usar arreglos como nombres de funciones?

@clemTheDasher El nombre de la función se puede calcular como clave ( NO matriz) en JavaScript. Referencia más detallada a las definiciones de Método |

var obj = {
  property( parameters… ) {},
  *generator( parameters… ) {},
  async property( parameters… ) {},
  async* generator( parameters… ) {},

  // with computed keys:
  [property]( parameters… ) {},
  *[generator]( parameters… ) {},
  async [property]( parameters… ) {},

  // compare getter/setter syntax:
  get property() {},
  set property(value) {}
};

Informes de recién llegados, ven aquí y continúa trabajando duro para aprender el conocimiento de front-end

@clemTheDasher Esa es una propiedad calculada.

¡Aprender!

admirar a dios

Gracias a Dios, gracias por el código abierto

¿No se me permite aprender de ustedes?

Aprendí, gracias por tener un marco tan conveniente para que lo usemos

Los enlaces de demostración en github han caducado.

@sorrycc, ¿dva admite ahora la representación del lado del servidor?

¿Se puede escribir reductor así:

const reducer = (state, { type, payload }) => {
  switch (type) {
    case 'products/query':
      return { ...state, loading: true, };
    case 'products/query/success':
      return { ...state, loading: false, list: payload };
    default
      return state;
  }
}

app.model({
  reducer
})

Esto hace posible aplicar algunos métodos de orden superior al reductor.

El estilo de escritura de Redux es conciso, y solo se requiere una línea para modificar el estado, pero parece que varias líneas de código se escriben juntas a través del azúcar sintáctico. Pero todavía necesito usar ...state para entregar el estado restante a la siguiente parada, de lo contrario, el estado estará incompleto. En otras palabras, durante la fase de reducción, se puede perder algún estado si se escribe incorrectamente.

De alguna manera, la idea de Vuex es más fácil de leer y más natural. Escribe algo como esto (no exactamente).

const mutation = {
  ['products/query'](state) {
    state.loading = true
  },
  ['products/query/success'](state, payload) {
    state.loading = false
    state.list = payload
  }
}

En cuanto al código, solo me importa qué estado modifico (sincrónicamente). Vuex también debe envolver una capa en el exterior para la entrega estatal en la próxima parada. Posiblemente, también se realizan algunos controles defensivos (adivinanzas) antes de la entrega, o se plantan anzuelos.

Pregúntame si la página de ejemplo del sitio web oficial de dva no puede aparecer y reportar un error, ¿es una actualización?

por favor similar

['products/query']: function*() {}
['products/query'](state) {}

¿Cuál es la sintaxis? ¿Se pueden usar arreglos como nombres de funciones?

ES6 permite literales para definir objetos, (expresión) como el nombre de la propiedad del objeto, es decir, poner la expresión entre corchetes.
Tal como

obj = {
  ['xxname']: 'what ever you defined',
  ['xxyy'](args) {
    ....
  }
}

Hay una pregunta, 'productos/consulta' se usa para procesar la llamada de los reductores, y se asocia con el espacio de nombres a través de una cadena. Más tarde, si el proyecto se vuelve más grande, como cientos de métodos. Si mi espacio de nombres tiene que ser cambiado. ¿Para cambiar cien métodos?

@yesmeck 👍, el papel de potenciador reductor ha sido subestimado antes.

No sé si hay apoyo aquí?

¿Fue útil esta página
0 / 5 - 0 calificaciones