ES6模块-团队分享

大纲

  • 前端模块化的历史
  • ES6模块的使用
  • ES6模块背后的思考
  • 组件和模块
  • 参考文章

前端模块化的历史

前端模块化的历史

  • 全局Function模式
  • 命名空间模式
  • IIFE模式
  • IIFE模式(引入依赖)
  • CommonJS
  • AMD
  • CMD
  • ES6 模块

ES6模块的使用

什么是模块?

模块是通过不同于脚本的形式进行加载的JavaScript文件。区别在于:

  1. 模块代码自动运行在严格模式,并无法退出
  2. 模块中定义的变量只在模块的作用域中存在,不会自动添加到全局的作用域
  3. 模块中的thisundefined
  4. 模块中不支持HTML风格的注释<!-- comments -->
  5. 模块必须要导出任何可以在模块外可用的一切
  6. 模块可以从其他模块导入内容

基本导出

使用export关键字可以从模块中导出变量、函数和类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// export data
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;

// export function
export function sum(num1, num2) {
return num1 + num1;
}

// export class
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
}

// this function is private to the module
function subtract(num1, num2) {
return num1 - num2;
}

// define a function...
function multiply(num1, num2) {
return num1 * num2;
}

// ...and then export it later
export { multiply };

导出说明:

  1. 每个导出的函数和类都有一个名字,除了使用default关键字外无法使用上面的语法导出匿名函数和类
  2. 查看multiply()函数,你可以不导出声明,支持导出引用
  3. 查看substract()函数,外部不可访问,没有显式导出的变量、函数和类对于模块是私有的

基本导入

基本格式:

1
import { identifier1, identifier2 } from "./example.js";

说明: 从一个模块中导入一个绑定,就类似定义一个const的内容,也就是无法定义重名变量、导入前使用和修改值。

导入一个单一绑定

1
2
3
4
5
6
// import just one
import { sum } from "./example.js";

console.log(sum(1, 2)); // 3

sum = 1; // error

尽管example.js中定义了多个导出,你可以只导入其中一个;为了在浏览器和Node.js中保持兼容性,尽量在文件名前使用相对路径。

导入多个绑定

1
2
3
4
// import multiple
import { sum, multiply, magicNumber } from "./example.js";
console.log(sum(1, magicNumber)); // 8
console.log(multiply(1, 2)); // 2

全部导入

特殊的应用场景支持导入整个模块作为一个对象,类似导入一个库文件,所有的模块导出均作为对象属性。

1
2
3
4
5
// import everything
import * as example from "./example.js";
console.log(example.sum(1,
example.magicNumber)); // 8
console.log(example.multiply(1, 2)); // 2

不管多少次使用import对一个模块进行导入操作,模块只会执行一次;在导入模块的代码执行后,实例化的模块在保存在内存中并在其他import语句使用时复用。

1
2
3
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { magicNumber } from "./example.js";

代码中example.js只会执行一次.

模块语法不支持在语句或者函数中使用,也就是说不支持动态的导入和导出。ECMAScript dynamic-import Stage3

导入绑定的修改

对导入的内容是无法修改的,但是在模块内部可以。

模块导出:

1
2
3
4
export var name = "Nicholas";
export function setName(newName) {
name = newName;
}

模块导入:

1
2
3
4
5
6
7
import { name, setName } from "./example.js";

console.log(name); // "Nicholas"
setName("Greg");
console.log(name); // "Greg"

name = "Nicholas"; // error

setName函数回到模块内执行并修改变量name的内容,随后这个修改会反映到导入的name这个绑定。

重命名导出和导入

有时候,你可能不想使用变量、函数、类在模块中的原始名字,你可以在导入和导出的时候使用as关键字来指定新的名字

导出时重命名

1
2
3
4
5
function sum(num1, num2) {
return num1 + num2;
}

export { sum as add };

导入

1
import { add } from "./example.js";

导入时重命名

1
2
3
import { add as sum } from "./example.js";
console.log(typeof add); // "undefined"
console.log(sum(1, 2)); // 3

模块中的默认值

模块中的默认值是指使用default指定的一个单独变量、函数或者类,每个模块中只能设定一个默认导出,指定多个默认导出会报语法错误。

导出默认值

直接导出

1
2
3
export default function(num1, num2) {
return num1 + num2;
}

函数可以不写名字,因为模块本身代表这个函数。

导出引用

1
2
3
4
5
function sum(num1, num2) {
return num1 + num2;
}

export default sum;

重命名导出

1
2
3
4
5
function sum(num1, num2) {
return num1 + num2;
}

export { sum as default };

导入默认值

javascript
// import the default
import sum from “./example.js”;

console.log(sum(1, 2)); // 3

1
2
3
4
5
6
7
8
9
10
11
12

1. 默认导入不需要大括号
2. 名称可以代表任意模块中的默认函数
3. 默认导入的语法是最精简的,ES6模块规范制定者希望默认值作为主流的使用场景

**具名和默认混合**
```javascript
export let color = "red";

export default function(num1, num2) {
return num1 + num2;
}
1
2
3
4
import sum, { color } from "./example.js";

console.log(sum(1, 2)); // 3
console.log(color); // "red"

默认值要在非默认值前面导入。

重命名默认值

1
2
3
4
5
// equivalent to previous example
import { default as sum, color } from "example";

console.log(sum(1, 2)); // 3
console.log(color); // "red"

重新导出一个绑定

几种重新导出的方式

1
2
import { sum } from "./example.js";
export { sum }
1
export { sum } from "./example.js";
1
export { sum as add } from "./example.js";

全部导出

1
export * from "./example.js";

这里只能导出所有的具名导出,不包括默认导出。如果要处理默认导出,你需要显式的导入后再导出。

没有绑定的导入

有些模块并没有导出任何内容,仅仅是在全局作用域中做了些修改;虽然模块无法直接修改全局作用域的变量、函数和类,但是对于內建对象的修改可以反映到其他模块中。

1
2
3
4
5
6
7
8
9
10
11
// module code without exports or imports
Array.prototype.pushAll = function(items) {

// items must be an array
if (!Array.isArray(items)) {
throw new TypeError("Argument must be an array.");
}

// use built-in push() and spread operator
return this.push(...items);
};

这个模块对Array原型增加一个pushAll方法.

1
2
3
4
5
6
import "./example.js";

let colors = ["red", "green", "blue"];
let items = [];

items.pushAll(colors);

这段代码导入和执行模块代码,添加pushAll方法到array原型中。

没有绑定的导入通常用于创建polyfill或者shim。

加载ES6 模块

  • 使用打包工具和Babel转译为ES5

  • 使用script标签,并设置typemodule(IE11不兼容,使用前查看兼容性)

默认使用defer属性,按照代码出现的顺序进行加载,执行需要在文档解析完成后,执行顺序也是按照代码出现的顺序。

1
2
3
4
5
6
7
8
9
10
11
12
<!-- this will execute first -->
<script type="module" src="module1.js"></script>

<!-- this will execute second -->
<script type="module">
import { sum } from "./example.js";

let result = sum(1, 2);
</script>

<!-- this will execute third -->
<script type="module" src="module2.js"></script>

如果模块代码中又引入其他的模块,执行的顺序会变得复杂一些:

加载顺序:

  1. Download and parse module1.js.
  2. Recursively download and parse import resources in module1.js.
  3. Parse the inline module.
  4. Recursively download and parse import resources in the inline module.
  5. Download and parse module2.js.
  6. Recursively download and parse import resources in module2.js

执行顺序:

  1. Recursively execute import resources for module1.js.
  2. Execute module1.js.
  3. Recursively execute import resources for the inline module.
  4. Execute the inline module.
  5. Recursively execute import resources for module2.js.
  6. Execute module2.js.
  • web worker
1
2
// load module.js as a module
let worker = new Worker("module.js", { type: "module" });

ES6模块背后的思考

对比CommonJS和AMD

CommonJS

在Node.js中的主要实现方式,特性如下:

  • 紧凑的语法
  • 同步加载
  • 主要使用: 服务端

Asynchronous Module Definition(AMD)

主要的实现是RequireJS,特性如下:

  • 稍微复杂的语法
  • 异步加载
  • 主要使用: 浏览器

ES6模块

目标:

  • 偏爱默认导出
  • 静态模块结构
  • 支持同步和异步加载
  • 支持模块间的循环依赖

优势:

  • 比CommonJS更紧凑的语法
  • 结构可以被静态分析(静态检测、优化等)
  • 对循环引用比CommonJS更好
  • 支持异步加载

八卦: ES6 Module的主要设计者Dave Herman和Sam Tobin Hochstadt。

组件和模块

  • 模块为基础,组件基于模块
  • 组件侧重UI封装,针对,包括html,css,js和image;模块侧重数据、功能封装

组件化参考张云龙-前端工程

参考文章

ES6中的模块化

这里主要是针对ES6 Module技术出现的讨论,对比历史的一些模块化解决方案。

背景、初衷、目标

背景

简单的讲,随着前端应用的日益庞大和复杂,对于代码的拆分复用要求更高,也就出现了对代码模块化的需求。

在ES6 Module出现前,社区中出现的两个方案CommonJS和AMD:

CommonJS

在Node.js中的主要实现方式,特性如下:

  • 紧凑的语法
  • 同步加载
  • 主要使用: 服务端

Asynchronous Module Definition(AMD)

主要的实现是RequireJS,特性如下:

  • 稍微复杂的语法
  • 异步加载
  • 主要使用: 浏览器

这里提一句,ES6 Module的主要设计者Dave Herman 和 Sam Tobin Hochstadt。

初衷

这里讲的ES6 Module的设计初衷,在ES6 Module出现前社区已经在模块化上有一些令人印象深刻的变通方案,以CommonJS和AMD为代表,这两者各有优缺点并且不兼容,因此ES6 Module的设计初衷是为了吸取这两者的优点实现ECMAScript的标准。

目标

ES6 Module的目标是出一个让CommonJS和AMD社区都能接受的方案:

  • 偏爱默认导出
  • 静态模块结构
  • 支持同步和异步加载
  • 支持模块间的循环依赖

优势和劣势(trade-off)

ES6 Module实际达成的方案特性如下:

  • 比CommonJS更紧凑的语法
  • 结构可以被静态分析(静态检测、优化等)
  • 对循环引用比CommonJS更好
  • 支持异步加载

适用的场景(业务场景、技术场景)

适用于浏览器端和服务端(Nodejs),目前(2019年)主流的浏览器大部分都不支持ES6 Module的特性,因此使用的话还得使用Babel来转译。

组成部分和关键点

ES6 Module分两部分:

  • 声明式语法(import和export)
  • 编程式加载API: 支持配置如何加载模块以及条件加载

使用场景

在浏览器侧的使用

使用<script>进行加载

通过type属性值为module来标识模块进行加载,支持外部文件和内联的方式,默认使用defer的行为,也可以指定async

1
2
3
4
5
6
7
8
9
10
11
<!-- load a module JavaScript file -->
<script type="module" src="module.js"></script>

<!-- include a module inline -->
<script type="module">

import { sum } from "./example.js";

let result = sum(1, 2);

</script>

通过Worker进行加载

1
2
// load module.js as a module
let worker = new Worker("module.js", { type: "module" });

通过打包工具进行打包

使用webpack或者browserify等打包工具进行打包后使用。

声明式语法

声明式语法分为两种类型:具名导出(每个模块有多个)和默认导出(每个模块一个)。

具名导出(named exports)

通过关键字export前缀可以导出多个内容,并有名称来进行区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

默认导出(default exports)

一个模块只有一个默认导出,并且默认导出尤其容易进行导入。

1
2
3
4
5
6
//------ myFunc.js ------
export default function () { ... };

//------ main1.js ------
import myFunc from 'myFunc';
myFunc();

对于class的使用

1
2
3
4
5
6
//------ MyClass.js ------
export default class { ... };

//------ main2.js ------
import MyClass from 'MyClass';
let inst = new MyClass();

默认导出其实可以理解为特殊的具名导出

导入:

1
2
import { default as foo } from 'lib';
import foo from 'lib';

导出:

1
2
3
4
5
6
//------ module1.js ------
export default 123;

//------ module2.js ------
const D = 123;
export { D as default };

答疑

  • 为什么我们需要具名导出(named exports)?

The answer is that you can’t enforce a static structure via objects and lose all of the associated advantages (described in the next section).

如果通过对象进行导出会丢失静态结构,从而失去原先的优势。

  • ES6 Module输出的是值的引用,并且是在编译时输出接口。

编程式语法

针对ES6 Module的编程式实现,目前规范处于stage 3。

使用编程式语法,可以做到:

  • 对模块和脚本进行编程操作
  • 配置模块的加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>

<main>Content will load here!</main>

<script>
const main = document.querySelector("main");
for (const link of document.querySelectorAll("nav > a")) {
link.addEventListener("click", e => {
e.preventDefault();

import(`./section-modules/${link.dataset.entryModule}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
});
}
</script>

已有的实现和它之间的对比

有关模块化的进程,前端模块化详解一句讲解得比较详细了,可以参考。

参考文档

create-react-app-from-scratch

React社区中提供了类似create-react-app这样的命令行工具,作为React项目的脚手架,这个工具提供了大而全的功能,直接拿过来使用,你可能不明白背后的原理,这里跟着Creating a React App… From Scratch这篇文章一起从零搭建一个React App,有条件的同学可以直接阅读英文版,这里实现的是一个最简单的功能,支持ES6+和JSX语法、热加载。

起步

创建一个目录并且通过npm init初始化一个项目,有需要也可以通过git init创建版本管理,新项目的文件结构如下:

1
2
3
.
+-- public
+-- src

此时可以添加一个.gitignore文件,并把node_modulesdist目录排除在提交范围。

public目录中,将会存放一些静态的资产,最主要的是放置index.html文件,让react用来渲染app用。

index.html如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- sourced from https://raw.githubusercontent.com/reactjs/reactjs.org/master/static/html/single-file-example.html -->
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Starter</title>
</head>

<body>
<div id="root"></div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="../dist/bundle.js"></script>
</body>

</html>

这里react通过root这个id进行关联渲染元素,我们后续打包的文件名称为bundle.js

Babel

接下来添加babel相关的软件包:

1
npm install --save-dev @babel/core@7.1.0 @babel/cli@7.1.0 @babel/preset-env@7.1.0 @babel/preset-react@7.0.0

babel-core是主要的babel包,用于转译代码。babel-cli用于在命令行中编译文件。preset-reactpreset-env用于预先配置要转换的代码类型,env预置用以转换ES6+的代码到ES5的代码,react预置转换JSX的代码。

接着在项目的根目录,创建一个文件.babelrc,用于告诉bebel我们要配置的预置项。

1
2
3
{
"presets": ["@babel/env", "@babel/preset-react"]
}

Babel还有很多的插件值得研究。

Webpack

接下来需要配置Webpack来打包和启用开发热加载等功能。

安装开发依赖:

1
npm install --save-dev webpack@4.19.1 webpack-cli@3.1.1 webpack-dev-server@3.1.8 style-loader@0.23.0 css-loader@1.0.0 babel-loader@8.0.2

Webpack使用loader来处理不同类型的文件并进行打包,并提供一个开发服务器方便高效的进行开发。

在项目根目录下创建一个新文件webapck.config.js,在这里导出一个包含webpack配置的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const path = require("path");
const webpack = require("webpack");

module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devServer: {
contentBase: path.join(__dirname, "public/"),
port: 3000,
publicPath: "http://localhost:3000/dist/",
hotOnly: true
},
plugins: [new webpack.HotModuleReplacementPlugin()]
};

这里webpack的配置项暂时不解释,接下来进行React的配置。

React

首先,我们要添加两个软件包: react@16.5.2react-dom@16.5.2

1
npm install --save react@16.5.2 react-dom@16.5.2

接下来,要告诉React app在哪里关联DOM,在src文件夹中创建一个index.js文件:

1
2
3
4
import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
ReactDOM.render(<App />, document.getElementById("root"));

ReactDOM.render这个函数告诉React渲染什么和在哪里渲染,这里渲染一个叫做App的组件,并且渲染在ID为root的DOM元素上。

接下来在src文件夹下创建一个App.js文件,这是一个React组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component} from "react";
import "./App.css";

class App extends Component{
render(){
return(
<div className="App">
<h1> Hello, World! </h1>
</div>
);
}
}

export default App;

src目录下创建一个App.css文件

1
2
3
4
.App {
margin: 1rem;
font-family: Arial, Helvetica, sans-serif;
}

最终的项目结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
.
+-- public
| +-- index.html
+-- src
| +-- App.css
| +-- App.js
| +-- index.js
+-- .babelrc
+-- .gitignore
+-- package-lock.json
+-- package.json
+-- webpack.config.js

在webpack的script中增加一个脚本命令,start

1
start: webpack-dev-server --mode development

使用npm start命令即可启动

Finishing HMR

这个时候修改文件内容,页面并不会自动更新,要增加热加载功能,需要另外一个软件包的支持

1
npm install --save react-hot-loader@4.3.11

这个包可以安装在依赖中,而不是开发依赖,因为包会自动判断是否执行。

App.js中导入react-hot-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component} from "react";
import {hot} from "react-hot-loader";
import "./App.css";

class App extends Component{
render(){
return(
<div className="App">
<h1> Hello, World! </h1>
</div>
);
}
}

export default hot(module)(App);

这个时候修改文件的内容,页面就会即时刷新了。

Build

最后,可以在package.json中增加一个build的脚本命令,

1
build: webpack --mode production

2019年D2和SEE Conf的参会感想

2019年的第一个周末在杭州参加了两个都是阿里主导的前端技术会议,一个是SEE Conf,另一个是D2,从参会人员规模和演讲嘉宾来看,D2的级别更高一点,干货也更多。但是,SEE Conf的初心是很不错的,而且也是免费分享,提出了体验的重要性和可持续的未来相关观点。

SEE Conf

SEE Conf,全称是Seeking Experience and Engineer Conference,关注体验和工程,他们的口号是探索极致用户体验和最佳工程实践,他们也是身体力行的在探索。

从一整天的会议听下来,不免有一些蚂蚁金服在推广他们的开源产品,但是其中也分享了不少新鲜的观点。这里列几个对我印象比较深刻的主题。

玉伯提的SaaS应用和体验

开场玉伯致辞,久闻大名,第一次在现场见到,很接地气。

玉伯提出了当前SaaS应用渐渐成为主流,也因此应用的体验变得日趋重要,这也是他们一直在推行探索体验技术的一个推力之一。另外一点,现场中玉伯也提到了Ant Design项目的“圣诞彩蛋”项目,并且现场道歉了,对于开源项目的态度还是比较诚恳的,对于后续他们对开源项目的维护有些期待,希望阿里和蚂蚁对开源社区多用心,负责。

同济大学娄永琪院长的《交互设计与人类未来》

这个主题前半部分一直在谈人类的历史和地球面临的问题,一直没有和设计扯上关系。到了中期,慢慢过渡到交互设计,人类可持续发展与设计的关联,再扩展到设计与社区的实践。这个主题分享确实能开阔眼界,学术前沿的研究方向。

其中娄院长提到了其中一本书《翻转极限》,应该值得一看,讲目前人类遇到的种种问题,以及现今的解决方案。

林峰的《科技与人文结合的体验度量》

相信很多人知道林峰是因为ECharts,现在林峰应该是在蚂蚁带领可视化的团队。林峰的演讲整体给人的感觉,演讲很流畅,口才听好的。演讲的内容也很有趣,主要谈及的是产品在体验方面的定量度量方法,并且能提供建议,他们内部的产品名称是“九色鹿”,应该还没有开源。主要的依据是PTECH指标,后续可以了解一下,TECH指标是在上一届SEE Conf中提出的。

云谦的《蚂蚁金服前端框架探索之路》

用过dva和roadhog项目,在github上的id是sorrycc,现场看到真人和头像不怎么像,可能是胖了。

这个分享中云谦提到了蚂蚁金服在前端框架选型和造轮子路上,一路走来的历程,从roof、redux、dva、umi,踩过不少坑。现场看,他们内部主要在用umi以及基于umi的一套插件生态,dva他们只用在数据流方面,已经濒临淘汰;不过现场的观众来看,还是有不少人在使用dva,大家也表示出对dva后续开发维护的担忧,云谦给出的答复是,dva后续会有第3版的升级,roadhog将会进入维护模式。而且,期间云谦也分享了他在技术选型方面的建议,除了技术或者工具本身的情况,另外还有一点是运气。

D2

今年的D2是第13届,据说来参加的人数有近千人。之前听说是没有收费的,今年也开始进行收费了,场地选在杭州下沙的和达希尔顿酒店,场地费应该也不便宜,这次会议不止是阿里的专家演讲,也请了外边的好几个讲师,包括facebook工程师和德国的小哥等等。整体而言,干货很多,也了解了当前前端方面的一些趋势。

有关前端趋势方面,前段时间,2018年的stateofjs网站进行问卷调查,出了一个js生态相关的报告,可以了解到世界范围前端的趋势。这里,阿里的的前端委员会主席圆心也提了几点,主要涉及几点:

  • 前端往无线端
  • nodejs生态的成熟
  • toB业务对前端的要求见多
  • 前端工程化
  • 前端渐稳定,国内选择前端框架主要在react和vue

从整个会议下来,Angular基本被国内开发者忽视,国内的开源生态也基本是基于react和vue。

GraphQL

台湾小哥王启安带来的GraphQL实践,布道。国内的实践还是处于萌芽阶段,真正使用的团队还比较少,不过GraphQL相对于Rest API而言,性能上是有很大优势的。通过前端自定义查询字段,从而减少http请求和无效数据带宽浪费。相信这是一个未来的趋势。

海量数据渲染

阿里西蒙有关天猫双十一的海量数据渲染,涉及到WebGL 2、WebAssembly等的使用和优化案例,很精彩。

大规模地理数据绘制

这个topic是有关大规模地理数据的绘制的理论研究,其中提到了一些方法,蓝噪声采样处理数据,以及如何解决大数据量的前端绘制的方案。其中提到,数据量较大的情况下,其实有些时候是可以在后台进行绘制,而前端进行展示。

React suspense API

来自Facebook的工程师,讲解了suspense的使用,用于组件懒加载时显示loading内容的方式。以及介绍了后续react支持异步数据加载的API,值得期待。

WebAssembly

这个topic是由法国的一个哥们讲的有关WebAssembly,由浅入深的讲解,没有过实践,也只能听听。从D2整体的报告来看,WebAssembly技术在国内外实践落地的不少,而且从兼容性来看也基本可以上生产了。

Class Field

最后一个报告是在第3号报告厅,听的Hax讲的关于JS中Class Field相关的问题,据说是浓缩了3个小时的精华,信息量确实很大。这个话题主要涉及到ECMAScript标准中有争议的内容,槽点较多。

结语

今年的主题,都比较有含金量,毕竟是收费了,还是值得参加的。另外,还有一些感兴趣的话题,时间上是冲突的,没法去听,后续通过视频回放再看。

2019年阅读计划

2019年阅读计划

方法

  • 【精读】
  • 【略读】
  • 【主题阅读】

2019年计划阅读共32本,每个季度8本

类别

技术类:

  • 【精度】《Linux: 鸟哥的私房菜》
  • 【精读】《JavaScript:The Good Parts》
  • 【精读】《You Don’t Know JS - this & Object Prototype》
  • 【精读】《You Don’t Know JS - scope & closures》
  • 【精读】《You Don’t Know JS - Async & Performance》
  • 【精读】《You Don’t Know JS - Types & Grammer》
  • 【精读】《You Don’t Know JS - ES6 &Beyond》
  • 【精读】《You Don’t Know JS - Up & Going》
  • 【主题阅读】《JavaScript权威指南》
  • 【精读】《Python编程快速上手》
  • 【精读】《计算机程序的构造和解释》
  • 【略读】《软技能》
  • 【精读】《精通CSS: 高级Web标准解决方案》Andy budd,《CSS Mastery-Advanced Web Standards Solutions》
  • 【精读】《算法》第4版
  • 【精读】《计算机网络》Andrew S.Tanenbaum 著 潘爱民 译

人文类:

  • 【略读】《失控》
  • 【精读】《论语今读》
  • 【略读】《Animal Farm》by George Orwell
  • 【略读】《1984》by George Orwell
  • 【略读】《三体》刘慈欣
  • 【略读】《哥德尔、艾舍尔、巴赫》侯世达 商务印书馆

经济类:

  • 【略读】《穷查理宝典》
  • 【略读】《How to Worry Less About Money》By John Armstrong

其他类:

  • 【略读】《清醒思考的艺术》罗尔夫•多贝里
  • 【略读】《结网@改变世界的互联网产品经理》 王坚 编著
  • 【略读】《你的灯亮着吗——发现问题所在》 康纳德.高斯 杰拉尔德.温伯格 著 俞月圆 译
  • 【略读】《见识-商业的本质和人生的智慧》吴军
  • 【略读】《Thinking, Fast and Slow》 by Daniel Kahneman
  • 【略读】《Getting Real》by 37Sigal
  • 【略读】《习惯的力量》(The Power of Habit)
  • 【略读】《活出生命的意义》弗兰克尔
  • 【略读】《技巧:如何用一年时间获得十年的经验》

更新于2019.12.29

2018年Perry的阅读

2018年Perry的阅读书单

2018年共阅读了34本书,上半年比较侧重阳明心学的相关阅读,逐步转向儒学的基础方面阅读,比如《论语》等。

收获最大的一本书是《人性的弱点》,其中包含人性的优点,书中讲述了许多为人处世的道理,最重要的一点,教会了我如何摆脱忧虑。技术书中《Understanding ES6》学到的东西最多,基本可以上手ES6在开发中的常用特性。《只要会呼吸,就会冥想》这本书,我一直在跟着做,保持一颗宁静的心很重要。

技术类

《编程人生》

阅读时间:2018.2.16~2018.04.01

作者采访了15位计算机的先驱,讲述自己的成长经历和对编程语言、技术趋势的见解。

《JavaScript高级程序设计》

阅读时间: 2017.12.15~2018.4.12

红宝书,JavaScript语言的一本经典,作者是Nicholas C. Zakas,多产的作者。

《DOM Script》

阅读时间:2018.4.23~2018.5.27

DOM的经典入门书,实例和最佳实践的结合。

《HTML5秘籍》

阅读时间:2018.5.30~ 2018.6.15

讲HTML5实战和最佳实践的一本书。

《Understanding ES6》

阅读时间: 2018.6.15~ 2018.8.23

同时Nicholas C. Zakas的作品,结合实例讲解了ES6的语法。

《算法图解》

阅读时间:2018.10~2018.12

作者利用漫画和实例讲解常用的算法。

《AngularJS深度剖析和最佳实践》

阅读时间: 2018.7.1~2018.7.30

国内一本关于Angularjs不错的书,不过Angularjs已经日落西山了,现在前端框架已经被React和Vue统治了。

《HTTP权威指南》

阅读时间:2018.10.22~2018.10.26

关于HTTP大而全的指南,只略读了前4章,有关HTTP基础内容,涉及http历史、URL、数据包结构、消息、连接相关信息。

《锋利的jQuery》单东林

阅读时间: 10.30-11.1

主要描述jQuery 1.3版本的用法,以及一个网站的实例,参考意义不大,用法可直接到官方文档查阅。

人文类

《京华烟云》

阅读时间: 2017.12.18~2018.2.16

阅读起来有现代版《红楼梦》的感觉,世事沧桑,不过还是比较喜欢林语堂的作品,有点洒脱。

《富兰克林传》

阅读时间: 2018.2.25~2018.04.01

讲述富兰克林的一生,包括富兰克林的家族迁移历史,到富兰克林功成名就的整个历程。富兰克林属于比较典型的凭借自己后天的努力达到伟人级成就的一类人。

《传习录》

阅读时间: 2018.1.15~2018.4.15

儒学经典之一,阳明先生的心学承载著作。

《阳明学概要》

阅读时间: 2018.4.7~2018.4.26

钱穆先生针对阳明学的入门导读作品,有助于理解阳明心学的时代背景和解决的问题。

《王阳明与明末儒学》

阅读时间: 2018.5.1~2018.5.16

略读,讲述明末儒学学者及心学的学派,不具有可读性。

《致良知》

阅读时间: 2018.5.21~ 2018.6.10

王阳明精选语录,包括阳明先生的生平历史记录、书信以及弟子的语录。

《十日谈》

阅读时间:2018.4.20~2018.5.3

略读,黄段子,称为“人曲”,表达在当时压抑环境下的反抗。

《生活的艺术家》

阅读时间: 2018.7.26~2018.9.5

李小龙有关武术的哲学的讨论,以及相关他所写的文章、书信等,可略读,不推荐细读,哲学和武术相结合,相辅相成。

《人生的盛宴》

阅读时间: 2018.8.1~2018.8.26

林语堂先生对中国文学各方面的看法,包括读书、写作、花草等等。

《人性的弱点》

阅读时间: 2018.8月

推荐,遇到人生的问题可以拿出来再翻翻。讲述人际交往,人生中遇到的困难等等解决方法,类似于一本人生经验手册。

《百年孤独》加西亚·马尔克斯

阅读时间: 2018.10.1~2018.10.25

魔幻般的讲述马孔多一个家族的百年故事,夹着爱恨情仇以及孤独、战争等等,整体感觉很宏大。

《浮生若梦》by林语堂

阅读时间: 10.29-11.1

讲述林语堂自己的人生经历,以及对于文学、信仰等的见解。读起来比较闲适。

《别闹了,费曼》

阅读时间: 11.01-12.02

物理学家费曼的人生经历自述,真实有趣,有些经历还带有哲理。

其他类

《摄影笔记》

阅读时间: 2018.10~2018.12

比较推荐《摄影笔记》,感谢作者的无私分享,提供免费版本在网上传播。今年摄影的基础知识进步很多,主要归功于反复阅读《摄影笔记》和不断实践。

《如何阅读一本书》

阅读时间: 2018.4.5~ 2018.4.27

阅读的理论方法,基础阅读、略读、精读、主题阅读几个层次,推荐看看。

《国富论》

阅读时间: 2018.5.13~ 2018.5.27

讲述资本主义下经济各种概念,包括国家的运转、税收、国家基础设施的费用支出等,值得读。

《富豪俱乐部第一部》

阅读时间: 2018.5.26~2018.6.17

一本小说,讲述一个大城市顶级富豪俱乐部中发生的争斗故事,涉及到官商、财富、女人等等,让人一窥富豪的生活。

《可爱的诅咒:圣母型人格心理自助手册》

阅读时间:2018.5.28~ 2018.6.3

提出了一个心理学概念“可爱的诅咒”,指一些比较敏感的人,总是生活在别人的期许和压抑中,介绍了几种方法,试着去释放自己的压抑,活出真的自己。

《一本书读懂财报》

阅读时间: 2018.6.1~ 2018.6.16

企业财报的入门启蒙书,讲了许多企业财报相关的概念,以及用了一些实例讲解如何分析财报,对投资或者经营有兴趣的可以读一读。

《只要会呼吸,就会冥想》

阅读时间: 2018.6.17~ 2018.7.1

推荐,一步一步教你做冥想,冥想的主要目的是达到内心的宁静,与灵魂进行交流。

《创业维艰》by 本·霍格沃兹

阅读时间:2018.8.26~2019.9.11

讲述作者作为一名创业公司的CEO,管理公司的经历以及管理的技巧,可作为CEO手册。

《精益创业-新创企业的新思维》by莱斯

阅读时间: 2018.9.12~2018.9.24

本书的主要观点在于快速最小化实现想法并推向市场,根据反馈决定后续的发展。书本读起来较乏味,偏理论。

《黑客与画家》by 保罗•格雷厄姆

阅读时间: 2018.10.15~2018.10.25

第二次阅读本书,有关技术的观点依然犀利。印象深刻的有两点,一个是创业是致富的重要方式,另一个是关于编程语言的讨论,极力推荐了Lisp,值得学习一下,学习Lisp的话,个人希望和学习《计算机的设计和构造》一起。

《弗兰克尔自传-活出生命的意义》

阅读时间:2018.10.22~2018.10.26

弗兰克尔本身是个传奇人物,从纳粹的集中营中走出来,创立意识疗法。准备读他的著作《活出生命的意义》。

《正念的奇迹》

阅读时间:2018.12

一行禅师关于禅修的方法指导,正念即专注于当下。

SSH使用指南

SSH使用指南

目录

  • 简介
  • 历史
  • 入门
  • 进阶
  • 原理
  • 参考

简介

SSH(Secure Shell)是一种用于在不安全网络中进行操作网络服务的加密网络协议,提供两台电脑不安全网络间进行认证和加密数据传输。主要用于网络管理员进行远程管理使用,允许用户远程登录、远程执行命令和拷贝文件。

历史

Version 1.x

1995, 芬兰赫尔辛基科技大学的一个研究员Tatu Ylönen设计了第一版的协议(称为SSH-1),并发布了一版免费的软件。

  • 启发:校园网络受到密码嗅探攻击。
  • 目的:代替早期没有提供强认证和保密性的rlogin,Telnet,ftp和rsh协议。

1995的12月,Ylönen成立了一家公司SSH Communications Security来推广和开发SSH,并将后续的版本转为商业软件。

Version 2.x

SSH-2修订版在2006年确认为标准,在安全和特性上都胜过SSH-1,并和SSH-1不兼容。

Version 1.99

一个区分向下兼容的命名方法,指支持2.0和之前版本的一个协议版本。

OpenSSH和OSSH

1999年Björn Grönvall基于原始的SSH v1.2.12开源版本开发了OSSH,不久OpenBSD分叉了OSSH并创建了OpenSSH,此后OpenSSH继续维护广为移植,OSSH被淘汰。

OpenSSH 7.6版本移除了对SSH-1的支持。

入门

SSH可以在多平台下有多种应用场景,包括大部分的Unix变种(GNU/Linux,BSD包括MacOS,Solaris)和Windows。

使用场景:

  • 远程登录
  • 远程执行单个命令
  • 配置无密码登录远程机器
  • 配合rsync备份、拷贝和镜像文件
  • 端口转发或者端口隧道化
  • 实现一个成熟的VPN
  • 远程主机转发X window
  • 从支持Socks协议的SSH客户端进行安全的代理网页浏览
  • 使用SSHFS挂载远程主机的目录到本地主机文件系统
  • 自动化远程监控和管理服务器
  • 支持ssh的移动端或者嵌入式的开发

客户端

Unix和Linux系统中基本都包含ssh命令,这个命令启动SSH客户端用以在远程机器和SSH服务端建立安全的连接,通常使用的是OpenSSH SSH Client。

登录

1
ssh -p port_number remote_username@remote_host
  • -p port_number指定端口号,如果是默认22端口,可省略
  • remote_username为登录用户名
  • remote_host: 主机名,可以是域名或者IP

如果是首次连接该远程机器,则会出现以下的提示信息:

1
2
3
The authenticity of host 'example.com' cannot be established.
DSA key fingerprint is 04:48:30:31:b0:f3:5a:9b:01:9d:b3:a7:38:e2:b1:0c.
Are you sure you want to continue connecting (yes/no)?

选择yes继续后,将会添加这个服务器到你的已知主机列表中~/.ssh/known_hosts:

1
Warning: Permanently added 'example.com' (DSA) to the list of known hosts.

每台服务器都有一个key,上述的问题来确认和保存主机key,在下一次连接服务器时,会确认是否为同一台服务器。连接建立后,用户即认证成功。正常情况下,命令会要求用户输入密码。

退出

使用exit命令退出远程会话

1
exit

执行远程命令

ssh也可以用于不登录服务器直接远程执行命令

1
2
3
ssh host command

ssh host ls /opt

X11转发

1
ssh -X remote_host

服务端

启动ssh服务,服务端ssh的配置在进阶中讲解

1
2
3
4
sudo service ssh start

# 使用systemd的命令
sudo systemctl start ssh

进阶

使用SSH key登录

通过登录ssh时会使用每次输入密码的方式,不过使用SSH的key更方便和安全,配置key稍微麻烦一点,后续就是一劳永逸的事情,直接一条命令或者一个别名直接登录。

创建SSH Key

在你的客户端电脑创建ssh key:

1
ssh-keygen -t rsa

按回车确认,你的key会被创建在~/.ssh/id_rsa.pub和~/.ssh/id_rsa

切换到.ssh目录:

1
cd ~/.ssh

查看各文件的权限:

1
ls -l

输出结果如下:

1
2
3
-rw-r--r-- 1 demo demo  807 Sep  9 22:15 authorized_keys
-rw------- 1 demo demo 1679 Sep 9 23:13 id_rsa
-rw-r--r-- 1 demo demo 396 Sep 9 23:13 id_rsa.pub

id_rsa是仅文件拥有者可读,需要保持私密;id_rsa_pub可以被共享和读取。

传送公有Key到服务器

拷贝公有Key到服务器:

1
ssh-copy-id remote_host

这个命令会开启一个ssh会话,需要输入密码进行认证;输入密码成功后,将会拷贝公有Key到服务器的认证Key文件中,后续将允许不输入密码登录。

服务端SSH配置

服务端ssh配置是指配置sshd服务,默认情况,配置文件位于/etc/ssh/sshd_config

备份当前配置文件

1
sudo cp /etc/ssh/sshd_config{,.bak}

使用编辑器打开文件

1
sudo vi /etc/ssh/sshd_config

修改部分配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 修改端口号,默认为22,修改后客户端连接需要指定这里设置的端口
Port 22

# 设置主机的keys
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key

# 设置日志级别,排错时可增加日志级别,分别为QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3
SyslogFacility AUTH
LogLevel INFO

# 设置没有登录成功时的连接等待时间
LoginGraceTime 120

# 设置是否允许root用户登录,大多数普通用户能通过sudo提高权限并支持登录的话,该项应该设为yes
PermitRootLogin yes

# 设置安全防范,配置文件不安全时禁止登录
StrictModes yes

# 设置x窗口的转发
X11Forwarding yes
X11DisplayOffset 10

重启服务

上述设置保存后,需要重启服务才能生效

1
2
3
sudo service ssh restart
# 或
sudo systemctl restart ssh

检查配置修改是否有效,在修改配置的时候尽量保证有个用户已经登录连接,帮助设置有误时进行修改。

关闭密码认证

如果你可以安装了ssh key,并且可以通过key免密登录,否则关闭密码认证后将无法登录远程服务器。

使用管理员权限打开配置文件

1
sudo vi /etc/ssh/sshd_config

修改配置文件,关闭密码认证

1
PasswordAuthentication no

有两个配置需要确认设置正确

1
2
PubkeyAuthentication yes
ChallengeResponseAuthentication no

重启服务

1
2
3
sudo service ssh restart
# 或
sudo systemctl restart ssh

此时,密码认证将不可用,你的服务器将只能通过ssh key进行登录。

原理

基本原理

SSH的工作时通过客户端程序连接到ssh服务器。

基于Key的认证工作原理

基于Key认证的方式,需要一对Key,一个私有Key和一个公有Key。
私有Key保存在客户端电脑,并且要是安全和私密的。
公有Key放置在任何你想访问的服务器上。

当你通过键对访问时,服务器会使用公有Key创建一个消息给客户端,并且只有通过私有Key才能读取;接着,客户端发送合适的响应给服务端并认证。

参考

HTTP Learning Notes

Intro

HTTP is a protocol which allows the fetching of resources, such as HTML documents.
Clients and servers communicate by exchanging individual messages(as opposed to stream of data).The messages sent by the client, usually a Web browser, are called requests and the messages sent by server as an answer are called responses.

阅读更多

升级博客的Hexo和Next主题版本

本次升级更新了如下内容:

Hexo的版本,从3.3.8升级到3.7.1版本,没有重大的破坏性变更。

主题Next版本,从5.1升级到v6.4.2版本,两者间有不兼容的地方,配置需要重新配;另外,新版本的主题支持更多的特性。

有关Next主题的升级文档,这里给一下官方的指南,希望帮助有需要的朋友

hexo-theme-next中文文档

从 NexT v5.1.x 更新

升级过程中需要注意的是,v6和v5重大的变更之一是,插件依赖需要自己手动安装。

例如,假设你想要在你的站点中使用 fancybox 插件,请进入 NexT 配置文件,你会看到如下内容:

1
2
3
# Fancybox
# Dependencies: https://github.com/theme-next/theme-next-fancybox
fancybox: false

升级愉快!感谢next团队的贡献,v6带来了很多很棒的特性。

如何找到生活的目标并高效的生活

文章来自lifehack上的how to find the purpose of life,学习后总结了下面的要点,观点和《少即是多》书中的观点类似,核心是寻找到真正的目标并设定优先级,减少不必要的干扰。

如何找到生活的目标并高效的生活

定义你真正的优先级并减少错误的目标

方法:

  • 你最关心的是什么?
  • 哪些你正在努力的错误目标?

形成一个你所期望生活的清晰愿景

写下你能想到的所期望生活相关的所有事情,并拥有可实现的信念去执行。

创建一个计划来实现你的愿景

希望不是一种策略,你需要一个计划。

使用SMART法则创建一个详细的计划,并设置一个截止日期来实现。

有效的处理遇到的困难

最重要的一点,过你自己的生活,专注于你所关心的,围绕着你的愿景构建你的生活。

你会遇到很多困难和阻力,利用策略来有效的处理这些阻力很重要,这样你才能在困难的时候还保持正确的方向。

结论

如果做到了以上几点,你的生活会发生改变。

  • 你变得更加专注,并且围绕着你自己真正的优先级来做决定。
  • 你会发现生活的目标。
  • 你会展现更多的激情和热情。
  • 你会围绕你最关心的事物构建生活,生活将会变得很满足。