一个优势是,这将为three.js 的持续开发强制实施模块化架构。
node/browserify 中的常见样式是每个文件在顶部声明其依赖项,并将全局变量视为反模式。
这是一个示例片段:
// src/geometry/BoxGeometry.js
var Geometry = require('./Geometry.js');
var Vector3 = require('../core/Vector3.js');
module.exports = BoxGeometry;
function BoxGeometry() {
// ...
}
BoxGeometry.prototype = Geometry.prototype;
另一个优点是使用 browserify 的three.js
消费者将能够挑选他们想要的部分。 他们可以只导入Scene
、 BoxGeometry
、 PerspectiveCamera
和WebGLRenderer
,自动获取所有这些的依赖项( Object3D
等),并有一小部分 javascript 支持他们想要的功能集。
这可以通过不施加破坏性更改的方式来完成。 在顶层,我们将导出我们认为是标准包一部分的所有类
// src/three.js
var THREE = { rev: 101 }
module.exports = THREE
THREE.Geometry = require('./geometry/Geometry.js')
THREE.BoxGeometry = require('./geometry/BoxGeometry.js')
// ...
注意:在此示例中,我并不完全需要顶部的依赖项,因为此文件几乎完全是 require 语句。
最后,我们将其包装在通用模块定义中,该window
)。
我们来复习:
three.js
消费者使用 browserify 选择功能这将需要更换构建系统,但新的将非常简单。
其他一些优点:
@shi-314 我想我有点困惑,我觉得You can structure your code
和You can build for production
是您可以在没有架构转变的情况下做的事情吗? 您是在谈论three.js 源代码还是使用three.js 构建的东西?
Three.js 使用的一种使在 commonjs 环境中使用变得棘手的做法是使用instanceof
: https :
这是因为在应用程序中,您的源代码树中经常会出现同一库的不同版本,因此检查 instanceof 在同一库的不同版本之间不起作用。 为迁移到 commonjs 模块系统做准备,用Geometry.isGeometry(geom)
样式界面后面的功能检查替换那些 instanceof 检查会很好。
@kumavis我说的是在three.js 中构建的东西。 假设你想用你的着色器等创建你自己的材质。目前你需要扩展全局 THREE 对象以保持与 Three.js 代码的其余部分保持一致:
THREE.MeshMyCoolMaterial = function (...) { ... }
但是如果我们有 Browserify 比你可以做的:
var MeshLambertMaterial = require('./../MeshLambertMaterial');
var MeshMyCoolMaterial = function (...) {...}
所以你的命名空间保持一致,你不需要在你的代码中使用THREE.MeshLambertMaterial
和MeshMyCoolMaterial
。
而对于You can build for production
我的意思基本上和你提到的一样: allows three.js consumers using browserify to pick and choose functionality
。
@shi-314 谢谢你,这更清楚。 这确实影响了我提出的反序列化消费者定义类的通用解决方案:
// given that `data` is a hash of a serialized object
var ObjectClass = THREE[ data.type ]
new ObjectClass.fromJSON( data )
这是我提议的序列化/反序列化重构
https://github.com/mrdoob/three.js/pull/4621
性能不应受到此类更改的影响。
这是一个相当大的变化,但我也赞成。
其他一些主要优点:
standalone
选项为您生成 UMD 构建。 无需手动修改 UMD 包装器。threejs-vecmath
进行重大更改,而不必担心每个人的代码被破坏。 另一方面,如果我们在特定模块中发布补丁或次要版本,使用这些模块的人将能够自动获得更改。npm install threejs-shader-bloom
)require()
应用程序实际使用的模块。致@mrdoob和其他作者; 如果您对 NPM/Browserify 没有太多经验,我建议您用它制作几个小项目并了解它的“哲学”。 它与 ThreeJS 架构非常不同; 它鼓励许多小事情,而不是大框架。
这种方法的另一个优点是可以有一个开源的生态系统,第三方 Three.JS 模块,特别是着色器、几何图形、模型加载器等。 通过 NPM 或 Github/Component 发布,人们可以轻松地引用和使用。 目前,通过主持一个演示来共享内容,然后人们可以在该演示上“查看源代码”。 三.JS值得更好!
我认为 Three.JS 的问题之一是代码与 Three.JS 的当前版本不兼容的速度有多快。 切换到这样的东西的另一个优点是能够指定 Three.JS 的 _bits_ 的特定版本会非常强大和方便。
+1
+1 对于 CommonJS/browserify 架构,它将使核心更轻量级,即使扩展来自第三方也适合
将three.js 分成小模块也有很多成本。 当前系统允许非常简单的第三方插件(例如jetienne 的THREEx 模块)。 关于当前设置的简单性,有很多要说的,只要 JS 模块系统只是构建系统的包装器。
另一种最小化构建大小的方法是 ClojureScript 所做的。 它们遵循一些约定以允许 Google 的 Closure 编译器进行整个程序分析和死代码消除。
+1 表示未被重视且经常被忽视的简洁优雅
+1
将three.js 分成小模块也有很多成本。 当前系统允许非常简单的第三方插件(例如jetienne 的THREEx 模块)。
这里的想法是仍然会为非节点环境提供 UMD 构建。 像 THREEx 这样的插件对于那些依赖于 ThreeJS 和简单的<script>
标签的插件来说也是一样的。
棘手的事情是:如果我们在 CommonJS 环境中,我们如何require()
一个特定的插件? 也许 browserify-shim 可以提供帮助。
关于当前设置的简单性,有很多要说的,只要 JS 模块系统只是构建系统的包装器。
ThreeJS 当前的插件/扩展系统很难使用,而且远非“简单”或容易。 大多数 ThreeJS 项目倾向于使用某种形式的插件或扩展,比如 EffectComposer,或 FirstPersonControls,或模型加载器,或漂浮在examples
文件夹中的其他许多 JS 文件之一。 现在依赖这些插件的唯一方法:
vendor
文件夹中现在,想象一下,使用 browserify 你可以做这样的事情:
var FirstPersonControls = require('threejs-controls').FirstPersonControls;
//more granular, only requiring necessary files
var FirstPersonControls = require('threejs-controls/lib/FirstPersonControls');
这些插件将require('threejs')
以及他们可能需要的任何其他内容(如GLSL 片段或文本三角剖分)。 依赖/版本管理对用户都是隐藏的,不需要手动维护 grunt/gulp concat 任务。
棘手的事情是:如果我们在 CommonJS 环境中,我们如何 require() 一个特定的插件?
我已经将 CommonJS 用于 THREE.js 项目一段时间了。 这是一个有点手动的过程,将其他人的代码块转换为模块,我认为没有一种简单的方法可以避免作者或贡献者未转换的遗留代码。
重要的一点是有一个模块导出整个“标准”三对象,然后任何希望扩展它的东西都可以需要它。
var THREE = require('three');
THREE.EffectComposer = // ... etc, remembering to include copyright notices :)
这对我来说效果很好,尤其是随着项目的发展,我开始将自己的着色器和几何图形添加到他们自己的模块等中。
只要有一个 'threejs-full' 或 'threejs-classic' npm 包,那么这就会成为在 CommonJS 环境中使用旧 Three.js 东西的一种非常可行的方式,但我怀疑这非常小众!
+1
我相信曾经在 npm、plugin 中可以使用碎片化的 Threejs 模块
开发人员会喜欢迁移到 CommonJS 环境。
2014 年 6 月 5 日晚上 9:19,“Charlotte Gore”通知@ github.com 写道:
棘手的事情是:如果我们需要()一个特定的插件
在 CommonJS 环境中?我已经将 CommonJS 用于 THREE.js 项目一段时间了。 有点
手动过程,将其他人的代码块转换为模块
而且我认为对于遗留代码没有一种简单的方法可以避免这种情况
不是由作者或贡献者转换的。重要的一点是有一个模块导出整个“标准”
三个对象,然后可以被任何希望扩展的对象所需要
它。var THREE = require('三');
THREE.EffectComposer = // ... 等等,记得包括版权声明:)这对我来说效果很好,尤其是随着项目的发展,我
开始将我自己的着色器和几何图形添加到他们自己的模块等中。只要有一个 'threejs-full' 或 'threejs-classic' npm 包然后
这成为处理旧 Three.js 东西的一种非常可行的方式
CommonJS 环境,但我怀疑这是非常小众的!—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -45236911。
它还可以使着色器也模块化,例如使用glslify 。 甚至像制作一个按需生成着色器的 Express 中间件这样的事情也变得更容易了。
几个月前,我将frame.js移到了 require.js,我终于明白了 AMD 的东西是如何工作的。
但是,我仍然需要学习如何“编译”它。 从模块列表中生成three.min.js
的工具/工作流程是什么?
我更喜欢gulp.js作为带有-browserify插件的构建系统。 在我看来,它真的很容易理解,而且代码看起来比 grunt 更简洁。 看看这个: http ://travismaynard.com/writing/no-need-to-grunt-take-a-gulp-of-fresh-air :wink:
一些想法:(当然基于我对 node、npm、browserify 的有限经验)
也就是说,在讨论完这个线程之后,我不确定每个人是否对 browserify 有相同的理解(browserify、commonjs、requirejs、amd、umd 有点相关,尽管它们可能不一定是同一件事)。
现在,如果您可以稍微遵循我的思路。
这就是 Browserify 出现的地方。 好吧,技术上可以在浏览器中使用 requireJS。 但是你希望在不进行太多网络调用的情况下将 js 文件捆绑在一起(与快速的文件系统 require() 不同)。 Browserify 在那里做了一些很酷的事情,比如静态分析,以查看需要导入哪些模块并创建更适合您的应用程序的构建。 (当然有限制,它可能无法解析 require('bla' + variable))它甚至可以换出需要 node.js 依赖项的仿真层的部分。 是的,它生成了一个 js 版本,我现在可以将其包含在我的浏览器中。
以下是 browserify 可以做的一些事情https://github.com/substack/node-browserify#usage
听起来到目前为止一切都很好……但我认为有几点值得考虑我们转向“浏览器架构”
因此,如果我们看到这种多样性和方便的模块加载(主要依赖于 npm 生态系统)以及定制构建是一件好事,那么改变范式、重构代码和改变我们当前的构建系统可能值得一试。
@mrdoob这里列出了一些关于https :
关于three.min.js
,您不会在项目中使用缩小的代码。 你所做的只是var three = require('three')
中的project.js
然后运行browserify project.js > bundle.js && uglifyjs bundle.js > bundle.min.js
。 注意:您仍然可以为<script src="min.js">
发送缩小代码。
我目前正在用 Three.js 包装
if ('undefined' === typeof(window))
var window = global && global.window ? global.window : this
var self = window
和
module.exports = THREE
然后我用
module.exports = function(THREE) { /* extension-code here */ }
所以我可以这样要求:
var three = require('./wrapped-three.js')
require('./three-extension')(three)
所以这不是最优的,但我个人实际上可以接受它并认为它不是那么糟糕 - 尽管@kumavis提议将是一个_巨大的_优势。
但也许分叉三个并将所有东西放在单独的模块中只是为了看看它会如何工作是有意义的。
还可以查看http://modules.gl/ ,它在很大程度上基于 browserify(尽管您可以在没有 browserify 的情况下单独使用每个模块)。
@mrdoob @shi-314 browserify已被列入黑名单,支持直接使用 browserify(即通过vinyl-source-stream)。
像 grunt/gulp/etc 这样的工具在不断变化,你会发现很多不同的意见。 最后,您选择哪个并不重要,或者您是否只是使用自定义脚本来完成它。 更重要的问题是:用户将如何使用 ThreeJS,以及您希望保持多少向后兼容性?
经过更多思考,我认为在不完全重构框架及其架构的情况下将所有内容模块化将_真的_很难。 这里有一些问题:
../../../math/Vector2
等。three-scene
将与three-lights
等解耦。然后您可以分别对每个包进行版本控制。 这种碎片化对于 ThreeJS 这么大的框架来说似乎不太现实,维护起来也很麻烦。require('three/src/math/Vector2')
我的建议? 我们考虑向前推进的两件事:
我很想看到所有东西都模块化,但我不确定是否有一种适合 ThreeJS 的方法。 也许有人应该在叉子上做一些实验,看看事情的可行性。
谢谢各位大佬的解释!
我害怕让刚开始的人把事情复杂化。 强迫他们学习这个 browserify/modules 的东西可能不是一个好主意......
在这里必须同意@mrdoob 。 我和很多同事都不是网络程序员(而是 VFX/动画 TD)。 除了我们当前的工作量之外,学习 WebGL 和 Three 肯定已经足够了(在某些情况下,我们中的一些人不得不当场学习 js)。 我在这个线程中读到的大部分内容有时让我不寒而栗,想到如果三搬到这个结构,我的盘子里会增加多少工作。 我可能是错的,但这对我来说肯定是这样。
使用 repo 中的预编译 UMD ( browserify --umd
) 构建,现有开发人员的工作流程没有变化。
我很想看到所有东西都模块化,但我不确定是否有一种适合 ThreeJS 的方法。 也许有人应该在叉子上做一些实验来看看事情的可行性
我认为这是要走的路,真的。 完成工作,展示它是如何工作的。
并且使用 API 会非常好
ugly: require('three/src/math/Vector2')
作为一项实验,我刚刚将“入门”从三个文档转换为这种新的模块化方法。 我可以想象会有很多引用,除非人们非常严格地将他们的代码分解成小模块。
这样做的主要优点是生成的构建大小将是完整 Three.js 大小的一小部分,因为您将只包含此处特别引用的内容以及这些内容所依赖的内容。
我想引用您需要的所有依赖项(并单独安装它们)在实践中可能会证明有点太糟糕了。
如果您明确针对移动设备,那么这种高度细化的方法将是完美的,但实际上我怀疑我们需要导出整个三个 api 的包,这些包将正常工作,然后是封装所有奖励几何的较小包,所有渲染器、所有数学、所有材料等,然后向下到单个模块级别,以便开发人员可以自己决定。
是的,为网络编码是一种痛苦。
无论如何,继续实验......
安装我们的依赖项..
npm install three-scene three-perspective-camera three-webgl-renderer three-cube-geometry three-mesh-basic-material three-mesh three-raf
编写我们的代码...
// 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();
然后构建我们的文件
browserify entry.js -o scripts/hello-world.js
然后将其包含在我们的页面中
<script src="/scripts/hello-world.js" type="text/javascript"></script>
我想引用您需要的所有依赖项(并单独安装它们)在实践中可能会证明有点太糟糕了。
最终用户不一定需要在他们的项目中使用 browserify,以便 Three 使用 browserify 来管理其代码库。 三个可以作为全局THREE
公开,因为它现在......包括构建文件并使用它运行。
@repsac @mrdoob更改将向后兼容,因此如果当前用户不想更改,则无需更改任何内容。 这些建议是为了提高 ThreeJS 庞大而单一的代码库的长期可维护性和寿命。 依赖和版本管理之类的事情对于初学者来说可能听起来很头疼,但对于那些在 ThreeJS 之上开发工具、框架、插件和大型网站的人来说,它们非常棒。
即最终用户代码看起来仍然相同,并且examples
根本不需要更改:
<script src="three.min.js"></script>
<script>
var renderer = new THREE.WebGLRenderer();
</script>
对于正在寻找模块化构建的雄心勃勃的开发人员,_或_对于那些希望在 ThreeJS 之上开发长期解决方案的开发人员(即并利用版本/依赖项管理),它可能看起来更像这样:
npm install three-vecmath --save
然后,在代码中:
var Vector2 = require('three-vecmath').Vector2;
//.. do something with Vector2
此外,这允许人们在 ThreeJS 的范围之外使用 ThreeJS 的向量数学、颜色转换、三角剖分等。
尽管我认为 require() 混乱是一个坏主意和不好的权衡,但让用户接触两种不同类型的three.js 代码将是一个更糟糕的主意,告诉用户一种是花哨的模块系统风格,另一种是更简单(但二等)的模块系统风格。
@erno我认为您已经错过了这一点, three.js
将在内部由模块结构组织,但这用于生成与当前设置没有区别的构建文件。
主要的收获是改进了开发和维护three.js
的体验。
@kumavis - 不, @erno实际上并没有错过这一点,但我明白(*)他指出,如果three.js
有时通过 require 使用,有时不使用,这可能会令人困惑。 例如,有人同时查看三个来源,然后查看一些 3rd 方示例,并在这一切和工作方式方面遇到差异。
(*)我们今天早些时候在irc上讨论过这个。
我认为这是一种有效的观点,但我不确定它最终是否/如何解决 - 模块和构建事物的使用是否真的存在问题。 但似乎确实值得一想,总体而言,我认为这里已经仔细考虑了整个问题,这对我来说似乎很好,感谢到目前为止我提供的信息和观点。
@antont我明白了。 人们在这里提出了各种不同的方法,我假设我们将主要提供顶级使用的文档(从THREE
提取所有内容),但其他人可能会创建不遵循此的示例,并且可能导致一些混乱。 这是一个有效的关注。
我想我对语言有点困惑。
另一个是更简单(但二等)的模块系统风格。
这只是指构建文件,是吗?
在我的理解中,是的。 无法想象还有什么,但可能会错过一些东西。
antont, kumavis:这里的提案也谈到了将 require() 风格的代码暴露给最终用户,参见例如。 mattdesl 的最新评论。
“对于正在寻找模块化构建的更有雄心的开发人员,或者对于那些希望在 ThreeJS 之上开发长期解决方案的开发人员(即利用版本/依赖项管理)[...]”
获得更优化构建的一种方法实际上是使用一个脚本来自动确定您的依赖项并生成所需的模块。
现在 bower 和 browserify 没有 require,但它们不是唯一的解决方案。 我不知道是否有其他现成的开源项目可以做到这一点(可能像 ng-dependencies),但我之前写过这样的工具,我认为还有其他方法可以解决这些问题。
谷歌的闭包编译器可能是这样的工具吗?
在用户方面,这可能有帮助吗?
http://marcinwieprzkowicz.github.io/three.js-builder/
这很有趣@erichlof :) 我想知道@marcinwieprzkowicz是否https://github.com/marcinwieprzkowicz/three.js-builder/blob/gh-pages/threejs-src/r66/modules.json
Three.js 使用的一种在 commonjs 环境中使用起来很棘手的做法是使用 instanceof: https :
这是因为在应用程序中,您的源代码树中经常会出现同一库的不同版本,因此检查 instanceof 在同一库的不同版本之间不起作用。 准备迁移到 commonjs 模块系统以用 Geometry.isGeometry(geom) 样式界面后面的功能检查替换那些 instanceof 检查会很好。
在 git/three.js/src 中:
grep -r instanceof . | wc -l
164
在 git/three.js/examples 中:
grep -r instanceof . | wc -l
216
所以在three.js 中总共有380 次使用instanceof
。 作为替代品的最佳实现是什么?
我最近添加了一个type
属性,可以用来替换大部分属性。
我最近添加了一个 type 属性,可以用来替换其中的大部分。
好的! 准备PR。
有关如何在另一个流行的大型 JS 库中处理此问题的示例,请查看https://github.com/facebook/react 。 代码库是使用基于节点样式的模块系统(browserify 实现的)构建的,但它是为使用 grunt 发布而构建的。 此解决方案可灵活用于 3 个用例。
require
特定的依赖项。 适当的依赖管理的好处已被充分证明。我做了一些研究...
昨天我编写了一个(相当愚蠢的)脚本,该require()
语句来声明文件之间的依赖关系。 只是为了看看会发生什么......这个:
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');
我们需要一些重大的重构,可能将 WebGLRenderer(等)拆分为多个模块(atm 文件超过 6000 行)。
THREE.ShaderChunk
,然后在运行时编译为THREE.ShaderLib
(将THREE.ShaderChunk
的数组连接在一起),这对于仅使用 browserify 来说是相当棘手的。 我认为它需要一个执行相同操作的 browserify 转换。React.js 使用commoner来查找它们的模块,而不必通过文件路径来引用它们。 也许我们可以做同样的事情并定义自定义规则,允许我们将require
GLSL 文件转换为 JS 语法。
@rasteiner你可能很高兴了解https://github.com/stackgl/glslify ,它来自不断增长的http://stack.gl家族
在过去的几个月里,我在模块和“unixy”方法方面有相当多的经验,现在我的想法是太少太晚了,为模块化或 npm 模块重构 Threejs 将是一个不切实际的目标。
这是我目前为解决模块化/可重用性问题所做的工作:
我的新项目倾向于在 npm 上使用“三个”来启动和运行。 如果 ThreeJS 使用与版本号一致的版本标签将构建正式发布到 npm,那将是非常棒的。
PS:对于那些有兴趣将可重用/模块化着色器引入他们的工作流程的人:
https://gist.github.com/mattdesl/b04c90306ee0d2a412ab
从我的iPhone发送
在二零一四年十一月二十日,在上午07点42分,阿隆[email protected]写道:
@rasteiner你可能很高兴了解https://github.com/stackgl/glslify ,它来自不断增长的http://stack.gl家族
—
直接回复此邮件或在 GitHub 上查看。
如果它可以帮助可能正在寻找如何将 Three.js 与 browserify 一起使用的其他人,并偶然发现这个线程,我自己设置的方法是使用browserify-shim 。
在 _“您有时会 a) 通过全局公开全局变量”_ 的自述部分之后,我为 Three.js 包含了一个单独的脚本标记,并将其配置为公开全局变量 THREE。
然后我必须自己解决的一点是如何包含诸如 ColladaLoader、OrbitControls 等附加功能。我是这样做的:
从 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" }
};
然后在我自己的脚本 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 需要重建整个脚本。 我曾经用browserify打包了一个需要THREE.js的项目,然后每次修改都需要两秒多的时间来构建boundle并阻塞livereload。 这太令人沮丧了。
您通常在使用livereload进行开发期间使用 watchify。 那个以增量方式构建捆绑包。
watchify 对我不起作用。 当我更改一个文件并保存它时,watchify 和 beefy 的 livereload 会提供旧的/缓存版本。 我不知道为什么会发生这种情况。 值得庆幸的是,browserify 已经运行良好。
@ChiChou 传入--noparse=three
到 browserify。 这使我的机器上的 browserify 捆绑步骤从 1000 毫秒减少到 500 毫秒,这对于即时反馈感觉来说已经足够了。
@rasteiner我想再次感谢您对three.js 相互依赖的非正式研究。 虽然庞大的 deps 列表是一些看起来很丑的代码,但实际上这种丑陋是存在的,只是不可见。 Browserify 的优势在于它要求我们晾晒脏衣服并追求不那么复杂的系统。
Three.js 中有很多地方我们接收某个对象,感知它的类型,并根据该类型执行不同的任务。 在大多数情况下,依赖于类型的代码可以移动到类型本身,我们不必了解我们正在操作的所有可能的类型。
以下是来自WebGLRenderer的节略示例:
if ( texture instanceof THREE.DataTexture ) {
// ...
} else if ( texture instanceof THREE.CompressedTexture ) {
// ...
} else { // regular Texture (image, video, canvas)
// ...
}
应该更多的形式
texture.processTexImage( _gl, mipmaps, otherData )
让类型决定如何处理自己。 这也允许库使用者使用我们没有想到的新颖的纹理类型。 这种结构应该减少相互依赖。
我认为转向 browserify 架构绝对是要走的路。 UMD 构建将使使用 THREE.js 更容易。 它还允许我们将 WebGLRenderer 拆分为多个文件,因为现在它看起来相当单一和可怕。
我已经开始了一个分支,我目前正在将它移到这里: https :
请让我知道你的想法。
这是@coballast的变化。
看起来您正在对browserifyify.js
文件采用自动转换方法,我认为这是正确的方法。
我们还没有讨论过的一件事是如何最好地将这个庞大的、不断变化的库过渡到 browserify。 您可以进行更改,然后打开 PR,但它会立即过时。 这就是自动化方法的引人注目之处。
如果我们可以:
browserifyify.js
)...然后我们可以把它变成一个按钮转换,在可预见的未来仍然有效。 当意识形态争论胜出时,这种简单性使这个梦幻般的基本架构转变为如此大的项目的概念得以实现。
@coballast为此,如果它的工作原理相同,我将删除对 src/Three.js 的更改。
注意:不仅仅是恢复,而是通过新分支或强制推送从分支的历史记录中删除这些更改
@coballast我想知道转换实用程序不是three.js
的分支是否更有意义,而是指向three.js
开发目录的外部实用程序,它会转换源文件,添加构建脚本并运行测试。
@kumavis我同意单独留下 src 目录。 我认为要做的事情是让实用程序使用 commonjs 代码编写一个重复的目录,我们可以从那里测试和运行 browserify 构建,以确保示例在我们尝试做任何重大事情之前都能正常工作。
这里还有一个有趣的机会来编写一些静态分析的东西,这些东西将自动检查并在整个代码库中强制执行一致的风格。
@coballast听起来很棒。
有大量工具可用于自动代码重写,例如escodegen 。 需要确保我们维护评论等。
想要启动一个 Threejs-conversion-utility 仓库吗?
@coballast说,保持对该实用程序的
@kumavis认为它完成了。 我真的希望这发生。 这只是我目前正在进行的两个项目之一。
@kumavis @mrdoob这里的一些讨论似乎是围绕着将三个模块分成多个单独的模块的想法,这些模块大概可以通过 npm 安装,然后用 browserify 编译。 我并不完全反对这个想法,但我认为这应该是一个单独的问题。 在这一点上,我唯一提倡的是使用 browserify 在内部管理 THREE 的代码库。 把它移过来,让它像对用户一样的方式工作,然后评估什么是有意义的。
我很想知道该实用程序的输出是什么^^
@coballast链接一个 repo 供我们跟踪,即使此时它只是空的。 我们可以从那里建造。
开始了! :火箭:
我现在拥有该实用程序处于生成 browserify src 的状态,并且可以毫无问题地构建它。 我将使用有关如何自己执行此操作的说明更新存储库。 在这一点上,这些示例不起作用。 有几个问题需要解决才能解决。 如果有人想挽起袖子并提供帮助,我会将它们添加到回购中。
@coballast是的,请将问题作为尽我们所能。
严重的问题出现了。 见#6241
这是我对需要发生什么才能使其起作用的分析: https :
由于它的设计,browserify 至少是传输冗余(conjestive)。 这使得它的使用成本膨胀(任何人都有数据计划?)而且速度很慢。
对此的一个简单补救措施是将文档与库代码分开,这将需要两个客户端文件而不是一个。 这是客户端 js 的常见做法。
如果一开始 browserify 有问题并且本身需要修复,我几乎不明白为什么它甚至应该被考虑改进任何东西,更不用说像 Threejs 这样的东西了。
@spaesani因为无论如何都必须下载
如果出于某种原因您仍然想将“文档与库代码”分开,您仍然可以这样做并像我们现在一样使用预先构建的版本。 您甚至可以使用--standalone 和 --exclude标志将
Browserify 只是一种在浏览器上使用经过战斗验证的模块定义 API (CommonJS) 的方法。 它将极大地简化 Threejs 插件的开发并提高代码清晰度并因此提高生产力,它将使我们能够集成到更大的生态系统(npm)中,其中代码本质上由更多人维护,同时仍然通过版本控制系统保持完整性(想想stackgl家族),如果人们不想要它,它甚至不会强迫人们使用 CommonJS。
当然有缺点,但它们不是你提到的那些。
Three.js 和three.min.js 可以通过代理、通用移动解决方案或缓存浏览器缓存以节省传输(数据)。
当您挑选并聚合 Threejs 代码与文档特定代码时,缓存是不可行的。
如果 browserify 允许一个
sp
On Mar 28, 2015 1:06 PM, Roman Steiner notifications@github.com wrote:@spaesani Because the data for threejs has to be downloaded anyway. If we split threejs into smaller modules and let an automated build system cherry pick what it needs for a single app, actually most threejs apps out there would be lighter.
If for some reason you still want to separate "document from library code", you could still do this and use a pre-built version like we do now. You could even split your browserify-built app into separate modules by using the --standalone flag.
Browserify is just a way to use a battle proven module definition API (CommonJS) on the browser. It would greatly simplify the development of threejs plugins and enhance code clarity and therefore productivity, it would allow us to integrate into a bigger ecosystem (npm) where the code is inherently maintained by more people while still maintaining integrity through the versioning system, and it wouldn't even force people into CommonJS if they don't want it.
Of course there are downsides, but they're not the ones you've mentioned.
—Reply to this email directly or view it on GitHub.
@spaesani It (
许多移动网络负载问题会通过 http/2 之类的东西得到一定程度的改善。 像这样的问题最好通过修改抽象堆栈中较低的层来解决。 性能问题不应阻止我们遵循软件工程最佳实践,例如模块化/关注点分离等。
我发现这个问题是因为我的团队最近开始使用 jspm。 我们能够导入threejs(我相信这是因为主文件已被浏览器化)。 我想看看是否有人将 Threejs 制作成 es6 模块,主要是因为 jspm 的构建功能(将所有依赖项捆绑到一个文件中,但只抓取使用的依赖项)。
虽然 mrdoob 将 Threejs 的大小保持在 100kb 以下很棒,但我发现我的大多数项目并没有使用太多的代码库(我觉得它是最多的,但我没有试图弄清楚)。 CubeCamera、OrthographicCamera、CanvasRenderer、各种灯光、加载器、曲线、几何图形、助手等。此外,我发现我的大部分项目都包含一些示例脚本。
我希望可以将所有这些模块(通常与 Threejs 绑定的模块以及示例中的模块)放在一个位置,然后简单地导入我需要的模块,然后当我捆绑项目时,结果是在一个比原来的 Threejs 小得多的文件中,即使它包含许多最初没有包含的部分。
我还想补充一点,如果threejs是使用browserify模块构建的,它会增加文件大小的小开销(但无法与r70当前的403kb相比),但也会从代码中删除全局变量THREE的使用,从而允许像 THREE.Geometry 这样的变量被闭包缩小。
我做了一个快速测试,做了一个查找替换来摆脱三个对象(所以它的所有子对象都污染了命名空间)并将整个文件包装在一个 IIFE 中,然后通过谷歌关闭运行整个过程。 生成的文件(未压缩)为 238kb,低于 777kb。
虽然结果会有所不同,但我认为绝对值得确保这种情况发生。
感谢您的告知 - 那里有很多优点,我们也很熟悉。 我们也从不在一个项目中使用许多渲染器,而是从示例中做一些 lib 事情。
没有意识到关于缩小 - 这是一个很大的区别。
和 es6 模块确实看起来很有希望——也很高兴听到 AMD/CommonJS/此类模块模式和库的使用也有一条路径。
@colin不确定我是如何关注浏览器的
帮助你的心理幸福':ness。
它在文档中吗?
browserify 是运营商摇钱树...
2015 年 3 月 31 日,星期二,晚上 10:11 -04:00 来自 Colin Ballast notices@github.com :
@spaesani It (
许多移动网络负载问题会通过 http/2 之类的东西得到一定程度的改善。 像这样的问题最好通过修改抽象堆栈中较低的层来解决。 性能问题不应阻止我们遵循软件工程最佳实践,例如模块化/关注点分离等。
—
直接回复此邮件或在 GitHub 上查看。
现在,如果 browserify 会在没有 require 语句的情况下自动确定依赖项......嘿等等......
@spaesani我实际上更喜欢显式依赖项 - 帮助您了解代码如何组合在一起。
browserify 是运营商摇钱树...
@spaesani browserify开销非常小。 它实际上将帮助人们创建更多的最小构建。
状态更新:
我有一个带有一些浏览器代码的分支:
https://github.com/coballast/three.js/tree/browserify
请记住,这是一项正在进行的工作。 这段代码是自动生成的,因此在一段时间内看起来会很糟糕。 我仍在尝试解决一些构建问题。 如果您认为可以修复它,请参阅 coballast/threejs-browserify-conversion-utility#10。 它建造了一段时间,但不是现在。
@kumavis和我一直致力于解决一些运行时问题(以及改进软件架构)。 我相信我在上面的某个地方提到过。
抱歉回复太长。 TLDR:jspm/es6 运行,但有一些奇怪:1)在定义对象之前导出对象; 2)导出包含单个类的对象,而不是只导出单个类; 3)IIFE使用循环依赖; 4) 文件结构。
我在 jspm 中使用了你浏览器化的分支(上面的@spinchristopher是我)并有一些笔记,但首先:在那个叉子上打开问题不是很好吗,所以这个线程没有充满它们,它们也不是混在一起?
虽然它运行了,但它实际上并没有输出正确的结果。 (使用入门时的简单演示)。 创建画布并将其填充为黑色(除非我将透明颜色设置为透明),但实际上并未渲染立方体。 然而,我并不指望它在这一点上起作用,因为这仍处于过程的早期。
我遇到了3个主要问题:
一。 这是最烦人的一个,老实说,我不确定您是如何通过这个特定错误编译任何东西的,因为它根本不应该工作。 许多文件都是这样开始的(这很好,因为函数定义被提升到时间,并且本质上在 module.exports 行之前运行,即使它首先出现):
module.exports.Foo = Foo;
function Foo() {}
问题出在许多类似的文件中(我看到的第一个文件是 math/Math.js)。 对象初始化被提升(这就是为什么没有未定义错误)但定义保持原位(因此导出是未定义的)。
module.exports.Foo = Foo;
var Foo = {};
我在这里找到的唯一解决方法是将导出行移到最后,或者像这样重写它(首选):
var Foo = module.exports.Foo = {};
二。 导出的数据。 在处理模块化文件时,标准是每个文件都导出一个对象。 虽然大多数文件都是这种方式(尽管有些导出更多),但它们不导出单个构造函数,而是导出包含该构造函数的对象(即: module.exports.Foo = Foo;
而不是module.exports = Foo;
。后者是所有 browserify 示例的工作方式)。 因此,在使用 requires 时,您必须更深一层( var Vector3 = require('../math/Vector3').Vector3;
)。 除了不必要之外,在导入 es6 时没有办法做到这一点。 ( import Vector3 from '../math/Vector3'; var vector = new Vector3.Vector3();
)。 虽然在 es6 中可以获取特定的导出,但它确实适用于使用浏览器模块,并且仍然具有相同的冗余( import { Vector3 } from '../math/Vector3';
)。 有一些文件只是简单地收集其他对象(显然是 Three.js),但这些应该保持在最低限度,并且真的应该只用于构建过程,而不是作为一种在生产中获取很多东西的方式.
三。 这与循环依赖有关。 System.js(jspm 使用的模块加载器)可以很好地处理循环依赖,但有一个问题。 在许多地方,代码读取如下内容。 问题是,虽然 Vector3 已作为依赖项传入,但此时它尚未完全加载(因为 Vector3 也包含此文件,每个都无法解析,直到另一个解析为止)并且无法创建。 我添加了一个非常糟糕的修复(如下所示),但我不确定如何更好地修复它。 似乎这是一个架构问题,可能没有简单的解决方案。 它发生了很多很多次。 这似乎是一种优化,可以防止每次调用该函数时都创建一个新的 Vector3。 如果确实存在无法通过优化 Vector3 来修复的重大性能损失,那么也许可以向 Vector3 添加一个函数以返回一个未使用的 vector3 稍后发布?
Foo.prototype.bar = function() {
var vector = new Vector3();
return function() {
// some data which reuses vector repeatedly.
};
}();
修复:
Foo.prototype.bar = function() {
var vector;
return function() {
if(!vector) vector = new Vector3();
// some data which reuses vector repeatedly.
};
}();
最后,我想补充一点关于文件组织的内容。 这显然是在当前系列构建正确后需要解决的问题,但我现在想提出它。 虽然当前的文件结构有效,但其中的某些部分相当奇怪甚至笨拙。 主要分组(相机、材料、几何图形等)似乎很好,尽管我会做一些更改,如下所示。 我还会将 ThreeGlobals 中的每个全局变量移动到它们是全局的事物。 IE:FrontSide、BackSide、DoubleSide 都属于 Material(还有 NoShading、FlatShading 和 SmoothShading。实际上,它们中的大多数似乎都...)。
我的主要困惑来自核心和附加功能。 core/Geometry.js 应该在geometries 文件夹中,就像Material 在materials 文件夹中一样。 但是没有几何文件夹,它在附加文件中。 顺便说一下,extras 也有一个核心和一个几何图形,但基础几何图形不在此文件夹中。 有这么多帮手,但每个帮手不应该和它正在帮助的东西在一起吗? 构建过程可以轻松配置为仅获取您想要的文件,因此没有理由将不重要的文件放在其他地方。
我目前在我的代码中有一行读取import BoxGeometry from 'threejs/extras/geometries/BoxGeometry';
和var geometry = new BoxGeometry.BoxGeometry( 1, 1, 1 );
习惯于使用 `new THREE.BoxGeometry()``` 花了很长时间才找到文件。 当以模块化方式使用它时,文件位置与函数签名之类的东西同样重要。
我会对文件结构进行一般更改。 这些在很多地方都适用,但我只是举个例子。 (请注意,我一直更喜欢在它导出的单个类之后命名文件的模型,并将文件放在同名文件夹中。任何直接后代通常会进入该文件夹,但同样,在文件夹中与自己同名。但是如果不喜欢这种模式,下面的相同结构也适用,只是去掉额外的一层。此外,任何文件加载器都可以很容易地修改[通常直接提供钩子,实际上]自动分层并简化 require 语句]。)(我也更喜欢所有小写文件,必要时使用下划线。)
Three.js - This is _only_ used in the build process, so it should actually be with the build files, but run as if it is in this location.
geometry/geometry.js - Currently at core/Geometry.js
geometry/face3/face3.js - from core/Face3.js
geometry/box_geometry/box_geometry.js - Currently at extras/geometries/BoxGeometry.js
geometry/circle_geometry/circle_geometry.js - Similar to above.
geometry/utils/utils.js - from extras/GeometryUtils.js
camera/camera.js
camera/cube_camera/cube_camera.js
camera/perspective_camera/perspective_camera.js
camera/helper/helper.js - or camera/camera_helper/camera_helper.js
scene/scene.js
scene/fog/fog.js
scene/fog_exp2/fog_exp2.js
我也可能将 math 重命名为 utils(每个类别也可能有一个 utils,如上面的几何),以便它可以容纳的不仅仅是数学(许多来自核心的东西)。
@HMUDesign @spinchristopher感谢您的精彩分析! 最好在将来将此类问题放入 coballast/threejs-browserify-conversion-utility 存储库中。
好的,让我现在正确阅读您的评论。
我不知何故错过了上面那个 repo 的链接。 我会很高兴地移动我的问题
明天到那个回购,然后根据需要拆分(我注意到至少
其中一些已经存在)
2015 年 4 月 9 日上午 12:09,“kumavis”通知@github.com 写道:
@HMUDesign https://github.com/HMUDesign @spinchristopher
https://github.com/spinchristopher感谢您的精彩分析! 最好的事物
把这些问题放到
未来的 coballast/threejs-browserify-conversion-utility 存储库。好的,让我现在正确阅读您的评论。
—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -91132413。
是的,我为自己的 util 类型库这样做(*Util 文件是
只有当我没有默认导出时,虽然我有时会添加一个
除了默认值之外的专家),但是此语法仅在您
导出多个命名变量。 常见 js 模块的加载器处理
modules.exports 作为默认导出,不能在
进口 =(
2015 年 4 月 9 日上午 12:12,“kumavis”通知@github.com 写道:
在 es6 中,您可以通过以下方式从导出对象导入属性
解构。从 '../math/Vector3' 导入 { Vector3 };
也就是说,我同意每个模块一个导出是首选。
—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -91132982。
@HMUDesign再次感谢您的精力和分析——这里我们正在构建一个待办事项列表,请随时加入。 https://github.com/coballast/threejs-browserify-conversion-utility/issues/17
+1 用于浏览器。
还 +1 用于使用 glslify 将着色器移动到单独的文件中。
还为采用一些 ES6 功能(如类和模块)+1。 如有必要,新的构建堆栈将允许我们编译回 ES5。 见示例:
import Object3D from '../core/Object3D';
import Geometry from '../core/Geometry';
import MeshBasicMaterial from '../materials/MeshBasicMaterial';
class Mesh extends Object3D {
constructor(
geometry = new Geometry(),
material = new MeshBasicMaterial({color: Math.random() * 0xffffff}
) {
super();
this.geometry = geometry;
this.material = material;
this.updateMorphTargets();
}
}
export default Mesh;
@lmcd虽然我们正在这样做,但我们可以使用 es6 模块并使用 babeljs 来编译所有内容。
@coballast我有兴趣将您的browserify
分支分支出来,并让其中一些事情发生
@lmcd我不会打扰。 我将开发自动化工具来自动将 es5 内容移动到 es6。 这是有道理的,因为那里有大量的 es5 代码,并且手工移动它的劳动力需求是天文数字。
@coballast我正在考虑更多地运行5to6
通行证: https :
@lmcd很好的发现
但是 tbh,从头开始重写three.js 似乎更容易:P
@lmcd我很高兴你找到了。 这是我出于必要而要做的事情之一,但听起来显然不好玩。
@mrdoob您目前对这个问题有何看法?
@anvaka目前我正专注于重构WebGLRenderer
。 我没有更多的心理带宽😅
我最近遇到了一些使用 es6 模块和这里已经提到的 babel polyfill 的项目。 现在不记得也找不到它是什么,无论如何对我来说看起来不错。
此外,es6 现在似乎在标准方面已经完成:“最后,ECMA-262 第 6 版于 2015 年 6 月 17 日获得正式批准并作为标准发布” https://developer.mozilla.org/en-US/docs /Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla
请注意,关于模块规范的工具和稳定性,这方面的情况似乎很好。
我最近遇到了一些使用 es6 模块和这里已经提到的 babel polyfill 的项目。 现在不记得也找不到它是什么,无论如何对我来说看起来不错。
它还有 47kb 的额外 js,可以在浏览器中读取您包含的 javascript 并将其转换为 es5,因此不太适合启动时间。
没有什么能阻止使用three.js 的人在自己的代码中使用es6; 然而,在库中使用它会全面引入浏览器兼容性和性能问题。
啊 - 站在这里更正,感谢您的信息。 我想 browserify 以及在构建过程中的工作现在仍然更好。
啊 - 站在这里更正,感谢您的信息。 我想 browserify 以及在构建过程中的工作现在仍然更好。
es6 的主要问题之一是它与 es5 的语法发生了重大变化; 例如,粗箭头 => 是无效的 es5,将导致 javascript 解析器失败并试图编译代码。 希望有人会想出办法解决这个问题,但他们还没有。
我实际上只是在考虑模块系统、导入语句等。
它还有 47kb 的额外 js,可以在浏览器中读取您包含的 javascript 并将其转换为 es5,因此不太适合启动时间。
例如,粗箭头 => 是无效的 es5,将导致 javascript 解析器失败并试图编译代码
可以在 _build_ 期间将 es6 代码转换为 es5 而不会有运行时损失。 这只是在构建管道中添加 babel 步骤的问题。
例如,可以在构建期间将箭头函数转换为 es5,而没有 polyfill 或运行时损失。 Babel 将转译这个 es6 片段
function MyObj() {
this.step = 1;
this.increment = function ( arr ) {
return arr.map( v => v + this.step );
}
}
进入这个便携式版本:
function MyObj() {
this.step = 1;
this.increment = function (arr) {
var _this = this;
return arr.map(function (v) {
return v + _this.step;
});
};
}
其他功能,如 es6 类,将生成一个小的 polyfill(您可以在 babel repl http://babeljs.io/repl/ 中看到)。
@mrdoob明白了。 您是否支持将 Three.js 分解为托管在 npm 上的较小模块的想法?
主要的three.js 存储库将保持不变:消费者不必构建任何东西。 更有经验的用户将能够挑选所需的three.js 位。
听起来不错。 虽然我不知道细节。
可以在构建期间将 es6 代码转换为 es5,而没有运行时损失。 这只是在构建管道中添加 babel 步骤的问题。
例如,可以在构建期间将箭头函数转换为 es5,而没有 polyfill 或运行时损失。 Babel 将转译这个 es6 片段
ES6 转译仍然会带来显着的运行时损失: http :
模块/npm/browserfy 等但是可能是个好主意
+1 使用 ES6 模块正确模块化
+1 使用任何合理的模块系统(commonjs、amd、es6)进行适当的模块化
我认为 commonjs 和 amd 是首选的选择 b/c 他们不需要转译
然而,它们确实需要一个构建步骤,这相当于
转译步骤。
使用 ES6,除了成为该语言的下一个版本之外,还将允许
根据需要使用下一个功能,但不破坏现有的本机代码。
将代码库反应到一个已经存在的系统中真的明智吗?
不是最新的?
2015 年 7 月 20 日下午 12:05,“kumavis”通知@github.com 写道:
+1 使用任何合理的模块系统(commonjs、amd、
es6)
我认为 commonjs 和 amd 是他们不喜欢的首选 b/c
需要转译—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -122990605。
然而,它们确实需要一个构建步骤,这相当于
转译步骤。
就它们引入的复杂性而言,构建和转译并不等同。
将代码库反应到一个已经存在的系统中真的明智吗?
不是最新的?
commonjs 很简单,而且效果很好。 我不认为“最新”===“更好”。
使用 ES6 [...] 将允许根据需要使用下一个功能
所以这是值得考虑的。 我们想要 es6 功能吗? 如果我们开始转译 es6,人们就会开始 PR'ing es6。 正如@benaadams 所暗示的那样,使用 es6 功能会产生非直观的性能影响。
此外,我们不需要将“模块系统”和“es6 功能”的问题混为一谈。 您可以转译 es6 并使用 commonjs。 你可以分别介绍那些。
+1 用于 browserify / commonjs - 使用 browserify 进行编译很简单,如果他们愿意,人们仍然可以以传统方式使用该库 - 这就是 UMD 的用途,允许 AMD 需要(如 require.js),根据我们运行的环境,CommonJS 需要(如 node + browserify)和一个全局窗口(用于脚本标签)。
PIXI.js 刚刚使用 browserify 迁移到模块化架构,并且库的设置方式非常相似——所有内容都附加到全局 PIXI 对象。 他们的设置与@kumavis在第二篇文章中描述的非常相似。
browserify 和 commonjs 都没有解决 3D 引擎的特定需求,这并不意味着它们不能使用,但它们应该被视为一个更大的难题:
组件需要导出有关其实例属性的元信息,因此可以有一个用于任意对象和共享内存数据连接的加载器。 对于这样的架构,在首次使用时加载核外组件代码只是常识问题。 在 #6464 和 #6557 中就这些主题进行了头脑风暴。
+1
+1
作为混合解决方案,还可以将需求添加为注释。 我知道 browserfy 已经在许多生态系统中,但我只是想把它留在这里作为一个快速实现的变体:) 因为你不需要改变任何东西。 您只需要在每个文件的顶部添加注释。
作为开发人员,您可以使用@requires
信息和 gulp 插件轻松创建自己的 min.js 文件。
大家好,我最近有类似的需求,需要以干净和结构化的方式加载具有自己的依赖项的任意资源,我想出了为每种类型的资源编写 require.js 插件的解决方案。 通过这种方式,我让 require.js 依赖解析来处理正确下载和缓存资源......同时,这种方法创建了可重用的资源“包”,我可以在我的各种项目中使用它们。
如果您有兴趣,可以在这里找到该项目: https :
(使用此库的示例将很快可用)
将来,我计划在这些插件中包含一个优化阶段,以允许 require.js 优化器将资源编译为更紧凑的格式。
看看这个对话https://twitter.com/defunctzombie/status/682279526454329344,es6模块似乎不会在不久的将来实现。 要记住的事情。
我用 commonjs 模块和 browserify 做了一些原型。
我的最终浏览器包包含src
文件夹中的每个文件,并导致962K
文件大小(与原始未浏览版本885K
)。
cloth
示例的目标构建是:
580K
(缩小约 44%)431K
(小了约 8%)这是包大小细分: http :
我认为我们可以通过以下方式减小包的大小:
instance of
检查 - 即使不使用它们,它们也需要显式引用模块。 我看到一些类已经有type
- 我们可以在整个库中使用它。glslify
被带来了几次,它肯定会有所帮助。 理想情况下,每个需要着色器的组件都需要明确依赖于着色器。您可以验证结果并检查代码:
git clone --depth 1 --branch commonjs https://github.com/anvaka/three.js.git
cd three.js
npm i
# build backward compatible three.js library from commonjs modules.
# The output will be save into `build/three.min.js`. I'm using `.min.js` just
# to quickly verify examples. The actual file is not minified.
npm run build
# build cloth example
# the output is saved into ./examples/cjs/webgl_animation_cloth.bundle.js
npm run demo
es6 模块似乎不会在不久的将来实现。 要记住的事情。
确实如此,但同样重要的是要意识到 CommonJS 模块支持_永远_不会在浏览器中实现,所以选择是
像 D3 这样的库正在采用 ES6 模块,因为它们已经可以完成 CommonJS 模块可以做的所有事情(除了在 Node.js 中本地运行,这对于像 Three.js 这样的库来说并不是真正的问题),并导致构建更小。
我已经在https://github.com/rollup/three-jsnext 上做了一些实验,虽然它还没有准备好生产(我需要花更多时间移植示例等!)它生成的 UMD 构建实际上是 _smaller_比当前的构建。
我同意关于 es6 模块与其他系统的说明。 是否
它们不是 es 标准,而是社区标准。 虽然他们
不能在 node 中“本地”运行,它可以通过 Babel 钩子看起来是本地的。
我会尽快跟进你的回购。
此外,它更小的事实是我早些时候提出的
这次谈话。 "THREE.Geometry" 变成 "geometry" 可以是
例如缩小到“a”。
此外,检查实例的解决方案是将它们全部删除
一起。 一个模块不应该因什么而不同
它被给予,而是按照提供的事情去做,因为它需要
去做。 然后没有实例或类型检查。
2016 年 1 月 1 日晚上 8:23,“Rich Harris” notification@github.com写道:
es6 模块似乎不会在近期实现
未来。 要记住的事情。确实如此,但认识到 CommonJS 模块也很重要
支持将_永远_不会在浏览器中实现,所以选择是
- 继续使用非模块化架构和临时构建
系统,到目前为止已经很好地为 Three.js 服务,但却阻碍了增长
从长远来看- 使用 CommonJS 模块,这涉及一些关于循环的技巧
依赖并导致更大的构建,或- 使用 ES6 模块,它们非常适合像 Three.js 这样的代码库
具有循环依赖性,并导致最小和最大
可以缩小构建。 最终,浏览器将原生支持它们,
以及适应装载机中任何不可预见的怪癖所需的更改
与所涉及的工作相比,规范很可能是微不足道的
从 CommonJS 代码库升级。像 D3 这样的库正在采用 ES6 模块,因为它们已经可以做到
CommonJS 模块可以做的一切(除了在 Node.js 中本地运行,它
对于像 Three.js 这样的库来说并不是真正关心的问题),并导致更小
建立。我已经做了一些实验
https://github.com/rollup/three-jsnext ,虽然它不是生产
准备好(我需要花更多时间移植示例等!)UMD 构建
它生成的实际上比当前构建_更小_。—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -168363092。
CommonJS 是否仍会导致更大的构建,其代码库没有循环依赖?
@cecilemuller是的 - 请参阅https://github.com/nolanlawson/rollup-comparison。 使用 CommonJS 模块,您需要支付每个模块的成本(每个模块都需要包含在一个函数中,并且需要重新声明在整个包中共享的导入,因此您会因更加模块化的代码库而受到惩罚),每个包的成本(它需要模拟 Node.js 环境),以及其他成本,例如不可缩小的对象属性名称,这些名称将是 ES6 模块中可缩小的变量名称。 ES6 模块允许您以几乎零开销的方式进行捆绑。
尽管转译到 es5 会有一些开销。 目前,
我使用带有 babel 的 webpack,它添加的很少。 每个模块的成本
因为它也包含在 s 函数中。 依赖项在最后声明
通过调用具有整数索引的类似 require 的函数来编写代码,因此它得到
从最初的“导入”缩小为“var a=f(5)”之类的东西
来自“./geometry”的几何图形;'
使用生成器也增加了一点,但我没有想象的结构
代码很快就会发生如此大的变化。
2016 年 1 月 2 日凌晨 5:53,“Rich Harris”通知@github.com写道:
@cecilemuller https://github.com/cecilemuller是的——见
https://github.com/nolanlawson/rollup-comparison。 使用 CommonJS 模块
您支付每个模块的成本(每个模块都需要包装在一个函数中,
并且需要重新声明在整个包中共享的导入,所以
你会因为更模块化的代码库而受到惩罚),每个包的成本(它需要
来模拟 Node.js 环境),以及其他成本,例如不可缩小的
对象属性名称将是 ES6 中可缩小的变量名称
模块。 ES6 模块允许您以几乎零开销的方式进行捆绑。—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -168394376。
虽然转译到 es5 会有一些开销
如果您只使用import
和export
语法来描述模块之间的关系,则无需使用 Babel 或任何类似的方法来编译代码本身。 只有当您开始添加其他 ES6 功能(如类和块作用域和箭头函数等)时,转译才变得必要,因此使用import
和export
开销为零。 D3 和 PouchDB 是使用import
和export
的库的两个示例,但在其他方面是无 Babel 的 ES5,而 Three-jsnext 也是如此。
好吧,我们都有同样的想法。 有一个像 lodash 这样的故事会很棒。
我建议为每个可以模块化的组件 _foo_(例如三向量 2)创建一个 _three-foo_ 包,几乎没有代码更改,因此它可以在此 repo 中导入而不会产生影响。
在 npm 上发布的人应该玩得很好并与@mrdoob合作,因为他是这个伟大软件的创造者,所以如果他想像 _babel_ author 那样再次集中所有包(我的意思是所有核心包都在同一个文件夹中),发布者应该让他控制所采用的 npm 命名空间。
对于我需要的包,我会尝试这样做。 让我们看看发生了什么。
只有一个大社区:)
我没有注意到有人建议像
洛达什。 Lodash 是一个通用名称下的实用程序集合; 你的出租车
抓住它的一块并使用它。 Threejs 不是; 这是一个全面的
库,如果没有其余部分,其中大部分都是无用的。 有几块
可以分开的,比如具体的材料类型我们具体的
几何生成器,但那些必须非常接近
绑定到核心,可能需要精确的版本匹配。 考虑到他们
大小,它会造成维护头痛,没有可衡量的收益。
但是,如果 doob 先生批准这种性质的拆分,我认为不会
适合除官方维护者之外的任何人声称 Threejs-*
包。
不管上述如何,我认为让它在模块化中工作是明智的
环境比什么都重要。 有几个项目与此有关
目标,但似乎都陷入困境。
2016 年 3 月 6 日上午 11:39,“Gianluca Casati”通知@ github.com 写道:
好吧,我们都有同样的想法。 有这样的故事会很棒
洛达什。我建议为每个组件 _foo_ 创建一个 _three-foo_ 包(对于
实例三向量2)可以模块化,几乎没有变化
代码,因此可以将其导入此 repo 中而不会产生任何影响。在 npm 上发布的人应该玩得很好并与 @mrdoob 合作
https://github.com/mrdoob因为他是这个伟大作品的创造者
软件,所以如果他想再次集中所有包,如
_babel_,发布者应该让他控制所采用的 npm 命名空间。对于我需要的包,我会尝试这样做。 让我们看看发生了什么。
只有一个大社区:)
—
直接回复此邮件或在 GitHub 上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -192970867。
@mattdesl:比如你的三着色器FXAA使用THREE.Vector2和THREE.Texture,除了三effectcomposer我没有检查,它会使用以上提出的模块化方法导致真的很轻的版本。
@HMUDesign :我理解您的疑虑,但在我看来这仍然是一个好方法。 我想尝试,但我会听从你的建议,使用来自 GitHub URL 的 npm 包,而不是将它们发布到神圣的registry 上。
我试了一下,从依赖于 Vector2、Vector3、Quaternion 等的 TrackballControls 开始。
有循环 deps(例如 Matrix4 取决于 Vector3,反之亦然)。 如果 lib(即threejs)是单片启动的,则无法完成。
遗憾的是,模块模式的所有优点不能轻易应用于像这样的重要项目。
我也尝试过其他项目,如 svg.js、vvvvjs,甚至 x3dom,但作者并不完全相信这种激进的选择是不可能完成的。
抱歉收到垃圾邮件,但我想主动尝试:顺便说一下,我从三轨迹球控件存储库开始。
@fibo ES6 模块模式不应该有 circ 依赖的问题 afaik。 是不是在模块执行之前就设置了绑定,就像在普通的 JS 中提升一样?
我肯定你已经看到了这个: https :
@drcmda真的很有趣,我会尝试一下
+1 用于迁移到模块化架构。
+1
+1
@drcmda是对的。 ES6 模块有一个初始化步骤和一个允许循环引用的执行步骤。 但是,一旦您直接从模块执行上下文(在模块的全局区域中)拥有循环依赖项,那么在运行时加载的第一个依赖项将遇到未定义值的依赖项。 只要在运行时执行顺序重要的不同上下文中使用引用,循环依赖就没有问题。
我建议也考虑 webpack 而不是 browserify。
@gionkunz我们在模式的初始化步骤 bc 中有循环引用,其中有一个闭包来生成临时变量
Webpack 2 的 beta 版刚刚发布(https://twitter.com/TheLarkInn/status/747955723003322368/photo/1),因此 es6 模块在捆绑时也可以从摇树中受益。
@mrdoob
最近有官方声明吗? 像许多人一样,我们很久以前就已经放弃了 ES5 和胶水连接,而且在现代构建系统中,三者有多么不合时宜,这是非常糟糕的。 我们可能只使用了它可以做的 10%,但它是我们发布的最大依赖项。
这可能是我个人在 Github 上最喜欢的项目——我真诚地希望能够重新考虑优先级。
嗯,我想了解更多有关浏览器支持的详细信息。 哪些浏览器可以,哪些不可以。 对于不这样做的浏览器,有什么解决方法以及性能损失是什么。
实际上,浏览器支持不再是问题(也许比现在更不重要)。 构建系统采用 ES6 代码并将其转换为 es5(有时占用的空间比原始 ES5 少)。 某些类型的转译的东西最终会很大(主要是:生成器和异步函数),但如果你避免这些,你就不会受到惩罚。
正如@drcmda提到的,构建系统仍然会产生一个整体输出(并且很容易定制该输出中包含的内容),但是各个模块也可以包含在我们自己的项目中,因此只使用那些我们需要。 为了充分利用
需要调整相互依存关系,但这可能会随着时间的推移而发生。 我认为我们想要的主要功能是通过导入/导出对其进行模块化。 从您的角度来看,它可以在原型上使用类(它们仍然在幕后使用原型,因此您仍然可以
根据需要弄乱它)。
有一些构建系统。 我的投票是 webpack(使用 babel 进行转译)。 使用 babel,您可以定义自定义加载器,因此您为着色器开发的分块系统可以简化为带有 #include 扩展名的实际 glsl 代码(我实际上以这种方式制作我的着色器,并且很乐意将其贡献给项目)。 这与您的系统具有相同的好处(无代码重复),但使用起来非常简单。
我很想成为模块化项目的一部分,但我知道如果没有你的支持(和可能的帮助),这不会以任何方式成功。 我们中的许多人都知道如何使用该库,但没有人知道它在内部是如何工作的。
某些类型的转译的东西最终会很大(主要是:生成器和异步函数),但如果你避免这些,你就不会受到惩罚。
多大?
另外,您没有谈论性能损失。 那这不是问题吗?
据我所知,任何浏览器仍然不支持 ES6 导入,所以这个模块重构主要用于构建系统,对吧?
不要忘记使用 rollupjs 等工具获得的好处,这会自动排除用户不使用的所有导出。 (这是 JSPM 的默认设置)
babel-polyfil 包,仅在您使用时才需要
生成器(在这个项目中可能甚至没有意义)或异步
功能(我真的不认为这会在项目中发生太大变化
或者),为最终版本增加大约 50k。 但同样,这是可选的。
就性能而言,这实际上取决于您的具体功能
使用。 例如,箭头函数会慢一点,因为
底层绑定,类的创建速度有点慢,尽管
实例化时间是一样的。 https://kpdecker.github.io/six-speed/
浏览器不支持 ES6 导入/导出,但因为它
通过构建系统,这不是问题。 产品输出将是
完全可以像现在一样使用(甚至向后兼容),但是
将允许它集成到我们的构建系统中,并使
内部组件可重用给我们。
另一件需要注意的是最终构建大小。 目前,像几何这样的东西,
Material、Mesh 等是 THREE 命名空间的一部分。 缩小后,
对 THREE.Geometry、THREE.Material、THREE.Mesh 等的引用保留在
代码。 使用模块化系统,每个文件都会得到类似
var Geometry = require('./geometry');
然后引用
变量Geometry
之后。 然后在 minifaciton, Geometry
和require
都切换到单个字符,'./geometry' 被替换为
数量,从而节省了相当多的费用。 餐巾纸数学:缩小版
build 是 511,794 字节,包含 2942 个对
THREE\.[A-Z][a-zA-Z]+
。 用一个字符替换所有这些
导致文件大小减少近 10%(降至 464,782)。 (压缩的
大小分别为 117,278 和 110,460,减少了 6%)。 构建
可能会进行调整以进一步减少这种情况。
Rollup(从最终构建中消除未使用的代码)是默认设置
使用 jspm,将是 webpack2 的默认设置(我相信它可以使用
与 webpack)。 如果事情是模块化编写的,我认为这不会是
不过很有帮助。 无论如何,只要代码可以转译
babel,它可以在任何构建系统中使用(我提到的 glsl 加载器
before 也可以与 webpack 一起使用)。
在星期四,2016年7月7日在下午1点28分,Mr.doob notifications@github.com写道:
某些类型的转译事物最终会变大(主要是:
生成器和异步函数),但如果你避免这些,你就不会
那个惩罚。多大?
—
你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/mrdoob/three.js/issues/4776#issuecomment -231197171,
或静音线程
https://github.com/notifications/unsubscribe/AA71cqAqmgxsUjpvamnI_xyL2wpzeWrdks5qTWGBgaJpZM4B4aA7
.
不确定这是否非常有用,但这是 D3 关于同一问题的讨论主题: https :
非常有趣的@jpweeks!
所以......使用这种导入/导出方法......像object instanceof THREE.Mesh
这样的东西会是什么样子?
@mrdoob
import/export
只是声明和需要模块的方式。 它根本不会影响/更改模块中定义的代码:
源代码/对象/Mesh.js
// Mesh class, stays the same as today (except the export part)
var Mesh = function ( geometry, material ) {
// ...
}
export default Mesh
源代码/三.js
// Library entry point, exports all files using som bundling tech
// In a "THREE" namespace for browsers
// As import three from 'three' in node
import Mesh from './objects/Mesh'
export {Mesh} // All three objects, such as Geometry, Material etc..
应用程序.js
// In node
import {Mesh} from 'three'
var mesh = new Mesh(geo, mat)
console.log(mesh instanceof Mesh) // true
客户端.js
// In a browser
var mesh = new THREE.Mesh(geo, mat)
console.log(mesh instanceof THREE.Mesh) // true
这是超级有用的@GGAlanSmithee! 谢谢!
我是一个视觉人士,所以伪代码示例比大段文字更能说服我😅
是的,所以它需要一些重构......
有谁知道闭包编译器是否计划支持这个?
是的,所以它需要一些重构......
我接到你了! 由于这个线程在过去几天变得活跃,我一直在研究三 jsnext 。 这是一个采用现有 Three.js 代码库并自动将其转换为 ES 模块的项目。 只是在处理一些棘手的循环依赖项(特别是在KeyframeTrack
周围),但很快就会有一些东西可以真正分享。 据我所知,所有示例都可以继续工作,并且缩小版本比当前版本小(使用 Rollup 生成 UMD 文件),所以这都是好消息。
好的,我已经为此打开了一个拉取请求:#9310
@mrdoob
我们在生产中有一个库,其结构或多或少类似于三。 它适用于浏览器和模块化环境。 代码库是 ES6,但浏览器根本不是你关心的。
你会在 npm _as is_ 上发布它,包含所有模块 + 一个编译的全局命名空间浏览器单体(three.js)。 需要使用它的单个部分的人使用工具来创建包。
考虑这样的结构:
/src
classA.js
classB.js
classC.js
/index.js
/browser.js
index.js 只是将所有模块和函数重新导出到一个文件中:
export ClassA from './src/classA';
export ClassB from './src/classB';
export ClassC from './src/classC';
因此,最终用户可以 npm install lib 并直接使用它,无需再费力:
// all exports from index.js will be under: mylib.ClassA, etc.
import * as mylib from 'libname':
// selected exports from index.js
import { ClassA, ClassC } from 'libname';
// or, specific modules
import ClassB from 'libname/src/classB'
browser.js 将是包的唯一编译部分。 通常通过 Babel 转译为 ES5 并导出到全局命名空间,因此它可以用作脚本包含。 Rollup、Webpack 等可以轻松创建它。
@mrdoob这是一次美妙的旅程🚀
最有用的评论
我接到你了! 由于这个线程在过去几天变得活跃,我一直在研究三 jsnext 。 这是一个采用现有 Three.js 代码库并自动将其转换为 ES 模块的项目。 只是在处理一些棘手的循环依赖项(特别是在
KeyframeTrack
周围),但很快就会有一些东西可以真正分享。 据我所知,所有示例都可以继续工作,并且缩小版本比当前版本小(使用 Rollup 生成 UMD 文件),所以这都是好消息。