Three.js: Continúe con el soporte para las bibliotecas JS junto con las bibliotecas ES6 JSM

Creado en 5 oct. 2020  ·  51Comentarios  ·  Fuente: mrdoob/three.js

¿Tu solicitud de función está relacionada con un problema?

Creo que es una buena higiene de la biblioteca admitir importaciones de archivos estáticos clásicos y de módulos. Esto mantiene la biblioteca accesible para un grupo más amplio de desarrolladores y les permite usar su estilo preferido.

Personalmente, realmente trato de evitar el uso de módulos en general, me gustan los proyectos que son archivos estáticos con archivos JS simples clásicos incluidos. Tal vez solo soy un bicho raro, pero realmente odio hasta qué punto los marcos te abstraen innecesariamente de las cosas y cuánto reinventan las ruedas o te encierran en una caja negra. Sé que puede usar módulos sin ningún marco, sin embargo, los módulos incluidos son menos intuitivos que los tradicionales JS, a menudo me han fallado al intentar usarlos dentro de una configuración de archivo estático.

El uso de módulos ES6 no es ideal para todas las implementaciones, aunque sin duda son una adición bienvenida. Enseño a muchos programadores nuevos threejs porque me encanta la biblioteca y, en mi opinión, es una forma excelente y satisfactoria de iniciarse en la programación. Es mucho más fácil enseñar a las personas CSS/JS/HTML básico sin tener que empujar todo el nodo/npm + framework al mismo tiempo. Las bibliotecas estáticas son más fáciles de usar/comprender y mantienen baja la barrera de entrada aquí.

Estilísticamente, también prefiero sobrecargar TRES con funcionalidad adicional en lugar de agregar nuevas funciones con nombre que flotan libremente. Aunque esto es obviamente una preferencia.

Describa la solución que le gustaría

Tal vez pueda responder mejor a esto después de obtener un poco más de información sobre por qué se tomó la decisión de hacer la transición solo a los módulos, pero lo intentaré.

La documentación aborda que los módulos ES6 pueden no funcionar en todas las situaciones y para esas situaciones sugiere usar un paquete como browserify/rollup/webpack/parcel, etc....

Mi solución sería hacer que un script empaquetador ES6 automático pasara por los módulos en /examples/jsm para generar /examples/js versiones que no sean módulos. De esta forma, los desarrolladores ya no tendrán que preocuparse por realizar cambios en dos lugares y podrán seguir disfrutando del uso de las versiones sin módulos de JS y el estilo de importación de var global, si lo desean.

Esta generación automática de archivos JS que no son módulos podría realizarse como parte del proceso de compilación o ser un comando en el paquete.json que alguien podría ejecutar manualmente. Aunque yo optaría por la generación automática.

Crear esta automatización o mantener las versiones de JS que no son módulos de esta biblioteca es algo a lo que puedo dedicar mi tiempo. Si el razonamiento detrás de saltar a ES6 solo no es simplemente eliminar la necesidad de actualizar dos versiones paralelas de la misma cosa manualmente, me encantaría discutir otras soluciones para abordar esas preocupaciones también.

Describa las alternativas que ha considerado

La otra consideración obvia sería dejar las cosas como están y continuar manteniendo las versiones JS y JSM de todas las bibliotecas. Aunque teniendo en cuenta el anuncio, estos están siendo obsoletos, me parece algo poco probable. Pero estaría feliz de asumir la responsabilidad de asegurarme de que las bibliotecas JS se mantengan actualizadas con sus contrapartes JSM manualmente si decidimos seguir esta ruta.

Contexto adicional

Mucho amor para esta biblioteca y todos los que contribuyen, ya sea en código o informando/discutiendo problemas.

Suggestion

Comentario más útil

Gracias a todos por compartir los pros y los contras. Siempre es bueno compartir esto para asegurarnos de que estamos tomando decisiones informadas.

Esto es algo en lo que he estado gastando algunos ciclos cerebrales este año, e incluso les pregunté a los proveedores de navegadores sobre sus prioridades para poder planificar con anticipación.

Estoy de acuerdo en que los módulos ES6 son el futuro, pero desarrollar con ellos sin importar mapas puede causar grandes dolores de cabeza y romper por completo su flujo. Cuando decidimos desaprobar examples/js , esperaba que los mapas de importación tuvieran más tracción, pero parece que actualmente no es una prioridad para los navegadores.

Debido a esto, decidí suspender la obsolescencia de la carpeta examples/js hasta que los navegadores implementen mapas de importación. Odiaría obligar a los novatos a aprender sobre polyfills o bundlers para renderizar su primer cubo.

Llegué a la misma conclusión que @ Bug-Reaper. Hoy estoy echando un vistazo a la creación de un script que construye examples/js a partir de examples/jsm archivos.

Todos 51 comentarios

Es mucho más fácil enseñar a las personas CSS/JS/HTML básico sin tener que empujar todo el nodo/npm + framework al mismo tiempo. Las bibliotecas estáticas mantienen baja la barrera de entrada aquí.

Solo para aclarar los módulos js de ejemplo, tal como se mantienen en este proyecto, no requieren nodo, npm ni ningún marco de compilación para usar. Se pueden usar como archivos servidos estáticamente al igual que las antiguas importaciones globales. Solo requieren la sintaxis de importación es6 para usar, pero eso funcionará en todos los navegadores modernos.

Solo para aclarar los módulos js de ejemplo, tal como se mantienen en este proyecto, no requieren nodo, npm ni ningún marco de compilación para usar. Se pueden usar como archivos servidos estáticamente al igual que las antiguas importaciones globales. Solo requieren la sintaxis de importación es6 para usar, pero eso funcionará en todos los navegadores modernos.

¡Gracias por la aclaración! ¡Ese sí que es un buen punto!
Yo creo:

<script type="module">

  import { OrbitControls } from 'https://unpkg.com/three@<VERSION>/examples/jsm/controls/OrbitControls.js';

  const controls = new OrbitControls();

</script>
````
is perhaps less intuitive and harder to understand for newcomers than: 

espera un segundo... todavía tenemos:

<script src="path/to/local/build/three.js"></script>

como opuesto a:

<script type=module src="path/to/local/build/three.module.js"></script>

El primero es un script estático que se puede usar según la antigua forma global dentro del html de alguien... ¿verdad? ¿Qué era lo que ya no podías hacer después de la transición a ES6?

Si entiendo correctamente, el plan es incluir un "/build/tres.js" además de "/build/tres.module.js".

Si. Sin embargo, es cuestionable si este enfoque tiene sentido. Cuando se elimina examples/js , solo quedan unos pocos casos de uso en los que three.js y three.min.js siguen siendo útiles.

Sería realmente beneficioso eliminar three.js y three.min.js ya que nos permitiría cambiar el punto de entrada main npm del paquete npm, consulte #19575.

Si podemos hacerlo fácilmente, creo que tiene sentido continuar admitiendo /examples/js generándolos automáticamente a través de un script de empaquetado de ES6 como parte del proceso de compilación.

La idea es mover examples/jsm a características más modernas del lenguaje JavaScript, como clases. Dado que examples/js aún debería funcionar con un navegador anterior, sería necesario configurar una nueva compilación (ejemplos) con funciones de transpilación de código. Además, aún mantendríamos la base del código duplicado ( examples/js vs examples/jsm ) que, desde mi punto de vista, es un mal enfoque. Hace que el mantenimiento sea más complicado.

Creo que el usuario debe encargarse de la conversión de ES6 a ES5 si es necesario. Lo mismo para la minificación de código u otras tareas relacionadas con la compilación.

Creo que tienes razón. Si entiendo correctamente, el plan es incluir un " /build/three.js " además de " /build/three.module.js ".

Cierto

El problema con los archivos de la carpeta /examples es que necesita usar archivos de /examples/js cuando usó /build/three.js y archivos de /examples/jsm cuando usó /build/three.module.js , también conocido como mantener la consistencia en el método de carga.

¿Por qué? Porque cuando se usan las importaciones de módulos, el objeto principal THREE ya no es un objeto js simple THREE = {} sino un objeto de módulo de navegador interno que está sellado (no extensible), por lo tanto, archivos de /examples/js que intenta escribir una nueva propiedad en el objeto THREE falla.

Así que no puedes mezclar import * as THREE from '/build/three.module.js y THREE.WhateverExample = function() ...

Una forma posible es cambiar el nombre de la librería importada a cualquier otra cosa que no sea THREE y volver a crear un objeto global js simple THREE para que se escriban ejemplos en él...

Este suele ser el problema de

JS tradicional incluye

eso contamina la nomenclatura del espacio global, y debido a que no puede modificar los nombres en el archivo cargado, puede obtener errores como ese ...
Por otro lado, con los módulos, el usuario obtiene el poder de nombrar durante la importación y ya no es el autor de la librería quien elige el nombre resultante...

ex:

<script>
// a script you can't modify already use the name THREE
var THREE = document.getElementById('div-nb-3')
</script>

<script type="module">
import * as foo from '/build/three.module.js'

THREE.appendChild( new foo.WebGLRenderer().domElement )
</script>

@ Mugen87 Tienes 100% de razón. Si nos deshacemos de /examples/js , también podríamos deshacernos de three.js y three.min.js , ya que son esencialmente incompatibles con cualquiera de los módulos adicionales. Su caso de uso sería un nicho y esto casi garantiza que creará confusión.

@devingfx Tiene razón en que los módulos tienen ventajas y eliminan posibles conflictos de nombres globales. En años de uso, nunca he tenido ningún conflicto con la variable global TRES y creo que este es un escenario poco probable, pero su punto es técnicamente correcto.

lo cual es, desde mi punto de vista, un mal enfoque. Hace que el mantenimiento sea más complicado.

Creo que el usuario debe encargarse de la conversión de ES6 a ES5 si es necesario. Lo mismo para la minificación de código u otras tareas relacionadas con la compilación.

@ Mugen87 ¿Es realmente tan terrible mantener una inclusión js tradicional que usa una var global además de un módulo? Muchas bibliotecas son compatibles con ambos y, por lo que puedo decir, la versión tradicional de JS a menudo se usa tan popularmente como las contrapartes de la versión del módulo. Ambos tienen ventajas/desventajas y algo de eso se reduce a la preferencia. ¿No es bueno darles a los desarrolladores la opción de usar una biblioteca en un contexto sin módulos?

Estoy dispuesto a encargarme de crear/probar las funciones de transpilación de código necesarias para generar automáticamente three.min.js, three.js y /examples/js a partir de three.module.js y /examples/jsm . Una vez perfeccionado el flujo de trabajo de la transpilación, es posible que requiera un mantenimiento mínimo, ¡pero != mantiene dos versiones paralelas. En su mayor parte, el código solo sería necesario para actualizarse en los archivos del módulo y solo ocasionalmente necesitaría arreglar algún error de transpilación.

Tengo suficientes proyectos que se basan en la sintaxis global tradicional e incluye que, de todos modos, haré el trabajo de automatizar la transpilación de los módulos. Creo que, como mínimo, podríamos incluir un comando en el paquete.json y llamarlo "compilación heredada" que transpila los módulos en tres.min.js, tres.js y /examples/js que se comportan de manera similar al original. archivos ahora. Estos archivos ni siquiera tienen que confirmarse en el repositorio o crearse de forma predeterminada. También podríamos advertir que son para soporte heredado, no se garantiza que funcionen, sugerimos usar módulos en su lugar, etc.

De manera realista, creo que tiene más sentido mantenerlos en el repositorio y simplemente hacer que se generen automáticamente a través de la transpilación en la compilación.

un comando en el paquete.json y llámelo "legacy-build" que transpila los módulos

parece razonable. ¿No se fusionó Babel recientemente? así que creo que esto podría ser factible como es

editar: para aclarar, que nadie ejecute dicho nuevo comando, excepto los usuarios que desean dicha compilación

¿Es realmente tan terrible mantener una inclusión js tradicional que usa una var global además de un módulo?

Creo que se está subestimando la complejidad de mantener esto. Desafortunadamente, no creo que sea tan simple con la forma en que se configuran los ejemplos en el proyecto.

Veamos GLTFLoader como ejemplo. En este momento, todo GLTFLoader está contenido en un solo archivo, lo que facilita su inclusión en la parte superior de un archivo HTML. Uno de los beneficios de los módulos es que algunos de los archivos más grandes se pueden dividir en archivos separados que GLTFLoader puede importar como dependencias. ¿Cómo debería ser el script global creado una vez que GLTLoader dependa de cuatro archivos externos, algunos de los cuales son compartidos? ¿Los usuarios de los scripts globales creados ahora tendrán que incluir todos esos archivos js de ejemplo individualmente? ¿O se agruparán algunos archivos que requerirán mantener manualmente una lista de archivos que se pueden agrupar y cuáles no?

Creo que el único caso realmente configurado y olvidado es agrupar todos los archivos js de ejemplo en un solo blob monolítico que no creo que sea razonable. Creo que con cualquiera de estas soluciones también habría otros gastos generales de publicación y documentación.

Tal vez haya una mejor manera de hacerlo, pero cuando traté de hacer una compilación acumulada que retuviera la compatibilidad con versiones anteriores o al menos una estructura consistente con los archivos js existentes, estos son los problemas con los que me encontré.

Si entiendo correctamente, el plan es incluir un "/build/tres.js" además de "/build/tres.module.js".

Si. Sin embargo, es cuestionable si este enfoque tiene sentido.
Cuando se elimina ejemplos/js, solo quedan unos pocos casos de uso en los que tres.js y tres.min.js siguen siendo útiles.

@mugen87 @mrdoob

Miguel,
De hecho, mantener "tres.min.js" durante al menos 2 años más es IMPRESCINDIBLE.
No porque todas mis muestras se basen en él.
¡Pero porque muchos miles de archivos y los mejores perros de Google se basan en él!
Ejemplo: https://www.google.com/search?source=hp&q=webgl+benchmark

Por otro lado, desde mi punto de vista, "tres.min.js" significa un desarrollo y pruebas más rápidos.
Sin mencionar que funciona sin conexión y no necesita localhost.
Simplemente coloque todos los archivos en una carpeta en algún lugar, use Firefox y haga doble clic en el archivo HTML.
¡Siempre me encantó eso para el desarrollo!

Ricardo también debería pensar en todo esto.
salud

La eliminación de three.js y three.min.js es algo que se puede discutir y planificar cuando se haya ido examples/js . Era importante para mí resaltar su pérdida de importancia cuando ya no se pueden importar archivos desde examples/js .

Creo que se está subestimando la complejidad de mantener esto. Desafortunadamente, no creo que sea tan simple con la forma en que se configuran los ejemplos en el proyecto.

Me gustan mucho los puntos que sigues mencionando. Hay una complejidad absolutamente imprevista en la agrupación y el ejemplo de módulos anidados es bueno. En cuanto a su punto, creo que podemos llegar a decisiones sensatas sobre cómo manejar la agrupación de módulos anidados cuando llegue ese momento. No estoy diciendo que un script de paquete será una situación de configurarlo y olvidarlo, simplemente que será de menor mantenimiento.

Si llega el momento en que es demasiado difícil de mantener, siempre podemos dejarlo, pero creo que es una tontería descartar intentarlo debido a problemas que aún no tenemos. Será más fácil de implementar ahora mientras todavía tengamos una paridad de 1 a 1 entre /examples/jsm y Examples /js . Es probable que no reorganicemos masivamente la jerarquía del módulo /example/jsm y creo que podemos realizar actualizaciones incrementales en el paquete cuando lo hagamos. Voy a seguir adelante y comenzar a trabajar en la prueba de trabajo para esto (¿con babel porque ya está agregado?) Para poner mi dinero donde está mi boca como dicen.

Para Mugen, esto ayudaría a mantener cierta relevancia en three.js y three.min.js mientras continuamos manteniéndolos. También podría ayudar a los cientos de sitios que podrían estar buscando una actualización compatible con su implementación TRES no basada en módulos. La refactorización de TRES proyectos para usar módulos puede ser bastante extensa incluso si sabe lo que está haciendo.

No puedo hablar por los demás colaboradores, pero no cambiaré de opinión sobre este tema. Voto a favor de eliminar examples/js con el lanzamiento de diciembre de 2020 como se discutió y confirmó aquí #18749.

Voto para eliminar ejemplos/js con el lanzamiento de diciembre de 2020 como se discutió y confirmó aquí #18749.

No tengo ningún problema con eso.
Mientras "tres.min.js" esté disponible por un par de años más...

Gracias por el aporte Mugen, leí ese hilo, pero parece más un anuncio que una explicación de la decisión. Mi suposición es que ese desarrollo simplificado es la razón principal para avanzar en esta dirección, ¿existen otras?

Creo que tener un script de transpilación que podamos ejecutar para generar /examples/js style include debería ser un buen compromiso aquí. Debería disminuir drásticamente la cantidad de mantenimiento/complicación requerida aquí. Incluso estaría bien si fuera solo un comando en el paquete. json que tuvo que ejecutar por su cuenta y los archivos no se generaron de forma predeterminada. Hay beneficios para algunos desarrolladores y otros que necesitarán transpilar de todos modos. Preferiría que no tuviéramos que crear un flujo de trabajo de transpilación/agrupación por nuestra cuenta por separado cuando algo podría mantenerse en el repositorio principal para permitirnos colaborar mejor. :)

Leí ese hilo, pero parece más un anuncio que una explicación de la decisión.

Lamentablemente, no siempre podemos vincular todos los argumentos válidos a un solo hilo, ya sea porque la realización de un cambio de diseño avanza lentamente en múltiples debates a lo largo de los años, o simplemente porque se crean una y otra vez múltiples hilos sobre el mismo tema (como este ). Los colaboradores intentan minimizar el ruido y la segmentación, pero no siempre es posible.

Mi suposición es que ese desarrollo simplificado es la razón principal para avanzar en esta dirección, ¿existen otras?

El más grande que veo es la capacidad de usar e importar submódulos que minimizan el código redundante y hacen implementaciones reutilizables.

Por ejemplo, la mayoría de los cargadores necesitan crear algún tipo de estructura/clase de análisis de datos, esto se debe a que cada cargador debe ser autosuficiente para que los archivos example/js sean reutilizables. Sin embargo, si eliminamos por completo la restricción no modular, podríamos crear una única instancia de una clase DataParser e importar esa implementación estándar en todos los cargadores, lo que facilitaría inmediatamente el desarrollo y también eliminaría el código redundante de todos los cargadores.

Sí, buen punto. Ya tenemos que hacer trucos sucios como incrustar la clase Pass (la clase base de todos los pases FX) en EffectComposer solo para garantizar que el código heredado no se rompa.

muy buenos puntos en general.

conseguir y mantener a la gente al corriente/actualizada parece (y según mi propia experiencia) un tema difícil. Voy a tratar de pensar un poco en esto.

De hecho, mantener "tres.min.js" durante al menos 2 años más es IMPRESCINDIBLE.

Siempre será posible generar una compilación ES5 usando Babel. La pregunta que debemos responder cuando se trata de eso es si la responsabilidad de esto recae en nosotros o en el desarrollador que usa three.js.

Ya hemos decidido que dependerá del desarrollador crear versiones ES5 de los archivos de ejemplo, por lo que probablemente tenga sentido hacer lo mismo con los archivos de compilación. En mi opinión, también tiene sentido hacer esto en toda la biblioteca en una versión en lugar de espaciarla, pero mantener tres.min.js por un poco más de tiempo también está bien.

¡Pero porque muchos miles de archivos y los mejores perros de Google se basan en él!
Ejemplo: google.com/search?source=hp&q=webgl+benchmark

Este es el sitio principal que aparece para mí en esa búsqueda, y están usando R53, así que no creo que este cambio los afecte demasiado: https://www.wirple.com/bmark/

Como puede ver, las versiones antiguas de three.js todavía funcionan bien. Después de hacer la transición a los módulos, podemos indicarle a cualquiera que quiera una compilación de ES5 sin usar Babel que use la última versión antes de que elimináramos los archivos de ES5. Pueden consultar el repositorio completo de esa versión y usar los documentos de esa versión también.

@looeee Tocas algunos puntos buenos. Como se mencionó anteriormente, estoy de acuerdo en que tiene sentido desaprobar el ES5 three.min.js y three.js al mismo tiempo aquí. ¿Quizás esa debería ser su propia discusión por separado?

De cualquier manera, me gustaría llegar a un consenso sobre la inclusión de un script babel en el repositorio principal que se puede usar para generar archivos /js/example estilo ES5 de la vieja escuela. De ninguna manera se trata de si alguien es responsable de brindar este apoyo. Hay colaboradores, como yo, que van a necesitar esta función. Hay beneficios para algunos desarrolladores y otros que necesitarán transpilar de todos modos. Preferiría que no tuviéramos que crear un flujo de trabajo de transpilación/agrupación por nuestra cuenta por separado cuando algo podría mantenerse en el repositorio principal para permitirnos colaborar mejor.

Creo que es un compromiso justo permitirnos un archivo en el repositorio principal para que podamos trabajar juntos en el script transpiler de babel ES6 a ES5. ¿Hay realmente un problema allí? ¿Permitir que los colaboradores trabajen juntos en una característica que necesitan en el repositorio principal?

No estoy pidiendo ayuda o recursos a los colaboradores para hacer esto, simplemente estoy pidiendo que permitan que las personas que lo necesitan puedan trabajar juntos en el repositorio principal. Si hago un PR para esto y funciona, ¿realmente votarías para rechazarlo?

Si hago un PR para esto y funciona

Quiero decir, estoy feliz de ver que esto comience

¿De verdad votarías para rechazarlo?

todas las apuestas se cancelarán si falla el pase de pelusa 😂

¿Hay realmente un problema allí?

Sí, ya que el repositorio no debe promover patrones de codificación en desuso.

Sí, ya que el repositorio no debe promover patrones de codificación en desuso.

Todavía no está obsoleto oficialmente si no estamos despidiendo three.js + three.min.js (reconociendo el consenso ITT es que también deberíamos despedirlos) y tener un script de babel que debe ejecutar manualmente por su cuenta es apenas un respaldo brillante. Estoy de acuerdo en que definitivamente deberíamos alentar a las personas a usar módulos en su lugar y tener una advertencia en el script de babel y los archivos generados al respecto. No estoy de acuerdo en permitir que los contribuyentes trabajen juntos en un script de babel para personas en situaciones que no pueden usar módulos por cualquier motivo, ya que está promoviendo un patrón de codificación obsoleto. Principalmente porque todavía hay situaciones en las que el uso de módulos es inviable/poco práctico. Los documentos reconocen esta necesidad. Creo que podemos agregar con seguridad un archivo para que las personas que lo necesiten trabajen en él juntas.

Estoy de acuerdo en que tiene sentido desaprobar ES5 three.min.js y three.js al mismo tiempo aquí.

Quise decir que deberíamos descartar ejemplos/js, tres.min.js y tres.js al mismo tiempo, es decir, eliminar todo el código ES5 en una versión en lugar de distribuirlo en varias versiones.

@Mugen87

Sí, ya que el repositorio no debe promover patrones de codificación en desuso.

Todavía puedes ejecutar juegos de DOS en Windows 10.
Y eso no significa que Microsoft esté promocionando "patrones de codificación obsoletos".

Solo para aclarar los módulos js de ejemplo, tal como se mantienen en este proyecto, no requieren nodo, npm ni ningún marco de compilación para usar.

Bueno, no olvidemos que construir una aplicación lista para producción significa agrupar su código :)

Agradezco la agrupación de herramientas como Rollup que están disponibles, pero creo que deberíamos considerar un par de preguntas:

  • ¿Es justo suponer que si los desarrolladores quieren usar TRES en producción, también necesitan usar una de estas herramientas de empaquetado?
  • ¿Es justo dejar de admitir otras bibliotecas que dependen de las actualizaciones de los módulos ES5/UMD en la carpeta de ejemplos?

Mis sentimientos personales sobre esto:

Esta biblioteca tiene una década. Existe un ecosistema enorme que se basa en los módulos de la carpeta de ejemplos escritos en ES5/UMD. No creo que sea justo dejar de apoyar a todo un ecosistema.

Creo que la gente olvida que aún puede usar ES6 sin un patrón de agrupación de módulos. Uso ES6 todos los días, pero no uso patrones de agrupación de módulos en mis aplicaciones de interfaz. He trabajado en tiendas empresariales donde las herramientas de construcción se vuelven muy personalizadas por necesidad y no podrían incorporar un patrón de agrupación de módulos.

¿Qué debemos hacer?

Compilemos los módulos ES6 en módulos ES5/UMD para una distribución determinada después de cada lanzamiento.

Sí, ya que el repositorio no debe promover patrones de codificación en desuso.

Para casi todo en la vida, una solución aún puede ser de gran calidad utilizando patrones, técnicas y herramientas más antiguos.

Como analogía - En mi tiempo libre disfruto tallando piedra con cinceles de punta. Las herramientas y técnicas son diferentes de las herramientas eléctricas, pero al final la escultura seguirá siendo de alta calidad. He ejercido una preferencia personal para usar cinceles de punta porque disfruto usarlos y tengo las habilidades necesarias para producir algo con lo que yo mismo o los demás estén felices.

Siento lo mismo acerca de los módulos ES5/UMD. He podido encontrar patrones, técnicas y herramientas que mantienen bases de código de muy alta calidad y quiero seguir ejerciendo esa preferencia personal.

Compilemos los módulos ES6 en módulos ES5/UMD para una distribución determinada después de cada lanzamiento.

Estoy de acuerdo con lo que dijo looeeee.

¿Es justo suponer [...]

¿Qué? Estamos hablando de qué enfoque preferiríamos, la 'suposición' viene después. La preferencia parece ser alentar a otros a usar módulos pero (suponiendo que algunas personas aún quieran los TRES antiguos) ofrece un camino para aquellos que realmente lo quieren.

Compilemos los módulos ES6 en módulos ES5/UMD para una distribución determinada después de cada lanzamiento.

Esto lo puede hacer cualquiera; ese costo no necesita ser asumido por los mantenedores de three.js. Me gustaría reiterar lo que dijo @gkjohnson anteriormente, el costo de mantener los directorios examples/js y examples/jsm es alto. No podemos hacer esto indefinidamente y está claro que los módulos ES6 son los más modernos de los dos enfoques. Considere los siguientes costos:

  • Creación y mantenimiento de la automatización.
  • Depuración de errores de versión cuando se interrumpe la automatización
  • Asegurarse de que todas las solicitudes de extracción actualicen el archivo de origen, no el generado
  • Mantener documentación que explique cómo se utilizan ambos flujos de trabajo
  • Responder informes de errores y preguntas de soporte de los usuarios que intentan usar los flujos de trabajo de CJS y ES6

Ese último elemento es posiblemente el más grande. Siempre que haya dos copias de todo disponibles en este repositorio, ambas se considerarán totalmente compatibles. Regularmente dedicamos tiempo a ayudar a los usuarios que confunden los dos flujos de trabajo o intentan usar un cargador de módulos ES6 con la biblioteca principal de CJS, que falla de manera complicada.

Podemos reformular el problema de manera simple: todos nuestros ejemplos, que son partes importantes pero opcionales de la biblioteca three.js, actualmente no usan ninguna sintaxis de módulo. Ni UMD, ni CommonJS, ni módulos ES6. Simplemente parchean un espacio de nombres global THREE . Nos gustaría actualizar eso, usando la sintaxis de importación/exportación de ES6 en su lugar, y ha habido muchas advertencias tempranas de que este cambio estaba planeado.

Existe un ecosistema enorme que se basa en los módulos de la carpeta de ejemplos escritos en ES5/UMD. No creo que sea justo dejar de apoyar a todo un ecosistema.

No creo que sea justo decir que algo en el ecosistema three.js depende tanto de los espacios de nombres globales THREE.* que no se puede actualizar para usar la sintaxis de importación/exportación, o transpilar a ES5, o para usar un paquete. Hay varias soluciones aquí, y nos complacerá trabajar con los usuarios para ayudarlos a encontrar una opción adecuada para ellos.

el costo de mantener los directorios de ejemplos/js y ejemplos/jsm es alto.

Me gustaría profundizar un poco más en esto. He escrito una gran cantidad de herramientas personalizadas y compilación de scripts de automatización para aplicaciones front-end y me encantaría ayudar en todo lo que pueda.

Creación y mantenimiento de la automatización.
Depuración de errores de versión cuando se interrumpe la automatización

Ayúdame a comprender un poco más el impuesto de mantenimiento, ¿es algo exclusivo del código base TRES? En mi experiencia, este tipo de código suele ser el más duradero y necesita la menor cantidad de mantenimiento. Estos son guiones que escribes una vez y no vuelves a mirar durante largos períodos de tiempo.

Asegurarse de que todas las solicitudes de extracción actualicen el archivo de origen, no el generado

Tal vez una pequeña secuencia de comandos o prueba podría ayudar con esto en el flujo de trabajo de lanzamiento.

Mantener documentación que explique cómo se utilizan ambos flujos de trabajo

También votaría para eliminar la documentación de los espacios de nombres globales. Creo que es una tontería respaldar la documentación de dos flujos de trabajo. Esto no es algo malo. La mayoría de las bibliotecas que agrupan su código para diferentes contextos, los módulos UMD/ES6 solo tienen un conjunto de documentos.

Responder informes de errores y preguntas de soporte de los usuarios que intentan usar los flujos de trabajo de CJS y ES6.

Creo que el volumen de problemas relacionados con algo como esto es relativo al tamaño de la popularidad de THREE. Tú y yo vemos este tipo de problemas en Stack Overflow todo el tiempo. Un usuario que no puede distinguir entre los dos flujos de trabajo es probablemente un nuevo programador inspirado en la biblioteca y solo está tratando de aprender los conceptos básicos de la programación en general.

Si el objetivo es reducir el volumen de problemas específicamente relacionados con la confusión entre los dos flujos de trabajo , entonces eliminar el código ES5 probablemente ayudaría con eso, pero dudo que el volumen de problemas en general cambie. Un nuevo programador siempre estará atascado en la siguiente pregunta que puede o no estar relacionada con la biblioteca.

¿Cómo reducir el volumen de problemas en general?

Si el objetivo real es reducir el volumen de problemas en general, quizás las políticas de problemas más estrictas puedan ayudar con eso. Veo que están haciendo un gran trabajo con esto ya usando etiquetas como Help (please use the forum) pero tal vez sea necesario que haya más de este tipo de cosas.

De manera más general, podría ser mejor simplemente descartar algunos tipos de problemas que los TRES colaboradores están dispuestos a discutir e investigar si actualmente se sienten abrumados por el volumen total.

Ideas de pareja:

  • Al momento de escribir suggestions y enhancements tienen (271) problemas abiertos. Estas etiquetas parecen generar mucho ruido. Tal vez solo tome PR listo / cheques aprobados como la sugerencia real. Cierre instantáneamente todo lo demás y márquelo como Discussion (please use the forum) .
  • Al momento de escribir loaders tiene (61) problemas abiertos. Esta etiqueta también parece generar mucho ruido. Veo muchos problemas con esta etiqueta relacionados con suggestions y enhancements o informes de errores mal formados. Tal vez solo tome informes de errores bien formados y relaciones públicas listas / comprobaciones aprobadas para sugerencias. Cierre todo lo demás y márquelo en consecuencia.

No creo que sea justo decir que algo en el ecosistema three.js depende tanto de los espacios de nombres globales THREE.* que no se podría actualizar para usar la sintaxis de importación/exportación, o para transpilar a ES5, o para usar un empaquetador

Estoy de acuerdo en que se puede actualizar cualquier cosa, pero si podemos encontrar una manera de hacer un poco de trabajo para continuar apoyando a estos usuarios de una manera sostenible, estoy de acuerdo con @Bug-Reaper al decir:

Preferiría que no tuviéramos que crear un flujo de trabajo de transpilación/agrupación por nuestra cuenta por separado cuando algo podría mantenerse en el repositorio principal para permitirnos colaborar mejor.

En conjunto, estaríamos ahorrando a estos usuarios una enorme cantidad de tiempo al actualizar sus aplicaciones/bibliotecas, sistemas de compilación y documentación.

Me gustaría profundizar un poco más en esto. He escrito una gran cantidad de herramientas personalizadas y compilación de scripts de automatización para aplicaciones front-end y me encantaría ayudar en todo lo que pueda.

bueno.

¿Cómo reducir el volumen de problemas en general?

Mantengamos esto encaminado, por favor. Feliz de discutir más en otro hilo. Se relaciona un poco con mi comentario anterior.

Estoy de acuerdo con @ Bug-Reaper al decir:

Preferiría que no tuviéramos que crear un flujo de trabajo de transpilación/agrupación [...]

Creo que todos estamos de acuerdo en esto.

Gracias a todos por compartir los pros y los contras. Siempre es bueno compartir esto para asegurarnos de que estamos tomando decisiones informadas.

Esto es algo en lo que he estado gastando algunos ciclos cerebrales este año, e incluso les pregunté a los proveedores de navegadores sobre sus prioridades para poder planificar con anticipación.

Estoy de acuerdo en que los módulos ES6 son el futuro, pero desarrollar con ellos sin importar mapas puede causar grandes dolores de cabeza y romper por completo su flujo. Cuando decidimos desaprobar examples/js , esperaba que los mapas de importación tuvieran más tracción, pero parece que actualmente no es una prioridad para los navegadores.

Debido a esto, decidí suspender la obsolescencia de la carpeta examples/js hasta que los navegadores implementen mapas de importación. Odiaría obligar a los novatos a aprender sobre polyfills o bundlers para renderizar su primer cubo.

Llegué a la misma conclusión que @ Bug-Reaper. Hoy estoy echando un vistazo a la creación de un script que construye examples/js a partir de examples/jsm archivos.

@mrdoob

Decidí suspender la eliminación de la carpeta de ejemplos/js hasta que los navegadores implementen mapas de importación.
Llegué a la misma conclusión que @ Bug-Reaper. Hoy estoy echando un vistazo a la creación de un script que crea ejemplos/js a partir de archivos de ejemplos/jsm.

Una sabia decisión.
👍

@mrdoob Por supuesto que acepto su decisión, pero creo que es una oportunidad perdida. Tarde o temprano, los desarrolladores tendrán que alejarse de los scripts globales. Y no creo que Import Maps haga mucha diferencia aquí. En lugar de "forzar" a los usuarios a flujos de trabajo mejores y preparados para el futuro, les permitimos continuar usando scripts globales. En 2020.

Y no creo que Import Maps haga mucha diferencia aquí.

El otro día vi a alguien haciendo esto:

<script src="js/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/loaders/GLTFLoader.js"></script>
<script type="module" src="js/main.js"></script>

Y, dentro main.js estaban haciendo esto:

import {OrbitControls} from "https://threejsfundamentals.org/threejs/resources/threejs/r119/examples/jsm/controls/OrbitControls.js";

Y la cosa realmente funcionó... 😐

No podemos simplemente esperar que los usuarios hagan lo correcto, están aprendiendo y están probando cosas hasta que algo funciona. El reto es encontrar un diseño que les ayude a hacer lo correcto sin que se den cuenta.

El problema con los módulos ES6 sin mapas de importación es que el usuario no puede simplemente copiar OrbitControls.js a una carpeta /js en su propio proyecto e importarlo como solía hacerlo. No funcionará porque OrbitControls.js busca ../../build/three.module.js .

Con los mapas de importación, OrbitControls.js solo importaría desde three . El usuario puede copiar el archivo donde quiera y luego ajustar la ruta en el mapa de importaciones.

Importar mapas nos acerca a la facilidad de importar archivos como en los "viejos" días. No será tan fácil como lo era antes, pero al menos el usuario no tendrá que preocuparse por el orden al importar archivos. Ganar algo perder algo.

Acordó que importar mapas hará que la importación sea configurable y, por lo tanto, más flexible. Aunque el usuario todavía tiene que ajustar el mapa de importación (y así comprender lo que realmente es).

Simplemente creo que todo "copiar archivos JS en una carpeta" es un antipatrón malvado y esperaba poder evitarlo recomendando a nuevos usuarios/principiantes que trabajen con importaciones de CDN (que también es una opción para los desarrolladores que no No quiero usar una compilación por las razones que sean). Las aplicaciones adecuadas (deberían) usar herramientas de compilación de todos modos.

Realmente no lo veo como un anti-patrón.

Así es como aprendí a hacer sitios web. Uno colocaría los archivos .css en la carpeta /css , luego las imágenes en /img y los archivos .js en /js .

Durante los últimos meses, he estado haciendo algunos experimentos con el enfoque de módulos/CDN de ES6 y no me parece bien que las bibliotecas provengan de un dominio diferente al de mi proyecto.

Una gran cosa que perdemos cuando no copiamos archivos es poder editarlos. Siempre se supuso que los archivos en examples/js eran ejemplos sobre los que se puede construir. Si copié OrbitControls.js en mi proyecto y no hizo exactamente lo que necesitaba, podría modificarlo porque era solo un archivo local.

Así es como solía configurar mis proyectos:

<script src="js/libs/three.js"></script>
<script src="js/libs/three/OBJLoader.js"></script>
<script src="js/libs/three/OrbitControls.js"></script>
<script>
    console.log( THREE, THREE.OBJLoader, THREE.OrbitControls );
</script>

Con mapas de importación se vería así:

<script type="importmap">
{
  "imports": {
    "three": "js/libs/three.module.js",
    "OBJLoader": "js/libs/three/OBJLoader.js",
    "OrbitControls": "js/libs/three/OrbitControls.js"
  }
}
</script>
<script type="module">
    import * as THREE from 'three';
    import { OBJLoader } from 'OBJLoader';
    import { OrbitControls } from 'OrbitControls';

    console.log( THREE, OBJLoader, OrbitControls );
</script>

No es tan bonito como lo era antes, pero se ocupa de las dependencias/orden de importación por usted y no requiere un paquete.

Sin embargo, todavía funciona para las personas que realizan desarrollo basado en paquetes. De hecho, lo hace mejor para ellos porque los complementos ahora se importan desde three en lugar de ../../build/three.module.js .

Y la cosa realmente funcionó... 😐

FWIW, esto solo parece funcionar en una pequeña cantidad de casos. Cuando no funciona, falla de manera extremadamente confusa y hemos presentado varios problemas relacionados con eso, dado que también sucede con los procesos de compilación. Podría decirse que si le preocupa que el novato le dé múltiples formas de usar los mismos archivos, es más propenso a errores y confuso.

Tal vez tangencial, pero podría valer la pena informar a las personas que tienen dos copias de three.js incluidas en la página a través de una advertencia en la consola (incluso si son de la misma versión), lo que puede causar problemas a menos que se tenga cuidado de no mezclarlas. ~Creo que React hace esto por razones similares~ React puede señalar esto como una posible fuente de error. Eso podría ayudar a alejar a las personas de mezclar estas modalidades al aprender.

Llegué a la misma conclusión que @ Bug-Reaper. Hoy estoy echando un vistazo a la creación de un script que crea ejemplos/js a partir de archivos de ejemplos/jsm.

Si este es el nuevo plan, estaría feliz de ayudar a revivir # 15526 / # 15543 (que ahora se han eliminado del proyecto) que compila cada archivo de módulo en uno ES6. Dado que algunos ejemplos están repartidos entre tantos archivos (Shader Nodes, por ejemplo) y es posible que estemos interesados ​​en dividir algunos de los módulos en varios archivos, probablemente valga la pena actualizar el script acumulativo para tomar una lista explícita de archivos que queremos convertir. y salida. También deberíamos poder crear automáticamente dependencias entre los archivos que se generan.

Una gran cosa que perdemos cuando no copiamos archivos es poder editarlos

Estoy de acuerdo, aunque si podemos llegar a clases en todas partes, espero algo como:

import orbitalcontrols from  orbitalcontrolsURL

class mycontrols extends orbitalcontrols {
// do the edits I care about
}

y luego mas tarde

let controls = new myorbitalcontrols

Una gran cosa que perdemos cuando no copiamos archivos es poder editarlos

Estoy de acuerdo, aunque si podemos llegar a clases en todas partes, espero algo como:

importar controles orbitales desde orbitalcontrolsURL

clase mycontrols extender orbitalcontrols {
// hago las ediciones que me interesan
}

y luego mas tarde

dejar controles = nuevos controles miorbitales

Ya puede hacerlo ... ¡incluso si la "clase" principal es una función js simple!

El código realmente funciona (en una prueba rápida del depurador):

Promise.all([
    import('https://unpkg.com/three/build/three.module.js')
        .then( mod=> [mod.Camera, mod.WebGLRenderer] ),
    import('https://unpkg.com/three/examples/jsm/controls/OrbitControls.js')
        .then( mod=> mod.OrbitControls )
])
.then( ([
    [ Camera, WebGLRenderer ],
    OrbitControls
])=> new ( class extends OrbitControls {} )( new Camera, (new WebGLRenderer).domElement )
)
.then( console.log )

... o una sintaxis más simple:

(async function() {

let { Camera, WebGLRenderer } = await import('https://unpkg.com/three/build/three.module.js')
,   { OrbitControls } = await import('https://unpkg.com/three/examples/jsm/controls/OrbitControls.js')

class Con extends OrbitControls { }

let my = new Con( new Camera, (new WebGLRenderer).domElement )
console.log( my )

})()

aparte de esa función aynom y preocuparse por las promesas async/await, genial

class mycontrols extend orbitalcontrols {
 // do the edits I care about
 }

Idealmente, este es el patrón que deberíamos promover, en lugar de decirles a los usuarios que editen los archivos originales cuando realicen cambios. Sin embargo, los ejemplos no están escritos teniendo en cuenta la extensibilidad, por lo que existen fuertes límites en lo que puede lograr. En mi experiencia, termina teniendo que copiar todo el ejemplo original en el constructor de la clase extendida para que funcione, por lo que no tiene sentido usar extend .

Por ejemplo, el cambio solicitado más común para OrbitControls es limitar la bandeja . Esto se logra fácilmente como se demuestra en el violín de @ Mugen87 de ese hilo.

En resumen, agregas los vectores minPan y maxPan y fijas controls.target en el método controls.update .

Intenté hacer esto extendiendo OrbitControls . Puede crear una clase extendida y funciona bien. Sin embargo, los problemas se vuelven evidentes cuando comienza a hacer cambios. No puede simplemente extender el método update :

class OrbitControlsPanLimit extends OrbitControls {
    constructor(object, domElement) {
        super(object, domElement);
    }

    update() {
        super.update();
        console.log('Custom update function');
    }
}

Esta clase extendida funciona ( falla ), pero este nuevo método OrbitControlsPanLimit.update se ignora. El método original OrbitControls.update todavía se usa.

Puede sobrescribirlo redefiniendo en el constructor:

class OrbitControlsPanLimit extends OrbitControls {
    constructor(object, domElement) {
        super(object, domElement);

        this.update = () => {
            console.log('Custom update function');
        }
    }
}

No puede usar super.update() aquí, por lo que la única opción es copiar todo el método de actualización original. Sin embargo, ese método se basa en muchas de estas cosas dentro de OrbitControls , que se comparte entre todos los métodos.

    //
    // internals
    //

    var scope = this;

    var changeEvent = { type: 'change' };
    var startEvent = { type: 'start' };
    var endEvent = { type: 'end' };

    var STATE = {
        NONE: - 1,
        ROTATE: 0,
        DOLLY: 1,
        PAN: 2,
        TOUCH_ROTATE: 3,
        TOUCH_PAN: 4,
        TOUCH_DOLLY_PAN: 5,
        TOUCH_DOLLY_ROTATE: 6
    };

    var state = STATE.NONE;

    var EPS = 0.000001;

    // current position in spherical coordinates
    var spherical = new THREE.Spherical();
    var sphericalDelta = new THREE.Spherical();

    var scale = 1;
    var panOffset = new THREE.Vector3();
    var zoomChanged = false;

    var rotateStart = new THREE.Vector2();
    var rotateEnd = new THREE.Vector2();
    var rotateDelta = new THREE.Vector2();

    var panStart = new THREE.Vector2();
    var panEnd = new THREE.Vector2();
    var panDelta = new THREE.Vector2();

    var dollyStart = new THREE.Vector2();
    var dollyEnd = new THREE.Vector2();
    var dollyDelta = new THREE.Vector2();

El resultado final es que tendrá que copiar casi todo el OrbitControls original en el constructor OrbitControlsPanLimit lo que anula el propósito de extender la clase. A menos que escribamos los controles como una clase con la extensibilidad en mente, no creo que sea viable extenderla.

gracias @looeee por intervenir allí. Estaba pensando que tal vez me había perdido una solución fácil dentro de mis propios esfuerzos, pero ahora que lo mencionas, ahí es donde me encontré.

Idealmente, este es el patrón que deberíamos promover, en lugar de decirles a los usuarios que editen los archivos originales cuando realicen cambios.

Cuidado, eso es caminar de cerca hacia un argumento de herencia versus composición.

Idealmente, una biblioteca no debería promocionar ningún patrón. Debería promocionar sus características y cómo pretende resolver sus problemas.

Tampoco debería asumir un flujo de trabajo de desarrolladores, pila, sistema de compilación, caso de uso. Una gran biblioteca se adapta tanto como sea posible a las muchas necesidades complejas de su comunidad.

Lo que es nuevo hoy es viejo mañana, los patrones van y vienen. La única constante entonces sería el software que ofrece un gran soporte para los muchos casos de uso en el camino para mantener la mayor compatibilidad posible con versiones anteriores.

Todavía puedes ejecutar juegos de DOS en Windows 10.

herencia vs argumento de composición

por favor no. la solución a este 'argumento' es utilizar la mejor herramienta para el trabajo. hay un lugar para la herencia, la composición, funcional, basado en pruebas... lo que sea.

Dado que estamos hablando de cómo otros desarrolladores (usan, reutilizan, modifican) three.js, es válido promover un patrón que sea fácilmente comprensible y utilizable sin salirse de las funciones del navegador js.

promocionar no significa que no se pueda usar un estilo diferente.

tanta compatibilidad con versiones anteriores como sea posible

si y no.

Debería promocionar sus características y cómo pretende resolver sus problemas.

tal vez para que quede claro, ¿cuál es el conjunto de características/problemas para usted?

Tampoco debería asumir un flujo de trabajo de desarrolladores, pila, sistema de compilación, caso de uso

Estoy mayormente de acuerdo. El caso de uso de threejs es actualmente el navegador. la advertencia es que algunos de nuestros cargadores son útiles para algunas aplicaciones de nodo por lo que he escuchado.

La única constante entonces sería el software que ofrece un gran soporte para los muchos casos de uso en el camino.

el cambio es la única constante. los desarrolladores usan la herramienta que les gusta y, a veces, probamos otras cosas.

como un aparte:

Debería promocionar sus características y cómo pretende resolver sus problemas.

¿Cuál vino primero? la característica, el patrón o el problema?
seguramente el patrón ayudó a resolver el problema y luego se convirtió en una característica
... ¿o fue la función la que creó el problema y encontramos un patrón para resolverlo?

¿Cuál vino primero? la característica, el patrón o el problema?

¿Cuál vino primero? ¿La gallina o el huevo?
Algunas personas dicen que el Gallo...

Gran discusión en general, gracias a todos por todos los aportes.

Me gustaría saber qué piensan sobre qué paquete (rollup, babel, paquete, webpack, etc.) es el más adecuado para la tarea de transpilar nuestros módulos de ejemplo de ES6. Creo que @gigablox mencionó tener experiencia aquí y estoy seguro de que otros también la tienen.

El repositorio actual ya contiene babel, rollup y un par de complementos relacionados. Seguí adelante y comencé a hackear esto esta noche y tengo un script de configuración de acumulación extremadamente tosco para compartir:

// jsm-transpiler.js
export default [
  {
    input: './examples/jsm/controls/OrbitControls.js',
    output: {
      banner:"//warning this file was generated automatically",
      file: './examples/js/controls/OrbitControls.js',
      name:'OC',
      footer:'THREE["OrbitControls"]=OC.OrbitControls',
      format: 'umd'
    }
  }
];

Esta secuencia de comandos de configuración acumulativa convierte el módulo $# OrbitControls .js que no es un módulo que asigna TRES.OribitControls al constructor apropiado. Funcionó, lo cual es genial :)! También incluyó las 40.000 líneas de TRES.js en el archivo de salida, no tan bueno, jaja. También estoy contaminando perezosamente el espacio de variables globales al declarar una var global intermedia llamada OC para ayudar a transportar el constructor OrbitControls a TRES.

Rollup parece tener algunas características realmente geniales que creo que pueden solucionar muchos de nuestros problemas. En particular, el mapeo y otros controles para asegurarse de que se incluyan/excluyan los módulos anidados correctos. La capacidad de inyectar código antes y después de la carga útil transpilada a través de propiedades de encabezado/pie de página/introducción/salida.

Soy cautelosamente optimista de que podemos lograr lo que necesitamos con un script de configuración de resumen modificado. Sin embargo, sería genial si alguien que haya investigado/comprendido las diferencias entre los muchos paquetes pueda opinar aquí. Necesitaremos algo bastante robusto para manejar los módulos a medida que se vuelvan más impresionantes y apostaría a que algunos transpilarán el código mejor que otros.

Aquí está mi opinión al respecto:
https://github.com/mrdoob/tres.js/pull/20529

Este es un script de compilación personalizado de poc que convierte todos los módulos JSM en módulos JS con espacios de nombres globales en aproximadamente 30 segundos. Tuvo bastante éxito con este método. Necesita más pruebas, pero probé algunos de los módulos más complejos como el GLTFLoader en un hola mundo y estuvo bien.

Podría usar la ayuda de cualquier asistente experimentado de RegExp :) para discutir algunos casos extremos sobre los que puede leer más en PR.

Intención de envío de mapas de importación de Chrome:
https://groups.google.com/a/chromium.org/g/blink-dev/c/rVX_dJAJ-eI

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