Использование r.js для упаковки приложения SPA, которое загружает представления, используя 'текст'
Я пытаюсь собрать приложение SPA (requirejs, durandal 2, knockout) в один файл main-build.js, используя grunt, и столкнулся с серьезными проблемами с плагином 'text', который durandal использует для загрузки моего Просмотры.
В dev я успешно использую 'text' для динамической загрузки представлений в соответствии со стандартным способом создания приложений для Durandal. Разница в том, что мне нужно сделать некоторые серверные шаблоны для представлений, и поэтому они на самом деле генерируются динамически.
Имея это в виду, я бы хотел использовать r.js для упаковки моделей приложений, просмотра моделей и сервисов (через плагин grunt-durandal) в один файл, но НЕ для упаковки представлений (.html) и их загрузки. динамически по мере необходимости.
В моем конфиге я использую inlineText: false
опция, которую я проверил, подавляет модули 'text!*' в сборке. Но когда я запускаю приложение, я получаю:
Uncaught TypeError: undefined is not a function
изнутри text.load
на следующей строке:
var parsed = text.parseName(name),
nonStripName = parsed.moduleName +
(parsed.ext ? '.' + parsed.ext : ''),
url = req.toUrl(nonStripName), // EXCEPTION THROWN HERE
Загружаемое имя модуля кажется правильным (это "текст! *"), Но кроме этого я понятия не имею, как приступить к устранению этой проблемы. Что я делаю неправильно?
Моя грубая конфигурация:
/*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']);
};
2 ответа
Миндаль не поддерживает динамическую загрузку. Это не реализует require.toUrl
и не поддерживает плагины асинхронного загрузчика. Джеймс Берк, создатель RequireJS и Almond, ответил об этой проблеме здесь.
Чтобы обойти это, вы можете включить RequireJS в комплект вместо Almond. Смотрите пример здесь. Вы должны создать псевдоним для require.js
в paths
раздел конфигурации r.js как require
это специальное зарезервированное имя зависимости. Затем укажите этот псевдоним в name
поле конфига вместо миндаля. Вы получите что-то вроде этого:
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'
}
Просто хочу добавить немного дополнительного контекста к этому вопросу, так как я продолжал возвращаться к нему для руководства и в конечном итоге решил свою проблему. Я использую Durandal с TypeScript и Grunt для оптимизации. Я получал некоторые исключения вокруг текстового плагина, например
<div data-bind="compose: { model: someObject, view: '../../components/something/something.html' }"></div>
В итоге я удалил относительный путь и просто сделал следующее:
<div data-bind="compose: { model: someObject, view: 'components/something/something.html' }"></div>
С этим я смог получить все с оптимизированной сборкой.