跳到主要内容

· 阅读需 1 分钟
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

· 阅读需 2 分钟

MuYunyun

dongyuanxin / blog

chencl1986/Blog

jsliang 的文档库

语雀前端小册

张鑫旭

技术胖

小智

面试

前端面试每日 3+1,以面试题来驱动学习,提倡每日学习与思考,每天进步一点!

壹题

qianguyihao / Web

reactjs-interview-questions

Awsome-Front-End-learning-resource

Front-end-Web-Development-Interview-Question

FE-interview

代码规范/示例代码

ryanmcdermott / clean-code-javascript

Airbnb JavaScript 风格指南

Airbnb React/JSX 风格指南

Airbnb CSS-in-JavaScript 风格指南

前端各类规范集合

realworld

算法

javascript-algorithms

leetcode 题解,记录自己的 leetcode 解题之路

CODE

CSS

iCSS

CSS-Inspiration

JS

每个 JavaScript 工程师都应懂的 33 个概念

30-seconds-of-code

前端特效存档

Vue

uni-app 移动端多端开发

D2admin

vue-element-admin

React

react-pxq 一个 react + redux 的完整项目 和 个人总结

《深入浅出 React 和 Redux》代码

react-universal-example

[小度鱼 - 年轻人的交流社区](54sword / xiaoduyu.com)

NodeJS

hackathon-starter Node 模板

nodebestpractices

practicalnode

WebGL

WebGL-Fluid-Simulation

pixi.js 2D WebGL 渲染库

工具

MAC

Awesome Mac

VSCODE

vscode-recipes

富文本编辑器

tiptap Vue

MikuTools

MikuTools

播放器

ArtPlayer

网易云

网易云音乐 Node.js API

GIT

Git 的奇技淫巧

Github 教程 awesome-github

Webpack

webpack-demos

Jupyter

awesome-jupyter

演示

reveal 网页版 ppt

人工智能

deep-learning-drizzle

MachineLearning

微软人工智能教育与学习共建社区

· 阅读需 24 分钟

花了一周时间了解 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

四项确认

  • 确认系统网络(ping)
  • 确认 yum 可用 (yum list | grep gcc
  • 确认关闭 iptables (iptables -F)
  • 确认停用 selinux

两项安装

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 安装

确定 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 启动

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 简述

nginx 是一个开源且高性能、可靠的 http 中间件,代理服务。Nginx(发音同 engine x)是一个 Web 服务器,也可以用作反向代理,负载平衡器和 HTTP 缓存。该软件由 Igor Sysoev 创建,并于 2004 年首次公开发布。同名公司成立于 2011 年,以提供支持。

为什么选择 nginx

io 多路复用 epoll

多个描述符的 i/o 操作都能在一个线程内并发交替地顺序完成,这就教 i/o 多路复用,这里的” 复用 “指的是复用同一个线程。i/o 多路复用的实现方式为:select、poll、epoll

什么是 select

epoll 模型

  • 当 FD 就绪,采用系统的回调函数之间将 fd 放入,效率更高
  • 最大连接无限制

轻量级

  • 功能模块少
  • 代码模块化

cpu 亲和(affinity)

是一种 cpu 核心和 nginx 工作进程绑定方式,把每个 worker 进程固定在一个 cpu 上执行,减少切换 cpu 的 cache miss,获得更好的性能。

说白了就是减少 cpu 切换所损耗的性能

sendfile

nginx 版本

  • Mainline version 开发版
  • Stable version 稳定版
  • Legacy version 历史版本

基本参数使用

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 | 设置附加的参数,链接系统库 |

nginx 基本配置语法

http 相关

展示每次请求的请求头

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


nginx 日志类型

  • error.log、 access.log
  • log_format

格式 *

syntax: log_format name [escape=default | json] string...;
default: log_format combined "...";
context:http

nginx 变量

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;
}


变量类型

  • http 请求变量:arg_PARAMETER,http_header,sent_http_header
  • 内置变量:nginx 内置的
  • 自定义变量: 自己定义

nginx 模块

  • nginx 官方模块
  • 第三方模块

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_modulenginx 的客户端状态
--with-http_random_index_module目录中选择一个随机主页
--with-http_sub_modulehttp 内容替换
--limit_conn_module连接频率限制
--limit_req_module请求频率限制
http_access_module基于 ip 的访问控制
http_auth_basic_module基于用户的信任登录

http_stub_status_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 的活跃连接数;握手的总次数、处理连接数;读、写、等待个数

http_random_index_module

配置语法

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;
}

重启后多刷几次网页:

主页出现不同了。

http_sub_module (替换)

配置语法

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;
#}


压测结果

限制前

限制后

http_access_module

配置语法

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,...

解决方法

  • 采用别的 http 头信息控制访问,如 http_x_forward_for
  • 结合 geo 模块操作
  • 通过 http 自定义变量传递

http_auth_basic_module

配置语法

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;
}

测试

局限性
  • 用户信息依赖文件方式
  • 操作管理机械、效率低下
解决方案
  • nginx 结合 LUA 实现高效验证
  • nginx 和 LDAP 打通,利用 nginx-auth-ldap 模块

场景实践

静态资源 web 服务

静态资源

定义

非服务器动态生成的文件

静态资源服务场景 - CDN

文件读取配置

sendfile

syntax: sendfile on | off;
default:sendfile off
context:http,server,location,if in location

--with-file-aio 异步文件读取

tcp_nopush

作用: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

扩展 nginx 压缩模块

  • http_gzip_static_module: 预读 gzip 功能
  • http_gunzip_module: 应用支持 gunzip 的压缩方式

配置截图

浏览器缓存

http 协议定义的缓存机制(如:expires,cache-control 等)

  • 浏览器无缓存

  • 浏览器有缓存

检测过期机制

作用请求头
检验是否过期expires, cache-control (max-age)
协议中 Etag 头信息校验etag
last-modified 头信息校验last-modified

浏览器请求服务器过程(缓存版本)

相关配置

expires

添加 cache-control、expires 头

syntax: expires [modified] time;
expires epoch | max | off;
default: expires off;
context:http, server, location

配置例子

跨域访问

为什么浏览器禁止跨域访问

不安全,容易出现 CSRF 攻击

nginx 配置

syntax: add_header name value [always]
default: -
context:http, server, location, if in location

添加请求头:Access-Control-Allow-Origin

配置截图

防盗链

目的

防止资源被盗用

防盗链设置思路

首要方式:区别哪些请求是非正常的用户请求

基于 http_refer 防盗链配置模块

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

扩展

  • proxy_buffer_size
  • proxy_buffers
  • proxy_busy_buffers size

跳转重定向配置

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

扩展

  • proxy_read_timeout
  • proxy_send_timeout

总的配置

负载均衡调度器 SLB

nginx 负载均衡

GSLB

GSLB 是英文 Global Server Load Balance 的缩写,意思是全局负载均衡。作用:实现在广域网(包括互联网)上不同地域的服务器间的流量调配,保证使用最佳的服务器服务离自己最近的客户,从而确保访问质量。

SLB

负载均衡(Server Load Balancer,简称 SLB)是一种网络负载均衡服务,针对阿里云弹性计算平台而设计,在系统架构、系统安全及性能,扩展,兼容性设计上都充分考虑了弹性计算平台云服务器使用特点和特定的业务场景。

4 层负载均衡

在 iso 模型中的传输层(包的转发)

7 层负载均衡

在应用层实现

nginx 实现的负载均衡(7 层)

配置

syntax: upstream name{...}
default: -
context:http

配置截图

upstream 举例

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

iphash

url——hash

配置语法

url_hash

syntax: hash key [consistent];
default:-
context:upstream
this directive appeared in version 1.7.2

配置截图

动态缓存

缓存的类型

代理缓存

proxy_cache 配置语法

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: 不活跃就清理

如何清理指定缓存

  • rm-rf 缓存目录内容
  • 第三方扩展模块 ngx_cache_purge

如何让部分页面不缓存

syntax : proxy_no_cache string ...;
default: -;
context:http,server,location

配置截图

大文件分片请求

syntax : slice size
default: slice o
context:http,server,location

http_slice_module

优势

  • 每个子请求收到的数据都会形成一个独立的文件,一个请求断了,其它请求不受到影响

缺点

  • 当文件很大或者 slice 很小的时候, 可能会导致文件描述符耗尽等情况。

深度学习

动静分离

通过中间件将动态请求和静态请求分离

为什么

  • 分离资源,减少不必要的请求消耗,减少请求延时

场景

rewrite 规则

应用场景

  • url 访问跳转,支持开发设计
    • 页面跳转、兼容性支持、展示效果等
  • seo 优化
  • 维护
    • 后台维护、流量转发
  • 安全

配置语法

syntax: rewrite regex replacement [flag];
default:-
context:server, location,if

维护页面实例

rewrite ^(.*)$ /pages/maintain.html break;

flag

字段作用
last停止 rewrite 检测
break停止 rewrite 检测
redirect返回 302 临时重定向,地址栏会显示跳转后的地址
permanent返回 301 永久重定向,地址栏会显示跳转后的地址(浏览器下次直接访问重定向后的地址

一些实例

rewrite 规则优先级

  • 执行 server 块的 rewrite 指令
  • 执行 location 匹配
  • 执行指定的 locaiton 中的 rewrite

nginx 高级模块

  • 制定并允许检查请求的链接的真实性以及保护资源免遭未经授权的访问。
  • 限制链接生效周期
syntax: secure_link expression
default:-
context:server, location,server

syntax: secure_link_md5 expression
default:-
context:server, location,http

图示

配置例子

http_geoip_module 使用场景

基于 ip 地址匹配 MaxMind GeoIp 二进制文件,读取 ip 所在地域信息

yum install nginx-module-geoip

  • 区别国内外做 http 访问规则
  • 区别国内城市地域做 http 访问规则

配置截图

https 服务

对传输内容进行加密以及身份验证。

为什么需要 https

  • 传输数据被中间人盗用,信息泄露
  • 数据内容劫持,篡改

对称加密和非对称加密

https 加密协议原理

中间人伪造客户端和服务器

如何解决

生成密钥和 CA 证书

安装 openssl 和 http_ssl_module 模块

#openssl version
openSSL 1.0.1e-fips 11 feb 2013

#nginx -V
--with-http_ssl_module

步骤

  • 生成 key 密钥
openssl genrsa -idea -out jesonc.key 1024

结果

  • 生成证书签名请求文件(csr 文件)
openssl req -new -key jesonc.key -out jesonc.csr

结果

  • 生成证书签名文件(CA 文件)
openssl x509 -req -days 3650 -in jesonc.csr -signkey jesonc.key -out jesonc.crt

结果

相关实验

配置 docker(443)

docker run -d -p 443:443 --name nginx_443 nginx_443:latest /sbin/init
nginx -c /etc/nginx/nginx.conf

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;
}
}

测试结果

https 服务优化

  • 激活 keepalive 长连接
  • 设置 ssl session 缓存

配置截图

· 阅读需 4 分钟

前言

项目开发中经常需要用到元素大小,网页高度,视口高度,各种距离等等,本文总结了获取各种宽高、距离的方法。

元素大小

祭出这几张神图,简单明了又清晰😄

各种方法总结

注意:这些方法都不考虑混杂模式,请确保你的文档在标准模式下(填写了正确的 doctype)否则获取的值会不准。

1. 获取整个网页高度与宽度

代码说明

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();

2. 获取视口高度与宽度

代码说明

1. 同上,火狐不兼容 document.body,所以使用 document.documentElement

function getViewport() {
   return {
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
   }
}

PS:jq 的话 $(window).height();           $(window).width();

3. 获取元素距页面高度

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

4. 获取元素到视口的距离

使用 el.getBoundingClientRect 方法 

getBoundingClientRect 方法返回元素的大小及其相对于视口的位置。  

5. 获取纵向滚动条高度或横向滚动条长度

代码说明

同 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)
};

6. 获取鼠标到元素、视口、文档、屏幕距离

这种主要是读取 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('元素出现')
}
}

· 阅读需 5 分钟

Flex(Flexible Box)布局 称为 "弹性布局",可以为网页的布局提供最大的灵活性,取代了往常的 浮动(float) 布局,并且任何一个容器都可以设置 Flex 布局。  注:设置 Flex 布局后,子元素的 Float 布局将失效

Flex 中的四大概念

容器: 如果给一个标签添加display:flex;,那么这个标签就是一个容器 项目: 在容器中的直接子元素叫项目(一定是 直接 子元素) 主轴: 项目的默认排序方向就是主轴(默认横向排列,一个容器可以有多根主轴) 交叉轴: 和主轴垂直的那个轴,就是交叉轴

容器的属性

Flex-direction | Flex-wrap | Flex-flow | justify-content | align-items | align-content

  1. Flex-direction(属性决定主轴的方向)

flex-direction:row | row-reverse | column | column-reverse;

  • row(默认值):主轴为水平方向,起点在左端
  • row-reverse:主轴为水平方向,起点在右端
  • column:主轴为垂直方向,起点在上端
  • column-reverse:主轴为垂直方向,起点在下端
  1. Flex-wrap(属性决定项目排不下时如何换行)

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

  1. order(属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0)

order: <integer>;

  1. flex-grow(属性定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大)

flex-grow:<number>;

  1. flex-shrink(属性定义了项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小)

flex-shrink: <number>;

  1. flex-basis(属性定义了在分配多余空间之前,项目占据的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即项目的本来大小, 也可以设置 xx px, 项目将占据固定空间)

flex-basis: <length> | auto;

  1. flex(属性是 flex-grow, flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。后两个属性可选)

flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ];

  1. align-self(属性允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch)

align-self: auto | stretch | flex-start | flex-end | center | baseline;

· 阅读需 20 分钟

正则表达式一直是困扰很多程序员的一门技术,当然也包括曾经的我。大多数时候我们在开发过程中要用到某些正则表达式的时候,都会打开谷歌或百度直接搜索然后拷贝粘贴。当下一次再遇到相同问题的时候,同样的场景又再来一遍。作为一门用途很广的技术,我相信深入理解正则表达式并能融会贯通是值得的。所以,希望这篇文章能帮助大家理清思路,搞懂正则表达式各种符号之间的内在联系,形成知识体系,当下次再遇到正则表达式的时候可以不借助搜索引擎,自己解决。

正则表达式到底是什么

正则表达式 (Regular Expression) 其实就是一门工具,目的是为了字符串模式匹配,从而实现搜索和替换功能。它起源于上个 20 世纪 50 年代科学家在数学领域做的一些研究工作,后来才被引入到计算机领域中。从它的命名我们可以知道,它是一种用来描述规则的表达式。而它的底层原理也十分简单,就是使用状态机的思想进行模式匹配。大家可以利用 regexper.com 这个工具很好地可视化自己写的正则表达式:

/\d\w+/这个正则生成的状态机图:

对于具体的算法实现,大家如果感兴趣可以阅读《算法导论》。

从字符出发

我们学习一个系统化的知识,一定要从其基础构成来了解。正则表达式的基本组成元素可以分为:字符和元字符。字符很好理解,就是基础的计算机字符编码,通常正则表达式里面使用的就是数字、英文字母。而元字符,也被称为特殊字符,是一些用来表示特殊语义的字符。如 ^ 表示非,| 表示或等。利用这些元字符,才能构造出强大的表达式模式 (pattern)。接下来,我们就来从这些基本单位出发,来学习一下如何构建正则表达式。

单个字符

最简单的正则表达式可以由简单的数字和字母组成,没有特殊的语义,纯粹就是一一对应的关系。如想在'apple'这个单词里找到‘a'这个字符,就直接用/a/这个正则就可以了。

但是如果想要匹配特殊字符的话,就得请出我们第一个元字符 \, 它是转义字符字符,顾名思义,就是让其后续的字符失去其本来的含义。举个例子:

我想匹配*这个符号,由于*这个符号本身是个特殊字符,所以我要利用转义元字符\来让它失去其本来的含义:

如果本来这个字符不是特殊字符,使用转义符号就会让它拥有特殊的含义。我们常常需要匹配一些特殊字符,比如空格,制表符,回车,换行等, 而这些就需要我们使用转义字符来匹配。为了便于记忆,我整理了下面这个表格,并附上记忆方式:

特殊字符正则表达式记忆方式
换行符\nnew line
换页符\fform feed
回车符\rreturn
空白符\sspace
制表符\ttab
垂直制表符\vvertical tab
回退符[\b]backspace, 之所以使用 [] 符号是避免和 \ b 重复

多个字符

单个字符的映射关系是一对一的,即正则表达式的被用来筛选匹配的字符只有一个。而这显然是不够的,只要引入集合区间和通配符的方式就可以实现一对多的匹配了。

在正则表达式里,集合的定义方式是使用中括号[]。如/[123]/这个正则就能同时匹配 1,2,3 三个字符。那如果我想匹配所有的数字怎么办呢?从 0 写到 9 显然太过低效,所以元字符-就可以用来表示区间范围,利用/[0-9]/就能匹配所有的数字, /[a-z]/则可以匹配所有的英文小写字母。

即便有了集合和区间的定义方式,如果要同时匹配多个字符也还是要一一列举,这是低效的。所以在正则表达式里衍生了一批用来同时匹配多个字符的简便正则表达式:

匹配区间正则表达式记忆方式
除了换行符之外的任何字符.句号, 除了句子结束符
单个数字, [0-9]\ddigit
除了 [0-9]\Dnot digit
包括下划线在内的单个字符,[A-Za-z0-9_]\wword
非单字字符\Wnot word
匹配空白字符, 包括空格、制表符、换页符和换行符\sspace
匹配非空白字符\Snot space

循环与重复

一对一和一对多的字符匹配都讲完了。接下来,就该介绍如何同时匹配多个字符。要实现多个字符的匹配我们只要多次循环,重复使用我们的之前的正则规则就可以了。那么根据循环次数的多与少,我们可以分为 0 次,1 次,多次,特定次。

0 | 1

元字符?代表了匹配一个字符或 0 个字符。设想一下,如果你要匹配colorcolour这两个单词,就需要同时保证u这个字符是否出现都能被匹配到。所以你的正则表达式应该是这样的:/colou?r/

>= 0

元字符*用来表示匹配 0 个字符或无数个字符。通常用来过滤某些可有可无的字符串。

>= 1

元字符+适用于要匹配同个字符出现 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/这个正则,就会同时匹配到catscattered这两处文本。这时候我们就需要使用边界正则表达式\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。前者的意思是忽略大小写,后者的意思是找到所有符合的匹配。

最后,总结一下:

边界和标志正则表达式记忆方式
单词边界\bboundary
非单词边界\Bnot 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) 是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。举个简单的例子: applepeople都包含ple这个后缀,那么如果我只想找到appleple,该怎么做呢?我们可以通过限制app这个前缀,就能唯一确定ple这个单词了。

其中(?<=regex)的语法就是我们这里要介绍的后向查找。regex指代的子表达式会作为限制项进行匹配,匹配到这个子表达式后,就会继续向查找。另外一种限制匹配是利用(?<!regex) 语法,这里称为负后向查找。与正前向查找不同的是,被指定的子表达式不能被匹配到。于是,在上面的例子中,如果想要查找appleple也可以这么写成/(?<!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 和!

总结

对于正则来说,符号之抽象往往让很多程序员却步。针对不好记忆的特点,我通过分类和联想的方式努力让其变得有意义。我们先从一对一的单字符,再到多对多的子字符串介绍,然后通过分组、回溯引用和逻辑处理的方式来构建高级的正则表达式。

· 阅读需 34 分钟

本文由 简悦 SimpRead 转码, 原文地址 https://juejin.im/post/5cc1da82f265da036023b628

开篇

前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。

winter在他的《重学前端》课程中提到:

到现在为止,前端工程师已经成为研发体系中的重要岗位之一。可是,与此相对的是,我发现极少或者几乎没有大学的计算机专业愿意开设前端课程,更没有系统性的教学方案出现。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。

这样是一个非常真实的现状,实际上很多前端开发者都是自学甚至转行过来的,前端入门简单,学习了几个API以后上手做项目也很简单,但是这往往成为了限制自身发展的瓶颈。

只是停留在会用阶段是远远不够的,我们还需要不断探索和深入。现在市面上并不缺少学习教程,技术文章,如果盲目的学习你会发现看过以后的知识留存率会很低,而且发现没有了解到的知识越来越多,这会让人产生焦虑。

实际上,除了坚持学习的强大的自驱力,你还需要一个很简单的学习方法。那就是:建立自己的知识体系。它能帮助你更系统性的学习,同时你也时刻能知道自己哪些地方是不足的。

我会把我工作和学习中接触到的知识全部归纳到我的知识体系中,其中不仅仅包括我已经学过的,还有很多我没有来得及学习的。

这不仅仅是我的知识体系,更是我时刻提醒自己的自检清单。

下面我会把我的自检清单分享给大家,你可以按照清单上的知识检测自己还有哪些不足和提升,我也建议大家建自己的知识体系,这样工作或者学习甚至面试时,你能快速定位到知识清单中的点,如果你有哪些我没归纳到的点,欢迎在评论区告诉我。

一、JavaScript 基础

前端工程师吃饭的家伙,深度、广度一样都不能差。

变量和类型

  • 1.JavaScript规定了几种语言类型

  • 2.JavaScript对象的底层数据结构是什么

  • 3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol

  • 4.JavaScript中的变量在内存中的具体存储形式

  • 5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作

  • 6. 理解值类型和引用类型

  • 7.nullundefined的区别

  • 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里面放returnfinally还会执行,理解其内部机制

  • 2.JavaScript如何实现异步编程,可以详细描述EventLoop机制

  • 3. 宏任务和微任务分别有哪些

  • 4. 可以快速分析一个复杂的异步嵌套逻辑,并掌握分析方法

  • 5. 使用Promise实现串行

  • 6.Node与浏览器EventLoop的差异

  • 7. 如何在保证页面运行流畅的情况下处理海量数据

语法和 API

  • 1. 理解ECMAScriptJavaScript的关系

  • 2. 熟练运用es5es6提供的语法规范,

  • 3. 熟练掌握JavaScript提供的全局对象(例如DateMath)、全局函数(例如decodeURIisNaN)、全局属性(例如Infinityundefined

  • 4. 熟练应用mapreducefilter 等高阶函数解决问题

  • 5.setInterval需要注意的点,使用settimeout实现setInterval

  • 6.JavaScript提供的正则表达式API、可以使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题

  • 7.JavaScript异常处理的方式,统一的异常处理方案

二、HTML 和 CSS

HTML

  • 1. 从规范的角度理解HTML,从分类和语义的角度使用标签

  • 2. 常用页面标签的默认样式、自带属性、不同浏览器的差异、处理浏览器兼容问题的方式

  • 3. 元信息类标签 (headtitlemeta) 的使用目的和配置方法

  • 4.HTML5离线缓存原理

  • 5. 可以使用Canvas APISVG等绘制高性能的动画

CSS

  • 1.CSS盒模型,在不同浏览器的差异

  • 2.CSS所有选择器及其优先级、使用场景,哪些可以继承,如何运用at规则

  • 3.CSS伪类和伪元素有哪些,它们的区别和实际应用

  • 4.HTML文档流的排版规则,CSS几种定位的规则、定位参照物、对文档流的影响,如何选择最好的定位方式,雪碧图实现原理

  • 5. 水平垂直居中的方案、可以实现6种以上并对比它们的优缺点

  • 6.BFC实现原理,可以解决的问题,如何创建BFC

  • 7. 可使用CSS函数复用代码,实现特殊效果

  • 8.PostCSSSassLess的异同,以及使用配置,至少掌握一种

  • 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.1HTTP2.0带来的改变

  • 9.HTTPS的加密原理,如何开启HTTPS,如何劫持HTTPS请求

  • 10. 理解WebSocket协议的底层原理、与HTTP的区别

设计模式

  • 1. 熟练使用前端常用的设计模式编写代码,如单例模式、装饰器模式、代理模式等

  • 2. 发布订阅模式和观察者模式的异同以及实际应用

  • 3. 可以说出几种设计模式在开发中的实际应用,理解框架源码中对设计模式的应用

四、数据结构和算法

据我了解的大部分前端对这部分知识有些欠缺,甚至抵触,但是,如果突破更高的天花板,这部分知识是必不可少的,而且我亲身经历——非常有用!

JavaScript 编码能力

  • 1. 多种方式实现数组去重、扁平化、对比优缺点

  • 2. 多种方式实现深拷贝、对比优缺点

  • 3. 手写函数柯里化工具函数、并理解其应用场景和优势

  • 4. 手写防抖和节流工具函数、并理解其内部原理和应用场景

  • 5. 实现一个sleep函数

手动实现前端轮子

  • 1. 手动实现call、apply、bind

  • 2. 手动实现符合Promise/A+规范的Promise、手动实现async await

  • 3. 手写一个EventEmitter实现事件发布、订阅

  • 4. 可以说出两种实现双向绑定的方案、可以手动实现

  • 5. 手写JSON.stringifyJSON.parse

  • 6. 手写一个模版引擎,并能解释其中原理

  • 7. 手写懒加载下拉刷新上拉加载预加载等效果

数据结构

  • 1. 理解常见数据结构的特点,以及他们在不同场景下使用的优缺点

  • 2. 理解数组字符串的存储原理,并熟练应用他们解决问题

  • 3. 理解二叉树队列哈希表的基本结构和特点,并可以应用它解决问题

  • 4. 了解的基本结构和使用场景

算法

  • 1. 可计算一个算法的时间复杂度和空间复杂度,可估计业务逻辑代码的耗时和内存消耗

  • 2. 至少理解五种排序算法的实现原理、应用场景、优缺点,可快速说出时间、空间复杂度

  • 3. 了解递归和循环的优缺点、应用场景、并可在开发中熟练应用

  • 4. 可应用回溯算法贪心算法分治算法动态规划等解决复杂问题

  • 5. 前端处理海量数据的算法方案

五、运行环境

我们需要理清语言和环境的关系:

ECMAScript描述了JavaScript语言的语法和基本对象规范

浏览器作为JavaScript的一种运行环境,为它提供了:文档对象模型(DOM),描述处理网页内容的方法和接口、浏览器对象模型(BOM),描述与浏览器进行交互的方法和接口

Node 也是JavaScript的一种运行环境,为它提供了操作I/O、网络等API

浏览器 API

  • 1. 浏览器提供的符合W3C标准的DOM操作API、浏览器差异、兼容性

  • 2. 浏览器提供的浏览器对象模型 (BOM) 提供的所有全局API、浏览器差异、兼容性

  • 3. 大量DOM操作、海量数据的性能优化 (合并操作、DiffrequestAnimationFrame等)

  • 4. 浏览器海量数据存储、操作性能优化

  • 5.DOM事件流的具体实现机制、不同浏览器的差异、事件代理

  • 6. 前端发起网络请求的几种方式及其底层实现、可以手写原生ajaxfetch、可以熟练使用第三方库

  • 7. 浏览器的同源策略,如何避免同源策略,几种方式的异同点以及如何选型

  • 8. 浏览器提供的几种存储机制、优缺点、开发中正确的选择

  • 9. 浏览器跨标签通信

浏览器原理

  • 1. 各浏览器使用的JavaScript引擎以及它们的异同点、如何在代码中进行区分

  • 2. 请求数据到请求结束与服务器进行了几次交互

  • 3. 可详细描述浏览器从输入URL到页面展现的详细过程

  • 4. 浏览器解析HTML代码的原理,以及构建DOM树的流程

  • 5. 浏览器如何解析CSS规则,并将其应用到DOM树上

  • 6. 浏览器如何将解析好的带有样式的DOM树进行绘制

  • 7. 浏览器的运行机制,如何配置资源异步同步加载

  • 8. 浏览器回流与重绘的底层原理,引发原因,如何有效避免

  • 9. 浏览器的垃圾回收机制,如何避免内存泄漏

  • 10. 浏览器采用的缓存方案,如何选择和控制合适的缓存方案

Node

  • 1. 理解Node在应用程序中的作用,可以使用Node搭建前端运行环境、使用Node操作文件、操作数据库等等

  • 2. 掌握一种Node开发框架,如ExpressExpressKoa的区别

  • 3. 熟练使用Node提供的APIPathHttpChild Process等并理解其实现原理

  • 4.Node的底层运行原理、和浏览器的异同

  • 5.Node事件驱动、非阻塞机制的实现原理

六、框架和类库

轮子层出不穷,从原理上理解才是正道

TypeScript

  • 1. 理解泛型接口等面向对象的相关概念,TypeScript对面向对象理念的实现

  • 2. 理解使用TypeScript的好处,掌握TypeScript基础语法

  • 3.TypeScript的规则检测原理

  • 4. 可以在ReactVue等框架中使用TypeScript进行开发

React

  • 1.Reactvue选型和优缺点、核心架构的区别

  • 2.ReactsetState的执行机制,如何有效的管理状态

  • 3.React的事件底层实现机制

  • 4.React的虚拟DOMDiff算法的内部实现

  • 5.ReactFiber工作原理,解决了什么问题

  • 6.React RouterVue Router的底层实现原理、动态加载实现原理

  • 7. 可熟练应用React API、生命周期等,可应用HOCrender propsHooks等高阶用法解决问题

  • 8. 基于React的特性和原理,可以手动实现一个简单的React

Vue

  • 1. 熟练使用VueAPI、生命周期、钩子函数

  • 2.MVVM框架设计理念

  • 3.Vue双向绑定实现原理、Diff算法的内部实现

  • 4.Vue的事件机制

  • 5.template转换成真实DOM的实现机制

多端开发

  • 1. 单页面应用(SPA)的原理和优缺点,掌握一种快速开发SPA的方案

  • 2. 理解Viewportemrem的原理和用法,分辨率、pxppidpidp的区别和实际应用

  • 3. 移动端页面适配解决方案、不同机型适配方案

  • 4. 掌握一种JavaScript移动客户端开发技术,如React Native:可以搭建React Native开发环境,熟练进行开发,可理解React Native的运作原理,不同端适配

  • 5. 掌握一种JavaScript PC客户端开发技术,如Electron:可搭建Electron开发环境,熟练进行开发,可理解Electron的运作原理

  • 6. 掌握一种小程序开发框架或原生小程序开发

  • 7. 理解多端框架的内部实现原理,至少了解一个多端框架的使用

数据流管理

  • 1. 掌握ReactVue传统的跨组件通信方案,对比采用数据流管理框架的异同

  • 2. 熟练使用Redux管理数据流,并理解其实现原理,中间件实现原理

  • 3. 熟练使用Mobx管理数据流,并理解其实现原理,相比Redux有什么优势

  • 4. 熟练使用Vuex管理数据流,并理解其实现原理

  • 5. 以上数据流方案的异同和优缺点,不情况下的技术选型

实用库

  • 1. 至少掌握一种UI组件框架,如antd design,理解其设计理念、底层实现

  • 2. 掌握一种图表绘制框架,如Echart,理解其设计理念、底层实现,可以自己实现图表

  • 3. 掌握一种GIS开发框架,如百度地图API

  • 4. 掌握一种可视化开发框架,如Three.jsD3

  • 5. 工具函数库,如lodashunderscoremoment等,理解使用的工具类或工具函数的具体实现原理

开发和调试

  • 1. 熟练使用各浏览器提供的调试工具

  • 2. 熟练使用一种代理工具实现请求代理、抓包,如charls

  • 3. 可以使用AndroidIOS模拟器进行调试,并掌握一种真机调试方案

  • 4. 了解VueReact等框架调试工具的使用

七、前端工程

前端工程化:以工程化方法和工具提高开发生产效率、降低维护难度

项目构建

  • 1. 理解npmyarn依赖包管理的原理,两者的区别

  • 2. 可以使用npm运行自定义脚本

  • 3. 理解BabelESLintwebpack等工具在项目中承担的作用

  • 4.ESLint规则检测原理,常用的ESLint配置

  • 5.Babel的核心原理,可以自己编写一个Babel插件

  • 6. 可以配置一种前端代码兼容方案,如Polyfill

  • 7.Webpack的编译原理、构建流程、热更新原理,chunkbundlemodule的区别和应用

  • 8. 可熟练配置已有的loadersplugins解决问题,可以自己编写loadersplugins

nginx

  • 1. 正向代理与反向代理的特点和实例

  • 2. 可手动搭建一个简单的nginx服务器、

  • 3. 熟练应用常用的nginx内置变量,掌握常用的匹配规则写法

  • 4. 可以用nginx实现请求过滤、配置gzip、负载均衡等,并能解释其内部原理

开发提速

  • 1. 熟练掌握一种接口管理、接口mock工具的使用,如yapi

  • 2. 掌握一种高效的日志埋点方案,可快速使用日志查询工具定位线上问题

  • 3. 理解TDDBDD模式,至少会使用一种前端单元测试框架

版本控制

  • 1. 理解Git的核心原理、工作流程、和SVN的区别

  • 2. 熟练使用常规的Git命令、git rebasegit stash等进阶命令

  • 3. 可以快速解决线上分支回滚线上分支错误合并等复杂问题

持续集成

  • 1. 理解CI/CD技术的意义,至少熟练掌握一种CI/CD工具的使用,如Jenkins

  • 2. 可以独自完成架构设计、技术选型、环境搭建、全流程开发、部署上线等一套完整的开发流程(包括Web应用、移动客户端应用、PC客户端应用、小程序、H5等等)

八、项目和业务

后端技能

  • 1. 了解后端的开发方式,在应用程序中的作用,至少会使用一种后端语言

  • 2. 掌握数据最终在数据库中是如何落地存储的,能看懂表结构设计、表之间的关联,至少会使用一种数据库

性能优化

  • 1. 了解前端性能衡量指标、性能监控要点,掌握一种前端性能监控方案

  • 2. 了解常见的WebApp性能优化方案

  • 3.SEO排名规则、SEO优化方案、前后端分离的SEO

  • 4.SSR实现方案、优缺点、及其性能优化

  • 5.Webpack的性能优化方案

  • 6.Canvas性能优化方案

  • 7.ReactVue等框架使用性能优化方案

前端安全

  • 1.XSS攻击的原理、分类、具体案例,前端如何防御

  • 2.CSRF攻击的原理、具体案例,前端如何防御

  • 3.HTTP劫持、页面劫持的原理、防御措施

业务相关

  • 1. 能理解所开发项目的整体业务形态、业务目标、业务架构,可以快速定位线上业务问题

  • 2. 能理解所开发项目整体的技术架构、能快读的根据新需求进行开发规划、能快速根据业务报警、线上日志等定位并解决线上技术问题

  • 3. 可以将自己的想法或新技术在业务中落地实践,尽量在团队中拥有一定的不可替代性

九、学习提升

vczh大神在知乎问题【如何能以后达到温赵轮三位大神的水平?】下的回答:

这十几年我一共做了三件事:

  • 1、不以赚钱为目的选择学习的内容;
  • 2、以自己是否能造出轮子来衡量学习的效果;
  • 3、坚持每天写自己的代码,前 10 年每天至少 6 个小时,不包含学习和工作的时间。

上面几点可能有点难,第一点我就做不到,但是做到下面绩点还是比较容易的。

关于写博客说明下,能给别人讲明白的知识会比自己学习掌握的要深刻许多

  • 1. 拥有自己的技术博客,或者在一些博客平台上拥有自己的专栏

  • 2. 定期的将知识进行总结,不断完善自己的知识体系

  • 3. 尽量将自己的知识转换成真实的产出,不要仅仅停留在书面理解层面,更重要的是实际应用

  • 4. 坚持输出自己的代码,不要盲目的扎进公司业

十、技术之外

这部分可能比上面九条加起来重要!

  • 1. 了解互联网人员术语:CEOCTOCOOCFOPMQAUIFEDEVDBAOPS

  • 2. 了解互联网行业术语:B2BB2CC2CO2O

  • 3. 掌握互联网行业沟通、问答、学习的

  • 4. 有一定的"PPT"能力

  • 5. 有一定的理财意识,至少了解储蓄、货币基金、保险、指数基金、股票等基本的理财知识

  • 6. 掌握在繁重的工作和长期的电脑辐射的情况下保持健康的方法,建立正确的养生知识体系

十一、资源推荐

有了知识体系,在阅读一篇技术文章的时候就很容易把它归类,我一直以来就是这样做的。

事实证明,在阅读文章或书籍时,有目的和归类的阅读比 "随便看看" 后的只是留存率要高很多。

每阅读到一篇好的文章或者书籍,我都会收藏并归类到我的知识体系中。

下面是一些我觉得还不错的文章、博客或者书籍教程等等,分享给大家,资源不多,但都是精品。

学习一门知识,最好先阅读官方文档,把所有的API大概浏览一遍,再继续看大佬们总结的进阶知识,什么东西是搬运过来的,什么是干货,一目了然。

语言基础

计算机基础

数据结构和算法

运行环境

框架和类库

前端工程

项目和业务

学习提升

另外推荐我一直在关注的几位大佬的个人博客:

技术之外

其实在这个信息发达的时代最不缺的就是资源,如何从众多的资源中获取到真正精华的部分,是非常重要的,资源在于精不在于多,强烈建议在保证深度的情况下再保证广度。

小结

希望你阅读本篇文章后可以达到以下几点:

  • 从知识清单中找到自己的知识盲点与欠缺

  • 具有知识体系化的思想,开始建立自己的知识体系

  • 阅读文章时将知识归类到知识体系中,并不断完善自己的知识体系

  • 从文章中获取到了有用的资源

· 阅读需 8 分钟

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  属性。

ID 和 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}  是被当成独立元素解析的(类似于  divp ),但当其跟在其它元素后面时则有所不同。例如, 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 需要一些标志的原因。如果你仍然认为复杂的缩写需要一些格式使其更易读:

  • 缩写不是模板语言,它们不需要”易读 “,它们必须” 可快速扩展和移动“。
  • 不需要写复杂的缩写。不要认为在 web 编程中” 键入 “是最慢的运算。想快速找出构建单个的复杂缩写比构造和键入一些较短较简单的缩写更慢。