我如何将 React 与一个重要的 AngularJS 应用程序和现有的 grunt 配置集成?

How do I integrate React with a non-trivial AngularJS app and existing grunt configurations?

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

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

到目前为止,另一位开发人员已经开始使用 React starter kit 开发 React 组件,其中包括 React、Webpack、Babel、一个可以启动的开发服务器以及各种其他功能。

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

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

以下是解决此问题所需的步骤(component/file 更改名称以保护无辜者):

  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,并使用 link 将其配置到 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>

经过所有这些步骤,终于成功了!如果将来有人遇到此问题,希望这将有助于将这些步骤放在一起。

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

这是 React 的完整 grunt 配置:

它创建了一个单独的任务以使用唯一的名称进行反应,因此您可以随意操作它们。

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

}