在 Angular 项目中使用 lodash 时的优化救助警告
Optimization bailouts warning when using lodash in Angular project
x component depends on 'lodash'. CommonJS or AMD dependencies can
cause optimization bailouts. For more info see:
https://angular.io/guide/build#configuring-commonjs-dependencies
这就是我在 x 中使用 lodash 的方式 component.ts
import * as _ from 'lodash';
....
....
foo(){
this.myObject = _.mapValues(this.myObject , () => true);
}
如何摆脱这个警告?
您可以使用 lodash-es(ES 模块化)而不是使用 lodash 的 CommonJS 变体。
你也可以考虑完全不使用lodash。从这篇文章 10 Lodash Features You Can Replace with ES6 我们有以下功能可以替换为 ES6
- 映射、过滤、缩减
这些收集方法使数据转换变得轻而易举,并且几乎得到了普遍支持。我们可以将它们与箭头函数配对,以帮助我们为 Lodash 提供的实现编写简洁的替代方案:
_.map([1, 2, 3], function(n) { return n * 3; });
// [3, 6, 9]
_.reduce([1, 2, 3], function(total, n) { return total + n; }, 0);
// 6
_.filter([1, 2, 3], function(n) { return n <= 2; });
// [1, 2]
// becomes
[1, 2, 3].map(n => n * 3);
[1, 2, 3].reduce((total, n) => total + n);
[1, 2, 3].filter(n => n <= 2);
也不止于此。如果我们使用现代浏览器,我们也可以使用 find
、some
、every
和 reduceRight
。
- 头尾
解构语法允许我们在没有实用函数的情况下获取列表的头部和尾部
_.head([1, 2, 3]);
// 1
_.tail([1, 2, 3]);
// [2, 3]
// becomes
const [head, ...tail] = [1, 2, 3];
同样可以得到初始元素和最后一个元素:
_.initial([1, 2, 3]);
// -> [1, 2]
_.last([1, 2, 3]);
// 3
// becomes
const [last, ...initial] = [1, 2, 3].reverse();
如果你觉得反向改变数据结构很烦人,那么你可以在调用反向之前使用扩展运算符克隆数组:
const xs = [1, 2, 3];
const [last, ...initial] = [...xs].reverse();
- 休息和伸展
rest
和 spread
函数允许我们定义和调用接受可变数量参数的函数。 ES6 为这两种操作引入了专用语法:
var say = _.rest(function(what, names) {
var last = _.last(names);
var initial = _.initial(names);
var finalSeparator = (_.size(names) > 1 ? ', & ' : '');
return what + ' ' + initial.join(', ') +
finalSeparator + _.last(names);
});
say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"
// becomes
const say = (what, ...names) => {
const [last, ...initial] = names.reverse();
const finalSeparator = (names.length > 1 ? ', &' : '');
return `${what} ${initial.join(', ')} ${finalSeparator} ${last}`;
};
say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"
- 咖喱
如果没有像 [TypeScript][5]
或 [Flow][6]
这样的高级语言,我们就不能给我们的函数类型签名,这使得柯里化变得非常困难。当我们收到柯里化函数时,很难知道已经提供了多少参数,接下来我们需要提供哪些参数。使用箭头函数,我们可以显式定义柯里化函数,让其他程序员更容易理解它们:
function add(a, b) {
return a + b;
}
var curriedAdd = _.curry(add);
var add2 = curriedAdd(2);
add2(1);
// 3
// becomes
const add = a => b => a + b;
const add2 = add(2);
add2(1);
// 3
这些明确柯里化的箭头函数对于调试特别重要:
var lodashAdd = _.curry(function(a, b) {
return a + b;
});
var add3 = lodashAdd(3);
console.log(add3.length)
// 0
console.log(add3);
// function (a, b) {
// /* [wrapped with _.curry & _.partial] */
// return a + b;
// }
// becomes
const es6Add = a => b => a + b;
const add3 = es6Add(3);
console.log(add3.length);
// 1
console.log(add3);
// function b => a + b
如果我们使用像 lodash/fp
或 ramda
这样的函数库,我们也可以使用箭头来消除对自动咖喱样式的需要:
_.map(_.prop('name'))(people);
// becomes
people.map(person => person.name);
- 部分
与柯里化一样,我们可以使用箭头函数使部分应用变得简单和明确:
var greet = function(greeting, name) {
return greeting + ' ' + name;
};
var sayHelloTo = _.partial(greet, 'hello');
sayHelloTo('fred');
// "hello fred"
// becomes
const sayHelloTo = name => greet('hello', name);
sayHelloTo('fred');
// "hello fred"
也可以将剩余参数与扩展运算符一起使用以部分应用可变参数函数:
const sayHelloTo = (name, ...args) => greet('hello', name, ...args);
sayHelloTo('fred', 1, 2, 3);
// "hello fred"
- 运算符
Lodash 附带了一些函数,将语法运算符重新实现为函数,以便将它们传递给集合方法。
在大多数情况下,箭头函数使它们变得足够简单和简短,我们可以将它们定义为内联:
_.eq(3, 3);
// true
_.add(10, 1);
// 11
_.map([1, 2, 3], function(n) {
return _.multiply(n, 10);
});
// [10, 20, 30]
_.reduce([1, 2, 3], _.add);
// 6
// becomes
3 === 3
10 + 1
[1, 2, 3].map(n => n * 10);
[1, 2, 3].reduce((total, n) => total + n);
- 路径
Lodash 的许多函数都将路径作为字符串或数组。我们可以使用箭头函数来创建更多可重用的路径:
var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// [3, 4]
_.at(['a', 'b', 'c'], 0, 2);
// ['a', 'c']
// becomes
[
obj => obj.a[0].b.c,
obj => obj.a[1]
].map(path => path(object));
[
arr => arr[0],
arr => arr[2]
].map(path => path(['a', 'b', 'c']));
因为这些路径“只是函数”,我们也可以组合它们:
const getFirstPerson = people => people[0];
const getPostCode = person => person.address.postcode;
const getFirstPostCode = people => getPostCode(getFirstPerson(people));
我们甚至可以创建接受参数的高阶路径:
const getFirstNPeople = n => people => people.slice(0, n);
const getFirst5People = getFirstNPeople(5);
const getFirst5PostCodes = people => getFirst5People(people).map(getPostCode);
- 选择
pick 实用程序允许我们select 我们想要从目标对象获得的属性。我们可以使用解构和 shorthand 对象文字来获得相同的结果:
var object = { 'a': 1, 'b': '2', 'c': 3 };
return _.pick(object, ['a', 'c']);
// { a: 1, c: 3 }
// becomes
const { a, c } = { a: 1, b: 2, c: 3 };
return { a, c };
- 常量,身份,Noop
Lodash 提供了一些用于创建具有特定行为的简单函数的实用程序:
_.constant({ 'a': 1 })();
// { a: 1 }
_.identity({ user: 'fred' });
// { user: 'fred' }
_.noop();
// undefined
我们可以使用箭头内联定义所有这些函数:
const constant = x => () => x;
const identity = x => x;
const noop = () => undefined;
或者我们可以将上面的例子重写如下:
(() => ({ a: 1 }))();
// { a: 1 }
(x => x)({ user: 'fred' });
// { user: 'fred' }
(() => undefined)();
// undefined
- 链接与流
Lodash 提供了一些函数来帮助我们编写链式语句。在许多情况下,内置的集合方法 return 一个可以直接链接的数组实例,但在某些情况下,方法会改变集合,这是不可能的。
但是,我们可以定义与箭头函数数组相同的转换:
_([1, 2, 3])
.tap(function(array) {
// Mutate input array.
array.pop();
})
.reverse()
.value();
// [2, 1]
// becomes
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
This way, we don’t even have to think about the difference between tap and thru. Wrapping this reduction in a utility function makes a great general purpose tool:
const pipe = functions => data => {
return functions.reduce(
(value, func) => func(value),
data
);
};
const pipeline = pipe([
x => x * 2,
x => x / 3,
x => x > 5,
b => !b
]);
pipeline(5);
// true
pipeline(20);
// false
结论
Lodash is still a great library, and this article only offers a fresh perspective on how the evolved version of JavaScript is allowing us to solve some problems in situations where we would have previously relied on utility modules.
Don’t disregard it. Instead, next time you reach for an abstraction, think about whether a simple function would do instead!
x component depends on 'lodash'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies
这就是我在 x 中使用 lodash 的方式 component.ts
import * as _ from 'lodash';
....
....
foo(){
this.myObject = _.mapValues(this.myObject , () => true);
}
如何摆脱这个警告?
您可以使用 lodash-es(ES 模块化)而不是使用 lodash 的 CommonJS 变体。
你也可以考虑完全不使用lodash。从这篇文章 10 Lodash Features You Can Replace with ES6 我们有以下功能可以替换为 ES6
- 映射、过滤、缩减
这些收集方法使数据转换变得轻而易举,并且几乎得到了普遍支持。我们可以将它们与箭头函数配对,以帮助我们为 Lodash 提供的实现编写简洁的替代方案:
_.map([1, 2, 3], function(n) { return n * 3; });
// [3, 6, 9]
_.reduce([1, 2, 3], function(total, n) { return total + n; }, 0);
// 6
_.filter([1, 2, 3], function(n) { return n <= 2; });
// [1, 2]
// becomes
[1, 2, 3].map(n => n * 3);
[1, 2, 3].reduce((total, n) => total + n);
[1, 2, 3].filter(n => n <= 2);
也不止于此。如果我们使用现代浏览器,我们也可以使用 find
、some
、every
和 reduceRight
。
- 头尾
解构语法允许我们在没有实用函数的情况下获取列表的头部和尾部
_.head([1, 2, 3]);
// 1
_.tail([1, 2, 3]);
// [2, 3]
// becomes
const [head, ...tail] = [1, 2, 3];
同样可以得到初始元素和最后一个元素:
_.initial([1, 2, 3]);
// -> [1, 2]
_.last([1, 2, 3]);
// 3
// becomes
const [last, ...initial] = [1, 2, 3].reverse();
如果你觉得反向改变数据结构很烦人,那么你可以在调用反向之前使用扩展运算符克隆数组:
const xs = [1, 2, 3];
const [last, ...initial] = [...xs].reverse();
- 休息和伸展
rest
和 spread
函数允许我们定义和调用接受可变数量参数的函数。 ES6 为这两种操作引入了专用语法:
var say = _.rest(function(what, names) {
var last = _.last(names);
var initial = _.initial(names);
var finalSeparator = (_.size(names) > 1 ? ', & ' : '');
return what + ' ' + initial.join(', ') +
finalSeparator + _.last(names);
});
say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"
// becomes
const say = (what, ...names) => {
const [last, ...initial] = names.reverse();
const finalSeparator = (names.length > 1 ? ', &' : '');
return `${what} ${initial.join(', ')} ${finalSeparator} ${last}`;
};
say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"
- 咖喱
如果没有像 [TypeScript][5]
或 [Flow][6]
这样的高级语言,我们就不能给我们的函数类型签名,这使得柯里化变得非常困难。当我们收到柯里化函数时,很难知道已经提供了多少参数,接下来我们需要提供哪些参数。使用箭头函数,我们可以显式定义柯里化函数,让其他程序员更容易理解它们:
function add(a, b) {
return a + b;
}
var curriedAdd = _.curry(add);
var add2 = curriedAdd(2);
add2(1);
// 3
// becomes
const add = a => b => a + b;
const add2 = add(2);
add2(1);
// 3
这些明确柯里化的箭头函数对于调试特别重要:
var lodashAdd = _.curry(function(a, b) {
return a + b;
});
var add3 = lodashAdd(3);
console.log(add3.length)
// 0
console.log(add3);
// function (a, b) {
// /* [wrapped with _.curry & _.partial] */
// return a + b;
// }
// becomes
const es6Add = a => b => a + b;
const add3 = es6Add(3);
console.log(add3.length);
// 1
console.log(add3);
// function b => a + b
如果我们使用像 lodash/fp
或 ramda
这样的函数库,我们也可以使用箭头来消除对自动咖喱样式的需要:
_.map(_.prop('name'))(people);
// becomes
people.map(person => person.name);
- 部分
与柯里化一样,我们可以使用箭头函数使部分应用变得简单和明确:
var greet = function(greeting, name) {
return greeting + ' ' + name;
};
var sayHelloTo = _.partial(greet, 'hello');
sayHelloTo('fred');
// "hello fred"
// becomes
const sayHelloTo = name => greet('hello', name);
sayHelloTo('fred');
// "hello fred"
也可以将剩余参数与扩展运算符一起使用以部分应用可变参数函数:
const sayHelloTo = (name, ...args) => greet('hello', name, ...args);
sayHelloTo('fred', 1, 2, 3);
// "hello fred"
- 运算符
Lodash 附带了一些函数,将语法运算符重新实现为函数,以便将它们传递给集合方法。
在大多数情况下,箭头函数使它们变得足够简单和简短,我们可以将它们定义为内联:
_.eq(3, 3);
// true
_.add(10, 1);
// 11
_.map([1, 2, 3], function(n) {
return _.multiply(n, 10);
});
// [10, 20, 30]
_.reduce([1, 2, 3], _.add);
// 6
// becomes
3 === 3
10 + 1
[1, 2, 3].map(n => n * 10);
[1, 2, 3].reduce((total, n) => total + n);
- 路径
Lodash 的许多函数都将路径作为字符串或数组。我们可以使用箭头函数来创建更多可重用的路径:
var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// [3, 4]
_.at(['a', 'b', 'c'], 0, 2);
// ['a', 'c']
// becomes
[
obj => obj.a[0].b.c,
obj => obj.a[1]
].map(path => path(object));
[
arr => arr[0],
arr => arr[2]
].map(path => path(['a', 'b', 'c']));
因为这些路径“只是函数”,我们也可以组合它们:
const getFirstPerson = people => people[0];
const getPostCode = person => person.address.postcode;
const getFirstPostCode = people => getPostCode(getFirstPerson(people));
我们甚至可以创建接受参数的高阶路径:
const getFirstNPeople = n => people => people.slice(0, n);
const getFirst5People = getFirstNPeople(5);
const getFirst5PostCodes = people => getFirst5People(people).map(getPostCode);
- 选择
pick 实用程序允许我们select 我们想要从目标对象获得的属性。我们可以使用解构和 shorthand 对象文字来获得相同的结果:
var object = { 'a': 1, 'b': '2', 'c': 3 };
return _.pick(object, ['a', 'c']);
// { a: 1, c: 3 }
// becomes
const { a, c } = { a: 1, b: 2, c: 3 };
return { a, c };
- 常量,身份,Noop
Lodash 提供了一些用于创建具有特定行为的简单函数的实用程序:
_.constant({ 'a': 1 })();
// { a: 1 }
_.identity({ user: 'fred' });
// { user: 'fred' }
_.noop();
// undefined
我们可以使用箭头内联定义所有这些函数:
const constant = x => () => x;
const identity = x => x;
const noop = () => undefined;
或者我们可以将上面的例子重写如下:
(() => ({ a: 1 }))();
// { a: 1 }
(x => x)({ user: 'fred' });
// { user: 'fred' }
(() => undefined)();
// undefined
- 链接与流
Lodash 提供了一些函数来帮助我们编写链式语句。在许多情况下,内置的集合方法 return 一个可以直接链接的数组实例,但在某些情况下,方法会改变集合,这是不可能的。
但是,我们可以定义与箭头函数数组相同的转换:
_([1, 2, 3])
.tap(function(array) {
// Mutate input array.
array.pop();
})
.reverse()
.value();
// [2, 1]
// becomes
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
This way, we don’t even have to think about the difference between tap and thru. Wrapping this reduction in a utility function makes a great general purpose tool:
const pipe = functions => data => {
return functions.reduce(
(value, func) => func(value),
data
);
};
const pipeline = pipe([
x => x * 2,
x => x / 3,
x => x > 5,
b => !b
]);
pipeline(5);
// true
pipeline(20);
// false
结论
Lodash is still a great library, and this article only offers a fresh perspective on how the evolved version of JavaScript is allowing us to solve some problems in situations where we would have previously relied on utility modules.
Don’t disregard it. Instead, next time you reach for an abstraction, think about whether a simple function would do instead!