之前


在动物厂的笔试和面试环节,都问到了用JS来做全站统计信息的上报的思路
面试官当时举例问了三个涉及的方面

  • PV / UV
  • 页面加载性能信息
  • JS运行报错信息

以及如何去部署这些公用脚本


PV / UV


根据定义,先设置相关的用户识别码,然后使用js获取各种信息,发送到后端接口。

  • PV: 页面访问次数,页面每刷新一次就可以count加一次
  • UV: 独立访客数,通过在cookie中设置唯一码进行区分不同用户
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
// 1. 生成用户识别码 uid 
var uid = "xxxx-xxxx-xxxx-xxxx".replace(/x/g, function(char) {
var charCode = Math.random() * 16 | 0;
return charCode.toString(16).toUpperCase();
});
// "1325-2E6F-A3E1-E2AD"

// 2. 保存UID到 cookie,并设置7天过期
var expiresTime = new Date();
expiresTime.setTime(expiresTime.getTime() + 7 * 24 * 3600000);
document.cookie = "UUID=" + escape (uid) + ";expires=" + expiresTime.toGMTString();
// 为了统一和操作、最好使用其他包装好的 cookie 操作库

// 3. 使用 sessionStorage 存储一次会话中的浏览量
sessionStorage && sessionStorage.PVCOUNT ? sessionStorage.PVCOUNT = Number(sessionStorage.PVCOUNT) + 1 : sessionStorage.PVCOUNT = 1;

// 4. 增加其他一些姿势
// 当前网址
window.location.href
// "http://buybuybuy.me/"

// 跳转前网址,可以查看是否通过搜索引擎跳转来的
document.referre
// "https://www.google.co.jp/"

// 客户端信息,可以抽取系统版本、浏览器等
navigator.userAgent
// "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"

// 5. 之后就可以把前面的信息 ajax 传输到后端统计接口

另外需要注意的一点是,前台js是无法直接获取客户端的ip地址。
若要使用ip地址:

  • 可以使用ajax向后台发送一个请求返回ip地址
  • 或者使用服务器预先渲染js代码到页面中

页面加载性能信息


了解前端各种资源在客户端的加载情况,便于己方对各类资源进行CDN缓存等来改进用户体验。
这里可以直接使用 window.performance 对象来评估。

window.performance.timing

包含了各种页面加载的时序信息

  • navigationStart:前一网页unload事件时间
  • redirectStart:发生http跳转时间
  • requestStart:发送http请求时间
  • domLoading:dom开始解析时间

timing

更多参考:Performance API - W3C Performance API - ruanyifeng

window.performance.getEntries

返回页面加载过程中,http文件请求的结果。
以数组形式保存每个文件请求的相关信息。
firefox和chrome都支持该api。

getEntries

更多参考:Evaluating network performance


JS运行报错信息


除去代码中try-catch和throw配对地进行处理可能的错误之外。
当js发生运行时错误和语法错误时,window 对象的 onerror 方法会被调用执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// onerror 函数提供的参数阐述了发生错误的各种信息
// main.js
window.onerror = function (message, url, lineNumber, columnNumber, error) {
// 1. message
// 错误信息
// 2. url
// 脚本资源url
// 3. lineNumber & 4. columnNumber
// 发生错误的行号和列号
// 5. 发生错误相关的error对象
console.log(message + '\n' + url + '\n' + lineNumber + '\n' + columnNumber + '\n' + error.stack);


// fun.js
var k = 1;
var f = Number(k + k2);

// console
> Uncaught ReferenceError: k2 is not defined
> http://localhost:8088/fun/fun.js
> 2
> 20
> ReferenceError: k2 is not defined
> at http://localhost:8088/fun/fun.js:2:20

但是页面上资源(img等)加载错误,只会在该element上触发事件,且该事件并不会向上冒泡。


利用 Nginx 部署全站脚本


因为这类信息统计脚本算是全站所有页面的公有脚本,可以考虑在服务器页面响应中插入此段代码。
这里可以使用 nginx 的 ngx_http_sub_module 模块完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 因为之前使用homebrew安装的nginx
// 查看后发现,默认安装没有开启这个模块
nginx -V

// 于是编辑 nginx-full.rb 文件
brew edit nginx-full

// 在编译参数数组中添加 --with-http_sub_module
args = %W[
...
--with-http_sub_module
]

// 保存好修改的文件,使用brew卸载掉nginx再重装

然后修改nginx配置文件,在对应的 server > location 上下文中,添加 sub_filter 指令。
这里使用了变量 $injected 保存了脚本地址,对response中有 body 标签的地方修改插入。

1
2
3
4
5
6
7
8
9
// nginx.conf
location / {
...
set $injected '<script src="http://localhost:8088/other/status.js"></script>';

sub_filter '<body>' '<body>${injected}';
sub_filter_types *;
sub_filter_once on;
}

html代码注入脚本后的效果:

nginx

这里还可以在 sub_filter 多添加一条规则,将客户端ip地址替换注入到页面中。

1
2
// 替换响应中的 UUIP_IP 字段为 ip 地址
sub_filter 'UUIP_IP' '${remote_addr}';

今天


参加了一次前端方向的面试,也是人生中第一次正式的面试。
主要在一面和二面中被问及了技术相关的问题,面试完总的感觉还是基础部分薄弱了,特别是一些生产中的情形,其实很常见很常规的问题和方法,但是自己并没掌握。
面试官所提问题一些是根据简历来的,一些是常规提问。
根据回忆列出了大部分问题,之后若有想起再补充。

一面:

  • 有做过切图吗
  • 堆和栈理解、相关的内存分配
  • 进程和线程的理解
  • 阐述下MVC模式、对比再说说MVVM模式的理解
  • 如何理解强类型和弱类型语言
  • 有哪些情况会触发同源策略
  • 清除浮动有哪些方法
  • 对JS闭包的理解
  • 对Prototype的理解
  • Json 和 Jsonp 有什么区别
  • 实现一个图片轮播的思路及无限滚动
  • Gulp和Grunt你理解的有什么区别
  • Seajs、Requirejs、CMD、AMD区别以及为什么你们的项目从Seajs换成了Requirejs
  • Vue.js用了多久、用到了什么功能
  • Webpack用了多久、有什么收获
  • eslint或者jslint用过吗、使用的原因
  • 平时学习的思路和方法、慕课网之类的看过什么课程
  • 还用过什么前端的框架或者技术、补充说明(自由发挥)
  • 平时喜欢逛什么网站、哪些技术类型的社区
  • 最近还有在做什么项目
  • 最近看过什么书
  • 对自己进行一个优缺点的总结

二面:

  • 为什么某项目要选Vue.js、Vue和React比较
  • 你想做iOS和前端、二者有何联系对你有什么帮助
  • 平时有读过源码或者改过发pr吗
  • 有自己的vps和管理的线上网站吗
  • 如何设计全站的异常捕获、UV/PV收集、前段页面加载性能统计,并将这些数据上报,同时这些统计代码怎么部署
  • 前端的安全防范了解多少,如CSRF、XSS
  • 如何平滑地实现全站https化
  • 正则表达式除了字段校验还做过什么
  • 接下来最想做的事情
  • 网站性能优化的思路
  • 哪个项目最有收获、有何收获
  • 你有什么问题要问我

总的


整个面试的过程都很平滑和友好,面试官都很和蔼可亲的,没有故意刁难或者甩黑脸。
一面的问题偏向于基础,基本都是抛出一句话的问题,然后让你阐述一下,他负责记录每个问题的评价,所以基本上你阐述的越全越好。
二面不管问你项目经历方面还是他提出的假设问题,感觉都是有一个聊天的引导,然后看你的思路如何去解决。比如在你解决不了或者卡壳的时候,他会适当的提示和阐述。最后看你实在是没有解决的方法后,他会告诉你该问题的解决方法,或者对你思考不足的地方作出一个补充。难度方面会比一面深入,所以即使问题看起来少了,但是面试的时间比一面多将近一倍。

最近


需要使用weka这种ML工具预跑UK2007数据集查看粗略结果。
为了举办Web Spam Challenge 2008大赛,UK2007官方主页分别提供了SET1和SET2两个类标集用于训练和测试,因为是志愿者交叉标注的,所以类标含spam, nonspam和undecided三种。
针对不需要亲自提取特征和数据预处理的小伙伴,官方页面最下方提供了基于content、link等几种预处理后的数据文件,有csv和weka中适用的arff格式。

  • csv中样本很多但是没有label
  • arff是部分样本也赋好了label的文件,但是label只有spam和nonspam

因为我想测试三种类标都加入进去的分类效果,所以只有自行使用类标集和预处理后的特征csv文件,再根据每条数据唯一的id合并产生trainset.csv和testset.csv两个数据集。

然后


  1. 打开weka,在主界面 Preprocess Tab 选择 open file 读取训练集文件 trainset.csv
  2. 切换至 Classfify Tab 点击 choose 选择合适的分类算法
  3. test options 框中选择 Supplied test set(使用测试集) 项并设置读取测试集文件 testset.csv
  4. 下拉菜单 选取类标所在属性列名 class 并点击 start 开始分类

然后就愉快地得到了 Train and test set are not compatible 的错误提示。
明明两个文件都是从特征csv中,分别加入一列class类标来的,按理说trainset和testset是一致的格式信息才对。

error info

test options 的其余三种选择经测试和预想的一样是可以用的,因为十倍交叉和比例划分本质都是使用trainset自身的数据。

最后


搜索到了weka官方wiki对于这个问题的解释,结果漏看了别人要求 Nominal Attributes(标称属性) 必须完全一致包括取值和顺序。

In case of nominal attributes, you must ensure that the number of labels and the order of the labels are the same.

其间又做了不少无用功,终于在回头查看的时候发现了问题。于是改造原本的数据集最终跑出了结果。

  1. 打开weka,在主界面 Preprocess Tab 选择 open file 读取数据集文件 trainset.csv
  2. Attributes 框中选取与分类不相关的Nominal Attributes,如 域名ID。点击 Remove 预处理清除掉这些特征。
  3. 点击 save 保存处理后的数据为weka的arff格式
  4. 重复上面三个步骤处理testset.csv并保存
  5. 打开新的trainset.arff和testset.arff,查看class列定义取值顺序是否一致,若不一致(如图)则修改为一致。
  6. 重复上一节2~4步骤可以成功运行并取得结果

问题表现是在使用npm的时候经常因为权限问题,需要使用sudo才能安装package或其他操作。

1
npm install some-package // EACCES error

因为你OS X上使用homebrew或其他安装方式安装node的时候使用了sudo权限,导致以后每次对npm主目录的操作都要请求root权限。

我使用官方Fixing npm permissions 推荐中的第一种修改npm默认目录的的权限修复了这个问题。

1. Download & Install For Yosemite


Octave 3.8.0 installer

2. 基础操作


  1. 原来如此

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    % fvnc
    pwd % 当前 work directory
    who % 查看当前变量
    whos % 查看当前变量详细信息
    clear [var_name] % 清除 所有|[变量名]

    % load & save data
    load('data.dat') % 加载数据到程序
    save binary.mat var_name % 将变量存为二进制文件
    save plain.txt var_name -ascii % 将变量存为文本文件

    % note
    1 ~= 2 % 逻辑非 返回真(1)
    disp(sprinf('result is %0.2f',result)); % 格式化字符串 & 控制台输出
  2. 建立矩阵

    1
    2
    3
    4
    5
    6
    7
    8
    A = [3:1:10];         % 从 3 至 10 ,步进 1
    B = [ 1 2; 3 4; 5 6]; % 3x2 matrix
    ones(m,n) % 全 1 阵
    zeros(m,n) % 零阵
    rand(m,n) % 随机元素不大于 1
    randn(m,n) % 随机元素服从均值 0 ,标准差 1 高斯分布
    eye(n) % 单位阵
    magic(n) % 魔方:横、竖、对角线相加的值都一样
  3. 操作矩阵

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    % 选取
    A(:) % 顺序选中组合成新的列向量
    A( m,: ) % 选中第 m 行行向量
    A( :, n) % 选中第 n 列列向量
    A(( 1 3),:) % 选中 1,3 行向量
    C = ( A, B) % 横向拼接矩阵 A, B为C
    D = ( A; B) % 纵向拼接矩阵 A, B为C

    % fvnc
    size(A) ; % 返回 m 和 n
    length(A) ; % 返回 m 和 n 的大者,适于向量长

    % 计算
    A^2 % 实际计算的是A*A
    A .^ 2 % 每个元素求2次方
    A .* B % 对应元素相乘
    A' % 求转置
    pinv(A) % 求伪逆
    inv(A) % 求逆
    log(A) % 对每个元素求log,类似还有exp、abs等

    % max,find,flipud等参考help
  4. 基础编程

    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
    % for statement
    for i=1:10,
    disp(i);
    end;

    % while statement
    i=0;
    while i<10,
    disp(i);
    i++;
    end;

    % if statement
    if 1 == 6,
    disp('true');
    else
    disp('false');
    end;

    % define function (save as Testfunc.m)
    % >> [r1,r2] = Testfunc(10)
    % >> r1 = 400, r2 = 8000
    function [y1,y2] = Testfunc(p1)
    p1 = p1+10;
    y1 = p1^2;
    y2 = p1^3;
    end;

1. 终于有静态博客了


早先在常乐小区的时候,huxos就爱给我安利markdown,git,hexo这类东西,但是那哈儿我总是忙着烤烧烤,从来没有摸过。
前段时间吴小蛆把他的蛆巢从wp迁移成了hexo也安利了我几次。
然而懒笔如我
本来这个域名之前一直是挂在vv的一台linode上,现在他没钱跑路了就空起了。昨天想起了这个事就决定搭起博客把域名用起来。

2. 相关问题马一个


主要参考了

  1. 在上面的教程中都是推荐在狗爹买域名,然而受GFW的影响都要去多设置个dnspod。
    这里推荐一家之前星()云()菇推荐给我的一家毛子域名商Ahnames,Paypal支付大部分比狗爹便宜还送whois保护,也不用多去设置域名解析。
  2. 在设置deploy的时候出错,发现init后并没有相关git deploy包。

    1
    $ npm install hexo-deployer-git --save
  3. CNAME文件是新建在/source

  4. 第一个博客换成了Jekyll 233

使用的主题

  1. 相关配置看GitHub上的wiki就可以了,readme中的那个doc站有些错误没更新。
  2. 在icomoon生成的自定义icon包中,复制selection.json/fonts中的文件到自己新建的文件夹。另一边的styl文件参考icno-default.styl中的写法,如下配套写出来就可以了。
    .icon-home:before {
    content: "\e600";
    }
    ...
    .icon-next-home:before {
    @extend .icon-home:before;
    }
    ...