0%

在你的应用中安装sdk向Azure Application Insight服务发送遥测数据,支持web客户端,web服务和后台服务等
对应用性能影响小。非阻塞,单独线程

The instrumentation monitors your app and directs the telemetry data to an Azure Application Insights Resource using a unique GUID that we refer to as an Instrumentation Key. You can instrument not only the web service application, but also any background components, and the JavaScript in the web pages themselves. The application and its components can run anywhere - it doesn’t have to be hosted in Azure. 使用Guid作为Instrumentation Key(ikey)标识作为监视器的Azure Insights资源。 监测web服务应用或后台组件亦或页面js 这些应用或组件、命令不必托管在Azure中

创建Azure App Insights资源

Azure Portal -> Application Insights -> Create

添加Javascript SDK

1
npm i --save @microsoft/applicationinsights-web

使用ikey或连接字符串创建客户端instance

1
2
3
4
5
6
7
8
9
import { ApplicationInsights } from '@microsoft/applicationinsights-web'

const appInsights = new ApplicationInsights({ config: {
instrumentationKey: 'YOUR_INSTRUMENTATION_KEY_GOES_HERE' /* 使用ikey */
/* 或使用 connectionString: 'YOUR_CONNECTION_STRING_GOES_HERE' */
/* ...Other Configuration Options... */
} });
appInsights.loadAppInsights();
appInsights.trackPageView(); // Manually call trackPageView to establish the current user/session/pageview

React Plugin
1
2
npm install @microsoft/applicationinsights-react-js
npm install @microsoft/applicationinsights-web

创建实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// AppInsights.js
import { ApplicationInsights } from '@microsoft/applicationinsights-web';
import { ReactPlugin } from '@microsoft/applicationinsights-react-js';
import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory({ basename: '' });
const reactPlugin = new ReactPlugin();
const appInsights = new ApplicationInsights({
config: {
instrumentationKey: 'YOUR_INSTRUMENTATION_KEY_GOES_HERE',
extensions: [reactPlugin],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory }
}
}
});
appInsights.loadAppInsights();
export { reactPlugin, appInsights };

在AppComponent使用Context.Provider 使所有子组件内可使用 useContext hook调用AppInsights实例
1
2
3
4
5
6
7
8
9
10
11
import React from "react";
import { AppInsightsContext } from "@microsoft/applicationinsights-react-js";
import { reactPlugin } from "./AppInsights";

const App = () => {
return (
<AppInsightsContext.Provider value={reactPlugin}>
/* your application here */
</AppInsightsContext.Provider>
);
};

子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from "react";
import { useAppInsightsContext } from "@microsoft/applicationinsights-react-js";

const MyComponent = () => {
const appInsights = useAppInsightsContext();

appInsights.trackMetric("Component 'MyComponent' is in use");
appInsights.trackEvent({ name: 'Component Init', properties: { 'mydata' } });

return (
<h1>My Component</h1>
);
}
export default MyComponent;

useTrackMetric

1
2
3
4
5
6
7
8
const MyComponent = () => {
const appInsights = useAppInsightsContext();
const trackComponent = useTrackMetric(appInsights, "MyComponent");

return (
<h1 onHover={trackComponent} onClick={trackComponent}>My Component</h1>
);
}

useTrackEvent

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
import React, { useState, useEffect } from "react";
import { useAppInsightsContext, useTrackEvent } from "@microsoft/applicationinsights-react-js";

const MyComponent = () => {
const appInsights = useAppInsightsContext();
const [cart, setCart] = useState([]);
const trackCheckout = useTrackEvent(appInsights, "Checkout", cart);
const trackCartUpdate = useTrackEvent(appInsights, "Cart Updated", cart);
useEffect(() => {
trackCartUpdate({ cartCount: cart.length });
}, [cart]);

const performCheckout = () => {
trackCheckout();
// submit data
};

return (
<div>
<ul>
<li>Product 1 <button onClick={() => setCart([...cart, "Product 1"])}>Add to Cart</button></li>
<li>Product 2 <button onClick={() => setCart([...cart, "Product 2"])}>Add to Cart</button></li>
<li>Product 3 <button onClick={() => setCart([...cart, "Product 3"])}>Add to Cart</button></li>
<li>Product 4 <button onClick={() => setCart([...cart, "Product 4"])}>Add to Cart</button></li>
</ul>
<button onClick={performCheckout}>Checkout</button>
</div>
);
}

export default MyComponent;

Click Analytics Auto-collection plugin

自动跟踪网页上的单击事件,并使用 HTML 元素上的 data-* 属性来填充事件遥测数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { ApplicationInsights } from '@microsoft/applicationinsights-web';
import { ClickAnalyticsPlugin } from '@microsoft/applicationinsights-clickanalytics-js';

const clickPluginInstance = new ClickAnalyticsPlugin();
// Click Analytics configuration
const clickPluginConfig = {
autoCapture: true
};
// Application Insights Configuration
const configObj = {
instrumentationKey: "YOUR INSTRUMENTATION KEY",
extensions: [clickPluginInstance],
extensionConfig: {
[clickPluginInstance.identifier]: clickPluginConfig
},
};

const appInsights = new ApplicationInsights({ config: configObj });
appInsights.loadAppInsights();

监视对象

  • User 用浏览器cookie中存储的匿名id区分用户
    JavaScript SDK 自动生成匿名用户和会话 ID,然后在从应用发送这些 ID 后使用这些 ID 填充遥测事件。
  • Session 不活动半小时重新记Session 活动24h后重新记Session
  • Event 每次执行trackEvent逻辑 Event参数已加入一组标准属性,包括匿名用户id(anonymous user ID)QQs存疑!

    其他

    cookie处理 visit time

简写为SOLID

单一职责(Sigle Responsibility Principle)

避免设计对象(类,方法)承担多项职责,在对职责1操作或修改时可能会造成职责2的异常。

  • 在需要if else时考虑划分为两个类或方法
  • 在需求变更导致职责细分时考虑将类或方法划分

开放关闭(Open Closed Principle)

设计对象应该根据需求被扩展,而不应该因需求而做修改,即对类进行抽象和继承
违反开放关闭原则:

1
2
3
4
5
6
7
8
9
10
11
class Factory {
public Computer produceComputer(String type) {
Computer c = null;
if(type.equals("macbook")){
c = new Macbook();
}else if(type.equals("surface")){
c = new Surface();
}
return c;
}
}

抽象出produce接口
1
2
3
4
5
6
7
8
9
10
11
12
13
interface Factory {
public Computer produceComputer();
}
class AppleFactory implements Factory {
public Computer produceComputer() {
return new Macbook();
}
}
class MSFactory implements Factory {
public Computer produceComputer() {
return new Surface();
}
}

里氏替换(Liskov Substitution Principle)

所有引用基类的地方必须能透明地使用其子类的对象。

A的子类B继承父类时,不应改变原功能,只做扩展

接口隔离(Interface Segregation Principle)

合理设计接口的粒度,避免类型依赖它不需要的接口

依赖倒置(Dependency Injection Principle)

高层模块不应依赖低层模块,以免因为低层修改而牵扯高层,两者应该共同依赖接口,将改动限制在接口的实现上
即最少知道,类型对其直接引用的对象A保持最少的了解,不会通过该对象与第三者对象B建立间接的调用关系,要求A将必要的方法封装为public提供外部调用,而不暴露其引用了B的事实

23种设计模式

  • 触发器是一类存储过程
  • 由数据表的事件(如insert update delete)触发,而不是手动调用

登录触发器

官网例子 场景:如果登录名login_test 已经创建了三个用户会话,触发器将拒绝该用户的登录尝试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
USE master;  
GO
CREATE LOGIN login_test WITH PASSWORD = N'3KHJ6dhx(0xVYsdf' MUST_CHANGE,
CHECK_EXPIRATION = ON;
GO
GRANT VIEW SERVER STATE TO login_test;
GO
CREATE TRIGGER connection_limit_trigger
ON ALL SERVER WITH EXECUTE AS N'login_test'
FOR LOGON
AS
BEGIN
IF ORIGINAL_LOGIN()= N'login_test' AND
(SELECT COUNT(*) FROM sys.dm_exec_sessions
WHERE is_user_process = 1 AND
original_login_name = N'login_test') > 3
ROLLBACK;
END;

可知登录触发器在身份认证之后,建立会话之前触发
多个触发器的顺序,即支持指定the first和the last 见Microsoft Docs

DDL

data define language 数据定义语言
即在使用会改变数据库数据结构的语句时触发,如CREATE、ALTER、DROP、GRANT、DENY、REVOKE 或 UPDATE STATISTICS 开头的 Transact-SQL 语句
场景

  • 防止对数据库架构进行某些更改。
  • 希望数据库中发生某种情况以响应数据库架构的更改。
  • 记录数据库架构的更改或事件。

    DML

    data manipulation language (DML) 数据操作语言 INSERT、UPDATE 或 DELETE 语句
    after 触发器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    CREATE TRIGGER schemaA.SyncData 
    ON schemaA.TableA
    AFTER INSERT
    AS
    BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.

    -- Insert statements for trigger here
    insert into schemaB.TableB(Name,TrustedId,EmailAddress,Logo,Type,Enable,RecordStatus,RecordCreated,RecordLastUpdated)
    select name,record_pk,email,logo,'1',0,0,SYSDATETIME(),SYSDATETIME() from schemaA.TableA
    SET NOCOUNT ON;
    END
    GO
    instead of 触发器

  1. DOCTYPE 有什么作用?怎么写?
    1
    <!DOCTYPE html>
    html5规范的文档声明,示意浏览器以相应的标准解析文档,使支持html5规范如新的标签等
  2. 列出常见的标签,并简单介绍这些标签用在什么场景?
    canvas
  3. 页面出现了乱码,是怎么回事?如何解决?
    1
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    可能是即字符编码不匹配造成
  4. title 属性和 alt 属性分别有什么作用?
  5. HTML 的注释怎样写?
  6. data- 属性的作用?
    自定义数据属性data-*
    1
    2
    var box = document.getElementById("box"); // <div id="box" data-user-name="QQs"></div>
    var username = box.dataset.userName;
  7. \ 的 title 和 alt 有什么区别?
  8. Web 标准以及 W3C 标准是什么? web标准
  9. HTML 全局属性(Global Attribute)有哪些?
    id class style title name data-* contenteditable translate
  10. meta 有哪些常见的值?charset
  11. meta viewport 是做什么用的,怎么写?\
  12. 列出常见的标签,并简单介绍这些标签用在什么场景?
  13. 如何在 HTML 页面上展示
    这几个字符?
  14. 你是如何理解 HTML 语义化的?
  15. 前端需要注意哪些 SEO?

VBoxManage

ubuntu可ssh远程用此命令行工具管理虚拟机

  • VBoxManage list vms/runningvms
  • VBoxManage startvm MyUbuntu
    1
    vboxmanage startvm MyUbuntu --type headless #在宿主机端隐藏图形界面 
  • VBoxManage controlvm MyUbuntu poweroff

OData和GraphQL

用了微软家的OData,就不得不再看一遍GraphQL,两者都让前端调用api获得了很大的自由度
相比OData的底层渗透性,GraphQL只在Http接口层做文章,实际上将底层模型封装成schema,开放给接口,这个过程更大程度上做到了安全性和前端业务的可控。

Quick Start

见官方GraphQL Doc:各种语言实现

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
var express = require('express');
var {graphqlHTTP} = require('express-graphql');
var { buildSchema } = require('graphql');

var schema = buildSchema(`
type Query {
heros: [Hero]
battle(name:String): String
random: Float!
}
type Hero{
name:String
abilities: [String]
}
`);

var root = { heros: () => ([
{
name: 'Luke skywalker',
abilities: ['light sward', 'force']
},
{
name: 'Anakin skywalker',
abilities: ['light sward', 'force', 'dark']
}
]),
battle: (name) => {
return 'I am your father';
},
random: () => {
return Math.random();
}
};

var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(3000, () => console.log('Now browse to localhost:3000/graphql'));

关于graphqlHTTP(也就是GraphiQL客户端)的Options:
schema是查询涉及的类型声明, rootValue api的查询方法的集,详见下文章节

schema 类型声明

1
2
3
4
5
6
7
var schema = buildSchema(`
type Hero {
name: String!
abilities: [String!]!
length(unit: LengthUnit = METER): Float
}
`);

标量类型

  • ID
  • String
  • Boolean
  • Int Float
    注意 !表示非空
    枚举
    1
    2
    3
    4
    5
    enum Episode {
    NEWHOPE
    EMPIRE
    JEDI
    }

接口和实现接口的类型

1
2
3
4
5
6
7
8
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}

联合类型

1
union SearchResult = Human | Droid | Starship

操作类型

  • query 查询:获取数据、查找
  • mutation 变更:对数据进行变更,比如增加、删除、修改
  • substription 订阅:当数据发生更改,进行消息推送

GraphQL client

前面的express-graphQL启动后打开GraphiQL页面,GraphiQL就是一个客户端,使用GraphQL client向 GraphQL 服务器上的入口端点发送一个 HTTP POST 请求,其中将 GraphQL 查询作为 JSON 载荷的 query 字段,就能调用 GraphQL 服务器。
其js实现大致是

1
2
3
4
5
6
7
8
9
10
fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({query: "{ hello }"})
})
.then(r => r.json())
.then(data => console.log('data returned:', data));

查询参数

schema 中的 Query声明了若干查询方法,查询方法的具体实现在root中定义,其参数类型的指定格式与typescript相同!
调用格式如下

1
2
3
{
battle(name:"dark lord")
}

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)