我正在构建一个网络应用程序,我开始了解并喜欢Browserify。但有一件事让我感到烦恼。
我正在使用一些需要在旧版浏览器中进行填充/填充的ES6功能,例如es6-promise
和object-assign
(npm上的软件包)。
目前我只是将它们加载到需要它们的每个模块中:
var assign = require('object-assign');
var Promise = require('es6-promise');
我知道这绝对不是要走的路。它很难维护,我想透明地使用ES6功能,而不是通过需要“依赖”它们。
加载这些垫片的最终方法是什么?我在互联网上看到了几个例子,但它们各不相同。我可以:
从外部加载它们:
var bundle = browserify();
bundle.require('s6-promise');
// or should I use it bundle.add to make sure the code is runned???
我在这里遇到的问题是我不知道模块的顺序 将在浏览器中加载。因此,填充可能不会发生 但是在需要polyfilled功能的呼叫站点。
这还有一个额外的缺点,即后端代码无法从中受益 polyfills(除非我遗漏了什么)。
使用browserify-shim
或类似的东西。我真的不明白这对ES6功能有什么作用。
手动设置polyfilling:
Object.assign = require('object-assign');
答案 0 :(得分:12)
不要在模块中使用polyfill,这是一种反模式。您的模块应该假设运行时已修补(在需要时),这应该是合同的一部分。一个很好的例子就是ReactJS,它们明确定义了运行时的最低要求,以便库可以工作:http://facebook.github.io/react/docs/working-with-the-browser.html#browser-support-and-polyfills
您可以使用polyfill服务(例如:https://cdn.polyfill.io/)在页面顶部包含一个优化的脚本标记,以确保运行时使用您需要的部分正确修补,而现代浏览器将无法获得惩罚。
答案 1 :(得分:1)
对我有用的一个解决方案是使用bundle.add
我将我的捆绑包分成两部分,app.js
用于应用程序代码,appLib.js
用于库(这一部分将被缓存,因为它不会经常更改)。
请参阅https://github.com/sogko/gulp-recipes/tree/master/browserify-separating-app-and-vendor-bundles
对于appLibs.js
我使用bundle.add
进行polyfill,因为它们必须在加载脚本时加载,而我将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>
这样可以安全地假设即使在初始化其他lib之前也会加载所有polyfill。不确定它是最好的选择,但它对我有用。
我的完整设置:
"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()));
}
如你所见
答案 2 :(得分:1)
或者使用polyfill服务 https://cdn.polyfill.io/v2/docs/
答案 3 :(得分:1)
这是我正在使用的方法。关键是您必须在主条目文件的顶部正确导出polyfill 。
// Using ES6 imports
import './polyfill';
// Using CommonJS style
require('./polyfill');
... // rest of your code goes here
// Using ES6 export
export * from './polyfill';
// Using CommonJS style
var polyfill = require('./polyfill');
... // rest of your code goes here
如果您使用后一种方法中的任何一种,您的polyfills将正确加载。
下面你可以找到我的polyfills的例子。
import './polyfill/Array.from';
import './polyfill/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;
};
}());
}