Как создать автономный пакет React & React с аддонами, используя Grunt + Browserify?
Я пытаюсь настроить Grunt & Browserify для вывода отдельного пакета, содержащего, помимо прочего, React как модуль CommonJS, чтобы на него могли ссылаться другие пакеты.
Проблема, с которой я столкнулся сейчас, заключается в том, что псевдонимы не работают. Несмотря на то, что в моем внешнем пакете указаны псевдонимы vendor
ниже, и указав, что эти модули должны загружаться извне во всех других моделях, я все еще получаю сообщение об ошибке во время выполнения, утверждающее, что модуль "реагировать" не может быть найден.
Было бы здорово, если бы кто-нибудь знал, что может быть не так в моем синтаксисе grunt-browserify:
var externals = [
'react',
'react/addons',
'jquery',
'backbone',
'react-router'
];
module.exports = function(grunt) {
grunt.config.set('browserify', {
main: {
src: 'assets/js/main.jsx',
dest: '.tmp/public/js/main.js',
options: {
debug: true,
extensions: ['.jsx'],
external: externals,
transform: [
['babelify', {'stage': 0}]
]
}
},
signup: {
src: 'assets/js/signup.jsx',
dest: '.tmp/public/js/signup.js',
options: {
debug: true,
extensions: ['.jsx'],
external: externals,
transform: [
['babelify', {'stage': 0}]
]
}
},
login: {
src: 'assets/js/login.jsx',
dest: '.tmp/public/js/login.js',
options: {
debug: true,
insertGlobals: true,
extensions: ['.jsx'],
external: externals,
transform: [
['babelify', {'stage': 0}]
]
}
},
vendor: {
src: [
'./node_modules/react/dist/react.js',
'./node_modules/react/dist/react-with-addons.js',
'./node_modules/jquery/dist/jquery.js',
'./node_modules/backbone/backbone.js',
],
dest: '.tmp/public/js/dependencies/vendor.js',
options: {
debug: false,
alias: {
'react:': './node_modules/react/dist/react.js',
'react/addons': './node_modules/react/dist/react-with-addons.js',
'jquery': './node_modules/jquery/dist/jquery.js',
'backbone': './node_modules/backbone/backbone.js',
'react-router': './node_modules/react-router/lib/index.js'
},
shim: {
react_router: {
path: './node_modules/react-router/lib/index.js',
exports: 'react-router'
}
},
external: null
}
}
});
grunt.loadNpmTasks('grunt-browserify');
};
3 ответа
Я нашел эту ссылку полезной для меня. Следуя этому подходу, ваш vendor
раздел должен выглядеть так
vendor: {
src: ['.'],
dest: '.tmp/public/js/dependencies/vendor.js',
options: {
debug: false,
alias: externals.map(function(module) {
return module + ':';
}),
shim: {
react_router: {
path: './node_modules/react-router/lib/index.js',
exports: 'react-router'
}
},
external: null
}
}
Я не уверен насчет shim
раздел выше, потому что я пробовал это только для react
модуль.
Вот полная конфигурация Grunt для React:
Я создал фиктивный проект, чтобы вы могли его протестировать.
Gruntfile.js для проекта:
module.exports = function (grunt) {
let concat = {};
let clean = {};
let uglify = {};
let copy = {};
let htmlmin = {};
let cssmin = {};
let browserify = {};
let watch = {};
let template = {};
let run = {};
/* React configuration. */
const reactSourcePath = './source';
const reactCompiledPath = './client';
const reactHtmlPathDest = './client/index.html'
const reactTargetName = "react";
const reactFileName = "react_main";
/* ### TASK CONFIGURATIONS ### */
/* Clean compiled files. */
clean[reactTargetName] = [
`${reactCompiledPath}`
];
/* Concatenate all CSS files to one. */
const cssConcatSourceTemplate = `${reactSourcePath}/**/**.css`;
const cssDestinationFile = `${reactCompiledPath}/css/${reactFileName}.css`;
concat[reactTargetName] = {
src: [cssConcatSourceTemplate],
dest: cssDestinationFile
};
/* Convert JSX to JS, prepare JS files for a browser and copy to the destination. */
const jsSourceFile = `${reactSourcePath}/index.js`;
const jsDestinationFile = `${reactCompiledPath}/js/${reactFileName}.js`;
browserify[reactTargetName] = {
options: {
transform: [['babelify', {presets: ['es2015', 'react']}]]
},
files: {
[jsDestinationFile]: jsSourceFile
}
};
/* Replace js/css placeholders and copy html file to destination. */
const applicationData = {
css: [
'./css/react_main.css'
],
js: [
'./js/react_main.js'
]
};
var jsFiles = "";
var cssFiles = "";
applicationData.css.forEach(function(item) {
cssFiles = cssFiles + `\n<link rel="stylesheet" type="text/css" href=${item}>`;
});
applicationData.js.forEach(function(item) {
jsFiles = jsFiles + `\n<script type="text/javascript" src=${item}></script>`;
});
template[reactTargetName] = {
options: {
data: {
appName: '<%= pkg.name %>' + '-react',
productVersion: '<%= pkg.version %>',
reactEmbeddedCssFiles: cssFiles,
reactEmbeddedJsFiles: jsFiles
}
},
files: {
[`${reactHtmlPathDest}`]: `${reactSourcePath}/index.template.html`,
}
};
/* Uglify react JS file. */
uglify[reactTargetName] = {
files: {
[jsDestinationFile]: jsDestinationFile
}
};
/* Copy bootstrap CSS/JS files. */
copy[reactTargetName] = {
files: {
[`${reactCompiledPath}/css/bootstrap.min.css`]: 'node_modules/bootstrap/dist/css/bootstrap.min.css',
[`${reactCompiledPath}/js/bootstrap.min.js`]: 'node_modules/bootstrap/dist/js/bootstrap.min.js',
[`${reactCompiledPath}/js/jquery.min.js`]: 'node_modules/jquery/dist/jquery.min.js',
}
}
/* Minify HTML files. */
htmlmin[reactTargetName] = {
options: {
removeComments: true,
collapseWhitespace: true
},
files: {
[`${reactHtmlPathDest}`]: `${reactHtmlPathDest}`
}
};
/* Minify react CSS file. */
cssmin[reactTargetName] = {
files: {
[cssDestinationFile]: cssDestinationFile
}
};
/* Watch for any changes in react app.
There are three separate watches for css, js, and html files. */
watch[reactTargetName + '_css'] = {
files: [`${reactSourcePath}/**/*.css`],
tasks: [`concat:${reactTargetName}`],
options: {
livereload: true
}
};
watch[reactTargetName + '_js'] = {
files: [`${reactSourcePath}/**/*.js`],
tasks: [`browserify:${reactTargetName}`],
options: {
livereload: true
}
};
watch[reactTargetName + '_hmtl'] = {
files: [`${reactSourcePath}/**/*.html`],
tasks: [`template:${reactTargetName}`],
options: {
livereload: true
}
};
/* Jest tests */
jestTestsTaskName = reactTargetName + '_jest_tests';
run[jestTestsTaskName] = {
exec: 'npm test'
};
/* Generate task names for react. */
var reactTasks = {
debug: [
"clean",
"browserify",
"concat",
"copy",
"template"
].map(x => x + `:${reactTargetName}`),
release: [
"clean",
"browserify",
"concat",
"copy",
"template",
"htmlmin",
"uglify",
"cssmin"
].map(x => x + `:${reactTargetName}`)
};
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch:watch,
copy:copy,
concat:concat,
clean:clean,
uglify:uglify,
template:template,
browserify: browserify,
htmlmin: htmlmin,
cssmin: cssmin,
run:run
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-template');
grunt.loadNpmTasks("grunt-browserify");
grunt.loadNpmTasks("grunt-contrib-htmlmin");
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-run');
grunt.registerTask('react_build_debug', reactTasks.debug);
grunt.registerTask('react_build_release', reactTasks.release);
}
Я работаю над проектом Sails 0.11.0 с ReactJS. Я запускаю задачу grunt watchify для обработки повторного раскладывания / преобразования кода моего приложения, используя задачу вендора для связывания запланированного кода. Мне также пришлось добавить vendor.js в мой файл макета.
Я довольно новичок в мире ворчания, поэтому могут быть более эффективные способы сделать это.
browserify.js
var externals = [
'react',
'react/addons',
'jquery',
'react-router',
'events'
]
module.exports = function(grunt){
grunt.config.set('browserify', {
dist: {
options: {
external: externals,
transform: [
['babelify', {
loose: 'all'
}]
]
},
files: {
".tmp/public/js/bundle.js": ["assets/js/bundle.js", "react/**/*"]
}
},
vendor: {
src: [
'./node_modules/react/dist/react.js',
'./node_modules/react/dist/react-with-addons.js',
'./node_modules/jquery/dist/jquery.js',
'./node_modules/react-router/lib/index.js',
'./node_modules/events/events.js'
],
dest: '.tmp/public/js/dependencies/vendor.js',
options: {
alias: {
'react': './node_modules/react/dist/react.js',
'react/addons': './node_modules/react/dist/react-with-addons.js',
'jquery': './node_modules/jquery/dist/jquery.js',
'react-router': './node_modules/react-router/lib/index.js',
'events': './node_modules/events/events.js'
}
}
}
});
grunt.loadNpmTasks('grunt-browserify');
}
Задачи / конфигурация:
module.exports = function(grunt) {
grunt.config.set('watch', {
api: {
// API files to watch:
files: ['api/**/*', '!**/node_modules/**']
},
assets: {
// Assets to watch:
files: ['assets/**/*', 'tasks/pipeline.js', '!**/node_modules/**'],
// When assets are changed:
tasks: ['syncAssets' , 'linkAssets', 'browserify:dist']
},
react: {
files: ['react/**/*'],
tasks: ['browserify:dist']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
};