notes

杂记

white-space表现形式

white-space👇 空格( ) 非断空格( ) 换行符(\n)
normal 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行
pre 保留全部的空格 全部非断空格都保留 会换行,但当文本溢出容器时不会自动换行
pre-line 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 会换行
pre-wrap 保留全部的空格 全部非断空格都保留 会换行
no-wrap 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行

查看更多

分享到

Grid

css-grid(手机自带浏览器和webview,以及非IE的主流桌面浏览器)

demo

网格

⚠👇 以下显式网格、隐式网格暂不考虑溢出的情况

显式网格

  • grid-template-columns:指定显式网格列的数量和列宽
  • gird-template-rows:指定显式网格行的数量和行高

grid-template-columns和gird-template-rows所构成的网格为显式网格,其它额外创建的网格为隐式网格

  1. 网格容器宽度固定时
    1. 网格宽度为固定值,该列的列宽等于这个固定值
    2. 网格宽度为nfr时,该列的列宽为容器宽度-固定网格宽度后被列等分后的n份
  2. 网格容器高度固定时,
    1. 网格高度为固定值,该行的行高等于这个固定值
    2. 网格高度为nfr时,网格的行高为容器高度-固定网格高度后被行等分后的n份
  3. 网格容器未指定高度时,显式网格内的网格高度由同行内容高度最高的网格决定
  4. 网格容器未指定宽度且不超过父级元素宽度时,所有网格宽度由所有网格中内容宽度最大的网格决定

隐式网格

  • grid-auto-columns:指定隐式网格的列宽

  • grid-auto-rows:指定隐式网格的行高

  1. 容器大小固定时,网格大小按网格容器等分计算,同显式网格的1和2
  2. 容器大小不固定时,同显式网格的3和4

显示/隐式混合同时存在于网格容器中

若网格容器内同时存在隐式网格和显式网格:

  1. 容器大小固定时,所有网格的宽度计算同显式网格的1,隐式网格高度计算同显式网格的3,显式网格的高度计算是在网格容器高度减去隐式网格占用高度后剩余高度的基础上,进行同显式网格的2
  2. 容器大小不固定时,同显式网格的3和4

fr

fr单位表示网格布局前,扣除明确的使用空间(如:500px)后,剩余可用空间被等分后的一份

1
2
3
.grid{
grid-template-columns: 500px 1fr 2fr; // 计算方式 => (width - 500px)/(1fr + 2fr)
}

minmax

网格行高或列宽的大小限定区间

参考

分享到

css选择器和style属性的权值计算和比较

16. Calculating a selector’s specificity

6.4.3 Calculating a selector’s specificity

误区

在学习过程中,你可能发现给选择器加权值的说法,即 ID 选择器权值为 100,类选择器权值为 10,标签选择器权值为 1,当一个选择器由多个 ID 选择器、类选择器或标签选择器组成时,则将所有权值相加,然后再比较权值。这种说法其实是有问题的。比如一个由 11 个类选择器组成的选择器和一个由 1 个 ID 选择器组成的选择器指向同一个标签,按理说 110 > 100,应该应用前者的样式,然而事实是应用后者的样式。错误的原因是:权重的进制是并不是十进制,CSS 权重进制在 IE6 为 256,后来扩大到了 65536,现代浏览器则采用更大的数量。。还是拿刚刚的例子说明。11 个类选择器组成的选择器的总权值为 110,但因为 11 个均为类选择器,所以其实总权值最多不能超过 100, 你可以理解为 99.99,所以最终应用后者样式。

查看更多

分享到

border-image-slice

border-image-slice将元素区域(border-box)分割成9个区域,其中8个区域分别对应构成border的8个区域。
每个由borderr-image-slice划分的区域内的图像将被渲染到对应的border区域中。

如下图:
示意图

假如border-image-slice的值为40 60,表示垂直距离为40px,水平距离为60px,区域1的大小为60x40(长*宽),内中渲染对应区域的图像。

假如border的值为20,表示border-top-width为20px,border-left-width为20px,区域1的大小为20x20(长*宽),内中渲染的图像为border-image-slice划分区域中的区域1内的图像。

分享到

css主题化

css主题化(use Less)

  1. 使用css var(全局变量)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 当给定值未定义时将会用备用值替换
    @font-size:var(--theme-font-size,36px);

    :root{

    }
    /*
    https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties#%E6%9C%89%E6%95%88%E6%80%A7%E5%92%8C%E5%80%BC
    计算时有效性
    1. 检查属性 color 是否为继承属性。是,但是 <p> 没有任何父元素定义了 color 属性。转到下一步。
    2. 将该值设置为它的默认初始值,比如 black。
    */
    body{
    font-size:20PX;
    }
    #app{
    font-size:@font-size !important;
    }

    脚本改变css变量的值

    1
    2
    3
    function changeTheme(n){
    document.documentElement.style.setProperty('--theme-font-size',n+'px')
    }

    查看更多

分享到

glob_match

glob match总结(bash下)

  • *匹配任意数量的非/字符
  • ? 仅匹配一个非/字符
  • **匹配任意数量的非/字符,而且当它独立构成匹配模式中的一段时,也可以匹配/,在bash下要开启globstar,shopt -s globstar
  • [...]匹配[]之间的字符,存在范围匹配时,需要在bash下需开启globasciiranges,保证大小写匹配的正常;[^…] 或 [!…]匹配不包括在[]内的字符;可以通过[:class:]使用POSIX标准的匹配关键词:alnumalphaasciiblankcntrldigitgraphlowerprintpunctspaceupperwordxdigitword 匹配字符、数字和_
  • 多模式匹配及反选
    • ?(pattern|pattern|…):匹配0个或1个pattern
    • *(pattern|pattern|…):匹配0个或以上的pattern
    • +(pattern|pattern|…):匹配1个或以上pattern
    • @(pattern|pattern|…):匹配其中1个pattern,测试效果和+(pattern|pattern|...)相似(需进一步研究区别
    • !(pattern|pattern|…):不匹配任一pattern

参考:man bash下的Pattern Matching部分

分享到

原码、反码、补码

原码、反码、补码

原码

1
2
3
1   => 00000001
1 => 00000010
-1 => 10000001

当一个正数与一个负数的原码做加法时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 + (-1)
00000001
+ 10000001
-----------
10000010

10000010 => -2 // 按原码的方式转化为十进制

2 + (-1)
00000010
+ 10000001
-----------
10000011

10000011 => -3 // 按原码的方式转化为十进制

异号数字的原码相加,原码的符号位一定为1,和实际情况不服,比如: 2 + -1。

查看更多

分享到

es6-es11

ECMAScript版本

编年版 简版 描述
ECMAScript2015 ECMAScript6 -
ECMAScript2016 ECMAScript7 -
ECMAScript2017 ECMAScript8 -
ECMAScript2018 ECMAScript9 -
ECMAScript2019 ECMAScript10 -
ECMAScript2020 ECMAScript11 -
ECMAScript2021 ECMAScript12 -

ECMAScript2016

  • Array.prototype.includes
  • **替代Math.pow实现指数的计算

ECMAScript2017

  • Object.values()
  • Object.entries()
  • String.prototype.padStart
  • String.prototype.padEnd
    (⚠️ padStart and padEnd on Emojis and other double-byte chars)
  • Object.getOwnPropertyDescriptors(参考)
  • 允许在函数入参的末尾添加,,比如function a(a1,a2,){...}
  • Async/Await

ECMAScript2018

  • js引擎主进程和worker进程间可使用SharedArrayBuffer进行数据共享,并通过Atomics来管理共享内容数据

  • 允许模板字符串和函数组合使用

    1
    2
    3
    4
    5
    function fn(staticStr,...dynamicParams){
    console.log(staticStr)// ['static1 ',' static2 ','']
    console.log(dynamicParams)// [1,2]
    }
    fn`static1 ${1} static2 ${2}`
  • 正则表达式中.可以在s模式下匹配所有字符(包括\n\r\f等)

    1
    2
    3
    4
    5
    6
    //Before
    /first.second/.test('first\nsecond'); //false

    //ECMAScript 2018
    /first.second/s.test('first\nsecond'); //true
    // Notice: /s
  • 正则表达式中可以对匹配组合进行命名,格式为:(?<name>...)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    (/(?<cname>abc)/.exec('testabc'))
    /*
    输出:
    [
    'abc',
    'abc',
    groups:{ cname: 'abc' },
    index: 4,
    input: 'testabc'
    ]
    */

    // \k使用之前组合名称匹配到的内容
    let sameWords=/(?<fruit>apple|orange)===\k<fruit>/
    sameWords.test('apple===apple') // true
    sameWords.test('apple===orange') // false

    // 在replace函数中使用组合命名
    let replaceReg=/(?<firstName>[a-zA-z]+) (?<secondName>[a-zA-z]+)/
    'abc def'.replace(replaceReg,'$<secondName> $<firstName>') // 'def abc'
  • 在解构中用...varName的方式提取对象中未被指定的属性,重组为一个新的对象,记为varName

  • 对象可使用...进行扩展

  • 正则表达式可以使用向前查找,查找时消耗查询字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // ['winning',groups:undefined,index:1,input:'#winning']
    (/(?<=#).*/.exec('#winning'))

    // 反向
    // ['inning',groups:undefined,index:2,input:'#winning']
    (/(?<!#)[a-z]+/.exec('#winning'))
    // ['',groups:undefined,index:0,input:'#winning']
    (/(?<!#)[a-z]*/.exec('#winning'))

    // PS: (?<!...)会去匹配行首空字符,当匹配到第一个符合匹配规则的,就直接输出
    // 比如:`/(?<!#)[a-z]*/.exec('#winning')`中,(?<!#)匹配的是行首空字符,[a-z]*匹配的是空字符,因为规则无法匹配首字符`#`
  • 在正则表达式中添加ScriptScript_Extensions来扩展Unicode的匹配范围

    1
    2
    3
    4
    5
    6
    7
    //The following matches a single Greek character
    /\p{Script_Extensions=Greek}/u.test('π'); // true
    // 等价于
    /\p{Script=Greek}/u.test('π'); // true

    // 反向
    /\P{Script=Greek}/u.test('π'); // false
  • Promise.prototype.finally()

  • for-of中使用await,在for循环中,上一个promise resolve后,才会执行下一个promise

    1
    2
    3
    4
    5
    6
    7
    const promises=[...] // Promise数组
    async function a(){
    for await (const p of promises){
    console.log(p)
    }
    }
    a()

ECMAScript2019

  • String.prototype.trimStart()

  • String.prototype.trimEnd()

  • Object.fromEntries()

  • Array.prototype.flat()

  • Array.prototype.flatMap()

  • try-catch中catch的参数改为可选

  • Symbol.prototype.description输出Symbol类型变量的描述

  • Array.prototype.sort(),在原来的基础上使用稳定的排序算法,尤其在对象数组中,会根据给定的键之间比较进行排序,同键值的多个对象的顺序与这些对象在原数组中的顺序一致

  • Function.prototype.toString(),将返回函数的注释、空格和语法详细信息

  • JSON成为ECMAScript的完全子集,可解析之前解析不了的JSON字符串:行分隔符(\u2028)和段分隔符(\u2029

  • JSON.stringify()输出进行了优化,原来显示未知的字符,现在在字符代码之前插入\字符后仍能保持可读性

    1
    2
    3
    4
    5
    // before
    JSON.stringify('\uD83D');// '"�"'

    // after
    JSON.stringify('\uD83D');// '"\\ud83d"'

    ECMAScript2020

  • 在变量名前加#,来定义类私有变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class a{
    #p='private'
    print(){
    console.log(this.#p)
    }
    }

    var aa=new a

    // 正常
    aa.print() // 'private

    // 异常
    // Uncaught SyntaxError: Private field '#p' must be declared in an enclosing class
    console.log(aa.#p)
  • ?.可选链运算符,符号左侧的值为undefinednull时,运算表达式返回undefined,否则继续执行后续的操作

    1
    2
    3
    4
    5
    // 形式:
    obj?.prop
    obj?.[expr]
    arr?.[index]
    func?.(args)
  • ??空值合并运算符,运算符左侧为undefinednull时,取符号右侧的值,否则取左侧的值

  • 动态引入,import('some.js').then(...)

  • Promise.allSettled,处理多个Promise对象的集合,无论这些Promise对象的状态怎么变化,都会被记录下来,并在所有Promise状态发生变化后,执行then回调,入参为每个Promise状态的集合

  • globalThis

  • BigInt,通过在数字后面添加n来指定数的类型为BigInt,此类型用于处理大于(2^53 - 1)的整数,不可与普通number类型的数据进行操作,但可比较大小

    1
    11n+11 // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
  • String.prototype.matchAll,返回的是一个可迭代对象,可以通过for-of访问每一个匹配组的结果(包含使用括号定义的捕获组信息)

ECMAScript2021

  • String.prototype.replaceAll

  • Promise.any

    1
    2
    3
    4
    5
    6
    7
    8
    Promise.any(promises).then(
    (first) => {
    // 任何一个 Promise 完成了
    },
    (error) => {
    // 所有的 Promise 都拒绝了
    }
    )
  • 数字分隔符_,let x=2_3333等价于let x=23333

  • Intl.ListFormat,国际化格式化

分享到

es6相关

super在babel中的实现

1
2
3
4
5
6
7
8
9
10
11
class a {
constructor(){
this.a=1
}
}

class b extends a {
constructor(){
super()
}
}

编译后

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
// ...some helper function
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct()
return function _createSuperInternal() {
// 在调用前,_inherits函数中已将父类函数挂到子类函数的原型链上,即b.__proto__===a
// 所以这个的Super就是b.__proto__,即父类a
var Super = _getPrototypeOf(Derived), result
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor
result = Reflect.construct(Super, arguments, NewTarget)
} else {
result = Super.apply(this, arguments)
} return _possibleConstructorReturn(this, result)
}
}

var a = function a() {
_classCallCheck(this, a);

this.a = 1;
};

var b = /*#__PURE__*/function (_a) {
// b的prototype的__proto__指向a的prototype,属性调用时:检查b实例的属性=>检查b的prototype的属性=>检查a的prototype的属性
// 其中会调用_setPrototypeOf函数,将a挂到b的原型链上,b.__proto__===a
_inherits(b, _a);

// 创建super函数,内部存储b为Derived参数
var _super = _createSuper(b);

function b() {
_classCallCheck(this, b);

return _super.call(this);
}

return b;
}(a);

迭代器规范

参考
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols

可迭代协议

要成为可迭代对象,一个对象(或原型链上)必须实现@@iterator方法,当一个对象需要被迭代的时候(比如被置入一个 for…of 循环时),首先,会不带参数调用它的@@iterator方法,然后使用此方法返回的迭代器获得要迭代的值。

内置类型拥有默认的迭代器行为:

  • Array.prototype[@@iterator]()
  • TypedArray.prototype[@@iterator]()
  • String.prototype[@@iterator]()
  • Map.prototype[@@iterator]()
  • Set.prototype[@@iterator]()

可迭代对象特征

  1. 此对象实现了可迭代协议——拥有@@iterator函数(即typeof fn[Symbol.iterator]==='function'),且@@iterator函数执行后返回一个迭代器对象。
  2. @@iterator函数的this默认指向该可迭代对象

迭代器协议

迭代器对象特征

  1. 此对象实现了迭代器协议——拥有next函数,且执行next函数后返回{done:Boolean,value:any}这样的对象(可在其上加另外的对象属性)
  2. for-ofdonetrue时,value属性的值会被忽略
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
const obj2={
[Symbol.iterator](){
let i=-1
const txt=[
'this will output in both case',
'just output in call `next` by hand'
]
return {
next(){
i++
return {done:i===txt.length-1,value:txt[}
}
}
}
}
const obj1={
[Symbol.iterator]:function* (){
yield 'this will output in both case'
return 'just output in call `next` by hand'
}
}
console.log('obj1 output')
var g=obj1[Symbol.iterator]()
console.log(g.next().value)
console.log(g.next().value)
console.log('----')
for(let i of obj1){
console.log(i)
}
console.log('obj2 output')
var g=obj2[Symbol.iterator]()
console.log(g.next().value)
console.log(g.next().value)
console.log('----')
for(let i of obj2){
console.log(i)
}
// => 输出结果:

// obj1 output
// this will output in both case
// just output in call `next` by hand
// ----
// this will output in both case

// obj2 output
// this will output in both case
// just output in call `next` by hand
// ----
// this will output in both case
分享到

元素resize

监听元素尺寸的变化

传统的,通过resize来监听尺寸大的变化(以高度变化为例)

由于resize事件仅在某些元素和window上触发,对于其他元素需要借助这些元素来监听尺寸的变化。

首先保持iframe的高度与变化元素的高度一致,当元素的高度变化时,iframe内的窗口大小也变化,会触发iframe内window的resize事件,以此来监听元素尺寸的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<button>change div's height</button>
<div class="size-div" style="height:100px;">
<iframe></iframe>
the div can change size
</div>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
document.querySelector("button").addEventListener("click", function () {
var target = document.querySelector(".size-div");
var rand = Math.floor(Math.random() * 200 + 50);
target.setAttribute("style", "height:" + rand + "px;border:1px solid;");
});
document
.querySelector("iframe")
.contentWindow.addEventListener("resize", function () {
console.log("div changed size");
});
1
2
3
4
5
6
7
8
9
10
11
12
.size-div {
border: 1px solid;
position: relative;
}
iframe {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0;
border:0;
}

查看更多

分享到

移动端兼容性问题记录

  1. 仅在android版钉钉中嵌入的网页里,全屏api的调用会失效,所以点击视频无法默认将video元素全屏来进行播放

  2. 仅在ios版钉钉中嵌入的网页里,上传文件时,xhr.upload.onprogress不会触发

  3. video元素在ios上默认是调用原生的播放器全屏播放,而在android上则默认是在video元素中内联播放

  4. ios上为了让video能内联播放,需要在video元素上设置playsinline(ios10及之后系统版本的safari中有效)和webkit-playsinline(ios10之前的safari中有效)属性

  5. 隐藏系统默认的视频播放按钮

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    *::-webkit-media-controls-start-playback-button {
    display: none;
    -webkit-appearance: none;
    }
    *::-webkit-media-controls-panel {
    display: none;
    -webkit-appearance: none;
    }
    *::--webkit-media-controls-play-button {
    display: none;
    -webkit-appearance: none;
    }
  6. 钉钉中要改变网页标题,需要调用钉钉的api,引用dingtalk-jsapi/api/biz/navigation/setTitle

  7. 在ios下的网页中,input输入框内的内容溢出输入框的宽度时,无法通过移动光标来查看超出部分的内容,不管这个输入框是否只读。因为在移动光标时,输入框中显示的内容不会随着改变

分享到

基于docker搭建gitlab-runner

基于docker搭建gitlab-runner

  1. 下载gitlab镜像

    1
    docker pull gitlab/gitlab-runner
  2. 注册runner

    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
    # ps:在注册过程中直接以人机交互的方式一步一步进行注册的话,可能会出现ssl验证不通过的问题,所以在这种情况下直接用命令行参数进行注册

    # --rm 临时创建一个容器,用来运行gitlab/gitlab-runner镜像
    sudo docker run --rm \
    # 挂载宿主的文件目录到容器内,用以存储注册之后生成的config.toml文件
    # 当以root用户运行gitlab-runner时,/etc/gitlab-runner为默认存储配置文件的地方
    -v /home/dcxx/gitlab-runner:/etc/gitlab-runner \
    # 指定镜像
    gitlab/gitlab-runner \
    # 执行gitlab-runner register命令
    register \
    # 不需要交互式
    --non-interactive \
    # gitlab web服务的https的证书
    --tls-ca-file '/etc/gitlab-runner/git.dcyun.com.crt' \
    # gitlab web服务地址
    --url "https://git.dcyun.com/" \
    # gitlab网站上添加runner时提供的token
    --registration-token "T3V4vNvQrAMZDyNVFrg7" \
    # 指定在什么环节跑构建脚本
    --executor "docker" \
    # 指定跑构建脚本时的docker环境,即镜像
    --docker-image ubuntu:latest \
    # runner的名称,主要显示在job详情中pipeline选择runner时的下拉项中
    --name '前端自动部署' \
    # 运行没有配置tag的任务执行
    --run-untagged="true" \
    # runner的tag
    --tag-list 'docker,font-end,common'

查看更多

分享到

docker环境配置

宿主机配置

  1. docker-ce 国内镜像下载

  2. 设置Ubuntu软件源为清华镜像

  3. 设置docker hub国内镜像

    1
    2
    3
    4
    5
    6
    7
    // /etc/docker/daemon.json
    {
    "registry-mirrors" : [
    // 网易
    "https://hub-mirror.c.163.com",
    ]
    }

    或者使用阿里的镜像

分享到

CSRF

CSRF

防范措施:

  • 检测请求头中的Referer字段,判断请求的来源是否是来自合法(意料之中)的网站
  • 在token无法被他人窃取的情况下,客户端和服务端对同一用户在两端都保留一个相同的token,通过检测token是否一致来确定是否为同一用户
    • token不可以放在cookie中,防止在访问服务端时浏览器自动给请求带上cookie(浏览器cookie设置的行为)
    • JWT(含有少量信息的token,在一定程度上后端代码可以减少与数据库的交互)
    • 将token放在页面的url上有时是不安全的,因为跳转后的页面也可能会获取到上一个页面的url内容
分享到

typescript学习记录

typescript配置相关的

在vscode中,通过配置typescript,让vscode可以识别css-module,进而提供css-module相关的代码补全

tsconfig.json中配置typescript-plugin-css-modules插件

vscode中workaround的setting配置为"typescript.tsdk": "node_modules\\typescript\\lib"来使用工作区下的node_modules中的typescript来加载插件。reference

eslint中行末结束符无法自动修复

Expected linebreaks to be 'LF' but found 'CRLF'.eslint(linebreak-style)
在eslint可识别但由于bug暂不可自动修复

暂时使用EditorConfig这个vscode插件来解决行末IFCRLF的问题


typescript规定相关的

ts中对对象字面量做参数过多检测的情况,以如何绕过

ts中对对象字面量做检测的时候,在特定情况下(字面量直接赋值给其它变量、字面量直接作为函数入参)会额外做参数过多(excess property checking)的检测

However, TypeScript takes the stance that there’s probably a bug in this code. Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments. If an object literal has any properties that the “target type” doesn’t have, you’ll get an error:

绕过excess property checking的方法

1
2
3
1. use a type assertion(类型断言)
2. add a string index signature(定义string型的索引签名)
3. One final way to get around these checks, which might be a bitsurprising, is to assign the object to another variable

ts文件中类型的申明作用域如何决定

ts根据该文件是否存在import或者export来决定当前文件是个模块还是运行在全局环境下,以此来推断类型申明是否存在于全局中

参考:

  1. https://stackoverflow.com/questions/40900791/cannot-redeclare-block-scoped-variable-in-unrelated-files
  2. github-issus
分享到

react-hooks

React Hooks

hooks的调用顺序

Basic Hooks

  • useState
  • useEffect
  • useContext

Additional Hooks

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue
分享到

webApi

IntersectionObserver

定义的视口和目标元素之间相交的关系

1
var observer = new IntersectionObserver(callback[, options])

当目标元素在定义的视口中的可见百分比超过阈值时,callback会被调用,入参有两个:

  1. entries:由IntersectionObserverEntry对象构成的数组,仅包含触发阈值的目标元素的IntersectionObserverEntry对象,每个IntersectionObserverEntry对象对应一个目标元素,顺序和目标元素被监听的先后一致
  2. observer:当前的IntersectionObserver实例

options包含3个属性:

  1. root:定义视口元素,当options未指定或者root为null时,默认未document

  2. rootMargin:值的语法与css的margin一样,正值为在root视口的边缘向外扩展,即视口变大,反之则变小

  3. threshold:值为数组,数组中的每个元素的取值范围为[0,1],当目标元素在视口中的可见百分比越过数组中某个元素的值时,则callback会被调用。比如:

原来目标元素可见百分比 滚动之后目标元素可见百分比 数组中的某一阈值 结果
0.45 0.51 0.5 callback被触发
0.51 0.45 0.5 callback被触发

observer实例对象上含有4个函数:

  1. disconnect:停止此observer对象中监听的所有目标元素
  2. observer:用于指定observer需要监听的目标元素,入参为1个目标元素,且为root的子孙元素
  3. takeRecords:获取observer内部保存的由IntersectionObserverEntry对象构成的数组
  4. unobserver:用于停止监听某一目标元素,入参为1个对应需要停止监听的目标元素,

附:IntersectionObserverEntry

分享到

cookie总结

Cookie知识点记录

参考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies

  • Expires:cookie 的最长有效时间,形式为符合 HTTP-date 规范的时间戳,比如:Date: Wed, 21 Oct 2015 07:28:00 GMT,如果没有设置这个属性,那么表示这是一个会话期 cookie 。一个会话结束于客户端被关闭时,这意味着会话期 cookie 在彼时会被移除。(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Session_cookies)
  • Max-Age:在 cookie 失效之前需要经过的秒数。一位或多位非零(1-9)数字。假如二者 (指 Expires 和Max-Age) 均存在,那么 Max-Age 优先级更高
  • Domain:指定 cookie 可以送达的目标。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。后端在设置set-Cookie时,不指定Domain,则默认是当前接口地址的主域名+port或者ip+port
  • Path:指定一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送此Cookie。
  • Secure:一个带有安全属性的 cookie 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器
  • HttpOnly:设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)。
  • SameSite(实验属性):允许服务器设定一则 cookie 不随着跨域请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)
分享到

mvc与mvp与mvvm

MVC

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
// view
function View(controller) {
var target = $('#some-element')[0]
this.render = function (model) {
target.innerText = model.getValue()
}
target.addEventListener('click', controller.click)
}
// model
function Model() {
var _value = 0
var registerViews = []
this.register = function (viewInstance) {
registerViews.push(viewInstance)
}
this.notify = function () {
var self = this
registerViews.forEach(function (viewInstance) {
viewInstance.render(self)
})
}
this.add = function () {
_value += 1
}
this.getValue = function () {
return _value
}
}
// controller
function Controller() {
var model, view
this.init = function () {
model = new Model()
view = new View(this)
model.register(view)
model.notify()
setTimeout(function () {
view.render({ getValue: function () { return 1000 } })
}, 1000)
}
this.click = function () {
model.add()
model.notify()
}
}
// 调用
var controller = new Controller()
controller.init()

总结

  1. mvc模式中model层可以直接通知view层重新渲染视图
  2. 仅由controller层发起model层的修改
  3. 代码的执行入口是controller
  4. controller内部维护着model层和view层之间的关系——同步视图和数据
  5. controller可以通过model层使view层重新渲染,也可以直接使view层重新渲染

查看更多

分享到

基本排序法概览

本文所有测试及排序算法

公用测试部分

1
2
3
4
5
6
7
function createTestData() {
var testData = []
for (var i = 0; i < 10; i++) {
testData.push(Math.floor(Math.random() * 100))
}
return testData
}

冒泡排序

可以先找大的,也可以先找小的

  • 含有两层循环,外层循环决定冒泡排序的结束位置索引,内层循环决定当前这一项冒泡排序的开始位置索引
  • 每次内层循环,仅比较当前项和下一项的大小,如果当前项较大,则将两项位置互换。
  • 保证每次内层循环结束后,内层循环的数列的末尾值是该数列的最大值
1
2
3
4
5
6
7
8
9
10
11
12
13
function sort(data = []) {
data = Object.assign([], data)
for (sp = data.length - 2; sp >= 1; sp--) {
for (mp = 0; mp <= sp; mp++) {
if (data[mp] > data[mp + 1]) {
var temp = data[mp]
data[mp] = data[mp + 1]
data[mp + 1] = temp
}
}
}
return data
}

选择排序

可以先找大的,也可以先找小的

  • 含有两层循环,外层循环决定选择排序当前比较数列的开始索引,同时假定当前开始索引中存储的值最小,内层循环决定选择排序仍然需要比较的数列范围
  • 当需要比较的数列范围内有比当前假定的最小值小的值时,将两个值的位置互换
  • 这种算法保证每次内层循环后都找到当前内层循环队列中的最小值,并且处于当前数列的头部
  • 这是一种循环找极值的排序算法
1
2
3
4
5
6
7
8
9
10
11
12
13
function sort(data=[]) {
data = Object.assign([], data)
for (var sp = 0; sp < data.length - 1; sp++) {
for (var mp = sp + 1; mp < data.length; mp++) {
if (data[mp] < data[sp]) {
var temp = data[sp]
data[sp] = data[mp]
data[mp] = temp
}
}
}
return data
}

插入排序

可以先找大的,也可以先找小的

  • 从第二项开始遍历,从当前项往前检查之前的项是否比当前项大,如果大则两项位置互换,然后重复此过程,直到检查完所有之前的项或者碰到小于当前项的项,即在之前的项中找到适合当前项的位置,并将项插入到这个合适的位置上
  • 由于每一次项插入后的数列都是有序的,保证了当前项之前的项一定是有序的
1
2
3
4
5
6
7
8
9
10
11
12
13
function sort(data = []) {
data = Object.assign([], data)
for (let i = 1; i < data.length; i++) {
const temp = data[i]
let j = i
while (j > 0 && data[j - 1] > temp) {
data[j] = data[j - 1]
j -= 1
}
data[j] = temp
}
return data
}
分享到

位置属性总结

位置属性总结

window

无offsetParent属性

  • innerHeight
    浏览器窗口的视口(viewport)高度(以像素为单位),如果存在水平滚动条,则包括它
  • outerHeight
    Window.outerHeight 获取整个浏览器窗口的高度(单位:像素),包括侧边栏(如果存在)、窗口镶边(window chrome)和窗口调正边框(window resizing borders/handles)

inner

  • innerWidth
    浏览器视口(viewport)宽度(单位:像素),如果存在垂直滚动条则包括它。
  • outerWidth
    Window.outerWidth 获取浏览器窗口外部的宽度。表示整个浏览器窗口的宽度,包括侧边栏(如果存在)、窗口镶边(window chrome)和调正窗口大小的边框(window resizing borders/handles)
  • screen
    • availHeight
      屏幕中非固定占用空间^固定占用空间的高度
    • availLeft
      屏幕中非固定占用空间的左边离显示器左边的距离
    • availTop
      屏幕中非固定占用空间的顶部离显示器顶部的距离
    • availWidth
      屏幕中非固定占用空间的宽度
    • height
      显示器的纵向分辨率
    • width
      显示器的横向分辨率
  • screenLeft
    同screen.availLeft
  • screenTop
    同screen.availTop
  • screenX
    返回浏览器左边界到操作系统桌面左边界的水平距离
  • screenY
    返回浏览器顶部距离系统桌面顶部的垂直距离
  • scrollX
    返回文档/页面水平方向滚动的像素值
  • scrollY
    返回文档在垂直方向已滚动的像素值

查看更多

分享到

页面缩放、逻辑像素与物理像素转化

页面缩放

<meta name=”viewport” content=”width=device-width, initial-scale=scale“/>

物理屏:真正展现图像的屏幕,此屏上的像素称为物理像素
逻辑屏:css层面上布局的屏幕,此屏上的像素称为逻辑像素

以上的scale指的就是页面的缩放倍数,符合公式:

元素在物理屏上布局的宽度 = 元素在逻辑屏上布局的宽度 * scale * devicePixelRatio

逻辑像素 * devicePixelRatio = 物理像素(即实际用户看到的画面)

所以scale的计算公式:
scale = 元素在物理屏上布局的宽度 / (元素在逻辑屏上布局的宽度 * devicePixelRatio)

一般来说devicePixelRatio在pc上为1,手机端上常见的是2和3

分享到

浮点数的表示与计算

[TOC]

浮点数的表示与计算

IEEE754中规定了64位浮点数的格式:

  • 符号位(1bit),取值范围:[0,1]
  • 指数位(11bit),取值范围:[1,2046]
  • 尾数位(52bit),取值范围:[0,2^53-1]

内存中的存储格式

分享到

git-flow-introduction

Git 开发流程

开始一个新功能的开发 (Work on a Feature)

1. 建立新的分支

$ git checkout -b feature/-

例如: feature/chb-add-wishlist 如果是bugfix, branch命名以bugfix开始 例如: bugfix/chb-some-user-cannot-login

2. 开发功能并测试

3. 提交修改

$ git push origin

4.在github上创建pull request, 找人做代码审核(可跳过)

特别提醒:创建的pull request应是功能分支到develop分支

5. 审核(或自审)通过后,清理提交记录

1
2
3
4
5
git checkout develop 
git pull origin develop
git checkout <feature branch name>
git rebase -i develop
git push origin <feature branch name> -f

目标:将所有的commits整理成一个或者几个比较重要的commit,并确保新的代码会出现在develop分支现有代码之后,以保持提交历史的整洁性

在这一步可能会出现conflicts, 需要人工解决

注:前三步操作可以用git alias来简化, 需要在~/.gitconfig配置一下git的alias

1
2
$ ~/.gitconfig [alias]   sync = "!f() { echo Syncing $1 with develop && git checkout develop && git pull origin develop && git checkout $1 && git rebase -i develop; }; f"
$ git sync <feature branch name>

6. 在github上merge pull request并删除分支

7. 终结这个功能分支

$ git checkout develop && git pull origin develop && git branch -d

分享到

使用过的工具和库

工具

名称/地址 描述 版本 minzipped
sortable 前端拖拽功能组件 1.9.0 9.3KB
yapi 提供mock服务的系统
handsontable 类Excel sheet视图工具 7.0.3 310.5KB
scrollbooster 点击拖拽表格左右滑动,可以优化window下横向滚动条的用户体验 2.7KB
eruda 在手机端模拟browser的devtool进行调试 1.5.5 123.8KB
docute 通过markdown生成单页应用
infinite-scroll jQuery无限滚动 3.0.6 5.2KB
Puppeteer chrome出品的headless Node Api
granim 背景渐变插件 2.0.0 4.7KB
nanoid 唯一id生成器 2.0.3 286B
superstruct 简易可自定义数据类型验证器 0.6.1 3.2KB
draggable 拖拽库 1.0.0-beta.8 11.8KB
dragula 拖拽库 3.7.2 3.7KB
webpackmonitor webpack打包后的资源查看器
webpack-bundle-analyzer webpack打包后的资源查看器
lozad 图片和iframe懒加载 1.9.0 972B
workbox Chrome出品的PWA库 4.3.1 802B
commitizen git commit 格式化message
conf 在本地磁盘中创建配置文件
Numeral-js 对数字进行格式化及简单的加减操作 2.0.6 3.8KB
hammer.js 手势库 2.0.8 6.9KB
anime 动画库 3.0.1 6.8KB
marked markdown转码器 0.6.3 7.7kB
分享到

webpack针对循环引用以及赋值export的处理注意事项

webpack打包相互引用的模块

示例项目

(github)

遇到的问题

在有2个或2个以上的文件之间的相互依赖关系构成闭环的时候,有时会出现Can't read Property 'xxx' of undefined或者(0,xxx) is not a function这类的错误,比如:

1
示例项目中的src/index.js引用src/a.js,而src/a.js中也引用了src/index.js

为什么会出现这样的问题

查看更多

分享到

react中Context的传递

ccontext的传递方向是由定义了根数据源的组件向子组件传递,父级

  • 通过getChildContext(Class)在context上定义要传递给子级的数据
  • 通过组件(Class)的childContextTypes定义传递给子级数据的数据类型

上上级组件传递的context数据可以被上级组件同key的context数据所替换,比如:

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
// App
import React from "react";
import PropTypes from "prop-types";
import Parent from "./Parent";
import Child from "./Child";

class App extends React.Component {
getChildContext() {
return {
from: "this is from App"
};
}

render() {
console.log("App_Context-->", this.context);
return (
<div className="container">
<Parent>
<Child />
</Parent>
</div>
);
}
}

App.childContextTypes = {
from: PropTypes.string
};

App.contextTypes = {
from: PropTypes.string,
};

export default App

查看更多

分享到