Using ES6 `import` with CSS/HTML files in Meteor p

2020-07-03 03:52发布

问题:

I am currently learning Meteor and I found out something that intrigued me.

I can load HTML and CSS assets from a JS file using the import statement.

import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
import * as myApp from '../imports/hello/myapp.js';

This was a surprise to me so I ran to google but could not find this behavior documented in the specification for ES6 import or in Meteor's Docs.

So my questions are:

  • Can I rely on this behavior to build my apps?
  • Will my app will break when Meteor gets around to fix it -- if it's a bug --?

Notes

  • I am using Meteor v1.3, not sure if this works also with previous versions.
  • You can download the app to see this behavior from Github

回答1:

After going through the implementation of the built files for my app I found out why this works.

HTML

Files are read from the file system and their contents added to the global Template object, e.g.,

== myapp.html ==

<body>
  <h1>Welcome to Meteor!</h1>
  {{> hello}}
</body>

results in the following JS code:

Template.body.addContent((function () {                                                                       
  var view = this;                                                                                          
  return [
     HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n  "),      
     Spacebars.include(view.lookupTemplate("hello"))
  ];
}));                                                                                                        

Which is wrapped in a function with the name of the file as it's key:

"myapp.html": function (require, exports, module) {

     Template.body.addContent((function () {                                                                       
          var view = this;                                                                                          
          return [
             HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n  "),   
             Spacebars.include(view.lookupTemplate("hello"))]; 
     })); 

     Meteor.startup(Template.body.renderToDocument);                                                              

     Template.__checkName("hello");                                                                               
     Template["hello"] = new Template("Template.hello", (
         function () {                                            
           var view = this;                                                                                         
           return [
               HTML.Raw("<button>Click Me</button>\n  "), 
               HTML.P("You've pressed the button ", 
                      Blaze.View("lookup:counter", 
                      function () {
                        return Spacebars.mustache(view.lookup("counter"));                                                   
                      }), " times.")
          ];                                                                                         
     }));                                                                                                         

},

So all of our HTML is now pure JS code which will be included by using require like any other module.

CSS

The files are also read from the file system and their contents are embedded also in JS functions, e.g.

== myapp.css ==

/* CSS declarations go here */

body {
    background-color: lightblue;
}

Gets transformed into:

"myapp.css": ["meteor/modules", function (require, exports, module) {
    module.exports = require("meteor/modules").addStyles("/* CSS declarations go here */\n\nbody {\n    background-color: lightblue;\n}\n");

}]

So all of our CSS is also now a JS module that's again imported later on by using require.

Conclusion

All files are in one way or another converted to JS modules that follow similar rules for inclusion as AMD/CommonJS modules. They will be included/bundled if another module refers to them. And since all of them are transformed to JS code there's no magic behind the deceitful syntax:

import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';

They both are transpiled to their equivalent forms with require once the assets have been transformed to JS modules.

Whereas the approach of placing static assets in the imports directory is not mentioned in the official documentation, this way of importing static assets works.

This seems to be at the core of how Meteor works so I'd bet this functionality is going to be there for a long while.

I don't know if to call this a feature maybe a more appropriate description is unexpected consequence but that would only be true from the user's perspective, I assume the people who wrote the code understood this would happen and perhaps even designed it purposely this way.



回答2:

One of the features in Meteor 1.3 is lazy-loading where you place your files in the /imports folder and will not be evaluated eagerly.

Quote from Meteor Guide:

To fully use the module system and ensure that our code only runs when we ask it to, we recommend that all of your application code should be placed inside the imports/ directory. This means that the Meteor build system will only bundle and include that file if it is referenced from another file using an import.

So you can lazy load your css files by importing them from the /imports folder. I would say it's a feature.



回答3:

ES6 export and import functionally are available in Meteor 1.3. You should not be importing HTML and CSS files if you are using Blaze, the current default templating enginge. The import/export functionality is there, but you may be using the wrong approach for building your views.