0%

template

vue和angular都鼓励开发者编写尽可能与常规Html相似的template,react却并不是,react则崇尚all in js. 不过从编译角度看,两种方式都是语法糖而已,无论编码风格,编译后都是一个可执行函数

render principle

了解Angular Ivy: Incremental DOM 和 Virtual DOM

ecosystem

react生态庞杂,官方定义了基架,各种具体场景的实现技术在社区百花齐放,各路大神乐于造自己的轮子,库选型劝退选择恐惧症。。。
angular:桶里啥都有。。。

https://www.canva.com/
这个网站的404是一个拼图游戏,纯html img + css以及拖动实现

基础

1
2
3
4
5
6
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

ctx.beginPath()
ctx.arc(75,75,50,0,Math.PI*2,true);//以(75, 75)为圆心50为半径绘制从0~2pi的圆弧,true为顺时针
ctx.stroke() //使用线条绘制图形轮廓 相对的fill是填充图形

canvas元素创造了一块固定的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。
getContext(‘2d’)提供了二维渲染上下文 并没有‘3d’参数,而是‘webgl’或‘webgl2’ 见GetContext MDN

hexo tag cloud

Hexo plugin: hexo-tag-cloud
该项目使用使用胶水语言(如swig),将tagCanvas.js植入Hexo项目
tagCanvas.js 库
在本项目qqsnote中,并按hexo-tag-cloud的README示意的没有集成到sidebar,而是集成到了theme/next/layout/page.swig,并按tagCanvas.js 库文档示意设置了权重显示模式

在屏幕上画一个立体球

看一眼 吖猩的3D旋转球

在球面的经纬焦点处绘制粒子,从球心到球半径R分n层纬度,2n经度,则共4n2+2n个点
假设在每个点处放置半径r的粒子小球,小球在二维屏幕的投影用arc绘制填充即可,投影圆的位置为(x,y),z=(R2-x2-y2)1/2
有意思的来了:球在二维的投影,位置(远近z)反映在投影上是投影圆的半径(也可以进而根据这个比例设置opacity),两个成像物体成像到固定像平面,像高r与物距l的关系是
r’/r = (l-f)/(f-z)
当l=2f时成像与物体等大 即r’=f/(f-z)*r

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
<script type="text/javascript">
// init webgl
const canvas = document.getElementById('wordCloud')
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height);
const vpx = canvas.width/2 // 圆心偏移原点(0,0) 使x,y不为负数
const vpy = canvas.height/2
const R = 300
// z: [-300 300]
f = 600
opacity_min = 0.3
const atoms=[]
// solid ball 经纬
// 粒子
const atom = function(x,y,z,r){
this.x = x;
this.y = y;
this.z = z;
this.r = r;
}
atom.prototype.paint=function(){
ctx.save() //?
ctx.beginPath()
const r_projection = this.r*f/(f-this.z) // projection 投影
ctx.arc(vpx+this.x,vpy+this.y,this.r,0,Math.PI*2,true)
ctx.fillStyle=`rgba(255,255,255,${opacity_min + (R+this.z)/2/R})`
ctx.fill()
ctx.restore()
}
const N = 5
for(let k=-N; k<=N; k++){ // 2N+1个纬度
const R_latitude = Math.pow(R*R - Math.pow(R*k/N,2),0.5)
for(let i = 0; i<2*N; i++){ // 2N条经线
const x = R_latitude*Math.cos(i*Math.PI/N)
const y = R_latitude*Math.sin(i*Math.PI/N)
const z = k/N*R
const a = new atom(x,y,z,1.5)
a.paint()
atoms.push(a)
}
}
</script>

五层纬度球

如上相当于从z轴方向俯视一个球,了解另一种“撒点”方式:
球坐标点(R, θ, φ)
球坐标系
x = R sinθ sinφ
y = R sinθ cosφ
z = R cosθ
θ不取0和180就可以避免出现‘南北极’(上面的算法中极点是一圈粒子重合在一起)
设共N个粒子,i取[0, N)
找一个f(i)取值(-1, 1) 作为cosθ 那就f(i) = 2
(i + 1)/N - 1
找一个g(i)

旋转

绕z轴 转过角度θ

旋转矩阵的推导

实际上绕z旋转在投影屏幕上就是同心圆环绕,并没有粒子的前后(z)不变,也难以用一个二维的变量控制,因此前辈们都使用将鼠标(x, y)的偏移对应并分解到绕x,y轴的旋转之实现
旋转矩阵是相同的

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
let angleX = angleY = 0
function rotateX(angle){
atoms.forEach(atom=>{
const y = atom.y*Math.cos(angle) - atom.z*Math.sin(angle)
const z = atom.y*Math.sin(angle) + atom.z*Math.cos(angle)
atom.y = y
atom.z = z
})
}
function rotateY(angle){
atoms.forEach(atom=>{
const z = atom.z*Math.cos(angle) - atom.x*Math.sin(angle)
const x = atom.z*Math.sin(angle) + atom.x*Math.cos(angle)
atom.z = z
atom.x = x
})
}
function rolling(){ // click事件,点击一下随机绕x或y转十分之一圆周
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(Math.random()>0.5){
rotateX(Math.PI/36)
}else{
rotateY(Math.PI/36)
}
atoms.forEach(atom=>{
atom.paint()
})
}

根据鼠标偏移控制旋转角度
1
2
3
4
5
6
canvas.addEventListener('mousemove',function(e){
var x = e.clientX - vpx
var y = e.clientY - vpy
angleX = -x*0.00001
angleY = -y*0.00001
})

对于博客,该事件加在body上,使整个页面上都可以响应鼠标位置,相应的应减去球心位置偏移 (canvas.offsetLeft + document.body.scrollLeft + document.documentElement.scrollLeft, canvas.offsetTop + document.body.scrollTop + document.documentElement.scrollTop)

window.requrestAnimationFrame

告诉浏览器:你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

因为渲染canvas和执行js不是一个线程,使用for循环来串起动画是行不通的,setInterval和setTimeout倒可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let start
function animation(timestamp){
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(!start){
start = timestamp
}
rotateX(angleX)
rotateY(angleY)
atoms.forEach(atom=>{
atom.paint()
})
const elapsed = timestamp - start
if(true){
requestAnimationFrame(animation)
}
}
requestAnimationFrame(animation)

replace

常规用法

1
2
str.replace('_','')
str.replace(/[^0-9a-zA-Z]/g,"");

指定函数作为参数
即第二个参数为函数
见于hexo源码 /node_modules/hexo/lib/hexo/post.js line 42, line 25
1
2
3
4
5
6
7
8
9
restoreCodeBlocks(str) {
return str.replace(rCodeBlockPlaceHolder, _restoreContent(this.cache));
}
const _restoreContent = cache => (_, index) => {
assert(cache[index]);
const value = cache[index];
cache[index] = null;
return value;
};

rCodeBlockPlaceHolder是一串正则表达式,replace对str中符合正则表达式的结果(即str.match(rCodeBlockPlaceHolder))应用参数二方法,方法执行结果替换相应的字符串
值得注意的是,_restoreContent(this.cache)返回一个带迭代的方法,index对应str.match(rCodeBlockPlaceHolder)的次序

Progressing Web Application
https://create-react-app.dev/docs/making-a-progressive-web-app/
https://web.dev/progressive-web-apps/

渐进式意使是网站根据浏览器的功能相应地呈现,高级的浏览器呈现高级的效果。就目前来看主要表现是对于版本比较高的 chrome,firefox 等浏览器,pwa 可以使用 add to home screen 的功能,使网络应用固定在桌面、移动设备主屏幕,成为独立应用

脱机使用的渐进式web应用更快速也更可靠,还具备更吸引人的移动端体验,比如在网络不佳的场景下使用,还有添加到主屏幕的功能

网络应用清单 manifest.json

参考PWA manifest 配置

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
{
"short_name": "MY APP",
"name": "MY APP Full Name",
"icons": [
{
"src": "icon/lowres.webp",
"sizes": "48x48",
"type": "image/webp"
},{
"src": "icon/lowres",
"sizes": "48x48"
},{
"src": "icon/hd_hi.ico",
"sizes": "72x72 96x96 128x128 256x256"
},{
"src": "icon/hd_hi.svg",
"sizes": "257x257"
}
],
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#FFB71B",
"url": "/",
"manifestUrl": "/manifest.json",
"lang": "en",
"description": "blabla",
"scope": "."
}

manifest的生成可以借助webpack plugin生成

Service Worker

Service Worker 是一直在浏览器后台运行的worker线程,设置其拦截用户的请求(如加载脚本和图片)进而可以改变请求行为,如不访问服务器直接返回, 再如从缓存加载资源 以提升在离线或加载繁重的应用场景

如下注册Service Worker,Service Worker必须由HTTPS协议载入,Service Worker脚本须与网站同域

1
2
3
4
5
6
7
8
9
if ('serviceWorker' in navigator) {
// register service worker
navigator.serviceWorker.register('./sw.js', {scope: './'}) // 参数1:注册提供的脚本URL 参数2:导航匹配
.then(()=>{
console.log('注册成功')
}).catch(()=>{
console.log('注册失败')
});
}

sw.js

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
// 缓存静态文件
self.addEventListener('install', (event) => {
event.waitUntil(caches.open('myapp').then((cache) => cache.addAll(['**/*'])));
});

// 缓存接口数据
self.addEventListener('fetch', (event) => {
event.respondWith(caches.match(event.request).then((response) => {
// 匹配到请求
if (response !== undefined) {
return response;
} else {
return fetch(event.request).then((response) => {
// 缓存响应数据
let responseClone = response.clone();
caches.open('v1').then((cache) => {
cache.put(event.request, responseClone);
});
return response;
}).catch(() => {
return caches.match('/gallery/myLittleVader.jpg');
});
}
}));
});

// 更新缓存
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
// 如果有更新
if (cacheName !== 'v1') {
return caches.delete(cacheName);
}
})
);
}).then(function(){
return self.clients.claim()
})
);
});

Service Worker生命周期:

如index.html中 加载应用时即注册service worker,注册成功即触发安装事件(install event),见sw.js代码,在安装事件的响应中,处理应用缓存

安装成功后激活service worker触发激活事件(active event)

激活成功后service worker进入idle状态 在该状态下所有请求会触发fetch event 直到应用关闭

Service Worker的更新:

触发更新

  • 导航到作用域内页面时
  • 某事件触发后24h未下载Service Worker

若Service Worker下载后被发现是新文件,无论Service Worker内容是否与现有的相同,都将触发install

如果是第一次安装Service Worker,如上所述安装成功后触发激活,但对于更新,Service Worker已启用的情形,会等待所有已加载的页面不再依赖旧的Service Worker后,触发激活

添加到主屏幕(a2hs)

使应用支持a2hs:

  • https
  • 正确配置的manifest.json
  • 合适的图标
  • 注册service worker
    注册service worker是作为a2hs条件的,但是可以a2hs,未必可以离线使用

安装

1
2
yarn add react-intl
yarn add -D @formatjs/ts-transformer

we highly recommend declaring defaultMessages inline along with their usages because of the following reasons: 建议使用声明内联的defaultMessages, 连同他们的usage,原因如下

  1. Messages colocated with their usages become self-managed, as their usages change/removed, so are the messages. 搭配usage的Message成为自治的 当usage更改或移除,Message亦同
  2. Messages are highly contextual. We’ve seen a lot of cases where developers assume a certain grammar when they write their messages. Buttons/Call-To-Actions and labels are also translated differently. Message高度关联上下文, 杜绝为message发明新语法或编规则
  3. Text styling is also dependent on the message itself. Things like truncation, capitalization… certainly affect the messages themselves. 会与样式相关
  4. Better integrations with toolchains. Most toolchains cannot verify cross-file references to validate syntax/usage. 易于工具链集成

格式化语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as React from 'react'
import {IntlProvider, FormattedMessage, FormattedNumber} from 'react-intl'

<IntlProvider messages={messagesInFrench} locale="fr" defaultLocale="en">
<p>
<FormattedMessage
id="myMessage"
defaultMessage="Today is {ts, date, ::yyyyMMdd}"
values={{ts: Date.now()}}
/>
<br />
<FormattedNumber value={19} style="currency" currency="EUR" />
</p>
</IntlProvider>

Error: [React Intl] Could not find required ‘intl’ object. IntlProvider needs to exist in the component ancestry

当没有IntlProvider父组件时报上述异常

提取文本映射

1
yarn add -D @formatjs/cli

package.json中添加脚本命令

1
"extract": "formatjs extract"

执行
1
2
3
yarn extract 'src/**/*.ts*' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'

npm run extract -- 'src/**/*.ts*' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'

将待翻译的message保存为指定语言的json文件,没id的message自动编码id

分发

将翻译好的多语言lang/*.json编译为Intl使用的格式
package.json中添加脚本命令

1
"compile": "formatjs compile"

执行
1
2
3
yarn compile lang/fr.json --ast --out-file compiled-lang/fr.json

npm run compile -- lang/fr.json --ast --out-file compiled-lang/fr.json

切换语言代码

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
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import {IntlProvider} from 'react-intl'

function loadLocaleData(locale: string) {
switch (locale) {
case 'fr':
return import('compiled-lang/fr.json')
default:
return import('compiled-lang/en.json')
}
}

function App(props) {
return (
<IntlProvider
locale={props.locale}
defaultLocale="en"
messages={props.messages}
>
<MainApp />
</IntlProvider>
)
}

async function bootstrapApplication(locale, mainDiv) {
const messages = await loadLocaleData(locale)
ReactDOM.render(<App locale={locale} messages={messages} />, mainDiv)
}

切换语言触发IntlProvider下的组件初始化,将本地化的messages词条传入组件上下文

useIntl钩子

返回当前语言的provider对象,提供formatData,formatMessage等方法,从messages词条中映射字符串

1
2
3
4
5
6
const intl = useIntl()
return (
<span title={intl.formatDate(date)}>
<FormattedDate value={date} />
</span>
)

带参数的格式化Message
1
2
3
4
5
6
7
8
9
dragAreaSupportTip(format: string) {
const messages = defineMessages({
supportTip: {
id: 'dragArea.supportTip',
defaultMessage: 'Only supports {format} files',
},
})
return intl.formatMessage(messages.supportTip, { format });
}

formatjs extract和compile命令可以在此使用
switch case 场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
translateEnums(name:string){
name = name.replace(/ /g, '_')
const messages = defineMessages({
transName: {
id: 'enums.name',
defaultMessage: `{name, select,
dog {dog}
cat {cat}
King_Kong {King Kong}
other {{name}}
}`,
},
})
const translatedName = this.intl.formatMessage(messages.transName, { name:name });
return translatedName.replace(/_/g, ' ')
}

格式是{key, select, 选项..}的样子,见format.js Doc:select format 选项是’value {text}的格式,value据说遵循Unicode Pattern_Syntax 然而我并没有找到空格的表示法,如上’金刚’的名字用下划线替换了空格,另外匹配失败返回原字符串,变量key需再加大括号括起来

参考https://www.cnblogs.com/dreamroute/p/8484457.html

全文搜索引擎

‘全文检索’不是ElasticSearch,ElasticSearch是一个开源的基于全文搜索引擎(Apache Lucene)的搜索和分析引擎。

全文搜索引擎面向文档型存储,即插入其数据库的每条记录作为一个文档,搜索引擎为提取文档的词,生成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)

文档-类型-索引

关系数据库: ⇒ 数据库 ⇒ 表 ⇒ 行 ⇒ 列(Columns)

Elasticsearch: ⇒ 索引(Index) ⇒ 类型(type) ⇒ 文档(Docments) ⇒ 字段(Fields)

二叉树 倒排

Kibana

ElasticSearch的可视化仪表盘

Philosophy

湖是水源汇聚之处

Azure Data Lake includes all the capabilities required to make it easy for developers, data scientists, and analysts to store data of any size, shape, and speed, and do all types of processing and analytics across platforms and languages. It removes the complexities of ingesting(插入) and storing all of your data while making it faster to get up and running with batch(批量), streaming, and interactive analytics. ———— Azure Data Lake Solution

Material UI System

System是Material UI 自定义主题、样式的一套方法
依赖

1
npm install @material-ui/system@next @emotion/react @emotion/styled

  • 直接就可以在你需要的组件上面进行样式定制
  • 避免定义一些列的样式(className)或带样式组件(styled-component)
  • 制定统一的标准 如字体 字号 色系

关于使用sx系统样式与定义styled-component的书写差异
styled-component

1
2
3
4
5
6
7
8
9
10
const StatHeader = styled('div')(z
({ theme }) => `
color: blue;
`,
);

...
return (
<StatHeader>font color is blue here</StatHeader>
)

‘轻’组件(Box, Stack, Typography, and Grid) 和 sx属性
1
2
3
4

return (
<Box sx={{ color: 'text.secondary' }}>this color is blue here</Box>
)

Material Design

Material Design

Color Palette tool
一套以上述工具生成的颜色模板(基于#36b0c9)👇

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
$qqs-design:(
50: #e7f6f9,
100: #c3e7ef,
200: #9bd8e4,
300: #72c8d9,
400: #54bcd1,
500: #36b0c9,
600: #30a9c3,
700: #29a0bc,
800: #2297b5,
900: #1687a9,
A100: #dcf6ff,
A200: #a9e9ff,
A400: #76ddff,
A700: #5dd6ff,
contrast: (
50: #262626,
100: #262626,
200: #262626,
300: white,
400: white,
500: white,
600: white,
A100: #262626,
A200: white,
A400: white,
A700: white
)
);

material套用自定义颜色模板
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
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@import '~@angular/material/theming';
// Plus imports for other components in your app.
@import 'theme.scss';
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();

// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$CSDPartnerPortal-primary: mat-palette($csd2021);
$CSDPartnerPortal-accent: mat-palette($csd2021, 500, A100, A400);

// The warn palette is optional (defaults to red).
$CSDPartnerPortal-warn: mat-palette($mat-red);

// Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography".
$CSDPartnerPortal-theme: mat-light-theme((
color: (
primary: $CSDPartnerPortal-primary,
accent: $CSDPartnerPortal-accent,
warn: $CSDPartnerPortal-warn,
)
));

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($CSDPartnerPortal-theme);