







This is the summary of a very long blog post,
Use a <!-- truncate --> comment to limit blog post size in the list view.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
花了一周时间了解 nginx 相关的知识,主要内容有:基础知识: Nginx 的快速部署安装、模块、基础配置语法,Nginx 的日志输出、Nginx 默认配置模块、Nginx 做为 http 代理服务, 介绍代理服务的类型,正向反向代理配置,nginx 作为的应用层负载均衡服务的各种应用,hash 负载均衡策略, Nginx 缓存,高级知识: Nginx 常用配置模块, rewirte 的配置语法和规则,配置基于指定地域的规则访问, geoip 模块、https 的实现原理,配置 nginx 的 https 服务, secure_link_module 的防盗链实现,讲解,讲解 Lua 的开发语法、配合 Nginx 实现高效的认证系统和其他场景。
docker 启动
docker run -d -p 8088:80 --name nginx_8088 nginx_80:latest /sbin/init
yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim
cd /opt/ mkdir app download logs work backup
确定 nginx 源
cd /etc/yum.repos.d
vim nginx.repo
添加:
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
安装
yum list | grep nginx
yum install nginx
查看版本
nginx -v
查看 nginx 编译的参数
nginx -V
nginx -c /etc/nginx/nginx.conf
重启 nginx 服务
systemctl restart nginx.service
柔和重启
nginx -s reload -c /etc/nginx/nginx.conf
检查配置文件
nginx -t -c /etc/nginx/nginx.conf
nginx 是一个开源且高性能、可靠的 http 中间件,代理服务。Nginx(发音同 engine x)是一个 Web 服务器,也可以用作反向代理,负载平衡器和 HTTP 缓存。该软件由 Igor Sysoev 创建,并于 2004 年首次公开发布。同名公司成立于 2011 年,以提供支持。
多个描述符的 i/o 操作都能在一个线程内并发交替地顺序完成,这就教 i/o 多路复用,这里的” 复用 “指的是复用同一个线程。i/o 多路复用的实现方式为:select、poll、epoll
什么是 select
epoll 模型
是一种 cpu 核心和 nginx 工作进程绑定方式,把每个 worker 进程固定在一个 cpu 上执行,减少切换 cpu 的 cache miss,获得更好的性能。
说白了就是减少 cpu 切换所损耗的性能
rpm
rpm 命令是 RPM 软件包的管理工具。rpm 原本是 Red Hat Linux 发行版专门用来管理 Linux 各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux 易于安装,升级,间接提升了 Linux 的适用度。
列出服务的安装目录
rpm -ql nginx
列出下面目录
/etc/logrotate.d/nginx
/etc/nginx
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
/etc/nginx/fastcgi_params
/etc/nginx/koi-utf
/etc/nginx/koi-win
/etc/nginx/mime.types
/etc/nginx/modules
/etc/nginx/nginx.conf
/etc/nginx/scgi_params
/etc/nginx/uwsgi_params
/etc/nginx/win-utf
/etc/sysconfig/nginx
/etc/sysconfig/nginx-debug
/usr/lib/systemd/system/nginx-debug.service
/usr/lib/systemd/system/nginx.service
/usr/lib64/nginx
/usr/lib64/nginx/modules
/usr/libexec/initscripts/legacy-actions/nginx
/usr/libexec/initscripts/legacy-actions/nginx/check-reload
/usr/libexec/initscripts/legacy-actions/nginx/upgrade
/usr/sbin/nginx
/usr/sbin/nginx-debug
/usr/share/doc/nginx-1.12.2
/usr/share/doc/nginx-1.12.2/COPYRIGHT
/usr/share/man/man8/nginx.8.gz
/usr/share/nginx
/usr/share/nginx/html
/usr/share/nginx/html/50x.html
/usr/share/nginx/html/index.html
/var/cache/nginx
/var/log/nginx
目录解释
| 路径 | 类型 | 作用 |
|---|---|---|
| /etc/logrotate.d/nginx | 配置文件 | nginx 日志轮转,用于 logrotate 服务的日志切割 |
| /etc/nginx /etc/nginx/conf.d /etc/nginx/conf.d/default.conf /etc/nginx/nginx.conf | 目录、配置文件 | nginx 主配置文件 | | /etc/nginx/fastcgi_params /etc/nginx/scgi_params /etc/nginx/uwsgi_params | 配置文件 | cgi 配置相关,fastcgi 配置 | | /etc/nginx/koi-utf /etc/nginx/koi-win /etc/nginx/win-utf | 配置文件 | 编码映射转化文件 | | /etc/nginx/mime.types | 配置文件 | 设置 http 协议的 Content-Type 与扩展名对应关系 | | /etc/sysconfig/nginx /etc/sysconfig/nginx-debug /usr/lib/systemd/system/nginx-debug.service /usr/lib/systemd/system/nginx.service | 配置文件 | 用于配置出系统守护进程管理器管理方式 | | /etc/nginx/modules /usr/lib64/nginx/modules | 目录 | nginx 模块目录 | | /usr/sbin/nginx /usr/sbin/nginx-debug | 命令 | nginx 服务的启动管理的终端命令 | | /usr/share/doc/nginx-1.12.2 /usr/share/doc/nginx-1.12.2/COPYRIGHT /usr/share/man/man8/nginx.8.gz | 文件目录 | nginx 的手册和帮助文件 | | /var/cache/nginx | 目录 | nginx 的缓存目录 | | /var/log/nginx | 目录 | nginx 的日志目录 |
列出编译参数的命令
nginx -V
结果
nginx version: nginx/1.12.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
参数解释
| 路径 | 类型 |
|---|
| --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock | 安装目的目录或路径 | | --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp | 执行对应模块时,nginx 所保留的临时性文件 | | --user=nginx --group=nginx | 设定 nginx 进程启动的用户和组用户 | | --with-cc-opt=parameters | 设置额外的参数将被添加到 CFLAGS 变量 | | --with-ld-opt=parameters | 设置附加的参数,链接系统库 |
展示每次请求的请求头
curl -v http://www.baidu.com
结果
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* About to connect() to www.baidu.com port 80 (#0)
* Trying 61.135.169.121...
* Connected to www.baidu.com (61.135.169.121) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: bfe/1.0.8.18
< Date: Thu, 30 Nov 2017 02:14:02 GMT
< Content-Type: text/html
< Content-Length: 2381
< Last-Modified: Mon, 23 Jan 2017 13:27:32 GMT
< Connection: Keep-Alive
< ETag: "588604c4-94d"
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Pragma: no-cache
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
< Accept-Ranges: bytes
<
{ [data not shown]
100 2381 100 2381 0 0 88266 0 --:--:-- --:--:-- --:--:-- 91576
* Connection #0 to host www.baidu.com left intact
格式 *
syntax: log_format name [escape=default | json] string...;
default: log_format combined "...";
context:http
nginx 配置的内容
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
变量类型
default.conf
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
nginx 开启的模块
--with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module
| 编译选项 | 作用 |
|---|---|
| --with-http_stub_status_module | nginx 的客户端状态 |
| --with-http_random_index_module | 目录中选择一个随机主页 |
| --with-http_sub_module | http 内容替换 |
| --limit_conn_module | 连接频率限制 |
| --limit_req_module | 请求频率限制 |
| http_access_module | 基于 ip 的访问控制 |
| http_auth_basic_module | 基于用户的信任登录 |
配置语法
syntax: stub_status;
default:-
context:server, location
在 default.conf 中添加:
# my config
location /mystatus {
stub_status;
}
检查和重新启动配置
nginx -tc /etc/nginx/nginx.conf
重启服务
nginx -s reload -c /etc/nginx/nginx.conf
检查效果 输入:http://127.0.0.1:8088/mystatus
表示 nginx 的活跃连接数;握手的总次数、处理连接数;读、写、等待个数
配置语法
syntax: random_index on | off;
default:random_index off;
context:location
在 default.conf 中将下面的配置:
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
改为:
location / {
root /usr/share/nginx/html;
random_index on;
#index index.html index.htm;
}
重启后多刷几次网页:
主页出现不同了。
配置语法
syntax: sub_filter string replacement;
default:-
context:http,server,location
syntax: sub_filter_last_modified on | off (重要用户缓存)
default:sub_filter_last_modified off;
context:http,server,location
syntax: sub_filter_once on | off
default:sub_filter_once on;
context:http,server,location
是否只替换一次
压力测试
ab -n 50 -c 20 http://127.0.0.1/index.html
-n 表示请求次数 -c 表示并发数
配置语法
连接限制
syntax: limit_conn_zone key zone=name:size;
default:-
context:http
syntax:limit_conn zone number;
default:-
context:http, server, location
请求限制
syntax: limit_req_zone key zone=name:size rate=rate;
default:-
context:http
syntax: limit_req_zone name [burst=number] [nodelay];
default:-
context:http,server,location
在 default.conf 中将下面的配置:
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# my config
location /mystatus {
stub_status;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
改为:
limit_conn_zone $binary_remote_addr zone=conn_zone:1m;
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
#limit_conn conn_zone 1;
limit_req zone=req_zone;
# limit_req zone=req_zone burst nodelay;
# limit_req zone=req_zone burst nodelay;
index index.html index.htm;
}
# my config
location /mystatus {
stub_status;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
压测结果
限制前
限制后
配置语法
syntax: allow address | CIDR | unix: | all
default:-
context:http,server,location,limit_except
syntax:deny address | CIDR | unix: | all
default:-
context:http, server, location ,limit_except
测试 配置如下
location ~ ^/admin.html {
root /opt/app/code;
deny all;
index index.html index.htm;
}
结果
http_x_forwarded_for
http_x_forwarded_for = client ip, proxy(1)ip,proxy(2)ip,...
解决方法
配置语法
syntax: auth_basic string | off;
default:-
context:http,server,location,limit_except
syntax:auth_basic_user_file file;
default:-
context:http, server, location ,limit_except
生成 password 文件
htpasswd -c ./auth_conf wujunqi
修改 conf 文件
location ~ ^/admin.html {
root /opt/app/code;
auth_basic "please input you user name and passwd";
auth_basic_user_file /etc/nginx/auth_conf;
index index.html index.htm;
}
测试
定义
非服务器动态生成的文件
syntax: sendfile on | off;
default:sendfile off
context:http,server,location,if in location
注
--with-file-aio 异步文件读取
作用:sendfile 开启的情况下,提高网络包的传输效率 (等待,一次传输)
syntax:tcp_nopush on | off
default:tcp_nopush off
context:http, server, location
相反的
syntax:tcp_nodelay on | off
default:tcp_nodelay on
context:http, server, location
作用 在 keepalive 连接下,提高网络包的传输实时性
作用
压缩传输
syntax:gzip on | off
default:gzip off
context:http, server, if in location
syntax:gzip_comp_level level;
default:gzip_comp_level 1;
context:http, server, location
http 协议定义的缓存机制(如:expires,cache-control 等)
检测过期机制
| 作用 | 请求头 |
|---|---|
| 检验是否过期 | expires, cache-control (max-age) |
| 协议中 Etag 头信息校验 | etag |
| last-modified 头信息校验 | last-modified |
浏览器请求服务器过程(缓存版本)
添加 cache-control、expires 头
syntax: expires [modified] time;
expires epoch | max | off;
default: expires off;
context:http, server, location
配置例子
为什么浏览器禁止跨域访问
不安全,容易出现 CSRF 攻击
syntax: add_header name value [always]
default: -
context:http, server, location, if in location
添加请求头:Access-Control-Allow-Origin
配置截图
目的
防止资源被盗用
防盗链设置思路
首要方式:区别哪些请求是非正常的用户请求
syntax: valid_referers none | blocked | server_names | string...;
default: -
context:server, location
配置截图
none: 表示如果没带 refer blocked: 代表不是标准的 http 写过过来的
一个命令
curl -e "http://www.baidu.com" -I http://116.62.103.228/wei.png
-e: 表示 refer -i: 表示只显示请求头
代理 - 代为办理(代理理财、代理收货等)
代理服务
区别在于代理的对象不一样
正向代理
反向代理
syntax: proxy_pass URL;
default: -
context:location, if in location, limit_except
url 一般为:
想访问 8080,只能访问到 80,通过 80 然后通过反向代理可以访问到 8080
116.62.103.228 的配置如下(其实和反向代理配置参不多)
客户端配置
syntax: proxy_buffering on | off
default: proxy_buffering on
context:location,http,server
扩展
syntax: proxy_redirect default;proxy_redirect off;proxy_redirect redirect replacement;
default: proxy_redirect default;
context:location,http,server
syntax: proxy_set_header field value;
default: proxy_set_header host $proxy_host
proxy_set_header connection close;
context:location,http,server
syntax: proxy_connect_timeout time;
default: proxy_connect_timeout 60s;
context:location,http,server
扩展
nginx 负载均衡
GSLB 是英文 Global Server Load Balance 的缩写,意思是全局负载均衡。作用:实现在广域网(包括互联网)上不同地域的服务器间的流量调配,保证使用最佳的服务器服务离自己最近的客户,从而确保访问质量。
负载均衡(Server Load Balancer,简称 SLB)是一种网络负载均衡服务,针对阿里云弹性计算平台而设计,在系统架构、系统安全及性能,扩展,兼容性设计上都充分考虑了弹性计算平台云服务器使用特点和特定的业务场景。
在 iso 模型中的传输层(包的转发)
在应用层实现
syntax: upstream name{...}
default: -
context:http
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.exmple.com:8080 backup;
server backup2.example.com:8080 backup;
}
| 字段 | 作用 |
|---|---|
| down | 当前的 server 暂时不参与负载均衡 |
| backup | 预留的备份服务器 |
| max_fails | 允许请求失败的次数 |
| fail_timeout | 经过 max_fails 失败后,服务暂停的时间 |
| max_conns | 限制最大的接收的连接数 |
| 字段 | 作用 |
|---|---|
| 轮询 | 按时间顺序逐一分配到不同的后端服务器 |
| 加权轮询 | weight 值越大,分配到的访问几率越高 |
| ip_hash | 每个请求按访问 ip 的 hash 结果分配,这样来自同一个 ip 的固定访问一个后端服务器 |
| least_conn | 最少链接数,那个机器连接数少就分发 |
| url_hash | 按照访问的 url 的 hash 结果来分配请求,是每个 url 定向到同一个后端服务器 |
| hash 关键值 | hash 自定义的 key |
url_hash
syntax: hash key [consistent];
default:-
context:upstream
this directive appeared in version 1.7.2
配置截图
Syntax: proxy_cache_path path [levels=levels]
Default:-
context:http
开关
syntax: proxy_cache zone | off;
default:proxy_cache off;
context:http, sercer, location
过期周期
syntax: proxy_cache_valid[code] time;
default:-
context:http, sercer, location
缓存的维度
syntax: proxy_cache_key string;
default: proxy_cache_key $scheme$proxy_host$request_uri;
context:http,server,location
配置截图
level:目录分级 inactive: 不活跃就清理
syntax : proxy_no_cache string ...;
default: -;
context:http,server,location
配置截图
syntax : slice size
default: slice o
context:http,server,location
http_slice_module
优势
缺点
通过中间件将动态请求和静态请求分离
为什么
场景
syntax: rewrite regex replacement [flag];
default:-
context:server, location,if
维护页面实例
rewrite ^(.*)$ /pages/maintain.html break;
| 字段 | 作用 |
|---|---|
| last | 停止 rewrite 检测 |
| break | 停止 rewrite 检测 |
| redirect | 返回 302 临时重定向,地址栏会显示跳转后的地址 |
| permanent | 返回 301 永久重定向,地址栏会显示跳转后的地址(浏览器下次直接访问重定向后的地址 |
syntax: secure_link expression
default:-
context:server, location,server
syntax: secure_link_md5 expression
default:-
context:server, location,http
图示
配置例子
基于 ip 地址匹配 MaxMind GeoIp 二进制文件,读取 ip 所在地域信息
yum install nginx-module-geoip
配置截图
对传输内容进行加密以及身份验证。
如何解决
安装 openssl 和 http_ssl_module 模块
#openssl version
openSSL 1.0.1e-fips 11 feb 2013
#nginx -V
--with-http_ssl_module
步骤
openssl genrsa -idea -out jesonc.key 1024
结果
openssl req -new -key jesonc.key -out jesonc.csr
结果
openssl x509 -req -days 3650 -in jesonc.csr -signkey jesonc.key -out jesonc.crt
结果
docker run -d -p 443:443 --name nginx_443 nginx_443:latest /sbin/init
nginx -c /etc/nginx/nginx.conf
server {
listen 443;
server_name localhost;
ssl on;
ssl_certificate /etc/nginx/ssl_key/jesonc.crt;
ssl_certificate_key /etc/nginx/ssl_key/jesonc.key;
location / {
root /usr/share/nginx/html;
#limit_conn conn_zone 1;
#limit_req zone=req_zone;
# limit_req zone=req_zone burst nodelay;
# limit_req zone=req_zone burst nodelay;
index index.html index.htm;
}
# my config
location /mystatus {
stub_status;
}
#error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
项目开发中经常需要用到元素大小,网页高度,视口高度,各种距离等等,本文总结了获取各种宽高、距离的方法。
祭出这几张神图,简单明了又清晰😄
注意:这些方法都不考虑混杂模式,请确保你的文档在标准模式下(填写了正确的 doctype)否则获取的值会不准。
代码说明
1. 火狐不兼容 document.body,所以使用 document.documentElement
2. 理论上没有滚动条时 scrollHeight 和 clientHeight 相同,事实上各种浏览器有不同的处理,所以最保险的方法是取这俩中最大者。
function getPagearea(){
return {
width: Math.max(document.documentElement.scrollWidth,
document.documentElement.clientWidth),
height: Math.max(document.documentElement.scrollHeight,
document.documentElement.clientHeight)
}
}
PS:jq 的话 $(document).height(); $(document).width();
代码说明
1. 同上,火狐不兼容 document.body,所以使用 document.documentElement
function getViewport() {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
}
}
PS:jq 的话 $(window).height(); $(window).width();
function getElementTop(el) {
let actualTop = el.offsetTop;
let current = el.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
PS:jq 的话 jq 对象. offset().top jq 对象. offset().left
使用 el.getBoundingClientRect 方法
getBoundingClientRect 方法返回元素的大小及其相对于视口的位置。
代码说明
同 1,火狐不兼容 document.body,所以使用 document.documentElement
function getScrollTop() {
let doc = document;
return Math.max(doc.body.scrollTop, doc.documentElement.scrollTop)
};
function getScrollLeft() {
let doc = document;
return Math.max(doc.body.scrollLeft, doc.documentElement.scrollLeft)
};
这种主要是读取 event 对象中的值,具体看下图比较清晰。
这个例子使用到了上面的方法
document.onscroll = () => {
let dom = document.getElementById('box');
let top = getElementTop(dom); // 元素距页面高度
let scrollTop = getScrollTop(); // 获取滚动条高度
let viewPortHeight = getViewport().height; // 获取视口宽高
if (top > scrollTop && top <= scrollTop + viewPortHeight) {
console.log('元素出现')
}
}
// 写法2:配合getBoundingClientRect判断
document.onscroll = () => {
let dom = document.getElementById('box2');
let rectTop = dom.getBoundingClientRect().top;
let viewPortHeight = getViewport().height;
if (rectTop > 0 && rectTop < viewPortHeight) {
console.log('元素出现')
}
}
// 用jq的话
document.onscroll = () => {
let $dom = $('#box');
let top = $dom.offset().top;
let scrollTop = $(window).scrollTop();
let viewPortHeight = $(window).height();
if (top > scrollTop && top <= scrollTop + viewPort.height) {
console.log('元素出现')
}
}
Flex(Flexible Box)布局 称为 "弹性布局",可以为网页的布局提供最大的灵活性,取代了往常的 浮动(float) 布局,并且任何一个容器都可以设置 Flex 布局。 注:设置 Flex 布局后,子元素的 Float 布局将失效
容器: 如果给一个标签添加
display:flex;,那么这个标签就是一个容器 项目: 在容器中的直接子元素叫项目(一定是 直接 子元素) 主轴: 项目的默认排序方向就是主轴(默认横向排列,一个容器可以有多根主轴) 交叉轴: 和主轴垂直的那个轴,就是交叉轴
Flex-direction | Flex-wrap | Flex-flow | justify-content | align-items | align-content
flex-direction:row | row-reverse | column | column-reverse;
- row(默认值):主轴为水平方向,起点在左端
- row-reverse:主轴为水平方向,起点在右端
- column:主轴为垂直方向,起点在上端
- column-reverse:主轴为垂直方向,起点在下端
flex-wrap:nowrap | wrap | wrap-reverse;
- norwrap(默认):不换行
- wrap:换行,第一行在上方
- wrap-reverse:换行,第一行在下方
3.Flex-flow(属性是 flex-direction 和 flex-wrap 的简写形式)
flex-flow: flex-direction || flex-wrap;
4.justify-content(处理项目外的 富余空间)
justify-content:flex-start | flex-end | center | space-between | space-around;
- flex-start;(默认值),项目左对齐
- flex-end:项目右对齐
- center: 项目居中
- space-between:项目两端对齐,项目之间的间隔都相等
- space-around:每个项目两侧的间隔相等
5.align-items(属性决定项目在交叉轴上如何对齐)
align-items:stretch | flex-start | flex-end | center | baseline;
- stretch(默认值):如果项目未设置高度或设为 auto,将占满整个容器的高度
- flex-start:交叉轴的起点对齐
- flex-end:交叉轴的终点对齐
- center:交叉轴的中点对齐
- baseline:项目的第一行文字的基线对齐
6.align-content(属性决定了多根主轴的对齐方式)
align-content:stretch | flex-start | flex-end | center | space-between | space-around;
- stretch(默认值):轴线占满整个交叉轴
- flex-start:与交叉轴的起点对齐
- flex-end:与交叉轴的终点对齐
- center:与交叉轴的中点对齐
- space-between:与交叉轴两端对齐,轴线之间的间隔平均分
- space-around:每根轴线两侧的间隔都相等
order | flex-grow |flex-shrink | flex-basis | flex | align-self
- order(属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0)
order: <integer>;
- flex-grow(属性定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大)
flex-grow:<number>;
- flex-shrink(属性定义了项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小)
flex-shrink: <number>;
- flex-basis(属性定义了在分配多余空间之前,项目占据的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即项目的本来大小, 也可以设置
xx px, 项目将占据固定空间)
flex-basis: <length> | auto;
- flex(属性是 flex-grow, flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。后两个属性可选)
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ];
- align-self(属性允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch)
align-self: auto | stretch | flex-start | flex-end | center | baseline;
正则表达式一直是困扰很多程序员的一门技术,当然也包括曾经的我。大多数时候我们在开发过程中要用到某些正则表达式的时候,都会打开谷歌或百度直接搜索然后拷贝粘贴。当下一次再遇到相同问题的时候,同样的场景又再来一遍。作为一门用途很广的技术,我相信深入理解正则表达式并能融会贯通是值得的。所以,希望这篇文章能帮助大家理清思路,搞懂正则表达式各种符号之间的内在联系,形成知识体系,当下次再遇到正则表达式的时候可以不借助搜索引擎,自己解决。
正则表达式 (Regular Expression) 其实就是一门工具,目的是为了字符串模式匹配,从而实现搜索和替换功能。它起源于上个 20 世纪 50 年代科学家在数学领域做的一些研究工作,后来才被引入到计算机领域中。从它的命名我们可以知道,它是一种用来描述规则的表达式。而它的底层原理也十分简单,就是使用状态机的思想进行模式匹配。大家可以利用 regexper.com 这个工具很好地可视化自己写的正则表达式:
如/\d\w+/这个正则生成的状态机图:
对于具体的算法实现,大家如果感兴趣可以阅读《算法导论》。
我们学习一个系统化的知识,一定要从其基础构成来了解。正则表达式的基本组成元素可以分为:字符和元字符。字符很好理解,就是基础的计算机字符编码,通常正则表达式里面使用的就是数字、英文字母。而元字符,也被称为特殊字符,是一些用来表示特殊语义的字符。如 ^ 表示非,| 表示或等。利用这些元字符,才能构造出强大的表达式模式 (pattern)。接下来,我们就来从这些基本单位出发,来学习一下如何构建正则表达式。
最简单的正则表达式可以由简单的数字和字母组成,没有特殊的语义,纯粹就是一一对应的关系。如想在'apple'这个单词里找到‘a'这个字符,就直接用/a/这个正则就可以了。
但是如果想要匹配特殊字符的话,就得请出我们第一个元字符 \, 它是转义字符字符,顾名思义,就是让其后续的字符失去其本来的含义。举个例子:
我想匹配*这个符号,由于*这个符号本身是个特殊字符,所以我要利用转义元字符\来让它失去其本来的含义:
如果本来这个字符不是特殊字符,使用转义符号就会让它拥有特殊的含义。我们常常需要匹配一些特殊字符,比如空格,制表符,回车,换行等, 而这些就需要我们使用转义字符来匹配。为了便于记忆,我整理了下面这个表格,并附上记忆方式:
| 特殊字符 | 正则表达式 | 记忆方式 |
|---|---|---|
| 换行符 | \n | new line |
| 换页符 | \f | form feed |
| 回车符 | \r | return |
| 空白符 | \s | space |
| 制表符 | \t | tab |
| 垂直制表符 | \v | vertical tab |
| 回退符 | [\b] | backspace, 之所以使用 [] 符号是避免和 \ b 重复 |
单个字符的映射关系是一对一的,即正则表达式的被用来筛选匹配的字符只有一个。而这显然是不够的,只要引入集合区间和通配符的方式就可以实现一对多的匹配了。
在正则表达式里,集合的定义方式是使用中括号[和]。如/[123]/这个正则就能同时匹配 1,2,3 三个字符。那如果我想匹配所有的数字怎么办呢?从 0 写到 9 显然太过低效,所以元字符-就可以用来表示区间范围,利用/[0-9]/就能匹配所有的数字, /[a-z]/则可以匹配所有的英文小写字母。
即便有了集合和区间的定义方式,如果要同时匹配多个字符也还是要一一列举,这是低效的。所以在正则表达式里衍生了一批用来同时匹配多个字符的简便正则表达式:
| 匹配区间 | 正则表达式 | 记忆方式 |
|---|---|---|
| 除了换行符之外的任何字符 | . | 句号, 除了句子结束符 |
| 单个数字, [0-9] | \d | digit |
| 除了 [0-9] | \D | not digit |
| 包括下划线在内的单个字符,[A-Za-z0-9_] | \w | word |
| 非单字字符 | \W | not word |
| 匹配空白字符, 包括空格、制表符、换页符和换行符 | \s | space |
| 匹配非空白字符 | \S | not space |
一对一和一对多的字符匹配都讲完了。接下来,就该介绍如何同时匹配多个字符。要实现多个字符的匹配我们只要多次循环,重复使用我们的之前的正则规则就可以了。那么根据循环次数的多与少,我们可以分为 0 次,1 次,多次,特定次。
元字符?代表了匹配一个字符或 0 个字符。设想一下,如果你要匹配color和colour这两个单词,就需要同时保证u这个字符是否出现都能被匹配到。所以你的正则表达式应该是这样的:/colou?r/。
元字符*用来表示匹配 0 个字符或无数个字符。通常用来过滤某些可有可无的字符串。
元字符+适用于要匹配同个字符出现 1 次或多次的情况。
在某些情况下,我们需要匹配特定的重复次数,元字符{和}用来给重复匹配设置精确的区间范围。如'a'我想匹配 3 次, 那么我就使用/a{3}/这个正则,或者说'a'我想匹配至少两次就是用/a{2,}/这个正则。
以下是完整的语法:
- {x}: x次
- {min, max}: 介于min次到max次之间
- {min, }: 至少min次
- {0, max}: 至多max次
由于这些元字符比较抽象,且容易混淆,所以我用了联想记忆的方式编了口诀能保证在用到的时候就能回忆起来。
| 匹配规则 | 元字符 | 联想方式 |
|---|---|---|
| 0 次或 1 次 | ? | 且问, 此事有还无 |
| 0 次或无数次 | * | 宇宙洪荒, 辰宿列张:宇宙伊始,从无到有,最后星宿布满星空 |
| 1 次或无数次 | + | 一加, +1 |
| 特定次数 | {x}, {min, max} | 可以想象成一个数轴,从一个点,到一个射线再到线段。min 和 max 分别表示了左闭右闭区间的左界和右界 |
上面我们把字符的匹配都介绍完了,接着我们还需要位置边界的匹配。在长文本字符串查找过程中,我们常常需要限制查询的位置。比如我只想在单词的开头结尾查找。
单词是构成句子和文章的基本单位,一个常见的使用场景是把文章或句子中的特定单词找出来。如:
The cat scattered his food all over the room.
我想找到cat这个单词,但是如果只是使用/cat/这个正则,就会同时匹配到cat和scattered这两处文本。这时候我们就需要使用边界正则表达式\b,其中 b 是 boundary 的首字母。在正则引擎里它其实匹配的是能构成单词的字符 (\w) 和不能构成单词的字符 (\W) 中间的那个位置。
上面的例子改写成/\bcat\b/这样就能匹配到cat这个单词了。
匹配完单词,我们再来看一下一整个字符串的边界怎么匹配。元字符^用来匹配字符串的开头。而元字符$用来匹配字符串的末尾。注意的是在长文本里,如果要排除换行符的干扰,我们要使用多行模式。试着匹配I am scq000这个句子:
I am scq000. I am scq000. I am scq000.
我们可以使用/^I am scq000\.$/m这样的正则表达式,其实 m 是 multiple line 的首字母。正则里面的模式除了 m 外比较常用的还有 i 和 g。前者的意思是忽略大小写,后者的意思是找到所有符合的匹配。
最后,总结一下:
| 边界和标志 | 正则表达式 | 记忆方式 |
|---|---|---|
| 单词边界 | \b | boundary |
| 非单词边界 | \B | not boundary |
| 字符串开头 | ^ | 小头尖尖那么大个 |
| 字符串结尾 | $ | 终结者,美国科幻电影,美元符 $ |
| 多行模式 | m 标志 | multiple of lines |
| 忽略大小写 | i 标志 | ignore case, case-insensitive |
| 全局模式 | g 标志 | global |
字符匹配我们介绍的差不多了,更加高级的用法就得用到子表达式了。通过嵌套递归和自身引用可以让正则发挥更强大的功能。
从简单到复杂的正则表达式演变通常要采用分组、回溯引用和逻辑处理的思想。利用这三种规则,可以推演出无限复杂的正则表达式。
其中分组体现在:所有以(和)元字符所包含的正则表达式被分为一组,每一个分组都是一个子表达式,它也是构成高级正则表达式的基础。如果只是使用简单的(regex)匹配语法本质上和不分组是一样的,如果要发挥它强大的作用,往往要结合回溯引用的方式。
所谓回溯引用(backreference)指的是模式的后面部分引用前面已经匹配到的子字符串。你可以把它想象成是变量,回溯引用的语法像\1,\2,...., 其中\1表示引用的第一个子表达式,\2表示引用的第二个子表达式,以此类推。而\0则表示整个表达式。
假设现在要在下面这个文本里匹配两个连续相同的单词,你要怎么做呢?
Hello what what is the first thing, and I am am scq000.
利用回溯引用,我们可以很容易地写出\b(\w+)\s\1这样的正则。
回溯引用在替换字符串中十分常用,语法上有些许区别,用$1,$2... 来引用要被替换的字符串。下面以 js 代码作演示:
var str = 'abc abc 123'; str.replace(/(ab)c/g,'$1g'); // 得到结果 'abg abg 123'
如果我们不想子表达式被引用,可以使用非捕获正则(?:regex)这样就可以避免浪费内存。
var str = 'scq000'. str.replace(/(scq00)(?:0)/, '$1,$2') // 返回scq00,$2 // 由于使用了非捕获正则,所以第二个引用没有值,这里直接替换为$2
有时,我们需要限制回溯引用的适用范围。那么通过前向查找和后向查找就可以达到这个目的。
前向查找 (lookahead) 是用来限制后缀的。凡是以(?=regex)包含的子表达式在匹配过程中都会用来限制前面的表达式的匹配。例如happy happily这两个单词,我想获得以happ开头的副词,那么就可以使用happ(?=ily)来匹配。如果我想过滤所有以happ开头的副词,那么也可以采用负前向查找的正则happ(?!ily),就会匹配到happy单词的happ前缀。
介绍完前向查找,接着我们再来介绍一下它的反向操作:后向查找 (lookbehind)。后向查找(lookbehind) 是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。举个简单的例子: apple和people都包含ple这个后缀,那么如果我只想找到apple的ple,该怎么做呢?我们可以通过限制app这个前缀,就能唯一确定ple这个单词了。
其中(?<=regex)的语法就是我们这里要介绍的后向查找。regex指代的子表达式会作为限制项进行匹配,匹配到这个子表达式后,就会继续向后查找。另外一种限制匹配是利用(?<!regex) 语法,这里称为负后向查找。与正前向查找不同的是,被指定的子表达式不能被匹配到。于是,在上面的例子中,如果想要查找apple的ple也可以这么写成/(?<!peo)ple。
需要注意的,不是每种正则实现都支持后向查找。在 javascript 中是不支持的,所以如果有用到后向查找的情况,有一个思路是将字符串进行翻转,然后再使用前向查找,作完处理后再翻转回来。看一个简单的例子:
// 比如我想替换apple的ple为ply var str = 'apple people'; str.split('').reverse().join('').replace(/elp(?=pa)/, 'ylp').split('').reverse().join('');
最后回顾一下这部分内容:
| 回溯查找 | 正则 | 记忆方式 |
|---|---|---|
| 引用 | \0,\1,\2 和 $0, $1, $2 | 转义 + 数字 |
| 非捕获组 | (?:) | 引用表达式 (()), 本身不被消费 (?), 引用 (:) |
| 前向查找 | (?=) | 引用子表达式 (()),本身不被消费 (?), 正向的查找 (=) |
| 前向负查找 | (?!) | 引用子表达式 (()),本身不被消费 (?), 负向的查找 (!) |
| 后向查找 | (?<=) | 引用子表达式 (()),本身不被消费 (?), 后向的 (<,开口往后),正的查找 (=) |
| 后向负查找 | (?<!) | 引用子表达式 (()),本身不被消费 (?), 后向的 (<,开口往后),负的查找 (!) |
计算机科学就是一门包含逻辑的科学。让我们回忆一下编程语言当中用到的三种逻辑关系,与或非。
在正则里面,默认的正则规则都是与的关系所以这里不讨论。
而非关系,分为两种情况:一种是字符匹配,另一种是子表达式匹配。在字符匹配的时候,需要使用^这个元字符。在这里要着重记忆一下:只有在[和]内部使用的^才表示非的关系。子表达式匹配的非关系就要用到前面介绍的前向负查找子表达式(?!regex)或后向负查找子表达式(?<!regex)。
或关系,通常给子表达式进行归类使用。比如,我同时匹配 a,b 两种情况就可以使用(a|b)这样的子表达式。
| 逻辑关系 | 正则元字符 | |
|---|---|---|
| 与 | 无 | |
| 非 | regex 和! | |
| 或 |
对于正则来说,符号之抽象往往让很多程序员却步。针对不好记忆的特点,我通过分类和联想的方式努力让其变得有意义。我们先从一对一的单字符,再到多对多的子字符串介绍,然后通过分组、回溯引用和逻辑处理的方式来构建高级的正则表达式。
本文由 简悦 SimpRead 转码, 原文地址 https://juejin.im/post/5cc1da82f265da036023b628
前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。
winter在他的《重学前端》课程中提到:
到现在为止,前端工程师已经成为研发体系中的重要岗位之一。可是,与此相对的是,我发现极少或者几乎没有大学的计算机专业愿意开设前端课程,更没有系统性的教学方案出现。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。
这样是一个非常真实的现状,实际上很多前端开发者都是自学甚至转行过来的,前端入门简单,学习了几个API以后上手做项目也很简单,但是这往往成为了限制自身发展的瓶颈。
只是停留在会用阶段是远远不够的,我们还需要不断探索和深入。现在市面上并不缺少学习教程,技术文章,如果盲目的学习你会发现看过以后的知识留存率会很低,而且发现没有了解到的知识越来越多,这会让人产生焦虑。
实际上,除了坚持学习的强大的自驱力,你还需要一个很简单的学习方法。那就是:建立自己的知识体系。它能帮助你更系统性的学习,同时你也时刻能知道自己哪些地方是不足的。
我会把我工作和学习中接触到的知识全部归纳到我的知识体系中,其中不仅仅包括我已经学过的,还有很多我没有来得及学习的。
这不仅仅是我的知识体系,更是我时刻提醒自己的自检清单。
下面我会把我的自检清单分享给大家,你可以按照清单上的知识检测自己还有哪些不足和提升,我也建议大家建自己的知识体系,这样工作或者学习甚至面试时,你能快速定位到知识清单中的点,如果你有哪些我没归纳到的点,欢迎在评论区告诉我。
前端工程师吃饭的家伙,深度、广度一样都不能差。
1.JavaScript规定了几种语言类型
2.JavaScript对象的底层数据结构是什么
3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
4.JavaScript中的变量在内存中的具体存储形式
5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作
6. 理解值类型和引用类型
7.null和undefined的区别
8. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
10. 出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
1. 理解原型设计模式以及JavaScript中的原型规则
2.instanceof的底层实现原理,手动实现一个instanceof
4. 实现继承的几种方式以及他们的优缺点
5. 至少说出一种开源项目 (如Node) 中应用原型继承的案例
6. 可以描述new一个对象的详细过程,手动实现一个new操作符
7. 理解es6 class构造以及继承的底层实现原理
1. 理解词法作用域和动态作用域
2. 理解JavaScript的作用域和作用域链
3. 理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题
4.this的原理以及几种不同使用场景的取值
5. 闭包的实现原理和作用,可以列举几个开发中闭包的实际应用
6. 理解堆栈溢出和内存泄漏的原理,如何防止
7. 如何处理循环的异步操作
8. 理解模块化解决的实际问题,可列举几个模块化方案并理解其中原理
1. 为何try里面放return,finally还会执行,理解其内部机制
2.JavaScript如何实现异步编程,可以详细描述EventLoop机制
3. 宏任务和微任务分别有哪些
4. 可以快速分析一个复杂的异步嵌套逻辑,并掌握分析方法
5. 使用Promise实现串行
6.Node与浏览器EventLoop的差异
7. 如何在保证页面运行流畅的情况下处理海量数据
1. 理解ECMAScript和JavaScript的关系
2. 熟练运用es5、es6提供的语法规范,
3. 熟练掌握JavaScript提供的全局对象(例如Date、Math)、全局函数(例如decodeURI、isNaN)、全局属性(例如Infinity、undefined)
4. 熟练应用map、reduce、filter 等高阶函数解决问题
5.setInterval需要注意的点,使用settimeout实现setInterval
6.JavaScript提供的正则表达式API、可以使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题
7.JavaScript异常处理的方式,统一的异常处理方案
1. 从规范的角度理解HTML,从分类和语义的角度使用标签
2. 常用页面标签的默认样式、自带属性、不同浏览器的差异、处理浏览器兼容问题的方式
3. 元信息类标签 (head、title、meta) 的使用目的和配置方法
4.HTML5离线缓存原理
5. 可以使用Canvas API、SVG等绘制高性能的动画
1.CSS盒模型,在不同浏览器的差异
2.CSS所有选择器及其优先级、使用场景,哪些可以继承,如何运用at规则
3.CSS伪类和伪元素有哪些,它们的区别和实际应用
4.HTML文档流的排版规则,CSS几种定位的规则、定位参照物、对文档流的影响,如何选择最好的定位方式,雪碧图实现原理
5. 水平垂直居中的方案、可以实现6种以上并对比它们的优缺点
6.BFC实现原理,可以解决的问题,如何创建BFC
7. 可使用CSS函数复用代码,实现特殊效果
8.PostCSS、Sass、Less的异同,以及使用配置,至少掌握一种
9.CSS模块化方案、如何配置按需加载、如何防止CSS阻塞渲染
10. 熟练使用CSS实现常见动画,如渐变、移动、旋转、缩放等等
11.CSS浏览器兼容性写法,了解不同API在不同浏览器下的兼容性情况
12. 掌握一套完整的响应式布局方案
1. 手写图片瀑布流效果
2. 使用CSS绘制几何图形(圆形、三角形、扇形、菱形等)
3. 使用纯CSS实现曲线运动(贝塞尔曲线)
4. 实现常用布局(三栏、圣杯、双飞翼、吸顶),可是说出多种方式并理解其优缺点
关于编译原理,不需要理解非常深入,但是最基本的原理和概念一定要懂,这对于学习一门编程语言非常重要
1. 理解代码到底是什么,计算机如何将代码转换为可以运行的目标程序
2. 正则表达式的匹配原理和性能优化
3. 如何将JavaScript代码解析成抽象语法树 (AST)
4.base64的编码原理
5. 几种进制的相互转换计算方法,在JavaScript中如何表示和转换
1. 理解什么是协议,了解TCP/IP网络协议族的构成,每层协议在应用程序中发挥的作用
2. 三次握手和四次挥手详细原理,为什么要使用这种机制
3. 有哪些协议是可靠,TCP有哪些手段保证可靠交付
4.DNS的作用、DNS解析的详细过程,DNS优化原理
5.CDN的作用和原理
6.HTTP请求报文和响应报文的具体组成,能理解常见请求头的含义,有几种请求方式,区别是什么
7.HTTP所有状态码的具体含义,看到异常状态码能快速定位问题
8.HTTP1.1、HTTP2.0带来的改变
9.HTTPS的加密原理,如何开启HTTPS,如何劫持HTTPS请求
10. 理解WebSocket协议的底层原理、与HTTP的区别
1. 熟练使用前端常用的设计模式编写代码,如单例模式、装饰器模式、代理模式等
2. 发布订阅模式和观察者模式的异同以及实际应用
3. 可以说出几种设计模式在开发中的实际应用,理解框架源码中对设计模式的应用
据我了解的大部分前端对这部分知识有些欠缺,甚至抵触,但是,如果突破更高的天花板,这部分知识是必不可少的,而且我亲身经历——非常有用!
1. 多种方式实现数组去重、扁平化、对比优缺点
2. 多种方式实现深拷贝、对比优缺点
3. 手写函数柯里化工具函数、并理解其应用场景和优势
4. 手写防抖和节流工具函数、并理解其内部原理和应用场景
5. 实现一个sleep函数
1. 手动实现call、apply、bind
2. 手动实现符合Promise/A+规范的Promise、手动实现async await
3. 手写一个EventEmitter实现事件发布、订阅
4. 可以说出两种实现双向绑定的方案、可以手动实现
5. 手写JSON.stringify、JSON.parse
6. 手写一个模版引擎,并能解释其中原理
7. 手写懒加载、下拉刷新、上拉加载、预加载等效果
1. 理解常见数据结构的特点,以及他们在不同场景下使用的优缺点
2. 理解数组、字符串的存储原理,并熟练应用他们解决问题
3. 理解二叉树、栈、队列、哈希表的基本结构和特点,并可以应用它解决问题
4. 了解图、堆的基本结构和使用场景
1. 可计算一个算法的时间复杂度和空间复杂度,可估计业务逻辑代码的耗时和内存消耗
2. 至少理解五种排序算法的实现原理、应用场景、优缺点,可快速说出时间、空间复杂度
3. 了解递归和循环的优缺点、应用场景、并可在开发中熟练应用
4. 可应用回溯算法、贪心算法、分治算法、动态规划等解决复杂问题
5. 前端处理海量数据的算法方案
我们需要理清语言和环境的关系:
ECMAScript描述了JavaScript语言的语法和基本对象规范
浏览器作为
JavaScript的一种运行环境,为它提供了:文档对象模型(DOM),描述处理网页内容的方法和接口、浏览器对象模型(BOM),描述与浏览器进行交互的方法和接口
Node 也是
JavaScript的一种运行环境,为它提供了操作I/O、网络等API
1. 浏览器提供的符合W3C标准的DOM操作API、浏览器差异、兼容性
2. 浏览器提供的浏览器对象模型 (BOM) 提供的所有全局API、浏览器差异、兼容性
3. 大量DOM操作、海量数据的性能优化 (合并操作、Diff、requestAnimationFrame等)
4. 浏览器海量数据存储、操作性能优化
5.DOM事件流的具体实现机制、不同浏览器的差异、事件代理
6. 前端发起网络请求的几种方式及其底层实现、可以手写原生ajax、fetch、可以熟练使用第三方库
7. 浏览器的同源策略,如何避免同源策略,几种方式的异同点以及如何选型
8. 浏览器提供的几种存储机制、优缺点、开发中正确的选择
9. 浏览器跨标签通信
1. 各浏览器使用的JavaScript引擎以及它们的异同点、如何在代码中进行区分
2. 请求数据到请求结束与服务器进行了几次交互
3. 可详细描述浏览器从输入URL到页面展现的详细过程
4. 浏览器解析HTML代码的原理,以及构建DOM树的流程
5. 浏览器如何解析CSS规则,并将其应用到DOM树上
6. 浏览器如何将解析好的带有样式的DOM树进行绘制
7. 浏览器的运行机制,如何配置资源异步同步加载
8. 浏览器回流与重绘的底层原理,引发原因,如何有效避免
9. 浏览器的垃圾回收机制,如何避免内存泄漏
10. 浏览器采用的缓存方案,如何选择和控制合适的缓存方案
1. 理解Node在应用程序中的作用,可以使用Node搭建前端运行环境、使用Node操作文件、操作数据库等等
2. 掌握一种Node开发框架,如Express,Express和Koa的区别
3. 熟练使用Node提供的API如Path、Http、Child Process等并理解其实现原理
4.Node的底层运行原理、和浏览器的异同
5.Node事件驱动、非阻塞机制的实现原理
轮子层出不穷,从原理上理解才是正道
1. 理解泛型、接口等面向对象的相关概念,TypeScript对面向对象理念的实现
2. 理解使用TypeScript的好处,掌握TypeScript基础语法
3.TypeScript的规则检测原理
4. 可以在React、Vue等框架中使用TypeScript进行开发
1.React和vue选型和优缺点、核心架构的区别
2.React中setState的执行机制,如何有效的管理状态
3.React的事件底层实现机制
4.React的虚拟DOM和Diff算法的内部实现
5.React的Fiber工作原理,解决了什么问题
6.React Router和Vue Router的底层实现原理、动态加载实现原理
7. 可熟练应用React API、生命周期等,可应用HOC、render props、Hooks等高阶用法解决问题
8. 基于React的特性和原理,可以手动实现一个简单的React
1. 熟练使用Vue的API、生命周期、钩子函数
2.MVVM框架设计理念
3.Vue双向绑定实现原理、Diff算法的内部实现
4.Vue的事件机制
5. 从template转换成真实DOM的实现机制
1. 单页面应用(SPA)的原理和优缺点,掌握一种快速开发SPA的方案
2. 理解Viewport、em、rem的原理和用法,分辨率、px、ppi、dpi、dp的区别和实际应用
3. 移动端页面适配解决方案、不同机型适配方案
4. 掌握一种JavaScript移动客户端开发技术,如React Native:可以搭建React Native开发环境,熟练进行开发,可理解React Native的运作原理,不同端适配
5. 掌握一种JavaScript PC客户端开发技术,如Electron:可搭建Electron开发环境,熟练进行开发,可理解Electron的运作原理
6. 掌握一种小程序开发框架或原生小程序开发
7. 理解多端框架的内部实现原理,至少了解一个多端框架的使用
1. 掌握React和Vue传统的跨组件通信方案,对比采用数据流管理框架的异同
2. 熟练使用Redux管理数据流,并理解其实现原理,中间件实现原理
3. 熟练使用Mobx管理数据流,并理解其实现原理,相比Redux有什么优势
4. 熟练使用Vuex管理数据流,并理解其实现原理
5. 以上数据流方案的异同和优缺点,不情况下的技术选型
1. 至少掌握一种UI组件框架,如antd design,理解其设计理念、底层实现
2. 掌握一种图表绘制框架,如Echart,理解其设计理念、底层实现,可以自己实现图表
3. 掌握一种GIS开发框架,如百度地图API
4. 掌握一种可视化开发框架,如Three.js、D3
5. 工具函数库,如lodash、underscore、moment等,理解使用的工具类或工具函数的具体实现原理
1. 熟练使用各浏览器提供的调试工具
2. 熟练使用一种代理工具实现请求代理、抓包,如charls
3. 可以使用Android、IOS模拟器进行调试,并掌握一种真机调试方案
4. 了解Vue、React等框架调试工具的使用
前端工程化:以工程化方法和工具提高开发生产效率、降低维护难度
1. 理解npm、yarn依赖包管理的原理,两者的区别
2. 可以使用npm运行自定义脚本
3. 理解Babel、ESLint、webpack等工具在项目中承担的作用
4.ESLint规则检测原理,常用的ESLint配置
5.Babel的核心原理,可以自己编写一个Babel插件
6. 可以配置一种前端代码兼容方案,如Polyfill
7.Webpack的编译原理、构建流程、热更新原理,chunk、bundle和module的区别和应用
8. 可熟练配置已有的loaders和plugins解决问题,可以自己编写loaders和plugins
1. 正向代理与反向代理的特点和实例
2. 可手动搭建一个简单的nginx服务器、
3. 熟练应用常用的nginx内置变量,掌握常用的匹配规则写法
4. 可以用nginx实现请求过滤、配置gzip、负载均衡等,并能解释其内部原理
1. 熟练掌握一种接口管理、接口mock工具的使用,如yapi
2. 掌握一种高效的日志埋点方案,可快速使用日志查询工具定位线上问题
3. 理解TDD与BDD模式,至少会使用一种前端单元测试框架
1. 理解Git的核心原理、工作流程、和SVN的区别
2. 熟练使用常规的Git命令、git rebase、git stash等进阶命令
3. 可以快速解决线上分支回滚、线上分支错误合并等复杂问题
1. 理解CI/CD技术的意义,至少熟练掌握一种CI/CD工具的使用,如Jenkins
2. 可以独自完成架构设计、技术选型、环境搭建、全流程开发、部署上线等一套完整的开发流程(包括Web应用、移动客户端应用、PC客户端应用、小程序、H5等等)
1. 了解后端的开发方式,在应用程序中的作用,至少会使用一种后端语言
2. 掌握数据最终在数据库中是如何落地存储的,能看懂表结构设计、表之间的关联,至少会使用一种数据库
1. 了解前端性能衡量指标、性能监控要点,掌握一种前端性能监控方案
2. 了解常见的Web、App性能优化方案
3.SEO排名规则、SEO优化方案、前后端分离的SEO
4.SSR实现方案、优缺点、及其性能优化
5.Webpack的性能优化方案
6.Canvas性能优化方案
7.React、Vue等框架使用性能优化方案
1.XSS攻击的原理、分类、具体案例,前端如何防御
2.CSRF攻击的原理、具体案例,前端如何防御
3.HTTP劫持、页面劫持的原理、防御措施
1. 能理解所开发项目的整体业务形态、业务目标、业务架构,可以快速定位线上业务问题
2. 能理解所开发项目整体的技术架构、能快读的根据新需求进行开发规划、能快速根据业务报警、线上日志等定位并解决线上技术问题
3. 可以将自己的想法或新技术在业务中落地实践,尽量在团队中拥有一定的不可替代性
vczh大神在知乎问题【如何能以后达到温赵轮三位大神的水平?】下的回答:
这十几年我一共做了三件事:
上面几点可能有点难,第一点我就做不到,但是做到下面绩点还是比较容易的。
关于写博客说明下,能给别人讲明白的知识会比自己学习掌握的要深刻许多
1. 拥有自己的技术博客,或者在一些博客平台上拥有自己的专栏
2. 定期的将知识进行总结,不断完善自己的知识体系
3. 尽量将自己的知识转换成真实的产出,不要仅仅停留在书面理解层面,更重要的是实际应用
4. 坚持输出自己的代码,不要盲目的扎进公司业
这部分可能比上面九条加起来重要!
1. 了解互联网人员术语:CEO、CTO、COO、CFO、PM、QA、UI、FE、DEV、DBA、OPS等
2. 了解互联网行业术语:B2B、B2C、C2C、O2O等
3. 掌握互联网行业沟通、问答、学习的
4. 有一定的"PPT"能力
5. 有一定的理财意识,至少了解储蓄、货币基金、保险、指数基金、股票等基本的理财知识
6. 掌握在繁重的工作和长期的电脑辐射的情况下保持健康的方法,建立正确的养生知识体系
有了知识体系,在阅读一篇技术文章的时候就很容易把它归类,我一直以来就是这样做的。
事实证明,在阅读文章或书籍时,有目的和归类的阅读比 "随便看看" 后的只是留存率要高很多。
每阅读到一篇好的文章或者书籍,我都会收藏并归类到我的知识体系中。
下面是一些我觉得还不错的文章、博客或者书籍教程等等,分享给大家,资源不多,但都是精品。
学习一门知识,最好先阅读官方文档,把所有的API大概浏览一遍,再继续看大佬们总结的进阶知识,什么东西是搬运过来的,什么是干货,一目了然。
[📚]JavaScript高级程序设计(必看):book.douban.com/subject/105…
[📚] 高性能JavaScript:book.douban.com/subject/536…
现代JavaScript教程:zh.javascript.info/
阮一峰的ECMAScript 6教程:es6.ruanyifeng.com/
ECMAScript 6标准:www.ecma-international.org/ecma-262/6.…
HTML meta标签总结与属性使用介绍:segmentfault.com/a/119000000…
CSS编码指导:github.com/chadluo/CSS…
大前端开发者需要了解的基础编译原理和语言知识:fullstack.blog/2017/06/24/…
图解HTTP:book.douban.com/subject/258…
[📚]JavaScript设计模式与开发实践:book.douban.com/subject/263…
正则表达式 30 分钟入门教程:link.juejin.im/?target=htt…
数据结构与算法之美:time.geekbang.org/column/intr…
用动画的形式呈现解LeetCode题目的思路:github.com/MisterBooo/…
JavaScript数据结构和算法:github.com/ConardLi/aw…
30-seconds-of-code(里面有很多js代码非常巧妙,我正在将它翻译成中文):github.com/ConardLi/30…
《重学前端》中的浏览器原理章节:time.geekbang.org/column/arti…
图解浏览器的基本工作原理:zhuanlan.zhihu.com/p/47407398
七天学会NodeJS:github.com/nqdeng/7-da…
Node.js模块加载与运行原理:efe.baidu.com/blog/nodejs…
TypeScript Handbook:zhongsp.gitbooks.io/typescript-…
React.js小书:huziketang.mangojuice.top/books/react…
React深入系列:juejin.im/post/5cad39…
Webpack React小书:fakefish.github.io/react-webpa…
Vue.js技术揭秘:github.com/ustbhuangyi…
Vuex- 在Vue中管理状态:sabe.io/tutorials/g…
你需要Mobx还是Redux?:juejin.im/post/5a7fd7…
Underscore源码分析:yoyoyohamapi.gitbooks.io/undersercor…
微信小程序开发资源汇总:github.com/justjavac/a…
腾讯移动Web前端知识库:github.com/AlloyTeam/M…
一口(很长的)气了解babel:zhuanlan.zhihu.com/p/43249121
Webpack傻瓜式指南:zhuanlan.zhihu.com/p/20367175
Webpack原理:segmentfault.com/a/119000001…
廖雪峰的git教程:www.liaoxuefeng.com/wiki/001373…
前端开发者必备的Nginx知识:juejin.im/post/5c85a6…
使用 Jenkins 进行持续集成:www.liaoxuefeng.com/article/001…
常见六大Web安全攻防解析:github.com/ljianshu/Bl…
深入理解前端性能监控:juejin.im/post/5caaac…
[📚] 高性能网站建设指南:book.douban.com/subject/313…
新人如何快速融入技术实力强的前端团队:juejin.im/post/5cb860…
印记中文(各种中文开发文档):www.docschina.org/
前端学习方法:github.com/helloqingfe…
如何在工作内外获得持续的技术成长:juejin.im/post/5cbd74…
优秀的前端博客汇总:github.com/foru17/fron…
另外推荐我一直在关注的几位大佬的个人博客:
冴羽的博客:github.com/mqyqingfeng…
左耳朵耗子:coolshell.cn/
互联网术语大全:www.jianshu.com/p/9a7ca206c…
互联网沟通、问答、学习的艺术:zhuanlan.zhihu.com/p/41431775
经常加班至深夜,怎样才能保持身体健康:www.zhihu.com/question/21…
其实在这个信息发达的时代最不缺的就是资源,如何从众多的资源中获取到真正精华的部分,是非常重要的,资源在于精不在于多,强烈建议在保证深度的情况下再保证广度。
希望你阅读本篇文章后可以达到以下几点:
从知识清单中找到自己的知识盲点与欠缺
具有知识体系化的思想,开始建立自己的知识体系
阅读文章时将知识归类到知识体系中,并不断完善自己的知识体系
从文章中获取到了有用的资源
Emmet 使用类似于 CSS 选择器的语法描述元素在生成的文档树中的位置及其属性。
可以使用元素名(如 div 或者 p)来生成 HTML 标签。Emmet 没有预定义的有效元素名的集合,可以把任何单词当作标签来生成和使用:div → <div></div>, foo → <foo></foo> 等。
嵌套运算符用于以缩写的方式安排元素在生成文档树中的位置:将其放在内部或成为相邻的元素。
>可以使用 > 运算符指定嵌套元素在另一个元素内部:
div>ul>li
生成的结果为:
<div>
<ul>
<li></li>
</ul>
</div>
+使用 + 运算符将相邻的其它元素处理为同级:
div+p+bq
生成的结果为:
<div></div>
<p></p>
<blockquote></blockquote>
^使用 > 运算符将会降低所有后续所有元素在生成树中的级别,每一级的兄弟元素也被解析成相同深度的元素:
div+div>p>span+em
将生成:
<div></div>
<div>
<p><span></span><em></em></p>
</div>
使用 ^ 运算符,能够提升元素在生成树中的一个级别,并同时影响其后的元素:
div+div>p>span+em^bq
将生成:
<div></div>
<div>
<p><span></span><em></em></p>
<blockquote></blockquote>
</div>
可以连续使用多个 ^ 运算符,每次提高一个级别:
div+div>p>span+em^^^bq
将生成:
<div></div>
<div>
<p><span></span><em></em></p>
</div>
<blockquote></blockquote>
*使用 * 运算符可以定义一组元素:
ul>li*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
()括号用于在复杂的 Emmet 缩写中处理一组子树:
div>(header>ul>li*2>a)+footer>p
将生成:
<div>
<header>
<ul>
<li><a href=""></a></li>
<li><a href=""></a></li>
</ul>
</header>
<footer>
<p></p>
</footer>
</div>
如果想与浏览器 DOM 协同工作,可能想要对文档片段分组:每个组包含一个子树,所有的后续元素都插入到与组中第一个元素相同的级别中。
能够在组中嵌套组并且使用 * 运算符绑定它们:
(div>dl>(dt+dd)*3)+footer>p
将生成:
<div>
<dl>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
</dl>
</div>
<footer>
<p></p>
</footer>
使用分组,可以使用单个缩写逐个写出整页的标签,不过尽量不要这么做。
属性运算符用于编辑所生成的元素的属性,在 HTML 和 XML 中可以快速地为生成元素添加 class 属性。
在 CSS 中,可以使用 elem#id 和 elem.class 注解来达到为元素指定 id 或 class 属性的目的。在 Emmet 中,可以使用几乎相同的语法来为指定的元素添加这些属性:element:
div#header+div.page+div#footer.class1.class2.class3
生成:
<div></div>
<div></div>
<div></div>
可以使用 [attr] 注解(就像在 CSS 中一样)来为元素添加自定义属性:
将生成:
<td title="Hello world!" colspan="3"></td>
td[colspan title] 将生成 <td colspan="" title=""> ,如果你的编辑器支持,可以使用 tab 来跳到每个空属性中填写。td[title=hello colspan=3] 是正确的。$使用 * 运算符可以重复生成元素,如果带 $ 就可以为它们编号。把 $ 放在元素名、属性名或者属性值中,将为每个元素生成正确的编号:
ul>li.item$*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
使用多 $ 可以填充前导的零:
ul>li.item$$*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
使用 @ ,可以改变数字的走向(升序或降序)和基数(例如起始值)。
在 $ 后添加 @- 来改变数字走向:
ul>li.item$@-*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
在 $ 后面添加 @N 改变编号的基数:
ul>li.item$@3*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
这些附加的运算符可以同时使用:
ul>li.item$@-3*5
将生成:
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
{}可以用花括号向元素中添加文本:
a{Click me}
将生成:
<a href="">Click me</a>
注意,这个 {text} 是被当成独立元素解析的(类似于 div, p ),但当其跟在其它元素后面时则有所不同。例如, a{click} 和 a>{click} 产生相同的输出,但是 a{click}+b{here} 和 a>{click}+b{here} 的输出就不同了:
<!-- a{click}+b{here} -->
<a href="">click</a><b>here</b>
<!-- a>{click}+b{here} -->
<a href="">click<b>here</b></a>
在第二示例中, <b> 元素放在了 <a> 元素的里面。差别如下:当 {text} 写在元素的后面,它不影响父元素的上下文。下面是展示这种差别的重要性的较复杂的例子:
p>{Click }+a{here}+{ to continue}
生成:
<p>Click <a href="">here</a> to continue</p>
在这个例子里, 我们用 > 运算符明确的将 Click here to continue 下移一级,放在 <p> 元素内,但对于 a 元素的内容就不需要了,因为 <a> 仅有 here 这一部分内容,它不改变父元素的上下文。
作为比较,下面是不带有 > 运算符的相同缩写:
p{Click }+a{here}+{ to continue}
生成:
<p>Click</p>
<a href="">here</a> to continue
当熟悉了 Emmet 的缩写语法后,可能会想要使用一些格式来生成更可读的缩写。例如,在元素和运算符之间使用空格间隔:
(header > ul.nav > li*5) + footer
但是这种写法是错误的,因为空格是 Emmet 停止缩写解析的标识符。
请多用户误以为每个缩写都应写在新行上,但是他们错了:可以在文本的任意位置键入和扩展缩写。
这也就是为什么当想要停止解析和扩展时,Emmet 需要一些标志的原因。如果你仍然认为复杂的缩写需要一些格式使其更易读: