简体   繁体   中英

Backbone + RequireJS: HTML files loaded with RequireJS are interpreted as JS files

I would like to rollout with Backbone.js + Require.js. I can load modules just fine. But when I try and load my .html files using the text! plugin (text.js), I get these errors:

Resource interpreted as Script but transferred with MIME type text/html: " https://host.net/templates/login.html ". require.js:1843

Uncaught SyntaxError: Unexpected token < login.html:1

Uncaught TypeError: Cannot call method 'replace' of undefined underscore.js:1130

Here's the specs of what I'm working with:

Browser : Chrome

Server-Side : PHP w/ Slim

Machines : AWS micro-instance with bitnami AMI // These things ship with production defaults set, so it could be possible that an Apache config isn't right, or a php.ini isn't correct. I just don't know what.

Directory structure:

/
   dev/    // Webroot: Behind basic_auth for dev reasons
       web/
           index.php    // Starts up the Slim router and PHP backend 
               app/
                   app.html    // The entry point for our SPA, data-main defined here
       js/   // At root for module accessibility in case of reuse
           apps/
           app/
               main.js  // Here we do require.config and bootstrapping
           app.js   
           router.js
           text.js
           modules/
               models/
                   login.js
               views/
                   login.js
           lib/
                   backbone/
                   backbone.js
               jquery/
                   jquery.js
               require/
               require.js
               underscore/
               underscore.js
       templates/   // Also at root for access to reuseable markup, such as login.html
        login.html

Here's some code I believe relevant:

/js/apps/app/main.js

requirejs.config({
 baseUrl: '/js/apps/app',

  shim: {
    'backbone' : {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
    },
    'underscore' : {
      exports: '_'
    },
    'jquery' : {
      exports: '$'
    },
    'backbone.ajaxcommands' : {
      deps : ['backbone', 'underscore', 'jquery'],
    }
  },

  paths: {
    jquery:     'lib/jquery/jquery-1.9.1.min',
    underscore: 'lib/underscore/underscore',
    backbone:   'lib/backbone/backbone-min'
  },

  config: {
    text: {
      useXhr: function (url, protocol, hostname, port)
      {
        protocol = 'https';
  //      return true; 
      }
    }
  }
});
require(['app'],function(App){
    App.initialize(); 
    App.start(); 
  }
); 

/js/apps/app/modules/views/login.js

define([
  'backbone',
  'underscore',
  'modules/views/login',
  'text!/templates/login.html'
], function(Backbone, _, Login, loginTemplate){
  var LoginView = Backbone.View.extend({
    el: $("#login-form"),

    events: {
      "click #login": "login"
    },

    template: _.template(loginTemplate),

    initialize: function(){
      var self = this; 

      this.username = $("#username"); 
      this.password = $("#password"); 

      this.username.change(function(e){
        self.model.set({username: $(e.currentTarget).val()});
      }); 

      this.password.change(function(e){
        self.model.set({password: $(e.currentTarget).val()}); 
      });

    },

    login: function(){
      var user= this.model.get('username'); 
      var pword= this.model.get('password'); 
      if(this.model.save() == false)
      {
        console.log("We got issues loggin' in"); 
      }
      else
      {
        console.log("We should have cookies now"); 
      }
    }

    //render: function(){
    //  this.$el.append(this.template); 
    //}
  });
  return LoginView; 
}); 

/templates/login.html

`<div>hi</div>`

Work to find solution: When I look at the Chrome debugger, under the 'network' tab, I see that indeed login.html was retrieved, but chrome thinks it's a JS file.

I stepped through the code using a breakpoint, and found that in Require.js 1843 the node object on that line has an attribute called 'outerHtml' equal to a '' tag with a bunch of attributes. So perhaps it is wrapping my html in a tag? When I look under the preview tab in the network tab of the debugger, I indeed see the markup. If login.html has valid js code, then I don't get the syntax error. I still get the underscore.js error because it's malformed html though.

I've tried these solutions: Chrome says "Resource interpreted as script but transferred with MIME type text/plain.", what gives?

Moving the js/template code under the project (made paths relative instead of absolute). Everything seemed to work, but text.js would append .js to the end of login.html, so I got a 404 not found. This is because of cross-domain access aparentally?

various config options with require.config, including setting the baseURL

tons of other tweaks that I have unfortunately forgotten. This has been really frustrating.

Thank you for your time.

Edit: I've put a stand-alone that exhibits the same behavior I'm seeing on my drive . The file structure should be:

/
    index.html //This can actually be anywhere visible on the web
    so-js/
        ...
    so-templates/
        ...

notice that the so-js/ and so-templates are at the webroot, the index file can be where ever.

The problem is caused by this line:

   config: {
    text: {
      useXhr: function (url, protocol, hostname, port)
      {
        protocol = 'https';
      //  return true; 
      }
    }
  }

It's overriding text!'s useXhr method, which is meant to return a boolean to determine whether a Javascript version of the text file is available. If it is, it'll return false, otherwise true.

Because you're not returning anything and undefined is falsy, it takes that to mean that it should load it in a <script> tag, which is why you get the MIME type warning and the subsequent errors.

If you remove this entire config property the errors go away and loginTemplate is populated in login.html.

also you can use grunt to generate module from html template

like this:

templates/hello.html

<div>hello world!</div>

to

js/templates/hello.js

define('templates/hello', '<div>hello world!</div>')

grunt task config:

function converTemplate(srcDir){
    return function(){
        grunt.file.recurse(srcDir, function(abspath, rootdir, subdir, filename){
            if(/^.*\.html$/.test(filename)){
                if(subdir){
                    subdir = subdir.replace(/\\/g, '/');
                }

                var content = grunt.file.read(abspath),
                    targetPath = 'js/templates' + '/' + (subdir ? subdir + '/': '') + filename.replace('.html', '.js'),
                    moduleName = 'templates/' + (subdir ? subdir + '/': '') + filename.replace('.html', '');
                content = content.replace(/[ \t]*\r?\n[ \t]*/g, '');
                content = content.replace(/'/g, '\\\'');
                content = "define('"+moduleName+"', [], '"+content+"');";
                subdir && grunt.file.mkdir('js/templates/' + (subdir ? subdir + '/': ''));
                grunt.file.write(targetPath, content);
            }
        });
    };
}

grunt.registerTask('template', 'conver template', converTemplate('templates'));

Directory structure:

templates/
    hello.html
js/
    templates/
        hello.js

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM