结尾
正式学习前端大概 3 年多了,很早就想整理这个书单了,因为常常会有朋友问,前端该如何学习,学习前端该看哪些书,我就讲讲我学习的道路中看的一些书,虽然整理的书不多,但是每一本都是那种看一本就秒不绝口的感觉。
以下大部分是我看过的,或者说身边的人推荐的书籍,每一本我都有些相关的推荐语,如果你有看到更好的书欢迎推荐呀。
export { default as AddonArea } from './AddonArea';
export { default as Answer } from './AddonArea/Answer';
export { default as Analysis } from './AddonArea/Analysis';
export { default as OriginalText } from './AddonArea/OriginalText';
export { default as Labels } from './AddonArea/Labels';
这样的文件结构是无法进行 tree-shaking 的, 因为没有 import?!
自执行的模块 import
自执行模块我们通常会使用 import 'xxx' 来进行模块引用,而不进行显式的调用。因此模块本身就有副作用。
import 'utils/refresh'
对于这种模块可以这样处理:
-
在 sideEffects 中通过数组声明,使其在 Tree Shaking 的范围之外
-
模块改造,暴露成员支持显式调用
unused harmony export
如果该模块被标识为 unused harmony export,则说明没有外部引用使用到该成员,webpack 认为是可以安全去除的。
harmony export
部分被标识为 harmony export 的模块也会被去除。这个是跟 UglifyJS 的机制有关系。
没有提供导出成员的模块
// ./src/modules/edu-discount/seckill/index.ts
import * as SeckillTypes from './types';
export { SeckillTypes };
对于只有暴露的成员,但是没有被引用的成员,这种模块会被直接删除。
-
[x] exports provided
-
[ ] exports used
配置
--
babel的配置文件
{
"presets": [
["env", {
"modules": false // 配置了这个,babel就不会像默认那样转变成 require 形式。
}],
"stage-2",
"react"
]
}
为 webpack 进行 tree-shaking 创造了条件。
⚠️不能引用类似 @babel/plugin-transform-modules-commonjs会把模块编译成 commonjs 的插件;
webpack 的配置文件
webpack 4 通过 optimization 取代了4个常用的插件:
| 废弃插件 | optimization 属性 | 功能 |
|
| --- | --- | --- | --- |
| UglifyjsWebpackPlugin | sideEffects | minimizer | Tree Shaking & Minimize |
| ModuleConcatenationPlugin | concatenateModules | Scope hoisting | 生产环境默认开启 |
| CommonsChunkPlugin | splitChunks | runtimeChunk | OccurrenceOrder |
| NoEmitOnErrorsPlugin | NoEmitOnErrors | 编译出现错误时,跳过输出阶段 | 生产环境默认开启 |
usedExports
Webpack 将识别出它认为没有被使用的代码,并在最初的打包步骤中给它做标记。
// Base Webpack Config for Tree Shaking
const config = {
mode: 'production',
optimization: {
usedExports: true,
minimizer: [
new TerserPlugin({...}) // 支持删除死代码的压缩器
]
}
};
package.json 的配置
用过 redux 的童鞋应该对纯函数不陌生,自然也就应该了解函数式编程,函数式编程中就有副作用一说。
照顾一下不知道的同学,那什么是副作用呢?
一个函数会、或者可能会对函数外部变量产生影响的行为。
具有副作用的文件不应该做 tree-shaking,因为这将破坏整个应用程序。比如全局样式表及全局的 JS 配置文件。
webpack 总会害怕把你要用的代码删除了,所以默认所有的文件都有副作用,不能被 Tree Shaking。
// 所有文件都有副作用,全都不可 tree-shaking
{
"sideEffects": true
}
// 没有文件有副作用,全都可以 tree-shaking,即告知 webpack,它可以安全地删除未用到的 export。
{
"sideEffects": false
}
// 除了数组中包含的文件外有副作用,所有其他文件都可以 tree-shaking,但会保留符合数组中条件的文件
{
"sideEffects": [
"*.css",
"*.less"
]
}
所以,首先关闭你的 sideEffects,
直接通过 module.rules 中的 sideEffects 配置可缩小你的影响范围。
加了 sideEffect 配置后,构建出来的一些 IIFE 函数也会加上/PURE/注释,便于后续 treeshaking。
组件不支持DCE?
我们的组件用的是 father,可以看到其依赖的father-build 是基于 rollup 的,那就好办了。webpack 的 Tree Shaking 还是 copy 的 rollup家的。
关键是在应用组件的业务项目里面配置optimization.sideEffects: true
// webpack.config.js
const path = require('path')
const webpackConfig = {
module : {
rules: [
{
test: /.(jsx|js)$/,
use: 'babel-loader',
exclude: path.resolve(__dirname, 'node_modules')
}
]
},
optimization : {
sideEffects: true,
minimizer: [
// 这里配置成空数组是为了使最终产生的 main.js 不被压缩
]
},
plugins:[]
};
module.exports = webpackConfig;
// package.json
{
"name": "treeshaking-test",
"version": "0.1.0",
"description": "",
"main": "src/index.js",
"scripts": {
"build": "webpack --config webpack.config.js"
},
"author": "lu.lu lulu27753@163.com (github.com/lulu27753)",
"license": "MIT",
"dependencies": {
"big-module": "^0.1.0",
"big-module-with-flag": "^0.1.0",
"webpack-bundle-analyzer": "^3.7.0"
},
"devDependencies": {
"babel-preset-env": "^1.7.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
}
}
// .babelrc
{
"presets": [
["env", { "modules": false }]
]
}
可以看到最终打包后的文件如下:
// dist/main.js
"use strict";
// ESM COMPAT FLAG
webpack_require.r(webpack_exports);
// CONCATENATED MODULE: ./node_modules/big-module/es/a.js
var a = 'a';
// CONCATENATED MODULE: ./node_modules/big-module/es/b.js
var b = 'b';
// CONCATENATED MODULE: ./node_modules/big-module/es/c.js
var c = 'c';
// CONCATENATED MODULE: ./node_modules/big-module/es/index.js
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/a.js
var a_a = 'a';
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/b.js
var b_b = 'b';
// CONCATENATED MODULE: ./src/index.js
console.log(a, b, a_a, b_b);
/***/ })
/******/ ]);
可以很清楚的看到 big-module-with-flag 中的 c 模块被DCE了。
做个小小的改动,将 .babelrc 中的 modules 改为"commonjs"
{
"presets": [
["env", { "modules": "commonjs" }]
]
}
"use strict";
// ESM COMPAT FLAG
webpack_require.r(webpack_exports);
// EXPORTS
webpack_require.d(webpack_exports, "a", function() { return /* reexport */ a; });
webpack_require.d(webpack_exports, "b", function() { return /* reexport */ b; });
webpack_require.d(webpack_exports, "c", function() { return /* reexport */ c; });
// CONCATENATED MODULE: ./node_modules/big-module/es/a.js
var a = 'a';
// CONCATENATED MODULE: ./node_modules/big-module/es/b.js
var b = 'b';
// CONCATENATED MODULE: ./node_modules/big-module/es/c.js
var c = 'c';
// CONCATENATED MODULE: ./node_modules/big-module/es/index.js
/***/ }),
/* 2 */
/***/ (function(module, webpack_exports, webpack_require) {
"use strict";
// ESM COMPAT FLAG
webpack_require.r(webpack_exports);
// EXPORTS
webpack_require.d(webpack_exports, "a", function() { return /* reexport */ a; });
webpack_require.d(webpack_exports, "b", function() { return /* reexport */ b; });
webpack_require.d(webpack_exports, "c", function() { return /* reexport */ c; });
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/a.js
var a = 'a';
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/b.js
var b = 'b';
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/c.js
var c = 'c';
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/index.js
/***/ })
/******/ ]);
结果是 CDE 失败!
将 modules 的值改回去,并升级big-module-with-flag为0.2.0。CDE 成功,可以打假一波了(????,网上很多文章都是基于webpack3的,过时了)
升级big-module-with-flag为0.5.0, 并更改 src/index.js
import { a as a1, b as b1 } from "big-module";
import { a as a2, b as b2, Apple } from "big-module-with-flag";
console.log(a1, b1, a2, b2);
const appleModel = new Apple({model: 'IphoneX'}).getModel()
console.log(appleModel)
var Apple = /#PURE/function () {
function Apple(_ref) {
var model = _ref.model;
_classCallCheck(this, Apple);
this.className = 'Apple';
this.model = model;
}
_createClass(Apple, [{
key: "getModel",
value: function getModel() {
return this.model;
}
}]);
return Apple;
}();
// CONCATENATED MODULE: ./src/index.js
console.log(a, b, es_a, es_b);
var appleModel = new Apple({
model: 'IphoneX'
}).getModel();
console.log(appleModel);
DCE 成功!
var _bigModule = webpack_require(2);
var _bigModuleWithFlag = webpack_require(1);
console.log(_bigModule.a, _bigModule.b, _bigModuleWithFlag.a, _bigModuleWithFlag.b);
var appleModel = new _bigModuleWithFlag.Apple({
model: 'IphoneX'
}).getModel();
console.log(appleModel);
/***/ }),
/* 1 */
/***/ (function(module, webpack_exports, webpack_require) {
"use strict";
// ESM COMPAT FLAG
webpack_require.r(webpack_exports);
// EXPORTS
webpack_require.d(webpack_exports, "a", function() { return /* reexport */ es_a; });
webpack_require.d(webpack_exports, "b", function() { return /* reexport */ es_b; });
webpack_require.d(webpack_exports, "c", function() { return /* reexport */ es_c; });
webpack_require.d(webpack_exports, "Person", function() { return /* reexport */ Person; });
webpack_require.d(webpack_exports, "Apple", function() { return /* reexport */ Apple; });
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/a.js
var a = 'a';
/* harmony default export */ var es_a = (a);
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/b.js
var b = 'b';
/* harmony default export */ var es_b = (b);
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/c.js
var c = 'c';
/* harmony default export */ var es_c = (c);
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/Person.js
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Person = /#PURE/function () {
function Person(_ref) {
var name = _ref.name,
age = _ref.age,
sex = _ref.sex;
_classCallCheck(this, Person);
this.className = 'Person';
this.name = name;
this.age = age;
this.sex = sex;
}
_createClass(Person, [{
key: "getName",
value: function getName() {
return this.name;
}
}]);
return Person;
}();
// CONCATENATED MODULE: ./node_modules/big-module-with-flag/es/Apple.js
function Apple_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function Apple_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function Apple_createClass(Constructor, protoProps, staticProps) { if (protoProps) Apple_defineProperties(Constructor.prototype, protoProps); if (staticProps) Apple_defineProperties(Constructor, staticProps); return Constructor; }
TCP协议
- TCP 和 UDP 的区别?
- TCP 三次握手的过程?
- 为什么是三次而不是两次、四次?
- 三次握手过程中可以携带数据么?
- 说说 TCP 四次挥手的过程
- 为什么是四次挥手而不是三次?
- 半连接队列和 SYN Flood 攻击的关系
- 如何应对 SYN Flood 攻击?
- 介绍一下 TCP 报文头部的字段
- TCP 快速打开的原理(TFO)
- 说说TCP报文中时间戳的作用?
- TCP 的超时重传时间是如何计算的?
- TCP 的流量控制
- TCP 的拥塞控制
- 说说 Nagle 算法和延迟确认?
- 如何理解 TCP 的 keep-alive?
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
浏览器篇
- 浏览器缓存?
- 说一说浏览器的本地存储?各自优劣如何?
- 说一说从输入URL到页面呈现发生了什么?
- 谈谈你对重绘和回流的理解
- XSS攻击
- CSRF攻击
- HTTPS为什么让数据传输更安全?
- 实现事件的防抖和节流?
- 实现图片懒加载?