Three.js: Pasar a una arquitectura modular

Creado en 6 may. 2014  ·  153Comentarios  ·  Fuente: mrdoob/three.js

Browserify
Pasar a esta arquitectura tiene ventajas y desventajas. Por favor agregue sus pensamientos.

Nota: esto no requiere que los consumidores de three.js usen browserify.

Suggestion

Comentario más útil

Correcto, entonces requerirá un poco de refactorización ...

¡Te entiendo! Desde que este hilo se animó durante los últimos días, he estado trabajando un poco más en three-jsnext . Es un proyecto que toma la base de código Three.js existente y la convierte en módulos ES automáticamente. Solo estoy discutiendo con un par de dependencias cíclicas complicadas (particularmente alrededor de KeyframeTrack ), pero debería tener algo para compartir muy pronto. Por lo que puedo decir, todos los ejemplos continúan funcionando, y la compilación minificada es más pequeña que la actual (usando Rollup para generar un archivo UMD), por lo que son buenas noticias.

Todos 153 comentarios

Una ventaja es que esto impondría una arquitectura modular para el desarrollo continuo de three.js.

El estilo común en node / browserify hace que cada archivo declare sus dependencias en la parte superior y considere las variables globales como un anti-patrón.

Aquí hay un fragmento de ejemplo:

// src/geometry/BoxGeometry.js
var Geometry = require('./Geometry.js');
var Vector3 = require('../core/Vector3.js');
module.exports = BoxGeometry;

function BoxGeometry() {
  // ...
}

BoxGeometry.prototype = Geometry.prototype;

Otra ventaja es que los consumidores de three.js usen browserify podrían elegir las partes que deseen. Podrían simplemente importar Scene , BoxGeometry , PerspectiveCamera y WebGLRenderer , obtener las dependencias para todos esos automáticamente ( Object3D etc.), y tener un pequeño paquete de javascript que admita solo el conjunto de funciones que desean.

Esto podría hacerse de una manera que no imponga cambios importantes. En el nivel superior, exportaríamos todas las clases que consideramos parte del paquete estándar

// src/three.js
var THREE = { rev: 101 }
module.exports = THREE

THREE.Geometry = require('./geometry/Geometry.js')
THREE.BoxGeometry = require('./geometry/BoxGeometry.js')
// ...

nota: No estoy requiriendo exactamente las dependencias en la parte superior en este ejemplo, porque este archivo sería casi exclusivamente declaraciones de requerimiento.

Finalmente, envolveríamos eso en una Definición de módulo universal que detecta si un sistema de módulo (nodo / browserify, AMD) está en uso y, si es así, lo exporta o lo agrega al objeto global ( window ).

Revisemos:

  • refuerza un buen estilo modular
  • permite a los consumidores de three.js utilizan browserify elegir la funcionalidad
  • sin cambios importantes

Esto requeriría reemplazar el sistema de compilación, pero el nuevo sería bastante sencillo.

Algunas otras ventajas:

  • Puedes estructurar tu código
  • Puede crear / reutilizar módulos sin contaminar el espacio de nombres global
  • Puedes construir para producción
  • Puede depurar más fácilmente ya que cada módulo tiene su propio archivo, no es necesario buscar el módulo correspondiente en un archivo grande de three.js

@ shi-314 Supongo que estoy un poco confundido, siento que You can structure your code y You can build for production son cosas que puedes hacer sin el cambio arquitectónico. ¿Estás hablando de la fuente de three.js o de cosas creadas con three.js?

Una práctica que usa three.js y que dificulta su uso en entornos commonjs es el uso de instanceof : https://github.com/mrdoob/three.js/blob/master/src/core/Geometry .js # L82

Esto se debe a que en una aplicación a menudo termina con diferentes versiones de la misma biblioteca en su árbol de fuentes, por lo que la verificación de instanceof no funciona entre diferentes versiones de la misma biblioteca. Sería bueno en preparación para un cambio a un sistema de módulos commonjs para reemplazar esas instancias de comprobaciones con verificación de características detrás de una interfaz de estilo Geometry.isGeometry(geom) .

@kumavis Estoy hablando de cosas integradas en three.js. Digamos que quieres crear tu propio material con tus shaders, etc. En este momento necesitas extender el objeto THREE global para mantener la coherencia con el resto del código three.js:

THREE.MeshMyCoolMaterial = function (...) { ... }

Pero si tuviéramos Browserify de lo que podría hacer:

var MeshLambertMaterial = require('./../MeshLambertMaterial');
var MeshMyCoolMaterial = function (...) {...}

Por lo tanto, su espacio de nombres se mantiene constante y no necesita usar THREE.MeshLambertMaterial y MeshMyCoolMaterial en su código.

Y con You can build for production básicamente quise decir lo mismo que mencionaste: allows three.js consumers using browserify to pick and choose functionality .

@ shi-314 gracias, eso está más claro. Eso afecta mi solución general propuesta para la deserialización de clases definidas por el consumidor:

// given that `data` is a hash of a serialized object
var ObjectClass = THREE[ data.type ]
new ObjectClass.fromJSON( data )

Esto es de mi refactor de serialización / deserialización propuesto
https://github.com/mrdoob/three.js/pull/4621

El rendimiento no debería verse afectado por un cambio como este.

Este es un cambio bastante grande, pero también estoy a favor de él.

Algunas otras ventajas importantes:

  • Puede usar la opción standalone browserify para generar una compilación UMD para usted. No es necesario manipular manualmente las envolturas UMD.
  • Los usuarios de browserify / NPM pueden consumir el paquete fácilmente
  • Extraer dependencias para threejs (como poly2tri, color-string , etc.) se vuelve mucho más fácil
  • Los módulos que "realmente no pertenecen" a una biblioteca de renderizado (como bibliotecas vectoriales / matemáticas) pueden extraerse como módulos NPM separados y reutilizarse para muchos otros tipos de proyectos. Un beneficio importante de esto es que los módulos individuales tienen su propio repositorio para errores / problemas, PR, etc. (limpieza de problemas de ThreeJS).
  • NPM se encargará del control de versiones semántico por nosotros. por ejemplo, podemos impulsar un cambio rotundo en el threejs-vecmath sin preocuparnos por la rotura del código de todos. Y por otro lado, si hacemos un parche o una versión menor en un módulo en particular, las personas que consuman esos módulos podrán obtener los cambios automáticamente.
  • Hace que los "extras" como EffectComposer y varios sombreadores sean fáciles de empaquetar y consumir (imagina npm install threejs-shader-bloom )
  • A medida que se extraen los módulos, el tamaño de distribución final comenzará a hacerse más pequeño y más específico de la aplicación. Eventualmente no habrá necesidad de diferentes "tipos de compilaciones" ya que solo usaremos require() los módulos que nuestra aplicación está usando realmente.

Para @mrdoob y los demás autores; Si no tiene mucha experiencia con NPM / Browserify, sugeriría hacer un par de pequeños proyectos con él y familiarizarse con su "filosofía". Es muy diferente a la arquitectura ThreeJS; en lugar de grandes marcos, fomenta muchas cosas pequeñas .

Otra ventaja de este enfoque es que puede haber un ecosistema de código abierto, módulos Three.JS de terceros, especialmente sombreadores, geometrías, cargadores de modelos, etc. Publicado a través de NPM o Github / Component que la gente puede fácilmente referenciar y usar. Por el momento, las cosas se comparten al albergar una demostración en la que las personas luego 'ven la fuente'. Three.JS se merece algo mejor!

Creo que uno de los problemas que tengo con Three.JS es la rapidez con la que el código se vuelve incompatible con la versión actual de Three.JS. Otra ventaja de cambiar a algo como esto es poder especificar versiones específicas de _bits_ de Three.JS sería muy poderoso y práctico.

+1

+1 para una arquitectura CommonJS / browserify, haría que el núcleo sea más liviano y las extensiones encajarían incluso si provienen de terceros

Fragmentar three.js en pequeños módulos también tiene muchos costos. El sistema actual permite complementos de terceros bastante simples (por ejemplo, los módulos THREEx de jetienne). Hay mucho que decir sobre la simplicidad de la configuración actual, siempre que los sistemas del módulo JS sean simplemente envoltorios de los sistemas de compilación.

Otra forma de minimizar el tamaño de compilación es lo que hace ClojureScript. Siguen algunas convenciones para permitir que el compilador Closure de Google realice análisis de todo el programa y elimine el código muerto.

+1 para la elegancia de la simplicidad poco apreciada y a menudo pasada por alto

+1

Fragmentar three.js en pequeños módulos también tiene muchos costos. El sistema actual permite complementos de terceros bastante simples (por ejemplo, los módulos THREEx de jetienne).

La idea aquí es que se seguiría proporcionando una compilación UMD para entornos que no sean de nodo. Los complementos como THREEx funcionarían de la misma manera para aquellos que dependen de ThreeJS con etiquetas simples <script> .

Lo complicado será: ¿cómo require() un complemento en particular si estamos en un entorno CommonJS? Quizás browserify-shim podría ayudar.

Hay mucho que decir sobre la simplicidad de la configuración actual, siempre que los sistemas del módulo JS sean simplemente envoltorios de los sistemas de compilación.

El actual sistema de extensiones / complementos de ThreeJS es bastante horrible para trabajar, y está lejos de ser "simple" o fácil. La mayoría de los proyectos de ThreeJS tienden a usar algún tipo de complemento o extensión, como EffectComposer o FirstPersonControls, o un cargador de modelos, o uno de los otros muchos archivos JS que flotan en la carpeta examples . Ahora mismo, la única forma de depender de estos complementos:

  • Descargue la compilación actual de ThreeJS
  • Copie y pegue los archivos necesarios en su carpeta vendor
  • Conecte las tareas de gulp / gruñido para concat y minimice todos los complementos que necesita; asegurándose de colocarlos en el orden correcto, de lo contrario, las cosas se romperán. Mantenga esta lista manualmente a medida que agrega más complementos.
  • Repita los pasos 1 y 2 cada vez que se actualice ThreeJS; y luego tire de su cabello cuando se dé cuenta de que el nuevo código no es compatible con versiones anteriores

Ahora, imagina, con browserify podrías hacer algo como esto:

var FirstPersonControls = require('threejs-controls').FirstPersonControls;

//more granular, only requiring necessary files
var FirstPersonControls = require('threejs-controls/lib/FirstPersonControls');

Esos complementos serán require('threejs') y cualquier otra cosa que puedan necesitar (como fragmentos de GLSL o triangulación de texto ). La administración de dependencias / versiones está oculta para el usuario, y no hay necesidad de tareas de concat de gruñido / trago mantenidas manualmente.

Lo complicado será: ¿cómo necesitamos () un complemento en particular si estamos en un entorno CommonJS?

He estado usando CommonJS para proyectos THREE.js por un tiempo. Es un proceso un poco manual, convertir fragmentos de código de otras personas en módulos y no creo que haya una manera fácil de evitarlo para el código heredado que no es convertido por los autores o contribuyentes.

Lo importante es que hay un módulo que exporta todo el objeto TRES 'estándar', que luego puede ser requerido por cualquier persona que desee extenderlo.

var THREE = require('three');

THREE.EffectComposer = // ... etc, remembering to include copyright notices :)

Esto me ha funcionado bastante bien, especialmente a medida que el proyecto crece y comienzo a agregar mis propios sombreadores y geometrías en sus propios módulos, etc.

Siempre que haya un paquete npm 'threejs-full' o 'threejs-classic', esta se convierte en una forma bastante viable de trabajar con cosas antiguas de Three.js en un entorno CommonJS, ¡pero sospecho que es un nicho bastante!

+1
Creo que una vez que los módulos threejs fragmentados están disponibles en npm, plugin
A los desarrolladores les encantará migrar a CommonJS env.
El 5 de junio de 2014 a las 21:19, "Charlotte Gore" [email protected] escribió:

Lo complicado será: ¿cómo necesitamos () un complemento en particular si
están en un entorno CommonJS?

He estado usando CommonJS para proyectos THREE.js por un tiempo. Es un poco
de un proceso manual, convirtiendo fragmentos del código de otras personas en módulos
y no creo que haya una manera fácil de evitarlo para el código heredado
que no ha sido convertido por los autores o colaboradores.

Lo importante es que hay un módulo que exporta todo el 'estándar'
TRES objeto, que luego puede ser requerido por cualquier cosa que desee extender
eso.

var TRES = require ('tres');
THREE.EffectComposer = // ... etc, recordando incluir avisos de derechos de autor :)

Esto me ha funcionado bastante bien, especialmente a medida que el proyecto crece y yo
comenzar a agregar mis propios sombreadores y geometrías en sus propios módulos, etc.

Siempre que haya un paquete npm 'threejs-full' o 'threejs-classic', entonces
esto se convierte en una forma bastante viable de trabajar con cosas antiguas de Three.js en una
Entorno CommonJS, ¡pero sospecho que este es un nicho bastante!

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/mrdoob/three.js/issues/4776#issuecomment -45236911.

También podría hacer que los sombreadores se hicieran modulares, por ejemplo, usando glslify . Incluso cosas como crear un middleware Express que genere sombreadores a pedido se vuelven más fáciles en ese momento.

Hace algunos meses moví frame.js a

Sin embargo, todavía necesito aprender a "compilar" esto. ¿Cuál es la herramienta / flujo de trabajo para generar three.min.js partir de una lista de módulos?

Prefiero gulp.js como sistema de compilación con el complemento gulp-browserify . Es realmente fácil de entender y el código se ve más limpio que gruñido en mi opinión. Mira esto: http://travismaynard.com/writing/no-need-to-grunt-take-a-gulp-of-fresh-air : wink:

algunos pensamientos: (basado en mi experiencia limitada con node, npm, browserify, por supuesto)

  1. Creo que los módulos de node.js son geniales (eso es npm, módulos y require () s)
  2. creo que browserify también es genial

Dicho esto, después de la discusión en este hilo, no estoy seguro de si todos tenían el mismo conocimiento de browserify (browserify, commonjs, requirejs, amd, umd están algo relacionados, aunque es posible que no necesariamente sean lo mismo).

ahora si puedes seguir un poco mi cadena de pensamientos.

  1. JS es genial, se ejecuta rápido en todos los navegadores.
  2. wow, ahora JS también se ejecuta en el lado del servidor.
  3. eso es node.js, es genial, así que codifiquemos cosas en node.js
  4. pero no deseo escribir / no puedo escribir todo / encontrar algo para usar.
  5. no se preocupe, ahora ejecute npm install modules
  6. ahora requerimos estos módulos geniales para que podamos usarlos.
  7. ¡Funciona genial!
  8. ahora espera, acabamos de escribir un montón de cosas en JS que se ejecutan en node.js
  9. ¿No se supone js en los navegadores? ¿Cómo hacemos que estos códigos se ejecuten allí de nuevo?

Ahí es donde entra en escena Browserify. Bueno, técnicamente se puede usar requireJS en el navegador. Pero desea agrupar archivos js sin realizar demasiadas llamadas de red (a diferencia de los requisitos del sistema de archivos que son rápidos). Ahí es donde Browserify hace algunas cosas interesantes como análisis estático para ver qué módulos deben importarse y crea compilaciones que están más optimizadas para su aplicación. (Hay limitaciones, por supuesto, probablemente no puede analizar el requerimiento ('bla' + variable)) incluso puede intercambiar partes que requieren una capa de emulación para cosas dependientes de node.js. sí, genera una compilación js que ahora puedo incluir en mi navegador.

Estas son algunas de las cosas que puede hacer browserify https://github.com/substack/node-browserify#usage

Parece que todo va muy bien hasta ahora ... pero hay algunos puntos que pensé que valía la pena considerar. Pasamos a una "arquitectura de navegación".

  • es necesario que haya un cambio de mentalidad para los desarrolladores de three.js (el sistema de módulo obligatorio debe usarse, por supuesto)
  • se puede construir una capa de compatibilidad para que los usuarios de three.js puedan seguir usando three.js de la forma anterior sin cosechar los beneficios modulares
  • para poder producir compilaciones optimizadas, los usuarios de three.js tendrían que pasar al sistema requerido
  • El nuevo proceso de compilación probablemente involucraría la cadena de herramientas de browserify (actualmente podríamos usar python, node.js o simplemente copiar y pegar, etc.) o algunas herramientas requireJS.
  • si quisiéramos que three.js fuera realmente más modular, con versiones de cada componente, como por ejemplo TrackballControls, tendríamos que dividirlos, y eso puede conducir a la fragmentación
  • eso también podría conducir a la diversidad, sin embargo, una fortaleza de three.js actualmente parece ser un punto centralizado de muchas extensiones

Entonces, si vemos esta diversidad y la conveniente carga de módulos (principalmente en el ecosistema npm) junto con compilaciones personalizadas, algo bueno, entonces podría valer la pena hacer un cambio de paradigma, refactorizar el código y cambiar nuestro sistema de compilación actual.

@mrdoob, algunas herramientas relacionadas con browserify se enumeran aquí: https://github.com/substack/node-browserify/wiki/browserify-tools.

con respecto a three.min.js , no usaría el código minificado en su proyecto. todo lo que haces es var three = require('three') en tu project.js y luego ejecutar browserify project.js > bundle.js && uglifyjs bundle.js > bundle.min.js . nota: aún puede enviar código minificado por <script src="min.js"> .

Actualmente estoy envolviendo three.js con

if ('undefined' === typeof(window))
  var window = global && global.window ? global.window : this
var self = window

y

module.exports = THREE

luego envuelvo extensiones con

module.exports = function(THREE) { /* extension-code here */ }

entonces puedo requerirlo así:

var three = require('./wrapped-three.js')
require('./three-extension')(three)

así que esto no es óptimo, pero personalmente puedo vivir con eso y pensar que no es tan malo, aunque la propuesta de @kumavis sería una gran ventaja.

pero tal vez tenga sentido bifurcar tres y poner todas las cosas en módulos separados solo para ver cómo funciona.

también consulte http://modules.gl/, que se basa en gran medida en browserify (aunque puede usar cada módulo por sí solo sin browserify).

@mrdoob @ shi-314 gulp-browserify ha sido incluido en la lista negra a favor de usar browserify directamente (es decir, a través de vinyl-source-stream).

Herramientas como grunt / gulp / etc.están en constante cambio, y encontrará muchas opiniones diferentes. Al final, no importa cuál elijas o si lo haces con un script personalizado. Las preguntas más importantes son: ¿cómo consumirán los usuarios ThreeJS y cuánta compatibilidad con versiones anteriores desea mantener?

Después de pensarlo un poco más, creo que será _realmente_ difícil modularizar todo sin refactorizar por completo el marco y su arquitectura. A continuación se presentan algunos problemas:

  • Todo el código del espacio de nombres tiene que cambiar a CommonJS export / require. Esta es una empresa bastante grande y habría muchos ../../../math/Vector2 etc.
  • En un mundo ideal, la biblioteca estaría fragmentada, por lo que three-scene se desacoplaría de three-lights etc. Luego, puede versionar cada paquete por separado. Este tipo de fragmentación parece poco realista para un marco tan grande como ThreeJS, y sería un dolor de cabeza mantenerlo.
  • Si no estamos fragmentando el marco en componentes diminutos, el control de versiones semántico será una pesadilla. Un pequeño cambio rotundo en cualquier parte del marco necesitaría un aumento importante de la versión para todo. Y consumir la API sería bastante feo: require('three/src/math/Vector2')

¿Mi sugerencia? Consideramos dos cosas en el futuro:

  1. Empieza pequeño; extraiga algunas características esenciales y reutilizables como Vector / Quaternion, conversiones de color, triangulación, etc. Estas cosas son buenas candidatas para NPM ya que son útiles fuera del alcance de ThreeJS. También pueden tener su propio conjunto de pruebas, control de versiones y seguimiento de problemas.
  2. Cuando sea necesario agregar un nuevo código a ThreeJS, como una nueva característica o una dependencia (por ejemplo, poly2tri / Tess2), considere sacarlo como un módulo separado y depender de él a través de NPM.

Me encantaría ver todo modularizado, pero no estoy seguro de un enfoque realista para ThreeJS. Tal vez alguien debería hacer algunos experimentos en una bifurcación para ver qué tan factibles son las cosas.

¡Gracias por las explicaciones chicos!

Lo que temo es complicar las cosas a las personas que recién comienzan. Obligarlos a aprender estas cosas de browserify / modules puede no ser una buena idea ...

Tendría que estar de acuerdo con @mrdoob aquí. Yo, y muchos de mis colegas, no somos programadores web (más bien, TD de VFX / animación). Obtener WebGL y Three ciertamente ha sido suficiente trabajo, ya que además de nuestra carga de trabajo actual (y en algunos casos, algunos de nosotros tuvimos que aprender js en el acto). Mucho de lo que he leído en este hilo, a veces, me hace estremecer al pensar en cuánto trabajo más se agregaría a mi plato si Tres se mudara a esta estructura. Podría estar equivocado, pero eso es ciertamente lo que me dice.

Con una compilación de UMD precompilada ( browserify --umd ) en el repositorio, no hay cambios en el flujo de trabajo para los desarrolladores existentes.

@mrdoob La idea de un sistema de gestión de dependencias es la simplicidad. Leer docenas de publicaciones sobre opciones y sistemas de construcción puede ser abrumador, pero en última instancia, el sistema actual no es sostenible. Cada vez que un archivo depende de otro, es una búsqueda y una búsqueda que cualquier desarrollador nuevo debe realizar para encontrar una referencia. Con browserify, la dependencia es explícita y hay una ruta al archivo.

@repsac Un sistema de dependencia debería hacer que Three sea más accesible para los usuarios de otros lenguajes, ya que evita el alcance global, carga pesadillas en el orden y sigue un paradigma similar a otros lenguajes populares. var foo = require('./foo'); es (loosly) similar a C # 's using foo; o de Java import foo;

Me encantaría ver todo modularizado, pero no estoy seguro de un enfoque realista para ThreeJS. Tal vez alguien debería hacer algunos experimentos en una bifurcación para ver qué tan factibles son las cosas.

Creo que este es el camino a seguir, de verdad. Haga el trabajo, muestre cómo funciona.

Y consumir la API sería bastante ugly: require('three/src/math/Vector2')

Como experimento, acabo de convertir el "inicio" de los Tres documentos a este nuevo enfoque modular. Me imagino que habrá muchas referencias a menos que la gente sea bastante estricta a la hora de dividir su código en pequeños módulos.

La principal ventaja de hacer esto sería que el tamaño de compilación resultante sería una pequeña fracción del tamaño de Three.js completo porque solo incluiría las cosas a las que se hace referencia específicamente aquí y luego las cosas de las que esas cosas dependen.

Supongo que hacer referencia a todas las dependencias que necesita (e instalarlas todas individualmente) podría resultar un poco terrible en la práctica.

Si está apuntando explícitamente a dispositivos móviles, entonces este enfoque altamente granular sería perfecto, pero en realidad sospecho que necesitaremos paquetes que exporten las TRES api completas que funcionarán normalmente, luego paquetes más pequeños que encapsulen toda la geometría adicional, todos los renderizadores, todas las matemáticas, todos los materiales, etc., luego hasta el nivel de módulo individual para que los desarrolladores puedan decidir por sí mismos.

Y sí, codificar para la web es un fastidio.

De todos modos, adelante con el experimento ...

Instala nuestras dependencias ...

npm install three-scene three-perspective-camera three-webgl-renderer three-cube-geometry three-mesh-basic-material three-mesh three-raf

Escribe nuestro código ...

// import our dependencies..
var Scene = require('three-scene'),
  Camera = require('three-perspective-camera'),
  Renderer = require('three-webgl-renderer'),
  CubeGeometry = require('three-cube-geometry'),
  MeshBasicMaterial = require('three-mesh-basic-material'),
  Mesh = require('three-mesh'),
  requestAnimationFrame = require('three-raf');

// set up our scene...
var scene = new Scene();
var camera = new Camera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new Renderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// create the cube...
var geometry = new CubeGeometry(1, 1, 1);
var material = new MeshBasicMaterial({color: 0x00ff00});
var cube = new Mesh(geometry, material);
scene.add(cube);
// position the camera...
camera.position.z = 5;
// animate the cube..
var render = function () {
  requestAnimationFrame(render);
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  renderer.render(scene, camera);
};
// begin!
render();

luego construye nuestro archivo

browserify entry.js -o scripts/hello-world.js

luego inclúyelo en nuestra página

<script src="/scripts/hello-world.js" type="text/javascript"></script>

Supongo que hacer referencia a todas las dependencias que necesita (e instalarlas todas individualmente) podría resultar un poco terrible en la práctica.

El usuario final no necesita necesariamente usar browserify en su proyecto para que Three use browserify para administrar su base de código. Se pueden exponer tres como el THREE global tal como está ahora ... incluir el archivo de compilación y ejecutarlo.

@repsac @mrdoob los cambios serían compatibles con versiones anteriores, por lo que los usuarios actuales no necesitan cambiar nada si no quieren. Estas sugerencias son para mejorar la capacidad de mantenimiento a largo plazo y la longevidad del extenso y monolítico código base de ThreeJS. Cosas como la dependencia y la administración de versiones pueden parecer un dolor de cabeza para los no iniciados, pero son increíbles para quienes desarrollan herramientas, marcos, complementos y sitios web a gran escala sobre ThreeJS.

es decir, el código de usuario final aún puede verse igual, y el examples no necesita cambiar en absoluto:

<script src="three.min.js"></script>

<script>
var renderer = new THREE.WebGLRenderer();
</script>

Para los desarrolladores más ambiciosos que buscan una compilación modular, _o_ para aquellos que buscan desarrollar soluciones a largo plazo además de ThreeJS (es decir, y aprovechar la gestión de versiones / dependencias), podría parecerse más a esto:
npm install three-vecmath --save

Luego, en código:

var Vector2 = require('three-vecmath').Vector2;

//.. do something with Vector2

Y, además, esto permite a las personas usar cosas como las matemáticas vectoriales de ThreeJS, las conversiones de color, la triangulación, etc. fuera del alcance de ThreeJS.

Aunque creo que el desastre require () es una mala idea y una mala compensación, sería una idea aún peor exponer a los usuarios a dos tipos diferentes de código three.js, diciéndoles a los usuarios que uno es el tipo de sistema de módulo elegante y otro es el Tipo de sistema de módulo más simple (pero de segunda clase).

@erno Creo que te has perdido el punto, three.js estaría organizado por una estructura de módulo internamente, pero eso se usa para producir un archivo de compilación no diferente de la configuración actual.

La principal ventaja es mejorar la experiencia de desarrollar y mantener three.js .

@kumavis - no @erno en realidad no se perdió eso, pero entendí (*) que él señala que si three.js veces se usa a través de require y otras no, puede ser confuso. Por ejemplo, alguien mira las tres fuentes y luego algunos ejemplos de terceros y encuentra diferencias en cómo es y funciona todo.

(*) hablamos de esto en el irc hoy.

Creo que ese es un tipo de punto válido, pero no estoy seguro de si o cómo funciona al final, si realmente es un problema con los usos del módulo y la construcción. Pero ciertamente parece que vale la pena pensarlo y, en general, me ha parecido bien que el asunto general se haya considerado aquí cuidadosamente, gracias por la información y las opiniones hasta ahora de mi parte.

@antont ya veo. La gente ha sugerido una variedad de enfoques diferentes aquí, asumí que proporcionaríamos principalmente documentación para el uso de nivel superior (sacando todo de THREE ), pero otros pueden crear ejemplos que no seguirían esto y es posible que dar lugar a cierta confusión. Y esa es una preocupación válida.

Creo que estaba un poco confundido por el idioma.

y otro es el tipo de sistema de módulos más simple (pero de segunda clase).

Esto solo se refiere al archivo de compilación, ¿no?

A mi entender, sí. No puedo imaginar qué más, pero puede faltar algo.

antont, kumavis: Las propuestas aquí han hablado de exponer el código de estilo require () a los usuarios finales también, ver eg. comentario más reciente de mattdesl.

"Para desarrolladores más ambiciosos que buscan una compilación modular, o para aquellos que buscan desarrollar soluciones a largo plazo sobre ThreeJS (es decir, y aprovechar la gestión de versiones / dependencias) [...]"

una forma de tener compilaciones más optimizadas es en realidad tener un script que descubra sus dependencias automáticamente y produzca los módulos requeridos.

en este momento, Bower & Browserify cumple con los requisitos, pero no son las únicas soluciones. No sé si hay otros proyectos de código abierto listos para usar que hagan eso (tal vez como dependencias ng) pero he escrito tales herramientas antes y creo que habría otros enfoques para resolver estos problemas.

¿El compilador de cierre de Google podría ser una herramienta de este tipo?

Por parte del usuario, ¿esto podría ser de alguna ayuda?
http://marcinwieprzkowicz.github.io/three.js-builder/

eso es bastante interesante @erichlof :) @marcinwieprzkowicz generó esto a mano ... https://github.com/marcinwieprzkowicz/three.js-builder/blob/gh-pages/threejs-src/r66/modules.json

Una práctica que usa three.js y que dificulta su uso en entornos commonjs es el uso de instanceof: https://github.com/mrdoob/three.js/blob/master/src/core/Geometry.js#L82

Esto se debe a que en una aplicación a menudo termina con diferentes versiones de la misma biblioteca en su árbol de fuentes, por lo que la verificación de instanceof no funciona entre diferentes versiones de la misma biblioteca. Sería bueno en preparación para un cambio a un sistema de módulos commonjs para reemplazar esas instancias de comprobaciones con verificación de características detrás de una interfaz de estilo Geometry.isGeometry (geom).

en git / three.js / src:

grep -r instanceof . | wc -l 
164

en git / three.js / examples:

grep -r instanceof . | wc -l 
216

por lo que hay un total de 380 usos de instanceof en three.js. ¿Cuál sería la mejor implementación como reemplazo?

Recientemente agregué una propiedad type que se puede usar para reemplazar la mayoría de ellas.

Recientemente agregué una propiedad de tipo que se puede usar para reemplazar la mayoría de ellos.

¡bonito! Preparará un PR.

Para ver un ejemplo de cómo se maneja esto en otra biblioteca JS grande y popular, eche un vistazo a https://github.com/facebook/react . El código base está estructurado usando el sistema de módulos basado en estilo de nodo (que explora los implementos) pero está construido para su lanzamiento usando grunt. Esta solución es flexible para 3 casos de uso.

  1. El consumidor puro de Three.js escribiendo vanilla JS todavía puede usar el archivo de compilación como siempre. No hay cambios para este caso de uso.
  2. El consumidor de Three.js que usa browserify puede declarar Three.js como una dependencia en un proyecto y solo require dependencias específicas. Los beneficios de una gestión adecuada de la dependencia están bien documentados.
  3. Contribuir a Three.js ahora debería ser más simple, ya que las dependencias entre componentes están documentadas explícitamente.

Investigué un poco ...

Ayer pirateé un script (bastante estúpido) que transforma el código fuente Three.js para usar declaraciones CommonJS require() para declarar dependencias entre archivos. Solo para ver qué pasa ... Esto:

  1. Termina teniendo declaraciones de requerimiento bastante ridículas como esta (de WebGLRenderer):

var THREE = require('../Three.js'); require('../math/Color.js'); require('../math/Frustum.js'); require('../math/Matrix4.js'); require('../math/Vector3.js'); require('./webgl/WebGLExtensions.js'); require('./webgl/plugins/ShadowMapPlugin.js'); require('./webgl/plugins/SpritePlugin.js'); require('./webgl/plugins/LensFlarePlugin.js'); require('../core/BufferGeometry.js'); require('./WebGLRenderTargetCube.js'); require('../materials/MeshFaceMaterial.js'); require('../objects/Mesh.js'); require('../objects/PointCloud.js'); require('../objects/Line.js'); require('../cameras/Camera.js'); require('../objects/SkinnedMesh.js'); require('../scenes/Scene.js'); require('../objects/Group.js'); require('../lights/Light.js'); require('../objects/Sprite.js'); require('../objects/LensFlare.js'); require('../math/Matrix3.js'); require('../core/Geometry.js'); require('../extras/objects/ImmediateRenderObject.js'); require('../materials/MeshDepthMaterial.js'); require('../materials/MeshNormalMaterial.js'); require('../materials/MeshBasicMaterial.js'); require('../materials/MeshLambertMaterial.js'); require('../materials/MeshPhongMaterial.js'); require('../materials/LineBasicMaterial.js'); require('../materials/LineDashedMaterial.js'); require('../materials/PointCloudMaterial.js'); require('./shaders/ShaderLib.js'); require('./shaders/UniformsUtils.js'); require('../scenes/FogExp2.js'); require('./webgl/WebGLProgram.js'); require('../materials/ShaderMaterial.js'); require('../scenes/Fog.js'); require('../lights/SpotLight.js'); require('../lights/DirectionalLight.js'); require('../textures/CubeTexture.js'); require('../lights/AmbientLight.js'); require('../lights/PointLight.js'); require('../lights/HemisphereLight.js'); require('../math/Math.js'); require('../textures/DataTexture.js'); require('../textures/CompressedTexture.js');

Necesitaríamos una refactorización importante, tal vez dividiendo WebGLRenderer (y demás) en varios módulos (atm, el archivo tiene más de 6000 líneas).

  1. Necesitamos encontrar una solución para los fragmentos GLSL. Atm, esos archivos se compilan en THREE.ShaderChunk en tiempo de compilación y luego en THREE.ShaderLib en tiempo de ejecución (uniéndose a matrices de THREE.ShaderChunk s), lo cual es bastante complicado de hacer solo con browserify. Supongo que requeriría una transformación de browserify que haga lo mismo.

React.js usa commoner para buscar sus módulos sin tener que referirse a ellos por la ruta del archivo. Quizás podríamos hacer lo mismo y también definir reglas personalizadas que nos permitan require GLSL transformándolos a la sintaxis JS.

@rasteiner, es posible que esté muy feliz de conocer https://github.com/stackgl/glslify , proviene de la creciente familia http://stack.gl

He tenido un poco de experiencia con módulos y el enfoque "unixy" en los últimos meses y mi pensamiento ahora es que es muy poco y demasiado tarde, y refactorizar threejs para modularidad o módulos npm sería un objetivo poco realista.

Esto es lo que estoy haciendo actualmente para abordar el problema de la modularidad / reutilización:

  • Estoy poniendo algunos sombreadores reutilizables para difuminar / fxaa / etc en NPM. Mira esto:
    https://www.npmjs.org/package/three-shader-fxaa (que utiliza glsl-fxaa independiente del motor)
  • Los componentes reutilizables como OrbitController y EffectComposer también se publican según sea necesario. P.ej:
    https://www.npmjs.org/package/three-orbit-controls
  • en lugar de depender de "tres", estos módulos exportan una función que toma TRES y devuelve la clase de utilidad. De esta forma, se puede utilizar con threejs globales o commonjs.
  • el control de versiones es una molestia. Estoy tratando de alinear mis versiones principales con los números de lanzamiento de threejs. Cada nueva versión de threejs introduce muchos cambios importantes, por lo que deberá manejarse con cuidado.
  • Los módulos que se ocupan de las matemáticas deben operar en matrices y usar modulares gl-vec3, gl-mat4, etc. de esta manera son genéricos y útiles fuera de threejs. Luego, los usuarios de threejs solo tendrán que manejar el empaquetado / desenvuelto en matrices. Ver verlet-system, simplify-path, etc.
  • si necesito una función webGL verdaderamente modular o pequeña, estoy usando stackgl / glslify en el futuro. Estos también pueden funcionar dentro de ThreeJS siempre que restablezca el estado GL. Por ejemplo: https://www.npmjs.org/package/gl-sprite-text

Mis nuevos proyectos tienden a usar "tres" en npm solo para comenzar a funcionar. Sería bastante impresionante si ThreeJS publicara oficialmente la compilación en npm usando etiquetas de versión que se alinean con los números de lanzamiento.

PD: para aquellos interesados ​​en incorporar sombreadores modulares / reutilizables a su flujo de trabajo:
https://gist.github.com/mattdesl/b04c90306ee0d2a412ab

Enviado desde mi iPhone

El 20 de noviembre de 2014, a las 7:42 a.m., aaron [email protected] escribió:

@rasteiner, es posible que esté muy feliz de conocer https://github.com/stackgl/glslify , proviene de la creciente familia http://stack.gl

-
Responda a este correo electrónico directamente o véalo en GitHub.

En caso de que ayude a otros que podrían estar buscando cómo usar Three.js con browserify y tropiecen con este hilo, la forma en que lo acabo de configurar es usar browserify-shim .

Siguiendo la sección README sobre _ "A veces a) Expondrás variables globales a través de global" _, incluí una etiqueta de script separada para Three.js y la configuré para exponer la variable THREE global.

Y luego lo único que tuve que resolver fue cómo incluir extras como ColladaLoader, OrbitControls, etc. Hice esto así:

Desde package.json:

    "browser": {
        "three": "bower_components/threejs/build/three.js"
    },
    "browserify-shim": "browserify-shim-config.js",
    "browserify": {
        "transform": [ "browserify-shim" ]
    }

browserify-shim-config.js:

    module.exports = {
        "three": { exports: "global:THREE" },
        "./vendor/threejs-extras/ColladaLoader.js": { depends: {"three": null}, exports: "global:THREE.ColladaLoader" },
        "./vendor/threejs-extras/OrbitControls.js": { depends: {"three": null}, exports: "global:THREE.OrbitControls" }
    };

Luego, en mi propio script, main.js:

    require('../vendor/threejs-extras/ColladaLoader.js');
    require('../vendor/threejs-extras/OrbitControls.js');

    var loader = new THREE.ColladaLoader(),
        controls = new THREE.OrbitControls(camera);
...

Browserify requiere reconstruir todo el script cuando modifica incluso en bytes. Una vez utilizo browserify para empaquetar un proyecto que requiere THREE.js, luego toma más de dos segundos construir un límite y bloquea la recarga en vivo cada vez que hago un cambio. Eso es muy frustrante.

Normalmente usas watchify durante el desarrollo con livereload. Ese construye el paquete de forma incremental.

watchify no funciona para mí. Cuando cambio un archivo y lo guardo, watchify y la recarga en vivo de beefy muestran la versión anterior / en caché. No tengo idea de por qué sucede esto. Afortunadamente, browserify ya funciona bastante bien.

@ChiChou Pass en --noparse=three para navegar. Esto lleva el paso del paquete browserify de 1000ms a 500ms en mi máquina, lo cual es lo suficientemente decente para una sensación de retroalimentación instantánea.

@rasteiner Quiero agradecerle nuevamente por su investigación informal sobre las interdependencias de three.js. Si bien esa lista masiva de departamentos es un código de aspecto feo, en realidad esa fealdad está presente tal como está, simplemente invisible. La fortaleza de Browserify es que nos obliga a ventilar nuestra ropa sucia y buscar sistemas menos enredados.

Hay muchos lugares en Three.js donde tomamos algún objeto, percibimos su tipo y realizamos diferentes tareas basadas en ese tipo. En la mayoría de esos casos, ese código dependiente del tipo podría trasladarse al tipo en sí, y no tendríamos que comprender todos los tipos posibles con los que estamos operando.

El siguiente es un ejemplo abreviado de WebGLRenderer :

if ( texture instanceof THREE.DataTexture ) {
  // ...
} else if ( texture instanceof THREE.CompressedTexture ) {
  // ...
} else { // regular Texture (image, video, canvas)
  // ...
}

debería ser más de la forma

texture.processTexImage( _gl, mipmaps, otherData )

Deje que el tipo determine cómo manejarse. Esto también permite al consumidor de bibliotecas utilizar nuevos tipos de texturas en los que no habíamos pensado. Esta estructura debería reducir la interdependencia.

Creo que pasar a una arquitectura Browify es definitivamente el camino a seguir. La compilación de UMD facilitará el consumo de THREE.js. También nos permitirá dividir WebGLRenderer en varios archivos, porque en este momento parece bastante monolítico y aterrador.

Comencé una rama en la que actualmente estoy trabajando para moverla aquí: https://github.com/coballast/three.js/tree/browserify-build-system

Por favor dejame saber lo que tu piensas.

Aquí están los cambios de @coballast .

Parece que está adoptando el enfoque de conversión automática con su archivo browserifyify.js , que creo que es el camino correcto.

Una cosa que todavía no hemos discutido mucho es cuál es la mejor manera de hacer la transición de esta biblioteca grande y en constante cambio a la navegación. Puede realizar los cambios y luego abrir un PR, pero quedará obsoleto de inmediato. Eso es lo convincente del enfoque automatizado.

Si podemos:

  • proporcione un script de conversión de three.js src (como su browserifyify.js )
  • proporcionar un documento que explique cómo funciona el proceso de conversión
  • proporcionar un documento que explique cómo funciona el nuevo sistema de compilación
  • ejecutar pruebas después de la conversión
  • no incluir ningún cambio en los archivos existentes que pueda dar lugar a un conflicto de fusión

... entonces podemos convertir esto en una conversión de botón que aún funcionará en el futuro previsible. esa simplicidad permite que esta noción de ensueño de un cambio arquitectónico fundamental a un proyecto tan grande se lleve a cabo cuando los argumentos ideológicos triunfan.

@coballast con ese fin, eliminaría los cambios en src / Three.js si funciona de la misma manera.

Nota: no solo revertir, sino eliminar esos cambios del historial de la rama a través de una nueva rama o un empuje forzado

@coballast Me pregunto si tendría más sentido que la utilidad de conversión no sea una bifurcación de three.js , sino una utilidad externa que apunta al directorio de desarrollo three.js , y convierte la fuente archivos, agrega un script de compilación y ejecuta las pruebas.

@kumavis Estoy de acuerdo con dejar solo el directorio src. Creo que lo que hay que hacer es hacer que la utilidad escriba un directorio duplicado con el código commonjs, y podemos probar y ejecutar la compilación de browserify desde allí para asegurarnos de que todos los ejemplos funcionen antes de intentar hacer algo importante.

También hay una oportunidad interesante aquí para escribir algunas cosas de análisis estático que verifiquen y apliquen automáticamente un estilo consistente en todo el código base.

@coballast suena genial.
Existe una gran cantidad de herramientas para la reescritura automática de código, por ejemplo, escodegen . Necesitamos asegurarnos de que mantenemos comentarios, etc.
¿Quiere iniciar un repositorio de utilidad de conversión de threejs?

@coballast dicho esto, mantener un enfoque nítido para esta utilidad es importante

@kumavis Considérelo hecho. Realmente quiero que esto suceda. Este es solo uno de los dos proyectos en los que estoy trabajando en este momento.

@kumavis @mrdoob Parte de la discusión aquí parece girar en torno a la idea de dividir TRES en múltiples módulos separados que presumiblemente podrían instalarse a través de npm y luego compilarse con browserify. No estoy totalmente en contra de la idea, pero creo que debería ser un tema aparte. Lo único que estoy defendiendo en este momento es usar browserify para administrar la base de código de THREE internamente. Haga que se mueva y que funcione de la misma manera que siempre lo ha hecho para los usuarios, y luego evalúe lo que tiene sentido.

Tendré curiosidad por ver cuál es la salida de esa utilidad ^^

@coballast enlaza un repositorio para que podamos rastrearlo, incluso si está vacío en este momento. Podemos construir a partir de ahí.

https://github.com/coballast/threejs-browserify-conversion-utility

El código es un desastre, se limpiará pronto.

¡Aquí vamos! :cohete:

Ahora tengo la utilidad en un estado en el que genera browserify src y la construiré sin problemas. Actualizaré el repositorio con instrucciones sobre cómo hacerlo usted mismo. En este punto, los ejemplos no funcionan. Hay varios problemas que deben resolverse para solucionarlo. Los agregaré al repositorio si alguien quiere arremangarse y ayudar.

@coballast sí, por favor presente los problemas como TODOs, y yo u otros participaremos como podamos.

Han surgido problemas graves. Ver # 6241

Aquí está mi análisis de lo que debería suceder para que esto funcione: https://github.com/coballast/threejs-browserify-conversion-utility/issues/9#issuecomment -83147463

browserify es, como mínimo, un transporte redundante (conjuntivo) debido a su diseño. Esto hace que su costo de uso sea inflable (¿plan de datos alguien?) Y lento.

Un remedio simple para esto es separar el documento del código de la biblioteca, lo que implicaría dos archivos de cliente y no uno. Esta es una práctica común para los js del lado del cliente.

Si al principio browserify es defectuoso y necesita ser reparado, difícilmente puedo ver por qué debería considerarse para mejorar algo y mucho menos algo como threejs.

@spaesani Porque los datos de threejs deben descargarse de todos modos. Si dividimos threejs en módulos más pequeños y dejamos que un sistema de compilación automatizado elija lo que necesita para una sola aplicación, en realidad la mayoría de las aplicaciones de threejs serían más ligeras.

Si por alguna razón aún desea separar el "documento del código de la biblioteca", aún puede hacer esto y usar una versión prediseñada como lo hacemos ahora. Incluso puede dividir su aplicación integrada en browserify en módulos separados usando los indicadores --standalone y --exclude .

Browserify es solo una forma de usar una API de definición de módulo probada en batalla (CommonJS) en el navegador. Simplificaría enormemente el desarrollo de complementos de threejs y mejoraría la claridad del código y, por lo tanto, la productividad, nos permitiría integrarnos en un ecosistema más grande (npm) donde el código es inherentemente mantenido por más personas mientras se mantiene la integridad a través del sistema de control de versiones (piense en la familia stackgl ), y ni siquiera obligaría a las personas a ingresar a CommonJS si no lo quieren.

Por supuesto que hay desventajas, pero no son las que mencionaste.

three.js y three.min.js se pueden almacenar en caché para ahorrar en el transporte (datos) mediante un proxy, la solución móvil común o un navegador de almacenamiento en caché.
En el momento en que selecciona y agrega el código threejs con el código específico del documento, el almacenamiento en caché no es factible.
Si browserify le permite a uno

Temas relacionados

jlaquinte picture jlaquinte  ·  3Comentarios

clawconduce picture clawconduce  ·  3Comentarios

fuzihaofzh picture fuzihaofzh  ·  3Comentarios

akshaysrin picture akshaysrin  ·  3Comentarios

donmccurdy picture donmccurdy  ·  3Comentarios