huatten / mynote Goto Github PK
View Code? Open in Web Editor NEW📝记录点滴吧
📝记录点滴吧
我们在实现的时候,需要轮播图充满我们的轮播区域,通常我们的做法是banner设置固定宽高,然后超出隐藏,img设置max-width:100%,这样虽然能实现充满轮播区域,可是我们会发现,虽然宽度自适应了,高度并没有自适应,有时候我们会发现纵向上图片底部不见了,因为如果图片太高,当图片宽度100% 自适应的时候,高度也会按照宽度自适应的比例进行自适应(缩小或放大),此时高度超出banner区域被隐藏了,视觉上会造成图片丢失的感觉。
在默认的水平文档流方向下,CSS margin和padding属性的垂直方向的百分比值都是相对于 宽度
计算的,所以不管是 padding-left 和 padding-right 还是 padding-top 和 padding-bottom 都是基于父元素宽度的百分比。
首先我们要明白一件事情:子元素的 padding 属性百分比的值是相对于父容器的宽度而言的。明白这一点特别重要。因此我们需要计算出图片的宽高比例,本文案例中使用了一张 750x366 的图片,宽高比就是 750 / 366 = 2.05
,假设将图片父容器宽度设置为100%,那么图片的高度就是 100% / 2.05 = 48.8%
,所以图片的高度按比例缩放就是 基于父元素宽度的 48.8%
。
举例代码:
.contain{
max-width: 750px;
margin: 10px auto;
overflow: hidden;
}
.banner{
width: 100%;
padding-top: 48.8%; //当当当!划重点!
position: relative;
}
.contain img{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
页面结构
<div class="contain">
<div class="banner">
<img src="./image/banner.jpg" alt="banner"/>
</div>
</div>
首先要明白:视口单位(viewport units)是相对于视口(viewport) 尺寸而言的。 100vw 等于视口宽度的 100% ,1vw 相对于等于视口宽度的 1%。因此这个特性就特别适合在移动端实现宽高等比自适应容器。
举例代码:
.contain{
width: 100%;
height: 48.8vw; //当当当!划重点!
position:relative;
}
.contain img{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
页面结构
<div class="contain">
<img src="./image/banner.jpg" alt="banner"/>
</div>
这个方法相对于图片等比缩放特性有个优点就是,无论图片是否加载完成,容器的高度始终在那里,不会造成页面抖动而影响用户体验,还有不会造成页面重绘提升性能。
当然在实际使用过程要考虑容器的 margin,padding 等因素,所以计算高度比例时估计需要 calc()
函数配合稍微多点计算。但是你如果要支持很老的手机,那就没办法使用了。
find()
模拟实现find()
方法返回数组中满足提供的测试函数的 第一个
元素的值,否则返回 undefined
,方法的回调函数可以接受三个参数,依次为当前的值 value
、当前的位置 index
和原数组 arr
。
const arr = [1,2,3,4,5]
const found = arr.find(item => { item > 3 })
console.log(found) //4
遍历数组数据并将其回调,判断回调只要有一个结果为true则返回。
Array.prototype.myFind = function (fn) {
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) {
return this[i]
}
}
}
findIndex()
模拟实现findIndex()
方法返回数组中满足提供的测试函数的 第一个
元素的索引,否则返回 -1
,方法的回调函数可以接受三个参数,依次为当前的值 value
、当前的位置 index
和原数组 arr
。
const arr = [1, 2, 3, 4, 5]
const fundIndex = arr.findIndex(item => item > 2)
console.log(fundIndex)
遍历数组数据并将其回调,返回满足条件的数据的索引。
Array.prototype.myFindIndex = function (fn) {
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) {
return i
}
}
return -1
}
sort()
模拟实现sort()
方法用 原地算法 对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值(Unicode编码)来排序的。
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months); //["Dec", "Feb", "Jan", "March"]
//对于需要降序排列或非字符串排序,该方法就不能很好的执行了。
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1); //[1, 100000, 21, 30, 4]
//sort有一个可选参数,它能帮我们解决这个问题,通过为sort传入一个函数,sort根据函数返回值进行排序。
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1); //[1, 4, 21, 30, 100000]
const array1 = [1, 30, 4, 21, 100000, 'js', 'css'];
array1.sort();
console.log(array1); //[1, 100000, 21, 30, 4, 'css', 'js']
const array1 = [1, 30, 4, 21, 100000, 'js', 'css'];
array1.sort(function(a, b){
return a - b
})
console.log(array1); //[1, 4, 21, 30, 100000, "js", "css"]
sort的实现其实很像是一个简单的冒泡排序。
Array.prototype.mySort = function (fn) {
for (let n in this) { //比较次数
let isSorted = true; //假设已经排好
for (let m in this) { //每次比较之后会得出一个最大值
if (typeof fn === "function") { //传入回调
if (fn(this[n], this[m]) < 0) {
//交换
const temp = this[n];
this[n] = this[m]
this[m] = temp
isSorted = false
}
} else { //直接调用
if (this[n] < this[m]) {
//交换
const temp = this[n]
this[n] = this[m]
this[m] = temp
isSorted = false
}
}
}
if (isSorted) { //排序结束
break
}
}
return this
}
最近在开发vue组件库-slider滑块组件的过程中,按住拖动滑块比较卡顿而且老是报如下错误:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
情景复现:当我们给 document
添加了touch
事件的监听器的时候,如果同时在handler内部调用了event.preventDefault()
,这时候浏览器(Chrome56+)就会报如上warning。
翻译过来就是:
passive
(被动的)事件监听器preventDefault
,因为它被认为是passive。简而言之就是:
preventDefault
,这就导致了浏览器不能及时响应滚动,略有延迟。 window
、document
和 body
上注册的 touchstart
和 touchmove
事件处理函数,会默认为是 passive: true
,浏览器忽略 preventDefault
就可以第一时间滚动了。举例:
window.addEventListener('touchmove', func) 效果和下面一句一样
window.addEventListener('touchmove', func, { passive: true })
但是这就导致了一个问题:如果在 window
、document
和 body
元素的 touchstart
和 touchmove
事件处理函数中调用 e.preventDefault()
,会被浏览器忽略掉,并不会阻止默认行为。
测试:
window.addEventListener('touchmove', e => e.preventDefault()) // 在 chrome56 中,照样滚动,而且控制台会有提示
那么如何解决这个问题呢?不让控制台提示,而且 preventDefault() 有效果呢?有如下两种方案:
window.addEventListener('touchmove', func, { passive: false }) //注意第三个参数
在 touch 的事件监听方法上绑定第三个参数 { passive: false }
,通过传递 passive
为 false
来明确告诉浏览器:事件处理程序调用 preventDefault
来阻止默认滑动行为。
这样任何触摸事件都不会产生默认行为,但是 touch 事件照样触发,具体参考 touch-action
div{
touch-action: none;
}
2016 年 Google I/O 上提出的概念,目的是用来提高页面的滑动流畅度,可以通过这篇文章来了解一下 passive event listener。
A new feature in the DOM spec that enable developers to opt-in to better scroll performance by eliminating the need for scrolling to block on touch and wheel event listeners.
Developers can annotate touch and wheel listeners with {passive: true} to indicate that they will never invoke preventDefault.
简而言之就是当我们在滚动页面的时候(通常是我们监听touch事件的时候),页面其实会有一个短暂的停顿(大概200ms),浏览器不知道我们是否要preventDefault,所以它需要一个延迟来检测,这就导致了我们的滑动显得比较卡顿。从Chrome 51开始,passive event listener
被引进了Chrome,我们可以通过对 addEventListener
的第三个参数设置 { passive: true }
来避免浏览器检测这个我们是否有在touch事件的handler里调用preventDefault。在这个时候,如果我们依然调用了 preventDefault
,就会在控制台打印一个警告,告诉我们这个 preventDefault
会被忽略。
当我们给 addEventListener
的第三个参数设置了 { passive: true }
,这个事件监听器就被称为 passive event listener。
从Chrome 56开始,如果我们给 document
绑定 touchmove
或者 touchstart
事件的监听器,这个 passive
是会被默认设置为 true
以提高性能,但是我们大多数人并不知道这点,并且依旧调用了 preventDefault
。这并不会导致什么页面崩溃级的错误,但是这可能导致我们忽略了一个页面性能优化的点,特别是在移动端这种更加重视性能优化的场景下。
第一次在Mac上安装nginx,下面是我的安装过程在此做个记录,同时对nginx的配置文件有个初步的认识。
Homebrew 是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能,查看Homebrew是否安装:命令行可以输入brew -v
,回车即可看到版本号。
如果没看到则需要在终端输入下面的命令即可安装:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
成功以后会出现 **Installation successful! **,关于 Homebrew
的更多细节和使用方法,可自行搜索进行学习。
首先查看是否已经安装了nginx,终端输入下面的命令,看不到绿色的对勾说明没安装。
brew search nginx
######2.查询nginx信息
在命令行输入brew info nginx
我们可以看到nginx在本地还未安装(Not installed
),同时还可以看到nginx的来源(From
),Docroot默认为/usr/local/var/www
,默认端口已在/usr/local/etc/nginx/nginx.conf
中设置为8080,这样nginx就可以在没有sudo的情况下运行,nginx将加载/usr/local/etc/nginx/servers
中的所有文件,要立即启动nginx并在登录时重新启动可以使用命令: brew services start nginx
,如果你不想或者不需要后台服可以运行命令:nginx
。
终端输入命令
brew install nginx
这个过程比较漫长如果中途失败重新执行命令即可,细心观察安装过程可以发现依赖Homebrew安装nginx帮我们下载了很多相关东西。
根据第2步反馈的结果我们可以在终端运行/usr/local/etc/nginx/
来查看安装目录
或者不习惯命令行查看文件和目录的话可以直接打开访达文件管理器,在终端输入:open /usr/local/etc/nginx/
我们可以看到nginx.conf
这个配置文件,以后会经常用到它。但是我们并没有看见nginx被安装在哪个目录了,此时在终端运行/usr/local/Cellar
可以看见nginx的文件夹,其实这个才是nginx被安装到的目录
我们可以看见一个以当前安装的nginx版本号为命名的文件夹,进入1.17.3_1/bin
目录可以找到nginx的可执行启动文件,因此可以总结一下:
/usr/local/etc/nginx/
/usr/local/Cellar/nginx/1.17.3_1/bin/
/usr/local/var/www
同时我们可以观察到在1.17.3_1目录下面有一个html的文件夹快捷方式
显示原身可以看到它指向的就是/usr/local/var/www目录,上面第2步有提到关键字 Dcroot
在命令行输入 nginx
命令可启动服务
在/usr/local/Cellar/nginx/1.17.3_1/bin/
目录找到启动程序,双击就可以启动
打开浏览器访问localhost:8080
,正常情况下到这一步就会能看到nginx的欢迎界面啦!
💁♂️ 打开 http:localhost:8080
后如果也页面出现 403 forbidden 的错误,很可能是nginx目录下面(usr/local/var/www)缺少了index.html欢迎页面文件,可以自己找到目录新建一个index.html放进去就可以解决。
nginx -h
nginx -v
nginx
nginx -s quit
nginx -s stop
nginx -t
nginx -s reload
cat /usr/local/etc/nginx/nginx.conf
终端输入命令:cat /usr/local/etc/nginx/nginx.conf
ngnix配置文件结构图:
下面是一些基本配置的解读:
#全局块
#定义Nginx运行的用户和用户组
#user nobody;
#nginx进程数
worker_processes 1;
#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#进程文件,指定pid的存储文件位置
#pid logs/nginx.pid;
#events块
events {
#指定ngnix进程的连接数上限
worker_connections 1024;
#内核模型 [ kqueue | rtsig | epoll | /dev/poll | select | poll ] epoll模型是Linux 2.6以上版 本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll
}
#http块
http {
#用以包含其他配置文件 文件扩展名与文件类型映射表
include 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"';
#定义日志的格式,后面定义要输出的内容
#1.$remote_addr 和 $http_x_forwarded_for记录客户端的ip地址
#2.$remote_user 用于记录客户端的用户名称
#3.$time_local 用于记录访问的时间时区
#4.$request 用于记录请求的URL和Http协议
#5.$status 用于记录请求的状态
#6.$body_bytes_sent 用于记录发送给客户端的文件主体大小
#7.$http_referer 用于记录页面是从哪个页面地址访问过来的
#8.$http_user_agent 用于记录客户端浏览器的相关信息
#连接日志的路径 指定的日志格式放在最后
#access_log logs/access.log main;
#只记录更为严重的错误日志,减少IO压力
error_log logs/error.log crit;
#关闭日志
#access_log off;
#默认编码
#charset utf-8;
#服务器名字的hash表大小
server_names_hash_bucket_size 128;
#客户端请求单个文件的最大字节数
client_max_body_size 8m;
#指定来自客户端请求头的hearerbuffer大小
client_header_buffer_size 32k;
#指定客户端请求中较大的消息头的缓存最大数量和大小。
large_client_header_buffers 4 64k;
#指定是否开启高效文件传输模式
sendfile on;
#防止网络阻塞
#tcp_nopush on;
tcp_nodelay on;
#指定连接保持活动的超时时长 单位s
keepalive_timeout 65;
#客户端请求头读取超时时间
client_header_timeout 10;
#设置客户端请求主体读取超时时间
client_body_timeout 10;
#响应客户端超时时间
send_timeout 10;
#gzip压缩功能设置
#指定是否开启gzip压缩
#gzip on;
#最小压缩文件大小
gzip_min_length 1k;
#压缩缓冲区
gzip_buffers 4 16k;
#压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_http_version 1.0;
#压缩等级 1-9 等级越高,压缩效果越好,节约宽带,但CPU消耗大
gzip_comp_level 2;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_types text/plain application/x-javascript text/css application/xml;
#前端缓存服务器缓存经过压缩的页面
gzip_vary on;
#server块
server {
#指定端口号
listen 8080;
#指定ip地址或者域名,多个域名之间空格隔开
server_name localhost;
#指定默认的网页编码格式 若网页格式与此不同,将被自动转码
#charset koi8-r;
#指定访问日志的路径
#access_log logs/host.access.log main;
#location块 用于指定各个URL的处理方式
location / {
#表示默认由ngnix处理,root指定根目录的位置(相对/绝对
root html;
#index定义首页索引文件名称 按顺序匹配
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 html;
}
#访问以.php结尾的文件则自动派发给127.0.0.1
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
#php脚本请求全部转发给FastCGI处理
# 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;
#}
#禁止访问.ht页面
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server Https虚拟主机定义
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
include servers/*;
}
从配置文件开始到events块之间的内容,配置服务器整体运行的配置指令,一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。比如上面的配置第二行:
#nginx开启的进程数 建议设置为等于CPU总核心数。可以和worker_cpu_affinity配合使用
worker_processes 1;
影响ngnix服务器与用户的网络连接。
#events块
events {
#指定ngnix进程的连接数上限
worker_connections 1024;
#内核模型 [ kqueue | rtsig | epoll | /dev/poll | select | poll ] epoll模型是Linux 2.6以上版 本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll
}
可以嵌套多个server,配置代理,缓存,日志定义、gzip压缩等绝大多数功能和第三方模块的配置
http {
#用以包含其他配置文件 文件扩展名与文件类型映射表
include 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 logs/access.log main;
#只记录更为严重的错误日志,减少IO压力
error_log logs/error.log crit;
#关闭日志
#access_log off;
#默认编码
#charset utf-8;
#指定是否开启高效文件传输模式
sendfile on;
#防止网络阻塞
#tcp_nopush on;
tcp_nodelay on;
#指定连接保持活动的超时时长 单位s
keepalive_timeout 65;
#客户端请求头读取超时时间
client_header_timeout 10;
#设置客户端请求主体读取超时时间
client_body_timeout 10;
#响应客户端超时时间
send_timeout 10;
#gzip压缩功能设置
#指定是否开启gzip压缩
#gzip on;
#最小压缩文件大小
gzip_min_length 1k;
#压缩缓冲区
gzip_buffers 4 16k;
#压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_http_version 1.0;
#压缩等级 1-9 等级越高,压缩效果越好,节约宽带,但CPU消耗大
gzip_comp_level 2;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_types text/plain application/x-javascript text/css application/xml;
#前端缓存服务器缓存经过压缩的页面
gzip_vary on;
}
http 全局块配置的指令包括文件引入、MIME-TYPE 定义、日志定义、连接超时时间、单链接请求数上限、gzip压缩设置、http_proxy服务全局设置、负载均衡设定等等。
每个 http 块可以包括多个 server 块,而每个 server 块就相当于一个虚拟主机;
而每个 server 块也分为全局 server 块,以及可以同时包含多个 location 块。
最常见的配置是本虚拟机主机的监听配置和本虚拟主机的名称或 IP 配置。
http服务中,某些特定的URL对应的一系列配置项,一个 server 块可以配置多个location 块。
最近在做项目的时候发现,将input或textarea设置为disabled后,在iphone手机上样式将被覆写,解决方案如下:
input:disabled, textarea:diabled {
-webkit-text-fill-color: #ccc;
opacity: 1;
color: #ccc;
}
以上样式将覆盖其系统默认设置的值,能够实现android和ios的兼容性,使其表现一致。否则以上样式在iPhone下面表现为白色,安卓下为灰色。其中,-webkit-text-fill-color
是用来做填充色使用的,如果有设置这个值,则color属性将不生效。
/*
* @params arr
* @returns arr
*/
function sortArr(arr) {
let goNext = true
let entries = arr.entries();
while (goNext) {
let result = entries.next()
// next{ value: Array(4), done: false }
if (result.done !== true) {
// 用于指示迭代器是否完成:在每次迭代时进行更新而且都是false,直到迭代器结束done才是true
result.value[1].sort((x, y) => x - y)
goNext = true
} else {
goNext = false
break
}
}
return arr
}
var arr = [[1, 34], [456, 2, 3, 44, 234], [4567, 1, 4, 5, 6], [34, 78, 23, 1]]
sortArr(arr)
/*[
[ 1, 34 ],
[ 2, 3, 44, 234, 456 ],
[ 1, 4, 5, 6, 4567 ],
[ 1, 23, 34, 78 ]
]*/
图片加载失败,会抛出一个异常事件 error
,可以通过监听 error 事件的方式来对图片进行降级处理。
<img src="http://sina.com.cn/img/abc.png" id="logo" />
监听error事件为当前图片设定默认图
let img = document.getElementById('logo');
img.addEventListener('error',function(e){
e.target.str = 'http://xxx.xxx.xxx/default.png';
})
问题:
<img src="http://sina.com.cn/img/abc.png" onerror="this.src='http://xxx.xxx.xxx/default.png'"/>
问题:仍然需要手动的向 img 标签中添加内联事件,在实际开发过程中,很难保证每张图片都不漏写。
利用事件冒泡的机制来监听?可惜error事件并不会冒泡,但是可以捕获的。(注:DOM2级事件中,error事件是会冒泡的,DOM3级事件中,error事件不会冒泡。)
DOM事件发生的三个阶段:
addEventListener
的第三个参数 useCapture
为 true
,以使事件处理函数在该阶段运行。(低版本IE中无法指定事件处理函数在该阶段执行)addEventListener
的第三个参数 useCapture
为 true
,以使事件处理函数在该阶段运行。因此,首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到的事件。最后一个阶段是冒泡阶段。
我们上文中的监听图片自身的 error 事件,实际上在事件流中是处于目标阶段。
对于 img 的 error 事件来说,是无法冒泡的,但是是可以捕获的,我们的实现如下:
document.addEventListener("error", function (e) {
let el = e.target;
//图片加载异常引起
if (el.tagName.toLowerCase() === 'img') {
el.src = "http://xxx.xxx.xxx/default.png";
}
}, true /*指定事件处理函数在捕获阶段执行*/);
注意:由于低版本IE中 attachEvent
方法无法指定事件处理函数在捕获阶段执行,所以,该方案在低版本IE中不能适用。
效果如下:
当网络出现异常的时候,必然会出现什么网络图片都无法加载的情况,这样就会导致我们监听的 error
事件 被无限触发,所以我们可以设定一个计数器,当达到期望的错误次数时停止对图片赋予默认图片的操作:
document.addEventListener("error", function (e) {
let el = e.target,
times = Number(el.dataset.times) || 0, //失败次数
allTimes = 3; // 总失败次数,此时设定为3
if (el.tagName.toLowerCase() === 'img') {
if (times >= allTimes) {
// 断网
el.src = "http://xxx.xxx.xxx/error.png";
} else {
// 加载异常
el.dataset.times = times + 1;
el.src = "http://xxx.xxx.xxx/default.png";
}
}
}, true);
修改默认占位图路径模拟断网:
charCodeAt()
方法可返回指定位置的字符的 Unicode 编码
。这个返回值是 0 - 65535 之间的整数。
语法:
str.charCodeAt(index) // index: 必需。表示字符串中某个位置的数字,即字符在字符串中的下标。
toString(radix)方法可返回表示该数字的指定进制形式的字符串。
语法:
const n = 97
n.toString(radix) // "1100001" radix支持 [2, 36] 之间的整数。默认为10
结合上述两个api我们可以整理思路
ASCII 码
ASCII 码
转换成二进制因此最终简单代码实现:
// 字符串 => ASCII 码 => 二进制
'a'.charCodeAt(0).toString(2) // "a" => 97 => "1100001"
//将字符串转换成二进制形式,中间用空格隔开
function strToBinary(str) {
if (!str) {
return;
}
let total2Str = "";
for (let i = 0; i < str.length; i++) {
let binaryStr = str.charCodeAt(i).toString(2);
if (binaryStr == "") {
total2Str = binaryStr;
} else {
total2Str = total2Str + " " + binaryStr;
}
}
return total2Str;
}
strToBinary('abc') // 1100001 1100010 1100011
parseInt(string, radix)
将一个字符串 string 转换为 radix 进制的整数, radix 为介于2-36之间的数。
语法:
parseInt(string, radix)
fromCharCode()
可接受一个指定的 Unicode
值,然后返回一个字符串
语法:
String.fromCharCode(numX,numX,...,numX) // numX必需。一个或多个 Unicode 值,即要创建的字符串中的字符的 Unicode 编码。
结合两个api简单代码实现:
let str2 = '1100001'
let num10 = parseInt(str2, 2) // 97
String.fromCharCode(num10) // a
//将二进制字符串转换成Unicode字符串
function binaryToStr(str) {
let result = [];
let list = str.split(" ");
for (let i = 0; i < list.length; i++) {
let item = list[i];
let asciiCode = parseInt(item, 2);
let charValue = String.fromCharCode(asciiCode);
result.push(charValue);
}
return result.join("");
}
binaryToStr("1100001 1100010 1100011") // "abc"
Homebrew是Mac OSX上的软件包管理工具,能在Mac中方便的安装软件或者卸载软件,相当于linux下的apt-get、yum神器;Homebrew可以在Mac上安装一些OS X没有的UNIX工具,Homebrew将这些工具统统安装到了 /usr/local/Cellar 目录中,并在 /usr/local/bin 中创建符号链接。
简单来说,Homebrew提供 Apple 没有预装但你需要的东西。详情请见 Homebrew官网。
在我们的日常开发中经常会遇到这种情况:手上有好几个项目,每个项目的需求不同,进而不同项目必须依赖不同版的 NodeJS 运行环境。如果没有一个合适的工具,这个问题将非常棘手。nvm 应运而生,nvm 是 Mac 下的 node 管理工具,有点类似管理 Ruby 的 rvm。
Homebrew的安装很简单,只需在终端下输入如下指令:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Homebrew安装成功后,会自动创建目录 /usr/local/Cellar 来存放Homebrew安装的程序。 这时你在命令行状态下面就可以使用 brew 命令了,安装完成如下:
我们可以输入命令 brew -v
来查看是否安装成功,如果出现版本号说明安装ok啦
下面列一下常见的使用操作:
brew install 软件名
,例:brew install wgetbrew search 软件名
,例:brew search wgetbrew uninstall 软件名
,例:brew uninstall wgetbrew update
(brew upgrade 软件名
,例:brew upgrade gitbrew list / brew ls
brew info/home 软件名
,例:brew info git / brew home git(brew outdated
brew reps
卸载brew安装的 node/npm
brew uninstall node
卸载官网下载安装的 node/npm
npm ls -g --depth=0 #查看已经安装在全局的模块,以便删除这些全局模块后再按照不同的 node 版本重新进行全局安装
sudo rm -rf /usr/local/lib/node_modules #删除全局 node_modules 目录
sudo rm /usr/local/bin/node #删除 node
cd /usr/local/bin && ls -l | grep "../lib/node_modules/" | awk '{print $9}'| xargs rm #删除全局 node 模块注册的软链
终端直接输入命令:
brew install nvm
回车之后即可进行安装,安装成功提示:
==> Summary
🍺 /usr/local/Cellar/nvm/0.35.1: 7 files, 148.0KB, built in 8 seconds
安装成功之后,还不能直接使用nvm命令,否则会报错提示 zsh: command not found: nvm
,还必须在你的 .bash_profile 加入以下这行,让你可以直接在shell使用nvm指令,将以下命令复制到终端执行:
echo "source $(brew --prefix nvm)/nvm.sh" >> .bash_profile
修改之后,需要重新定向来源,复制以下命令并执行:
. ~/.bash_profile
此时在终端输入:
nvm --version
可以看到当前nvm的版本号,表明nvm安装成功啦
查看Node所有版本,用nvm ls-remote
命令,如下:
macbook@MacBookdeMacBook-Pro ~ nvm ls-remote
v0.1.14
v0.1.15
v0.1.16
...
v7.7.1
v7.7.2
v13.1.0
正常安装的话会比较慢,可以使用下面的命令:
nvm install v10.15.3
如果没翻墙安装很慢可以推荐国内淘宝镜像:
NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node nvm install 10
需要哪个版本,就在最后将10换成对应的版本号就行,默认下载版本中最新的版本号!不过在安装过程中可能会出现下面这样的情况
运行命令 nvm use --delete-prefix v10.15.3
,此时提示安装成功
但是切换窗口打开新的命令行,会提示 zsh: command not found: node
,好崩溃啊,至于为什么用homebrew安装会出现上面的问题,可参考Github上的一个issue。
查看nvm文档发现:文档中Installation 那一小节倒数第二行有一句 Homebrew installation is not supported.
,👴💊☁️🌶️重新卸载了按照官网推荐方式安装!
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.1/install.sh | bash
完成后nvm就被安装在了~/.nvm下啦,接下来就需要配一下环境变量了
首先进入当前用户的home目录
cd ~
新建.bash_profile
touch .bash_profile
编辑.bash_profile文件
open -e .bash_profile
输入下面的配置保存退出
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
然后更新刚配置的环境变量
source .bash_profile
这里注意一下,如果你使用了 zsh
的话,就不用按照上面四步配置了,而需要在 ~/.zshrc
这个配置文件中配置,打开 ~/.zshrc
,在最后一行加上:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
这一步的作用是每次新打开一个bash,nvm都会被自动添加到环境变量中了。
完成后输入source ~/.zshrc重新启动一下配置。
此时输入 nvm
看到如下说明安装成功了,切换新的命令窗口也依旧生效✌️。
接下来就是使用nvm啦,例举一些常见命令:
nvm ls-remote
列出所有可安装的版本
nvm install <version>
安装指定的版本,如nvm install v8.14.0
nvm uninstall <version>
卸载指定的版本
nvm ls
列出所有已经安装的版本
nvm use <version>
切换临时使用指定的版本
nvm current
显示当前使用的版本
nvm alias default <version>
设置永久默认node版本
function getUrlParams(url, key){
let ourl = new URL(url)
return ourl.searchParams.get(key)
}
测试:
const url = "https://www.imooc.com/article/288696?user=kd"
getUrlParams(url, 'user') //kd
参考:
公司的项目来来回回折腾了几个月,终于终于终于要上线了,先不说中间有多曲折各种新建分支,各种来回变更,👴简直8⃣️想提了..... 不过还好算是能上线一部分模块了🤮,正好有事就请假了,结果在火车上跟我说有的用户手机白屏没有请求,这锅是真的甩不掉了,开始怀疑是不是因为之前项目打包慢,更改了webpack配置采用 DllPlugin
优化打包性能导致,于是赶紧掏出电脑连上热点一把唆恢复webpack配置,然并卵。放开测试环境的 VConsole ,查看控制台发现红色一道杠,在开发中没有问题,但是打包后放到手机低版本中就会白屏因此想都8⃣️用想,白屏基本都是es6语法导致的,打开dist查看下打包后首页的js包含es6箭头函数 () =>
若干处,可以确定到,代码中es6语法没有完全解析导致白屏。
其实之前项目一开始就加过 babel-polyfill
。相关配置操作如下:
babel-polyfill
npm install --save-dev babel-polyfill
webpack
配置,添加 babel-polyfill
//webpack.base.conf.js
entry: {
app: ["babel-polyfill", "./src/main.js"]
},
main.js
中引入 babel-polyfill
import 'babel-polyfill'
其实关于 babel-polyfill
插件的使用是正确的,之所以出现这些未转义的代码,我在想是不是因为babel-loader的编译范围所限,因为我的static文件夹也用了es6的语法,所以也要加入编译,于是继续修改配置:
// webpack.base.conf.js
{
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'), //默认编译文件
resolve('static'), //编译静态文件
]
}
md,结果打包后的首页js还是包含es6
箭头函数语法,看到网上一篇文章说是用es6-promise
解决,用法配置如下:
//安装
npm install es6-promise--save
//使用 main.js
import Es6Promise from 'es6-promise'
Es6Promise.polyfill()
其实这个时候我也想到不是了,如果是promise导致的不兼容那不应该仅仅是首页报错。
这个时候首页推荐位置的产品位轻轻划过,突然一只🦙飘过,开发首页这位👴是不是 swiper
的引入有问题啊,打开首页推荐位代码一看果然是
import Swiper from 'swiper'
为什么我能一眼定位到是swiper
引起的,因为之前在开发日历组件的时候,采用的是swiper 4.0
版本,当时就踩坑🌶️, Unexpected token: name (Dom7)
..... 关键词就是 Dom7!Swiper4.0自带有Dom7库,因此无需另外加载Jquery即可对Dom7/Jquery对象使用以下常用操作。这些操作适用于Swiper5和Swiper4,但是要注意DOM7的更新。因此有结果🌶️:Dom7使用的是 es6 的语法,但是在使用过程中并没有转换成 es5。接下来就记录一下几种解决办法:
swiper.min.js
(推荐)import Swiper from 'swiper/dist/js/swiper.min.js';
ps:不要偷懒,该有的不要省!我问👴为什么不按照我之前的方式引入,收到的答复:我觉得你那样引入好麻烦
// vue-cli2.0 webpack.base.conf.js
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/dom7'), resolve('node_modules/swiper')]
}
//vue-cli3.0 vue.config.js
module.exports = {
chainWebpack: config => {
config.rule('js').include.add(/node_modules\/(dom7|swiper)\/.*/)
}
}
至此,解决了,以后遇见白屏的问题就按照上面的办法一步步排查吧,微博的webview这么🌶️🐔🐎,👴💊☁️🌶️.....
vue3项目eslint依赖eslint-plugin-vue
升级到 V9.16.1
之后,关于props有很多的报错如下:
ESLint: Getting a value from the `props` in root scope of `<script setup>` will cause the value to lose reactivity.(vue/no-setup-props-destructure)
大概意思就是 从<script setup>的根作用域中的props中获取值将导致该值失去响应式!!!
<script setup>
const props = defineProps({
count: {
type: Number,
default: 0,
},
})
console.log(props.count)
^^^^^^ Failing
console.log(ref(props.count))
^^^^^^ Failing
</script>
在9.15.1之前的版本没这个问题,这一处调整就是在9.16.0这个 版本更新 引起的。
关于这一点也有不同的争议,可以去 issue #2259 和 PR #2244 下面看讨论。比如下面来自 @volarname 的评论:
i strongly agree with a completely new opt-in rule instead of this update because vue/no-setup-props-destructure its not correct for this, and i will absolutely opt-out this rule, because in some cases you want the prop as initial non-reactive value for the component
const props = defineProps(['initialCounter'])
const counter = ref(props.initialCounter)
this is a correct code, also in vue docs
如果不想启用这个规则,可以在eslint中关闭 vue/no-setup-props-destructure 规则:
'vue/no-setup-props-destructure': 'off'
或者我们使用 toRef
<script setup>
const props = defineProps({
count: {
type: Number,
default: 0,
},
})
const count = toRef(() => props.count)
console.log(count)
</script>
我们在做vue项目的时候很少会直接操作dom了,但是有一些需求不得不操作dom,比如这个需求:监听滚动事件实现某元素吸顶或者固定位置显示。
首先在mounted
钩子中给window添加滚动监听事件:
export default{
mouted(){
window.addEventListener('scroll', this.handleScroll)
}
}
然后在方法中定义监听滚动执行的方法handleScroll
,监听tab元素到页面顶部的距离并监听滚动距离如果大于元素到顶部的距离时,表示此时tab元素要吸顶:
export default{
methods:{
handleScroll(){
let scrollY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
let offsetTop = document.querySelector('.tab-bar').offsetTop;
let headerHeight = document.querySelector('.header').offsetHeight; //头部导航高度
this.tabBarFixed = scrollY > (offsetTop - headerHeight) ? true : false
}
}
}
这个时候就能实现效果了,但是如果你的页面正好作为子组件,那么直接在mounted里addEventListener监听滚动是监听不到的,只能监听到父组件的滚动。
查阅文档得知:element.addEventListener(event, function, useCapture)
,第三个参数是一个布尔值,指定是使用事件冒泡还是事件捕获。默认是false使用冒泡传播。
因为子组件滚动是捕获事件,父组件滚动是冒泡事件,addEventListener第三参数默认false只监听冒泡事件,所以子组件的捕获事件默认监听不到,改为true才可以。
export default{
mouted(){
window.addEventListener('scroll', this.handleScroll, true);
}
}
此时子组件父组件的滚动都能被监听到了,只不过前者是捕获后者是冒泡,通过 Event
事件对象得知,冒泡事件的event.bubbles
值是true,捕获事件的值是false,因此可以通过这一点来判断是父组件滚动还是子组件滚动。
export default{
methods:{
handleScroll(e){
e = event || ev;
if(e.bubbles){
console.log('父组件滚动');
}else{
console.log('子组件滚动');
}
}
}
}
最后记得在组件销毁的时候移除监听事件。
export default{
destroyed(){
window.removeEventListener('scroll', this.handleScroll);
}
}
const n = 1234567890
const nf = new Intl.NumberFormat('us').format(n)
console.log(nf) // "1,234,567,890"
需求:将黑色图标变成红色图标。
原理:
CSS的mask是基于透明度实现遮罩层效果的,也就是实色区域显示,透明区域隐藏。因此只需要把目标图标的颜色作为背景色,然后将原始图标作为遮罩图片,即可实现效果。缺点是IE不支持。
代码:
<span class="icon"></span>
.icon{
display: inline-block;
width: 64px;
height: 64px;
-webkit-mask: url("happy.png") no-repeat;
mask: url("happy.png") no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.