如何将React与非平凡的AngularJS应用程序和现有的grunt配置集成?

时间:2017-05-02 04:08:11

标签: angularjs reactjs webpack ngreact

我目前正在创建一个新的React组件,需要与现有的Angular 1.x应用程序集成,该应用程序本身已经拥有以Grunt为中心的构建过程。在某些时候,应用程序可能会完全迁移到React,但目前只有这一个组件将在React中完成。

Angular应用程序使用grunt围绕它构建了一个广泛的构建过程,因此新组件的任何构建都必须能够通过grunt。

到目前为止,另一位开发人员已经开始使用React starter kit开发React组件,其中包括React,Webpack,Babel,它可以启动的开发服务器,以及其他各种各样的铃声和口哨。

这是我最初的研究成果,以及目前的障碍:

  • 看起来ngReact将是一个很好的工具,可以将React组件集成到应用程序中。
  • 当前的React设置可以使用React应用程序的嵌入式构建(没有真正可用的构建文件)来启动开发服务器,也可以创建一个缩小并准备发布的“生产”构建。这不太有效,因为我需要它来创建当前应用程序和ngReact可以使用的东西。
  • React应用程序正在使用Webpack捆绑。有一个可以运行webpack配置的grunt工具。

如何将所有这些移动组件放在一起,将React组件捆绑到现有的Angular应用程序中,同时保留尽可能多的现有配置?

2 个答案:

答案 0 :(得分:0)

以下是解决此问题所需的步骤(更改了组件/文件名以保护无辜者):

  1. 获取Webpack以输出稍后我可以使用ngReact的构建。在webpack.config.js中,我不得不将output:{ path: 'build' }更改为build_react,以便以后不会被Angular构建过程覆盖。另外,在module.loaders中,我添加了下面的预设行,以便输出非ES6,有效的JavaScript:

    //Process JS with Babel.
    {
      test: /\.(js|jsx)$/,
      include: paths.appSrc,
      loader: 'babel-loader',
      query: {
        presets: ['env', 'react'],
      }
    }
    
  2. 使用npm安装grunt-webpack,并使用指向webpack配置文件的链接对其进行配置。该文件将被引入具有所有其他类似配置的grunt initConfig。

    //webpackConfig.js
    module.exports.name = 'webpack';
    const webpackConfig = require('../webpack.config.dev.js');
    
    /**
     * Pull in our react webpack build
     */
    module.exports.getConfiguration = function(grunt) {
        return {
            options: {
                stats: !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
            },
            prod: webpackConfig,
            dev: webpackConfig
        };
    };
    
  3. 在index.html文件中包含react.jsreact-dom.jsng-react.min.js以及#1中内置的捆绑包。 (这些将在以后连接到生成版本的一个文件中。)至关重要,在所有Angular文件(包括所有应用程序.js之后)必须包含文件,以便React组件可以访问angular对象以进行下一步。

  4. 将此行添加到React组件,以便ngReact可以工作:angular.module('angularModule').value('MyComponent', MyComponent);
  5. 将以下内容添加到将包含React组件的Angular模板中:<react-component name="MyComponent"></react-component>
  6. 完成所有这些步骤后,它终于奏效了!希望如果有人在将来遇到这个问题,这将有助于某些人将这些步骤放在一起。

答案 1 :(得分:0)

在将React集成到当前的AngularJs构建过程中,我遇到了同样的问题,但是作为一个单独的应用程序。可能会对您有帮助。

这是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);

}