Hexo


  • 首页

  • 分类

  • 归档

svg和canvas的区别

发表于 2018-06-04 | 分类于 canvas
canvas svg
通过javascript来绘制2D图形 使用XML描述2D图形的
位图 矢量图
可以使用.jpg和.png格式图片 使用现有的图片作为标签 的link的话,放大会模糊
不支持事件处理器 (addHitRegion这个api兼容性暂时不够) 可以为某个元素添加事件(把元素当成dom使用)
不能够改变大小,只能缩放显示 能够很好的处理图形大小的改变
用户交互到像素点(x, y) 用户交互到图形元素(rect, path)
最适合图像密集型的游戏,其中的许多对象会被频繁重绘 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
Canvas提供的功能更原始,适合像素处理,动态渲染和大数据量绘制
webkitBackingStorePixelRatio会影响图片和成的模糊程度

webGL是canvas+openGL,功能是可以做3D展示。

css 3D 动画

发表于 2018-06-03

一. css 3D动画基本知识

1. 坐标系

注意:旋转的时候,都是以顺时针方向为正

2. 重要属性

1️⃣ perspective: 300px

perspective 属性指定了观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果。

当为元素定义 perspective 属性时,其子元素会获得透视效果,而不是元素本身。

目前浏览器都不支持 perspective 属性。Chrome 和 Safari 支持替代的 -webkit-perspective 属性。

2️⃣ perspective-origin: 30% 30%

perspective-origin 表示你眼睛看的位置,默认就是所看舞台或元素的中心(即50%, 50%)

一般我们需要将元素置于舞台中间,否则所看到的就不是元素的正前方了。

3️⃣ transform-style: preserve-3d

一般而言,该声明应用在3D变换的兄弟元素们的父元素上,也就是舞台元素。

perspective属性和 transform-style属性最好不要申明在同一级。应该让perspective申明在外层,transform-style申明在下一层,否则无法让包含所有3D元素的最外层div作为一个整体去旋转,而只能让每一个单独的3D元素旋转。(据说safari上即使申明在不同层也无法整体旋转?)

4️⃣ transform-origin: 50% 40% 30%

更改一个元素变形的原点。默认元素变形中心点是元素自身中心点,如果手动进行设置,则是相对于元素左上角位置。

注意区分perspective-origin和transform-origin。perspective-origin是指观看视角相对于整个舞台的位置,它只有2个值(x轴位置和y轴位置),z轴距离通过perspective的值来确定。transform-origin是设置元素自身转换时候的中心点,它有3个值(x,y,z三轴的位置)。

4️⃣ backface-visibility:hidden

是否可以看见3D元素的背面,默认情况下是可以看见,但是在现实世界中,元素的背面是无法看见的,所以一般我们设置成hidden。

二. 动画 demo

1. 旋转的八面体
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<html>
<div class="box">
<div class="wrapper">
<div class="block block1">11111</div>
<div class="block block2">22222</div>
<div class="block block3">3333</div>
<div class="block block4">4444</div>
<div class="block block5">5555</div>
<div class="block block6">6666</div>
<div class="block block7">7777</div>
<div class="block block8">8888</div>
</div>
</div>
</html>
<style>
.box {
perspective: 800px;
}
.wrapper {
margin: 300px;
width: 200px;
height: 200px;
transform-style: preserve-3d;
animation: blockRotate 5s infinite 1s;
}
.block {
position: absolute;
top: 0px;
width: 100%;
height: 100%;
transition: transform 0.2s;
}
.block1 {
transform: rotateY(0) translateZ(241.4px);
background: red;
}
.block2 {
transform: rotateY(45deg) translateZ(241.4px);
background: pink;
}
.block3 {
transform: rotateY(90deg) translateZ(241.4px);
background: yellow;
}
.block4 {
transform: rotateY(135deg) translateZ(241.4px);
background: #565656;
}
.block5 {
transform: rotateY(180deg) translateZ(241.4px);
background: green;
}
.block6 {
transform: rotateY(225deg) translateZ(241.4px);
background: blue;
}
.block7 {
transform: rotateY(270deg) translateZ(241.4px);
background: purple;
}
.block8 {
transform: rotateY(315deg) translateZ(241.4px);
background: #778833;
}
@keyframes blockRotate {
to {
transform: rotateY(360deg);
}
}

注意:先rotate 再 translate 和 先translate 再 rotate得到的结果不一样。因为rotate会改变元素的坐标系的方向。尝试将上面

1
2
3
4
5
6
7
8
9
.block2 {
transform: rotateY(45deg) translateZ(241.4px);
background: pink;
}
// 将上面的改成下面的代码,两者结果不一样
.block2 {
transform: translateZ(241.4px)rotateY(45deg);
background: pink;
}

手写一款promise的简单实现

发表于 2018-05-29 | 分类于 javascript

一. promise简单实现

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
class MyPromise {
constructor(excutor) {
if (!isFunction(excutor))
throw new TypeError(
'Pass function object to create a Promise object'
);
this.status = 'pending';
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// excutor函数有可能出错,所以用try catch包住
try {
// 需要绑定this
excutor(this.resolve.bind(this), this.reject.bind(this));
} catch (err) {
this.reject(err);
}
}
resolve(value) {
if (this.status === 'pending') {
this.value = value;
this.status = 'fulfilled';
this.onFulfilledCallbacks.forEach(item => {
if (isFunction(item)) {
item();
}
});
}
}
reject(reason) {
if ((this.status = 'pending')) {
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.forEach(item => {
if (isFunction(item)) {
item();
}
});
}
}
/**
*
* @param {*} onFulfilled
* @param {*} onRejected
* then 函数需要返回一个新的promise
*/
then(onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled)
? onFulfilled
: function(data) {
this.resolve(data);
};
onRejected = isFunction(onRejected)
? onRejected
: function(err) {
throw err;
};
let self = this;
if (this.status === 'fulfilled') {
return new MyPromise((resolve, reject) => {
try {
let x = onFulfilled(self.value);
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (err) {
reject(err);
}
});
}
if (this.status === 'rejected') {
return new Promise((resolve, reject) => {
try {
let x = onRejected(self.reason);
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (err) {
reject(err);
}
});
}
if (this.status === 'pending') {
return new MyPromise((resolve, reject) => {
self.onFulfilledCallbacks.push(() => {
let x = onFulfilled(self.value);
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
});
self.onRejectedCallbacks.push(() => {
let x = onRejected(self.reason);
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
});
});
}
}
catch(fn) {
return this.then(null, fn);
}
// 所有的入参promise都resolve之后才返回一个resolve的结果promise
static all(arr) {
if (!arr instanceof Array)
throw new TypeError('Pass array to create a Promise array');
return new MyPromise((resolve, reject) => {
let length = arr.length;
const resultArr = [];
arr.forEach((item, index) => {
try {
if (item instanceof MyPromise) {
item.then(value => {
resultArr[index] = value;
if (--length === 0) {
resolve(resultArr);
}
});
} else if (isFunction(item)) {
resultArr[index] = item();
if (--length === 0) {
resolve(resultArr);
}
}
} catch (err) {
reject(err);
}
});
});
}
/** race方法
* 只要有一个入参promise被resolve之后,就将返回的promise进行resolve。
* 注意这种事先方案并不会将返回得比较慢的promise进行取消执行。后面的promise即使resolve或者reject了也无法改变被返回的promise的状态
*/
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
if (!promiseArr instanceof Array)
throw new TypeError('Pass array to create a Promise array');
promiseArr.forEach(item => {
item.then(resolve, reject);
});
});
}
}

function isFunction(fn) {
return typeof fn === 'function';
}

module.exports = MyPromise;

二. 测试代码

  1. 测试基本功能———测试代码1

    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    const MyPromise = require('./my-promise.js');
    const ThePromise = MyPromise;
    const fs = require('fs');

    const test = () => {
    const pro = new ThePromise((resolve, reject) => {
    fs.readFile('./a.js', 'utf-8', (err, data) => {
    if (err) {
    throw err;
    }
    console.log('data111:', data);
    resolve(data);
    });
    });
    console.log('pro', pro);
    const pro2 = pro.then(
    value => {
    console.log('data2222:', value);
    return new ThePromise(resolve => {
    fs.readFile('./a.js', 'utf-8', (err, data) => {
    if (err) {
    throw err;
    }
    console.log('data3333:', data);
    resolve(data);
    });
    });
    },
    reason => {
    throw reason;
    }
    );
    // 注意这里是pro2.then 如果是pro.then 那么执行顺序会不一样
    pro2.then(data => {
    console.log('data4444:', data);
    return new ThePromise(resolve => {
    fs.readFile('./a.js', 'utf-8', (err, data) => {
    if (err) {
    throw err;
    }
    console.log('data555:', data);
    resolve(data);
    });
    });
    });
    };

    test();
  2. 测试promise.all功能——–测试代码2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const MyPromise = require('./my-promise.js');
    const ThePromise = MyPromise;
    const fs = require('fs');

    const testAll = function() {
    const pro1 = new MyPromise((resolve, reject) => {
    fs.readFile('./a.js', 'utf-8', (err, data) => {
    console.log('a.js:', data);
    resolve(data);
    });
    });
    const pro2 = new MyPromise((resolve, reject) => {
    fs.readFile('./b.js', 'utf-8', (err, data) => {
    console.log('b.js:', data);
    resolve(data);
    });
    });
    MyPromise.all([pro1, pro2]).then(arr => {
    console.log('arr[0]:', arr[0]);
    console.log('arr[1]:', arr[1]);
    });
    };

    testAll();
  3. 测试promise.race功能———测试代码3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const MyPromise = require('./my-promise.js');
    const ThePromise = MyPromise;
    const fs = require('fs');

    const testRace = function() {
    const pro1 = new MyPromise((resolve, reject) => {
    fs.readFile('./a.js', 'utf-8', (err, data) => {
    console.log('a.js:', data);
    resolve(data);
    });
    });
    const pro2 = new MyPromise((resolve, reject) => {
    fs.readFile('./b.js', 'utf-8', (err, data) => {
    console.log('b.js:', data);
    resolve(data);
    });
    });
    MyPromise.race([pro1, pro2]).then(data => {
    console.log('race-result:', data);
    });
    };
    testRace();

三. 注意点

  1. 一个promise在resolve之后,就会按照顺序执行这个promise所有的then函数里所注册的函数。

    注意是这个promise。而不是由then函数重新生成的promise。

    比如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const pro1 = new Promise((resolve, reject) => {resolve('aaa')})
    const pro2 = pro1.then((data) => {
    // 这是一个异步操作
    // 打印点0
    fs.readFile('./b.js', 'utf-8', (err, data) => {
    // 打印点1
    console.log('b.js:', data);
    resolve(data);
    });
    }, (reason) => {console.log(reason)});
    const pro22 = pro1.then(data => {
    // 打印点2
    consolele.log(data);
    }, (reason) => {console.log(reason)});
    const pro3 = pro2.then((data) => {
    // 打印点3
    console.log(data);
    });

    打印顺序为:打印点0 打印点2 打印点1 打印点3

    原因:0,1 和2本来都是应该在pro1被resolve之后就会执行,但是打印完0之后,1是在一个异步函数结束之后才会打印,所以pro1.then会先返回一个pending状态的promise,并且继续执行pro1的第二个then函数。而打印点3是必须要pro1的第一个then函数所返回的promise被resolve只有才能执行的,所以会在最后打印。

  2. 由此可见,promise之所以能够保持异步函数执行顺序,是因为每个promise只有一个then函数,在then函数里面又重新返回一个promise,然后下一个promise是在前一个then函数所返回的promise被resolve之后才执行,也就是这样:

    1
    2
    3
    4
    5
    // 可以保证顺序:
    pro1.then().then().then()
    // 下面两个then之间在有异步操作情况下不可以保证顺序,但是pro1和他对应的then还是可以保证顺序的
    pro1.then()
    pro1.then()

    也就是说:

    一个promise如果对应了两个then,而这两个then中又有异步操作的话,这两个then是无法保证顺序的。

    只有promise的入参函数里面的操作和这个promise的then函数里面的操作才是保证执行先后顺序的:即promise的入参函数里面的操作先执行,then函数里面的操作后执行。

    而两个并列的then函数之间如果有异步操作则无法保证顺序,同步的话还是可以保证按照顺序执行的。因为从上面实现代码可以看出来,当promise的入参函数返回的promise是pending状态时,then函数里面的操作是被存入一个数组里,等待promise被resolve再拿出来执行;当promise的入参函数返回的promise是resolve或者reject状态时,则直接可以执行相应的then函数里面的操作,不需要存入数组了

shell学习

发表于 2018-05-28 | 分类于 linux

一. 硬件,核心与shell的关系

shell是一个程序,他接受从键盘输入的命令,然后把命令传递给操作系统去执行。

也就是说,我们必须要透过『 Shell 』将我们输入的命令与 Kernel 沟通,好让 Kernel 可以控制硬件来正确无误的工作!

二. shell相关操作

1.查看shell的种类: 合法shell的类型放在/etc/shells文件里面

2.查看当前shell类型:echo $SHELL 或者 echo $0 或者 或者 ps -p $$

($$表示当前进程id. $0表示当前进程名字, $?表示上个运行命令的回传值)

3.切换shell类型:切换shell :chsh -s /bin/bash

4.PS1设置shell命令提示字符串

5.export PATH="$PATH :/usr/bin" 将变量export出去,这样子程序就能使用这个变量

三. 常用命令

首先说明什么是命令,命令可以是下面四种形式之一:
  1. 是一个可执行程序。就像我们看到的位于/usr/bin中的文件一样,属于这一类的程序,可以编译成二进制文件。它可以是用c和c++写成的程序,也可以是有脚本语言写成的程序,比如shell,perl,Python,ruby等。
  2. 是一个内置于shell的命令(builtins)。比如cd命令。
  3. 是一个命令别名,用aliass生成的命令。
  4. 是一个shell函数,把他们放到环境变量中就可以直接执行。
常用命令

type:查看命令类型(是上面四种的哪一种)

which:显示可执行程序位置,注意只是命令里面的可执行程序的位置。

Whereis: 不只可以查找命令,其他文件类型都可以, 范围比which大。但是一般使用which

file查看文件类型

四. shell的执行顺序

命令运行的顺序可以这样看:

  1. 以相对/绝对路径运行命令,例如『 /bin/ls 』或『 ./ls 』;

  2. 由 alias 找到该命令来运行;

  3. 由 bash 内建的 (builtin) 命令来运行;

  4. 透过 $PATH 这个变量的顺序搜寻到的第一个命令来运行。

    注意:使用source或者. 来执行的脚本是在当前shell环境中,如 source b.sh 或者 ./b.sh

    使用shell b.sh 命令来执行的脚本是在自己的shell环境下执行的

注意 关于shell的执行命令 : . 和source是等价的,他们都是在当前shell里面执行,不会建立子shell

1
2
3
. ./a.sh
source ./a.sh
./a.sh

上面三个命令都可以执行当前路径下的a.sh

前面已经说了. 和source是等价的。然而. ./a.sh 和./a.sh的区别在于:

. ./a.sh是执行当前路径下的a脚本( . 表示执行,文件名是参数),

./a.sh是传入了一个文件名作为命令,需要在文件内部去通过#!/bin/bash 或者#!/usr/bin/env node 来指定脚本解释器

而且./a.sh 是新开子shell来执行脚本,子shell里面的变量无法被父shell获取

五. 父子shell参数传递问题

  1. 父shell给子shell传参:

    父亲定义的变量如果使用source方式则儿子可以直接使用,如果是用./a.bash 的方式执行子shell,那么需要付shell export 变量才能让儿子拿到数据。

  2. 子shell给父shell传参

    (1)在子shell里面将数据echo出去

    (2)父shell如果使用source方式执行儿子(其实使用source执行,父子之间变量都是公用的,其实他们是在同一个shell里,也没有父子之说,为了区分两个shell,我暂时这么称呼)

    1
    2
    3
    4
    5
    6
    7
    8
    #! /bin/bash
    # 当前文件名字为father.sh
    export fatherVar="i am father"
    chmod +x ./son.sh
    ./son.sh
    sonA=$(. ./son.sh)
    echo "我是父亲,我打印来自儿子shell中的变量${sonVar}"
    echo "我是父亲,我打印来自儿子shell echo得到的数据:${sonA}"
    1
    2
    3
    4
    5
    #! /bin/bash
    # 当前文件名字为son.sh
    sonVar="i am son"
    echo "我是儿子,我打印来自父亲的变量${fatherVar}"
    echo "abc"

六. bash 环境中的特殊符号

符号 内容
# 批注符号:这个最常被使用在 script 当中,视为说明!在后的数据均不运行
\ 跳脱符号:将『特殊字符或通配符』还原成一般字符
| 管线 (pipe):分隔两个管线命令的界定(后两节介绍)
; 连续命令下达分隔符:连续性命令的界定 (注意!与管线命令并不相同)
~ 用户的家目录
$ 取用变量前导符:亦即是变量之前需要加的变量取代值
& 工作控制 (job control):将命令变成背景下工作
! 逻辑运算意义上的『非』 not 的意思!
/ 目录符号:路径分隔的符号
>, >> 数据流重导向:输出导向,分别是『取代』与『累加』
<, << 数据流重导向:输入导向 (这两个留待下节介绍)
‘ ‘ 单引号,不具有变量置换的功能
“ “ 具有变量置换的功能!
`` 两个『 ` 』中间为可以先运行的命令,亦可使用 $( )
( ) 在中间为子 shell 的起始与结束
{ } 在中间为命令区块的组合!

关于减号 - 的用途

管线命令在 bash 的连续的处理程序中是相当重要的!另外,在 log file 的分析当中也是相当重要的一环, 所以请特别留意!另外,在管线命令当中,常常会使用到前一个命令的 stdout 作为这次的 stdin , 某些命令需要用到文件名 (例如 tar) 来进行处理时,该 stdin 与 stdout 可以利用减号 “-“ 来替代, 举例来说:

tar -cvf - /home | tar -xvf -

上面这个例子是说:『我将 /home 里面的文件给他打包,但打包的数据不是纪录到文件,而是传送到 stdout; 经过管线后,将 tar -cvf - /home 传送给后面的 tar -xvf - 』。后面的这个 - 则是取用前一个命令的 stdout, 因此,我们就不需要使用 file 了!这是很常见的例子喔!注意注意!

七. 文件压缩命令

1. zip和unzip
  1. 我想把一个文件abc.txt和一个目录dir1压缩成为yasuo.zip:

# zip -r yasuo.zip abc.txt dir1

2.我下载了一个yasuo.zip文件,想解压缩:

# unzip yasuo.zip

3.我当前目录下有abc1.zip,abc2.zip和abc3.zip,我想一起解压缩它们:

# unzip abc\?.zip

4.我有一个很大的压缩文件large.zip,我不想解压缩,只想看看它里面有什么:(v:verbose)

# unzip -v large.zip

5.我下载了一个压缩文件large.zip,想验证一下这个压缩文件是否下载完全了(t:test)

# unzip -t large.zip

2.tar

-c 建立压缩文档(create)

-x 解压(extract)

-t 查看内容(list)

-r 向压缩归档文件末尾添加文件(accretion)

-u 更新原压缩包中的文件(update)

这5个是独立的命令,解压压缩都要用到其中的一个,可以和别的命令连用,但只能用其中一个。

下面的参数是根据需要在压缩或者解压的时候可选用的:

-z 带有gzip属性的

-j 带有bz2属性的

-v 显示所有过程(verbose)

参数-f是必须的:代表使用档案的名字(就是目标文件名),切记这个参数是最后一个参数,他的后面只能跟文件名(file)

例子

解压:

tar -xvf file.tar 解压file.tar

tar -xzvf file.tar.gz 解压file.tar.gz

tar -xjvf file.tar.bz2 解压file.tar.bz2

tar -xjvf file.tar.bz2 index.html 只解压file.tar.bz2里面的index.html文件

tar -xjvf file.tar.bz2 -C /etc 解压file.tar.bz2到/etc目录下

压缩:

tar -cvf jpg.tar *.jpg 将所有jpg文件打包压缩成tar.jpg

npm link的原理

发表于 2018-05-28 | 分类于 linux

一:npm link

首先讲一讲npm link 的原理

假设我们开发了一个模块叫 hello ,然后我们在 test-project 里引用这个模块 ,每次 hello 模块的变动我们都需要反映到 test-project 模块里。使用npm link将会非常有效

第一步:将hello模块放到全局上

1
2
cd /Users/yangxia/myproject/test-project # 进入test-project模块目录,这个目录是hello模块的开发目录
npm link # 建立软链接

第二步:在某一个项目里使用上一步的hello模块

1
2
cd /Users/yangxia/myproject/use-hello-project # 进入use-hello-project模块目录,这个目录是使用方,而test-project是提供方
npm link hello # 在执行

生效原因:

第一步:

因为test-project下的package.json下的bin字段是这样的

1
2
3
"bin": {
"hello": "./hello"
},

所以当执行npm link 的时候,命令行会出现:

1
2
/usr/local/bin/hello -> /usr/local/lib/node_modules/test-project/hello
/usr/local/lib/node_modules/test-project -> /Users/yangxia/myproject/test-project

意思是在/usr/local/bin/目录下的hello指向了/usr/local/lib/node_modules/test-project/hello,

/usr/local/lib/node_modules/目录下的test-project指向了/Users/yangxia/myproject/test-project

也就是建立了两个软连接。

第二步:

当在需要的项目里执行第二步的npm link hello时,use-hello-project这个项目下的node_modules下的hello模块就指向全局的hello,即:

1
/Users/yangxia/myproject/use-hello-project/hello -> /usr/local/lib/node_modules/test-project/hello

如果使用

1
ll -d /usr/local/lib/node_modules/test-project

会得到

1
lrwxr-xr-x  1 yangxia  admin    37B  5 28 15:18 /usr/local/lib/node_modules/test-project -> /Users/yangxia/myproject/test-project

代表这个目录只是一个软连接

同时

1
ll /usr/local/bin/hello

会得到

1
lrwxr-xr-x  1 yangxia  admin    38B  5 28 15:18 /usr/local/bin/hello -> ../lib/node_modules/test-project/hello

代表这个文件也是一个软连接

总结:npm link就是通过一个文件链接和一个目录链接来达到将一个模块放到全局上,让所有目录都可以访问到此模块的目的。

二 . npx

根据 zkat/npx 的描述,npx 会帮你执行依赖包里的二进制文件。

举例来说,之前我们可能会写这样的命令:

1
2
npm i -D webpack
./node_modules/.bin/webpack -v

如果你对 bash 比较熟,可能会写成这样

1
2
npm i -D webpack
`npm bin`/webpack -v

有了 npx,你只需要这样

1
2
npm i -D webpack
npx webpack -v

也就是说 npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装!

linux 学习

发表于 2018-05-23 | 分类于 linux

学习《鸟哥私房菜》,做一点学习记录。

一: 计算机硬件相关知识

1. 计算机硬件的五个组成

输入单元:键盘,鼠标,读卡机,扫描仪,手写板,触控屏幕等等。

输出单元:包括屏幕,打印机等。

主机部分:包括CPU(控制单元与算数逻辑单元),内存。

外部存储:硬盘等

2.CPU的种类

①精简指令集(SISC: reduced instruction set computer): 微指令集较为精简,每个指令执行时间都很短,指令的执行效率较好。但是要做复杂的事情,就需要多个指令集来完成。常用与特殊用于某一功能的机器,比如导航系统,网络设备(交换机和路由器)等,他们功能比较单一,不像PC功能复杂。

②复杂指令集(CISC: complex instruction set computer): 指令数目多而且复杂,每条指令长度不相同。因为指令执行较为复杂所以每条指令花费的时间较长, 但每条个别指令可以处理的工作较为丰富。常用与功能复杂的机器,比如我们的PC机。

3. 操作系统在计算机中的角色和功能

①组成:操作系统包括操作系统核心(Kernel)和系统调用(System Call)两部分:操作系统核心功能是管理计算机所有活动以及驱动所有硬件。系统调用是为了保护核心不被改动,而提供的一组开发界面。

②核心功能:主要负责计算机的资源分配与管理,包括文件管理,内存管理,进程控制,硬件管理等等。

③角色:操作系统处于硬件之上,应用程序之下,连接硬件和应用程序。

二: linux磁盘分区

linux系统是安装在计算机组件的那个位置呢?那就是磁盘啦。所以我们先来认识一下磁盘。

1. 磁盘组成

磁盘有扇区(sector)和磁柱(Cylinder)两种单位。其中扇区是512bytes。

是否每个扇区都一样重要呢?其实每个磁盘的第一个扇区是很重要的,因为第一个扇区记录了两个重要信息:

①主引导扇区(Master boot record :MBR):安装启动管理程序的地方,有446bytes

②分割表(partition table):记录整个硬盘分割状态,有64bytes

另外2bytes是结束标志 446+64+2=512bytes

2. 磁盘组成之分割表

一个分割槽就是一个文件系统。前面的图片中有“开始磁柱”和“结束磁柱”,从开始磁柱到结束磁柱就组成一个分割槽,也就是一个文件系统。在分割表的64个字节中,最多有4组记录区,每组记录区记录了这个分割槽的起始磁柱和结束磁柱号码。

有于分割表就只有64 bytes而已,最多只能容纳四笔分割的记录, 这四个分割的记录被称为主要(Primary)或延伸(Extended)分割槽。 根据上面的图示与说明,我们可以得到几个重点资讯:

  • 其实所谓的『分割』只是针对那个64 bytes的分割表进行配置而已!
  • 硬盘默认的分割表仅能写入四组分割资讯
  • 这四组分割资讯我们称为主要(Primary)或延伸(Extended)分割槽
  • 分割槽的最小单位为磁柱(cylinder)
  • 当系统要写入磁盘时,一定会参考磁盘分区表,才能针对某个分割槽进行数据的处理

磁盘分割的好处:

① 数据安全性:一个分割槽的数据损坏不会影响另一个分割槽

②读写磁盘更高效:如果不分割,那么每次都是从第一个磁柱开始搜索或读写,性能很低。

虽然分割表只有记录4组数据的空间,但是不代表我们的硬盘只能分割出4个分割槽,因为我们可以利用拓展分配进行逻辑分割:扩展分配的想法是: 既然第一个磁区所在的分割表只能记录四笔数据, 那我可否利用额外的磁区来记录更多的分割资讯?实际上图示有点像底下这样:

3. 启动流程与主要启动扇区(MBR)

既然操作系统也是软件,那么我的计算机又是如何认识这个操作系统软件并且运行他的? 明明启动时我的计算机还没有任何软件系统,那他要如何读取硬盘内的操作系统文件啊?嘿嘿!这就得要牵涉到计算机的启动程序了! 底下就让我们来谈一谈这个启动程序吧!

  1. 首先主板上有一个存储器CMOS,上面有一个软件程序BIOS,在计算机系统启动的时候,会主动启动这个BIOS,注意BIOS是主板上的一个程序,每个主板都有的,不需要我们去载入他。

  2. BIOS会去分析计算机里面有哪些存储设备,BIOS会根据使用者的配置去读取能够启动的硬盘,并且回去该硬盘上读取第一个磁区的MBR位置, MBR这个仅有446 bytes的硬盘容量里面会放置最基本的启动管理程序。此时BIOS就功成圆满,而接下来就是MBR内的启动管理程序的工作了。

    (注意MBR只是硬盘上的一个位置,是一个扇区,就是最前面的446个字节的地方;而这个地方所存放的基本启动程序才是用于启动系统的软件)

  3. 这个启动程序的任务是加载核心文件,然后接下来就是核心文件的工作,启动管理程序也功成圆满,之后就是大家所知道的操作系统的任务啦!

总结一下过程:CMOS上的BIOS —》MBR上的启动程序 —》核心文件开始操作系统的功能

4. MBR上的启动管理程序

由於MBR仅有446 bytes而已,因此这个启动管理程序是非常小而美的。 这个boot loader的主要任务有底下这些项目:

  • 提供菜单:使用者可以选择不同的启动项目,这也是多重启动的重要功能!

  • 加载核心文件:直接指向可启动的程序区段来开始操作系统;

  • 转交其他loader:将启动管理功能转交给其他loader负责。

    上面前两点还容易理解,但是第三点很有趣喔!那表示你的计算机系统里面可能具有两个以上的启动管理程序呢! 有可能吗?我们的硬盘不是只有一个MBR而已?是没错啦!但是启动管理程序除了可以安装在MBR之外, 还可以安装在每个分割槽的启动磁区(boot sector)喔!瞎密?分割槽还有各别的启动磁区喔? 没错啊!这个特色才能造就『多重启动』的功能啊!

    我们举一个例子来说,假设你的个人计算机只有一个硬盘,里面切成四个分割槽,其中第一、二分割槽分别安装了Windows及Linux, 你要如何在启动的时候选择用Windows还是Linux启动呢?假设MBR内安装的是可同时认识Windows/Linux操作系统的启动管理程序, 那么整个流程可以图示如下

在上图中我们可以发现,MBR的启动管理程序提供两个菜单,菜单一(M1)可以直接加载Windows的核心文件来启动; 菜单二(M2)则是将启动管理工作交给第二个分割槽的启动磁区(boot sector)。当使用者在启动的时候选择菜单二时, 那么整个启动管理工作就会交给第二分割槽的启动管理程序了。 当第二个启动管理程序启动后,该启动管理程序内(上图中)仅有一个启动菜单,因此就能够使用Linux的核心文件来启动罗。 这就是多重启动的工作情况啦!我们将上图作个总结:

  • 每个分割槽都拥有自己的启动磁区(boot sector)
  • 图中的系统槽为第一及第二分割槽,
  • 实际可启动的核心文件是放置到各分割槽内的!
  • loader只会认识自己的系统槽内的可启动核心文件,以及其他loader而已;
  • loader可直接指向或者是间接将管理权转交给另一个管理程序。

三. linux 文件系统

1. linux文件权限与目录配置
  1. 介绍文件权限与属性

上图每一列所代表的意义如下:

这部分知识比较简单,这里不做过多解释,需要注意的点如下:

①第一栏代表这个文件的类型与权限,其中第一个代表这个文件是『目录、文件或链接文件等等』

  • 当为[ d ]则是目录
  • 当为[ - ]则是文件
  • 若是[ l ]则表示为连结档(link file);
  • 若是[ b ]则表示为装置文件里面的可供储存的接口设备(可随机存取装置);
  • 若是[ c ]则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。

②图中倒数第二列代表的是文件最后修改的时间,如果想看最后一次显示的时间和第一次创建的时间,可以根据给ls 不同的参数来实现(比如mac上是-u和-U,linux上是 -u和-c),具体查看man ls 命令的输出结果

  1. 修改文件权限

①改变文件所属群组 :chgrp

1
chgrp users install.js

把install.js的文件所属群组改成users组

②改变文件拥有者 :chown

1
chown bin install.log

把install.js的文件所属用户组改成yang

③改变文件的权限,SUID, SGID, SBIT等等的特性 :chmod

不同权限对应的数值 r:4 w:2 x:1

1
chmod 754 .bashrc

上面的命令代表:改变.bashrc文件拥有者可以读写执行,同组的人可以读和执行,其他人可以读

同时还有下面的方式来改变权限:

u是user,g是group,o是other,a是all(所有人)

1
2
3
chmod  u=rwx,go=rx  .bashrc
chmod u+w .bashru
chmod a+w .bashrc
  1. 文件默认权限

    umask 可以得到 『目前使用者在创建文件或目录时候的权限默认值』

    查阅的方式有两种,一种可以直接输入 umask ,就可以看到数字型态的权限配置分数, 一种则是加入 -S (Symbolic) 这个选项,就会以符号类型的方式来显示出权限了! 奇怪的是,怎么 umask 会有四组数字啊?不是只有三组吗?是没错啦。 第一组是特殊权限用的,我们先不要理他,所以先看后面三组即可。

    1
    2
    3
    4
    [root@www ~]# umask
    0022 <==与一般权限有关的是后面三个数字!
    [root@www ~]# umask -S
    u=rwx,g=rx,o=rx

    umask 的分数指的是『该默认值需要减掉的权限!』

    一下是创建文件和目录时的(最高权限)-(被剥夺的权限)=还剩下的权限

    • 创建文件时:(-rw-rw-rw-) - (—–w–w-) ==> -rw-r–r–

    • 创建目录时:(drwxrwxrwx) - (d—-w–w-) ==> drwxr-xr-x

      注意目录一定要有x执行权限,否则无法进入该目录。

      可以修改文件和目录的默认权限:

      1
      umask 002
  2. 文件隐藏属性lsattr chattr和特殊权限 SUID/SGID/SBIT

    这是链接,可自行查看

  3. 权限和命令间的关系: 执行不同命令需要不同权限

    这是链接,可自行查看

  4. 目录配置

    linux下不同目录里面放置不同的内容,可自行上网查阅。

    另外区分两个命令 ll -d mydirecorty :显示mydirecorty这个目录文件本身的信息

    ​ ll mydirecorty:显示mydirecorty这个目录里面所含文件的文件信息

2. 磁盘与文件系统管理

这一节鸟哥说的非常清楚,直接点这里

如保持登录状态

发表于 2018-05-23

一:为什么需要保持登录状态

因为http协议是无状态的,第一次请求和第二次请求之间毫无联系,http协议不会记录两次请求之间的关联。而我们不能让用户登录之后马上又要登录,所以需要记录用户的登录状态。

二:怎样记录用户的登录状态

使用session+cookie的方式记录登录状态

这里解释一下什么是session

Session一般译作会话,牛津词典对其的解释是进行某活动连续的一段时间。从不同的层面看待session,它有着类似但不全然相同的含义。比如,在web应用的用户看来,他打开浏览器访问一个电子商务网站,登录、并完成购物直到关闭浏览器,这是一个会话。而在web应用的开发者开来,用户登录时我需要创建一个数据结构以存储用户的登录信息,这个结构也叫做session。因此在谈论session的时候要注意上下文环境。

三:具体方法

未登录或者登录失效情况下要进行的拦截部分

1.页面拦截

① 如果是单页面,怎在请求页面的时候,浏览器所请求的index.html会正常返回。同时需要有一个接口来查询登录态,根据这个接口返回的不同信息来区分展示页面不同的路由。

若已登录,则允许访问某页面;若未登录,跳转到登录路由。

②如果是多页面,则是在服务端查询登录态,若已登录,则返回相应的页面index.html;若未登录,则跳转到登录页面login.html。

2. 接口拦截

用户在访问某些有权限控制的接口时,需要做登录状态检测,这个是在服务端控制的,通过返回不同的状态码来区分(403或者200)

登录时前端和后端需要做的具体操作:登录状态存储

1. 登录信息存储在服务端

服务端通过cookie所存储的sessionid作为key,来获得服务端map里存储的此key所对应的value,这个value则是用户的详细信息。因此,服务端需要存储这个对象或者说是map,即我们所说的session。

存储session的方式有2种,包括存储在内存和存储在数据库里面。存储在内存是不合适的,因为服务重启就会导致信息丢失。所以应该存储在数据库里。

对于存储在数据库这种情况,需要一直同步用户登录状态,所以需要频繁修改数据库。当用户登陆的时候,需要在数据库添加一条记录。当用户退出时,删掉此用户的记录。并且因用户操作网站而导致cookie生效时间需要延长,这个时候也需要修改数据库(修改数据库里面session有效时间)。

存储在数据库里,重点是要注意同步前端cookie和后端session的过期时间。关键点包括:

①什么时候清掉失效的session。因为cookie过期时,浏览器不会再将cookie带到服务端,但是此时服务端session还在数据库里。当用户登录的时候,服务端不知道这个用户是由于cookie过期而没有cookie还是因为是新用户而没有cookie。所以要找到一个时机去删掉服务端的session来同步登录信息?

一种解决方法:把过期信息存在session里,让cookie一直不过期,所以只要登陆过,就有cookie,但是取出来的session可能是已经过期的,这时候将其删除,并把用户标记为未登录。

② 当用户请求接口或者页面的时候(即用户对网站有操作),需要不断刷新cookie的过期时间。这就意味着也要一直操作数据库更新session,成本太高。所以我们可以只让部分接口来刷新cookie过期时间,其他接口不刷新过期时间。

2. 登录信息存储在客户端

登录信息经过加密之后存储在cookie里面

1:客户端输入用户名和密码,提交到服务端验证

2:服务端验证成功后,给客户端返回以下值:

​ uid : 用户的唯一标示

​ time : 当前unix时间戳

​ key : MD5(uid+time+”一个只有你自己知道的字符串密钥”)

3:客户端保存以上3个值在本地,每次HTTP请求时,将以上3个值发送到服务端

4:服务端验证key,判断如果与客户端发送的key一致,则说明用户身份无误

5:服务端每次收到请求时,通过当前时间-客户端time字段得到的差值,可以控制这个key的有效期

矢量图和位图

发表于 2018-05-15 | 分类于 图片

一:概念

  1. 位图[bitmap],也叫点阵图,栅格图像,像素图。简单地说,就是由最小单位由像素构成的图,缩放会失真。构成位图的最小单位是像素,位图就是由像素阵列的排列来实现其显示效果的,每个像素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个像素,我们可以改变图像的色相,饱和度,明度,从而改变图像显示效果。

    举个例子来说,位图图像就好比在巨大的沙盘上画好的画,当你从远处看的时候,画面细腻多彩,但是当你靠的非常近的时候,你就能看到组成画面的每粒沙子以及每个沙粒单纯的不可变化颜色。

  2. 矢量图[vector],也叫向量图,简单说,就是缩放不失真的图像格式。最传统的矢量图svg实际上是一种xml格式,是通过路径 和 填充颜色 来描述渲染的,由于路径在缩放情况下不失真,因此矢量图具有不失真特性。

    矢量图是通过多个对象的组合生成的,对其中的每一个对象的纪录方式,都是以数学函数来实现的,也就是说,矢量图实际上并不是象位图那样纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法,当你打开一付矢量图的时候,软件对图形象对应的函数进行运算,将运算结果[图形的形状和颜色]显示给你看。无论显示画面是大还是小,画面上的对象对应的算法是不变的,所以,即使对画面进行倍数相当大的缩放,其显示效果仍然相同[不失真]

    位图:图像。

    矢量图:图形。

    位图是对现实世界的采样,位图的分辨率代表采样的精细程度。
    矢量图里面存储的是一些表达式,如果想看到图像,一般来说需要用程序根据表达式生成位图。

  3. 优缺点:

    矢量图

    优点:不失真。

    ​

    位图

    优点:表现力强(位图精确控制像素,这一点矢量图无法和位图相比)

    ​

BFC

发表于 2018-05-04 | 分类于 css

定义:BFC: 块级格式化上下文,他决定了块级元素如何对他的内容进行布局,以及与其他元素关系及相对关系。BFC里面的元素与外面的元素不会发生影响。

1⃣️触发BFC的方式(一下任意一条就可以)

​ 1.float的值不为none

​ 2.overflow的值不为visible

​ 3.display的值为table-cell、tabble-caption和inline-block之一

​ 4.position的值不为static或者releative中的任何一个(即为absolute或fixed)

2⃣️BFC布局与普通文档流布局的区别:

普通文档流布局规则:

​ A.浮动元素不会被父元素计算高度。

​ B.浮动元素会覆盖非浮动元素的位置。

​ C.margin会传递给父级。

​ D.相邻元素margin会重叠。

BFC布局规则:

​ A.浮动元素会被父级元素计算高度。(父级触发BFC)

​ B.浮动元素不会覆盖非浮动元素位置。(非浮动元素触发BFC)

​ C.margin 不会传递给父级。(父级触发BFC)

​ D.相邻元素margin不会重叠(给其中一个元素增加一个父级,让他的父级触发BFC)

由domContentLoaded和load事件讲浏览器渲染

发表于 2018-04-23 | 分类于 前端开发

一 domContentLoaded和load事件

1…3456

yangXia

56 日志
14 分类
11 标签
© 2020 yangXia
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4