使用Browserify加载polyfill和垫片的正确方法是什么?

我正在build立一个networking应用程序,我越来越知道和喜欢Browserify。 有一件事已经窃听了我。

我正在使用一些ES6function,需要在旧版本的浏览器中进行es6-promise / es6-promise比如es6-promiseobject-assign (npm上的包)。

目前我只是将它们加载到每个需要它们的模块中:

 var assign = require('object-assign'); var Promise = require('es6-promise'); 

我知道这绝对不是要走的路。 这很难维护,我想透明地使用ES6的function,而不是通过需求来“依赖”它们。

什么是加载这样的垫片的明确方式? 我已经看到了互联网上的几个例子,但他们都是不同的。 我可以:

  • 在外部加载它们:

     var bundle = browserify(); bundle.require('s6-promise'); // or should I use it bundle.add to make sure the code is runned??? 

    我在这里的问题是,我不知道模块将在浏览器中加载的顺序。 因此,在需要多填充function的呼叫站点,填充可能还没有发生。

    这有额外的缺点,后端代码不能从这些polyfills中受益(除非我失去了一些东西)。

  • 使用browserify-shim或类似的东西。 我真的不明白这是如何工作的ES6function。

  • 手动设置polyfilling:

     Object.assign = require('object-assign'); 

在模块中不要求使用polyfills,这是一种反模式。 您的模块应该假定运行时已经打上补丁(在需要的时候),这应该是合同的一部分。 一个很好的例子就是ReactJS,它们明确定义了运行时的最低要求,以便库可以工作: http : //facebook.github.io/react/docs/working-with-the-browser.html#browser -支持和-polyfills

您可以使用polyfill服务(例如: https : //cdn.polyfill.io/ )在页面的顶部包含一个优化的脚本标记,以确保运行时可以正确地修补所需的部分,而现代浏览器将不受惩罚。

一个解决scheme,为我工作是使用bundle.add

我把我的软件包分为两部分, app.js用于应用程序代码, appLib.js用于库(这一部分将被caching,因为它不会经常改变)。

https://github.com/sogko/gulp-recipes/tree/master/browserify-separating-app-and-vendor-bundles

对于appLibs.js我使用bundle.add进行bundle.add ,因为它们必须在加载脚本时加载,而我使用其他库的bundle.require ,只有在app.js中需要时才加载。

 polyfills.forEach(function(polyfill) { b.add(polyfill); }); libs.forEach(function(lib) { b.require(lib); }); 

该页面按顺序加载这两个包:

 <head> ... <script type="text/javascript" src="appLibs.js" crossorigin></script> <script type="text/javascript" src="app.js" crossorigin></script> ... </head> 

这样,假设所有的polyfill都会在其他lib被初始化之前加载,这似乎是安全的。 不知道这是最好的select,但它为我工作。

我的完整设置:

 "use strict"; var browserify = require('browserify'); var gulp = require('gulp'); var gutil = require('gulp-util'); var handleErrors = require('../util/handleErrors'); var source = require('vinyl-source-stream'); var watchify = require("watchify"); var livereload = require('gulp-livereload'); var gulpif = require("gulp-if"); var buffer = require('vinyl-buffer'); var uglify = require('gulp-uglify'); // polyfills should be automatically loaded, even if they are never required var polyfills = [ "intl" ]; var libs = [ "ajax-interceptor", "autolinker", "bounded-cache", "fuse.js", "highlight.js", "imagesloaded", "iscroll", "jquery", "keymaster", "lodash", "medium-editor", "mime-db", "mime-types", "moment", "packery", "q", "rangy", "spin.js", "steady", "store", "string", "uuid", "react-dnd" ]; // permits to create a special bundle for vendor libs // See https://github.com/sogko/gulp-recipes/tree/master/browserify-separating-app-and-vendor-bundles gulp.task('browserify-libs', function () { var b = browserify({ debug: true }); polyfills.forEach(function(polyfill) { b.add(polyfill); }); libs.forEach(function(lib) { b.require(lib); }); return b.bundle() .on('error', handleErrors) .pipe(source('appLibs.js')) // TODO use node_env instead of "global.buildNoWatch" .pipe(gulpif(global.buildNoWatch, buffer())) .pipe(gulpif(global.buildNoWatch, uglify())) .pipe(gulp.dest('./build')); }); // Inspired by http://truongtx.me/2014/08/06/using-watchify-with-gulp-for-fast-browserify-build/ gulp.task('browserify',['cleanAppJs','browserify-libs'],function browserifyShare(){ var b = browserify({ cache: {}, packageCache: {}, fullPaths: true, extensions: ['.jsx'], paths: ['./node_modules','./src/'], debug: true }); b.transform('reactify'); libs.forEach(function(lib) { b.external(lib); }); // TODO use node_env instead of "global.buildNoWatch" if ( !global.buildNoWatch ) { b = watchify(b); b.on('update', function() { gutil.log("Watchify detected change -> Rebuilding bundle"); return bundleShare(b); }); } b.on('error', handleErrors); //b.add('app.js'); // It seems to produce weird behaviors when both using "add" and "require" // expose does not seem to work well... see https://github.com/substack/node-browserify/issues/850 b.require('app.js',{expose: 'app'}); return bundleShare(b); }); function bundleShare(b) { return b.bundle() .on('error', handleErrors) .pipe(source('app.js')) .pipe(gulp.dest('./build')) // TODO use node_env instead of "global.buildNoWatch" .pipe(gulpif(!global.buildNoWatch, livereload())); } 

如你看到的

这是我正在使用的方法。 关键是你必须在你的主入口文件的顶部正确地导出你的polyfill

以下将不起作用:

 // Using ES6 imports import './polyfill'; // Using CommonJS style require('./polyfill'); ... // rest of your code goes here 

您实际上需要导出polyfill:

 // Using ES6 export export * from './polyfill'; // Using CommonJS style var polyfill = require('./polyfill'); ... // rest of your code goes here 

如果您使用后面的方法之一,您的polyfills将正确加载。

下面你可以find我的polyfills的例子。

polyfill.js:

 import './polyfill/Array.from'; import './polyfill/Object.assign'; 

Object.assign:

 if (typeof Object.assign !== 'function') { (function iife() { const ObjectHasOwnProperty = Object.prototype.hasOwnProperty; /** * Copy the values of all enumerable own properties from one source * object to a target object. It will return the target object. * @param {Object} target The target object. * @param {Object} source The source object. * @return {Object} The target object. */ function shallowAssign(target, source) { if (target === source) return target; Object.keys(source).forEach((key) => { // Avoid bugs when hasOwnProperty is shadowed if (ObjectHasOwnProperty.call(source, key)) { target[key] = source[key]; } }); return target; } /** * Copy the values of all enumerable own properties from one source * object to a target object. It will return the target object. * @param {Object} target The target object. * @param {Object} source The source object. * @return {Object} The target object. */ Object.assign = function assign(target, ...sources) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object'); } sources.forEach((source) => { if (source !== null) { // Skip over if undefined or null shallowAssign(Object(target), Object(source)); } }); return target; }; }()); }