On Wed, May 14, 2008 at 6:34 PM, Peter Michaux <petermich
...@gmail.com> wrote:
> I don't see the need to modify Rhino or add an XHR module for
> server-side use. In fact, I am not particularly a fan of the systems
> that try to simulate the client on the server. I am all for code
> sharing but simulating an XHR object so server-side code can load
> other server-side code seems like one level of abstraction too high
> for me. I'm a simpleton.
> I would think that there could just be two different "require"
> functions. One implemented for the server and one for the client. The
> server one uses what I have in Rhino and the client one uses XHR.
I agree completely. I think we need to have "require" and "include"
with all of the same module API, including "log", "error", "warn",
"foreignModuleBind", "moduleUrl", and "modulesUrl". I don't think
this would be hard, and if you provided a simple API for grabbing the
text of a script instead of simply loading it, I could easily
implement this in JavaScript land for you. Alternately, if you could
implement "getModule" instead of "require", the "include" or "require"
function could be written for both client and server in terms of
"getModule". Please disregard the implication that we need an exact
XHR emulation layer. The main idea is that I think we can provide
insular modularity with stronger primitives out of the box, easily and
simply.
> By the way, I would probably not use an XHR to load JavaScript into a
> client. There are at least two reasons. I'm willing to be corrected on
> any of these things.
> The first is caching. I'm not confident that all browsers take
> advantage of caching the response to an XHR.
All browsers do HTTP-level cache control and abstract it to the XHR
API. The only caveat is that Konqueror neglected for a time to change
the cache code 304 to 200 to provide a completely transparent cache
hit. I fix this in my code.
> The second is having a client-side "require" function encourages
> loading bits of JavaScript as needed. This encourages more
> client-server requests. More requests is something that I want to
> avoid. If the lazy loading of scripts all occur in the first, say, 30
> seconds, then Apache pipelining, for example, makes these many
> requests cheaper (but still not free.) However if these files are
> loaded as the user interacts over a long period of time then each
> little request will open a new connection which is expensive.
Lazy loading is actually beneficial for certain kinds of
optimizations; I use this to guarantee fast load of a splash page for
example, and then proceed to add particular behaviors when the user
demands them.
Lazy loading, as you mention, can be detrimental to performance if
you're performing lots of HTTP requests to serve very small modules
(made small by necessity of organization, rather than for
performance). When I'm developing for JavaScript, I just suck it up
and tolerate the performance loss (which isn't much) because it
provides fine grain line numbers and file names for thrown errors.
However, Chiron has an optional build step for taking a body of
JavaScript modules and condensing them into a single file, observing
all of that module's dependencies by tracking include and require
calls. After that, I pass the file through YUICompressor and gzip.
After passing through those three tools, I get a single, small HTTP
request with a lot of functionality. Particularly, having all of the
code in one gzip stream gets good performance out of the LZW
algorithm, and it's efficacy furthermore increases because
YUICompressor turns unrelated names into a short list of names that
are even more patternous and thus compressible.
> I have also never had a situation where one of my page loads takes
> long even though I load all my scripts in the <head> of the document.
> I write small JavaScript, use a build process to include only what is
> necessary, minify and gzip. The total size of the JavaScript never
> turns out to be even the size of a medium sized jpg. This is the
> easiest system on the server and when a user interacts with the page
> there is never the need to wait for some JavaScript to load. It is
> already there.
I think we'll find that liberating ourselves from the need for a small
client-side code base when writing server code will be a major boon.
I'm in favor of writing minimal code, but that goal is best served by
a big tight-knit framework that simplifies the interfaces to lots of
lower-level code.
> All that said, I don't think the idea of lazy loading JavaScript into
> the browser is a very bad idea. I just haven't encountered a situation
> so complex that the page load time is a problem due to the JavaScript
> loading.
> There is at least one other library that does use XHR to load scripts bit-by-bit
> http://openjsan.org/doc/c/cw/cwest/JSAN/0.10/lib/JSAN.html
There's also Dojo.
> I spent about half an hour poking around there last night. It does
> seem like some of the code could be shared both client-server and
> chiron-xjs.
> Much of your code is client-side. What I still haven't figured out is
> how to distribute (distribute to developers that is and where to put
> it on the server) code that will be shared client-server. It is a
> slightly strange problem. Security is an issue and I use a build
> process so that all has to be worked together. I haven't had a clear
> vision yet because I've never had the opportunity to share code
> client-server before.
I think that boot.js, base.js and boost.js would be the most useful in
a server side scripting environment. It's true that most everything
else is browser-bound. I think it would also be good to make the
build step optional, if possible, for native JavaScript only modules.
Perhaps we can knock a tier of organization out of the library source.
>> And my documentation for those two functions:
>> http://modulesjs.com/nightly/build/doc/glossary.html#require
> You write "returns or passes a Module object for the JavaScript file
> at a given module URL". That is confusing to me. For me, "returns" is
> what a function returns. "passes" is when I send to a function. Is the
> "or passes" part unnecessary? Would the following be more clear?
I should be more clear by breaking this doc up into two parts.
"require" supports "optional continuation passing". That is to say,
that when you call require like this:
var http = require('http.js');
It "blocks" (makes a synchronous call) and returns the HTTP module
object when it's ready. If you call require like this:
require('http.js', function (http) {
});
It's an asynchronous form where require passes what would be retuned
as an argument to a continuation. I'll have to rewrite that
particular bit of documentation to be more lucid. This is a pattern
in Chiron. http.request and its friends all support the same notion
of optional continuation passing. I even have a continuation passing
style decorator so that any function can support CPS, including error
passing.
> Your require is not like the Ruby, Perl or xjs require which just
> evaluate code in the global scope. I think your require could more
> descriptively be called "getModuleObject" if that name was not so
> long.
Ruby, Perl, Python, and PHP all have more complicated scope
arrangements than JavaScript or C. I agree that Chiron "require" is
not the same as these languages, but all of those languages do
distinguish global and package or module scope. However, to a casual
observer, there might appear to be the same. JavaScript does not
natively support a module scope. In C, you have a "module scope" by
virtue of the "static" keyword used in "global scope" that reduces the
scope of a term to a single file after the preprocessor has merged in
all your headers. Perl has package scope, which is just a better way
to organize global scope explicitly. Ruby and Python both have module
scope, wherein the variables of a file are part of a corresponding
module object. The "require" function (and "import" directive in
Python) operate by copying names from the other module into the
current module (or in the case of import *, adding the other module to
the current module's scope chain), not by evaluating the other module
in the same scope as your file. Again, I decided to call this
"include". I'm yet to think of a better couple of names. I did
consider "getModule" for "require", but it's just too kludgy for such
a common function call. "import" is a reserved keyword. "use", via
Perl parlance, is available. I could use "require" instead of
"include" if we can agree on a better name than "getModule" for my
current "require".
> I think exploration of the client-server code sharing is most
> important. How code is distributed to developers and where it is
> located on the server. How it is built or required bit-by-bit by the
> client. What code can be shared and what cannot.
I don't think we should separate the module name spaces for client and
server modules because this would reduce reusability in the long run.
I think it would be acceptable to use comment directives like
/*server-only*/ and /*client-only*/ to distinguish how a client and a
server build of the library would be pared.
> Peter
Kris