Using r.js to package a SPA application that loads views using 'text'

余生颓废 提交于 2019-12-05 16:05:07

问题


I'm attempting to build a SPA application (requirejs, durandal 2, knockout) into a single main-build.js file using grunt, and I'm running into serious issues with the 'text' plugin that durandal is using to load my views.

In dev, I'm successfully using 'text' to load views dynamically as per the standard way of building durandal apps. The difference is that I need to do some server side templating for the views and so they are actually being dynamically generated.

With that in mind I'd like to use r.js to package the apps models, view models and services (via the grunt-durandal plugin) into a single file, but to NOT package the views (.html) and still load them dynamically as needed.

In my grunt config, I'm using the inlineText: false option - which I have checked is suppressing the 'text!*' modules in the build. But when I run the application I'm getting:

Uncaught TypeError: undefined is not a function from inside text.load on the following line:

var parsed = text.parseName(name),
            nonStripName = parsed.moduleName +
                (parsed.ext ? '.' + parsed.ext : ''),
            url = req.toUrl(nonStripName), // EXCEPTION THROWN HERE

The module name being loaded seems to be correct (its a 'text!*' one) but beyond that I have no idea how to proceed to debug this issue. What am I doing wrong?

My grunt configuraiton is:

/*global module, require */

module.exports = function (grunt) {
'use strict';

// library that allows config objects to be merged together
var mixIn = require('mout/object/mixIn');

var requireConfig = {
    baseUrl: 'App/',
    paths: {
        'jquery': '../Scripts/jquery-2.1.0',
        'knockout': '../Scripts/knockout-3.1.0',
        'text': '../Scripts/text',
        'durandal': '../Scripts/durandal',
        'plugins': '../Scripts/durandal/plugins',
        'transitions': '../Scripts/durandal/transitions',
    }
};

grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
        options: {
            "-W099": true, // allowed mixed tabs and spaces
            "-W004": true, // needed to avoid errors where TS fakes inheritance
            ignores: [
                'App/main-built.js' // ingore the built/compacted file
            ]
        },
        all: [ // run jshint on these files
            'gruntfile.js',
            'App/**/*.js'
        ]
    },
    // TODO: could add jasmine here to do JS testing
    clean: {
        build: ['build/*']
    },
    copy: {
        scripts: {
            src: 'Scripts/**/**',
            dest: 'build/'
        }
    },
    durandal: {
        main: {
            src: [
                'App/**/*.*',
                '!App/main-built.js', // ignore the built file!
                'Scripts/durandal/**/*.js'
            ],
            options: {
                name: '../Scripts/almond-custom',
                baseUrl: requireConfig.baseUrl,
                mainPath: 'App/main',
                paths: mixIn(
                    {},
                    requireConfig.paths,
                    { 'almond': '../Scripts/almond-custom.js' }),
                exclude: [],
                inlineText: false, // prevent bundling of .html (since we are dynamically generating these for content)
                optimize: 'none', // turn off optimisations - uglify will run seperately by grunt to do this
                out: 'build/app/main-built.js'
            }
        }
    },
    uglify: {
        options: {
            banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n' +
                '* Copyright (c) <%= grunt.template.today("yyyy") %> Kieran Benton \n' +
                '*/\n'
        },
        build: {
            src: 'build/App/main.js',
            dest: 'build/App/main-built.js'
        }
    }
}
);

// loading plugin(s)
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-open');
grunt.loadNpmTasks('grunt-durandal');

// only one grunt task
grunt.registerTask('build', [
    'jshint',
    'clean',
    'copy',
    'durandal:main']);
};

回答1:


Almond doesn't support dynamic loading. It doesn't implement require.toUrl and doesn't support asynchronous loader plugins. James Burke, the creator of RequireJS and Almond, answered about the same issue here.

To work around this, you can include RequireJS into the bundle instead of Almond. See an example here. You have to create an alias for require.js in the paths section of the r.js config as require is a special reserved dependency name. Then, specify this alias in the name field of the config instead of Almond. You'll get something like this:

options: {
    name: 'requireLib', // use the alias
    baseUrl: requireConfig.baseUrl,
    mainPath: 'App/main',
    paths: mixIn(
        {},
        requireConfig.paths,
        { 'requireLib': '../Scripts/require.js' }), // declare the alias
    exclude: [],
    inlineText: false,
    optimize: 'none',
    out: 'build/app/main-built.js'
}



回答2:


Just want to add some extra context to this question since I kept coming back to it for guidance and eventually solved my problem. I'm using Durandal w/ TypeScript and grunt for optimization. I was getting some exceptions around the text plugin, e.g.

<div data-bind="compose: { model: someObject, view: '../../components/something/something.html' }"></div>

I ended up removing the relative path and just doing the following:

<div data-bind="compose: { model: someObject, view: 'components/something/something.html' }"></div>

With that, I was able to get everything with the optimized build working.



来源:https://stackoverflow.com/questions/23293185/using-r-js-to-package-a-spa-application-that-loads-views-using-text

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!