Presentando Vuecket





Versión rusa

Vuecket es un marco web que integra VueJS en el lado del cliente y Apache Wicket en el lado del servidor. Aprovecha lo mejor de ambos y hace que el desarrollo de aplicaciones de pila completa sea aún más rápido y sencillo. Por supuesto, todas estas son palabras importantes, porque Vuecket tiene en este momento (agosto de 2020) menos de un mes y aún no ha pasado por el bautismo de servidores de producción de "fuego y sangre". Pero ya ha incluido todo lo mejor que hemos desarrollado durante el desarrollo de nuestro producto clave de código abierto Orienteer (una plataforma para el desarrollo rápido de aplicaciones comerciales). Y es precisamente por su corta edad que Vuecket necesita tu ayuda: comparte lo que te gustó, lo que no es muy bueno, dónde se necesitan mejoras, etc.



Los principios básicos que nos guían a la hora de construir Vuecket son:



  1. Ser declarativo no es imperativo. Vuecket no impone ningún requisito de código especial. Se puede aplicar con bastante rapidez y facilidad a proyectos existentes de Vue.JS o Apache Wicket.
  2. Siga el principio de Pareto. Vuecket debe proporcionar el 80% de la funcionalidad que desea de fábrica, pero debe haber puntos de extensión buenos y convenientes para el 20% restante.


Es fácil ver que estos principios también se aplican a Vue.JS y Apache Wicket.



Entonces, ¿cómo vamos a empezar exactamente con Vuecket? Sugiero hacer un tablero de chat / invitado con el soporte de Markdown. No voy a atormentar demasiado: la aplicación terminada está aquí y el código está aquí .



Creamos un proyecto



Generemos nuestro proyecto a través de `mvn archetype: generate`. Para hacer esto, puede usar, por ejemplo, el siguiente comando:



mvn archetype:generate -DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-quickstart -DarchetypeVersion=8.9.0 -DgroupId=com.mycompany -DartifactId=mychat -DarchetypeRepository=https://repository.apache.org/ -DinteractiveMode=false


Vuecket aún no tiene su propia plantilla de proyecto Maven. Quizás en el futuro agreguemos esto también. Ahora conectemos Vuecket. Agregue la siguiente dependencia al proyecto `pom.xml`:



<dependency>
	<groupId>org.orienteer.vuecket</groupId>
	<artifactId>vuecket</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>


Salida de texto en Markdown



El proyecto Wicket ya contiene una página de bienvenida de Wicket por defecto. Agreguemos algo de código para asegurarnos de que Vuecket ya esté funcionando. Por ejemplo, vamos a mostrar Hello World, pero en Markdown, de modo que el texto en sí se establezca en el lado del servidor en el componente Apache Wicket. Usaremos la biblioteca vue-markdown para renderizar Markdown .



En HomePage.html, en lugar del saludo de Wicket, agregue:



<div wicket:id="app">
	<vue-markdown wicket:id="markdown">This will be replaced</vue-markdown>
</div>


Y en HomePage.java el siguiente código:



public HomePage(final PageParameters parameters) {
	super(parameters);
	add(new VueComponent<String>("app")
			.add(new VueMarkdown("markdown", "# Hello World from Vuecket")));
}


Pero, ¿dónde está la clase VueMarkdown? Y lo definiremos de la siguiente manera:



@VueNpm(packageName = "vue-markdown", path = "dist/vue-markdown.js", enablement = "Vue.use(VueMarkdown)")
public class VueMarkdown extends Label {
	public VueMarkdown(String id) {
		super(id);
	}
	public VueMarkdown(String id, Serializable label) {
		super(id, label);
	}
}


Preste atención a la anotación @VueNpm. Es necesario habilitar Vuecket en este componente de Wicket, que cargará todo lo necesario de NPM para ayudar al navegador a representar correctamente el componente de Vue para Markdown.



Si hizo todo correctamente, después de iniciar el proyecto a través de `mvn jetty: run` debería ver algo como esto en http: // localhost: 8080




Entonces, ¿qué pasó aquí y por qué funciona?



  • Marcamos la página agregando 2 componentes de Vue: para la aplicación y para la salida de Markdown
  • Hemos incluido componentes de Vue con componentes de Wicket en el lado del servidor (en HomePage.java)
  • Le dijimos a Vuecket qué biblioteca Vue.JS necesita para renderizar 'vue-markdown'
  • Y luego todo es simple: Wicket, al representar la página en el navegador, usó la línea "# Hello World from Vuecket", que establecimos al agregar el componente Wicket, y Vuecket ayudó al navegador a cargar las bibliotecas VueJS necesarias, lanzar la aplicación VueJS y renderizar el saludo ya como Markdown renderizado.


Github se compromete a ayudar



Ingresar un mensaje y previsualizarlo



En este paso complicaremos nuestra aplicación: haremos el mensaje de entrada y vista previa.

Agreguemos un área de texto a HomePage.html para ingresar un mensaje, además de vincular este campo y vue-markdown a la variable "texto" de VueJS.



<div wicket:id="app">
	<textarea v-model="text" style="width:100%" rows="5"></textarea>
	<vue-markdown wicket:id="markdown" :source="text">Will be replaced</vue-markdown>
</div>


Ya estamos usando la variable "texto", pero ahora necesitamos agregarla al componente de datos Vue. Hay varias formas de hacerlo en Vuecket, pero vayamos por la más larga:



  • Cree su propio VueComponent para la aplicación Vue
  • Vamos a asociarlo con nuestro archivo * .vue
  • Escribamos la lógica en el archivo * .vue: por ahora, solo el campo "texto"


Estos son algunos de los cambios que haremos:



//HomePage.java:
public HomePage(final PageParameters parameters) {
	super(parameters);
	add(new ChatApp("app")
			.add(new VueMarkdown("markdown")));
}
//ChatApp.java:
@VueFile("ChatApp.vue")
public class ChatApp extends VueComponent<Void> {
	public ChatApp(String id) {
		super(id);
	}
}


Bueno, ChatApp.vue en sí:



<script>
module.exports = {
    data: function() {
        return {
            text : ""
        }
    }
}
</script>


Como resultado, cuando inicia `mvn jetty: run` e ingresa algún texto, puede ver lo siguiente




En este capítulo, hemos aprendido cómo: crear archivos * .vue familiares y asociarlos con componentes de Apache Wicket.



GitHub se compromete a ayudar.



Mostrar una lista de mensajes y agregar uno nuevo



No habrá nada específico de Vuecket o Wicket en este capítulo: brillo puro de VueJS.

Si descomponemos la tarea, tendremos que hacer lo siguiente:



  • Agregue un cuadro de lista a nuestra aplicación Vue para guardar mensajes
  • Agregar un método para agregar un nuevo mensaje a la lista
  • Muestre una lista de mensajes y no se olvide de las rebajas


Primero, cambiemos nuestro ChatApp.vue y agreguemos la lógica necesaria: un nuevo campo `messages` con una lista de mensajes y el método ʻaddMessage` para agregar un nuevo mensaje. Y no olvidemos que al agregar un mensaje a la lista, es una buena idea borrar el campo de entrada original. Para los mensajes, almacenaremos no solo el texto, sino también la fecha de adición / envío. En el futuro, será posible expandir con campos adicionales, por ejemplo, quién envió este mensaje, prioridad, resaltado requerido, etc.



<script>
module.exports = {
    data: function() {
        return {
            text : "",
            messages: []
        }
    },
    methods: {
    	addMessage : function() {
    		this.messages.push({
    			message: this.text,
    			date: new Date()
    		});
    		this.text = "";
    	}
    }
}
</script>


También cambiaremos HomePage.html, agregaremos una pantalla de la lista de mensajes y agregaremos una llamada a nuestro método addMessage al presionar Ctrl-Enter.



<div wicket:id="app">
	<div v-for="(m, index) in messages">
		<h5>{{ index }}: {{ m.date }}</h5>
		<vue-markdown :source="m.message"></vue-markdown>
	</div>
	<textarea v-model="text" 
			  style="width:100%" 
			  rows="5" 
			  @keyup.ctrl.enter="addMessage"
			  placeholder="Enter message and Ctrl-Enter to add the message">
	 </textarea>
	<vue-markdown wicket:id="markdown" :source="text">Will be replaced</vue-markdown>
</div>


Cuando ejecute `mvn jetty: run` e ingrese algunos mensajes, verá algo como esto




En este capítulo, solo hemos enseñado a la aplicación que usa VueJS para agregar un mensaje a la lista y mostrar este último.



GitHub se compromete a ayudar



Activa la colaboración



Si antes de eso el contenido de nuestro libro de visitas era único para cada visitante de la página, entonces en este capítulo habilitaremos la comunicación con el servidor y permitiremos la sincronización con todos los visitantes. Para ello, necesitamos Vuecket Data Fibers, una solución que permite sincronizar los objetos del lado del navegador con los del lado del servidor. ¡Y lo más interesante es que no necesitamos hacer nada por esto del lado del cliente! ¿Suena bien? ¡Vamos a codificar! Aunque ... Solo hay dos líneas nuevas en nuestro componente ChatApp.java:



private static final IModel<List<JsonNode>> MESSAGES = Model.ofList(new ArrayList<JsonNode>());

public ChatApp(String id) {
	super(id);
	addDataFiber("messages", MESSAGES, true, true, true);
}


Que pasó aquí:



  • Hemos creado un modelo de MENSAJES que está al alcance de todos, ya que fue creado como final estático.
  • Se agregó fibra de datos, que une el objeto de mensajes en el lado del cliente y el objeto dentro del modelo MESSAGES en el lado del servidor.
  • , data-fiber 3 : load, observe, refresh. Load — , Observe — , Refresh — .


-




GitHub commit





En el capítulo anterior, hice un poco de trampa al otorgar acceso de lectura / escritura a la colección de mensajes a todos los visitantes del sitio a la vez. Se desaconseja mucho hacer esto, porque entonces, a través de la fibra de datos, cualquier remitente puede sobrescribir todos los mensajes en el servidor con algo propio o incluso borrarlos. Las fibras de datos solo deben usarse para vincular objetos personalizados del lado del navegador con objetos de datos del lado del servidor que pertenecen al mismo usuario. ¡Esto significa que no hay modelos o datos estáticos!



¿Cómo podemos arreglar la situación? Para esto tenemos que:



  • Deshágase de la fibra de datos, que funciona en todos los ámbitos, y úsela solo para cargar inicialmente la lista de mensajes.
  • Utilice el método del lado del servidor para agregar un nuevo mensaje a la lista.


Por lo tanto, todos solo pueden agregar nuevos mensajes a la lista general, pero no pueden eliminar ni cambiar los existentes. Compliquemos también un poco el lado del servidor: guardaremos no solo el JSON recibido del cliente, sino una clase de mensaje especializada con los campos, métodos, etc. para trabajar con datos en el lado del servidor.



Comencemos con una clase de mensaje para almacenar mensajes de usuario. Por cierto, esta podría ser alguna clase JPA que le permita guardar datos en una base de datos.



public class Message implements IClusterable {
	@JsonProperty("message")
	private String text;
	private Date date;
	
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}	
}


Preste atención a @JsonProperty. Por lo tanto, hemos redirigido el campo JSON "mensaje" a nuestro campo Java "texto".



A continuación, cambiemos ChatApp.java para hacer lo que se describe anteriormente: agregue un método vuecket para guardar el mensaje. También en el código, puede notar el recorte de la lista de mensajes a solo 20 (los usuarios de Habr son muy diligentes), pero cuando elimina un mensaje, aún se guarda para siempre en los registros del servidor.



@VueFile("ChatApp.vue")
public class ChatApp extends VueComponent<Void> {
	
	private static final Logger LOG = LoggerFactory.getLogger(ChatApp.class);
	private static final int MAX_SIZE = 20;
	private static final IModel<List<Message>> MESSAGES = Model.ofList(new ArrayList<Message>());

	public ChatApp(String id) {
		super(id);
		addDataFiber("messages", MESSAGES, true, false, false);
	}
	
	@VueMethod
	public synchronized void addMessage(Context ctx, Message message) {
		List<Message> list = MESSAGES.getObject();
		list.add(message);
		trimToSize(list, MAX_SIZE);
		IVuecketMethod.pushDataPatch(ctx, "messages", list);
	}
	
	private void trimToSize(List<Message> list, int size) {
		//It's OK to delete one by one because in most of cases we will delete just one record
		while(list.size()>size) LOG.info("Bay-bay message: {}", list.remove(0));
	}
}


¿Ves el método de anotación @VueMethod? En él recibimos un nuevo mensaje, lo guardamos en la lista, lo cortamos y enviamos la lista ya actualizada al cliente. También tenga en cuenta que la fibra de datos se ha reconfigurado para solicitar datos solo cuando se inicia la aplicación Vue.



También necesitamos cambiar la lógica en ChatApp.vue para que en lugar del campo local "mensajes", envíe un nuevo mensaje al servidor en modo asíncrono (vcInvoke)



module.exports = {
    data: function() {
        return {
            text : "",
            messages: []
        }
    },
    methods: {
    	addMessage : function() {
    		this.vcInvoke("addMessage", {
    			message: this.text,
    			date: new Date()
    		});
    		this.text = "";
    	}
    }
}


Lo que aprendimos del capítulo:



  • Cómo crear métodos del lado del servidor para Vuecket
  • Cómo llamar a métodos en el servidor desde un navegador
  • Cómo enviar los cambios deseados al cliente


Para un visitante respetuoso de la ley, nada ha cambiado, pero un pirata informático ya no puede cambiar tan fácilmente la lista general de mensajes en el servidor.




GitHub se compromete a ayudar



Conclusión



Sobre esto de hoy, permítanme terminar. Hay muchas más mejoras que se pueden hacer en nuestro código, pero se las dejo a ustedes, queridos lectores. Si necesitas ayuda, escribe, ¡te ayudaremos!



¿Te gusta el marco? Por favor comparta su opinión. Después de todo, es su opinión que el código abierto vive y se desarrolla.



Spoiler de las próximas mejoras de Vuecket
  • WebSocket' .
  • , .
  • data-fiber' .
  • Vuecket/Wicket , VueJS , , Markdown





All Articles