Al igual que un autocargador junior verde, escribió su caliente . Parte 2. CSS


Antes del prefacio


En los comentarios a la primera parte, acertadamente noté una aclaración de la terminología. Por lo tanto, ahora llamaré a mi autocargador de proyectos (en adelante, AR). Mantendré el título de la primera parte del artículo antiguo para la historia.

Prólogo


2 semanas después de escribir este recargador más simple, pude configurar completamente webpack y webpack-dev-server, lo que en teoría debería conducir a una negativa total a usar mi "bicicleta".


Al configurar una nueva asamblea, traté de respaldar la posibilidad de una asamblea de trabajo antigua y garantizada en el caso de "pero nunca se sabe qué".
El ensamblaje anterior se caracteriza por la ausencia de importación / necesidad en el proyecto, que no son compatibles con los navegadores, y el hecho de que en la etapa de desarrollo todos los archivos .js están incluidos en index.html dentro del cuerpo. Estos son archivos de trabajo del proyecto y todas las bibliotecas.

Al mismo tiempo, como dije antes, todas las bibliotecas están en la ordenada lib daddy.
El archivo package.json era casi puro en términos de dependencias y devDependencies (así como scripts). Solo express, un paquete para proxy en un servidor y mi agregado socket.io y node-watch.

Por lo tanto, la tarea de preservar el antiguo ensamblaje era bastante simple, pero la tarea de establecer uno nuevo, por el contrario, se complicó por la búsqueda de los paquetes necesarios y sus versiones.
Como resultado, se logró el objetivo. Rellené package.json con los paquetes necesarios, creé entry.js y entry.html como archivos de entrada de webpack. En .js, coloco todas las importaciones de todo lo que alcancé para lo más necesario, y entry.html es solo una copia de index.html, en la que eliminé todos los scripts adicionales para conectar archivos.

Para estar seguro, en el complemento ProvidePlugin describí una biblioteca que webpack sustituirá en los lugares correctos "a pedido". Pero ahora entiendo que puedes prescindir de él. Intentaré eliminar, veamos qué sucede.
Por lo tanto, apoyó la falta de importaciones en el proyecto principal y mantuvo el index.htm original.

En teoría, se suponía que esto me permitía recolectar con el viejo recolector y, lo cual es muy importante para mí personalmente, apoyar el desarrollo usando mi autocargador.
En la práctica, encontré un lugar donde apareció la exportación. Una de nuestras mini-librerías auto-escritas fue hecha en forma de objeto.

Para que el ensamblaje de paquete web funcione correctamente, debe exportarse; para un ensamblaje antiguo normal, solo un script en index.html es suficiente. Bueno, nada, lo tomo y lo reescribo con el servicio del angular. Copiar + pasado + pequeños cambios y - voila - ¡funciona!

Intento con un nuevo ensamblaje: funciona, webpack-dev-server, respectivamente, también.
Pruebo mi autocargador, ¡funciona!
Zumbido!
El prefacio ha terminado, sigue adelante.

La trama


Después de trabajar un par de días en webpack-dev-server, no puedo dejar de pensar en cuánto tiempo es.
Y es muy rápido al mismo tiempo, reiniciando literalmente 3-4 segundos después de guardar.
Pero ya estoy acostumbrado a que mi AR, debido a la falta de ensamblaje, se reinicie justo después de ctrl + s.
Como resultado, al principio mantengo ambas herramientas ejecutándose, y luego trabajo solo a través de AR.

Por mi parte, identifiqué 4 razones por las cuales:
1. AR es más rápido.
2. Está más claro con él lo que se ha roto, porque Toda la pila de errores en los archivos de origen es visible. No se requiere un recorrido de paquete de wds.
3. Wds no se reinicia cuando cambio algo en el archivo html que se inserta mediante include en otro archivo html. Mi, debido al hecho de que lee toda la carpeta del proyecto y se recarga en cualquier cambio, excepto las excepciones, se vuelve a cargar. Aquí la optimización de wds se dispara en el pie.
4. Bueno, subjetivo. Necesito mirar la parte posterior a menudo desde diferentes servidores y, en consecuencia, ejecutar esto en el navegador en diferentes puertos. 1 script es suficiente para dar servicio a AR
“example”: “node ./server.js”

Que corre
npm run example 1.100 9001

o
npm run example 1.101 9002

Donde servidor 1.101 y puerto 9001 para mostrar en mi host local.
Conveniente en general, no necesita recordar diferentes nombres de scripts, solo escriba los parámetros al comienzo del script y todo estará bien.
Las variables entran en process.argv y las elimino con éxito dentro de server.js

En cuanto a los wds, hasta ahora no he podido implementar tal comodidad, tuve que hacer varios scripts para las principales combinaciones. Bueno, al menos una configuración se utiliza para el desarrollo.

En general, es más conveniente para mí trabajar con una bicicleta escrita.
Bueno, como es más conveniente, decidí desarrollar el proyecto.

Cuales son las opciones?
1. Al cambiar los archivos CSS, no vuelva a cargar la página, sino que realice los cambios.
2. Del mismo modo, pero ya .html
3. Pruebe otras opciones diferentes además de location.reload () para js.
4. Similar a 1-2, pero ya .js
5. Aléjese de index.html + entry.html hacia un solo archivo para ambos ensamblajes. es decir Llego a una situación en la que lo que va a funcionar con webpack funcionará en mi AR.
6. Apriete el soporte de scss

CSS es genial, lo que necesitas.
HTML también es divertido, justo lo que necesitas.
Location.reload (). No sé por qué es malo, pero sería interesante considerar las diversas opciones disponibles.
JS: es genial, sería genial hacerlo, pero ¿es realmente necesario, teniendo en cuenta cuánto esfuerzo gastaré?
5 y 6: esto ya huele a ensamblaje, lo que significa que es probable que la velocidad haya desaparecido.
Conclusión: los ítems 4 - 6 no están planeados, haré los ítems 1 - 2, ítem 3 Voy a echar un vistazo a lo que está en general. A primera vista, la tarea es difícil, ¡pero puedo dividirme en subtareas! Lo desgloso y decido ir por etapas, mientras que solo pienso en la etapa actual (sí, bueno, ya pienso en html con might y main)

La parte principal


CSS


La tarea consta de dos subtareas:
1.Encuentre el archivo modificado.
2. Vuelva a cargar CSS sin volver a cargar la página.

Empiezo con el segundo. Utilizando la experiencia previa, lo primero que voy a google es, pero ¿cómo puede generalmente recargar css sin una recarga de página?

Me encuentro con una serie de artículos, entre los cuales este me parece el más interesante.

Los chicos simplemente recorren todo el enlace de css a head y cambian el atributo href a uno nuevo.
Tomé su idea como base e implementé mi versión.
Todavía tengo 2 archivos.
server.js es un servidor simple + comprobación de excepciones + lógica de seguimiento de cambios + envío de cambios por socket.

watch.js: en el cliente. Recibe mensajes de cambio + location.reload () del servidor. Ahora en watch.js ha agregado la lógica de verificar el nombre en css y reemplazar css, si es necesario. Podría extraerse en un módulo separado, pero hasta ahora no veo mucho código. La primera iteración resultó así:

server.js
 const express = require('express'), http = require('http'), watch = require('node-watch'), proxy = require('http-proxy-middleware'), app = express(), server = http.createServer(app), io = require('socket.io').listen(server), exeptions = ['git', 'js_babeled', 'node_modules', 'build', 'hotreload'], // ,   ,    backPortObj = { /*  ,   back*/ }, address = process.argv[2] || /*    back*/, localHostPort = process.argv[3] || 9080, backMachinePort = backPortObj[address] || /*   back */, isHotReload = process.argv[4] || "y", // "n" || "y" target = `http://192.168.${address}:${backMachinePort}`, str = `Connected to machine: ${target}, hot reload: ${isHotReload === 'y' ? 'enabled' : 'disabled'}.`, link = `http://localhost:${localHostPort}/`; server.listen(localHostPort); app .use('/bg-portal', proxy({ target, changeOrigin: true, ws: true })) .use(express.static('.')); if (isHotReload === 'y') { watch('./', { recursive: true }, (evt, name) => { let include = false; exeptions.forEach(item => { if (`${name}`.includes(item)) include = true; }) if (!include) { console.log(name); io.emit('change', { evt, name, exeptions }); }; }); }; console.log(str); console.log(link); 



watch.js
 const socket = io.connect(); const makeCorrectName = name => name.replace('\\','\/'); const findCss = (replaced) => { const head = document.getElementsByTagName('head')[0]; const cssLink = [...head.getElementsByTagName('link')] .filter(link => { const href = link.getAttribute('href'); if(href === replaced) return link; }) return cssLink[0]; }; const replaceHref = (cssLink, replaced) => { cssLink.setAttribute('href', replaced); return true; }; const tryReloadCss = (name) => { const replaced = makeCorrectName(name); const cssLink = findCss(replaced); return cssLink ? replaceHref(cssLink, replaced) : false; }; socket.on('change', ({ evt, name, exeptions }) => { const isCss = tryReloadCss(name); if (!isCss) location.reload(); }); 



Curiosamente, el paquete de vigilancia de nodo me envía el nombre del archivo modificado en la ruta del formulario \ a \ file.css, mientras que en href la ruta se escribe ruta / a / file.css. porque Compruebo el archivo por su nombre completo; tuve que cambiar la barra hacia atrás para verificar.
Y funciona!

Sin embargo, quedan 3 problemas.
1.Option funciona bien para Chrome y definitivamente no funciona para edge. Aquí tienes que cavar, porque sin embargo, el diseño de varios navegadores es muy necesario (y es precisamente para el diseño que es una mejora), pero esto probablemente se deba a 2 problemas.

2. El navegador es inteligente: almacena en caché los archivos ya cargados, y si no se cambian los parámetros, no cambia nada. Es decir, en teoría, si guarda el archivo con el mismo nombre, el navegador considerará que nada ha cambiado y no volverá a cargar el contenido. Para luchar contra esto, los chicos cambian su nombre cada vez. Funciona para mí en Chrome sin esto, sin embargo, este es un matiz demasiado importante.

3. es necesaria una coincidencia inequívoca de un nombre. es decir si configura la ruta absoluta para vincular (comienza con ./), entonces el programa no encuentra una coincidencia.
./path/to/file! = ruta / a / archivo para comprender la lógica de mi código. Y esto también necesita ser reparado.

Por lo tanto, necesito actualizar el nombre del archivo cada vez para que no haya almacenamiento en caché.
Más precisamente, cada vez que cambia el atributo href del enlace, en el que el archivo css ha cambiado.
Lea más sobre esto aquí.

Los chicos en el enlace de arriba luchan con el almacenamiento en caché con mucha elegancia, tomo su opción:
 cssLink.setAttribute('href', `${hrefToReplace}?${new Date().getTime()}`); 

Luego necesito comparar el nombre del archivo. Obtuve 1 signo de interrogación por línea, por lo que puedo prescindir de expresiones regulares (aún no las he estudiado) a favor de un método tan auto escrito:
 const makeCorrectName = (name) => name .replace('\\', '/') .split('?')[0]; 


Funciona!

A continuación, necesito determinar de forma exclusiva la ruta al archivo.
No conozco muy bien la magia de los caminos absolutos, relativos y en general. Algunos malentendidos sobre el tema se deben precisamente a esto.
El camino a href puede comenzar con '.', '/' O inmediatamente con un nombre.
En mi tiempo libre pensé en este tema.
El punto de entrada - index.html (y en mi caso entry.html) - está siempre (generalmente) en el nivel superior. Y los archivos CSS conectados por scripts siempre están (generalmente) en algún lugar de las profundidades. Por lo tanto, repito, la ruta siempre será la misma (nombres de carpeta y archivo), solo el primer carácter será diferente.
Por lo tanto, después de separar la parte con un signo de interrogación, vuelvo a dividir la línea de la misma manera, pero ya en '/', luego elimino el primer punto esperado y conecto los elementos de la matriz en una línea, según lo cual compararé para una búsqueda exacta.
Se ve así:
 const findFullPathString = (path) => path .split('/') .filter((item) => item !== '.') .filter((item) => item) .join(''); 


Ejecuto el código, saludos, ¡funciona!

¿Qué hay de Edge?


Y con Edge, el problema no se escondía donde se buscaba.
Resultó que mi código CSS no funcionaba en Edge, y debido a mi descuido, simplemente no me di cuenta de esto.
El problema estaba oculto en el método de procesamiento de la colección de elementos DOM.
Como sabes, una colección de elementos DOM no es una matriz; en consecuencia, los métodos de matriz no funcionan con ella (más precisamente, algunos funcionan, otros no).
Solía ​​hacer esto:
 const cssLink = [...head.getElementsByTagName('link')] 

Pero el viejo Edge no entiende esto y esa fue la razón.
Siéntase libre de cambiar y ahora se hace así:
 const cssLink = Array.from(head.getElementsByTagName('link'))// special for IE 

¡Corre, comprueba, trabaja!
imagen
La imagen es pequeña, una pequeña explicación.
Izquierda Chrome, centro de Firefox, borde derecho. Específicamente ingreso un valor en la entrada para mostrar que la página no se recarga y css cambia casi instantáneamente.
El retraso en el video está relacionado con el retraso entre editar y guardar el archivo.

En términos de css, trabajar con chromeDevTools puede ser más rápido debido al hecho de que pueden, por ejemplo, cambiar el margen con la flecha arriba / abajo, pero mi css también se actualiza tan rápido y todo desde un solo editor.
Vale la pena señalar que, en el momento de la publicación del artículo, he estado usando mi bicicleta de forma continua sin modificaciones durante aproximadamente 2 semanas y no deseo cambiar a wds. ¡Como no hay deseo de css en situaciones simples de usar devTools!

Total, server.js permanece igual y watch.js toma la siguiente forma:
watch.js
 const socket = io.connect(); const findFullPathString = (path) => path .split('/') .filter((item) => item !== '.') .filter((item) => item) .join(''); const makeCorrectName = (name) => name .replace('\\', '/') .split('?')[0]; const findCss = (hrefToReplace) => { const head = document.getElementsByTagName('head')[0]; const replacedString = findFullPathString(hrefToReplace); const cssLink = Array.from(head.getElementsByTagName('link'))// special for IE .filter((link) => { const href = link.getAttribute('href').split('?')[0]; const hrefString = findFullPathString(href); if (hrefString === replacedString) return link; }); return cssLink[0]; }; const replaceHref = (cssLink, hrefToReplace) => { cssLink.setAttribute('href', `${hrefToReplace}?${new Date().getTime()}`); return true; }; const tryReloadCss = (name) => { const hrefToReplace = makeCorrectName(name); const cssLink = findCss(hrefToReplace); return cssLink ? replaceHref(cssLink, hrefToReplace) : false; }; socket.on('change', ({ name }) => { const isCss = tryReloadCss(name); if (!isCss) location.reload(); }); 



Belleza!

Epílogo.


El siguiente paso es intentar volver a cargar HTML, pero hasta ahora mi visión se ve muy complicada. La advertencia es que tengo angularjs y esto debería funcionar juntos.
Estaría muy contento por las críticas constructivas y sus comentarios sobre cómo mejorar mi pequeño proyecto, así como consejos y artículos sobre el tema con HTML.

Source: https://habr.com/ru/post/467771/


All Articles