0%

该职位期望招聘高级软件开发人员 以完成基础软件开发 具备独立搭建框架能力和较丰富的前后端开发经验

1介绍一下原型链

2变量提升

  • js中变量和函数的声明都会被提升到函数最顶部
  • 这使得变量和函数在coding时可以先调用在声明

2属性绑定实现视图更新的原理

Angular的双向绑定原理:

React的虚拟dom和diff算法
虚拟dom是轻量化的js对象 用于描述真实的dom 当状态改变时 react通过比较虚拟dom的差异 最小化改动真实dom 从而提升性能

3编写单元测试用例

4只执行一次的生命周期钩子 constructor init之间进行了什么

钩子 目的

ngAfterContentInit | 在Angular将外部内容放到视图内之后。
ngAfterContentChecked | 在Angular检测放到视图内的外部内容的绑定后。
ngAfterViewInit | 在Angular创建了组件视图之后。
ngAfterViewChecked | 在Angular检测了组件视图的绑定之后。

constructor 依赖注入

5依赖注入 反射

6面向对象的核心——抽象

封装 继承 多态是面向对象的三个核心特征 对应的目的性是是代码具备可重用性、可扩展性,即可减少重复代码,进而使程序增加可维护性

面向对象未必是先进的,编程模式要根据需求而定,避免脱裤子放屁,近年流行的函数式编程就是与面向对象相斥的,在函数式编程中减少使用保存状态的‘对象’

7docker

8持续集成

前端表格1000w行数据流畅渲染

deepseek —— 前端

一、技术深度扩展能力
核心原理掌握

深入解释DOM树渲染机制(参考论文提及的节点树形结构抽象能力)

阐述AJAX异步通信的底层实现(如XHR对象与Fetch API差异)

展示CSS渲染层合成原理(如重排/重绘优化策略)

性能优化体系

优化维度 具体措施 考核要点
网络层面 HTTP/2协议应用、CDN加速策略 减少RTT时间
资源加载 按需加载、Tree Shaking 首屏加载时间优化
代码执行 Web Workers多线程优化 主线程阻塞规避

二、工程化实践能力
架构设计思维

展现B/S架构系统设计经验(参考论文的三层架构设计)

演示模块化开发能力(如Web Components应用)

数据库优化方案(MySQL索引策略、NoSQL选型)

质量保障体系

单元测试覆盖率(Jest/Mocha)

E2E测试实施(Cypress/Puppeteer)

持续集成部署(Jenkins/Travis CI)

三、软性能力表现
沟通演示技巧

使用语音模拟系统进行技术方案阐述(参考论文的语音面试模块)

白板编码时标注关键路径时间复杂度

职业形象管理

VR试衣系统准备的职业着装认知(参考论文的虚拟试衣功能)

简历制作突出开源项目贡献(GitHub Star/Fork数据)

四、前沿技术敏感度
新兴技术栈

WebAssembly在性能敏感场景的应用

Serverless架构的BFF层实践

WebGL/WebXR三维交互开发经验

标准演进跟踪

CSS Houdini底层API实践

ECMAScript提案阶段特性预研

W3C新规范解读(如WebGPU)

建议候选人准备3-5个典型优化案例,量化展示性能提升指标(如首屏加载从2.1s优化至780ms),并携带移动端适配、跨平台框架(Electron/Flutter)等扩展能力的实证材料。

hexo d

Error: spawn git ENOENT
hexo s
cannot GET

自定义js

放在/themes/next/source/js/, 官网说的根路径scripts文件夹并不行

配置依赖库。

  • gcc(GNU Compiler Collection)

  • pcre依赖库

  • zlib依赖库

  • SSL依赖库

1
yum install gcc pcre-devel zlib zlib-devel openssl openssl-devel

安装NGINX

官网下载地址: http://nginx.org/en/download.html

—> 解压

1
tar -zxvf nginx-1.14.2.tar.gz

z(gz格式)j(bz2格式)x(解压)v(显示所有过程)f(使用档案名称命名)
—> configure
进入解压目录下
1
./configure

—>编译
1
2
make
make install

启动和停止

1
2
3
4
/nginx/sbin/nginx
/nginx/sbin/nginx -s stop

/nginx/sbin/nginx -s reload

—>查看进程

1
ps -ef | grep nginx

访问index.html页面,404

—>检查端口
1
telnet 127.0.0.1 8080

显示Connection closed by foreign host

—>查看和关闭防火墙

1
2
firewall-cmd --state
systemctl stop firewalld.service

—>服务开机启动
1
2
systemctl disable firewalld.service
systemctl enable nginx.service

手动下载源码,并编译安装的是没有nginx.service的,手动创建
1
vi /lib/systemd/system/nginx.service

编辑内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=nginx
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true

[Install]
WantedBy=multi-user.target

web服务及反向代理

/usr/local/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
 server {
listen 8080; #nginx服务器的代理端口
server_name _;
location / {
proxy_pass http://172.18.78.14:6080; #需要反向代理的IP地址+端口
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}

负载均衡

juejin

代理服务

ngx_http_proxy_connect_module

查看RedHat版本

1
cat /etc/redhat-release

1
yum install nginx

error:cannot find a valid baseurl

ping
connect:network is unreachable

ls /etc/sysconfig/network-scripts/

ifcfg-enp0s3 ifcfg-lo ….
其中ifcfg-lo是localhost配置
将第一个配置文件重命名

1
mv ifcfg-enp0s3 ifcfg-eth0

查看网卡
1
ip add

显示了lo和enp0s3
修改默认网卡设置
编辑/etc/default/grub文件,在GRUB_CMD_LINE_LINUX=””项中,插入”net.ifnames=0 biosdevname=0”
修改ip配置
注意
1
DEVICE=eth0

1
ONBOOT=no //设置开机启动网卡,将值修改为“yes”

1
BOOTPROTO=static //默认为no,修改为static

重启
service network restart

reboot

直播原理

协议

H5 video标签

直播流的制作

Nginx+ffmpeg

安装

下载RTMP模块并重编译nginx

官方源代码https://github.com/arut/nginx-rtmp-module.git

配置

1
./configure --prefix=/usr/local/nginx --add-module=~/nginx-rtmp-module --with-http_ssl_module

编译并安装

1
make && make install

nginx reload后报“open() “/usr/local/nginx/logs/nginx.pid” failed”,执行下面的命令,指定nginx的configure路径

1
./nginx -c /usr/local/nginx/conf/nginx.conf

编译通过nginx可访问,还没完,配置nginx.conf,在末尾添加rtmp模块配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
rtmp{
server {
listen 1935;
chunk_size 4000;

# RTMP 直播流配置
application rtmplive {
live on;
max_connections 1024;
}

# hls 直播流配置
application hls{
live on;
hls on;
hls_path /usr/local/userdata/live;
hls_fragment 5s;
}
}
}

hls_path是分割文件存储路径

在http模块中添加服务路径

1
2
3
4
5
6
7
8
9
10
11
12
http {
server {
location /hls{
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /usr/local/userdata/live;
add_header Cache-Control no-cache
}
}
}

安装流媒体文件转换工具ffmpeg

官方release:http://www.ffmpeg.org/download.html#releases
解压

1
tar jxvf ffmpeg-4.1.3.tar.bz2

执行configure报“yasm/nasm not found or too old. Use —disable-x86asm for a crippled build”,ffmpeg默认的编译器未安装,需使用—disable-x86asm

将本地视频文件通过nginx推流

本地视频文件kon.mp4

推送RTMP流

1
ffmpeg -re -i test.mp4 -vcodec libx264 -acodec acc -f flv rtmp://127.0.0.1:1935/rtmplive/rtmp

推送HLS流

1
ffmpeg -re -i test.mp4 -vcodec libx264 -acodec acc -f flv rtmp://127.0.0.1:1935/hls/stream

这里有一个关于编码器的坑

vcodec acodec 分别指明了视频、音频的编码器,其实这里可以用copy也就是不需要转码,从官方GitHub下载安装的ffmpeg是没有libx264的编码器的,故而在执行上述推流命令时报unkown encoder libx264

而通用的h.264视频编码器是叫x264:https://www.videolan.org/developers/x264.html

安装x264需要将类库提供给外部应用程序(如ffmpeg)

1
2
3
./configure –enable-shared 

make && make install

配置编译的时候最好有—prefix指明安装路径,否则一般默认到/usr/local/lib路径下
需要将/usr/local/lib路径加入共享库配置文件/etc/ld.so.conf中
1
2
3
echo "/usr/local/lib" >> /etc/ld.so.conf

ldconfig

编译安装含外部解码器的FFmpeg
1
2
3
./configure --enable-static --enable-gpl --enable-libx264 --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib

make && make install

概念

(略)

方法

1
2
3
4
5
6
7
8
keys *
redis-cli
set key value
get key
del key
hget key fieldname
hget all
flushall

redis作为认证token数据库

ticket=(accountid, token)

centos安装Redis

参考:Linux 平台将 Redis 设置为服务并开机自启动

服务部署好之后,若无法远程访问

首先检查防火墙设置

1
2
3
systemctl status firewalld # 查看防火墙状态
firewall-cmd --zone= public --query-port=6379/tcp # 查询防火墙端口状态
firewall-cmd --zone=public --add-port=6379/tcp --permanent # 永久开发端口及协议

确认是否绑定服务主机ip 参考 处理CentOS 7.2 x64端口不通的问题

远程访问

修改/etc/redis/redis.conf

1
2
# 注释掉或将其改为服务器静态ip
# bind 127.0.0.1 ::1

DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients…..

受保护的模式,改为no

1
protected-mode no

ControlValueAccessor接口

定义一个接口,该接口充当 Angular 表单 API 和 DOM 中的原生元素之间的桥梁。 实现此接口以创建与 Angular 表单集成的自定义表单控件指令, from Angular Doc

删除线小注:见下文“关于原生控件”

1
2
3
4
5
6
7
// angular2/packages/forms/src/directives/control_value_accessor.ts 
export interface ControlValueAccessor {
writeValue(obj: any): void;
registerOnChange(fn: any): void;
registerOnTouched(fn: any): void;
setDisabledState?(isDisabled: boolean): void;
}

  • writeValue(obj: any):该方法用于将宿主传入的新值写入自定义组件的视图或 DOM 属性。
  • registerOnChange(fn: any):设置当控件接收到来自宿主的 change 事件后,调用的函数 一般将其对接到input控件的ngOnChange响应事件上
  • registerOnTouched(fn: any):设置当控件接收到 touched 事件后,调用的函数
  • setDisabledState?(isDisabled: boolean):当控件状态变成 DISABLED 或从 DISABLED 状态变化成 ENABLE 状态时,会调用该函数。该函数会根据参数值,启用或禁用指定的 DOM 元素。

自定义的组件实现ControlValueAccessor接口,可以像原生表单控件一样使用ngModel绑定值
stackbliz demo

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 {Component, OnInit, forwardRef, Input} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';

@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
export class CustomInputComponent implements ControlValueAccessor {
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
input: string;
writeValue(input: string) {
this.input = input;
}
}

关于原生控件
不直接操作自定义组件 而在组件内逻辑赋值绑定值input 控件会响应 但是自定义组件的ngOnChange不会响应 即父组件绑定ngModel的值并不更新, 即逻辑赋值input还需要手动调用registerOnChange注入的外部响应事件,也就是上述范例代码的onChange变量

参考ng-zorro的select控件源码发现 在实现ControlValueAccessor的自定义组件模板中原生组件是不必要的,自定义组件可以自行管理对外暴露的ngModel的值,没有必要响应原生组件的特性

OnChange接口

A lifecycle hook that is called when any data-bound property of a directive changes. Define an ngOnChanges() method to handle the changes.当指令的任何一个可绑定属性发生变化时调用。 定义一个 ngOnChanges() 方法来处理这些变更。

实际上是自定义组件继承OnChange接口,实现ngOnChange方法,当自定义组件@Input的变量更新时,该方法响应,传入SimpleChanges类型参数,该参数是发生更新的变量的哈希表, 即

1
2
3
4
5
6
7
8
9
10
11
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
// TODO(issue/24571): remove '!'.
@Input() prop!: number;
@Input() data:any

ngOnChanges(changes: SimpleChanges) {
const {prop, data} = changes
// prop and data is an SimpleChange boject which contains the old and the new value...
}
}

SimpleChanges是SimpleChange类型的map
1
2
3
4
5
6
7
8
9
10
export declare class SimpleChange {
previousValue: any;
currentValue: any;
firstChange: boolean;
constructor(previousValue: any, currentValue: any, firstChange: boolean);
/**
* Check whether the new value is the first value assigned.
*/
isFirstChange(): boolean;
}

Caution! ngOnChange的底层逻辑是使用‘===’判断前后值的,因此引用类型的属性变化不会触发ngOnChange,workaround是将此类输入属性拆成基本值类型变量,或者在宿主更新该输入属性时赋予新的引用地址,亦或使用ngDoCheck

1
2
3
4
<app-voter *ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)">
</app-voter>

@Input()

@Input()可以带set、get属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Component, Input } from '@angular/core';

@Component({
selector: 'app-voter',
template: '<h3>"{{name}}"</h3>'
})
export class VoterComponent {
private _name = '';

@Input()
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}

get name(): string { return this._name; }
}

在set、get时进行格式化等操作。

ngOnChanges()生命周期钩子是专门用来监听@Input入参的,非传入参数不会触发ngOnChanges()方法

@Output()

子组件发射事件

1
2
3
4
5
@Output() voted = new EventEmitter<boolean>();

vote(agreed: boolean) {
this.voted.emit(agreed);
}

模板标记子组件

1
2
3
4
5
6
7
8
<h3>Countdown to Liftoff (via local variable)</h3>

<button (click)="timer.start()">Start</button>
<button (click)="timer.stop()">Stop</button>

<div class="seconds">{{timer.seconds}}</div>

<app-countdown-timer #timer></app-countdown-timer>

timer 作为父组件的变量,标记在子组件标签中,用以代表子组件,即可使父组件获取子组件的引用,进而可以调用子组件公用方法

该方法有局限性在于,父组件-子组件的连接必须全部在父组件的模板中进行。父组件本身的代码对子组件没有访问权。

@ViewChild()

1
2
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;

使用@ViewChild()装饰器将CountdownTimerComponent实例注入到本组件,记为私有变量timerComponent

  • 特别注意
    被注入的组件只有在 Angular 显示了父组件视图之后才能访问
    下面的语法很关键

    1
    2
    3
    4
    5
    ngAfterViewInit() {
    // wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.timerComponent.init(), 0);
    }

    父组件要及时调用子组件方法进行初始化,须实现AfterViewInit生命周期钩子,且用定时器异步操作。
    否则报异常

    1
    ExpressionChangedAfterItHasBeenCheckedError

    引入第三者通信

    这类方法包括注入服务,借助相同父组件以及借助发布订阅可观察对象等,此处略。