【问题标题】:How to create standalone bundle of React & React with Addons using Grunt + Browserify?如何使用 Grunt + Browserify 创建独立的 React 和 React 捆绑包?
【发布时间】:2015-06-11 07:17:31
【问题描述】:

我正在尝试配置 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');
};

【问题讨论】:

    标签: gruntjs browserify grunt-browserify


    【解决方案1】:

    我发现这个link 对我很有帮助。按照这种方法,您的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 模块尝试过。

    【讨论】:

      【解决方案2】:

      我正在使用 ReactJS 开发 Sails 0.11.0 项目。我开始我的 grunt watchify 任务来处理重新捆绑/转换我的应用程序代码,同时使用供应商任务来捆绑模块化代码。我还必须将 vendor.js 添加到我的布局文件中。

      我对 grunt 世界还很陌生,所以可能有更有效的方法来做到这一点。

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

      【讨论】:

        【解决方案3】:

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

        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-07
          • 1970-01-01
          • 2017-12-21
          • 2014-12-03
          • 1970-01-01
          • 1970-01-01
          • 2014-04-15
          • 2017-07-15
          相关资源
          最近更新 更多