Skip to main content

11 posts tagged with "周记"

View All Tags

git新命令及别名

git命令简写(ohmyzsh中)#

原始命令简写命令备注
git statusgst查看
变化状态
git diffgd查看
代码改动
git addga
git pullgl拉取代码
git pushgp推送
git push --set-upstream origingpsup $()推送本地分支到远程
git branchgb查看分支
git checkoutgco切换分支
git checkout -bgcb新建分支
git branch -Dgbd删除分支
git log --statglg查看提交

官方手册

git 两个新命令来拆分git checkout(version: 2.23.0)#

git swtich

git switch -c t1
git switch main
// 等同于
git checkout -b t1
git checkout main
git switch -d cac2fbac22b0ec6a168984351327ec37b14349aa
HEAD is now at cac2fbac chore(lcof2): change images path
git:(cac2fbac)
git reset --hard c2db695e1c40b43b71e9e44497724b0b76179639
HEAD is now at c2db695e feat: add solutions to lc problem: No.0493.Reverse Pairs

git restore 表示将在工作空间但是不在暂存区的文件撤销更改

weekly248

vscode 插件: GitHub Copilot#

基于OpenAI’s GPT-3 model ​

警惕: step 1: developers share code data step2:    build an AI with that data step3:    reinforce the AI with developers step4:    replace developers with AI

寡头资本 --《世纪大拍卖》#

weekly247

处理特殊字符#

空格
\t 水平制表符
\v 垂直制表符
\f 换页符
\n 换行
\s 一个空白字符,包括空格、制表符、换页符和换行符

String.trim()#

场景: 多行文本尾部限制空格和换行符, 提交表单前, 处理数据

扩展: String.trimEnd()、 String.trimStart()

v-model.trim=""#

从操作上限制 场景:仅一行输入, 且限制无法输入空格、换行

CSS: 不允许换行white-space: nowrap;#

场景: 行内容器不希望换行

CSS:允许词内换行word-break: break-all;#

场景:较小的文本框, 遇到较长单词, 被撑开, 破坏正常布局。 决绝方案: 允许词内换行

DFS-FloodFill#

机器人的运动范围](https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/) 以 [0, 0] 为起点, 寻找可行路线 ⚠️ 仅需向右、向左即可 dfs

function movingCount(m: number, n: number, k: number): number {
let visited = Array.from({ length: m }, v => new Array(n).fill(false));
return dfs(m, n, k, 0, 0, visited);
};
function dfs(m: number, n: number, k: number, i: number, j: number, visited: boolean[][]): number {
if (i > m - 1 || j > n - 1 || visited[i][j] || getSum(i) + getSum(j) > k) {
return 0;
}
visited[i][j] = true;
let ans = 1;
for (let [dx, dy] of [[0, 1], [1, 0]]) {
let x = i + dx, y = j + dy;
ans += dfs(m, n, k, x, y, visited);
}
return ans;
}
function getSum(num: number): number {
let ans = 0;
while (num > 0) {
ans += (num % 10);
num = Math.floor(num / 10);
}
return ans;
}

bfs

function movingCount(m: number, n: number, k: number): number {
let visited = Array.from({ length: m }, v => new Array(n).fill(false));
let queue = [[0, 0]];
let ans = 1;
while (queue.length > 0) {
let [i, j] = queue.shift();
for (let [dx, dy] of [[0, 1], [1, 0]]) {
let x = i + dx, y = j + dy;
if (x > m - 1 || y > n - 1 || visited[x][y] || getSum(x) + getSum(y) > k) {
continue;
}
++ans;
visited[x][y] = true;
queue.push([x, y]);
}
}
return ans;
};
function getSum(num: number): number {
let ans = 0;
while (num > 0) {
ans += (num % 10);
num = Math.floor(num / 10);
}
return ans;
}

200. 岛屿数量#

由于只有0、1两种状态,直接修改代替visited标记访问状态 dfs 遍历每个点,搜索整个小岛后并标记为访问, 计数

function numIslands(grid: string[][]): number {
let m = grid.length, n = grid[0].length;
let ans = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
dfs(grid, i, j);
++ans;
}
}
}
return ans;
};
function dfs(grid: string[][], i: number, j: number) {
let m = grid.length, n = grid[0].length;
if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
for (let [dx, dy] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
let x = i + dx, y = j + dy;
dfs(grid, x, y);
}
}

bfs 遍历每个点,(宽度)搜索小岛全部并标记为已访问, 计数

function numIslands(grid: string[][]): number {
let m = grid.length, n = grid[0].length;
let ans = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
bfs(grid, i, j)
++ans;
}
}
}
return ans;
};
function bfs(grid: string[][], r: number, c: number): void {
let m = grid.length, n = grid[0].length;
let queue = new Array();
queue.push([r, c]);
while (queue.length > 0) {
let [i, j] = queue.shift();
for (let [dx, dy] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
let x = i + dx, y = j + dy;
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1') {
grid[x][y] = '0';
queue.push([x, y]);
}
}
}
}

695. 岛屿的最大面积#

由于只有0、1两种状态,直接修改代替visited标记访问状态 dfs

function maxAreaOfIsland(grid: number[][]): number {
let m = grid.length, n = grid[0].length;
let res = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] == 1) {
res = Math.max(dfs(grid, i, j), res);
}
}
}
return res;
};
function dfs(grid: number[][], i: number, j: number): number {
let m = grid.length, n = grid[0].length;
if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || grid[i][j] == 0) {
return 0;
}
grid[i][j] = 0;
let res = 1;
for (let [dx, dy] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
res += dfs(grid, i + dx, j + dy);
}
return res;
}

130. 被围绕的区域#

[["X","X","X"],["X","O","X"],["X","X","X"]] [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]

/**
Do not return anything, modify board in-place instead.
*/
function solve(board: string[][]): void {
let m = board.length, n = board[0].length;
if (m < 3 || n < 3) return;
let visited = Array.from({ length: m }, v => new Array(n).fill(false));
// 第一行,最后一行, 第一列, 最后一列
for (let i of [0, m-1]) {
for (let j = 0; j < n; ++j) {
if (board[i][j] == 'X') {
visited[i][j] = true;
} else {
dfs(board, i, j, visited, true);
}
}
}
for (let i = 0; i < m; ++i) {
for (let j of [0, n - 1]) {
if (board[i][j] == 'X') {
visited[i][j] = true;
} else {
dfs(board, i, j, visited, true);
}
}
}
for (let i = 1; i < m - 1; ++i) {
for (let j = 1; j < n - 1; ++j) {
!visited[i][j] && dfs(board, i, j, visited);
}
}
};
function dfs(board: string[][], i: number, j: number, visited: boolean[][], edge = false): void {
let m = board.length, n = board[0].length;
if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || visited[i][j]) {
return;
}
visited[i][j] = true;
if (board[i][j] == 'X') {
return;
}
if (!edge) {
board[i][j] = 'X';
}
for (let [dx, dy] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
let x = i + dx, y = j + dy;
dfs(board, x, y, visited, edge);
}
}

关于前端职业规划的一点思考#

weekly246

Map.values转数组#

Array.from (推荐)

const map1 = new Map().set('0', 'foo').set(1, 'bar');
console.log(Array.from(map1.values()));
// Array ["foo", "bar"]

[...] (不推荐,ts中编译告警)

const map1 = new Map().set('0', 'foo').set(1, 'bar');
console.log([...map1.values()]);
// Array ["foo", "bar"]

如何查看github热门?#

  1. 趋势

https://github.com/trending

  1. 话题

https://github.com/topics

  1. 搜索框查询
in:name react
in:name React stars:>5000

精准搜索 + 筛选条件

in:name xxx // 按照项目名搜索
in:readme xxx // 按照README搜索
in:description xxx // 按照description搜索
stars:>xxx // stars数大于xxx
forks:>3000 // forks数大于xxx
language:xxx // 编程语言是xxx
pushed:>YYYY-MM-DD // 最后更新时间大于YYYY-MM-DD

扩展: 如何学习开源项目?

chrome调试#

前端容易忽略的debugger调试技巧

iphone 移动端调试#

https://www.jianshu.com/p/da4ce4a19c04

语雀-移动端调试攻略

随手记#

如何激励员工上进?#

赏罚分明

如何克服不放心把工作交出去?#

用人不疑, 疑人不用

什么是埋点

介绍#

概念:#

构建一个数据平台,大体上包括数据采集、数据上报、数据存储、数据计算以及数据可视化展示等几个重要的环节。其中,数据采集与上报是整个流程中重要的一环,只有确保前端数据生产的全面、准确、及时,最终产生的数据结果才是可靠的、有价值的。

用途:#

  • 数据监控, 分析用户行为
    • PV
    • 漏斗
    • 行为路径
  • 性能监控, 提升用户体验
    • 首屏时间
    • 。。。
  • 异常监控, 避免故障

埋点方案#

三种埋点方案:

  • 代码埋点(浸入式埋点)
    • 前端埋点
      • 客户端
      • 内嵌h5
      • 站外h5
    • 后端埋点
  • 无痕埋点(可视化埋点)
  • 全埋点(无埋点)

前端埋点和后端埋点,哪个更科学?

代码埋点(前端)代码埋点(后端)无痕埋点全埋点
概念需要埋点的节点调用接口直接上传埋点数据-通过可视化工具配置采集节点,在前端自动解析配置并上报埋点数据并不是真正的不需要埋点,而是前端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据
优势事件标识明确业务参数丰富事件的触发方式可自定义分析方便、精准数据更准确无需随前端发版前端接入成本低,非开发人员可操作用户动作收集完整,不会漏失零埋点成本抓取用户行为全量数据,任何操作行为都会被上传
劣势开发成本较高依赖前端发版(严重问题只能采取热修复解决)有一定的数据丢失开发成本较高前端属性获取需要接口开发有用、没用的数据都会收集无法采集到特殊的行为动作、业务参数采集到的信息需要进行二次标注,才可以被用户识别当按钮的位置不固定、名称存在重复或页面重构时,无法做到准确的标识噪点多
适用场景用户分析关键行为粗粒度的快速业务探索无需埋点进行传参页面的url、APP的包名等点击元素的xpath路径、title或约定的dom元素
案例加入购物车购买成功、注册成功新功能快速上线迭代效果评估运行活动页

埋点事件#

  • 曝光
  • 点击事件
  • 页面停留 Tp = leaveTime - enterTime

声明式 & 命令式#

//自动发送埋点方式,举例:
<button data-utm-click="${did}" data-utm-data="${业务数据}">
//手动发送埋点方式,举例:
const utmCnt = g_UTM.batchSend('触发类型(click/browse)等',[{
utmCD:['区块信息','位置信息'],
bdata:{key:'其他业务数据'}
},{
utmCD:['001','008'],
bdata:{key:'value'}
}
]);

埋点上传#

  • 实时上报
  • 离线上传

质量保障#

安全性:#

  • https
  • 数据加密: md5、sha112

规范#

  • 命名规范
  • 建立指标字典
  • 流程约束

参考-有赞《埋点质量保证》 可能出现的问题:

  1. 平常数据稳定,突然过高
  2. 事件重复&丢失
  3. 事件参数错误
  4. 前端常见错误
  5. 事件断流

保障机制:#

  1. 准确登记 (定义上报要素)
  2. 实时校验
  3. 完备性/扩展性
    1. 分析校验规则, 抽象语义
    2. 开关、配置化
  4. 可解释/可分析
  5. 定时监控(如事件断流、异常波动)
  6. 专项优化
  7. 评估模型
  8. 质量中心

SDK#

美团点评前端无痕埋点实践

常见数据埋点#

常见数据埋点内容包括:访问量、停留时长、曝光量、点击量、跳出率等

点击埋点#

曝光埋点#

停留时长#

参考:

云音乐大前端|前端组件化埋点的实践

weekly242

<<1 替代 乘2, >>1 替代 除2取整#

⚠️ x 需 < (2 ** 31)

2147483648 >> 1
-1073741824
[0, 1, 2, 3, 4, 5, 6, 7].forEach((v, i) => {
console.log(v * 2, v << 1)
})
0 0
2 2
4 4
6 6
8 8
10 10
12 12
14 14
[0, 1, 2, 3, 4, 5, 6, 7].forEach((v, i) => {
console.log(v , v / 2, v >> 1)
})
0 0 0
1 0.5 0
2 1 1
3 1.5 1
4 2 2
5 2.5 2
6 3 3
7 3.5 3

思考: 如何实现除2向上取整

(3 + 1) >> 1
2
(4 + 1) >> 1
2
(v + 1) >> 1

字典树#

trie, 读tree或try, 也叫前缀树。 是一种有序数, 用于关联数组, 键通常是:

  • 字符串(决定在树中的位置)
  • 涉及到字符串的前缀

也可以看做是一个确定的有限状态自动机。 推荐练习: https://leetcode-cn.com/tag/trie/problemset/

堆和堆排序#

git中的merge策略#

https://morningspace.github.io/dummies-git/pages/#/git-merge-stories-2

Google Chrome 91开始支持剪贴板 带来全新的表单控件用户体验#

发布时间: 5月25 更新日程