Repository: xiaoliuxiansheng/work-summary Branch: master Commit: 3bcfae99ddf2 Files: 79 Total size: 299.7 KB Directory structure: gitextract_aeb36g8b/ ├── .gitignore ├── .idea/ │ ├── $CACHE_FILE$ │ ├── .gitignore │ ├── inspectionProfiles/ │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── vcs.xml │ └── work-sunnary.iml ├── CSS/ │ ├── Input placeholder属性样式修改(颜色,大小,位置).md │ ├── common.css │ ├── less 中使用grid布局.md │ ├── reset.css │ └── 常用css.md ├── Git/ │ └── 使用规范.md ├── HTML/ │ ├── 双飞翼布局.html │ ├── 圣杯布局.html │ └── 防止移动端点击输入框页面变大.md ├── HTTP/ │ ├── 封装axios请求拦截器.md │ └── 设置请求头.md ├── JAVASCRIPT/ │ ├── FormData使用方法.md │ ├── JavaScript规范.md │ ├── 上传oss.md │ ├── 下载文件流.md │ ├── 修改url的某个参数值.md │ ├── 功能性js插件.md │ ├── 基于vant上传组件oss/ │ │ └── 服务器.md │ ├── 完美url跳转方式.md │ ├── 常用js代码块.md │ ├── 数组中的对象去重.md │ ├── 文件流转为文件.md │ ├── 时间与时间戳的转换.md │ ├── 时间戳转日期.md │ ├── 深度克隆.md │ ├── 深度拷贝.md │ ├── 滚动加载demo.md │ ├── 用递归算法实现,数组长度为5且元素的随机数在2-32间不重复的值.html │ ├── 获取当前周每天对应的时间戳.md │ └── 获取当前月份 第一天 最后一天.md ├── Node/ │ ├── NodeJs文档归纳.md │ ├── Sequelize.md │ ├── express浅尝.md │ ├── koa浅尝.md │ └── 现有使用量较大的框架.md ├── README.md ├── React/ │ ├── React代码规范.md │ └── 常见使用总结.md ├── React-Native/ │ ├── APP端消息推送.md │ ├── APP端消息推送之极光推送.md │ ├── ios通过浏览器下载app/ │ │ └── 方法.md │ ├── 使用Promise封装fetch请求.md │ └── 使用总结.md ├── Redis/ │ ├── index.md │ └── 浅尝.md ├── package.json ├── vue/ │ ├── Vue导出页面为PDF、word、html格式.md │ ├── element-audio.md │ ├── element-ui/ │ │ └── element更改表格表头、行、列、指定单元格样式.md │ ├── js中使用filter里面的方法.md │ ├── vue 中使用rem.md │ ├── vue 项目中如何在页面刷新的状态下保留数据.md │ ├── vue中怎么重置data?.md │ ├── 上传图片oss.md │ ├── 上传文件oss.md │ ├── 如何动态添加页面title 跟 favicon.ico.md │ ├── 子组件向父组件传参父组件添加自定义数据.md │ ├── 资料文档集合.md │ ├── 阿里云oss 上传.md │ └── 页面刷新的时候如何保存store.md ├── webpack/ │ └── 日常问题总结.md ├── 插件/ │ ├── Canvas 插件(签名板).md │ ├── echart.js.md │ ├── moment.js.md │ ├── one.md │ └── postCSS.md ├── 数据库/ │ ├── 数据库建表规则.md │ └── 设计规范.md ├── 正则/ │ ├── input框只能输入正整数.md │ └── 文本保存换行 跟空格.md └── 温顾而知新/ └── 知识点集合.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ================================================ FILE: .idea/$CACHE_FILE$ ================================================ ================================================ FILE: .idea/.gitignore ================================================ # Default ignored files /workspace.xml ================================================ FILE: .idea/inspectionProfiles/profiles_settings.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: .idea/work-sunnary.iml ================================================ ================================================ FILE: CSS/Input placeholder属性样式修改(颜色,大小,位置).md ================================================ ## Input placeholder属性样式修改(颜色,大小,位置) ```html 1 2 3 4 5 6 20 21 22 23 24 25 26 ``` *间距用padding* ================================================ FILE: CSS/common.css ================================================ @charset "utf-8"; /* _ooOoo_ o8888888o 88" . "88 (| -_- |) O\ = /O ____/`---'\____ .' \\| |// `. / \\||| : |||// \ / _||||| -:- |||||- \ | | \\\ - /// | | | \_| ''\---/'' | | \ .-\__ `-` ___/-. / ___`. .' /--.--\ `. . __ ."" '< `.___\_<|>_/___.' >'"". | | : `- \`.;`\ _ /`;.`/ - ` : | | \ \ `-. \_ __\ /__ _/ .-` / / ======`-.____`-.___\_____/___.-`____.-'====== `=---=' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 佛祖保佑 永无BUG */ /* @名称: 通用css @功能: 重置样式,清除浏览器默认样式,并配置适合设计的基础样式(强调文本是否大多是粗体、主文字色,主链接色,主字体等)。 功能样式,从常用样式方法中抽离,按需使用,使用前请先阅读 CSS规范 中相关条列。 常见动画效果的集合,主要用于效果演示和参考,也可以直接调用。 */ /* ---------- reset ---------- */ /* 防止用户自定义背景颜色对网页的影响,添加让用户可以自定义字体 */ html{color:#666;background:#fff;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} /* 内外边距通常让各个浏览器样式的表现位置不同 */ /*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{margin:0;padding:0;}*/ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, main, menu, nav, output, ruby, section, summary, time, mark, audio, video {margin: 0;padding: 0;border: 0;font-size: 100%;font: inherit;vertical-align: baseline;} /* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */ /*article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block;}*/ /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section {display: block;} /* HTML5 hidden-attribute fix for newer browsers */ *[hidden] {display: none;} /* HTML5 媒体文件跟 img 保持一致 */ audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} /* 要注意表单元素并不继承父级 font 的问题 */ body,button,input,select,textarea{font:12px/1 "Microsoft YaHei","微软雅黑","宋体","PingFang SC",Arial,sans-serif;} body {line-height: 1;} input,select,textarea{font-size:100%;} /* 去掉各Table cell 的边距并让其边重合 */ table{border-collapse:collapse;border-spacing:0;} /* IE bug fixed: th 不继承 text-align*/ th,pre,code,kbd,samp{text-align:inherit;} /* 去除默认边框 */ fieldset,img{border:0;} /* ie6 7 8(q) bug 显示为行内表现 */ iframe{display:block;} /* 去掉 firefox 下此元素的边框 */ abbr,acronym{border:0;font-variant:normal;} /* 一致的 del 样式 */ del{text-decoration:line-through;} address,caption,cite,code,dfn,em,th,var {font-style:normal;} /* 去掉列表前的标识, li 会继承 */ ol,ul{list-style:none;} /* 对齐是排版最重要的因素, 别让什么都居中 */ caption,th{text-align:left;} /* 让标题都自定义, 适应多个系统应用 */ h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500;} blockquote, q {quotes: none;} blockquote:before, blockquote:after, q:before, q:after {content: '';content: none;} /* 统一上标和下标 */ sub, sup{font-size:75%; line-height:0; position:relative; vertical-align:baseline;} sup{ top:-0.5em; } sub{ bottom:-0.25em; } /* 超链接和按钮鼠标手型 */ a,button{cursor:pointer;} /* 默认不显示下划线,保持页面简洁 */ ins,a{text-decoration:none;} /* 代码字体 */ code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;} /* 让字体显示正常 */ i,cite,em,var,address,dfn{font-style:normal;} /*去掉浏览器系统默认的触发边框*/ input,a{outline:0;} /* 文本域正常显示 */ textarea{overflow:auto;resize:none;} /* ---------- function ---------- */ /* 清除浮动 */ .clearfix:after{display:block;clear:both;visibility:hidden;height:0;overflow:hidden;content:".";} .clearfix{zoom:1;} /* for IE6 IE7 */ /* 隐藏, 通常用来与 JS 配合 */ .fn-hide{display:none;} /* 设置内联, 减少浮动带来的bug */ .fn-left,.fn-right{display:inline;} .fn-left{float:left;} .fn-right{float:right;} /* 定位 */ .fn-pr{position:relative;} .fn-pa{position:absolute;} .fn-pf{position:absolute;} .fn-prz{position:relative;zoom:1;} /*溢出隐藏*/ .fn-oh{overflow:hidden;} /* 单行文字溢出时出现省略号,需设定宽度 */ .fn-toe{overflow:hidden;word-wrap:normal;white-space:nowrap;text-overflow:ellipsis;} .fn-lhn{line-height:normal;} /* 多行省略适用于webkit内核和移动端 */ .fn-toe2 {display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;overflow: hidden;} .fn-toe3 {display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden;} .fn-toe4 {display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 4;overflow: hidden;} /*字体*/ .fn-ff0{font-family:\5b8b\4f53;} /*宋体*/ .fn-ff1{font-family:"Microsoft YaHei",\5fae\8f6f\96c5\9ed1,arial,\5b8b\4f53;} /* 字体大小 */ .fn-fs12{font-size:12px;} .fn-fs14{font-size:14px;} .fn-fs16{font-size:16px;} .fn-fs18{font-size:18px;} /* 字体粗细 */ .fn-fwn{font-weight:normal;} .fn-fwb{font-weight:bold;} /* 对齐方式 */ .fn-tal{text-align:left;} .fn-tac{text-align:center;} .fn-tar{text-align:right;} .fn-taj{text-align:justify;text-justify:inter-ideograph;} /*两端对齐*/ .fn-vam{vertical-align:middle;} /* 换行 */ .fn-wsp{overflow:hidden;text-align:left;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;} /* 强制不换行 */ .fn-wsn{word-wrap:normal;white-space:nowrap;} /* 强制换行 */ .fn-wwb{white-space:normal;word-wrap:break-word;word-break:break-all;} /* 文字缩进 */ .fn-ti{overflow:hidden;text-indent:-30000px;} .fn-ti2{text-indent:2em;} /* 鼠标触发 */ .fn-tdu,.fn-tdu:hover{text-decoration:underline;} .fn-tdn,.fn-tdn:hover{text-decoration:none;} /* 鼠标样式 */ .fn-csp{cursor:pointer;} .fn-csd{cursor:default;} .fn-csh{cursor:help;} .fn-csm{cursor:move;} /* 人民币符号 */ .fn-rmb{font-family:arial;font-style:normal;padding-right:4px;} /* chrome 下字体过小的问题 */ .fn-webkit-adjust{-webkit-text-size-adjust:none;} /* 禁选文本 */ .fn-usn{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;} /* ---------- 文本选中颜色 ---------- */ ::selection {background: #00CCFF;color:#fff;} ::-moz-selection {background:#00CCFF;color:#fff;} ::-webkit-selection {background:#00CCFF;color:#fff;} /* ---------- animation ---------- */ /* 淡入 */ .a-fadein{-webkit-animation-name:fadein;-moz-animation-name:fadein;-ms-animation-name:fadein;animation-name:fadein;} /* define */ /* 淡入 */ @-webkit-keyframes fadein{ 0%{opacity:0;} 100%{opacity:1;} } @-moz-keyframes fadein{ 0%{opacity:0;} 100%{opacity:1;} } @-ms-keyframes fadein{ 0%{opacity:0;} 100%{opacity:1;} } @keyframes fadein{ 0%{opacity:0;} 100%{opacity:1;} } /*padding*/ .fn-p5{padding:5px;} .fn-p10{padding:10px;} .fn-p15{padding:15px;} .fn-p20{padding:20px;} .fn-p25{padding:25px;} .fn-p30{padding:30px;} .fn-p40{padding:40px;} .fn-pt5{padding-top:5px;} .fn-pt10{padding-top:10px;} .fn-pt15{padding-top:15px;} .fn-pt20{padding-top:20px;} .fn-pt25{padding-top:25px;} .fn-pt30{padding-top:30px;} .fn-pt40{padding-top:40px;} .fn-pb5{padding-bottom:5px;} .fn-pb10{padding-bottom:10px;} .fn-pb15{padding-bottom:15px;} .fn-pb20{padding-bottom:20px;} .fn-pb25{padding-bottom:25px;} .fn-pb30{padding-bottom:30px;} .fn-pb40{padding-bottom:40px;} .fn-pl5{padding-left:5px;} .fn-pl10{padding-left:10px;} .fn-pl15{padding-left:15px;} .fn-pl20{padding-left:20px;} .fn-pl25{padding-left:25px;} .fn-pl30{padding-left:30px;} .fn-pl40{padding-left:40px;} .fn-pr5{padding-right:5px;} .fn-pr10{padding-right:10px;} .fn-pr15{padding-right:15px;} .fn-pr20{padding-right:20px;} .fn-pr25{padding-right:25px;} .fn-pr30{padding-right:30px;} .fn-pr40{padding-right:40px;} /*margin*/ .fn-m5{margin:5px;} .fn-m10{margin:10px;} .fn-m15{margin:15px;} .fn-m20{margin:20px;} .fn-m25{margin:25px;} .fn-m30{margin:30px;} .fn-m40{margin:40px;} .fn-mt5{margin-top:5px;} .fn-mt10{margin-top:10px;} .fn-mt15{margin-top:15px;} .fn-mt20{margin-top:20px;} .fn-mt25{margin-top:25px;} .fn-mt30{margin-top:30px;} .fn-mt40{margin-top:40px;} .fn-mb5{margin-bottom:5px;} .fn-mb10{margin-bottom:10px;} .fn-mb15{margin-bottom:15px;} .fn-mb20{margin-bottom:20px;} .fn-mb25{margin-bottom:25px;} .fn-mb30{margin-bottom:30px;} .fn-mb40{margin-bottom:40px;} .fn-ml5{margin-left:5px;} .fn-ml10{margin-left:10px;} .fn-ml15{margin-left:15px;} .fn-ml20{margin-left:20px;} .fn-ml25{margin-left:25px;} .fn-ml30{margin-left:30px;} .fn-ml40{margin-left:40px;} .fn-mr5{margin-right:5px;} .fn-mr10{margin-right:10px;} .fn-mr15{margin-right:15px;} .fn-mr20{margin-right:20px;} .fn-mr25{margin-right:25px;} .fn-mr30{margin-right:30px;} .fn-mr40{margin-right:40px;} .fn-ma{ margin:auto;} /*border-radius*/ .fn-br {border-radius: 50%} .fn-br5 {border-radius: 5px} .fn-br10 {border-radius: 10px} ================================================ FILE: CSS/less 中使用grid布局.md ================================================ # less中使用grid布局 CSS网格列被转换为不同的值 #### 使用grid布局通常会使用 ‘/’ 符号 但是 直接使用会导致 最终转义的css属性值 为数于数相除的结果 ### 解决方案 使用less中的转义(Escaping) ```css grid-area: ~"2/2/3/3"; // 属性前后分别用该写法代替即可 ``` #### Escaping: 转义(Escaping)允许你使用任意字符串作为属性或变量值。任何 ~"anything" 或 ~'anything' 形式的内容都将按原样输出,除非 interpolation ================================================ FILE: CSS/reset.css ================================================ /* http://meyerweb.com/eric/tools/css/reset/ v4.0 | 20180602 License: none (public domain) */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, main, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section { display: block; } /* HTML5 hidden-attribute fix for newer browsers */ *[hidden] { display: none; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } ================================================ FILE: CSS/常用css.md ================================================ ### 以下是常用的代码收集,没有任何技术含量。 #### 1. css 2.x - 文字换行 ```css /*强制不换行*/ white-space:nowrap; /*自动换行*/ word-wrap: break-word; word-break: normal; /*强制英文单词断行*/ word-break:break-all; ``` - 两端对齐 ```css text-align:justify;text-justify:inter-ideogra ``` - [去掉Webkit(chrome)浏览器中input(文本框)或textarea的黄色焦点框](http://www.cnblogs.com/niao/archive/2012/09/07/2674511.html) ```css input,button,select,textarea{ outline:none;} textarea{ resize:none;} ``` - [去掉chrome记住密码后自动填充表单的黄色背景](http://www.tuicool.com/articles/EZ777n ) - ie6: position:fixed ```css .fixed-top /* position fixed Top */{position:fixed;bottom:auto;top:0; } * html .fixed-top /* IE6 position fixed Top */{position:absolute;bottom:auto;top:expression(eval(document.documentElement.scrollTop));} *html{background-image:url(about:blank);background-attachment:fixed;} ``` - clearfix ```css .clearfix:before,.clearfix:after{display:table;content:"";} .clearfix:after{clear:both;} .clearfix:after{visibility:hidden;display:block;font-size:0;content:" ";clear:both;height:0;} .clearfix{display:inline-block;} html[xmlns] .clearfix{display:block;} * html .clearfix{height:1%;} .clearfix{*zoom: 1;} .clearfix:after{clear:both;display:table;content:"";} .clearfix{overflow:hidden;_zoom:1;} ``` [http://www.daqianduan.com/3606.html](http://www.daqianduan.com/3606.html) - seperate-table ```css .tab{border-collapse:separate;border:1px solid #e0e0e0;} .tab th,.tab td{padding:3px;font-size:12px;background:#f5f9fb;border:1px solid;border-color:#fff #deedf6 #deedf6 #fff;} .tab th{background:#edf4f0;} .tab tr.even td{background:#fff;} ``` ```html
111 222
111 222
``` - min-height: 最小高度兼容代码 ```css .minheight500{min-height:500px;height:auto !important;height:500px;overflow:visible;} ``` - 鼠标不允许点击 ```css cursor:not-allowed; ``` - mac font: osx平台字体优化 ```css font-family:"Hiragino Sans GB","Hiragino Sans GB W3",'微软雅黑'; ``` - 文字过多后显示省略号 ```css .ellipsis,.ell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis} ``` #### 2. css 3 - title 换行 ```html ``` - 关闭 x 符号 ```html × ``` - 投影 ```css .b{box-shadow:inset 1px -1px 0 #f1f1f1;text-shadow:1px 1px 0px #630;} filter:progid:DXImageTransform.Microsoft.gradient(enabled='true',startColorstr='#99000000',endColorstr='#99000000');background:rgba(0,0,0,.6); background:rgba(0,0,0,0.5);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#50000000',endColorstr='#50000000')\9; ``` - [search占位](http://www.qianduan.net/search-box-style-custom-webkit.html) ```css ::-webkit-input-placeholder {} ::-moz-input-placeholder {} input:focus::-webkit-input-placeholder { color: transparent; } -webkit-appearance:none; google边框去除 input[type="search"]{-webkit-appearance:textfield;} // 去除chrome默认样式 http://i.wanz.im/2011/02/04/remove_border_from_input_type_search/ http://blog.csdn.net/do_it__/article/details/6789699 line-height: normal; /* for non-ie */ line-height: 22px\9; /* for ie */ ``` - [全部浏览器的兼容代码生成](http://www.colorzilla.com/gradient-editor/ ) [CSS 实现 textArea 的 placeholder 换行](http://segmentfault.com/a/1190000000362621) - 阻止默认事件 ```css pointer-events:none; ``` - [去掉输入框聚焦时候的白色背景](http://ntesmailfetc.blog.163.com/blog/static/20628706120139184457401/) ```css -webkit-user-modify: read-write-plaintext-only; ``` - [input:focus时input不随软键盘升起而抬高的情况](http://www.cnblogs.com/hongru/archive/2013/02/06/2902938.html) ```css :focus{-webkit-tap-highlight-color:rgba(255, 255, 255, 0); -webkit-user-modify:read-write-plaintext-only;} ``` - 变灰 gray ```css html{ filter: grayscale(100%); -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grayscale(100%); -o-filter: grayscale(100%); filter: url("data:image/svg+xml;utf8,#grayscale"); filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); -webkit-filter: grayscale(1); } ``` - firefox 阻止选中 ```css -moz-user-focus:ignore;-moz-user-input:disabled;-moz-user-select:none; ``` - 箭头 ```css display:block;border:solid transparent;line-height: 0;width:0; height:0;border-top:solid #0288ce;border-width:8px 6px 0 6px; border-style:solid; border-width:7px; border-color:transparent transparent transparent #ff7020; position:absolute;top: 0;left: 0;border-width:20px;border-style:solid;border-color:#d1ddde transparent transparent #d1ddde; ``` ie6 bug测试,把border-style设为dashed. - 取消textarea右下角可拖动手柄 ```css resize:none ``` - 取消chrome form表单的聚焦边框 ```css input,button,select,textarea{outline:none} textarea{resize:none} ``` - 取消a链接的黄色边框 ```css a{-webkit-tap-highlight-color:rgba(0,0,0,0);} ``` - 取消input,button焦点或点击时蓝色边框 ```css input{outline:none;} ``` - webkit 水平居中 ```css display:-webkit-box;-webkit-box-pack:center; -webkit-box-align: center; position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); ``` - 取消chrome 搜索x提示 ```css input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration { display: none; } ``` - [chrome取消默认黄色背景](http://stackoverflow.com/questions/2338102/override-browser-form-filling-and-input-highlighting-with-html-css) ```css input:-webkit-autofill {-webkit-box-shadow: 0 0 0px 1000px white inset;} input:-webkit-autofill, textarea:-webkit-autofill, select:-webkit-autofill { -webkit-box-shadow: 0 0 0 1000px white inset; } autocomplete="off" ``` - 手机版本网页a标记虚线框问题 ```css a:focus {outline:none;-moz-outline:none;} ``` - 焦点去除背景 ```css -webkit-tap-highlight-color:rgba(255, 255, 255, 0); -webkit-tap-highlight-color:transparent; // i.e. Nexus5/Chrome and Kindle Fire HD 7'' ``` - placeholder占位符颜色自定义 ```css input:-moz-placeholder {color: #369;} ::-webkit-input-placeholder {color:#369;} ``` - [IOS 禁用高亮](http://hi.barretlee.com/2014/03/31/tap-highlight-in-webview/) ```css -webkit-tap-highlight-color:rgba(255,0,0,0.5);-webkit-tap-highlight-color:transparent; /* For some Androids */ ``` - IOS iframe 滚动 [滚动回弹特效](http://www.cnblogs.com/flash3d/archive/2013/09/28/3343877.html) ```css -webkit-overflow-scrolling:touch;overflow-y:scroll; ``` - [禁止选中文本](http://www.qianduan.net/introduce-user-select/) ```css -moz-user-select:none; -webkit-user-select:none; -ms-user-select:none; user-select:none; ``` - [模糊(毛玻璃)效果1](http://www.zhangxinxu.com/wordpress/2013/11/%E5%B0%8Ftip-%E4%BD%BF%E7%94%A8css%E5%B0%86%E5%9B%BE%E7%89%87%E8%BD%AC%E6%8D%A2%E6%88%90%E6%A8%A1%E7%B3%8A%E6%AF%9B%E7%8E%BB%E7%92%83%E6%95%88%E6%9E%9C/) - [模糊(毛玻璃)效果2](http://mao.li/css3-blur-filter-pratice/) - [模糊(毛玻璃)逼真效果](http://codepen.io/ariona/pen/geFIK) ```css .blur { -webkit-filter: blur(10px); /* Chrome, Opera */ -moz-filter: blur(10px); -ms-filter: blur(10px); filter: blur(10px); } ``` ```html ``` - 显示旋转加载图片,[下拉加载数据](https://github.com/chalecao/chale/blob/master/iscroll.js) ```css #pullDown .pullDownIcon{display:inline-block;vertical-align:middle;width:40px;height:40px;background:url(https://github.com/chalecao/chale/blob/master/pull-icon%402x.png) 0 0 no-repeat;-webkit-background-size:40px 80px;background-size:40px 80px;-webkit-transition-property:-webkit-transform;-webkit-transition-duration:250ms} #pullDown .pullDownIcon{-webkit-transform:rotate(0deg) translateZ(0)} #pullDown .pullDownLabel{display:inline-block;vertical-align:middle;margin-left:5px;} #pullDown.flip .pullDownIcon{-webkit-transform:rotate(-180deg) translateZ(0)} #pullDown.loading .pullDownIcon{background-position:0 100%;-webkit-transform:rotate(0deg) translateZ(0);-webkit-transition-duration:0ms;-webkit-animation-name:loading;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear} @-webkit-keyframes loading{ from{-webkit-transform:rotate(0deg) translateZ(0)} to{-webkit-transform:rotate(360deg) translateZ(0)} } ``` ```html
正在载入中...
``` - 手机多终端适配 media query[web app iphone4 iphone5 iphone6 响应式布局 适配代码](http://club.zoomla.cn/PItem?id=12594) ```css @media (device-height:480px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone4/4s */ .class{} } @media (device-height:568px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone5 */ .class{} } @media (device-height:667px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone6 */ .class{} } @media (device-height:736px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone6 Plus */ .class{} } ``` - 屏蔽苹果浏览器对数字的识别[Meta标签中的format-detection属性及含义](http://blog.sina.com.cn/s/blog_51048da70101cgea.html) ```html ``` - 移除HTML5 input在type="number"时的上下小箭头 - 在chrome下: ```css input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{ -webkit-appearance: none !important; margin: 0; } ``` - Firefox下: ```css input[type="number"]{-moz-appearance:textfield;} ``` - 第二种方案: - 将type="number"改为type="tel",同样是数字键盘,但是没有箭头。 - [HTML5手机浏览直接给一个号码打电话,发短信](http://java-er.com/blog/html5-mobile-call-sms/) ```html 移动WEB页面JS一键拨打号码咨询功能 移动WEB页面JS一键发送短信咨询功能 ``` - [CSS判断横屏竖屏](http://www.w3cways.com/1772.html) ```css @media screen and (orientation: portrait) { /*竖屏 css*/ } @media screen and (orientation: landscape) { /*横屏 css*/ } ``` ```javascript //判断手机横竖屏状态: window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() { if (window.orientation === 180 || window.orientation === 0) { alert('竖屏状态!'); } if (window.orientation === 90 || window.orientation === -90 ){ alert('横屏状态!'); } }, false); //移动端的浏览器一般都支持window.orientation这个参数,通过这个参数可以判断出手机是处在横屏还是竖屏状态。 ``` - rem 适配,内容太多,只贴网址 - [rem自适应方案](https://github.com/imweb/mobile/issues/3) - [html5移动端页面分辨率设置及相应字体大小设置的靠谱使用方式](http://www.cnblogs.com/willian/p/3573353.html) - [移动端高清、多屏适配方案](http://www.html-js.com/article/Mobile-terminal-H5-mobile-terminal-HD-multi-screen-adaptation-scheme%203041) - [通过rem布局+media-query:aspect-ratio实现移动端全机型适配覆盖](http://xiaoyuze88.github.io/blog/2015/05/12/%E9%80%9A%E8%BF%87rem%E5%B8%83%E5%B1%80+media-query%E7%9A%84aspect-ratio%E5%AE%9E%E7%8E%B0%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%85%A8%E6%9C%BA%E5%9E%8B%E9%80%82%E9%85%8D%E8%A6%86%E7%9B%96/) - [web app变革之rem](http://isux.tencent.com/web-app-rem.html) - [手机淘宝的flexible设计与实现](http://www.html-js.com/article/2402) - [移动端自适应方案](https://github.com/amfe/lib-flexible) - [【原创】移动端高清、多屏适配方案](http://www.html-js.com/article/3041) - [6个html5页面适配iphone6的技巧](http://qietuwang.baijia.baidu.com/article/73861) - [关于移动端 rem 布局的一些总结](http://segmentfault.com/a/1190000003690140) - [从网易与淘宝的font-size思考前端设计稿与工作流](http://www.cnblogs.com/lyzg/p/4877277.html) - [移动端自适应方案](http://f2e.souche.com/blog/yi-dong-duan-zi-gua-ying-fang-an/) - [MobileWeb 适配总结](http://www.w3ctech.com/topic/979) - [移动端web app自适应布局探索与总结](http://www.html-js.com/article/JavaScript-learning-notes%203234) - 公式 ```javascript var PAGE_MAX_WIDTH = 1280, BASE_FONT_SIZE = 50; (function() { function n() { e.fontSize = Math.min(window.innerWidth / PAGE_MAX_WIDTH * BASE_FONT_SIZE, BASE_FONT_SIZE) + "px" } var e = document.documentElement.style; window.addEventListener("load", n), window.addEventListener("resize", n), n(); }()); ``` - 页面的切换使用了page-enter ```html ``` - css相关总结网址 - [css常用效果总结](http://www.haorooms.com/post/css_common) - [css的不常用效果总结](http://www.haorooms.com/post/css_notuse_common) - [css开发技巧](http://www.haorooms.com/post/css_skill) - [重温css的选择器](http://www.haorooms.com/post/css_selectelement) - [css的变量和继承](http://www.haorooms.com/post/css_inherit_bl) - [css3的混合模式](http://www.haorooms.com/post/css3_mixblendmode) - [css中伪元素before或after中content的特殊用法attr](http://www.haorooms.com/post/content_attr) - 如何实现label长度固定,文字分散分布的效果 ```css /*css*/ .label { width: 200px; height: 30px; /*高度需要添加,不然文字下面会多出一些空隙*/ text-align: justify; } .label:after{ content: ''; display: inline-block; width: 100%; } /*html*/
产 品
``` ================================================ FILE: Git/使用规范.md ================================================ ## 目的 * 统一团队Git Commit标准,便于后续代码review、版本发布、自动化生成change log; * 可以提供更多更有效的历史信息,方便快速预览以及配合cherry-pick快速合并代码; * 团队其他成员进行类git blame时可以快速明白代码用意; ## Git版本规范 ### 分支 master分支为主分支(保护分支),不能直接在master上进行修改代码和提交; * develop分支为测试分支,所以开发完成需要提交测试的功能合并到该分支; * feature分支为开发分支,大家根据不同需求创建独立的功能分支,开发完成后合并到develop分支; * fix分支为bug修复分支,需要根据实际情况对已发布的版本进行漏洞修复; ### Tag 采用三段式,v版本.里程碑.序号,如v1.2.1 * 架构升级或架构重大调整,修改第2位 * 新功能上线或者模块大的调整,修改第2位 * bug修复上线,修改第3位 ### Git提交信息 message信息格式采用目前主流的Angular规范,这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。 ### commit message格式说明 Commit message一般包括三部分:Header、Body和Footer。 ### Header type(scope):subject ### type:用于说明commit的类别,规定为如下几种 * feat:新增功能; * fix:修复bug; * docs:修改文档; * refactor:代码重构,未新增任何功能和修复任何bug; * build:改变构建流程,新增依赖库、工具等(例如webpack修改); * style:仅仅修改了空格、缩进等,不改变代码逻辑; * perf:改善性能和体现的修改; * chore:非src和test的修改; * test:测试用例的修改; * ci:自动化流程配置修改; * revert:回滚到上一个版本; * scope:【可选】用于说明commit的影响范围 * subject:commit的简要说明,尽量简短 ### Body 对本次commit的详细描述,可分多行 ### Footer * 不兼容变动:需要描述相关信息 * 关闭指定Issue:输入Issue信息 ================================================ FILE: HTML/双飞翼布局.html ================================================
header
middle
left
================================================ FILE: HTML/圣杯布局.html ================================================
header
middle
left
================================================ FILE: HTML/防止移动端点击输入框页面变大.md ================================================ ### 移动端页面不放大 ```html ``` 需要显示工具栏和菜单栏时,不需要添加,默认值为no,即正常显示。如果content设置为yes,Web应用会以全屏模式运行,可以通过只读属性window.navigator.standalone来确定网页是否以全屏模式显示。 * width - viewport的宽度 * height - viewport的高度 * initial-scale - 初始的缩放比例 * minimum-scale - 允许用户缩放到的最小比例 * maximum-scale - 允许用户缩放到的最大比例 * user-scalable - 用户是否可以手动缩放 ================================================ FILE: HTTP/封装axios请求拦截器.md ================================================ ### 封装axios请求拦截器 ````javascript 1.8 import axios from 'axios'; import { message } from 'antd'; import config from './config.js' // 基于开发环境的配置文件 import { remove, findLast } from 'lodash'; import storage from '../utils/storage.ts' // 验证信息配置文件 const baseWaitTime = 500; // 默认的等待时间500毫秒 const requestURLRate = []; // 如:{ api: '/api/standardRoles', timestamp: 1596597701181 } /** * 请求出入口 * @param {*} api 地址 * @param {*} method 方法,默认为GET * @param {*} params 参数,默认为空对象 * @param {*} maxRequestCycleCount 最大请求频次(与baseWaitTime结合),默认为1 * @param {*} serverHost 接口主机地址 * @param {*} headers 传入头部信息,默认为空对象 */ export default function axiosRequest(api, method = 'GET', params = {}, headers = {}, maxRequestCycleCount = 1, serverHost = config.HOST) { // 针对非GET请求进行限流拦截 if (method != 'GET') { let nowTimestamp = new Date().getTime(); // 当前时间戳 // 去除当前接口指定周期外的数据 remove(requestURLRate, (o) => { return o.api === api && o.timestamp < nowTimestamp - (maxRequestCycleCount * baseWaitTime); }); // 获取上一次请求信息(一般同周期只有一个,防止处理意外) let hasRequestURLRate = findLast(requestURLRate, o => (o.api === api)); if (hasRequestURLRate && maxRequestCycleCount !== 0) { message.warning('当前访问的频次过高,请适当放慢手速!', 1); // 为了保持数据完整性,返回数据与接口定义一致 return { errcode: -100, msg: null }; } else { requestURLRate.push({ api, timestamp: new Date().getTime() }); } } return new Promise((resolve, reject) => { const token = storage().get('TOKEN'); axios({ method, headers: { authorization: 'Bearer ' + token, ...headers, }, url: (serverHost || global.G_SERVER_HOST) + api, data: params }) .then((res) => { if (res.status === 200) { if( res.data.error === 1){ // console.log() message.error(res.data.detail ||res.data.msg || '请求错误!'); // todo:错误收集 reject(res.data); }else if(res.data.error === 0 || !res.data.error){ resolve(res.data); } } }) .catch((error) => { console.log(error) if (error) { // todo:错误收集 message.error('服务端发生逻辑错误!'); } reject(error); }) }) } ```` ================================================ FILE: HTTP/设置请求头.md ================================================ ## 设置请求头 ```javascript 1.8 xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Content-Length', JSON.stringify(data).length); ``` ================================================ FILE: JAVASCRIPT/FormData使用方法.md ================================================ ## formData的主要用途有两个 1. 将form表单元素的name与value进行组合,实现表单数据的序列化,从而减少表单元素的拼接,提高工作效率。 2. 异步上传文件 ### 具体使用方法 #### 一、创建formData对象 1. 创建一个空对象 ```javascript 1.8 let formdata = new FormData();//利用FormData构造函数 创建一个空对象 formdata.append('name','value') //通过append() 方法追加数据 两个参数分别对应 name value formdata.set('name','zhangsan')// 通过set()方法 设置 name 为‘name’ 的value值 formdata.get('name') //通过get()方法获取name 为‘zhangsan’ 的value值 ``` 2. 通过表单创建初始化FormData ```html

广告名称:

广告类别:

``` 通过表单元素作为参数,实现对formData的初始化: ```html //获得表单按钮元素 var btn=document.querySelector("#btn"); //为按钮添加点击事件 btn.onclick=function(){ //根据ID获得页面当中的form表单元素 var form=document.querySelector("#advForm"); //将获得的表单元素作为参数,对formData进行初始化 var formdata=new FormData(form); //通过get方法获得name为advName元素的value值 console.log(formdata.get("advName"));//xixi //通过get方法获得name为advType元素的value值 console.log(formdata.get("advType"));//1 } ``` 3. 通过has(key)来判断是否存在对应的key值 ```javascript 1.8 console.log(formdata.has('zhangsan'))// true ``` 4. 通过delete(key)删除数据 ```javascript 1.8 formdata.delete('zhangsan') ``` #### 二、通过XMLHttpRequest()发送数据 ```javascript 1.8 // var formdata=new FormData(document.getElementById("advForm")); let formdata = new FormData(); formdata.append('name','value') formdata.get('name') var xhr=new XMLHttpRequest(); xhr.open("post","http://127.0.0.1/xxx"); xhr.send(formdata); xhr.onload=function(){ if(xhr.status==200){ //... } } ``` ================================================ FILE: JAVASCRIPT/JavaScript规范.md ================================================ /** * @name: JavaScript规范 * @author: LIULIU * @date: 2020-11-20 11:05 * @description:JavaScript规范 * @update: 2020-11-20 11:05 */ # Airbnb JavaScript 代码规范() { *一种写JavaScript更合理的代码风格。* > **Note**: 本指南假设你使用了 [Babel](https://babeljs.io), 并且要求你使用 [babel-preset-airbnb](https://npmjs.com/babel-preset-airbnb) 或者其他同等资源。 并且假设你在你的应用中安装了 shims/polyfills ,使用[airbnb-browser-shims](https://npmjs.com/airbnb-browser-shims) 或者相同功能。 [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 其他代码风格指南 - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) - [React](react/) - [CSS-in-JavaScript](css-in-javascript/) - [CSS & Sass](https://github.com/airbnb/css) - [Ruby](https://github.com/airbnb/ruby) ## 目录 1. [类型](#types) 1. [引用](#references) 1. [对象](#objects) 1. [数组](#arrays) 1. [解构](#destructuring) 1. [字符](#strings) 1. [方法](#functions) 1. [箭头函数](#arrow-functions) 1. [类和构造器](#classes--constructors) 1. [模块](#modules) 1. [迭代器和发生器](#iterators-and-generators) 1. [属性](#properties) 1. [变量](#variables) 1. [提升](#hoisting) 1. [比较运算符和等号](#comparison-operators--equality) 1. [块](#blocks) 1. [控制语句](#control-statements) 1. [注释](#comments) 1. [空白](#whitespace) 1. [逗号](#commas) 1. [分号](#semicolons) 1. [类型转换和强制类型转换](#type-casting--coercion) 1. [命名规范](#naming-conventions) 1. [存取器](#accessors) 1. [事件](#events) 1. [jQuery](#jquery) 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) 1. [ECMAScript 6+ (ES 2015+) 风格](#ecmascript-6-es-2015-styles) 1. [标准库](#standard-library) 1. [测试](#testing) 1. [性能](#performance) 1. [资源](#resources) 1. [JavaScript风格指南的指南](#the-javascript-style-guide-guide) 1. [许可证](#license) 1. [修正案](#amendments) ## 类型 - [1.1](#types--primitives) **原始值**: 当你访问一个原始类型的时候,你可以直接使用它的值。 - `string` - `number` - `boolean` - `null` - `undefined` - `symbol` ```javascript const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9 ``` - 标识符不能完全被支持,因此在针对不支持的浏览器或者环境时不应该使用它们。 - [1.2](#types--complex) **复杂类型**: 当你访问一个复杂类型的时候,你需要一个值得引用。 - `object` - `array` - `function` ```javascript const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 ``` **[⬆ 返回目录](#table-of-contents)** ## 引用 - [2.1](#references--prefer-const) 使用 `const` 定义你的所有引用;避免使用 `var`。 eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign.html) > 为什么? 这样能够确保你不能重新赋值你的引用,否则可能导致错误或者产生难以理解的代码。. ```javascript // bad var a = 1; var b = 2; // good const a = 1; const b = 2; ``` - [2.2](#references--disallow-var) 如果你必须重新赋值你的引用, 使用 `let` 代替 `var`。 eslint: [`no-var`](https://eslint.org/docs/rules/no-var.html) > 为什么? `let` 是块级作用域,而不像 `var` 是函数作用域. ```javascript // bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; } ``` - [2.3](#references--block-scope) 注意,let 和 const 都是块级范围的。 ```javascript // const 和 let 只存在于他们定义的块中。 { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError ``` **[⬆ 返回目录](#table-of-contents)** ## 对象 - [3.1](#objects--no-new) 使用字面语法来创建对象。 eslint: [`no-new-object`](https://eslint.org/docs/rules/no-new-object.html) ```javascript // bad const item = new Object(); // good const item = {}; ``` - [3.2](#es6-computed-properties) 在创建具有动态属性名称的对象时使用计算属性名。 > 为什么? 它允许你在一个地方定义对象的所有属性。 ```javascript function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, }; ``` - [3.3](#es6-object-shorthand) 使用对象方法的缩写。 eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html) ```javascript // bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, }; ``` - [3.4](#es6-object-concise) 使用属性值的缩写。 eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html) > 为什么? 它的写法和描述较短。 ```javascript const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, }; ``` - [3.5](#objects--grouped-shorthand) 在对象声明的时候将简写的属性进行分组。 > 为什么? 这样更容易的判断哪些属性使用的简写。 ```javascript const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, }; // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, }; ``` - [3.6](#objects--quoted-props) 只使用引号标注无效标识符的属性。 eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html) > 为什么? 总的来说,我们认为这样更容易阅读。 它提升了语法高亮显示,并且更容易通过许多 JS 引擎优化。 ```javascript // bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, }; ``` - [3.7](#objects--prototype-builtins) 不能直接调用 `Object.prototype` 的方法,如: `hasOwnProperty` 、 `propertyIsEnumerable` 和 `isPrototypeOf`。 > 为什么? 这些方法可能被以下问题对象的属性追踪 - 相应的有 `{ hasOwnProperty: false }` - 或者,对象是一个空对象 (`Object.create(null)`)。 ```javascript // bad console.log(object.hasOwnProperty(key)); // good console.log(Object.prototype.hasOwnProperty.call(object, key)); // best const has = Object.prototype.hasOwnProperty; // 在模块范围内的缓存中查找一次 /* or */ import has from 'has'; // https://www.npmjs.com/package/has // ... console.log(has.call(object, key)); ``` - [3.8](#objects--rest-spread) 更喜欢对象扩展操作符,而不是用 [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 浅拷贝一个对象。 使用对象的 rest 操作符来获得一个具有某些属性的新对象。 ```javascript // very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // 变异的 `original` ಠ_ಠ delete copy.a; // 这.... // bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 } ``` **[⬆ 返回目录](#table-of-contents)** ## 数组 - [4.1](#arrays--literals) 使用字面语法创建数组。 eslint: [`no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor.html) ```javascript // bad const items = new Array(); // good const items = []; ``` - [4.2](#arrays--push) 使用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 取代直接赋值来给数组添加项。 ```javascript const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra'); ``` - [4.3](#es6-array-spreads) 使用数组展开方法 `...` 来拷贝数组。 ```javascript // bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; ``` - [4.4](#arrays--from) 将一个类数组对象转换成一个数组, 使用展开方法 `...` 代替 [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)。 ```javascript const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo]; ``` - [4.5](#arrays--mapping) 对于对迭代器的映射,使用 [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 替代展开方法 `...` , 因为它避免了创建中间数组。 ```javascript // bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar); ``` - [4.6](#arrays--callback-return) 在数组回调方法中使用 return 语句。 如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值, 具体查看 [8.2](#arrows--implicit-return)。 eslint: [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return) ```javascript // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map(x => x + 1); // bad - 没有返回值,意味着在第一次迭代后 `acc` 没有被定义 [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; }); // good [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; return flatten; }); // bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } }); // good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } return false; }); ``` - [4.7](#arrays--bracket-newline) 如果数组有多行,则在开始的时候换行,然后在结束的时候换行。 ```javascript // bad const arr = [ [0, 1], [2, 3], [4, 5], ]; const objectInArray = [{ id: 1, }, { id: 2, }]; const numberInArray = [ 1, 2, ]; // good const arr = [[0, 1], [2, 3], [4, 5]]; const objectInArray = [ { id: 1, }, { id: 2, }, ]; const numberInArray = [ 1, 2, ]; ``` **[⬆ 返回目录](#table-of-contents)** ## 解构 - [5.1](#destructuring--object) 在访问和使用对象的多个属性的时候使用对象的解构。 eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) > 为什么? 解构可以避免为这些属性创建临时引用。 ```javascript // bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // good function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`; } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; } ``` - [5.2](#destructuring--array) 使用数组解构。 eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) ```javascript const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr; ``` - [5.3](#destructuring--object-over-array) 对于多个返回值使用对象解构,而不是数组解构。 > 为什么? 你可以随时添加新的属性或者改变属性的顺序,而不用修改调用方。 ```javascript // bad function processInput(input) { // 处理代码... return [left, right, top, bottom]; } // 调用者需要考虑返回数据的顺序。 const [left, __, top] = processInput(input); // good function processInput(input) { // 处理代码... return { left, right, top, bottom }; } // 调用者只选择他们需要的数据。 const { left, top } = processInput(input); ``` **[⬆ 返回目录](#table-of-contents)** ## 字符 - [6.1](#strings--quotes) 使用单引号 `''` 定义字符串。 eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html) ```javascript // bad const name = "Capt. Janeway"; // bad - 模板文字应该包含插值或换行。 const name = `Capt. Janeway`; // good const name = 'Capt. Janeway'; ``` - [6.2](#strings--line-length) 使行超过100个字符的字符串不应使用字符串连接跨多行写入。 > 为什么? 断开的字符串更加难以工作,并且使代码搜索更加困难。 ```javascript // bad const errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; // bad const errorMessage = 'This is a super long error that was thrown because ' + 'of Batman. When you stop to think about how Batman had anything to do ' + 'with this, you would get nowhere fast.'; // good const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; ``` - [6.3](#es6-template-literals) 当以编程模式构建字符串时,使用字符串模板代替字符串拼接。 eslint: [`prefer-template`](https://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](https://eslint.org/docs/rules/template-curly-spacing) > 为什么? 字符串模板为您提供了一种可读的、简洁的语法,具有正确的换行和字符串插值特性。 ```javascript // bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // bad function sayHi(name) { return `How are you, ${ name }?`; } // good function sayHi(name) { return `How are you, ${name}?`; } ``` - [6.4](#strings--eval) 不要在字符串上使用 `eval()` ,它打开了太多漏洞。 eslint: [`no-eval`](https://eslint.org/docs/rules/no-eval) - [6.5](#strings--escaping) 不要转义字符串中不必要的字符。 eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape) > 为什么? 反斜杠损害了可读性,因此只有在必要的时候才会出现。 ```javascript // bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`; ``` **[⬆ 返回目录](#table-of-contents)** ## 方法 - [7.1](#functions--declarations) 使用命名的函数表达式代替函数声明。 eslint: [`func-style`](https://eslint.org/docs/rules/func-style) > 为什么? 函数声明是挂起的,这意味着在它在文件中定义之前,很容易引用函数。这会损害可读性和可维护性。如果您发现函数的定义是大的或复杂的,以至于它干扰了对文件的其余部分的理解,那么也许是时候将它提取到它自己的模块中了!不要忘记显式地命名这个表达式,不管它的名称是否从包含变量(在现代浏览器中经常是这样,或者在使用诸如Babel之类的编译器时)。这消除了对错误的调用堆栈的任何假设。 ([Discussion](https://github.com/airbnb/javascript/issues/794)) ```javascript // bad function foo() { // ... } // bad const foo = function () { // ... }; // good // 从变量引用调用中区分的词汇名称 const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... }; ``` - [7.2](#functions--iife) Wrap立即调用函数表达式。 eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife.html) > 为什么? 立即调用的函数表达式是单个单元 - 包装, 并且拥有括号调用, 在括号内, 清晰的表达式。 请注意,在一个到处都是模块的世界中,您几乎不需要一个 IIFE 。 ```javascript // immediately-invoked function expression (IIFE) 立即调用的函数表达式 (function () { console.log('Welcome to the Internet. Please follow me.'); }()); ``` - [7.3](#functions--in-blocks) 切记不要在非功能块中声明函数 (`if`, `while`, 等)。 将函数赋值给变量。 浏览器允许你这样做,但是他们都有不同的解释,这是个坏消息。 eslint: [`no-loop-func`](https://eslint.org/docs/rules/no-loop-func.html) - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 将 `block` 定义为语句列表。 函数声明不是语句。 ```javascript // bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; } ``` - [7.5](#functions--arguments-shadow) 永远不要定义一个参数为 `arguments`。 这将会优先于每个函数给定范围的 `arguments` 对象。 ```javascript // bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... } ``` - [7.6](#es6-rest) 不要使用 `arguments`, 选择使用 rest 语法 `...` 代替。 eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params) > 为什么? `...` 明确了你想要拉取什么参数。 更甚, rest 参数是一个真正的数组,而不仅仅是类数组的 `arguments` 。 ```javascript // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); } ``` - [7.7](#es6-default-parameters) 使用默认的参数语法,而不是改变函数参数。 ```javascript // really bad function handleThings(opts) { // No! We shouldn’t mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... } ``` - [7.8](#functions--default-side-effects) 避免使用默认参数的副作用。 > 为什么? 他们很容易混淆。 ```javascript var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3 ``` - [7.9](#functions--defaults-last) 总是把默认参数放在最后。 ```javascript // bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... } ``` - [7.10](#functions--constructor) 永远不要使用函数构造器来创建一个新函数。 eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func) > 为什么? 以这种方式创建一个函数将对一个类似于 `eval()` 的字符串进行计算,这将打开漏洞。 ```javascript // bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b'); ``` - [7.11](#functions--signature-spacing) 函数签名中的间距。 eslint: [`space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks) > 为什么? 一致性很好,在删除或添加名称时不需要添加或删除空格。 ```javascript // bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {}; ``` - [7.12](#functions--mutate-params) 没用变异参数。 eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html) > 为什么? 将传入的对象作为参数进行操作可能会在原始调用程序中造成不必要的变量副作用。 ```javascript // bad function f1(obj) { obj.key = 1; } // good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; } ``` - [7.13](#functions--reassign-params) 不要再赋值参数。 eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html) > 为什么? 重新赋值参数会导致意外的行为,尤其是在访问 `arguments` 对象的时候。 它还可能导致性能优化问题,尤其是在 V8 中。 ```javascript // bad function f1(a) { a = 1; // ... } function f2(a) { if (!a) { a = 1; } // ... } // good function f3(a) { const b = a || 1; // ... } function f4(a = 1) { // ... } ``` - [7.14](#functions--spread-vs-apply) 优先使用扩展运算符 `...` 来调用可变参数函数。 eslint: [`prefer-spread`](https://eslint.org/docs/rules/prefer-spread) > 为什么? 它更加干净,你不需要提供上下文,并且你不能轻易的使用 `apply` 来 `new` 。 ```javascript // bad const x = [1, 2, 3, 4, 5]; console.log.apply(console, x); // good const x = [1, 2, 3, 4, 5]; console.log(...x); // bad new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good new Date(...[2016, 8, 5]); ``` - [7.15](#functions--signature-invocation-indentation) 具有多行签名或者调用的函数应该像本指南中的其他多行列表一样缩进:在一行上只有一个条目,并且每个条目最后加上逗号。 eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline) ```javascript // bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, ); ``` **[⬆ 返回目录](#table-of-contents)** ## 箭头函数 - [8.1](#arrows--use-them) 当你必须使用匿名函数时 (当传递内联函数时), 使用箭头函数。 eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing.html) > 为什么? 它创建了一个在 `this` 上下文中执行的函数版本,它通常是你想要的,并且是一个更简洁的语法。 > 为什么不? 如果你有一个相当复杂的函数,你可以把这个逻辑转移到它自己的命名函数表达式中。 ```javascript // bad [1, 2, 3].map(function (x) { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); ``` - [8.2](#arrows--implicit-return) 如果函数体包含一个单独的语句,返回一个没有副作用的 [expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions) , 省略括号并使用隐式返回。否则,保留括号并使用 `return` 语句。 eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](https://eslint.org/docs/rules/arrow-body-style.html) > 为什么? 语法糖。 多个函数被链接在一起时,提高可读性。 ```javascript // bad [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map(number => `A string containing the ${number}.`); // good [1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map((number, index) => ({ [index]: number, })); // 没有副作用的隐式返回 function foo(callback) { const val = callback(); if (val === true) { // 如果回调返回 true 执行 } } let bool = false; // bad foo(() => bool = true); // good foo(() => { bool = true; }); ``` - [8.3](#arrows--paren-wrap) 如果表达式跨越多个行,用括号将其括起来,以获得更好的可读性。 > 为什么? 它清楚地显示了函数的起点和终点。 ```javascript // bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) )); ``` - [8.4](#arrows--one-arg-parens) 如果你的函数接收一个参数,则可以不用括号,省略括号。 否则,为了保证清晰和一致性,需要在参数周围加上括号。 注意:总是使用括号是可以接受的,在这种情况下,我们使用 [“always” option](https://eslint.org/docs/rules/arrow-parens#always) 来配置 eslint. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html) > 为什么? 减少视觉上的混乱。 ```javascript // bad [1, 2, 3].map((x) => x * x); // good [1, 2, 3].map(x => x * x); // good [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); ``` - [8.5](#arrows--confusing) 避免箭头函数符号 (`=>`) 和比较运算符 (`<=`, `>=`) 的混淆。 eslint: [`no-confusing-arrow`](https://eslint.org/docs/rules/no-confusing-arrow) ```javascript // bad const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; // bad const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; // good const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); // good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize; }; ``` - [8.6](#whitespace--implicit-arrow-linebreak) 注意带有隐式返回的箭头函数函数体的位置。 eslint: [`implicit-arrow-linebreak`](https://eslint.org/docs/rules/implicit-arrow-linebreak) ```javascript // bad (foo) => bar; (foo) => (bar); // good (foo) => bar; (foo) => (bar); (foo) => ( bar ) ``` **[⬆ 返回目录](#table-of-contents)** ## 类和构造器 - [9.1](#constructors--use-class) 尽量使用 `class`. 避免直接操作 `prototype` . > 为什么? `class` 语法更简洁,更容易推理。 ```javascript // bad function Queue(contents = []) { this.queue = [...contents]; } Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }; // good class Queue { constructor(contents = []) { this.queue = [...contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; } } ``` - [9.2](#constructors--extends) 使用 `extends` 来扩展继承。 > 为什么? 它是一个内置的方法,可以在不破坏 `instanceof` 的情况下继承原型功能。 ```javascript // bad const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; }; // good class PeekableQueue extends Queue { peek() { return this.queue[0]; } } ``` - [9.3](#constructors--chaining) 方法返回了 `this` 来供其内部方法调用。 ```javascript // bad Jedi.prototype.jump = function () { this.jumping = true; return true; }; Jedi.prototype.setHeight = function (height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20); ``` - [9.4](#constructors--tostring) 只要在确保能正常工作并且不产生任何副作用的情况下,编写一个自定义的 `toString()` 方法也是可以的。 ```javascript class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } } ``` - [9.5](#constructors--no-useless) 如果没有指定类,则类具有默认的构造器。 一个空的构造器或是一个代表父类的函数是没有必要的。 eslint: [`no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor) ```javascript // bad class Jedi { constructor() {} getName() { return this.name; } } // bad class Rey extends Jedi { constructor(...args) { super(...args); } } // good class Rey extends Jedi { constructor(...args) { super(...args); this.name = 'Rey'; } } ``` - [9.6](#classes--no-duplicate-members) 避免定义重复的类成员。 eslint: [`no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members) > 为什么? 重复的类成员声明将会默认倾向于最后一个 - 具有重复的类成员可以说是一个错误。 ```javascript // bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } } ``` **[⬆ 返回目录](#table-of-contents)** ## 模块 - [10.1](#modules--use-them) 你可能经常使用模块 (`import`/`export`) 在一些非标准模块的系统上。 你也可以在你喜欢的模块系统上相互转换。 > 为什么? 模块是未来的趋势,让我们拥抱未来。 ```javascript // bad const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // ok import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // best import { es6 } from './AirbnbStyleGuide'; export default es6; ``` - [10.2](#modules--no-wildcard) 不要使用通配符导入。 > 为什么? 这确定你有一个单独的默认导出。 ```javascript // bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide'; ``` - [10.3](#modules--no-export-from-import) 不要直接从导入导出。 > 为什么? 虽然写在一行很简洁,但是有一个明确的导入和一个明确的导出能够保证一致性。 ```javascript // bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6; ``` - [10.4](#modules--no-duplicate-imports) 只从一个路径导入所有需要的东西。 eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports) > 为什么? 从同一个路径导入多个行,使代码更难以维护。 ```javascript // bad import foo from 'foo'; // … 其他导入 … // import { named1, named2 } from 'foo'; // good import foo, { named1, named2 } from 'foo'; // good import foo, { named1, named2, } from 'foo'; ``` - [10.5](#modules--no-mutable-exports) 不要导出可变的引用。 eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) > 为什么? 在一般情况下,应该避免发生突变,但是在导出可变引用时及其容易发生突变。虽然在某些特殊情况下,可能需要这样,但是一般情况下只需要导出常量引用。 ```javascript // bad let foo = 3; export { foo }; // good const foo = 3; export { foo }; ``` - [10.6](#modules--prefer-default-export) 在单个导出的模块中,选择默认模块而不是指定的导出。 eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) > 为什么? 为了鼓励更多的文件只导出一件东西,这样可读性和可维护性更好。 ```javascript // bad export function foo() {} // good export default function foo() {} ``` - [10.7](#modules--imports-first) 将所有的 `import`s 语句放在所有非导入语句的上边。 eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md) > 为什么? 由于所有的 `import`s 都被提前,保持他们在顶部是为了防止意外发生。 ```javascript // bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init(); ``` - [10.8](#modules--multiline-imports-over-newlines) 多行导入应该像多行数组和对象一样缩进。 > 为什么? 花括号和其他规范一样,遵循相同的缩进规则,后边的都好一样。 ```javascript // bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path'; ``` - [10.9](#modules--no-webpack-loader-syntax) 在模块导入语句中禁止使用 Webpack 加载器语法。 eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) > 为什么? 因为在导入语句中使用 webpack 语法,将代码和模块绑定在一起。应该在 `webpack.config.js` 中使用加载器语法。 ```javascript // bad import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css'; // good import fooSass from 'foo.scss'; import barCss from 'bar.css'; ``` **[⬆ 返回目录](#table-of-contents)** ## 迭代器和发生器 - [11.1](#iterators--nope) 不要使用迭代器。 你应该使用 JavaScript 的高阶函数代替 `for-in` 或者 `for-of`。 eslint: [`no-iterator`](https://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](https://eslint.org/docs/rules/no-restricted-syntax) > 为什么? 这是我们强制的规则。 拥有返回值得纯函数比这个更容易解释。 > 使用 `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... 遍历数组, 和使用 `Object.keys()` / `Object.values()` / `Object.entries()` 迭代你的对象生成数组。 ```javascript const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15; // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // good const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); }); // best (keeping it functional) const increasedByOne = numbers.map(num => num + 1); ``` - [11.2](#generators--nope) 不要使用发生器。 > 为什么? 它们不能很好的适应 ES5。 - [11.3](#generators--spacing) 如果你必须使用发生器或者无视 [我们的建议](#generators--nope),请确保他们的函数签名是正常的间隔。 eslint: [`generator-star-spacing`](https://eslint.org/docs/rules/generator-star-spacing) > 为什么? `function` 和 `*` 是同一个概念关键字的一部分 - `*` 不是 `function` 的修饰符, `function*` 是一个不同于 `function` 的构造器。 ```javascript // bad function * foo() { // ... } // bad const bar = function * () { // ... }; // bad const baz = function *() { // ... }; // bad const quux = function*() { // ... }; // bad function*foo() { // ... } // bad function *foo() { // ... } // very bad function * foo() { // ... } // very bad const wat = function * () { // ... }; // good function* foo() { // ... } // good const foo = function* () { // ... }; ``` **[⬆ 返回目录](#table-of-contents)** ## 属性 - [12.1](#properties--dot) 访问属性时使用点符号。 eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation.html) ```javascript const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi; ``` - [12.2](#properties--bracket) 使用变量访问属性时,使用 `[]`表示法。 ```javascript const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi'); ``` - [12.3](#es2016-properties--exponentiation-operator) 计算指数时,可以使用 `**` 运算符。 eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties). ```javascript // bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10; ``` **[⬆ 返回目录](#table-of-contents)** ## 变量 - [13.1](#variables--const) 使用 `const` 或者 `let` 来定义变量。 不这样做将创建一个全局变量。 我们希望避免污染全局命名空间。 Captain Planet 警告过我们。 eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const) ```javascript // bad superPower = new SuperPower(); // good const superPower = new SuperPower(); ``` - [13.2](#variables--one-const) 使用 `const` 或者 `let` 声明每一个变量。 eslint: [`one-var`](https://eslint.org/docs/rules/one-var.html) > 为什么? 这样更容易添加新的变量声明,而且你不必担心是使用 `;` 还是使用 `,` 或引入标点符号的差别。 你可以通过 debugger 逐步查看每个声明,而不是立即跳过所有声明。 ```javascript // bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z'; ``` - [13.3](#variables--const-let-group) 把 `const` 声明的放在一起,把 `let` 声明的放在一起。. > 为什么? 这在后边如果需要根据前边的赋值变量指定一个变量时很有用。 ```javascript // bad let i, len, dragonball, items = getItems(), goSportsTeam = true; // bad let i; const items = getItems(); let dragonball; const goSportsTeam = true; let len; // good const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length; ``` - [13.4](#variables--define-where-used) 在你需要的使用定义变量,但是要把它们放在一个合理的地方。 > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 ```javascript // bad - 不必要的函数调用 function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // good function checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; } ``` - [13.5](#variables--no-chain-assignment) 不要链式变量赋值。 eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign) > 为什么? 链式变量赋值会创建隐式全局变量。 ```javascript // bad (function example() { // JavaScript 把它解释为 // let a = ( b = ( c = 1 ) ); // let 关键词只适用于变量 a ;变量 b 和变量 c 则变成了全局变量。 let a = b = c = 1; }()); console.log(a); // throws ReferenceError console.log(b); // 1 console.log(c); // 1 // good (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // throws ReferenceError console.log(b); // throws ReferenceError console.log(c); // throws ReferenceError // 对于 `const` 也一样 ``` - [13.6](#variables--unary-increment-decrement) 避免使用不必要的递增和递减 (`++`, `--`)。 eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus) > 为什么? 在eslint文档中,一元递增和递减语句以自动分号插入为主题,并且在应用程序中可能会导致默认值的递增或递减。它还可以用像 `num += 1` 这样的语句来改变您的值,而不是使用 `num++` 或 `num ++` 。不允许不必要的增量和减量语句也会使您无法预先递增/预递减值,这也会导致程序中的意外行为。 ```javascript // bad const array = [1, 2, 3]; let num = 1; num++; --num; let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } } // good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length; ``` - [13.7](#variables--linebreak) 避免在赋值语句 `=` 前后换行。如果你的代码违反了 [`max-len`](https://eslint.org/docs/rules/max-len.html), 使用括号包裹。 eslint [`operator-linebreak`](https://eslint.org/docs/rules/operator-linebreak.html). > 为什么? 在 `=` 前后换行,可能混淆赋的值。 ```javascript // bad const foo = superLongLongLongLongLongLongLongLongFunctionName(); // bad const foo = 'superLongLongLongLongLongLongLongLongString'; // good const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // good const foo = 'superLongLongLongLongLongLongLongLongString'; ``` **[⬆ 返回目录](#table-of-contents)** ## 提升 - [14.1](#hoisting--about) `var` 定义的变量会被提升到函数范围的最顶部,但是它的赋值不会。`const` 和 `let` 声明的变量受到一个称之为 [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone_and_errors_with_let) 的新概念保护。 知道为什么 [typeof 不再安全](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15) 是很重要的。 ```javascript // 我们知道这个行不通 (假设没有未定义的全局变量) function example() { console.log(notDefined); // => throws a ReferenceError } // 在引用变量后创建变量声明将会因变量提升而起作用。 // 注意: 真正的值 `true` 不会被提升。 function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // 解释器将变量提升到函数的顶部 // 这意味着我们可以将上边的例子重写为: function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // 使用 const 和 let function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; } ``` - [14.2](#hoisting--anon-expressions) 匿名函数表达式提升变量名,而不是函数赋值。 ```javascript function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log('anonymous function expression'); }; } ``` - [14.3](#hoisting--named-expressions) 命名函数表达式提升的是变量名,而不是函数名或者函数体。 ```javascript function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // 当函数名和变量名相同时也是如此。 function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); }; } ``` - [14.4](#hoisting--declarations) 函数声明提升其名称和函数体。 ```javascript function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } } ``` - 更多信息请参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/)。 **[⬆ 返回目录](#table-of-contents)** ## 比较运算符和等号 - [15.1](#comparison--eqeqeq) 使用 `===` 和 `!==` 而不是 `==` 和 `!=`。 eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq.html) - [15.2](#comparison--if) 条件语句,例如 `if` 语句使用 `ToBoolean` 的抽象方法来计算表达式的结果,并始终遵循以下简单的规则: - **Objects** 的取值为: **true** - **Undefined** 的取值为: **false** - **Null** 的取值为: **false** - **Booleans** 的取值为: **布尔值的取值** - **Numbers** 的取值为:如果为 **+0, -0, or NaN** 值为 **false** 否则为 **true** - **Strings** 的取值为: 如果是一个空字符串 `''` 值为 **false** 否则为 **true** ```javascript if ([0] && []) { // true // 一个数组(即使是空的)是一个对象,对象的取值为 true } ``` - [15.3](#comparison--shortcuts) 对于布尔值使用简写,但是对于字符串和数字进行显式比较。 ```javascript // bad if (isValid === true) { // ... } // good if (isValid) { // ... } // bad if (name) { // ... } // good if (name !== '') { // ... } // bad if (collection.length) { // ... } // good if (collection.length > 0) { // ... } ``` - [15.4](#comparison--moreinfo) 获取更多信息请查看 Angus Croll 的 [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) 。 - [15.5](#comparison--switch-blocks) 在 `case` 和 `default` 的子句中,如果存在声明 (例如. `let`, `const`, `function`, 和 `class`),使用大括号来创建块 。 eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations.html) > 为什么? 语法声明在整个 `switch` 块中都是可见的,但是只有在赋值的时候才会被初始化,这种情况只有在 `case` 条件达到才会发生。 当多个 `case` 语句定义相同的东西是,这会导致问题问题。 ```javascript // bad switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} } // good switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } } ``` - [15.6](#comparison--nested-ternaries) 三目表达式不应该嵌套,通常是单行表达式。 eslint: [`no-nested-ternary`](https://eslint.org/docs/rules/no-nested-ternary.html) ```javascript // bad const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null; // 分离为两个三目表达式 const maybeNull = value1 > value2 ? 'baz' : null; // better const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // best const foo = maybe1 > maybe2 ? 'bar' : maybeNull; ``` - [15.7](#comparison--unneeded-ternary) 避免不必要的三目表达式。 eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary.html) ```javascript // bad const foo = a ? a : b; const bar = c ? true : false; const baz = c ? false : true; // good const foo = a || b; const bar = !!c; const baz = !c; ``` - [15.8](#comparison--no-mixed-operators) 使用该混合运算符时,使用括号括起来。 唯一例外的是标准算数运算符 (`+`, `-`, `*`, & `/`) 因为他们的优先级被广泛理解。 eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators.html) > 为什么? 这能提高可读性并且表明开发人员的意图。 ```javascript // bad const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad const bar = a ** b - 5 % d; // bad // 可能陷入一种 (a || b) && c 的思考 if (a || b && c) { return d; } // good const foo = (a && b < 0) || c > 0 || (d + 1 === 0); // good const bar = (a ** b) - (5 % d); // good if (a || (b && c)) { return d; } // good const bar = a + b / c * d; ``` **[⬆ 返回目录](#table-of-contents)** ## - [16.1](#blocks--braces) 当有多行代码块的时候,使用大括号包裹。 eslint: [`nonblock-statement-body-position`](https://eslint.org/docs/rules/nonblock-statement-body-position) ```javascript // bad if (test) return false; // good if (test) return false; // good if (test) { return false; } // bad function foo() { return false; } // good function bar() { return false; } ``` - [16.2](#blocks--cuddled-elses) 如果你使用的是 `if` 和 `else` 的多行代码块,则将 `else` 语句放在 `if` 块闭括号同一行的位置。 eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style.html) ```javascript // bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); } ``` - [16.3](#blocks--no-else-return) 如果一个 `if` 块总是执行一个 `return` 语句,那么接下来的 `else` 块就没有必要了。 如果一个包含 `return` 语句的 `else if` 块,在一个包含了 `return` 语句的 `if` 块之后,那么可以拆成多个 `if` 块。 eslint: [`no-else-return`](https://eslint.org/docs/rules/no-else-return) ```javascript // bad function foo() { if (x) { return x; } else { return y; } } // bad function cats() { if (x) { return x; } else if (y) { return y; } } // bad function dogs() { if (x) { return x; } else { if (y) { return y; } } } // good function foo() { if (x) { return x; } return y; } // good function cats() { if (x) { return x; } if (y) { return y; } } // good function dogs(x) { if (x) { if (z) { return y; } } else { return z; } } ``` **[⬆ 返回目录](#table-of-contents)** ## 控制语句 - [17.1](#control-statements) 如果你的控制语句 (`if`, `while` 等) 太长或者超过了一行最大长度的限制,则可以将每个条件(或组)放入一个新的行。 逻辑运算符应该在行的开始。 > 为什么? 要求操作符在行的开始保持对齐并遵循类似方法衔接的模式。 这提高了可读性,并且使更复杂的逻辑更容易直观的被理解。 ```javascript // bad if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1(); } // bad if (foo === 123 && bar === 'abc') { thing1(); } // bad if (foo === 123 && bar === 'abc') { thing1(); } // bad if ( foo === 123 && bar === 'abc' ) { thing1(); } // good if ( foo === 123 && bar === 'abc' ) { thing1(); } // good if ( (foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening() ) { thing1(); } // good if (foo === 123 && bar === 'abc') { thing1(); } ``` - [17.2](#control-statements--value-selection) 不要使用选择操作符代替控制语句。 ```javascript // bad !isRunning && startRunning(); // good if (!isRunning) { startRunning(); } ``` **[⬆ 返回目录](#table-of-contents)** ## 注释 - [18.1](#comments--multiline) 使用 `/** ... */` 来进行多行注释。 ```javascript // bad // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } ``` - [18.2](#comments--singleline) 使用 `//` 进行单行注释。 将单行注释放在需要注释的行的上方新行。 在注释之前放一个空行,除非它在块的第一行。 ```javascript // bad const active = true; // is current tab // good // is current tab const active = true; // bad function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // also good function getType() { // set the default type to 'no type' const type = this.type || 'no type'; return type; } ``` - [18.3](#comments--spaces) 用一个空格开始所有的注释,使它更容易阅读。 eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment) ```javascript // bad //is current tab const active = true; // good // is current tab const active = true; // bad /** *make() returns a new element *based on the passed-in tag name */ function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } ``` - [18.4](#comments--actionitems) 使用 `FIXME` 或者 `TODO` 开始你的注释可以帮助其他开发人员快速了解,如果你提出了一个需要重新审视的问题,或者你对需要实现的问题提出的解决方案。 这些不同于其他评论,因为他们是可操作的。 这些行为是 `FIXME: -- 需要解决这个问题` 或者 `TODO: -- 需要被实现`。 - [18.5](#comments--fixme) 使用 `// FIXME:` 注释一个问题。 ```javascript class Calculator extends Abacus { constructor() { super(); // FIXME: 这里不应该使用全局变量 total = 0; } } ``` - [18.6](#comments--todo) 使用 `// TODO:` 注释解决问题的方法。 ```javascript class Calculator extends Abacus { constructor() { super(); // TODO: total 应该由一个 param 的选项配置 this.total = 0; } } ``` **[⬆ 返回目录](#table-of-contents)** ## 空白 - [19.1](#whitespace--spaces) 使用 tabs (空格字符) 设置为 2 个空格。 eslint: [`indent`](https://eslint.org/docs/rules/indent.html) ```javascript // bad function foo() { ∙∙∙∙let name; } // bad function bar() { ∙let name; } // good function baz() { ∙∙let name; } ``` - [19.2](#whitespace--before-blocks) 在主体前放置一个空格。 eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks.html) ```javascript // bad function test(){ console.log('test'); } // good function test() { console.log('test'); } // bad dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog', }); // good dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog', }); ``` - [19.3](#whitespace--around-keywords) 在控制语句(`if`, `while` 等)开始括号之前放置一个空格。 在函数调用和是声明中,在参数列表和函数名之间没有空格。 eslint: [`keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing.html) ```javascript // bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ('Swooosh!'); } // good function fight() { console.log('Swooosh!'); } ``` - [19.4](#whitespace--infix-ops) 用空格分离操作符。 eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops.html) ```javascript // bad const x=y+5; // good const x = y + 5; ``` - [19.5](#whitespace--newline-at-end) 使用单个换行符结束文件。 eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) ```javascript // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6; ``` ```javascript // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵ ``` ```javascript // good import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ``` - [19.6](#whitespace--chains) 在使用链式方法调用的时候使用缩进(超过两个方法链)。 使用一个引导点,强调该行是方法调用,而不是新的语句。 eslint: [`newline-per-chained-call`](https://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](https://eslint.org/docs/rules/no-whitespace-before-property) ```javascript // bad $('#items').find('.selected').highlight().end().find('.open').updateCount(); // bad $('#items'). find('.selected'). highlight(). end(). find('.open'). updateCount(); // good $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount(); // bad const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good const leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good const leds = stage.selectAll('.led').data(data); ``` - [19.7](#whitespace--after-blocks) 在块和下一个语句之前留下一空白行。 ```javascript // bad if (foo) { return bar; } return baz; // good if (foo) { return bar; } return baz; // bad const obj = { foo() { }, bar() { }, }; return obj; // good const obj = { foo() { }, bar() { }, }; return obj; // bad const arr = [ function foo() { }, function bar() { }, ]; return arr; // good const arr = [ function foo() { }, function bar() { }, ]; return arr; ``` - [19.8](#whitespace--padded-blocks) 不要在块的开头使用空白行。 eslint: [`padded-blocks`](https://eslint.org/docs/rules/padded-blocks.html) ```javascript // bad function bar() { console.log(foo); } // bad if (baz) { console.log(qux); } else { console.log(foo); } // bad class Foo { constructor(bar) { this.bar = bar; } } // good function bar() { console.log(foo); } // good if (baz) { console.log(qux); } else { console.log(foo); } ``` - [19.9](#whitespace--in-parens) 不要在括号内添加空格。 eslint: [`space-in-parens`](https://eslint.org/docs/rules/space-in-parens.html) ```javascript // bad function bar( foo ) { return foo; } // good function bar(foo) { return foo; } // bad if ( foo ) { console.log(foo); } // good if (foo) { console.log(foo); } ``` - [19.10](#whitespace--in-brackets) 不要在中括号中添加空格。 eslint: [`array-bracket-spacing`](https://eslint.org/docs/rules/array-bracket-spacing.html) ```javascript // bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good const foo = [1, 2, 3]; console.log(foo[0]); ``` - [19.11](#whitespace--in-braces) 在花括号内添加空格。 eslint: [`object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing.html) ```javascript // bad const foo = {clark: 'kent'}; // good const foo = { clark: 'kent' }; ``` - [19.12](#whitespace--max-len) 避免让你的代码行超过100个字符(包括空格)。 注意:根据上边的 [约束](#strings--line-length),长字符串可免除此规定,不应分解。 eslint: [`max-len`](https://eslint.org/docs/rules/max-len.html) > 为什么? 这样能够确保可读性和可维护性。 ```javascript // bad const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // bad $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // good const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // good $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.')); ``` - [19.13](#whitespace--block-spacing) 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距。 eslint: [`block-spacing`](https://eslint.org/docs/rules/block-spacing) ```javascript // bad function foo() {return true;} if (foo) { bar = 0;} // good function foo() { return true; } if (foo) { bar = 0; } ``` - [19.14](#whitespace--comma-spacing) 逗号之前避免使用空格,逗号之后需要使用空格。eslint: [`comma-spacing`](https://eslint.org/docs/rules/comma-spacing) ```javascript // bad var foo = 1,bar = 2; var arr = [1 , 2]; // good var foo = 1, bar = 2; var arr = [1, 2]; ``` - [19.15](#whitespace--computed-property-spacing) 在计算属性之间强化间距。eslint: [`computed-property-spacing`](https://eslint.org/docs/rules/computed-property-spacing) ```javascript // bad obj[foo ] obj[ 'foo'] var x = {[ b ]: a} obj[foo[ bar ]] // good obj[foo] obj['foo'] var x = { [b]: a } obj[foo[bar]] ``` - [19.16](#whitespace--func-call-spacing) 在函数和它的调用之间强化间距。 eslint: [`func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) ```javascript // bad func (); func (); // good func(); ``` - [19.17](#whitespace--key-spacing) 在对象的属性和值之间强化间距。 eslint: [`key-spacing`](https://eslint.org/docs/rules/key-spacing) ```javascript // bad var obj = { "foo" : 42 }; var obj2 = { "foo":42 }; // good var obj = { "foo": 42 }; ``` - [19.18](#whitespace--no-trailing-spaces) 在行的末尾避免使用空格。 eslint: [`no-trailing-spaces`](https://eslint.org/docs/rules/no-trailing-spaces) - [19.19](#whitespace--no-multiple-empty-lines) 避免多个空行,并且只允许在文件末尾添加一个换行符。 eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines) ```javascript // bad var x = 1; var y = 2; // good var x = 1; var y = 2; ``` **[⬆ 返回目录](#table-of-contents)** ## 逗号 - [20.1](#commas--leading-trailing) 逗号前置: **不行** eslint: [`comma-style`](https://eslint.org/docs/rules/comma-style.html) ```javascript // bad const story = [ once , upon , aTime ]; // good const story = [ once, upon, aTime, ]; // bad const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // good const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', }; ``` - [20.2](#commas--dangling) 添加尾随逗号: **可以** eslint: [`comma-dangle`](https://eslint.org/docs/rules/comma-dangle.html) > 为什么? 这个将造成更清洁的 git 扩展差异。 另外,像 Babel 这样的编译器,会在转换后的代码中删除额外的尾随逗号,这意味着你不必担心在浏览器中后面的 [尾随逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas) 。 ```diff // bad - 没有尾随逗号的 git 差异 const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // good - 有尾随逗号的 git 差异 const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], }; ``` ```javascript // bad const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // good const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // bad function createHero( firstName, lastName, inventorOf ) { // does nothing } // good function createHero( firstName, lastName, inventorOf, ) { // does nothing } // good (注意逗号不能出现在 "rest" 元素后边) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // bad createHero( firstName, lastName, inventorOf ); // good createHero( firstName, lastName, inventorOf, ); // good (注意逗号不能出现在 "rest" 元素后边) createHero( firstName, lastName, inventorOf, ...heroArgs ); ``` **[⬆ 返回目录](#table-of-contents)** ## 分号 - [21.1](#semicolons--required) **对** eslint: [`semi`](https://eslint.org/docs/rules/semi.html) > 为什么? 当 JavaScript 遇见一个没有分号的换行符时,它会使用一个叫做 [Automatic Semicolon Insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) 的规则来确定是否应该以换行符视为语句的结束,并且如果认为如此,会在代码中断前插入一个分号到代码中。 但是,ASI 包含了一些奇怪的行为,如果 JavaScript 错误的解释了你的换行符,你的代码将会中断。 随着新特性成为 JavaScript 的一部分,这些规则将变得更加复杂。 明确地终止你的语句,并配置你的 linter 以捕获缺少的分号将有助于防止你遇到的问题。 ```javascript // bad - 可能异常 const luke = {} const leia = {} [luke, leia].forEach(jedi => jedi.father = 'vader') // bad - 可能异常 const reaction = "No! That's impossible!" (async function meanwhileOnTheFalcon() { // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()) // bad - 返回 `undefined` 而不是下一行的值 - 当 `return` 单独一行的时候 ASI 总是会发生 function foo() { return 'search your feelings, you know it to be foo' } // good const luke = {}; const leia = {}; [luke, leia].forEach((jedi) => { jedi.father = 'vader'; }); // good const reaction = "No! That's impossible!"; (async function meanwhileOnTheFalcon() { // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()); // good function foo() { return 'search your feelings, you know it to be foo'; } ``` [更多信息](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214#7365214). **[⬆ 返回目录](#table-of-contents)** ## 类型转换和强制类型转换 - [22.1](#coercion--explicit) 在语句开始前进行类型转换。 - [22.2](#coercion--strings) 字符类型: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript // => this.reviewScore = 9; // bad const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string" // bad const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() // bad const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string // good const totalScore = String(this.reviewScore); ``` - [22.3](#coercion--numbers) 数字类型:使用 `Number` 进行类型铸造和 `parseInt` 总是通过一个基数来解析一个字符串。 eslint: [`radix`](https://eslint.org/docs/rules/radix) [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript const inputValue = '4'; // bad const val = new Number(inputValue); // bad const val = +inputValue; // bad const val = inputValue >> 0; // bad const val = parseInt(inputValue); // good const val = Number(inputValue); // good const val = parseInt(inputValue, 10); ``` - [22.4](#coercion--comment-deviations) 如果出于某种原因,你正在做一些疯狂的事情,而 `parseInt` 是你的瓶颈,并且出于 [性能问题](https://jsperf.com/coercion-vs-casting/3) 需要使用位运算, 请写下注释,说明为什么这样做和你做了什么。 ```javascript // good /** * parseInt 使我的代码变慢。 * 位运算将一个字符串转换成数字更快。 */ const val = inputValue >> 0; ``` - [22.5](#coercion--bitwise) **注意:** 当你使用位运算的时候要小心。 数字总是被以 [64-bit 值](https://es5.github.io/#x4.3.19) 的形式表示,但是位运算总是返回一个 32-bit 的整数 ([来源](https://es5.github.io/#x11.7))。 对于大于 32 位的整数值,位运算可能会导致意外行为。[讨论](https://github.com/airbnb/javascript/issues/109)。 最大的 32 位整数是: 2,147,483,647。 ```javascript 2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647 ``` - [22.6](#coercion--booleans) 布尔类型: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // best const hasAge = !!age; ``` **[⬆ 返回目录](#table-of-contents)** ## 命名规范 - [23.1](#naming--descriptive) 避免单字母的名字。用你的命名来描述功能。 eslint: [`id-length`](https://eslint.org/docs/rules/id-length) ```javascript // bad function q() { // ... } // good function query() { // ... } ``` - [23.2](#naming--camelCase) 在命名对象、函数和实例时使用驼峰命名法(camelCase)。 eslint: [`camelcase`](https://eslint.org/docs/rules/camelcase.html) ```javascript // bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {} ``` - [23.3](#naming--PascalCase) 只有在命名构造器或者类的时候才用帕斯卡拼命名法(PascalCase)。 eslint: [`new-cap`](https://eslint.org/docs/rules/new-cap.html) ```javascript // bad function user(options) { this.name = options.name; } const bad = new user({ name: 'nope', }); // good class User { constructor(options) { this.name = options.name; } } const good = new User({ name: 'yup', }); ``` - [23.4](#naming--leading-underscore) 不要使用前置或者后置下划线。 eslint: [`no-underscore-dangle`](https://eslint.org/docs/rules/no-underscore-dangle.html) > 为什么? JavaScript 在属性和方法方面没有隐私设置。 虽然前置的下划线是一种常见的惯例,意思是 “private” ,事实上,这些属性时公开的,因此,它们也是你公共 API 的一部分。 这种约定可能导致开发人员错误的认为更改不会被视为中断,或者不需要测试。建议:如果你想要什么东西是 “private” , 那就一定不能有明显的表现。 ```javascript // bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda'; // 好,在 WeakMapx 可用的环境中 // see https://kangax.github.io/compat-table/es6/#test-WeakMap const firstNames = new WeakMap(); firstNames.set(this, 'Panda'); ``` - [23.5](#naming--self-this) 不要保存 `this` 的引用。 使用箭头函数或者 [函数#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)。 ```javascript // bad function foo() { const self = this; return function () { console.log(self); }; } // bad function foo() { const that = this; return function () { console.log(that); }; } // good function foo() { return () => { console.log(this); }; } ``` - [23.6](#naming--filename-matches-export) 文件名应该和默认导出的名称完全匹配。 ```javascript // file 1 contents class CheckBox { // ... } export default CheckBox; // file 2 contents export default function fortyTwo() { return 42; } // file 3 contents export default function insideDirectory() {} // in some other file // bad import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // bad import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // good import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ supports both insideDirectory.js and insideDirectory/index.js ``` - [23.7](#naming--camelCase-default-export) 当你导出默认函数时使用驼峰命名法。 你的文件名应该和方法名相同。 ```javascript function makeStyleGuide() { // ... } export default makeStyleGuide; ``` - [23.8](#naming--PascalCase-singleton) 当你导出一个构造器 / 类 / 单例 / 函数库 / 暴露的对象时应该使用帕斯卡命名法。 ```javascript const AirbnbStyleGuide = { es6: { }, }; export default AirbnbStyleGuide; ``` - [23.9](#naming--Acronyms-and-Initialisms) 缩略词和缩写都必须是全部大写或者全部小写。 > 为什么? 名字是为了可读性,不是为了满足计算机算法。 ```javascript // bad import SmsContainer from './containers/SmsContainer'; // bad const HttpRequests = [ // ... ]; // good import SMSContainer from './containers/SMSContainer'; // good const HTTPRequests = [ // ... ]; // also good const httpRequests = [ // ... ]; // best import TextMessageContainer from './containers/TextMessageContainer'; // best const requests = [ // ... ]; ``` - [23.10](#naming--uppercase) 你可以大写一个常量,如果它:(1)被导出,(2)使用 `const` 定义(不能被重新赋值),(3)程序员可以信任它(以及其嵌套的属性)是不变的。 > 为什么? 这是一个可以帮助程序员确定变量是否会发生变化的辅助工具。UPPERCASE_VARIABLES 可以让程序员知道他们可以相信变量(及其属性)不会改变。 - 是否是对所有的 `const` 定义的变量? - 这个是没有必要的,不应该在文件中使用大写。但是,它应该用于导出常量。 - 导出对象呢? - 在顶级导出属性 (e.g. `EXPORTED_OBJECT.key`) 并且保持所有嵌套属性不变。 ```javascript // bad const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file'; // bad export const THING_TO_BE_CHANGED = 'should obviously not be uppercased'; // bad export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables'; // --- // 允许,但是不提供语义值 export const apiKey = 'SOMEKEY'; // 多数情况下,很好 export const API_KEY = 'SOMEKEY'; // --- // bad - 不必要大写 key 没有增加语义值 export const MAPPING = { KEY: 'value' }; // good export const MAPPING = { key: 'value' }; ``` **[⬆ 返回目录](#table-of-contents)** ## 存取器 - [24.1](#accessors--not-required) 对于属性的的存取函数不是必须的。 - [24.2](#accessors--no-getters-setters) 不要使用 JavaScript 的 getters/setters 方法,因为它们会导致意外的副作用,并且更加难以测试、维护和推敲。 相应的,如果你需要存取函数的时候使用 `getVal()` 和 `setVal('hello')`。 ```javascript // bad class Dragon { get age() { // ... } set age(value) { // ... } } // good class Dragon { getAge() { // ... } setAge(value) { // ... } } ``` - [24.3](#accessors--boolean-prefix) 如果属性/方法是一个 `boolean` 值,使用 `isVal()` 或者 `hasVal()`。 ```javascript // bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; } ``` - [24.4](#accessors--consistent) 可以创建 `get()` 和 `set()` 方法,但是要保证一致性。 ```javascript class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } } ``` **[⬆ 返回目录](#table-of-contents)** ## 事件 - [25.1](#events--hash) 当给事件(无论是 DOM 事件还是更加私有的事件)附加数据时,传入一个对象(通畅也叫做 “hash” ) 而不是原始值。 这样可以让后边的贡献者向事件数据添加更多的数据,而不用找出更新事件的每个处理器。 例如,不好的写法: ```javascript // bad $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingID) => { // do something with listingID }); ``` 更好的写法: ```javascript // good $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingID }); ``` **[⬆ 返回目录](#table-of-contents)** ## jQuery - [26.1](#jquery--dollar-prefix) 对于 jQuery 对象的变量使用 `$` 作为前缀。 ```javascript // bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar'); // good const $sidebarBtn = $('.sidebar-btn'); ``` - [26.2](#jquery--cache) 缓存 jQuery 查询。 ```javascript // bad function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); } ``` - [26.3](#jquery--queries) 对于 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')` 的格式。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - [26.4](#jquery--find) 对于有作用域的 jQuery 对象查询使用 `find` 。 ```javascript // bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide(); ``` **[⬆ 返回目录](#table-of-contents)** ## ECMAScript 5 兼容性 - [27.1](#es5-compat--kangax) 参考 [Kangax](https://twitter.com/kangax/)的 ES5 [兼容性表格](https://kangax.github.io/es5-compat-table/)。 **[⬆ 返回目录](#table-of-contents)** ## ECMAScript 6+ (ES 2015+) Styles - [28.1](#es6-styles) 这是一个链接到各种 ES6+ 特性的集合。 1. [箭头函数](#arrow-functions) 1. [类](#classes--constructors) 1. [对象简写](#es6-object-shorthand) 1. [对象简洁](#es6-object-concise) 1. [对象计算属性](#es6-computed-properties) 1. [字符串模板](#es6-template-literals) 1. [解构](#destructuring) 1. [默认参数](#es6-default-parameters) 1. [Rest](#es6-rest) 1. [数组展开](#es6-array-spreads) 1. [Let 和 Const](#references) 1. [求幂运算符](#es2016-properties--exponentiation-operator) 1. [迭代器和发生器](#iterators-and-generators) 1. [模块](#modules) - [28.2](#tc39-proposals) 不要使用尚未达到第3阶段的 [TC39 建议](https://github.com/tc39/proposals)。 > 为什么? [它们没有最终确定](https://tc39.github.io/process-document/), 并且它们可能会被改变或完全撤回。我们希望使用JavaScript,而建议还不是JavaScript。 **[⬆ 返回目录](#table-of-contents)** ## 标准库 [标准库](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects) 包含功能已损坏的实用工具,但因为遗留原因而保留。 - [29.1](#standard-library--isnan) 使用 `Number.isNaN` 代替全局的 `isNaN`. eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals) > 为什么? 全局的 `isNaN` 强制非数字转化为数字,对任何强制转化为 NaN 的东西都返回 true。 > 如果需要这种行为,请明确说明。 ```javascript // bad isNaN('1.2'); // false isNaN('1.2.3'); // true // good Number.isNaN('1.2.3'); // false Number.isNaN(Number('1.2.3')); // true ``` - [29.2](#standard-library--isfinite) 使用 `Number.isFinite` 代替全局的 `isFinite`. eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals) > 为什么? 全局的 `isFinite` 强制非数字转化为数字,对任何强制转化为有限数字的东西都返回 true。 > 如果需要这种行为,请明确说明。 ```javascript // bad isFinite('2e3'); // true // good Number.isFinite('2e3'); // false Number.isFinite(parseInt('2e3', 10)); // true ``` **[⬆ 返回目录](#table-of-contents)** ## Testing - [30.1](#testing--yup) **是的.** ```javascript function foo() { return true; } ``` - [30.2](#testing--for-real) **没有,但是认真**: - 无论你使用那种测试框架,都应该编写测试! - 努力写出许多小的纯函数,并尽量减少发生错误的地方。 - 对于静态方法和 mock 要小心----它们会使你的测试更加脆弱。 - 我们主要在 Airbnb 上使用 [`mocha`](https://www.npmjs.com/package/mocha) 和 [`jest`](https://www.npmjs.com/package/jest) 。 [`tape`](https://www.npmjs.com/package/tape) 也会用在一些小的独立模块上。 - 100%的测试覆盖率是一个很好的目标,即使它并不总是可行的。 - 无论何时修复bug,都要编写一个回归测试。在没有回归测试的情况下修复的bug在将来几乎肯定会再次崩溃。 **[⬆ 返回目录](#table-of-contents)** ## 性能 - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) - [Bang Function](https://jsperf.com/bang-function) - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) - [Long String Concatenation](https://jsperf.com/ya-string-concat) - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) - Loading... **[⬆ 返回目录](#table-of-contents)** ## 资源 **学习 ES6+** - [Latest ECMA spec](https://tc39.github.io/ecma262/) - [ExploringJS](http://exploringjs.com/) - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) - [Comprehensive Overview of ES6 Features](http://es6-features.org/) **读这个** - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) **工具** - Code Style Linters - [ESlint](https://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) - Neutrino preset - [neutrino-preset-airbnb-base](https://neutrino.js.org/presets/neutrino-preset-airbnb-base/) **其他编码规范** - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) - [StandardJS](https://standardjs.com) **其他风格** - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - [Popular JavaScript Coding Conventions on GitHub](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman **进一步阅读** - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock **书籍** - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke - [You Don’t Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson **博客** - [JavaScript Weekly](http://javascriptweekly.com/) - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/) - [Bocoup Weblog](https://bocoup.com/weblog) - [Adequately Good](http://www.adequatelygood.com/) - [NCZOnline](https://www.nczonline.net/) - [Perfection Kills](http://perfectionkills.com/) - [Ben Alman](http://benalman.com/) - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - [nettuts](http://code.tutsplus.com/?s=javascript) **播客** - [JavaScript Air](https://javascriptair.com/) - [JavaScript Jabber](https://devchat.tv/js-jabber/) **[⬆ 返回目录](#table-of-contents)** ## JavaScript风格指南的指南 - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) ## 许可证 (The MIT License) Copyright (c) 2012 康兵奎 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **[⬆ 返回目录](#table-of-contents)** ## 修正案 我们鼓励您使用此指南并更改规则以适应您的团队的风格指南。下面,你可以列出一些对风格指南的修正。这允许您定期更新您的样式指南,而不必处理合并冲突。 # }; ================================================ FILE: JAVASCRIPT/上传oss.md ================================================ ### 阿里云oss上传步骤 1. 获取oss token 示例如下 * ```javascript 1.8 GetOssToken (file) { let _this = this return this.$axios.post(process.env.VUE_APP_BASE_URL + "/oss/get-token", { context: this.context }).then(res =>{ _this.uploadFile(res.content, file) },()=>{ _this.$vux.toast.show({ text: '网络异常,请稍后再试' }) }) } ``` 2. 根据token信息上传文件至oss ```javascript 1.8 uploadFile (tokenObj, file) { this.$emit('upload-begin', file) let uploadUrl = tokenObj['host'] || '' let formData = new FormData() const data = new Date().getTime() //根据时间自定义文件名称 console.log(formData.url); formData.append('name', `${data}.png`) formData.append('key', this.context + '/' + `${data}.png`) formData.append('policy', tokenObj['policy'] || '') formData.append('OSSAccessKeyId', tokenObj['accessid'] || '') formData.append('success_action_status', '200') formData.append('callback', tokenObj['callback'] || '') formData.append('signature', tokenObj['signature'] || '') formData.append('file', this.dataURLtoFile( file, `${data}.png`)) let xhr = new XMLHttpRequest() xhr.open('POST', uploadUrl) xhr.send(formData) xhr.onload = () => { if (xhr.status === 200) { this.$emit('resulturl',uploadUrl + '/'+ JSON.parse(xhr.response).object) } } } ``` ================================================ FILE: JAVASCRIPT/下载文件流.md ================================================ /*导出表格*/ ```javascript 1.8 downHandler () { this.downloadBtn = true; let xhr = new XMLHttpRequest(); xhr.open('POST', process.env.VUE_APP_API_URL + '/plan/export-plans', true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.setRequestHeader('Authorization', VueCookie.get(process.env.VUE_APP_LOGIN_TOKEN_NAME)); xhr.responseType = 'blob'; xhr.onload = function () { let blob = new Blob([xhr.response], {type: 'application/vnd.ms-excel'}); if (window.navigator.msSaveOrOpenBlob) { navigator.msSaveBlob(blob, '日程总表.xlsx') } else { let link = document.createElement('a'); let evt = document.createEvent('HTMLEvents'); evt.initEvent('click', false, false); link.href = URL.createObjectURL(blob); link.download = '日程总表.xlsx'; link.style.display = 'none'; document.body.appendChild(link); link.click(); window.URL.revokeObjectURL(link.href); document.body.removeChild(link); } }; xhr.send(JSON.stringify({ examId:this.$route.params.id, })); xhr.onreadystatechange = () => { if (xhr.readyState === 4 && xhr.status === 200) { this.downloadBtn = false } } } ``` ## JavaScript 之 Blob 对象类型   Blob(Binary Large Object)术语最初来自数据库(oracle 中也有类似的栏位类型。),早期数据库因为要存储声音、图片、以及可执行程序等二进制数据对象所以给该类对象取名为Blob。         在Web领域,Blob被定义为包含只读数据的类文件对象。Blob中的数据不一定是js原生数据形式。常见的File接口就继承自Blob,并扩展它用于支持用户系统的本地文件。 ### 构建一个Blob对象通常有三种方式: 1. blob对象的构造函数来构建。 ```javascript 1.8 var blob = new Blob(array[optional], options[optional]); ``` 2. 从已有的Blob对象调用slice接口切出一个新的Blob对象。 3. canvas API toBlob方法,把当前绘制信息转为一个Blob对象。 #### 构造函数,接受两个参数
* 第一个参数:为一个数据序列,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。 * 第二个参数:用于指定将要放入Blob中的数据的类型(MIME)(后端可以通过枚举MimeType,获取对应类型: ```javascript 1.8 ``` #### Blob对象的基本属性: * size :Blob对象包含的字节数。(只读) * type : Blob对象包含的数据类型MIME,如果类型未知则返回空字符串。 #### Blob对象的基本方法: * 大文件分割 (slice() 方法),slice方法与数组的slice类似。 ```javascript 1.8 Blob.slice([start, [end, [content-type]]]) ``` slice() 方法接受三个参数,起始偏移量,结束偏移量,还有可选的 mime 类型。如果 mime 类型,没有设置,那么新的 Blob 对象的 mime 类型和父级一样。 当要上传大文件的时候,此方法非常有用,可以将大文件分割分段,然后各自上传,因为分割之后的 Blob 对象和原始的是独立存在的。 不过目前浏览器实现此方法还没有统一,火狐使用的是 mozSlice() ,Chrome 使用的是 webkitSlice() ,其他浏览器则正常的方式 slice()  ```javascript 1.8 // 兼容写法 function sliceBlob(blob, start, end, type) { type = type || blob.type; if (blob.mozSlice) { return blob.mozSlice(start, end, type); } else if (blob.webkitSlice) { return blob.webkitSlice(start, end type); } else { throw new Error("This doesn't work!"); } } ``` ================================================ FILE: JAVASCRIPT/修改url的某个参数值.md ================================================ ## 修改url的某个参数值 ```javascript 1.8 function replaceParamVal(paramName,replaceWith) { var oUrl = this.location.href.toString(); var re=eval('/('+ paramName+'=)([^&]*)/gi'); var nUrl = oUrl.replace(re,paramName+'='+replaceWith); this.location = nUrl;   window.location.href=nUrl } ``` * 使用 replaceParamVal("userId","333") ================================================ FILE: JAVASCRIPT/功能性js插件.md ================================================ ## 功能性js ```javascript 1.8 import moment from 'moment' function pluralize (time, label) { if (time === 1) { return time + label } return time + label + 's' } export function timeAgo (time) { const between = Date.now() / 1000 - Number(time) if (between < 3600) { return pluralize(~~(between / 60), ' minute') } else if (between < 86400) { return pluralize(~~(between / 3600), ' hour') } else { return pluralize(~~(between / 86400), ' day') } } export function parseTime (time, cFormat) { if (arguments.length === 0) { return null } if ((time + '').length === 10) { time = +time * 1000 } const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' let date if (typeof time === 'object') { date = time } else { date = new Date(parseInt(time)) } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() } const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { let value = formatObj[key] if (key === 'a') { return ['一', '二', '三', '四', '五', '六', '日'][value - 1] } if (result.length > 0 && value < 10) { value = '0' + value } return value || 0 }) return timeStr } export function formatTime (time, option) { time = +time * 1000 const d = new Date(time) const now = Date.now() const diff = (now - d) / 1000 if (diff < 30) { return '刚刚' } else if (diff < 3600) { // less 1 hour return Math.ceil(diff / 60) + '分钟前' } else if (diff < 3600 * 24) { return Math.ceil(diff / 3600) + '小时前' } else if (diff < 3600 * 24 * 2) { return '1天前' } if (option) { return parseTime(time, option) } else { return ( d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' ) } } /* 数字 格式化 */ export function nFormatter (num, digits) { const si = [ {value: 1e18, symbol: 'E'}, {value: 1e15, symbol: 'P'}, {value: 1e12, symbol: 'T'}, {value: 1e9, symbol: 'G'}, {value: 1e6, symbol: 'M'}, {value: 1e3, symbol: 'k'} ] for (let i = 0; i < si.length; i++) { if (num >= si[i].value) { return ( (num / si[i].value + 0.1) .toFixed(digits) .replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol ) } } return num.toString() } export function html2Text (val) { const div = document.createElement('div') div.innerHTML = val return div.textContent || div.innerText } export function toThousandslsFilter (num) { return (+num || 0) .toString() .replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) } /** * 格式化文件大小 * @param value * @returns {*} */ export function renderSize (value) { if (value === null || value === '') { return '0B' } let unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] let srcsize = parseFloat(value) let index = Math.floor(Math.log(srcsize) / Math.log(1024)) let size = srcsize / Math.pow(1024, index) size = size.toFixed(0) // 保留的小数位数 return size + unitArr[index] } /** * 时间戳转时分 * @param {number} Timestamps 时间戳 * @returns {string} 23:59(分) */ export function timestampToHm (Timestamps) { return moment(Timestamps * 1000).format('HH:mm') } /** * 时分转时间戳 */ export function timeToseconds (time) { var s = '' var hour = time.split(':')[0] var min = time.split(':')[1] s = Number(hour * 3600) + Number(min * 60) return s } /** * 时间戳转时分秒 * @param {number} Timestamps 时间戳 * @returns {string} 23:59:59(秒) */ export function timestampToHms (Timestamps) { return moment(Timestamps * 1000).format('HH:mm:ss') } /** * 时间戳转日期 * @param Timestamps 时间戳 * @returns {string} 2018-08-28 (日) */ export function timestampToYMD (Timestamps) { if (Timestamps) { return moment(Timestamps * 1000).format('YYYY-MM-DD') } else { return '--' } } /** * 时间戳转短时间 * @param Timestamps 时间戳 * @returns {string} 2018-08-28 23:59(分) */ export function timestampToYMDHm (Timestamps) { if (Timestamps) { return moment(Timestamps * 1000).format('YYYY-MM-DD HH:mm') } else { return '--' } } /** * 时间戳转长时间 * @param Timestamps 时间戳 * @returns {string} 2018-08-28 23:59:59(秒) */ export function timestampToYMDHms (Timestamps) { if (Timestamps) { return moment(Timestamps * 1000).format('YYYY-MM-DD HH:mm:ss') } else { return '--' } } /** * 时间转时间戳 * @param {number} time 时间 * @returns {string} 1534567891(秒) */ export function timeToTimestamp (time) { return moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() / 1000 } /** * 时间转时分 * @param {number} time 时间 * @returns {string} 23:59(分) */ export function timeToHm (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm').valueOf() return moment(Timestamps).format('HH:mm') } /** * 时间转时分秒 * @param {number} time 时间 * @returns {string} 23:59:59(秒) */ export function timeToHms (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm').valueOf() return moment(Timestamps).format('HH:mm:ss') } /** * 时间对象转时分秒 * @param {number} time 时间 * @return {string} 2018-08-08 23:59:59(秒) */ export function timeToYMDms (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() return moment(Timestamps).format('YYYY-MM-DD HH:mm:ss') } /** * 时间对象转时分 * @param {number} time 时间 * @return {string} 2018-08-08 23:59(分) */ export function timeToYMDm (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() return moment(Timestamps).format('YYYY-MM-DD HH:mm') } /** * 格式化性别 * @param {number} sex 0女 1男 * @return {string} */ export function sex (sex) { switch (parseInt(sex)) { case 0: return '女' case 1: return '男' } } /** * 格式化审批同意拒绝 * @param {number} status 0同意 1拒绝 * @return {string} */ export function approvalStatus (status) { switch (parseInt(status)) { case 0: return '同意申请' case 1: return '拒绝申请' } } /** * 处理耗时 * @param {Number} 送审时间ms * @returns {String} X天X小时X分 */ export function consumingTime (s) { s = s < 0 ? 1 : s let day = Math.floor(s / 86400) let hours = Math.floor((s - day * 86400) / 3600) let minutes = Math.floor((s - day * 86400 - hours * 3600) / 60) return (day ? day + '天' : '') + (hours ? hours + '小时' : '') + (minutes ? minutes + '分钟' : '<1分钟') } /** * 日期转s * @param {String} eg: "2018-09-18T16:00:00.000Z" * @returns {String} X秒 */ export function getDateFn (s) { return s ? new Date(s).getTime() / 1000 + '' : '' } /** * 深度克隆 * @param {Object} Obj 原数据 * @returns {Object} {*} 克隆数据 */ export function clone (Obj) { let buf if (Obj instanceof Array) { buf = [] // 创建一个空的数组 let i = Obj.length while (i--) { buf[i] = clone(Obj[i]) } return buf } else if (Obj instanceof Object) { buf = {} // 创建一个空对象 for (let k in Obj) { // 为这个对象添加新的属性 buf[k] = clone(Obj[k]) } return buf } else { return Obj } } /** * 生成随机字符串 * @param len 需要生生字符串的长度 默认生成6位字符串 * @param charSet 自定义字符 默认[a-zA-Z0-9],或者自定义字符串'adasdaq2r413123' * @returns {string} */ export function randomString (len, charSet) { charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' len = len || 6 let randomString = '' for (let i = 0; i < len; i++) { let randomPoz = Math.floor(Math.random() * charSet.length) randomString += charSet.substring(randomPoz, randomPoz + 1) } return randomString } /** * 格式化数字转汉字 */ export function numberToChinese (section) { let chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'] let chnUnitChar = ['', '十', '百', '千', '万'] let strIns = '' let chnStr = '' let unitPos = 0 let zero = true while (section > 0) { let v = section % 10 if (v === 0) { if (!zero) { zero = true chnStr = chnNumChar[v] + chnStr } } else { zero = false strIns = chnNumChar[v] strIns += chnUnitChar[unitPos] chnStr = strIns + chnStr } unitPos++ section = Math.floor(section / 10) } return chnStr } /** * 数组遍历拼接 删除最后一个拼接符 */ export function arrayToString (arr) { var str = '' for (var i = 0; i < arr.length; i++) { str += arr[i].name + '、' } // 去掉最后一个逗号(如果不需要去掉,就不用写) if (str.length > 0) { str = str.substr(0, str.length - 1) } return str } /** * 数字格式化星期几 * @param {number} status 0同意 1拒绝 * @return {string} */ export function weekDay (num) { switch (parseInt(num)) { case 1: return '星期一' case 2: return '星期二' case 3: return '星期三' case 4: return '星期四' case 5: return '星期五' case 6: return '星期六' case 7: return '星期日' } } /** * 时间戳格式化星期几 * @param {string} time 时间 * @return {string} */ export function timeToweekDay (time) { let weekDay = moment(time).format('dddd') switch (weekDay) { case 'Monday': return '星期一' case 'Tuesday': return '星期二' case 'Wednesday': return '星期三' case 'Thursday': return '星期四' case 'Friday': return '星期五' case 'Saturday': return '星期六' case 'Sunday': return '星期日' } } ``` ================================================ FILE: JAVASCRIPT/基于vant上传组件oss/服务器.md ================================================ ```javascript 1.8 ``` dz文件 (功能性插件) ```javascript 1.8 // import Vue from 'vue' import moment from 'moment' let dz = { /** * 生成随机字符串 * @param len * @returns {string} */ getRandomString: function (len) { let leng = len || 32 var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' var maxPos = chars.length var pwd = '' for (var i = 0; i < leng; i++) { pwd += chars.charAt(Math.floor(Math.random() * maxPos)) } return pwd }, /** * 时间戳转 xxxx-xx-xx * @param value 时间戳 * @returns {string} */ timestampToshortText (value) { return moment(value * 1000).format('YYYY-MM-DD') }, /** * 时间戳转 xxxx-xx-xx xx:xx(分) * @param value 时间戳 * @returns {string} */ timestampToMText (value) { return moment(value * 1000).format('YYYY-MM-DD HH:mm') }, /** * 时间戳转 xxxx-xx-xx xx:xx:xx(秒) * @param value 时间戳 * @returns {string} */ timestampToLongText (value) { return moment(value * 1000).format('YYYY-MM-DD HH:mm:ss') }, /** * 时间转时间戳 (秒) * 格式化时间戳 * @param {number} time 时间 */ timeToTimestamp (time) { return moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() / 1000 }, /** * 获取当前时间戳 (秒) * 格式化时间戳 * @param {number} time 时间 */ timeToTimestamps () { return moment(Date.now()).format('X') }, /** * 获取当前年月日 (秒) * 格式化时间戳 * @param {number} time 时间 */ timeToDatetimes () { return moment(Date.now()).format('YYYY-MM-DD') }, /** * 时间转日期 xxxx:xx:xx * 格式化时间 2018-08-28 * @param {string} time 时间 */ timeToDateTime (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() return moment(Timestamps).format('YYYY-MM-DD') }, /** * 时间转日期 xxxx:xx:xx * 格式化时间 2018-08-28 12:12 * @param {string} time 时间 */ timeToDateTimes (time) { let Timestamps = moment(time, 'YYYY-MM-DD HH:mm:ss').valueOf() return moment(Timestamps).format('YYYY-MM-DD HH:mm') }, // 循环 foreach (arr, func) { for (var i in arr) { if (func(arr[i], i) === false) { return false } } return true }, /** * 获取秒时间戳 * @param {Date} day 标准时间格式 * @return {Number} 转换成秒的时间戳 */ getSencondStamp (day) { return parseInt(moment(day)['_d'].getTime() / 1000) }, /** * 时间戳/常见时间格式 格式化为 XXXX年XX月XX日 * @param {Date} day 标准时间格式/时间戳 * @return {String} 2018年06月01日 */ timestampToYearText (day) { return moment(day).format('YYYY' + '年' + 'MM' + '月' + 'DD' + '日') }, /** * 浅克隆 * @param {Object} obj 克隆对象 * @return {Object} copy 克隆结果 */ clone (obj) { let copy = null // Handle the 3 simple types, and null or undefined if (obj == null || typeof obj !== 'object') return obj // Handle Date if (obj instanceof Date) { copy = new Date() copy.setTime(obj.getTime()) return copy } // Handle Array if (obj instanceof Array) { copy = [] for (var i = 0; i < obj.length; ++i) { copy[i] = this.clone(obj[i]) } return copy } // Handle Object if (obj instanceof Object) { copy = {} if (window.JSON) { copy = JSON.parse(JSON.stringify(obj)) } else { for (let j in obj) { copy = typeof obj[j] === 'object' ? this.clone(obj[j]) : obj[j] } } return copy } throw new Error("Unable to copy obj! Its type isn't supported.") }, /** * 是否为微信浏览器 * navigator.wxuserAgent 匹配win phone * @return {Boolean} true是 false */ isWexinAgent () { let agent = navigator.userAgent.toLowerCase() return (/micromessenger/.test(agent)) || typeof navigator.wxuserAgent !== 'undefined' }, /** * IOS字体大小设置为65% */ adaptIOS () { let isIOS = null let u = navigator.userAgent if (/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|ZTE/.test(navigator.userAgent))) { if (window.location.href.indexOf('?mobile') < 0) { try { if (/iPhone|mac|iPod|iPad/i.test(navigator.userAgent)) { isIOS = true } else { isIOS = false } } catch (e) { console.log(e) } } } else if (u.indexOf('iPad') > -1) { isIOS = true } else { isIOS = false } if (isIOS) { document.getElementsByTagName('html')[0].style.fontSize = '65%' } } } export default dz ``` ================================================ FILE: JAVASCRIPT/完美url跳转方式.md ================================================ ## 完美url跳转方式 `window.open().location = “http://-----”` ================================================ FILE: JAVASCRIPT/常用js代码块.md ================================================ /** * @name: 常用js代码块 * @author: LIULIU * @date: 2020-08-10 14:43 * @description:常用js代码块 * @update: 2020-08-10 14:43 */ #### 1. PC - js - 返回指定范围的随机数(m-n之间)的公式 ```javascript Math.random()*(n-m)+m ``` - [return false](http://stackoverflow.com/questions/1357118/event-preventdefault-vs-return-false) - [return false](http://www.75team.com/archives/201) ```javascript // event.preventDefault()会阻挡预设要发生的事件. // event.stopPropagation()会阻挡发生冒泡事件. // 而return false则是前面两者的事情他都会做: // 他会做event.preventDefault(); // 他会做event.stopPropagation(); // 停止callback function的执行并且立即return回来 ``` - 复制文本到剪切板 ```javascript function copyToClipboard(data) { const _tempInput = document.createElement('input') _tempInput.value = data.value document.body.appendChild(_tempInput) _tempInput.select() document.execCommand('Copy') document.body.removeChild(_tempInput) } ``` - 前端生成文件并下载 ```javascript function createAndDownloadFile(fileName, content) { const aTag = document.createElement('a'); const blob = new Blob([content]); aTag.download = `${fileName}.json`; aTag.href = URL.createObjectURL(blob); aTag.click(); URL.revokeObjectURL(blob); } ``` - 高亮文本 ```javascript function highlight(text, words, tag='span') { let i, len = words.length, re; for (i = 0; i < len; i++) { re = new RegExp(words[i], 'g'); if (re.test(text)) { text = text.replace(re, '<'+ tag +' class="highlight">$&'); } } return text; } ``` - 限制文本字数 ```javascript function excerpt(str, nwords) { let words = str.split(' '); words.splice(nwords, words.length-1); return words.join(' ') + (words.length !== str.split(' ').length ? '…' : ''); } ``` - 简单创建动态菜单下拉列表 ```javascript function createMenu(items, tags=['ul', 'li']) { const parent = tags[0]; const child = tags[1]; let item, value = ''; for (let i = 0, l = items.length; i < l; i++) { item = items[i]; if (/:/.test(item)) { item = items[i].split(':')[0]; value = items[i].split(':')[1]; } items[i] = '<'+ child +' '+ (value && 'value="'+value+'"') +'>'+ item +''; } return '<'+ parent +'>'+ items.join('') +''; } ``` - 防止被Iframe嵌套 ```javascript if(top != self){ location.href = ”about:blank”; } ``` - 两种图片lazy加载的方式 第一个By JS中级交流群 成都-猎巫 第二个By 上海-zenki ```javascript // @description 准备为图片预加载使用的插件 // 使用的图片容器css类名为lazy-load-wrap // 图片真实地址为data-lazy-src // 当lazy-load-wrap容器进入视口,则开始替换容器内所有需要延迟加载的图片路径,并更改容器的加载状态 //第一种方法 $.fn.compassLazyLoad=function(){ var _HEIGHT=window.innerHeight, _lazyLoadWrap=$('.lazy-load-wrap'); var methods={ setOffsetTop:function(){ $.each(_lazyLoadWrap,function(i,n){ $(n).attr({ 'top':n.offsetTop-_HEIGHT, 'status':'wait' }); }) }, isShow:function(){ var _scrollTop=$(window).scrollTop; //利用image容器判断是否进入视口,而非image本身 $.each(_lazyLoadWrap,function(){ var _that=$(this); if (_that.attr('status')==='done') { return; }; if (_that.attr('top')<=_scrollTop) { _that.find('img[data-lazy-src]').each(function(i,n){ n.src=$(n).data('lazy-src'); }); _that.attr('status','done'); }; }) }, scroll:function(){ $(window).on('scroll',function(){ methods.isShow(); }); }, init:function(){ methods.setOffsetTop(); methods.isShow(); methods.scroll(); } }; methods.init(); } //第二种方法 var exist=(function($){ var timer=null, temp=[].slice.call($('.container')); ret={}; for(var i=0,len=temp.length-1;i<=len;i++){ ret[i]=temp[i]; } var isExist=function(winTop,winEnd){ for(var i in ret){ console.log(ret); var item=ret[i], eleTop=item.offsetTop, eleEnd=eleTop+item.offsetHeight; if((eleTop>winTop&&eleTop<=winEnd)||(eleEnd>winTop&&eleEnd<=winEnd)){ $(item).css('background','none'); new Tab($(item).attr('id'),data).init; delete ret[i]; } } } return { timer:timer; isExist:isExist; }; })($); //第三种方法 Zepto(function ($) { var swiper = new Swiper('.swiper-container', { pagination: '.swiper-pagination', paginationClickable: true, autoplay: 3000, loop: true, autoplayDisableOnInteraction: false }); (function lazyLoad() { var imgs = $(".lazyLoad"); var src = ''; $.each(imgs, function (index, item) { src = $(item).attr('data-src'); $(item).attr('src', src); }); })(); }); $(function () { var lazyLoadTimerId = null; /// 智能加载事件 $(window).bind("scroll", function () { clearTimeout(lazyLoadTimerId); lazyLoadTimerId = setTimeout(function () { // 延迟加载所有图片 var isHttp = (location.protocol === "http:"); $("#ym_images img").each(function () { var self = $(this); if (self.filter(":above-the-fold").length > 0) { var originUrl = self.attr("data-original"); self.attr("src", originUrl); } }); }, 500); }); }); ``` - 某年某月的1号为星期几 ```javascript var weekday = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]; weekday[new Date(2015, 9, 1).getDay()]; //2015年10月1号 ``` #### 2. Mobile - js - [js 判断IOS, 安卓](http://caibaojian.com/browser-ios-or-android.html) ```javascript var u = navigator.userAgent, app = navigator.appVersion; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端或者uc浏览器 var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 alert('是否是Android:'+isAndroid); alert('是否是iOS:'+isiOS); ``` #### 3. [微信 weixin](http://loo2k.com/blog/detecting-wechat-client/) - UserAgent 判断微信客户端 ```javascript // Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12F70 MicroMessenger/6.1.5 NetType/WIFI function isWechat() { var ua = navigator.userAgent.toLowerCase(); return /micromessenger/i.test(ua) || /windows phone/i.test(ua); } ``` - 获取单个dom元素 ```js function $(selector, el) { if (!el) { el = document; } return el.querySelector(selector); } ``` - 获取多个dom元素 ```js function $$(selector, el) { if (!el) { el = document; } return el.querySelectorAll(selector); // Note: the returned object is a NodeList. // If you'd like to convert it to a Array for convenience, use this instead: // return Array.prototype.slice.call(el.querySelectorAll(selector)); } ``` - 将nodeList集合转换为数组 ```js function convertToArray(nodeList) { var array = null try { // IE8-NodeList是COM对象 array = Array.prototype.slice.call(nodeList, 0) } catch (err) { array = [] for (var i = 0, len = nodeList.length; i < len; i++) { array.push(nodeList[i]) } } return array } ``` - ajax函数 ```js function ajax(setting) { //设置参数的初始值 var opts = { method: (setting.method || "GET").toUpperCase(), //请求方式 url: setting.url || "", // 请求地址 async: setting.async || true, // 是否异步 dataType: setting.dataType || "json", // 解析方式 data: setting.data || "", // 参数 success: setting.success || function () { }, // 请求成功回调 error: setting.error || function () { } // 请求失败回调 }; // 参数格式化 function params_format(obj) { var str = ""; for (var i in obj) { str += i + "=" + obj[i] + "&"; } return str .split("") .slice(0, -1) .join(""); } // 创建ajax对象 var xhr = new XMLHttpRequest(); // 连接服务器open(方法GET/POST,请求地址, 异步传输) if (opts.method == "GET") { xhr.open( opts.method, opts.url + "?" + params_format(opts.data), opts.async ); xhr.send(); } else { xhr.open(opts.method, opts.url, opts.async); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(opts.data); } /* ** 每当readyState改变时,就会触发onreadystatechange事件 ** readyState属性存储有XMLHttpRequest的状态信息 ** 0 :请求未初始化 ** 1 :服务器连接已建立 ** 2 :请求已接受 ** 3 : 请求处理中 ** 4 :请求已完成,且相应就绪 */ xhr.onreadystatechange = function () { if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) { switch (opts.dataType) { case "json": var json = JSON.parse(xhr.responseText); opts.success(json); break; case "xml": opts.success(xhr.responseXML); break; default: opts.success(xhr.responseText); break; } } }; xhr.onerror = function (err) { opts.error(err); }; } ``` - JS接口安全域名不填写,分享onMenuShareAppMessage直接会取默认值。 ```javascript // 分享onMenuShareAppMessage直接会取默认值 ``` - 关闭当前页面 ```javascript WeixinJSBridge.call('closeWindow'); ``` - [支付接口方法调用必须在addevent里边调用](http://www.cnblogs.com/true_to_me/p/3565039.html) ```javascript document.addEventListener('WeixinJSBridgeReady', function onBridgeReady(){ that.initOrder(); }, false); ``` - 支付接口方法调用必须在 ```javascript WeixinJSBridge.invoke('getBrandWCPayRequest', d, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok"){ // alert("支付成功"); // union.release(d.orderId); resetUrl(); paySuccess('home', d.orderId); } else { cancelOrder(d.orderId); // alert(res.err_msg); } loading.hide(); }); ``` - 瀑布流无限加载实例 ```javascript // be dependent on jquery & jquery.infinitescroll.min.js // insert this '
' to your page.html (function($){ $(function(){ var $container = $('.list-wrap-gd'); function layOutCallBack() { $container.imagesLoaded(function(){ $container.masonry({ itemSelector: '.item-bar', gutter: 10 }); }); $container.imagesLoaded().progress( function() { $container.masonry('layout'); }); } layOutCallBack(); $container.infinitescroll({ navSelector : "#more", nextSelector : "#more a", itemSelector : ".item-bar", pixelsFromNavToBottom: 300, loading:{ img: "/images/masonry_loading.gif", msgText: ' ', finishedMsg: "已经到最后一页", finished: function(){ $("#more").remove(); $("#infscr-loading").hide(); } }, errorCallback:function(){ $(window).unbind('.infscr'); }, pathParse: function (path, nextPage) { var query = ""; var keyword=$("#search_keyword").val(); var cat_id=$("#cat_id").val(); var brand_id=$("#brand_id").val(); var country_id = $("#country_id").val(); query = query + "&namekeyword="+keyword; query = query +"&cat_id="+cat_id query = query + "&brand_id=" + brand_id; query = query + "&country_id=" + country_id; path = [path,query]; return path; } }, function(newElements) { var $newElems = $( newElements ).css({ opacity: 0 }); $newElems.imagesLoaded(function(){ $newElems.animate({ opacity: 1 }); $container.masonry( 'appended', $newElems, true ); layOutCallBack(); }); }); }); })(jQuery); ``` - [iOS,Safari浏览器,input等表单focus后fixed元素错位问题](https://www.snip2code.com/Snippet/176582/--iOS-Safari----input---focus-fixed-----) ```javascript if( /iPhone|iPod|iPad/i.test(navigator.userAgent) ) { $(document).on('focus', 'input, textarea', function() { $('header').css("position", 'absolute'); $('footer').css("position", 'absolute'); }); $(document).on('blur', 'input, textarea', function() { $('header').css("position", 'fixed'); $('footer').css("position", 'fixed'); }); } ``` - 得到地理位置 ```javascript function getLocation(callback){ if(navigator.geolocation){ navigator.geolocation.getCurrentPosition( function(p){ callback(p.coords.latitude, p.coords.longitude); }, function(e){ var msg = e.code + "\n" + e.message; } ); } } ``` - [rem计算适配](http://isux.tencent.com/web-app-rem.html) ```javascript (function(doc, win){ var docEl = doc.documentElement, resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', recalc = function(){ var clientWidth = docEl.clientWidth; if(!clientWidth) return; docEl.style.fontSize = 20 * (clientWidth / 320) + 'px'; }; if(!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); doc.addEventListener('DOMContentLoaded', recalc, false); })(document, window); ``` - [另外一种rem方案](http://www.html-js.com/article/3041) ```javascript var dpr, rem, scale; var docEl = document.documentElement; var fontEl = document.createElement('style'); var metaEl = document.querySelector('meta[name="viewport"]'); dpr = window.devicePixelRatio || 1; rem = docEl.clientWidth * 2 / 10; scale = 1 / dpr; // 设置viewport,进行缩放,达到高清效果 metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no'); // 设置data-dpr属性,留作的css hack之用 docEl.setAttribute('data-dpr', dpr); // 动态写入样式 docEl.firstElementChild.appendChild(fontEl); fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}'; // 给js调用的,某一dpr下rem和px之间的转换函数 window.rem2px = function(v) { v = parseFloat(v); return v * rem; }; window.px2rem = function(v) { v = parseFloat(v); return v / rem; }; window.dpr = dpr; window.rem = rem; ``` - 获取js所在路径 ```js function getJsDir (src) { var script = null; if (src) { script = [].filter.call(document.scripts, function (v) { return v.src.indexOf(src) !== -1; })[0]; } else { script = document.scripts[document.scripts.length - 1]; } return script ? script.src.substr(0, script.src.lastIndexOf('/')) : script; } ``` - 页面加载自执行函数 ```js function addload(func) { var old = window.onload; if (typeof window.onload != "function") { window.onload = func; } else { window.onload = function () { old(); func(); } } } ``` - 从全局捕获错误 ```js window.onerror = function (errMsg, scriptURI, lineNumber, columnNumber, errorObj) { setTimeout(function () { var rst = { "错误信息:": errMsg, "出错文件:": scriptURI, "出错行号:": lineNumber, "出错列号:": columnNumber, "错误详情:": errorObj }; alert(JSON.stringify(rst, null, 10)); }); }; ``` - [如何通过 js 修改微信浏览器的title?](https://www.zhihu.com/question/26228251/answer/32405529) ```javascript var $body = $('body'); document.title = 'title'; // hack在微信等webview中无法修改document.title的情况 var $iframe = $('').on('load', function(){ setTimeout(function(){ $iframe.off('load').remove() }, 0) }).appendTo($body) ``` #### 1. 常用方法 - js - 字符串长度截取 ```js function cutstr(str, len) { var temp, icount = 0, patrn = /[^\x00-\xff]/, strre = ""; for (var i = 0; i < str.length; i++) { if (icount < len - 1) { temp = str.substr(i, 1); if (patrn.exec(temp) == null) { icount = icount + 1 } else { icount = icount + 2 } strre += temp } else { break; } } return strre + "..." } ``` - 替换全部 ```js String.prototype.replaceAll = function(s1, s2) { return this.replace(new RegExp(s1, "gm"), s2) } ```` - 清除空格 ```js String.prototype.trim = function() { var reExtraSpace = /^\s*(.*?)\s+$/; return this.replace(reExtraSpace, "$1") } ``` - 清除左空格/右空格 ```js function ltrim(s){ return s.replace( /^(\s*| *)/, ""); } function rtrim(s){ return s.replace( /(\s*| *)$/, ""); } ``` - 判断是否以某个字符串开头 ```js String.prototype.startWith = function (s) { return this.indexOf(s) == 0 } ``` - 判断是否以某个字符串结束 ```js String.prototype.endWith = function (s) { var d = this.length - s.length; return (d >= 0 && this.lastIndexOf(s) == d) } ``` - 转义html标签 ```js function HtmlEncode(text) { return text.replace(/&/g, '&').replace(/\"/g, '"').replace(//g, '>') } ``` - 时间日期格式转换 ```js Date.prototype.Format = function(formatStr) { var str = formatStr; var Week = ['日', '一', '二', '三', '四', '五', '六']; str = str.replace(/yyyy|YYYY/, this.getFullYear()); str = str.replace(/yy|YY/, this.getFullYear().toString().substr(2)); str = str.replace(/MM/, (this.getMonth() + 1) > 9 ? (this.getMonth() + 1).toString() : '0' + (this.getMonth() + 1)); str = str.replace(/M/g, (this.getMonth() + 1)); str = str.replace(/w|W/g, Week[this.getDay()]); str = str.replace(/dd|DD/, this.getDate() > 9 ? this.getDate().toString() : '0' + this.getDate()); str = str.replace(/d|D/g, this.getDate()); str = str.replace(/hh|HH/, this.getHours() > 9 ? this.getHours().toString() : '0' + this.getHours()); str = str.replace(/h|H/g, this.getHours()); str = str.replace(/mm/, this.getMinutes() > 9 ? this.getMinutes().toString() : '0' + this.getMinutes()); str = str.replace(/m/g, this.getMinutes()); str = str.replace(/ss|SS/, this.getSeconds() > 9 ? this.getSeconds().toString() : '0' + this.getSeconds()); str = str.replace(/s|S/g, this.getSeconds()); return str } ``` - 判断日期是否有效 ```javascript function isValidDate(value, userFormat='mm/dd/yyyy') { const delimiter = /[^mdy]/.exec(userFormat)[0]; const theFormat = userFormat.split(delimiter); const theDate = value.split(delimiter); function isDate(date, format) { let m, d, y, i = 0, len = format.length, f; for (i; i < len; i++) { f = format[i]; if (/m/.test(f)) m = date[i]; if (/d/.test(f)) d = date[i]; if (/y/.test(f)) y = date[i]; } return ( m > 0 && m < 13 && y && y.length === 4 && d > 0 && d <= (new Date(y, m, 0)).getDate() ); } return isDate(theDate, theFormat); } ``` - 判断是否为数字类型 ```js function isDigit(value) { var patrn = /^[0-9]*$/; if (patrn.exec(value) == null || value == "") { return false } else { return true } } ``` - 判断具体类型 ```js function getType(a) { var typeArray = Object.prototype.toString.call(a).split(" "); return typeArray[1].slice(0, -1); } ``` - 设置cookie值 ```js function setCookie(name, value, Hours) { var d = new Date(); var offset = 8; var utc = d.getTime() + (d.getTimezoneOffset() * 60000); var nd = utc + (3600000 * offset); var exp = new Date(nd); exp.setTime(exp.getTime() + Hours * 60 * 60 * 1000); document.cookie = name + "=" + escape(value) + ";path=/;expires=" + exp.toGMTString() } ``` - 获取cookie值 ```js function getCookie(name) { var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)")); if (arr != null) return unescape(arr[2]); return null } ``` - 加载样式文件表 ```js function LoadStyle(url) { try { document.createStyleSheet(url) } catch(e) { var cssLink = document.createElement('link'); cssLink.rel = 'stylesheet'; cssLink.type = 'text/css'; cssLink.href = url; var head = document.getElementsByTagName('head')[0]; head.appendChild(cssLink) } } ``` - 返回脚本内容 ```js function evalscript(s) { if(s.indexOf(']*?>([^\x00]*?)<\/script>/ig; var arr = []; while(arr = p.exec(s)) { var p1 = /]*?src=\"([^\>]*?)\"[^\>]*?(reload=\"1\")?(?:charset=\"([\w\-]+?)\")?><\/script>/i; var arr1 = []; arr1 = p1.exec(arr[0]); if(arr1) { appendscript(arr1[1], '', arr1[2], arr1[3]); } else { p1 = /([^\x00]+?)<\/script>/i; arr1 = p1.exec(arr[0]); appendscript('', arr1[2], arr1[1].indexOf('reload=') != -1); } } return s; } ``` - 清除脚本内容 ```js function stripscript(s) { return s.replace(/.*?<\/script>/ig, ''); } ``` - 动态加载脚本文件 ```js function appendscript(src, text, reload, charset) { var id = hash(src + text); if(!reload && in_array(id, evalscripts)) return; if(reload && $(id)) { $(id).parentNode.removeChild($(id)); } evalscripts.push(id); var scriptNode = document.createElement("script"); scriptNode.type = "text/javascript"; scriptNode.id = id; scriptNode.charset = charset ? charset : (BROWSER.firefox ? document.characterSet : document.charset); try { if(src) { scriptNode.src = src; scriptNode.onloadDone = false; scriptNode.onload = function () { scriptNode.onloadDone = true; JSLOADED[src] = 1; }; scriptNode.onreadystatechange = function () { if((scriptNode.readyState == 'loaded' || scriptNode.readyState == 'complete') && !scriptNode.onloadDone) { scriptNode.onloadDone = true; JSLOADED[src] = 1; } }; } else if(text){ scriptNode.text = text; } document.getElementsByTagName('head')[0].appendChild(scriptNode); } catch(e) {} } ``` - 动态加载js或css文件 ```js function delay_js(url) { var type = url.split(".") , file = type[type.length - 1]; if (file == "css") { var obj = document.createElement("link") , lnk = "href" , tp = "text/css"; obj.setAttribute("rel", "stylesheet"); } else var obj = document.createElement("script") , lnk = "src" , tp = "text/javascript"; obj.setAttribute(lnk, url); obj.setAttribute("type", tp); file == "css" ? document.getElementsByTagName("head")[0].appendChild(obj) : document.body.appendChild(obj); return obj; } ``` - 返回按ID检索的元素对象 ```js function $(id) { return !id ? null : document.getElementById(id); } ``` - 检验URL链接是否有效 ```js function getUrlState(URL){ var xmlhttp = new ActiveXObject("microsoft.xmlhttp"); xmlhttp.Open("GET",URL, false); try{ xmlhttp.Send(); }catch(e){ }finally{ var result = xmlhttp.responseText; if(result){ if(xmlhttp.Status==200){ return(true); }else{ return(false); } }else{ return(false); } } } ``` - 获取当前路径 ```js var currentPageUrl = ""; if (typeof this.href === "undefined") { currentPageUrl = document.location.toString().toLowerCase(); }else { currentPageUrl = this.href.toString().toLowerCase(); } ``` - 获取页面高度 ```js function getPageHeight(){ var g = document, a = g.body, f = g.documentElement, d = g.compatMode == "BackCompat" ? a : g.documentElement; return Math.max(f.scrollHeight, a.scrollHeight, d.clientHeight); } ``` - 获取页面可视宽度 ```js function getPageViewWidth(){ var d = document, a = d.compatMode == "BackCompat" ? d.body: d.documentElement; return a.clientWidth; } ``` - 获取页面宽度 ```js function getPageWidth(){ var g = document, a = g.body, f = g.documentElement, d = g.compatMode == "BackCompat"? a: g.documentElement; return Math.max(f.scrollWidth, a.scrollWidth, d.clientWidth); } ``` - 随机数时间戳 ```js function uniqueId(){ var a=Math.random,b=parseInt; return Number(new Date()).toString()+b(10*a())+b(10*a())+b(10*a()); } ``` - 日期格式化函数 ```js Date.prototype.format = function(format){ var o = { "M+" : this.getMonth()+1, //month "d+" : this.getDate(), //day "h+" : this.getHours(), //hour "m+" : this.getMinutes(), //minute "s+" : this.getSeconds(), //second "q+" : Math.floor((this.getMonth()+3)/3), //quarter "S" : this.getMilliseconds() //millisecond }; if(/(y+)/.test(format)) format=format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); for(var k in o){ if(new RegExp("("+ k +")").test(format)) format = format.replace(RegExp.$1,RegExp.$1.length==1 ? o[k] :("00"+ o[k]).substr((""+ o[k]).length)); } return format; } //调用 //new Date().format("yyyy-MM-dd hh:mm:ss"); ``` - 返回顶部的通用方法 ```js function backTop(btnId) { var btn = document.getElementById(btnId); var d = document.documentElement; var b = document.body; window.onscroll = set; btn.style.display = "none"; btn.onclick = function() { btn.style.display = "none"; window.onscroll = null; this.timer = setInterval(function() { d.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1); b.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1); if ((d.scrollTop + b.scrollTop) == 0) clearInterval(btn.timer, window.onscroll = set); }, 10); }; function set() { btn.style.display = (d.scrollTop + b.scrollTop > 100) ? 'block': "none" } }; backTop('goTop'); ``` - 获得URL中GET参数值 ```js // 用法:如果地址是 test.htm?t1=1&t2=2&t3=3, 那么能取得:GET["t1"], GET["t2"], GET["t3"] function get_get(){ querystr = window.location.href.split("?") if(querystr[1]){ GETs = querystr[1].split("&"); GET = []; for(i=0;i -1) { this.splice(index, 1); } }; ``` - 判断数组里是否有某个元素 ```js Array.prototype.isContains = function (e) { for (i = 0; i < this.length && this[i] != e; i++); return !(i == this.length); } ``` - 按字典顺序,对每行进行数组排序 ```js function SetSort(){ var text=K1.value.split(/[\r\n]/).sort().join("\r\n");//顺序 var test=K1.value.split(/[\r\n]/).sort().reverse().join("\r\n");//反序 K1.value=K1.value!=text?text:test; } ``` - 字符串反序输出 ```js function IsReverse(text){ return text.split('').reverse().join(''); } ``` - 金额大写转换函数 ```js //格式转换 function transform(tranvalue) { try { var i = 1; var dw2 = new Array("", "万", "亿"); //大单位 var dw1 = new Array("拾", "佰", "仟"); //小单位 var dw = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"); //整数部分用 //以下是小写转换成大写显示在合计大写的文本框中 //分离整数与小数 var source = tranvalue.split("."); var num = source[0]; var dig = source[1]; //转换整数部分 var k1 = 0; //计小单位 var k2 = 0; //计大单位 var sum = 0; var str = ""; var len = source[0].length; //整数的长度 for (i = 1; i <= len; i++) { var n = source[0].charAt(len - i); //取得某个位数上的数字 var bn = 0; if (len - i - 1 >= 0) { bn = source[0].charAt(len - i - 1); //取得某个位数前一位上的数字 } sum = sum + Number(n); if (sum != 0) { str = dw[Number(n)].concat(str); //取得该数字对应的大写数字,并插入到str字符串的前面 if (n == '0') sum = 0; } if (len - i - 1 >= 0) { //在数字范围内 if (k1 != 3) { //加小单位 if (bn != 0) { str = dw1[k1].concat(str); } k1++; } else { //不加小单位,加大单位 k1 = 0; var temp = str.charAt(0); if (temp == "万" || temp == "亿") //若大单位前没有数字则舍去大单位 str = str.substr(1, str.length - 1); str = dw2[k2].concat(str); sum = 0; } } if (k1 == 3){ //小单位到千则大单位进一 k2++; } } //转换小数部分 var strdig = ""; if (dig != "") { var n = dig.charAt(0); if (n != 0) { strdig += dw[Number(n)] + "角"; //加数字 } var n = dig.charAt(1); if (n != 0) { strdig += dw[Number(n)] + "分"; //加数字 } } str += "元" + strdig; } catch(e) { return "0元"; } return str; } ``` - 格式化数字 ```js function fmoney(s, n) { //s:传入的float数字 ,n:希望返回小数点几位 n = n > 0 && n <= 20 ? n : 2; s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + ""; var l = s .split(".")[0] .split("") .reverse(), r = s.split(".")[1]; t = ""; for (i = 0; i < l.length; i++) { t += l[i] + ((i + 1) % 3 == 0 && i + 1 != l.length ? "," : ""); } return; t .split("") .reverse() .join("") + "." + r; } ``` ================================================ FILE: JAVASCRIPT/数组中的对象去重.md ================================================ ## js中数组对象去重的方法 ### 这里有两种方法 1. 采用对象访问属性的方法,判断属性值是否存在,如果不存在就添加 2. 采用数组中的reduce方法,遍历数组,也是通过对象访问属性的方法 ``` javascript 1.8 var arr = [{ key: '01', value: '乐乐' }, { key: '02', value: '博博' }, { key: '03', value: '淘淘' },{ key: '04', value: '哈哈' },{ key: '01', value: '乐乐' }]; // 方法1:利用对象访问属性的方法,判断对象中是否存在key var result = []; var obj = {}; for(var i =0; i
暂无数据

对不起,未搜索到相关信息

List.vue 父组件 ``` ================================================ FILE: JAVASCRIPT/用递归算法实现,数组长度为5且元素的随机数在2-32间不重复的值.html ================================================ Title dasdas ================================================ FILE: JAVASCRIPT/获取当前周每天对应的时间戳.md ================================================ ## 获取当前周 每天对应的时间戳 ```javascript 1.8 //方法: _initSearchTime() { let other = { startTime: '', endTime: '' } if (this.selectCont.selectView === '按周') { // 周数据的开始结束时间 if (this.searchData.dateTimeWeek === '') { // 没有开始时间 console.log("------------------------") this.searchData.dateTimeWeek = new Date(moment().startOf('isoWeek')) other.startTime = new Date(moment().startOf('isoWeek')).getTime() / 1000 other.endTime = Math.floor(new Date(moment().endOf('isoWeek')).getTime() / 1000) } else { // 有开始时间 other.startTime = new Date(moment(this.searchData.dateTimeWeek).startOf('isoWeek')).getTime() / 1000 other.endTime = new Date(moment(this.searchData.dateTimeWeek).endOf('isoWeek')).getTime() / 1000 } } else { let val = this.searchData.dateTimeMouth // 月数据的开始结束时间 if (this.searchData.dateTimeMouth === '') { // 没有开始时间 this.searchData.dateTimeMouth = new Date(moment().month(moment().month()).startOf('month')) other.startTime = moment().month(moment().month()).startOf('month').valueOf() / 1000 other.endTime = Math.floor(moment().month(moment().month()).endOf('month').valueOf() / 1000) } else { // 没有开始时间 var next = this.dz.getSencondStamp(new Date(val.getFullYear(), val.getMonth() + 1, 1)) var end = parseInt(parseInt(next) - 86400) // other.startTime = moment(this.searchData.dateTimeMouth).month().startOf('month').valueOf() / 1000 // other.endTime = Math.floor(moment(this.searchData.dateTimeMouth).month().endOf('month').valueOf() / 1000) // } other.startTime = this.dz.getSencondStamp(new Date(moment(val).startOf('month'))) other.endTime = end } } return other }, ``` ================================================ FILE: JAVASCRIPT/获取当前月份 第一天 最后一天.md ================================================ ### 获取当前月份第一天和最后一天 ```javascript var now = new Date(); //当前日期 var nowMonth = now.getMonth(); //当前月 var nowYear = now.getFullYear(); //当前年 //本月的开始时间 var monthStartDate = new Date(nowYear, nowMonth, 1); //本月的结束时间 var monthEndDate = new Date(nowYear, nowMonth+1, 0); var timeStar=Date.parse(monthStartDate)/1000;//s var timeEnd=Date.parse(monthEndDate)/1000;//s ``` ================================================ FILE: Node/NodeJs文档归纳.md ================================================ ## NodeJs 文档归纳 * [你需要了解的有关 Node.js 的所有信息](https://www.imooc.com/article/300129) * [Node.js 入门你需要知道的 10 个问题](https://www.imooc.com/article/289202) ================================================ FILE: Node/Sequelize.md ================================================ ## 一. api 文档定义:多对多关系 (Many-to-many association ) ### 简单使用 - 例子(三表联合查询): * 第一步: 创建表及关联表 1、学生表student : id、name、sex等 2、城市表city : id、province、city、county等 3、学生与城市关联表stu_city_info:id、stu_id、city_id等 * 第二步:sequelizejs 创建关联 ```javascript Department.associate = function() { const {Student, City, StuCityInfo } = app.model Student. belongsToMany(City, { through: StuCityInfo , foreignKey: 'stuId', otherKey: 'cityId' }) } ```` 详解: belongsToMany 多对多关系 , 源表Student、、源表City、关联表StuCityInfo(即中间表) foreignkey、otherkey为中间表的外键属性 ================================================ FILE: Node/express浅尝.md ================================================ ### 官网地址(https://www.expressjs.com.cn/) ### github(https://github.com/expressjs/expressjs.com) ```javascript 1.8 $ npm install express --save ``` ### express 热更新方法(express 框架不自带热更新) npm install -g node-dev或npm install node-dev -D 然后在package.json里加上"dev": "node-dev ./bin/www" ### 路由 ```javascript 1.8 var express = require('express'); var router = express.Router(); ```` router.get()处理GET请求和router.post() 处理POST请求 router.all() 处理所有HTTP方法,并使用router.use() 将中间件指定为回调函数 #### 简单封装路由 将所有的路由单独提取成一个router.js文件 引入express中提供的Router 将router替换成express中的router 最后采用module.exports的方式将router导出 > router.js ```javascript 1.8 var express = require('express'); var router = express.Router(); /* GET users listing. */ router.get('/users/:userId/books/:bookId', function(req, res, next) { res.send(req.params); }); router.get('/users', function(req, res, next) { res.send("userss"); }); router.post('/test', function(req, res, next) { res.send('test22222xx2'); }); module.exports = router; ``` > app.js ```javascript 1.8 var usersRouter = require('./router'); app.use(usersRouter); ``` #### 常用的返回方式 1. res.json([status|body], [body])  以json的形式返回数据 2. res.render(view [, locals] [, callback])  返回对应的view和数据,此方法可以有回调函数,以处理可能出现的异常 3. res.send([body|status], [body])  返回自定义的数据,比如json或者404等状态 4. res.redirect([status,] path)  这个方法其实不是返回,而是跳转到另外一个url上 ### 连接数据库 ### 示例 mysql 1. 安装依赖 ```javascript 1.8 npm i mysql --save ``` 2. 连接数据库 进行操作 ````javascript 1.8 let mysql = require('mysql') //连接数据库 let db=mysql.createConnection({host: "localhost", port: "3306", user: "root", password: "123456", database: "mysql"}); db.connect(); let sql = 'select * from websites' let str = " "; db.query(sql, function (err,result) { if(err){ console.log('[SELECT ERROR]:',err.message); } str = result; console.log(str); }); ```` 3. token(验证) https://www.npmjs.com/package/jsonwebtoken > jsonwebtoken ================================================ FILE: Node/koa浅尝.md ================================================ ### 官网地址 (https://koa.bootcss.com/) ### github (https://github.com/koajs) #### 创建koa2工程 > demo:(https://www.liaoxuefeng.com/wiki/1022910821149312/1099752344192192) #### 利用koa-generator快速生成koa2框架目录结构 > koa-generator 地址(https://www.npmjs.com/package/koa-generator) #### 开发时候 热更新 1. 安装 npm install nodemon --save 2. 修改packjson > start > = 'nodemon node bin/www' #### 数据库操作 第三方 插件 Sequelize(https://itbilu.com/nodejs/npm/sequelize-docs-v5.html#data-retrieval---finders) #### post请求参数解析 > 对于post请求处理,koa2没有封装轻便的方法获取参数,需要通过解析上下文context中的原生node.js请求对象req来获取。 >可安装第三方插件 koa-bodyparser插件处理 步骤如下 1、 安装koa-bodyparser ````javascript 1.8 cnpm i koa-bodyparser --save ```` 2、 引入bodyparser模块(该模块的引入要在router引入之前) ````javascript 1.8 const bodyParser = require('koa-bodyparser'); app.use(bodyParser()); ```` 3、 接收post请求(ajax或者表单提交)参数 ````javascript 1.8 ctx.request.body ```` ================================================ FILE: Node/现有使用量较大的框架.md ================================================ ### 一、框架选择(https://segmentfault.com/a/1190000018973856) #### 1、Express > github Weekly Downloads: 10,651,670 Express是一个最小且灵活的Web应用程序框架,为Web和移动应用程序提供了一组强大的功能,它的行为就像一个中间件,可以帮助管理服务器和路由 #### 2、Koa > github Weekly Downloads: 432,366 Koa 是一个新的 web 框架,由 Express幕后的原班人马打造,致力于成为web应用和API开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa帮你丢弃回调函数,并有力地增强错误处理Koa并没有捆绑任何中间件而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序 #### 3、Hapi > github Weekly Downloads:204,086 Hapi是基础功能相对丰富的框架。开发人员更专注于业务,而不是花时间构建基础架构。配置驱动的模式,区别于传统的web服务器操作。他还有比一个独特功能,能够在特定的IP上创建服务器,具有类似的功能onPreHandler。再需要的时候你可以拦截特地的请求做一些必要的操作 ================================================ FILE: README.md ================================================ # 工作 学习 总结 收集 总结 一些在开发工作中的所遇到的点滴 ,所涉及的技术栈 包括 css、js、vuejs、react.js、react Native、node.js、redis等前端知识 ## 教程文档 * [易百教程](https://www.yiibai.com/) * [开发者手册](https://cloud.tencent.com/developer/devdocs) * [Web 开发技术 mozilla](https://developer.mozilla.org/zh-CN/docs/Web) * [W3School](https://www.w3school.com.cn/) * [菜鸟教程](https://www.runoob.com/) * [印记中文](https://docschina.org/) * [Frontend Developer 路线](https://roadmap.sh/frontend) * [Awesome lists about all kinds of interesting topics](https://github.com/sindresorhus/awesome) * [Awesome Hacking](https://github.com/Hack-with-Github/Awesome-Hacking) * [GitHub秘籍](https://github.com/tiimgreen/github-cheat-sheet/blob/master/README.zh-cn.md) * [Awesome JavaScript](https://github.com/sorrycc/awesome-javascript) * [前端进阶必备,github 优质资源整理分享!](https://juejin.im/post/5d3edad9f265da03a652f133) ## books * [前端开发者指南 2016](https://github.com/FrontendMasters/front-end-handbook) * [前端开发者指南 2017](https://github.com/FrontendMasters/front-end-handbook-2017) * [前端开发者指南 2018](https://github.com/FrontendMasters/front-end-handbook-2018) * [前端开发者指南 2019](https://github.com/FrontendMasters/front-end-handbook-2019) * [开发人员路线图 2019](https://github.com/kamranahmedse/developer-roadmap) * [前端开发面试题](https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions) * [技术面试必备基础知识](https://github.com/CyC2018/CS-Notes) * [Free Programming Books](https://github.com/EbookFoundation/free-programming-books) * [免费编程书籍](https://github.com/justjavac/free-programming-books-zh_CN) * [前端精读周刊](https://github.com/dt-fe/weekly) * [Front-End Checklist](https://github.com/thedaviddias/Front-End-Checklist) * [全栈增长工程师指南](https://github.com/phodal/growth-ebook) * [前端技术清单](https://github.com/alienzhou/frontend-tech-list) * [前端收集](https://github.com/foru17/front-end-collect) * [Grab Front End Guide](https://github.com/grab/front-end-guide) * [JS 函数式编程指南](https://github.com/llh911001/mostly-adequate-guide-chinese) * [木易杨 高级前端进阶](https://github.com/yygmind/blog) * [每天一道面试题](https://github.com/Advanced-Frontend/Daily-Interview-Question) * [前端技术书](https://github.com/jobbole/awesome-web-dev-books) * [前端面试手册](https://github.com/yangshun/front-end-interview-handbook) * [Front-end Job Interview Questions](https://github.com/h5bp/Front-end-Developer-Interview-Questions) * [成为专业程序员路上用到的各种优秀资料、神器及框架](https://github.com/stanzhai/be-a-professional-programmer) * [微信小程序开发资源汇总](https://github.com/justjavac/awesome-wechat-weapp) * [Awesome JavaScript](https://github.com/sorrycc/awesome-javascript) * [GitHub最全的前端资源汇总仓库(包括前端学习、开发资源、求职面试等)](https://github.com/helloqingfeng/Awsome-Front-End-learning-resource) * [前端小密圈](https://github.com/jawil/blog) * [全栈工程师培训材料](https://github.com/ruanyf/jstraining) * [前端Web开发人员收集资源](https://github.com/dypsilon/frontend-dev-bookmarks) * [ECMAScript 6入门](https://github.com/ruanyf/es6tutorial) * [安邦的JavaScript学习笔记](https://github.com/anbang/javascript-notes) * [开源代码库和课程表](https://github.com/freeCodeCamp/freeCodeCamp) * [猫的前端回忆录](https://github.com/windiest/Front-end-tutorial) * [前端开发指南集合](https://github.com/icepy/Front-End-Develop-Guide) * [Front-end Tools](https://github.com/codylindley/frontend-tools) * [Web前端技术栈](https://github.com/unruledboy/WebFrontEndStack) * [收集优质的中文前端博客](https://github.com/FrankFang/best-chinese-front-end-blogs) * [前端面试每日 3+1(每日三问)](https://github.com/haizlin/fe-interview) * [前端开发面试题大收集](https://github.com/paddingme/Front-end-Web-Development-Interview-Question) * [前端入门和进阶学习笔记](https://github.com/qianguyihao/Web) * [jsliang 的文档库](https://github.com/LiangJunrong/document-library) * [100天学习前端开发的课程](https://github.com/nas5w/100-days-of-code-frontend) * [大迁世界前端翻译](https://github.com/qq449245884/xiaozhi) * [前端实验室](https://8788.github.io/lab/) * [前端九部 - 入门者手册2019](https://www.yuque.com/fe9/basic) * [大前端面试宝典 - 图解前端](https://github.com/azl397985856/fe-interview) ## html ## css * [normalize.css](https://github.com/necolas/normalize.css) * [reset.css](https://meyerweb.com/eric/tools/css/reset/) * [animate.css](https://github.com/daneden/animate.css) * [You-Dont-Need-JavaScript](https://github.com/you-dont-need/You-Dont-Need-JavaScript) * [30 Seconds of CSS](https://github.com/30-seconds/30-seconds-of-css) * [一个动图,一个CSS知识点](https://github.com/qdlaoyao/css-gif) * [CSS灵感](https://github.com/chokcoco/CSS-Inspiration) * [CSS 奇技淫巧](https://github.com/chokcoco/iCSS) * [CSS3动画编辑](https://www.w3cways.com/css3-animation-tool) ## JavaScript * [每个 JavaScript 工程师都应懂的33个概念 33-js-concepts](https://github.com/leonardomso/33-js-concepts) * [30 Seconds of Code](https://github.com/30-seconds/30-seconds-of-code) * [Highlight.js](https://github.com/highlightjs/highlight.js) * [Lodash](https://github.com/lodash/lodash) * [Underscore](https://github.com/jashkenas/underscore) * [Functional-Light JavaScript](https://github.com/getify/Functional-Light-JS) * [You-Dont-Need-Lodash-Underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore) * [List of (Advanced) JavaScript Questions](https://github.com/lydiahallie/javascript-questions) * [qs](https://github.com/ljharb/qs) * [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) * [Modern JavaScript Cheatsheet](https://github.com/mbeaudru/modern-js-cheatsheet) * [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) * [LeetCode](https://github.com/azl397985856/leetcode) * [JavaScript 算法与数据结构](https://github.com/trekhleb/javascript-algorithms) * [常用的资源或代码片段](https://github.com/jsfront/src) * [JS Tips](https://github.com/loverajoel/jstips) ## Node.js * [Node.js 最佳实践](https://github.com/goldbergyoni/nodebestpractices) * [Node.js 包教不包会](https://github.com/alsotang/node-lessons) * [Awesome Node.js](https://github.com/sindresorhus/awesome-nodejs) * [Express](https://www.expressjs.com.cn/) * [koa](https://koa.bootcss.com/) * [Sequelize 数据库操作](https://itbilu.com/nodejs/npm/sequelize-docs-v5.html#data-retrieval---finders) * [koa-generator 快速生成koa2框架目录结构](https://www.npmjs.com/package/koa-generator) * [token 验证](https://www.npmjs.com/package/jsonwebtoken) ## Vue * [Awesome Vue.js](https://github.com/vuejs/awesome-vue) ## React ## shell [Shell脚本](http://c.biancheng.net/shell/) ================================================ FILE: React/React代码规范.md ================================================ /** * @name: React代码规范 * @author: LIULIU * @date: 2020-11-27 11:24 * @description:React代码规范 * @update: 2020-11-27 11:24 */ ## React 代码规范 https://github.com/airbnb/javascript(网友整理Javascript规范) https://github.com/airbnb/javascript/tree/master/react(网友整理React规范) https://github.com/airbnb/css#css(网友整理CSS规范) 团队中每个开发人员的水平不同,技术关注点不同,如果没有一份代码规范的参照和约束,那么项目中的代码将会风格迥异,难以维护,为保证代码质量和风格统一,特此拟定一份《团队React 代码规范》,这样整个团队的开发人员可以参照这份代码规范进行编码,从而让团队的代码风格统一,利于维护。如果你的团队还没有这么一份 React 代码规范,也许这正是你需要的;如果你的团队已经有了 React 代码规范,这份规范也许能起到锦上添花的效果。 ### 1、基础规则 1. 一个文件声明一个组件: 尽管可以在一个文件中声明多个 React 组件,但是最好不要这样做;推荐一个文件声明一个 React 组件,并只导出一个组件; 2. 使用 JSX 表达式: 不要使用 React.createElement 的写法; 3. 函数组件和 class 类组件的使用场景: 如果定义的组件不需要 props 和 state ,建议将组件定义成函数组件,否则定义成 class 类组件。 ### 2、组件声明 (1)组件名称和定义该组件的文件名称建议要保持一致; ````javascript 1.8 推荐: import Footer from './Footer'; ```` ````javascript 1.8 不推荐: import Footer from './Footer/index'; ```` (2)不要使用 displayName 属性来定义组件的名称,应该在 class 或者 function 关键字后面直接声明组件的名称。 ````javascript 1.8 推荐: export default class MyComponent extends React.Component { } ```` ````javascript 1.8 不推荐: export default React.Component({ displayName: 'MyComponent', }); ```` ### 3、React 中的命名 • 组件名称: 推荐使用大驼峰命名; • 属性名称: React DOM 使用小驼峰命令来定义属性的名称,而不使用 HTML 属性名称的命名约定; • style 样式属性: 采用小驼峰命名属性的 JavaScript 对象; 推荐: ````javascript 1.8 // 组件名称 MyComponent // 属性名称 onClick // 样式属性 backgroundColor ```` ### 4、JSX 写法注意 ````javascript 1.8 4.1、标签 (1)当标签没有子元素的时候,始终使用自闭合的标签 。 推荐: // Good 不推荐: (2)如果标签有多行属性,关闭标签要另起一行 。 推荐: 不推荐: (3)在自闭标签之前留一个空格。 推荐: 不推荐: (4)当组件跨行时,要用括号包裹 JSX 标签。 推荐: render() { return ( ); } 不推荐: render() { return ; } ```` 4.2、对齐 ````javascript 1.8 JSX 语法使用下列的对齐方式 : // 推荐 // 如果组件的属性可以放在一行(一个属性时)就保持在当前一行中 // 多行属性采用缩进 // 不推荐 4.3、引号 JSX 的属性都采用双引号,其他的 JS 都使用单引号 ,因为 JSX 属性 不能包含转义的引号, 所以当输入 "don't" 这类的缩写的时候用双引号会更方便。 推荐: 不推荐: ```` ### 5、样式写法 React 中样式可以使用 style 行内样式,也可以使用 className 属性来引用外部 CSS 样式表中定义的 CSS 类,我们推荐使用 className 来定义样式。并且推荐使用 SCSS 来替换传统的 CSS 写法,具体 SCSS 提高效率的写法可以参照先前总结的文章。 ### 6、defaultProps 使用静态属性定义 defaultProps 推荐使用静态属性定义,不推荐在 class 外进行定义。 ````javascript 1.8 推荐: class Example extends React.Component { static defaultProps = { name: 'stranger' } render() { // ... } } 不推荐: class Example extends React.Component { render() { // ... } } Example.propTypes = { name: PropTypes.string }; ```` ### 7、key 属性设置 key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key,但是要主要如果列表项目的顺序可能会变化,如果使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。 ````javascript 1.8 推荐: {todos.map(todo => ( ))} 不推荐: {todos.map((todo, index) => )} ```` ### 8、为组件绑定事件处理器 React 为组件绑定事件处理器提供 4 种方法,有 public class fields 语法、构造函数中进行绑定、在回调中使用箭头函数、使用 Function.prototype.bind 进行绑定,我们推荐使用 public class fields 语法,在不满足需求情况下使用箭头函数的写法(传递参数给事件处理器)。 ````javascript 1.8 推荐: handleClick = () => { console.log('this is:', this); } 不推荐: constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(){ console.log('this is:', this); } ```` ### 9、State ````javascript 1.8 9.1、不要直接修改 state 除了 state 初始化外,其它地方修改 state,需要使用 setState() 方法,否则如果直接赋值,则不会重新渲染组件。 推荐: this.setState({comment: 'Hello'}); 不推荐: this.state.comment = 'hello'; 9.2、State 的更新可能是异步的 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用;因为 this.props 和 this.state 可能会异步更新,所以这种场景下需要让 setState() 接收一个函数而不是一个对象 。 推荐: this.setState((state, props) => ({ counter: state.counter + props.increment })); 不推荐: this.setState({ counter: this.state.counter + this.props.increment, }); ```` ### 10、组件的代码顺序 组件应该有严格的代码顺序,这样有利于代码维护,我们推荐每个组件中的代码顺序一致性。 ````javascript 1.8 class Example extends Component { // 静态属性 static defaultProps = {} // 构造函数 constructor(props) { super(props); this.state={} } // 声明周期钩子函数 // 按照它们执行的顺序 // 1. componentWillMount // 2. componentWillReceiveProps // 3. shouldComponentUpdate // 4. componentDidMount // 5. componentDidUpdate // 6. componentWillUnmount componentDidMount() { ... } // 事件函数/普通函数 handleClick = (e) => { ... } // 最后,render 方法 render() { ... } } ```` ### 11、使用高阶组件 使用高阶组件解决横切关注点问题,而不是使用 mixins ,mixins 导致的相关问题可以参照文档; ### 12、避免不必要 render 的写法 shouldComponentUpdate 钩子函数和 React.PureComponent 类都是用来当 state 和 props 变化时,避免不必要的 render 的方法。shouldComponentUpdate 钩子函数需要自己手动实现浅比较的逻辑,React.PureComponent 类则默认对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。我们推荐使用React.PureComponent 避免不要的 render。 ### 13、状态提升 如果多个组件需要反映相同的变化数据,建议将共享状态提升到最近的共同父组件中去;从而依靠自上而下的数据流,而不是尝试在不同组件间同步 state。 ### 14、推荐使用 Context 如果某个属性在组件树的不同层级的组件之间需要用到,我们应该使用 Context 提供在组件之间共享此属性的方式,而不不是显式地通过组件树的逐层传递 props。 ### 15、Refs 写法 Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素 。我们推荐使用 createRef API 的方式 或者 回调函数的方式使用 Refs ,而不是使用this.refs.textInput 这种过时的方式访问 refs ,因为它存在一些 问题。 ### 16、路由加载 建议使用路由懒加载当前用户所需要的内容,这样能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积,但你可以避免加载用户永远不需要的代码,并在初始加载的时候减少所需加载的代码量。 ````javascript 1.8 推荐: const OtherComponent = React.lazy(() => import('./OtherComponent')); 不推荐: import OtherComponent from './OtherComponent'; ```` ### 17、AJAX 发起请求的时机 推荐在 componentDidMount这个生命周期函数中发起 AJAX 请求。这样做你可以拿到 AJAX 请求返回的数据并通过 setState 来更新组件。 ================================================ FILE: React/常见使用总结.md ================================================ /** * @name: 常见使用总结 * @author: LIULIU * @date: 2020-11-23 10:01 * @description:常见使用总结 * @update: 2020-11-23 10:01 */ ### react 日常使用总结 1. React 子元素点击事件禁止触发父元素点击事件 ````javascript 1.8 e.stopPropagation() // 阻止事件冒泡 ```` ### 1.参考 官网文档 https://zh-hans.reactjs.org/ 慕课入门视频 https://www.imooc.com/learn/1023(React16.4 快速上手) CLI工具(快速创建项目骨架,建议将项目进行eject) https://www.html.cn/create-react-app/docs/getting-started/ 状态管理工具(非必须,视项目需求) https://cn.mobx.js.org/(Mobx中文站) https://mobx.js.org/README.html(Mobx英文站) https://dvajs.com/guide/(阿里,配合使用umi) https://www.redux.org.cn/ (redux中文站) https://redux.js.org/introduction/getting-started(redux英文站) redux其他变种react-redux、redux-thunk、redux-saga 框架 https://umijs.org/zh/(阿里生态一员) https://ice.work/docs/guide/about(飞冰,通用框架,主要用于后端玩前端) 扩展包仓库 https://www.npmjs.com/ 主要使用React公司(产品) Twitter、七牛、知乎、npm、Coding、美团、石墨文档、阿里巴巴(阿里云、Teambition、语雀、支付宝(部分)、云凤蝶、Antv) ### 2、学习路径 1. 了解React的基础概念; 2. 搭建开发环境(nvm安装nodejs、npm(或cnpm淘宝源)、yarn); 3. 通过CLI(create-react-app)工具创建项目骨架(认识目录结构); 4. 根据教程练习各知识点; a. js基础知识(在react中All In JS) b. JSX模板基础语法 c. state、生命周期、渲染机制(相对vue略多) d. 事件处理 e. 列表渲染 f. 表单处理(社区基础组件) g. 组件及props(组件之间的通讯机制、及实现方式) h. 高阶组件、hooks i. 路由集成(react-router-dom) j. 构建、项目部署(webpack) k. 请求封装(axios、fetch等) l. 认证、安全(鉴权:页面、请求、组件) m. 状态管理(Mobx、Dva、Redux,推荐Mobx轻量) n. 服务端渲染(已有成熟的SSR框架:Next.js) o. 性能优化(加载快、资源占用小、运行流畅、良好体验、深入模块化) p. 测试(单元测试) q. 移动端、PC端、桌面端(Electron、NWjs)、原生应用(react-native)、小程序(mpvue、uni-app等) r. typescript s. 其他... ### 3、工程化实践 CRA + react + react-router(或react-router-dom) + Mobx(或Redux) + axios(或fetch) + antd 路由建议使用react-router-dom,提供支持DOM的操作。 ### 4、问题找答案 养成主动思考、主动寻找答案的习惯,不能在自己都没怎么思考的时候去问,这样是不能解决根本问题的。 参考: https://zhuanlan.zhihu.com/p/55951841(程序员如何正确提问题) https://zhuanlan.zhihu.com/p/25747684(如何提问题) https://www.zhihu.com/question/20161362(如何用好谷歌等搜索引擎?) 1. 官方文档 2. 搜索引擎 3. 知识汇集平台(stackoverflow.com、segmentfault.com、知乎、百度、谷歌、社区等) 4. 平常可以关注知名平台(各大厂的社群,如:360奇舞团、阿里云栖社区等) 5、常用小工具汇总 https://www.yuque.com/hvfcy6/go0thg/ge7lom ================================================ FILE: React-Native/APP端消息推送.md ================================================ ### 可选择方案 (极光、TPNS(腾讯云,原名:信鸽)、阿里云、友盟、个推、华为(短信,貌似无APP消息流)、百度云...) ### 方案枚举 服务器主动push消息到设备端,主要依赖于长连接。不同终端需要各自维护连接的高可用、自我实现费力不讨好,建议还是采用成熟厂商。 * 方案1: Android自带的推送GCM给使用的Android应用程序发送数据(具体没用过,不了解) 缺点:国内用户不稳定 * 方案2: XMPP协议 缺点:协议较复杂、冗余(基于XML)、费流量、费电,部署硬件成本高 * 方案3: MQTT 缺点:不够成熟、实现较复杂、服务端组件rsmb不开源,部署硬件成本较高 * 方案4: HTTP轮循 缺点:实时性差 * 方案5: 采用第三方 第三方管理长连接,省心、服务有保障、有背书方。体验、数据形式都有保障 ================================================ FILE: React-Native/APP端消息推送之极光推送.md ================================================ ## 选择其原因 相对接入成本低、成熟度高、文档完善、推送渠道支持多、有自建社区、新版本更新及时、有一定的免费额度。 ### [github 地址](https://github.com/jpush/jpush-react-native) ## 自我使用总结 ### 1. 安装 ```javascript 1.8 npm install jpush-react-native --save npm install jcore-react-native --save ``` ### 2. 配置 #### 2.1 android * 进入[极光推送官网](https://www.jiguang.cn/) 注册账号 * 进入开发者平台 创建android应用 选择所需要的 产品服务 设置应用包名为对应的项目anroid包名 提交并组装sdk * 进入android/app/build.gradle 新增 ```javascript 1.8 android { ··· defaultConfig { ... manifestPlaceholders = [ JPUSH_APPKEY: "在此替换你的APPKey", //APPKey(上一步创建应用对应的APPKey) JPUSH_CHANNEL: "yourChannel" //在此替换你的channel 可自定义 ] } } ``` ```javascript 1.8 dependencies { ··· implementation project(':jpush-react-native') // 添加 jpush 依赖 implementation project(':jcore-react-native') // 添加 jcore 依赖 ··· } ``` * 进入android/settings.gradle 新增 ```javascript 1.8 ··· include ':jpush-react-native' project(':jpush-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jpush-react-native/android') include ':jcore-react-native' project(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android') ``` * 进入android/app/src/main/AndroidManifest.xml 新增 ```javascript 1.8 ··· ··· ``` #### 2.2 IOS(暂未总结) ### 3. 基础react native 代码(在项目首页中执行) ```javascript 1.8 import JPush from 'jpush-react-native'; // 可进入node_modules/jpush-react-native/index.js 针对项目需要查看具体方法 componentDidMount() { JPush.init(); //连接状态 this.connectListener = result => { console.log("connectListener:" + JSON.stringify(result)) }; JPush.addConnectEventListener(this.connectListener); //通知回调 this.notificationListener = result => { console.log("notificationListener:" + JSON.stringify(result)) }; JPush.addNotificationListener(this.notificationListener); //本地通知回调 this.localNotificationListener = result => { console.log("localNotificationListener:" + JSON.stringify(result)) }; JPush.addLocalNotificationListener(this.localNotificationListener); //自定义消息回调 this.customMessageListener = result => { console.log("customMessageListener:" + JSON.stringify(result)) }; JPush.addCustomMessagegListener(this.customMessageListener); //tag alias事件回调 this.tagAliasListener = result => { console.log("tagAliasListener:" + JSON.stringify(result)) }; JPush.addTagAliasListener(this.tagAliasListener); //手机号码事件回调 this.mobileNumberListener = result => { console.log("mobileNumberListener:" + JSON.stringify(result)) }; JPush.addMobileNumberListener(this.mobileNumberListener); } ``` ### 4.消息推送(必须真机调试 模拟器调试获取不到设备id 无法成功调试 真机调试时候 应设置该设备允许通知) 进入开发者平台 选择发送通知 编辑消息 设置目标人群即可 ================================================ FILE: React-Native/ios通过浏览器下载app/方法.md ================================================ ## 通过 itms-services://?action=download-manifest&url=https://xxxxx.plist 链接方式下载 ### plist文件(必须为https协议地址) ```javascript items assets kind software-package url https://xxxxxxx/pms.ipa //ipa资源地址(https协议) kind display-image needs-shine url https://xxxxxxxx/app_icon.png //图标地址 kind full-size-image needs-shine url https://xxxxxxxx/app_icon.png //完整尺寸图标地址 metadata bundle-identifier xxxx // 包名 bundle-version 1.0.0 //版本号 kind software title xxx // 软件名 ``` ================================================ FILE: React-Native/使用Promise封装fetch请求.md ================================================ ### 使用Promise封装fetch请求 ```javascript let common_url = 'http://192.168.1.1:8080/'; //服务器地址 let token = ''; /** * @param {string} url 接口地址 * @param {string} method 请求方法:GET、POST,只能大写 * @param {JSON} [params=''] body的请求参数,默认为空 * @return 返回Promise */ function fetchRequest(url, method, params = ''){ let header = { "Content-Type": "application/json;charset=UTF-8", "accesstoken":token //用户登陆后返回的token,某些涉及用户数据的接口需要在header中加上token }; console.log('request url:',url,params); //打印请求参数 if(params === ''){ //如果网络请求中没有参数 return new Promise(function (resolve, reject) { fetch(common_url + url, { method: method, headers: header }).then((response) => response.json()) .then((responseData) => { console.log('res:',url,responseData); //网络请求成功返回的数据 resolve(responseData); }) .catch( (err) => { console.log('err:',url, err); //网络请求失败返回的数据 reject(err); }); }); }else{ //如果网络请求中有参数 return new Promise(function (resolve, reject) { fetch(common_url + url, { method: method, headers: header, body:JSON.stringify(params) //body参数,通常需要转换成字符串后服务器才能解析 }).then((response) => response.json()) .then((responseData) => { console.log('res:',url, responseData); //网络请求成功返回的数据 resolve(responseData); }) .catch( (err) => { console.log('err:',url, err); //网络请求失败返回的数据 reject(err); }); }); } } ``` ================================================ FILE: React-Native/使用总结.md ================================================ ### 1、router: react-native-router-flux >基于react-navigation/native 二次封装 ### 2、字体图标:react-native-vector-icons(推荐) 遇到的问题: React Native CLI uses autolinking for native dependencies, but the following modules are linked manually: - react-native-vector-icons (to unlink run: "react-native unlink react-native-vector-icons") 解决方法: >1、xcode 配置info.list 将fonts 复制到ios 文件夹中与项目名相同的文件夹中 >2、在info.list中将以下代码复制进去: ```javascript 1.8 UIAppFonts AntDesign.ttf Entypo.ttf EvilIcons.ttf Feather.ttf FontAwesome.ttf FontAwesome5_Brands.ttf FontAwesome5_Regular.ttf FontAwesome5_Solid.ttf Foundation.ttf Ionicons.ttf MaterialIcons.ttf MaterialCommunityIcons.ttf SimpleLineIcons.ttf Octicons.ttf Zocial.ttf Fontisto.ttf ``` ### 3、teaset ui库 >(使用起来有很多吭 建议不使用 比如里面的drawer 需要注意 手动关闭Drawer 时 会出发当前页面更新 导致state中的data复原,Drawer 里面的state数据更新需要处理) ### 4、时间日期选择器 >import DatePicker from 'react-native-datepicker' ### 5、配置mobx >1、安装mobx npm install mobx --save > >2、安装bable转码 npm install @babel/plugin-proposal-decorators > >3、创建 .babelrc 配置文件 ```javascript 1.8 { "presets": ["module:metro-react-native-babel-preset"], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/transform-runtime", { "helpers": true, "regenerator": false }] ] } ``` >4、安装 以下 4个依赖包 yarn add @babel/core @babel/plugin-proposal-decorators @babel/plugin-transform-runtime @babel/runtime >5、仓库绑定到根组件上 ```javascript 1.8 import { Provider } from 'mobx-react' import store from './store' ``` ### 6、uI库 antdesign 配置antdesign: >1.这是安装antd-mobile-rn yarn add @ant-design/react-native >2.antd-mobile-rn里面有很多Icon和font如果需要引入,则下载 yarn add @ant-design/icons-react-native >3.在根目录创建.babelrc { "plugins": [ ["import", { "libraryName": "@ant-design/react-native" }] ] } >4.安装其他依赖 yarn add @react-native-community/cameraroll @react-native-community/picker @react-native-community/segmented-control @react-native-community/slider @react-native-community/viewpager >5.报错提示安装 npm install babel-plugin-import --save    npm install >6.ios端使用其字体图标(https://blog.csdn.net/lxyoucan/article/details/108334465) ### 7、地图 link 跳转第三方 >第三方依赖:https://github.com/starlight36/react-native-map-linking ### 8、数据缓存方案: >a、 AsyncStorage 一个简单的、异步的、持久化的 Key-Value 存储系统,它对于 App 来说是全局性的。有一个极大的缺点就是:只可以存储字符串。也就是说,如果需要将对象或者数组等存入到AsyncStorage中需要先将他们转为字符串 demo:https://blog.csdn.net/z93701081/article/details/90905178(已过时) 最新官网推荐:import AsyncStorage from '@react-native-community/async-storage'; git地址:https://react-native-community.github.io/async-storage/docs/install > >b、react-native-sqlite || react-native-sqlite-storage 存放数据结构复杂的数据的时候,我们就会想起sqlite,sqlite这种跨平台的数据存储方式在ReactNative里也有对应的方式,那就是react-native-sqlite ### 9、React Native封装请求函数 ```javascript 1.8 /** * @name: index * @author: LIULIU * @date: 2020-07-20 13:38 * @description:index * @update: 2020-07-20 13:38 */ /* * 网络请求相关 * */ import {Toast} from '@ant-design/react-native'; export const BaseUrl = `http://example.net/`; export default class Request { static get(url) { return new Promise((resolve, reject) => { fetch(BaseUrl + url) .then(response => { if (response.ok) { return response.text(); } else { throw new Error('网络请求失败!'); } }) .then(responseText => { const responseJSObj = JSON.parse(responseText); resolve(responseJSObj); }) .catch(error => { Toast.fail('访问异常') reject(error); }); }); } /** * RN提供的fetch方法,是异步的,它本身就会返回一个Promise对象。但因为这里我们对它进行了封装,所以外面又包了一层Promise,来给fetch这个异步任务提供回调,这样外界才能拿到fetch的结果。 * * @param url * @param params * @returns {Promise | Promise} */ static async post(url, params) { return new Promise(async (resolve, reject) => { fetch(BaseUrl + url, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(params), }) .then(response => { if (response.ok) { // 请求到的response其实是一个Response对象,它是一个很原始的数据格式,我们不能直接使用,先获取它的JSON字符串文本格式 return response.text(); } else { throw new Error('网络请求失败!'); } }) .then(responseText => { // 然后把JSON字符串序列化为JS对象 const responseJSObj = JSON.parse(responseText); // 把请求成功的数据传递出去 resolve(responseJSObj); }) .catch(error => { // 把请求失败的信息传递出去 Toast.fail('访问异常') reject(error); }); }); } } ``` ### 10、React Native适配安卓IOS刘海屏、异形屏方案 >a、引入以下模块 ```javascript 1.8 import { Platform, SafeAreaView, NativeModules, StatusBar } from "react-native"; const { StatusBarManager } = NativeModules; b、获取状态栏高度 let statusBarHeight; if (Platform.OS === "ios") { StatusBarManager.getHeight(height => { statusBarHeight = height; }); } else { statusBarHeight = StatusBar.currentHeight; } ``` >c、渲染的时候使用SafeAreaView(光使用SafeAreaView只能保证ios设备上正常) ```javascript 1.8 > ``` ### 11、基础架构配置文件 >a、server --封装请求函数 功能性js(get post) >b、store --mobx >c、router --react-native-router-flux(react native navigation) >d、.babelrc 文件 (使用andesign ui组件库、mobx时 需要的配置文件,andesign ui 为默认组件库) >e、global (配置接口地址 或者全局都需要使用到的常量、j s (配置一些基础的功能性js 如防抖、截流)) ### 12、配置APP 名称、图标、启动页 >1、app名字 Android: 打开android/项目名/src/main/res/values/strings.xml ```javascript 1.8 物联服务 ``` IOS: 打开ios/项目名/Info.plist 添加 ```JAVASCRIPT 1.8 CFBundleDisplayName 应用名称 ``` >2、app图标 直接用图标工场(https://icon.wuruihong.com/#/ios)一键在线生成,一键下载,然后自直接替换就行。 Android: 用图标工场下载的文件,替换 android/app/src/main/res 下对应的图标文件夹 IOS: 用图标工场下载的文件,替换 ios/MyApp/Images.xcassets/AppIcon.appiconset 中的内容 >3、启动页 react-native-splash-screen ios: 1. 按react-native-splash-screen 官网进行基础配置 在 images.xcassets文件下创建New ios launch image 命令'LaunchScreen' 进行配置图片(直接拖入图片) 2. 编辑launchScreen.storyboard ,添加image空间 在右上角搜索“LaunchScreen“ 获取刚刚所创建的图片 进行配配置 ### 13、打包、发布 ios:在package.json 文件中 添加命令: ```javascript 1.8 { ... "scripts": { "bundle-ios": "react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ios/bundle/main.jsbundle --assets-dest ios/bundle" } ... } ``` 执行 npm run bundle-ios 命令 直接生成静态资源 再通过xcode打包完成之后会让开发者确认是否发布在app store 上面 demo:https://blog.csdn.net/weixin_43586120/article/details/104622566 android:https://reactnative.cn/docs/0.51/signed-apk-android 打包完成后自行上传到指定的应用商店 ### 14、mac 环境android 调试环境 >a、安装virtualbox (简单易用还免费的开源虚拟机) >b、安装genymotion(是一个非常快速的 Android 模拟器,秒级开机关机速度,傻瓜式安装,易于使用,将复杂的技术隐藏于VitualBox、HardWare OpenGL等驱动引擎中) >c、androidstudio (推荐) ### 15、React Native 之 原生模块退出至React nativ模块 ### IOS 模块 ```objectivec // 注销事件函数 - (void)logoutAction { [self.navigationController popViewControllerAnimated:YES]; } - (void)viewWillDisappear:(BOOL)animated { // 取消状态栏 不取消i返回react native 页面后 会出现状态栏 self.navigationController.navigationBarHidden = YES; } ``` ##### viewWillDisappear (ios生命周期之视图将要消失) ```objectivec // ios 生命周期 1-1 initWithNibName:bundle:------初始化(xib和纯代码),初始化控制器,可以写数据初始化操作,不要写View相关操作StoryBoard: 1-2 init StoryBoard 1-1 initWithCoder:------初始化,不会直接初始化控制器 1-2 awakeFromNib------xib加载完成(xib),一些实例化加载写在此处 2.loadView------加载视图,默认从nib,如果nib为空则会创建一个空视图(重写时,不要写super) 3.viewDidLoad------视图已经加载完成(自带的View加载完成),用于初始化数据、设定、约束、移除视图等操作 4.viewWillAppear:------视图将要出现,用于设置设备不同方向时如何显示,状态栏方向,视图显示样式 5.viewWillLayoutSubviews------view将要布局子视图 6.viewDidLayoutSubviews------view已经布局子视图 7.viewDidAppear:------视图已经显示 8.viewWillDisappear:------视图将要消失 9.viewDidDisappear:------视图已经消失 10.didReceiveMemoryWarning------控制器出现内存警告 11.dealloc------视图被销毁,系统只会释放内存,不会释放对象的所有权,所以通常在这里置为nil ``` ### Android 模块 在退出时间中执行以下代码: ```java this.finish(); ``` ## [React Native deomo](https://github.com/xiaoliuxiansheng/React-Native-Mass) ================================================ FILE: Redis/index.md ================================================ * @name: index * @author: LIULIU * @date: 2020-08-07 09:47 * @description:index * @update: 2020-08-07 09:47 ## 什么是redis(https://redis.io/) REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。 > Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。 >Redis 与其他 key - value 缓存产品有以下三个特点: * Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。 * Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 * Redis支持数据的备份,即master-slave模式的数据备份。 > 优势: * 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。 * 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。 * 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。 * 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。 ================================================ FILE: Redis/浅尝.md ================================================ * @name: 浅尝 * @author: LIULIU * @date: 2020-08-07 14:26 * @description:浅尝 * @update: 2020-08-07 14:26 ## 简单demo > 安装下载依赖 ````javascript 1.8 npm install redis --save ```` > 配置信息 ````javascript 1.8 let options = { host: 'localhost', port: '6379', auth_pass: '!pemywN%qW%$E3miO6OvKuj@*BR&3sX1' }; let redisClient = require('redis').createClient(options); let wrapper = require('co-redis'); let Redis = wrapper(redisClient); Redis.select(6); // 数据库选择,可以在配置文件设置数据库总数 默认16个 ```` > 进行读写操作 ````javascript 1.8 //封装 set k—v function setKey(key,value,callback){ Redis.on("connect",()=>{ Redis.set(key,value,callback) }) } //封装get k—v function getKey(key,callback){ Redis.on("connect",()=>{ Redis.get(key,callback) }) } //删除 Redis.del('name',(err,val)=>{ if(err){ console.log(err); return; } console.log(val) }) //set设置 setKey("age","18",(err,res)=>{ if(err){ console.log(err) return } console.log(res) }) //get获取 getKey("age",(err,res)=>{ if(err){ console.log(err) return } console.log(res) }) ```` >设置过期 ````javascript 1.8 Redis.expire('key',60);//60秒自动过期 ```` > 全部操作方法 ````javascript 1.8 // redis配置参数 let redis_config = { "host": "127.0.0.1", "port": 6379 }; let password = ''; //密码 let dbs = {}; const redis = require("redis"); const client = redis.createClient(redis_config); if (password) { client.auth(password, function () { console.log("连接成功") }); } client.on("error", err => console.log('------ Redis connection failed ------' + err)) .on('connect', () => console.log('------ Redis connection succeed ------')); client.set('hello','This is a value'); client.expire('hello',10) //设置过期时间 client.exists('key') //判断键是否存在 client.del('key1') client.get('hello'); //stirng 命令 行为 返回值 使用示例(略去回调函数) set 设置存储在给定键中的值 OK set('key', 'value') get 获取存储在给定键中的值 value/null get('key') del 删除存储在给定键中的值(任意类型) 1/0 del('key') incrby 将键存储的值加上整数increment incrby('key', increment) decrby 将键存储的值减去整数increment decrby('key', increment) incrbyfloat 将键存储的值加上浮点数increment incrbyfloat('key', increment) append 将值value追加到给定键当前存储值的末尾 append('key', 'new-value') getrange 获取指定键的index范围内的所有字符组成的子串 getrange('key', 'start-index', 'end-index') setrange 将指定键值从指定偏移量开始的子串设为指定值 setrange('key', 'offset', 'new-string') //list 命令 行为 返回值 使用示例(略去回调函数) rpush 将给定值推入列表的右端 当前列表长度 rpush('key', 'value1' [,'value2']) (支持数组赋值) lrange 获取列表在给定范围上的所有值 array lrange('key', 0, -1) (返回所有值) lindex 获取列表在给定位置上的单个元素 lindex('key', 1) lpop 从列表左端弹出一个值,并返回被弹出的值 lpop('key') rpop 从列表右端弹出一个值,并返回被弹出的值 rpop('key') ltrim 将列表按指定的index范围裁减 ltrim('key', 'start', 'end') //set 命令 行为 返回值 使用示例(略去回调函数) sadd 将给定元素添加到集合 插入元素数量 sadd('key', 'value1'[, 'value2', ...]) (不支持数组赋值)(元素不允许重复) smembers 返回集合中包含的所有元素 array(无序) smembers('key') sismenber 检查给定的元素是否存在于集合中 1/0 sismenber('key', 'value') srem 如果给定的元素在集合中,则移除此元素 1/0 srem('key', 'value') scad 返回集合包含的元素的数量 sacd('key') spop 随机地移除集合中的一个元素,并返回此元素 spop('key') smove 集合元素的迁移 smove('source-key'dest-key', 'item') sdiff 返回那些存在于第一个集合,但不存在于其他集合的元素(差集) sdiff('key1', 'key2'[, 'key3', ...]) sdiffstore 将sdiff操作的结果存储到指定的键中 sdiffstore('dest-key', 'key1', 'key2' [,'key3...]) sinter 返回那些同事存在于所有集合中的元素(交集) sinter('key1', 'key2'[, 'key3', ...]) sinterstore 将sinter操作的结果存储到指定的键中 sinterstore('dest-key', 'key1', 'key2' [,'key3...]) sunion 返回那些至少存在于一个集合中的元素(并集) sunion('key1', 'key2'[, 'key3', ...]) sunionstore 将sunion操作的结果存储到指定的键中 sunionstore('dest-key', 'key1', 'key2' [,'key3...]) //hash 命令 行为 返回值 使用示例(略去回调函数) hset 在散列里面关联起给定的键值对 1(新增)/0(更新) hset('hash-key', 'sub-key', 'value') (不支持数组、字符串) hget 获取指定散列键的值 hget('hash-key', 'sub-key') hgetall 获取散列包含的键值对 json hgetall('hash-key') hdel 如果给定键存在于散列里面,则移除这个键 hdel('hash-key', 'sub-key') hmset 为散列里面的一个或多个键设置值 OK hmset('hash-key', obj) hmget 从散列里面获取一个或多个键的值 array hmget('hash-key', array) hlen 返回散列包含的键值对数量 hlen('hash-key') hexists 检查给定键是否在散列中 1/0 hexists('hash-key', 'sub-key') hkeys 获取散列包含的所有键 array hkeys('hash-key') hvals 获取散列包含的所有值 array hvals('hash-key') hincrby 将存储的键值以指定增量增加 返回增长后的值 hincrby('hash-key', 'sub-key', increment) (注:假如当前value不为为字符串,则会无输出,程序停止在此处) hincrbyfloat 将存储的键值以指定浮点数增加 //zset 命令 行为 返回值 使用示例(略去回调函数) zadd 将一个带有给定分支的成员添加到有序集合中 zadd('zset-key', score, 'key') (score为int) zrange 根据元素在有序排列中的位置,从中取出元素 zrangebyscore 获取有序集合在给定分值范围内的所有元素 zrem 如果给定成员存在于有序集合,则移除 zcard 获取一个有序集合中的成员数量 有序集的元素个数 zcard('key') keys命令组 命令 行为 返回值 使用示例(略去回调函数) del 删除一个(或多个)keys 被删除的keys的数量 del('key1'[, 'key2', ...]) exists 查询一个key是否存在 1/0 exists('key') expire 设置一个key的过期的秒数 1/0 expire('key', seconds) pexpire 设置一个key的过期的毫秒数 1/0 pexpire('key', milliseconds) expireat 设置一个UNIX时间戳的过期时间 1/0 expireat('key', timestamp) pexpireat 设置一个UNIX时间戳的过期时间(毫秒) 1/0 pexpireat('key', milliseconds-timestamp) persist 移除key的过期时间 1/0 persist('key') sort 对队列、集合、有序集合排序 排序完成的队列等 sort('key'[, pattern, limit offset count]) flushdb 清空当前数据库 // redis事务(支持连贯操作) /* * 常用命令 * multi() 事务 * exec([callback]) 执行事务 * discard 放弃事务 * watch 监视指定的键值 * unwatch 取消监视 * * 命令用法: * MULTI * MULTI 命令用于开启一个事务,它总是返回 OK 。 * MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, * 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时, 所有队列中的命令才会被执行。 * 另一方面, 通过调用 DISCARD , 客户端可以清空事务队列, 并放弃执行事务。 * EXEC * EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 * 其中, 回复元素的先后顺序和命令发送的先后顺序一致。 * DISCARD * 当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出 * WATCH * WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 * 被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 * 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, * EXEC 返回nil-reply来表示事务已经失败。 * * WATCH 使得 EXEC 命令需要有条件地执行: * 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。 * * WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。 * * 当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。 * 另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。 * UNWATCH * 使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 * * 使用示例: * 1、连贯操作 * client.multi().incr('key').incr('key').exec(function (err, reply) {}); * * 2、常规操作 * multi = client.multi(); * multi.incr('key'); * multi.incr('key'); * multi.exec(function (err, replies) {}); * * 3、取巧操作 * client.multi([ * ["mget", "multifoo", "multibar", redis.print], * ["incr", "multifoo"], * ["incr", "multibar"] * ]).exec(function (err, replies) { * console.log(replies); * }); * */ // 事件监听 /* * 事件:ready、connect、reconnecting、end、warning、error * 执行顺序: ready > connect > end(quit触发) * error事件需要设置监听(必选) * * 使用示例: * redisClient.on("ready", function(err) { * if (err) return false; * console.log("ready"); * }); */ redisClient.on("error", function (err) { console.log("Error " + err); }); ```` ================================================ FILE: package.json ================================================ { "name": "work-sunnary", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, "repository": { "type": "git", "url": "git+https://github.com/xiaoliuxiansheng/work-summary.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/xiaoliuxiansheng/work-summary/issues" }, "homepage": "https://github.com/xiaoliuxiansheng/work-summary#readme" } ================================================ FILE: vue/Vue导出页面为PDF、word、html格式.md ================================================ ## Vue导出页面为PDF、word、html格式 转载于链接: ================================================ FILE: vue/element-audio.md ================================================ ## vue 音频播放 audio 转载于(https://github.com/wangduanduan/element-audio) ================================================ FILE: vue/element-ui/element更改表格表头、行、列、指定单元格样式.md ================================================ ## element更改表格表头、行、列、指定单元格样式 ================================================ FILE: vue/js中使用filter里面的方法.md ================================================ ```javascript 1.8 this.$options.filters['方法名'](参数) ```` ================================================ FILE: vue/vue 中使用rem.md ================================================ ## vue 中使用rem ```javascript document.documentElement.style.fontSize = 100 * window.innerWidth/750 + 'px'; window.addEventListener('resize',{}={ document.documentElement.style.fontSize = 100 * window.innerWidth/750 + 'px'; }) ``` ================================================ FILE: vue/vue 项目中如何在页面刷新的状态下保留数据.md ================================================ ## vue 项目中如何在页面刷新的状态下保留数据 ### 解决方法: * 使用vuex作状态管理: 将vuex里面的数据同步更新到localStorage里面,改变vuex里的数据,便触发localStorage.setItem 方法 ``` javascript import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) function storeLocalStore (state) { window.localStorage.setItem("userMsg",JSON.stringify(state)); } const store = new Vuex.Store({ modules: { tags:[], curTagsIndex:"-1", }, mutations: { SET_CURTAGS: (state, index) => { state.curTagsIndex = index }, } }) export default store ``` * 在 App.vue 的 created 钩子函数里写下了如下代码; ``` //在页面加载时读取localStorage里的状态信息 localStorage.getItem("userMsg") && this.$store.replaceState(Object.assign(this.$store.state,JSON.parse(localStorage.getItem("userMsg")))); //在页面刷新时将vuex里的信息保存到localStorage里 window.addEventListener("beforeunload",()=>{ localStorage.setItem("userMsg",JSON.stringify(this.$store.state)) }) ``` ================================================ FILE: vue/vue中怎么重置data?.md ================================================ # vue中怎么重置data? 初始状态下设置data数据的默认值,重置时直接object.assign(this.$data, this.$options.data()) 说明: this.$data获取当前状态下的data this.$options.data()获取该组件初始状态下的data(即初始默认值) 如果只想修改data的某个属性值,可以this[属性名] = this.$options.data()[属性名],如this.message = this.$options.data().message ================================================ FILE: vue/上传图片oss.md ================================================ ### 上传图片至oss 基于element-ui ```javascript 1.8 ``` ================================================ FILE: vue/上传文件oss.md ================================================ ### 上传文件至oss 基于element-ui ```javascript 1.8 ``` ================================================ FILE: vue/如何动态添加页面title 跟 favicon.ico.md ================================================ ## 如何动态添加页面title 跟 favicon.ico ```javascript schoolName(newName) { document.title = newName }, h5Host(newUrl){ this.common.goH5(newUrl) }, logo(newLogo){ var link = document.querySelector("link[rel*='icon']") || document.createElement('link'); link.type = 'image/x-icon'; link.rel = 'shortcut icon'; link.href = newLogo; document.getElementsByTagName('head')[0].appendChild(link) } ``` ================================================ FILE: vue/子组件向父组件传参父组件添加自定义数据.md ================================================ ## 子组件向父组件传参父组件添加自定义数据 ```javascript 1.8 ``` $event:表示子组件默认数据 后面那个参数 可传父组件自定义数据 ================================================ FILE: vue/资料文档集合.md ================================================ /** * @name: 资料文档集合 * @author: LIULIU * @date: 2020-11-30 14:26 * @description:资料文档集合 * @update: 2020-11-30 14:26 */ ### 一. 资源教程 1. 综合类 - [vuejs 英文资料](https://github.com/vuejs/awesome-vue) - [Vue中文资料总汇](https://shimo.im/s/ab53739d-ca98-bd1e-aea0-fcc7378c6bb3) - [Vue.js 的一些资源索引](http://segmentfault.com/a/1190000000411057) - [vue资料](http://www.jianshu.com/p/afd8e1db7d9b) - [Vue.js——vue-resource全攻略](http://www.cnblogs.com/keepfool/p/5657065.html) 2. 入门类 - [vue 快速入门](http://segmentfault.com/a/1190000003968020) - [vuex - 入门教程实例1](https://segmentfault.com/a/1190000005018970) - [vuex - 入门教程实例2](https://segmentfault.com/a/1190000005148935) - [vuex - 入门教程实例3](https://segmentfault.com/a/1190000006988584) 3. 英文教程 - [Learning Vue 1.0: Step By Step](https://laracasts.com/series/learning-vue-step-by-step/) - [vue Learning Tutorials](https://coligo.io/) 4. 社区 - [sf - vue标签](http://segmentfault.com/t/vue.js) - [知乎 - vue标签](https://www.zhihu.com/topic/20022242/questions) - [Vue.js专业中文社区](http://vue-js.com/) - [vue - issues](https://github.com/vuejs/vue/issues) 5. 视频教程 - [小凡哥录制视频教程](https://github.com/bhnddowinf/vuejs-learn) ### 二. 文档 - API - [Vue.js - API](http://cn.vuejs.org/api/) - router - [vue-router文档](http://router.vuejs.org/zh-cn/index.html) ### 三. 组件 #### 1. 官方组件 - Loader - [html loader module for webpack](https://github.com/vuejs/vue-html-loader) - Router - [vue-spa-demo](https://github.com/lazyhero/vue-spa-demo) - Vuex:专门为 Vue.js 应用设计的状态管理架构 - [vuex](http://vuex.vuejs.org/zh-cn/intro.html) - Ajax - [vue + ajax](https://github.com/vuejs/vue-hackernews/blob/gh-pages/src/components/NewsView.vue#L61) - [vue + ajax](https://github.com/vuejs/vue-resource) - [vue-async-data](https://github.com/vuejs/vue-async-data) - Vue-cli - [Simple CLI for scaffolding Vue.js projects](https://github.com/vuejs/vue-cli) #### 2. 其它组件 - UI类 - [VueStrap:使用 Vue.js 和纯 JavaScript 构建的 Bootstrap 组件](https://github.com/yuche/vue-strap) - [针对 Vue 框架移植的 We UI 框架的适配,让 mobile开发过程成为一种享受](https://github.com/aidenzou/vue-weui) - [vue bootstrap](http://yuche.github.io/vue-strap/) - [基于Vue、Bootstrap的一套MVVM组件,简单、易用、功能强大](https://github.com/bravf/VueUI) - [用VUE 和 SUI-Mobile 写了一个移动端demo - vue-sui-demo](https://github.com/eteplus/vue-sui-demo) - [Mobile web UI based on Vue and Weui](https://github.com/airyland/vux) - [Mint UI 基于 Vue.js 的移动端组件库](http://mint-ui.github.io/) - [Vue Admin Panel Framework [WIP] ](https://github.com/fundon/vue-admin) - [Vue 的图片轮播组件](https://github.com/qusiba/vue-slider) - [基于vue开发的material design ui库](https://github.com/myronliu347/vue-carbon) - [iView:一套基于Vue的高质量UI组件库](http://gold.xitu.io/post/57b535405bbb50006300ffc9) - [iView - 一套基于 Vue.js 的高质量 UI 组件库](https://www.iviewui.com/overview) - [Muse-UI 基于 Vue 2.0 和 Material Desigin 的 UI 组件库](https://museui.github.io/) - [Vue,Node管理系统界面](https://github.com/ericjjj/vms) - [vue2 admin / a management system template](https://github.com/PanJiaChen/vue-element-admin) - [全新的 WDUI,助力移动开发](https://wdfe.github.io/wdui/#/) - [手摸手,带你用 vue 撸后台 系列一](https://juejin.im/post/59097cd7a22b9d0065fb61d2) - [一只基于Vue2.x的移动端&微信UI - YDUI Touch](http://vue.ydui.org/) - 表格 - [ZEE大神作品 - vue-editable](https://github.com/jinzhe/vue-editable) - 分页 - [vue入门-实现一个分页组件(1.0.3版本)](http://segmentfault.com/a/1190000003931500) - [vue(1.0.3) 分页组件](https://github.com/cycgit/vue-pagination) - [分页组件 -- 命令方式,支持多个,主要靠配置](https://www.npmjs.com/package/vue-pagination) - 移动 - [Hammer.js wrapper for Vue.js](https://github.com/vuejs/vue-touch) - [vue的tap手势插件](https://github.com/MeCKodo/vue-tap) - 微信 - [为微信Web服务量身设计](http://aidenzou.github.io/vue-weui/) - 日历 - [ZEE大神作品 - vue-calendar](https://github.com/jinzhe/vue-calendar) - [Awe - 日历组件](https://github.com/hilongjw/vue-datepicker) - [jas0ncn - 日历组件](https://github.com/jas0ncn/vue-timepicker) - [基于Vue.2x 的日期选择组件](https://github.com/watson-yan/vue-datepicker) - 进度条 - [vue 进度条](https://github.com/greyby/vue-spinner) - 验证 - [Validator component for VueJS](https://github.com/vuejs/vue-validator) - [form validator for vue 0.11](https://github.com/xrado/vue-validator) - Lazyload - [图片的lazyload](https://github.com/hilongjw/vue-lazyload) - [下拉加载](https://github.com/ElemeFE/vue-loadmore) - Loader - [【vue】vue组件化开发初体验-示例vue-loader-example学习记录](http://segmentfault.com/a/1190000004060034) - Uploader - [vue-file-upload](https://www.npmjs.com/package/vue-file-upload) - [vue-file-upload-component](https://www.npmjs.com/package/vue-file-upload-component) - Slide - [vue-onlySlider-x](https://github.com/guan6/vue-onlySlider-x) - Drag - [vue-drag-and-drop](https://github.com/james2doyle/vue-drag-and-drop) - 二维码 - [vue-qrcode](https://github.com/xiaokaike/vue-qrcode) - 省市联动 - [Vue 省市区三级联动组件](https://github.com/QingWei-Li/vue-region-picker) - TimeLine - [Vue企业级时间轴选择器](https://github.com/yelingfeng/vue-timelinepick) - Template - [vue2.0模板](https://github.com/yelingfeng/vue-ylf-template) - [vue2.0全家桶实例](https://github.com/yelingfeng/vue-2.0-FamilyBucket) ### 四. 示例 - 综合示例 - [官方例子 - 包括Markdown编辑器,表格组件等](http://cn-stage.vuejs.org/examples/) - [Vue, vue-router, Webpack 和 vue-loader](https://github.com/vingojw/vue-vueRouter-webpack) - [简单的vuejs例子 - 群内高手X-Roy大作](https://github.com/KennyWho/vue-chestnut) - [vue single page app example](https://github.com/toplan/vue-spa-example) - [使用node+vue.js实现SPA应用](http://segmentfault.com/a/1190000004372736) - Ajax - [A Vue.js component for creating simple AJAX forms.](https://github.com/james2doyle/vue-ajax-form-component) - Webpack - [Vue + webpack 项目实践](http://jiongks.name/blog/just-vue/) - [基于vue.js和webpack的Chat示例](http://segmentfault.com/a/1190000003630417) - [Vue.js 和 Webpack(一)](http://djyde.github.io/2015/08/29/vuejs-and-webpack-1/) - [Vue.js 和 Webpack(二)](http://djyde.github.io/2015/08/30/vuejs-and-webpack-2/) - [Vue.js 和 Webpack(三)](http://djyde.github.io/2015/08/31/vuejs-and-webpack-3/) - [二哲 - 结合具体项目的webpack配置](https://github.com/MeCKodo/webpack) - Gulp - [vue-gulp-webpack单页面组件开发](https://github.com/JsAaron/vue-gulp-webpack) - Tab - [vue tab](http://yuche.github.io/vue-strap/#tabs) - Shopping - [基于Vue模仿蘑菇街的单页应用](https://github.com/andylei18/vue-shopping) - 权限管理 - [基于Vue的前端权限管理解决方案](https://github.com/tower1229/Vue-Access-Control) - 其它 - [Ant Design 的 Vue 实现](http://okoala.github.io/vue-antd/#!/docs/introduce) - [基于vue.js重写Cnodejs.org社区的webapp](https://github.com/shinygang/Vue-cnodejs) - [北京-giscafer - vue+webpack+node.js 价格监测应用](https://github.com/giscafer/Ponitor) ### 五. 开发工具相关 1. Atom - [atom vue 代码高亮](https://github.com/CYBAI/language-vue-component) - [如何发布一个Atom的package](http://www.jianshu.com/p/98f99c20493c) - [vue-format](https://atom.io/packages/vue-format) 2. Sublime-text - [Vue Syntax Highlight](https://github.com/vuejs/vue-syntax-highlight) 3. Webstorm - [webstorm添加*.vue文件支持](http://www.lred.me/2016/01/07/webstorm%E6%B7%BB%E5%8A%A0-vue%E6%96%87%E4%BB%B6%E6%94%AF%E6%8C%81/) - [webstorm vue插件](https://plugins.jetbrains.com/plugin/8057?pr=webStorm) ### 六. 答疑 - [Vue.js为什么不支持templateUrl模式](http://www.jianshu.com/p/7f7f050c9edf) - [实现了vuejs组件之间的通讯问题](https://github.com/jrainlau/vuejs-demo) - [国内有哪些公司在用Vue.js,有什么心得](https://www.zhihu.com/question/38213423) - [vue 支持服务器端渲染吗](https://www.zhihu.com/question/39149401) - [Vue.js 和 Webpack](http://div.io/topic/1343) ### 七. 源码学习 - [Vue.js 源码学习笔记](http://jiongks.name/blog/vue-code-review) - [VUE 源码分析](http://www.cnblogs.com/sskyy/p/3695003.html) - [vue源码分析之如何实现observer和watcher](http://segmentfault.com/a/1190000004384515) - [vue源码解析之一:transition](https://segmentfault.com/a/1190000004670036) - [Vue源码学习 Vue.js v1.0.18](https://github.com/JsAaron/vue-analysis) ### 八. 框架相关 - [尤小右 - 4-1 Vue.js-数据驱动的组件化前端开...](http://www.imooc.com/video/6346) - [Evan You - Diving Deep into Vue.js](https://simplecast.fm/s/5e60d9be) ================================================ FILE: vue/阿里云oss 上传.md ================================================ ## oss 上传 ```JAVASCRIPT 1.8 ``` ================================================ FILE: vue/页面刷新的时候如何保存store.md ================================================ export default { name: 'App', created () { //在页面加载时读取sessionStorage里的状态信息 if (sessionStorage.getItem("store") ) { this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store")))) } //在页面刷新时将vuex里的信息保存到sessionStorage里 window.addEventListener("beforeunload",()=>{ sessionStorage.setItem("store",JSON.stringify(this.$store.state)) }) } } ================================================ FILE: webpack/日常问题总结.md ================================================ ## 项目打包后 路径出现问题 ### 解决方案如下 * 在 vue.config.js中配置 baseUrl:'./' ===> process.env.BASE_API:'./' * 配置 VUE_APP_PUBLIC_PATH=./ ================================================ FILE: 插件/Canvas 插件(签名板).md ================================================ ### canvas 签名板在h5的应用 插件:signature_pad 以下为基于vue vux的使用 * 组件 ```javascript 1.8 ``` * 使用方法 ```javascript 1.8 ``` > 精髓 * 动态设置canvas宽度 高度 解决在屏幕大的手机 点击位置不准确 ```javascript 1.8 var clientWidth = document.documentElement.clientWidth; let canvas = document.getElementById("signature-canvas") canvas.setAttribute('width',`${clientWidth*0.95}px`); // 0.95 =>设置canvas的宽度 为屏幕宽度的0.95 0.842 => 表示设置画布 宽高比例 根据不同的需求 自定义即可 canvas.setAttribute('height',`${clientWidth*0.95*0.842}px`); ``` * dataURLtoFile (dataurl, filename) > 因为通过signature_pad 画出来的图形为64进制文件流 需转成我们需要的格式 在这里 通过这个函数转为文件的格式(image/png) ```javascript 1.8 dataURLtoFile (dataurl, filename) { let arr = dataurl.split(','); let mime = arr[0].match(/:(.*?);/)[1]; let bstr = atob(arr[1]); let n = bstr.length; let u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, {type: mime}); } ``` ================================================ FILE: 插件/echart.js.md ================================================ ## echart.js 封装按需引入 转载于: ================================================ FILE: 插件/moment.js.md ================================================ ## moment.js Moment.js 是一个 JavaScript 日期处理类库,用于解析、检验、操作、以及显示日期。 ================================================ FILE: 插件/one.md ================================================ ## 聚合网址(https://www.juhe.cn/) 免费向开发者提供全国车辆违章查询API,天气API,基站数据,移动联通基站,电信基站,覆盖国内外1000多个主要城市公共交通信息数据,衣食住行,金融,LBS数据以及其他各种有效 ================================================ FILE: 插件/postCSS.md ================================================ ### PostCSS 是一个用 JavaScript 工具和插件转换 CSS 代码的工具 > 利用从 Can I Use 网站获取的数据为 CSS 规则添加特定厂商的前缀。Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。(当代码中使用了deep后 不能生效) ================================================ FILE: 数据库/数据库建表规则.md ================================================ /** * @name: 数据库建表规则 * @author: LIULIU * @date: 2020-09-25 15:44 * @description:数据库建表规则 * @update: 2020-09-25 15:44 */ # 什么是”范式(NF)” 按照教材中的定义,范式是“符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度”。很晦涩吧?实际上你可以把它粗略地理解为一张数据表的表结构所符合的某种设计标准的级别。就像家里装修买建材,最环保的是E0级,其次是E1级,还有E2级等等。数据库范式也分为1NF,2NF,3NF,BCNF,4NF,5NF。一般在我们设计关系型数据库的时候,最多考虑到BCNF就够。符合高一级范式的设计,必定符合低一级范式,例如符合2NF的关系模式,必定符合1NF ## 第一范式(1NF) 符合1NF的关系中的每个属性都不可再分 是所有关系型数据库的最基本要求 ## 第二范式(2NF) 2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖(码:关系中的某个属性或者某几个属性的组合,用于区分每个元组(可以把“元组”理解为一张表中的每条记录,也就是每一行)) * 函数依赖 -- 若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y。也就是说,在数据表中,不存在任意两条记录,它们在X属性(或属性组)上的值相同,而在Y属性上的值不同。这也就是“函数依赖”名字的由来,类似于函数关系 y = f(x),在x的值确定的情况下,y的值一定是确定的。 例如,对于表3中的数据,找不到任何一条记录,它们的学号相同而对应的姓名不同。所以我们可以说姓名函数依赖于学号,写作 学号 → 姓名。但是反过来,因为可能出现同名的学生,所以有可能不同的两条学生记录,它们在姓名上的值相同,但对应的学号不同,所以我们不能说学号函数依赖于姓名 ## 第三范式 满足2NF,非主键外的所有字段必须互不依赖 ## 第四范式 满足3NF,消除表中的多值依赖 ================================================ FILE: 数据库/设计规范.md ================================================ /** * @name: 设计规范 * @author: LIULIU * @date: 2020-09-25 17:05 * @description:设计规范 * @update: 2020-09-25 17:05 */ # 一、命名规范 ## 1、总命名规范 1. 不得使用数据库保留关键字,以及php/java等常用语言的保留关键字,或者可能成为关键字的单词作为完整命名。(对于一些疑似关键字的单词,可以在后面加一个下划线来避免,例如“key_”)。【附:MySQL保留关键字列表:https://dev.mysql.com/doc/refman/5.7/en/keywords.html】 2. 如无特殊说明,名称必须用英文字母开头,采用有特征含义的单词或缩写,单词中间用“_”分割,且只能由英文字母、数字和下划线组成,不能用双引号包含。 3. 除数据库名称长度为1至8个字符,其余(包括表、字段、索引等)不超过30个字符,Database link名称也不要超过30个字符。(30并不是凭空想象出来的,而是参考了Oracle的限制) ## 2、表名 (建议以2-3字项目名称为前缀开头),紧跟2-5个字符(英文字母或数字,但不得全是数字)的模块名(必须),最后跟上当前表的含义的单词(1-3个单词,用下划线连接),例如:SQ_SYS_CAR,SQ是项目名称的缩写,SYS是模块名称的缩写,CAR表示当前表的具体含义。 特别强调:项目名称和模块名用简写(建议长度为2-5个字符),而表含义的名称,可简写、也可以不简写,但是都不能超过3个单词,例如下面两个反面例子: 1. ABF_SUPERVISION_USER,问题:模块名称似乎比较长,建议控制在2-5个字符,缩写为 ABF_SUPV_USER; 2. ABF_SYS_USER_MANAGE_ORG_ROLE,问题:除去前缀ABF_SYS_,表含义(USER_MANAGE_ORG_ROLE)超过了3个单词。 ## 3、字段名 1. 表的字段数不超过50个。 2. 类型:各表之间相同含义的字段,类型定义要完全相同(包括精度、默认值等); 3. 命名:字段名无单词数的限制,但是名字的字符长度应该符合上面的“总命名规范”, 字段命名及其注释,要做到清楚、无歧义。 举两个实际的例子, 1)有些数据可能会存在多种完全不同类型的状态,例如,例如汽车数据,有启停状态,参保状态,维修状态,年审状态……总之,在有些数据表中,有许多的状态字段。如果没写清楚,例如有个字段 “STATUS tinyint NULL; -- 状态”,这是让人很疑惑的,状态?到底是什么状态?状态的取值有哪些?——如果改成“DELETE_STATUS tinyint default 0; -- 删除状态(1:已删除,默认为0:未删除)”,这样的命名和注释,让人一目了然。 2)再比如“belong_dept -- 所属部门”,这也有歧义,因为部门除了数据唯一ID之外,还有一个部门编码CODE也是唯一的。那到底是存 部门ID,还是 部门编码 CODE?实际情况是,有的人认为存ID,有的却认为存编码。所以,在命名上就应该做到无歧义,如果要存ID,就应该命名为“belong_dept_id -- 所属部门ID”,如果要存部门编码,就应该为“belong_dept_code -- 所属部门编码”。 3. 同一个字段名在一个数据库中只能代表一个意思。比如phone在一个表中代表“座机号码”的意思,在另外一个表中就不能代表其他意思(比如手机名称、品牌等,否则在A表中phone存的是座机号码,在B表中存的是手机品牌,那就混乱了) 4. 反之,代表同一个意思的字段,在各个表中都用相同单词表示,例如电话号码字段,在A表中叫telephone,在B表中叫phone,在C表中叫mobile,这样就很混乱。 特殊情况:如果有多个字段时,可以加前缀或后缀区分,代表复数含义时,单词后可以加s,例如user_ids。比如“电话号码”,在A表字段中名称为tel,在B表中也只能叫做tel(但是如果B表中有多种电话号码,可以加后缀,例如 保卫部 tel_bw,科技部 tel_kj,综合部 tel_zh)。 5. 对于多个表关联的外键字段,例如 create_user_id,关联的是 user表里面 id 字段,建议的命名规则是 “关联表名(无需前缀)+"_"+关联字段名”,也就是说,单词是根据表和字段名而来的,不是凭空随便想出来的。例如这个 create_user_id,create_是前缀,user_代表 abf_sys_user表,id代表abf_sys_user表的id字段。再比如create_user_dept_code,user_是abf_sys_user表的后缀,dept_是abf_sys_dept表的后缀,code是abf_sys_dept表的code_字段。 综合第4、5点,再举一例:有一个部门表abf_sys_dept,里面有一个部门编码字段code_,如果有一个表需要保存 "责任部门编号" 和 "创建人所属部门编号",按照规范,这两个字段可以命名为:resp_dept_code 和 create_user_dept_code。 ## 4、主键名 前缀为PK_。以PK_+表名+主键字段名构成。如果复合主键的构成字段较多,则只包含第一个字段。表名可以去掉前缀。例如PK_SYS_CAR_ID。 ## 5、外键名 前缀为FK_。以FK_+ 外键表名 + 主键表名 + 外键字段名构成。表名可以去掉前缀。例如FK_SYS_USR_SYS_CAR_ID。 ## 6、普通索引 前缀为IDX_。以IDX_+表名+索引字段名构成。如果复合索引的构成字段较多,则只包含第一个字段,并添加序号。表名可以去掉前缀。例如IDX_SYS_CAR_DIN。 ## 7、主键索引 前缀为IDX_PK_。以IDX_PK_+表名+索引字段名构成。表名可以去掉前缀。例如IDX_PK_SYS_CAR_ID。 ## 8、唯一索引 前缀为IDX_UK_。以IDX_UK_+表名+索引字段名构成。表名可以去掉前缀。例如IDX_UK_SYS_CAR_DIN。 ## 9、外键索引 前缀为IDX_FK_。以IDX_FK_+表名+外键字段名构成。表名可以去掉前缀。例如IDX_FK_SYS_CAR_ID。 ## 10、Oracle序列 前缀为SEQ_。以SEQ_+“序列业务名称”构成。如果“序列业务名称”就是某个表名,则使用表的全名,不可去掉前缀。例如SEQ_SQ_SYS_CAR。 # 二、表设计规范 采用UTF8字符集。 对于数据量可能很大的表(超过2000万),采用分库/分表/分区表,横向拆分控制单表容量。 必须为表、字段等添加注释。 遵守数据的设计规范3NF 规定。 表内的每一个记录都只能被表达一次。 表内的每一个记录都应该被唯一的标识(有唯一键)。 表内不应该存储依赖于其他键的非键信息。 反范式化冗余字段使用规范 考虑具体使用场景,当SQL关连查询比较频繁,或涉及到4张以上表时可考虑采用冗余字段。 必须设置唯一主键,尽量使用自增id作为主键。 建议主键为数字类型,且为递增顺序,主键不表示任何业务含义,严禁数据量大的表使用UUID/MD5作为主键。 不使用数据库外键,由程序保证。 MySQL: 使用InnoDB存储引擎。 数据库和表字符集类型统一(utf8mb4 -- UTF-8 Unicode),排序规则统一(utf8mb4_unicode_ci);建表语句中强制指定字符集; 自增字段类型必须是整型,使用 BIGINT类型。并且自增字段必须是主键或者是主键的一部分。 # 三、字段设计规范 1. 凡是可能被索引的字段,必须定义为NOT NULL,可以设置default值; 2. 非负值的数字统一使用unsigned(无符号)类型存储 ?? 3. 大对象字段 通常情况下,禁止使用LOB类型保存大文本、图片和文件,建议使用其他方式存储(例如文件系统,数据库只保存其地址信息)。 MySQL:尽量不要使用TEXT数据类型,mysql的varchar类型支持65535字节,满足大多数场景,仅当字符数特别大时,才考虑text类型; 附——大对象字段处理方法: 将大对象字段从主表中拆分出来单独存放,与原表主键单独存储在另外一个表里; 如果是Oracle 12g之前的版本,VARCHAR2最多支持4000,如果文本内容只是偶尔可能超过4000,但是不会超过8000,那么可以用两个VARCHAR2字段来存储,使用的时候将这两个字段拼接起来就行了。 如果有方便的文件系统,可以将大文本或附件,保存在文件系统中,数据库中只保存其位置和路径信息即可。 4. 禁止使用enum,对于boolean类型或者表示简单状态的字段,MySQL用tinyint,Oracle用NUMBER(1) 建议字段not null,根据业务要求来设置默认值(例如默认为0)。 对于boolean类型,以1代表是(true), 0 代表否(false)。 对于状态类型,注释中应该注明每一种状态的含义,例如“0:编辑中,1:审核中,2:已完成”。 5. 数字、小数类型 对于数字、小数类型,不得使用VACHAR等字符串类型来保存,应该使用相应精度的数字、小数类型。 尽量确保数值型列都有默认值 对于Oracle,确定好Number的精度。 对于MySQL,选好数字类型:TINYINT>SMALLINT>MEDIUMINT>INT>BIGINT>DECIMAL(存储空间逐渐变大,而性能却逐渐变小),超过tinyint(256)但不超过65536的使用smallint;当该字段超过42亿时,才使用bigint; 使用DECIMAL 代替 FLOAT 和 DOUBLE 存储精确浮点数??why? 6. 时间类型标准 对于Oracle,有两种时间类型:DATE和TIMESTAMP,DATE的精度只保存到秒,例如“2013-11-02 11:16:36”,而TIMESTAMP精度更高可以保存小数秒,例如“2013-11-03 11:16:36.000000” 。有时候,DATE只保存到秒,不足够区别出两个事件哪个先发生,这时建议使用TIMESTAMP类型。 MySQL:存储年使用year类型,存储日期使用date类型,使用精确时间戳(精确到秒)尽量使用timestamp类型,因为timestamp使用4字节,datetime使用8字节,它们的区别:TIMESTAMP值不能早于1970或晚于2037('1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC)。 7. 必须使用int unsigned存储IPV4; 8. 一些常见字段的命名统一 为了规范命名,并结合一般命名习惯,指定如下几个字段定义(以Oracle为例): ID 编号 NUMBER(22)(Integer) Create_By 创建人 NUMBER(22)(Integer) Create_Time 创建时间 TIMESTAMP --默认为系统当前时间 Update_By 修改人 NUMBER(22)(Integer) Update_Time 修改时间 TIMESTAMP --默认为系统当前时间 其他参考命名: Code_ 编码 VARCHAR2(30) Level_ 层级 NUMBER(1或2) Delete_Status 删除标志 NUMBER(1) --1:表示已经删除,默认为0:表示未删除 Description_ 描述或备注 VARCHAR2(200) # 四、索引规范 复合索引的字段数不能超过5个。 单表的索引数量尽量控制在5个以内。 联合索引的字段排列顺序以去重后字段的数值的个数大小排序先后顺序。比如表mk_task有id,name,id有50000个独立值,name有5000个独立值,那么,顺序是id在name前面,建立的索引是idx_id_name。 Order by、distinct、group by后的字段尽量建立索引。 update、delete的where尽量使用有索引的字段或主键。 超过20字节的varchar字段建议用前缀索引,禁止对字符串长度超过50个字符的列创建索引。 不建议在低基数列上创建索引,例如“性别”列; 合理创建联合索引(避免冗余),(a,b,c) 相当于(a)、(a、b)、(a、b、c)。 长文本类型字段(例如Text)不能使用索引。 # 五、其他 1. 主键ID 建议使用分布式全局唯一递增ID,比如类snowflake算法,很多大公司都在用,有许多成熟的案例,而且百度、腾讯、美团都把自己的ID生成工具开源了。 禁止使用存储过程、视图、事件、触发器、数据库自带的分区表。 临时库、表名必须以”tmp_日期”为后缀,如当日创建多个,则在日期后增加数字后缀; 备份库、表必须以”bak_日期”为后缀,如当日创建多个,则在日期后增加数字后缀; ================================================ FILE: 正则/input框只能输入正整数.md ================================================ ### input框只能输入正整数 ```javascript 1.8 onKeypress="return(/[\d]/.test(String.fromCharCode(event.keyCode)))" ``` ================================================ FILE: 正则/文本保存换行 跟空格.md ================================================ this.value.replace(/\r\n/g, '
').replace(/\n/g, '
').replace(/\s/g, ' '),//保存空格 换行 ================================================ FILE: 温顾而知新/知识点集合.md ================================================ ### 一、页面导入样式时,使用link和@import有什么区别? #### 区别: 1. link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。 2. link引入的样式页面加载时同时加载,@import引入的样式需等页面加载完成后再加载。 3. link没有兼容性问题,@import不兼容ie5以下。 4. link可以通过js操作DOM动态引入样式表改变样式,而@import不可以。 ### 二、用递归算法实现,数组长度为5且元素的随机数在2-32间不重复的值 ```javascript 1.8 var arr = new Array(5); var num = randomNumber(); var i = 0; randomArr(arr,num); function randomArr(arr,num) { if (arr.indexOf(num)< 0){ arr[i] = num; i++; } else { num = randomNumber(); } if (i>=arr.length){ console.log(arr); return; }else{ randomArr(arr,num) } } function randomNumber() { return Math.floor(Math.random()*31 + 2) } ``` ### 三、页面加载后,表单的第一个文本框如何自动获得焦点? * 方法一 ```html ``` * 方法二 ```html document.getElementById('input').focus(); ``` ### 四、移动端点击300ms的延迟出现的原因是什么?你的解决方案是什么? 原因:早期IOS为了区分用户是双击缩放还是点击链接行为,于是就有了300ms延迟,其他浏览器就效仿了。 解决办法:1,引入fastclick,一了百了;2、在meta禁用浏览器缩放;3、touch事件模拟 ### 五、cookie都有哪些缺点? * 存储量小 * 明文传输,可篡改,不安全 * 同域的每个请求都会请求,耗费带宽 * 不能跨域 * 客户端没有getCookie/setCookie的api,存取复杂 ### 六、写一个方法找出一段话里面出现频率最多的词 * 方法一 ```javascript 1.8 const texts = 'I have a pen, I have an apple, Uh! apple pen. Pen pineapple apple pen.' const mostFrequentWord = Object.entries(texts.toLowerCase() .split(/[\s\,\.\!\?;]/) .filter(word => word) .reduce((sum, word) => ({ ...sum, [word]: (sum[word] || 0) + 1 }), {})) .sort((a, b) => b[1] - a[1])[0]; console.log(mostFrequentWord); // ["pen", 4] ``` * 方法二 ```javascript 1.8 const fn = (texts) => (',' + texts).split(/\W+/).reduce((acc, i) => ( i = i.toLowerCase(), acc[i] = (acc[i] || 0) + 1, acc.$.n < acc[i] ? (acc.$.w = i, acc.$.n = acc[i], acc) : acc ), { $: { w: '', n: -1 } }).$ fn('I have a pen, I have an apple, Uh! apple pen. Pen pineapple apple pen.') ``` ### 七、CSS3伪类:valid和:invalid实现表单校验 1. :valid 用于匹配输入值为合法的元素 2. :invalid 用于匹配输入值为非法的元素 3. required 属性规定必需在提交之前填写输入字段 4. pattern 属性规定用于验证输入字段的正则表达式 :valid/:invalid 选择器用于在表单元素中的值是合法/非法时设置指定样式。 > 注意: :valid/:invalid 选择器只作用于能指定区间值的元素,例如 input 元素中的 min 和 max 属性,及正确的 email 字段, 合法的数字字段等。 required 属性适用于以下 类型:text, search, url, telephone, email, password, date pickers, number, checkbox, radio 以及 file。当然textarea也可以。 #### :valid、:invalid示例 ```html ``` ### 九、监听网络状态的js ```javascript 1.8 window.addEventListener('offline',function(){ alert('offline now'); }); // 没有区别局域网 ``` ### 十、stopPropagation()和preventDefault()这两个方法有什么区别? > stopPropagation 是阻止事件冒泡,即冒泡事件到当前元素处就终止了,不会继续向上级元素传递。 > preventDefault 是阻止默认事件,例如:在 a 标签的 click 事件中执行了该方法,则不会进行默认的链接跳转 ### 十一、举例说明如何实现浏览器桌面通知? ```javascript 1.8 Notification.requestPermission(function (perm) { if (perm == "granted") { var notification = new Notification("这是一个通知撒:", { dir: "auto", lang: "hi", tag: "testTag", icon: "https://static.cnblogs.com/images/adminlogo.gif", body: "通知content" }); } }) ``` ### 十二、HTML5的requestAnimationFrame > 为什么要使用requestAnimation呢? > 由于屏幕种类,分辨率,屏幕尺寸的不同,屏幕自动刷新的频率不同,使用requestAnimationFrame可以自动适配屏幕刷新频率。避免丢帧。 > 与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。除此以外,还可以节省CPU,函数节流。 * 运用场景: 1. js 动画:requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销 2. 大数据渲染:在大数据渲染过程中,比如将后台返回的十万条记录插入到表格中,如果一次性在循环中生成 DOM 元素,会导致页面卡顿,用户体验差。这时候就可以用 requestAnimationFrame 进行分步渲染,确定最好的时间间隔,使得页面加载过程中很流畅。 ### NaN表示什么呢?typeof NaN结果是什么? * NaN : not a number * typeof NaN === 'number' ### 怎样对css文件进行压缩合并 * 使用在线网站进行压缩,如http://tool.lu/css * 如使用Gulp,可使用gulp-minify-css进行压缩 * 如使用WebPack,可使用optimize-css-assets-webpack-plugin进行压缩 ### promise的理解 * Promise是ES6中对回调的处理方案,用于处理回调过多,形成回调地狱,不直观的问题;Promise可以链式调用,代码直观易操作,并且有Promise.all, Promise.race等语法糖便于操作 ### js里如何将字符串转成正则表达式 ```javascript 1.8 var regStr = '/iOs/ig' var regExp = eval(regStr) regExp.test('dksiOssd') ``` * eval > eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。 ### 前端路由指的是什么?它有什么好处?它有哪些方式可以实现呢? 前端路由指的是什么? * 前端路由一般用在单页应用(SPA),是利用地址栏URL的变化,动态的替换页面中的DOM 它有什么好处? * 后端路由需要刷新页面,SPA模式下的前端路由不需要刷新页面 * 除了Ajax外,完全的本地加载,提高响应速度 * 前后端代码更好的解耦 它有哪些方式可以实现呢? * hash - 利用url HASH值的变化,监听hashchange事件,来替换DOM节点 - 支持老旧的IE浏览器 * History API - 监听popstate事件,利用pushStateAPI,同时进行DOM替换,实现前端路由 需要服务器的特殊配置(一般配置除了接口之外的get请求都指向index.html),不然会 请求到服务器定向的资源 ### 说说form-data、x-www-form-urlencoded、raw、binary的区别是什么? 同 * 发送请求的方式 异 * multipart/form-data 其请求内容格式为Content-Type: multipart/form-data,用来指定请求内容的数据编码格式,一般用来文件上传。 * application/x-www-form-urlencoded 是post的默认格式,使用js中URLencode转码方法。 * raw 可上传任意格式的文本,可以上传text、json、xml、html等各种文本类型。 * binary 等同于Content-Type:application/octet-stream,只可上传二进制数据。 ### 分别写出防抖和节流的两个函数,并描述它们分别有什么运用场景? * 节流:规定在一个单位时间内,只能触发一次函数。 * 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。 ```javascript 1.8 /** * 节流 */ let throttling = () => { let e = true return event = () => { if (e) { e = false setTimeout(function () { e = true, console.log(1) }, 5000) } } } /** * 防抖 */ let antiShake = () => { let e = true return event = () => { if (!e) { { clearTimeout(time) } } e = false time = setTimeout(function () { e = true, console.log(1) }, 5000) } } ``` ### 本地git与远程仓库连接的方式有哪些? * 第一种,本地新建文件夹 -> git init -> git add . -> git commit -m 'xxx' -> git remote add origin xxx -> git push -u origin master * 第二种,直接git clone xxx(ssh链接)-> git add . -> git commit -m 'xxx' -> git push ### 主流浏览器内核私有属性的css前缀 * Chrome:Blink内核 -webkit- * Safari:WebKit内核 -webkit- * Firefox :Gecko内核 -moz- * IE:Trident内核 -ms- * Opera:Presto内核 -o-