0%

Babel是一个广泛使用的转码器,我觉得Babel这个名字起得非常的好,有个神话故事,据说之前人们想要建造一个通天塔,神为了阻止他们,讲他们分散到各个地方,让彼此语言不通,这就导致了这个塔无法继续建造。这个塔就巴别塔。

Jest 钩子函数

我们如果有一些工作是在每次跑测试用例之前或者结束的时候要做的。我们就需要使用Jest提供的beforeEachafterEach
如果我们只想跑一次的话,那么我们就要用Jest提供的beforeAllafterAll这两个函数会在所有测试用例开始之前,和全部结束之后调用。

我们可以通过具体代码看一下。

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
beforeEach(() => {
console.log('beforeEach');
})

afterEach(() => {
console.log('afterEach');
})

beforeAll(() => {
console.log('beforeAll');
})

afterAll(() => {
console.log('afterAll');
})

test('test', () => {
console.log('test');
})

test('test1', () => {
console.log('test1');
})

test('test2', () => {
console.log('test2');
})

输出结果

1
2
3
4
5
6
7
8
9
10
11
beforeAll
beforeEach
test
afterEach
beforeEach
test1
afterEach
beforeEach
test2
afterEach
afterAll

通过打印的内容我们就能很直观的看到,beforeAll和afterAll在整个测试文件的开头和结尾调用一次。beforeEach和afterEach会在每个测试用例的开始和结束都会调用。

Jest 作用域

我们可以用describe来将测试分组。当afterbeforedescribe内部的时候,只适用于该describe内部的测试,但是顶级的beforeEachafterEach也会作用在该describe内部的测试用例。但是顶部的beforeEach会比内部的先执行,顶部的afterEach会比内部的晚执行。具体可以看官网给的例子。

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
beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));
test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

describe 和 test 的执行顺序

Jest会在具体test代码块之前执行所有describe处理器部分,所以我们要将准备工作都放在before、after中。

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
35
36
37
describe('outer', () => {
console.log('describe outer-a');

describe('describe inner 1', () => {
console.log('describe inner 1');
test('test 1', () => {
console.log('test for describe inner 1');
expect(true).toEqual(true);
});
});

console.log('describe outer-b');

test('test 1', () => {
console.log('test for describe outer');
expect(true).toEqual(true);
});

describe('describe inner 2', () => {
console.log('describe inner 2');
test('test for describe inner 2', () => {
console.log('test for describe inner 2');
expect(false).toEqual(false);
});
});

console.log('describe outer-c');
});

// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test for describe inner 1
// test for describe outer
// test for describe inner 2

Jest Mock函数

在平时,我们需要测试一些回调函数是否被调用,而我们不关心调用内部的执行过程和结果,这时我们就需要jest.fn来mock一个函数。

1
2
3
4
5
6
7
const mockCallback = jest.fn();
forEach([0, 1], mockCallback);

test('该模拟函数被调用了两次', () => {
// 此模拟函数被调用了两次
expect(mockCallback.mock.calls.length).toBe(2);
})

我们可以看看.mock的属性都有哪些。

  1. calls: 每次调用传入的参数
  2. instances: 每次调用时this的值,
  3. invocationCallOrder: 每次调用的执行顺序
  4. results: 每次调用的返回值

如果我们想要取调用的参数可以这样写
expect(mockCallback.mock.calls[0][0])这是第一次调用的参数。

还可以通过mockReturnValueOnce()mockReturnValue()来控制返回值。

对于API的调用,我们不必等待API的返回结果,因为这非常耗时,我们可以直接用jest.mock()来模拟axios模块,可为 .get 提供一个 mockResolvedValue ,它会返回假数据用于测试。 实际上,我们想让 axios.get(‘/users.json’) 有个假的 response。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import axios from 'axios';
import Users from './users';

jest.mock('axios');

test('should fetch users', () => {
const users = [{name: 'Bob'}];
const resp = {data: users};
axios.get.mockResolvedValue(resp);

// or you could use the following depending on your use case:
// axios.get.mockImplementation(() => Promise.resolve(resp))

return Users.all().then(data => expect(data).toEqual(users));
});

Jest Snapshot 快照

快照是我个人觉得非常有用的功能,一般用于测试配置文件,UI有没有改动。
我们先通过测试配置文件来讲解测试快照。之后会具体讲使用snapshot来测试UI组件。

snapshot的原理简单的来说,就是第一次测试的时候生成一个快照文件,第二次跑测试用例的时候,会与这个快照进行对比,如果有变化则测试不通过,当然你也可以更新快照,这时快照的内容就是你最新的改变。

Jest VSCode插件

推荐使用 Facebook官方出的 vscode-jest 插件。 安装完插件后,不用每次都手动输入jest命令。每次回自动执行测试用例。并在通过的测试用例前用绿色的小点表示,失败的为红色小点。

Jest

Jest是Facebook开源的测试框架,几乎是0配置直接进行单元测试,相对其他测试框架,其一大特点就是内置了常用的测试工具,比如自带断言、测试覆盖率等工具。Jest还有很多好处这里就不一一介绍了,详情可以到Jest官网查看https://jestjs.io/

Jest 安装

这里默认安装了node。 如果没有安装的,可自行到Node官网进行下载安装https://nodejs.org/。安装好Node后是默认自带NPM包管理工具的。我们可以通过 node -vnpm -v 来检测node、和npm是否安装成功,如果都成功的显示了版本号,就说明node和npm都安装成功了。下面我们就可以对Jest来进行安装。

Jest的安装十分简单,就一行命令

npm install --save-dev jest

因为只有在开发的时候我们才去运行测试用例,所以我们在安装Jest的时候加上--save-dev。安装完成后我们就可以进行Jest的学习了。

Jest初体验

我们可以根据Jest官网给的简单的例子,来体验一下Jest。

我们写一个需要被测试的函数,这个函数就是做一个简单的两个数相加的运算然后返回结果。首先我们先创建一个名为 sum.js 的js文件:

1
2
3
4
function sum(a, b) {
return a + b;
}
module.exports = sum;

我们用module.exports将这个函数导出
然后我们创建一个名为 sum.test.js的js文件,这个文件里写我们真正的测试代码:

1
2
3
4
5
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});

然后我们修改 package.json 文件,使得我们输入 npm test 的时候就可以运行我们的测试用例:

1
2
3
4
5
{
"scripts": {
"test": "jest"
}
}

最后我们输入npm run test来运行我们的测试用例,并可以看到Jest打印的信息:

1
2
PASS  ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)

至此我们就成功的用Jest写了第一个测试用例。

我们可以看到这段测试代码的核心就两条语句

  1. 调用一个test方法,第一个参数是对这个测试用例的描述,第二参数是一个回调函数,里面是具体的测试方法。
  2. expect(resultValue).toBe(actualValue) 这行代码,我们可以看做,当expect方法中的传入的这个值和toBe这个方法中的值完全相等。这条测试用例就算通过(例子中,resultValue是调用sum函数的返回值,actualValue是我们认为函数运行正确应该返回的值)

我们可以简单地试着实现一下这两行代码,这样能更好的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function expect(resultValue) { //函数的返回值
return {
//返回一个对象,其中有toBe方法,toBe方法接收真实值。
toBe: function(actualValue) {
//判断函数的返回值,与真实值是否相等,如果不相等,抛出错误。
if (resultValue !== actualValue) {
throw new Error('Error');
}
}
}
}

function test(description: string, fn: Function) {
try {
fn(); //执行函数fn,如果没有抛出错误,输出PASS
console.log(`PASS`);
} catch(e) {
//如果fn抛出错误,将在这里捕获,并输出错误信息。
console.log(`${description} : ${e}`)
}
}

我们可以简单的理解为,expect(resultValue).toBe(actualValue) 就是对上述代码的简化。它的实质其实就是比较。expect函数中resultValue和toBe函数中actualValue是否完全相等。之后会介绍Jest中更多的方法。

配置你的Jest

首先 我们输入下面命令,来生成Jest的配置文件。
npx jest --init

注意,我们这里使用的是npx,不是npm,npx的意思是运行的时候,会到 node_modules/.bin 路径和环境变量 $PATH 里面,检查命令是否存在。 而不是去全局环境查找命令。

输入完命令后会有几个问题让你回答,来创建基本的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
Would you like to use Jest when running "test" script in "package.json"? › (Y/n)
这个就是帮你配置package.json当,npm test的时候执行Jest。

Choose the test environment that will be used for testing › - Use arrow-keys. Return to submit.
❯ node
jsdom (browser-like)
选择node环境还是浏览器环境

Do you want Jest to add coverage reports? › (y/N)
是否增加测试覆盖率报告

Automatically clear mock calls and instances between every test? › (y/N)
每次运行测试时自动清除所有mock

回答完问题之后,会生成一个 jest.config.js 的配置文件。我们可以在里面进行Jest更多的配置。我们将会在之后专门来讲Jest的配置,以及通过babel支持TypeScript和ES Modules。

Jest –watch 监视模式运行

  • --watch 监听变化的测试用例,如果想一个文件改变而运行全部测试用例的话需要使用--watchAll

Jest Matchers 常用匹配器

匹配器(Matchers)是Jest中非常重要的一个概念,它可以提供很多种方式来让你去验证你所测试的返回值

Truthiness

  • toBe 匹配器:相当于 Object.is 或者 ===
  • toEqual 匹配器: 只匹配内容,不匹配引用。
  • toBeNull 匹配器: 内容是否等于Null
  • toBeUndefined 匹配器: 内容是否等于undefined
  • toBeDefined 匹配器: 希望内容是定义过的。
  • toBeTruthy 匹配器: 内容是否为true。
  • toBeFalsy 匹配器: 内容是否为false。
  • not 匹配器: 在其他匹配器之前,相当于取反操作

与数字相关

  • toBeGreaterThan 匹配器: 输入的数字是否大于
  • toBeGreaterThanOrEqual 匹配器: 输入的数字是否大于等于
  • toBeLessThan 匹配器: 输入的数字是否小于
  • toBeLessThanOrEqual 匹配器: 输入的数字是否小于等于
  • 对于浮点数判断相等,为了解决浮点数的bug。要用toBeCloseTo匹配器。

与String相关

  • toMatch 匹配器: 结果中是否包含内容,可以是String也可以是正则

与Array相关

  • toContain 匹配器: 判断元素是否存在数组中。

与异常相关

  • toThrow匹配器来判断在调用一个函数出现异常时,这个函数是否抛出了异常。

Jest测试异步函数

callback

我们先看下面的测试用例

1
2
3
4
5
6
7
test('testing asynchronous code', () => {
function callback(data) {
expect(data).toBe('success');
}

fetchData(callback);
});

在上面这个函数中,fetchData是一个异步的方法,去请求数据,当数据返回时调用,callback函数。我们期望这个异步函数的返回值是”success“。但是Jest并不知道这个异步函数什么时候返回。Jest仅仅只是从头执行到尾。这样这个测试用例是无效的。

为了解决这个问题。Jest提供了一个 done 参数,这个参数通过test函数的回调方法传进去,done 是一个不接受任何参数的方法。具体怎么用我们来看代码

1
2
3
4
5
6
7
8
test('testing asynchronous code with done', done => {
function callback(data) {
expect(data).toBe('success');
done();
}

fetchData(callback);
})

我们对比两个代码可以看出,下面这块代买仅仅是在回调函数中多执行了一句done()它的意思就是告诉Jest,只有运行到了done()这个命令的时候,这个test才算完事。如果一直不调用done()的话会报出超时错误。
但是这里还有点问题。就是当测试不通过的时候,会到的done不被调用。这时我们还需要改写一下代码,用try/catch 来捕获expect错误从而实现我们这个需求。

1
2
3
4
5
6
7
8
9
10
11
12
test('testing asynchronous code with done', done => {
function callback(data) {
try {
expect(data).toBe('success');
done();
} catch (e) {
done(e);
}
}

fetchData(callback);
})

Promises

如果使用Promise,会简单很多。我们只需要把 promise返回, Jest会等待promise resolve。 如果 rejected 的话,这条测试用例会自动不通过。代码如下。

1
2
3
4
5
test('testing asynchronous code', () => {
return fetchData().then(data => {
expect(data).toBe('success');
});
});

如果你用catch捕获了rejected。 那就一定要添加assertions匹配器,assertions匹配器接收一个参数,这个参数表示expect的次数,如果没有出现指定的次数,就会报错。我们改写一下上面的方法。

1
2
3
4
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});

resolves / rejects 匹配器

我们也可以用 resolves和rejects匹配器。我们直接看代码

1
2
3
4
5
6
7
test('testing asynchronous code', () => {
return expect(fetchData()).resolves.toBe('success');
});

test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});

Async/Await

最后我们可以用,Await来等待异步函数执行完成,我们只需要把test中的回调函数改写成async/await形式即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});

至此我们简单的介绍了Jest的基本用法。更多的用法我们会在下节讲到。

为什么需要构建工具?
主要是为了实现转换ES6语法、转换JSX、CSS前缀补全/预处理器、压缩混淆、图片压缩等功能。

webpack 安装

npm install webpack webpack-cli --save-dev
安装完成后,我们可以通过npx webpack -v来检测webpack是否安装成功。

webpack 核心概念

webpack配置文件

webpack 默认配置文件: webpack.conf.js
可以通过 webpack –config 指定配置文件。

Entry 打包的入口文件

指定打包的入口,告诉webpack应该使用哪个模块,来作为入口文件,webpack会找出哪些模块库是入口的直接或间接依赖。

默认入口是./src/index/js,我们也可以配置来指定一个或多个不同的入口。

简单例子:

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js'
};

这是作为单文件入口时,其实是下面的简写。

1
2
3
4
5
module.exports = {
entry: {
main: './path/to/my/entry/file.js'
}
}

entry的值可以是一个字符串,也可以是一个数组,为数组时,表示有多个主入口,想要多个依赖文件一起注入时,可以用数组。
如果是多页应用的话,也就是多入口的话entry就得写成一个对象。

1
2
3
4
5
6
module.exports = {
entry: {
app: './path/to/my/entry/app.js',
adminApp: './path/to/my/entry/adminApp.js'
}
}

Output 打包的输出

告诉webpack在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。我们也可以来配置。下面是官网给出的例子。

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};

这其中涉及到一些Node的知识,__dirname我们可以把它当做node的一个全局变量,它的值为当前目录。我们在最上面引入了path模块,调用了path中的resolve方法。这个方法主要是从左到右拼接出一个绝对路径。注:若字符以 / 开头,不会拼接到前面的路径(因为拼接到此已经是一个绝对路径)
output中的path属性就是输出文件的地址,filename则是输出文件名。
如果entry是多入口配置的话,output就需要通过占位符确保文件名称的唯一。如下

1
2
3
output: {
filename: '[name].js'
}

Loader

webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
我们来配置一下,让webpack支持TypeScript,老规矩先上代码。

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');

module.exports = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.tsx?$/, use: 'ts-loader' }
]
}
};

在module对象中有一个rules属性,里面定义一个数组,数组中的对象的test属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。这里用正则表示所有ts或者tsx结尾的文件。use属性,表示进行转换时,应该使用哪个 loader,我们这里用的是ts-loader。现在我们就可以让webpack帮我们将TypeScript转为JavaScript。

Plugins 插件配置

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,增强webpack。包括:打包优化,资源管理,注入环境变量。
我们如果使用一个插件,就需要require()它,然后把它添加到plugins数组中,如果我们想要多次用一个插件,这时我们就可以用new来创建一个它的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

module.exports = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};

上面插件的作用就是将生成的bundle注入到指定的HTML文件中。

Mode 环境

webpack4新概念,通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。

1
2
3
module.exports = {
mode: 'production'
};

这一章,主要是讲的冯诺依曼体系结构和简单介绍计算机的发展史。

一、从逻辑元件的发展,看计算机硬件的发展

  1. 电子管时代。

  2. 晶体管时代。

  3. 中小规模集成电路时代。

  4. 超大规模集成电路。

在计算机元件的更新换代中有一个著名的 摩尔定律 ——当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。

二、 冯·诺依曼体系结构

前面我们了解了计算机的硬件发展,计算机的种类是多种多样的。笔记本电脑、台式电脑、手机、IPad、服务器,这些都属于计算机,因为它们都遵循着,计算机祖师爷冯·诺依曼老爷子(对,是男的,男的)提出的,冯·诺依曼体系结构,也叫存储程序计算机。

简单的概括一下冯老爷提出的这套理论的三个基本原则:

  1. 采用二进制运算
  2. 程序存储执行
  3. 有五部分组成(运算器、控制器、存储器、输入设备、输出设备)

二进制作为主要涉及思想之一,主要是因为电子元件的双稳定工作的特点,二进制的采用可以简化机器的逻辑线路。
程序的存储执行。就意味这个这个计算机是可编程的和可存储的。就是说程序的本身是存在内存中的,根据不同的需要去加载不同的程序,来解决不同的问题。而像老式的计算器就属于不可编程的。而第一台计算机ENIAC,通过在板子上不同的插头或者接口的位置插入线路,来实现不同的功能。ENIAC属于可编程,但它不可存储。因为每次要执行与当前程序不同的程序时,需要重新插板子,所以老式的计算器和ENIAC都不属于冯·诺依曼机。

冯·诺依曼机特点如下:

  1. 计算机硬件系统由运算器、存储器、控制器、输入设备和输出设备5大部件组成。
  2. 指令和数据以同等地位存储在存储器中,并可按地址寻访。
  3. 指令和数据均用二进制代码表示。
  4. 指令由操作码和地址码组成,操作码用来表示操作的性质,地址码用来表示操作数在存储器中的位置。
  5. 指令在存储器内按顺序。通常,指令是顺序执行的,在特定条件下可根据运算结果或根据设定的条件改变执行顺序。
  6. 早期的冯·诺依曼机以运算器为中心,输入/输出设备通过运算器与存储器传输数据。

典型冯诺依曼计算机结构
典型冯诺依曼计算机结构
由于大量I/O设备的速度和CPU的速度差距悬殊,现代计算机已经发展为以存储器为核心。
现代计算机结构

三、详细介绍功能部件

在介绍每个功能部件之前,先通俗了解一下寄存器,内存和辅存,在知乎上有一个回答我觉得很好。

寄存器就是你的口袋。身上只有那么几个,只装最常用或者马上要用的东西。
内存就是你的背包。有时候拿点什么放到口袋里,有时候从口袋里拿出点东西放在背包里。
辅存就是你家里的抽屉。可以放很多东西,但存取不方便。

输入输出设备(I/O设备)

输入输出设备是计算机与外界沟通的桥梁。这个很好理解。输入设备的主要功能是将程序和数据以机器所能识别和接受的信息形式输入计算机。而输出设备就是讲计算机处理的结果以人们所能接受的形式返回。比如鼠标、键盘就是我们常见的输入设备,而显示器就是输出设备。
I/O设备,都是通过主板上面的南桥芯片组,来和CPU进行通讯的。

存储器

存储器是计算机的存储部件,用来存放程序和数据。
存储器分为主存储器和外部存储器。主存储器就是我们常说的内存,CPU可以直接访问,而外部存储器中的信息必须调入主存储器后,才能被CPU所访问。

主存储器的工作方式

按存储单元的地址进行存取,这种存取方式称为按地址存取方式(相连存储器是按内容访问的)

主存储器的基本组成

  1. 地址寄存器(MAR-Memory Address Register)
    用于寻址,其位数对应着存储单元个数,MAR为N为,则有2^N个存储单元。
  2. 数据寄存器(MDR-Memory Data Register)
    MDR的位数与存储字长相等。一般为字节的二次幂的整数倍。
  3. 存储体
    存储体是由一个一个的存储单元构成的。一般以8位二进制(8bit)也就是一字节(1Byte)作为一个存储单元。一个存储单元所存储的二进制代码的组合叫做存储字。存储字的位数就称为存储字长。存储字长可以是1B(8bit)或是字节的偶数倍。
  4. 译码器
  5. 驱动器

现代计算机。 地址寄存器和数据寄存器是放在CPU里的。分别通过地址总线和数据总线与内存通讯。

运算器

计算机的执行部件,用于算术运算和逻辑运算。核心为ALU(Arithmetic and Logical Unit,算数逻辑单元)。
包含若干个通用寄存器,用于暂存操作数和中间结果,如累加器(ACC),乘商寄存器(MQ)、操作数寄存器(X)等。
还有程序状态寄存器(PSW),也称标志寄存器,用于存放ALU运算得到的一些标志信息或者处理机的状态。如是否溢出,有无进位、结果是否为负数。

控制器

计算机的指挥中心。
CU 控制单元(Control Unit)
IR 指令寄存器,存放当前指令(InstructionRegister)。
PC (Program Counter,程序计数器) 存放指令的地址,并且可以自动加一

一般运算器和控制器集成到一个芯片上,称为中央处理器(CPU)。

指令是由操作码和地址码构成
CPU区分指令和数据的依据:指令周期的不同阶段。

四、计算机的性能指标

机器字长

指计算机一次可以处理的二进制数,字数越长则计算机的处理速度越快,处理精度越高。(一般等于内部寄存器大小)。

运算速度

每秒所能执行的指令数。单位为MIPS(Million Instructions Per Second,即百万条指令每秒)
FLOPS:每秒执行多少次浮点运算。

时钟频率

说到时钟频率,我们先来说一下频率这个概念。频率是单位时间内完成周期性变化的次数,是描述周期运动频繁程度的量。为了纪念物理学家赫兹,将频率单位定义为Hz。时钟周期时间为频率的倒数。
时钟周期时间与频率的关系
CPU的执行时间 = CPU时钟周期数 CPU时钟周期时间。
CPU时钟周期数 = 指令数
每条指令的平均时钟周期数(CPI)。

主存容量

内存储器容量的大小反映了计算机即时存储信息的能力。

下面还有几个重要的性能指标:

数据通路带宽:数据总线一次能够并行传递信息的位数。

吞吐量:系统在单位时间内处理请求的数量。(评价计算机系统性能的综合参数)

总结一些iTerm2常用的快捷键(不断更新)

基本操作

全屏 : command + enter
查找 : command + f
查看历史命令 : command + ;
删除当前行 : ctrl + u
到行首 : ctrl + a
到行尾 : ctrl + e
前进后退 : ctrl + f/b (相当于前进后退)
上一条命令 : ctrl + p
搜索历史命令 : ctrl + r
删除当前光标字符 : ctrl + d
删除光标之前的字符 : ctrl + h
删除光标之前的单词 : ctrl + w
删除到文本末尾: ctrl + k
交换当前光标和前一个文本: ctrl + t

清屏: command + r | ctrl + l

标签

新建标签 : command + t
关闭标签 : command + w
切换标签 : command + 数字 | command + 方向键
显示所有标签(可搜索): command + option + e

分屏

垂直分屏 : command + d
水平分屏 : command + shift + d
切换屏幕 : command + option + 方向键 | command + [ 和 ]

本博文仅作为个人复习使用, 并没有清楚的描述问题的细节, 主要是为了构建Linux知识体系,熟悉Linux简单操作.

目录结构

从Windows系统向Linux系统转变的时候,最不是习惯的就是目录结构,如果Windows不分区,全部东西都放C盘那感觉跟Linux也差不多(手动狗头)

目录名 说明
/ 根目录;有且只有一个根目录,Linux目录结构是一个树的结构,所有的东西都从这里开始。
/home 用户的主目录, 每个用户都有一个自己的目录, 一般都是以用户的账号命名, 主要存放个人数据。
/bin 存放最经常使用命令, 如: cat, cp, ls, mkdir, rm等。
/sbin 存放管理员的系统管理指令, 如: shutdown, reboot, 命令通常只有管理员才可以运行
/usr 包含绝大多数的(多)用户工具和应用程序。
/usr/bin 下面的都是系统预装的可执行程序,会随着系统升级而改变。
/usr/local/bin 目录是给用户放置自己的可执行程序的地方,推荐放在这里,不会被系统升级而覆盖同名文件。
/usr/sbin 存放超级用户才能使用的应用程序
/boot Linux的内核及引导系统程序所需要的文件目录
/tmp 这个目录存放一些临时文件. 对于某些程序来说, 有些文件被用了一次两次之后,就不会再被用到,像这样的文件就放在这里. 有些Linux系统会定期自动对这个目录进行清理.
/opt 这是给主机额外安装软件所摆放的目录。
/etc 这个目录用来存放所有的系统管理所需要的配置文件和子目录.

常见命令

密码的修改与创建用户

passwd —— password 命令,通过这个命令来修改当前用户密码。
useradd这个命令来添加一个新的用户,注意,添加完会直接返回,需要配合passwd命令,来给这个新用户设置密码。

万能的帮助命令

man —— manual 命令
Linux命令太多,有时候我们记不住怎么办,这是就需要一个男人。man命令的作用就是告诉你这个命令的详情。
在man命令中,我们用空格,d,b以及上下箭头键来实现上下翻页。按下 h 键会显示所有有用的键盘快捷键和一般用法。
在man命令中,输入/之后跟想要搜索的内容进行搜索,按n或shift+n来匹配下一个或上一个。
man命令分为9章,我们可以通过man + 对应章节数字来进入对应章节。

序号 章节名称 说明
1 用户命令 可由任何人启动的
2 系统调用 即由内核提供的函数
3 例程 即库函数
4 设备 即/dev目录下的特殊文件
5 文件格式描述 例如/etc/passwd
6 游戏 与游戏相关的
7 杂项 例如宏命令包、惯例等
8 系统管理员工具 只能由root启动
9 其他(Linux特定的) 用来存放内核例行程序的文档

help
再讲help命令之前,我们首先的搞清楚一个概念。Linux的内建命令和外部命令,什么是内建命令呢。正所谓内建命令就是系统启动时就存在内存当中。所以执行效率高。而外部命令是系统的软件功能,用户需要时才从硬盘中读入内存,是磁盘中的可执行程序。
一般用type命令查看该命令是内建命令还是外部命令。
hele 内建命令来查看帮助,这种形式只支持内建命令,而绝大多数都支持命令 --help来获取帮助。

info
info命令与man命令类似。但是编排上要比man命令更好。也更加的完整

cheat
号称是更好用的帮助命令,它会通过简单的实例告诉你一个命令的具体用法。
具体可以看cheat的官网

文件管理

目录查看
pwd —— print name of current/working directory 显示当前目录名称
cd —— change the shell working directory 更改当前目录操作
cd常见用法:

  • cd+目录路径进入指定目录
  • cd + -返回上一次工作目录
  • cd + ~进入当前用户的家目录
  • cd + ..返回上一层目录

ls —— list directory contents 查看当前目录下的文件
ls常用参数:

  • ls -a 全部(all)列举目录中的全部文件,包括隐藏文件(.filename)。
  • ls -l 或者 ll 列举目录内容的细节。
  • ls -F 文件类型(File type)。在每一个列举项目之后添加一个符号。这些符号包括:/ 表明是一个目录;@ 表明是到其它文件的符号链接;* 表明是一个可执行文件。
  • ls -r逆向(reverse)。从后向前地列举目录中的内容。
  • ls -R递归(recursive)。该选项递归地列举所有目录(在当前目录之下)的内容。
  • ls -S大小(size)。按文件大小排序。

目录的创建与删除
mkdir —— 用来创建指定名称的目录

  • mkdir -p parents 若所建立的上层目录目前尚未建立,则会一并建立上层目录;

rm —— 删除(remove)

  • rm -d directory删除目录,目录中没有内容。
  • rm -f force略过不存在的文件,不显示任何信息,强制删除。
  • rm -r/R recursive同时删除该目录下的所有目录层。

目录的复制、移动和重命名
cp —— 复制(copy)

  • cp -i询问,如果目标文件已经存在,则会询问是否覆盖。
  • cp -l:把目标文件建立为源文件的硬链接文件,而不是复制源文件。
  • cp -s:把目标文件建立为源文件的软链接文件,而不是复制源文件。
  • cp -p:复制后目标文件保留源文件的属性(包括所有者、所属组、权限和时间)。
  • cp -r:递归复制,用于复制目录。

move —— 移动move(改名)

  • move -b若需覆盖文件,则覆盖前先行备份。
  • move -fforce 强制的意思,如果目标文件已经存在,不会询问而直接覆盖;
  • move -i若目标文件 (destination) 已经存在时,就会询问是否覆盖!
  • move -u若目标文件已经存在,且 source 比较新,才会更新(update)
    也可以用move来重命名,如果目标文件和源文件在同一目录下,就可以用move来重命名 move 目标文件 新文件名

文件查看
tail —— 用于显示指定文件末尾内容,一般用于查看日志。

  • tail -f实时监听文件变化。
  • tail -n <number>显示行数。

head —— 与tail相对应,用于显示指定文件开头内容。

  • head -n <number>显示行数。

moreless 这两个命令相似,都是用来查看内容比较多的文件。more命令,可以按Enter键向下逐行滚动查看,按空格键可以向下翻一屏,按b键向上翻一屏,按q键退出并返回原来的命令环境。more不能逐行向上滚动查看,而less可以,我们可以把less看做more的升级版,正所谓less is more。less可以按键盘上下方向键显示上下内容,less不必读整个文件,加载速度会比more更快。less退出后shell不会留下刚显示的内容,而more退出后会在shell上留下刚显示的内容。less可以用/和?进行向下搜索和向上搜索

cat —— 查看文件内容

  • cat file1 file > file可以将多个文件合并成一个文件。

文件创建
touch —— 一是可以来创建文件,二是可以用来修改时间戳。

  • touch 文件名 如果没有文件将创建一个文件。
  • touch -d 使用指定的日期时间,而非现在的时间。
  • touch -t 与-d功能相同,只是格式不同。
  • touch -r 把指定文档或目录的日期时间,统统设成和参考文档或目录的日期时间相同。

软件安装

Linux有两种,一种是rpm是CentOS体系,一种是deb是Ubuntu体系。
分别用 rpm -idpkg -i 来进行安装。
查看已安装的软件列表用 rpm -qadpkg -l
而删除软件用 rpm -edpkg -r命令。

Linux也有自己的软件管家,CentOS下面是yum,Ubuntu下是apt-get

文件的打包与压缩

tar——打包,将所有文件整合成一个文件,方便拷贝或者移动,但并不会压缩。

  • tar -c 建立打包
  • tar -x 解压
  • tar -t 查看内容
  • tar -r 向压缩归档文件末尾追加文件
  • tar -u 更新原压缩包中的文件

在Linux下可以用gzipbzip2两个主要命令对文件进行压缩。一般都会和tar命令组合使用
bzip2的压缩比率要比gzip的高。

  • -z用gzip的压缩方式
  • -j用bz2的压缩方式
  • -v显示所有过程

切记 -f 是必须的

  • -f使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。

文件与命令的查找

find 最强大的查找命令,可以用来查找任何文件。
find <指定目录> <指定条件> <指定动作>

  • <指定目录>: 所要搜索的目录及其所有子目录。默认为当前目录。
  • <指定条件>: 所要搜索的文件的特征。
  • <指定动作>: 对搜索结果进行特定的处理。

locate 它要比find搜索快得多,因为它不搜索任何目录,而去搜索一个数据库,这个数据库中含有本地所有文件信息。但是这个数据库每天更新一次会导致查不到最新变动过的文件。要避免这个问题,可以在使用locate之前先使用 updatedb 命令来手动更新数据库。

whereis 该命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。

which 在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。

学习web框架

本文转载过多,已经找不到原文出处,所有能找到的原文地址都无法访问,入侵必删

按照这个清单,一条一条的学习,事半功倍,很快就能掌握 一共25条,即便很磨蹭,2小时也能搞定一条,25*2=50。只需要50小时就能掌握任意一种web框架

各类web框架大同小异:现代web开发框架的6大元素,把握主线,就不会迷路

建议把本文打印到一张A4纸,搞定一条打个勾

web框架学习列表

  • 如何定义 url route
  • 如何组织 request handler 函数
    1. 写一个最简单的request handler 函数
    2. 如何从get/post请求中取出参数
    3. 如何定义全局url 拦截函数
    4. 如何获取/修改/存储 cookie,session数据
    5. 如何修改/输出 http header 数据
  • 如何部部署app 程序
    1. 服务器部署可以参考 python web 程序的9种部署方式
    2. 如何配置开发环境
    3. 如何配置静态文件访问
  • 如何访问数据库
    1. 如何维护表结构的变更
    2. 如何定义/组织/初始化 数据表
    3. 如何对接orm系统和现有的表结构
    4. 掌握最基本的add/delete/按字段查询/count/slice/order by
    5. 如何直接使用sql 访问数据库
  • 如何使用模板系统
    1. 如何组织/访问 模板文件的目录结构
    2. 如何在模板中嵌入代码
    3. 模板是否支持继承结构
    4. 模板之间如何include
    5. 如何自定义模板函数
  • 如何通过http get/post 获取远程数据
  • 如何parse json
  • 如何parse xml
  • 如何输出为 json
  • 如何处理状态码:404和50x
  • 如何处理文件上传
  • 可选的学习项目
    1. 发送email
    2. log
    3. 图片处理

误区

  • 表单验证辅助函数,很多框架的表单验证部分实现的特别复杂,初学者完全不需要,手写代码处理就够用
  • ORM中的hasone,manytomany,onetomany关系,概念很复杂,其实只是多写/少写一个查询字段的关系,学习成本太高,初学者完全不需要理会,直接跳过

现代web开发框架六大元素

  1. url route/map,表面看这个就是一组正则表达式,其实这是web开发框架的核心,它决定了代码的组织方式
  2. html 模版子系统
  3. orm,或者说数据库访问层
  4. form 前后端验证,表单验证是整个网站开发过程当中最复杂的一步,需要写手很多代码
  5. helper,包括:分页,cache,session,sitemap,rss,email等支持
  6. deployment 支持,简单说,这个框架起码要内置一个http server,更进一步要支持fastcgi,这样才能部署到生产环境