SegmentFault 前端那些事最新的文章
2019-03-11T18:30:35+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
那些很熟却又不是很熟的知识
https://segmentfault.com/a/1190000018463897
2019-03-11T18:30:35+08:00
2019-03-11T18:30:35+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
82
<p>本文为知识整理,可能工作中用到这些东西不多,可是总有人想让你会</p>
<hr>
<p><strong>前言:</strong>小时候很渴望长大,羡慕大人们的财富自由;长大后又很羡慕小时候,怀念小时候的无忧无虑,守候着那份天真。哦,还有,不是长大就能财富自由。。。</p>
<h3>一:js继承</h3>
<hr>
<p><strong>①:对象冒充实现继承:(可实现多继承)</strong></p>
<hr>
<p>——原理:让父类构造函数成为子类的方法,然后调用子类的方法,通过this关键字给所有属性和方法赋值</p>
<pre><code>function Parent(name)
{
this.name=name;
this.sayName=function () {
console.log(this.name);
}
}
function Child (cname) {
this.parent=Parent;
this.parent(cname);
delete this.parent; // 删除无用的parent函数 == f Parent(name)
}
var mychild=new Child("名字");
mychild.sayName();
</code></pre>
<p><strong>②:原型链继承 (不能实现多继承)</strong></p>
<p>prototype</p>
<hr>
<pre><code> function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
};
Parent.prototype.sayName = function () {
console.log(this)
};
function Child (cname, cage, height) {
this.height = height
}
Child.prototype = new Parent(); // Child原型指向Parent的一个实例
Child.prototype.constructor = Child; // 把Child指向自己,不与Parent共享
var child = new Child('测试名字', '测试年龄', '测试身高')
console.log(child) // 发现打印出来的属性都继承了,就是没值</code></pre>
<p>child.ParFunc():当访问ParFunc属性时,会先在child的实例属性中寻找,找不到就去child的原型对象上去找。一层一层的寻找构成了<strong>原型链</strong><br><img src="/img/bVbpeJF?w=692&h=302" alt="clipboard.png" title="clipboard.png"><br>因为无法向父类构造函数传参;可以 new Parent('名字', '年龄')这时传参<br>注:如果想用原型给Child拓展方法:</p>
<pre><code>Child.prototype.childHeight = function () {
console.log(this.height)
}</code></pre>
<p>一定要写到Child.prototype = new Parent()的下面,要么就被覆盖掉了。</p>
<p><strong>③:call、apply继承(不能继承原型链,prototype上的)</strong></p>
<hr>
<pre><code> function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.name)
}
}
function Child (cname, cage, height) {
Parent.call(this,cname,cage); // 继承的参数为当前函数的形参
// apply: Parent.call(this,arguments);
this.height = height;
}
var child = new Child('测试名字', '测试年龄', '测试身高')
console.log(child) // ParFunc: ƒ () age: "测试年龄"
name: "测试名字"</code></pre>
<p><strong>④:组合继承:call+原型链继承</strong></p>
<hr>
<pre><code>function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
}
Parent.prototype.sayName = function () {
console.log(this)
}
function Child (cname, cage, height) {
Parent.call(this, cname, cage); // 解决传参问题
this.height = height;
};
Child.prototype = new Parent()
Child.prototype.constructor = Child; // 把Child指向自己,否则一直指向Parent
var child = new Child('测试名字', '测试年龄', '测试身高')</code></pre>
<p>比较常用的继承方式,缺点是 两次调用 Parent构造函数</p>
<p><strong>⑤:寄生组合继承:</strong></p>
<p>cal+prototype</p>
<hr>
<pre><code>function Parent (name, age) { // 父函数
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
}
Parent.prototype.sayName = function () {
console.log(this)
}
function Child (cname, cage, height) { // 子函数
Parent.call(this, cname, cage)
this.height = height;
};
var createObj = function () { // 中间函数继承 Parent
function Trans() {};
Trans.prototype = Parent.prototype;
return new Trans();
};
Child.prototype = createObj()
Child.prototype.constructor = Child; // 改回指针
var child = new Child('名字', '年龄', '身高')
console.log(child)
</code></pre>
<h2>二:如何获取自定义属性,特例data-*如何获取</h2>
<p>官方定义:</p>
<pre><code> data-*是 **html5** 新属性
主要用于存储页面的自定义数据
不应该包含大写字母(会默认转为小写)
注释:用户代理会完全忽略前缀为 "data-" 的自定义属性。</code></pre>
<p>脑海里第一印象就是 getAttribute(),setAttribute()俩属性了,一个获取,一个设置<br>而平时又很少用到,但是平时用的框架什么的多数都用 data-* 这个自定义属性,那其实获取 data- 这类自定义属性的时候,还有个更方便的方法dataset</p>
<pre><code><div data-a="aa" id="div1" data-b="bb"></div>
eg:var div1 = document.getElementById('div1')
console.log(div1.dataset) // DOMStringMap {a: "测试", b: "222"}a: "测试"b: "222"</code></pre>
<p>用data-*作为自定义属性:可以一句话就获取到所有属性,获取方式也简便</p>
<h3>三:事件的几个阶段:捕获、目标(event.target,即触发过程)、冒泡</h3>
<p>先盗个图</p>
<p><img src="/img/bVbpfJ2?w=1028&h=716" alt="clipboard.png" title="clipboard.png"><br>——冒泡(IE事件流):从最深的节点向外传播,div -> window,就好比往水里丢一个石子,一层层波浪抵达河边缘</p>
<p>——捕获(网景):从最外层向目标传递,window -> div,就好比你去一个大企业集团找人,需要提供公司 > 大部门 > 小部门 > 小组 > 目标</p>
<p>——目标:即触发过程event.target</p>
<p>——target、currentTarget的区别:target这个属性指向的是目标过程中的DOM对象,也就是 <strong><em>触发事件监听的对象</em></strong>, currentTarget这个指向的是当前的对象,具体内容跟this一样,当this指向的是目标的时候(事件的目标阶段),target与currentTarget相同</p>
<p>——现在几乎所有的主流浏览器都支持冒泡;IE早起版本会跳过html直接跳到document,且不支持捕获。</p>
<p>——平时多数用到冒泡多一些,事件代理(委托)也是通过事件冒泡的原理,让DOM节点可追溯,也可以利用冒泡的原理做埋点,避免新增DOM节点,改代码上线</p>
<p>——事件句柄addEventListener('事件名', 'function', false),默认冒泡</p>
<h3>四:判断数据类型,返回数据的具体类型</h3>
<p>emm... 那不就直接 <code>return typeof n</code> 不就完了,哦不对,再识别一下数组,因为数组的typeof也是对象,Array.isArray(n)... <br>/^12/ 这返回啥? wc,也是object吧,那咋区分,对,正则有test方法,再判断一下</p>
<pre><code>if (n.test) {
return 'RegExp'
}</code></pre>
<p>null好像也返回obj吧,时间 Date嘞,都返回obj也没毛病,万物皆对象啊。</p>
<ul><li>据说instanceof也可以:左侧是否是右侧的实例,也就是说每个类型我们都得判断,于是</li></ul>
<pre><code>[] instanceof Array // true
[] instanceof Object // true</code></pre>
<p>不光麻烦,返回的也不精确啊</p>
<ul><li>据说constructor也可以:js引擎会为函数添加prototype,并让其指向'该函数'的引用</li></ul>
<p>/^12/.constructor // f RexExp(){[native code]}<br>new Date().constructor // ƒ Date() { [native code] }<br>null.constructor // 报错:Cannot read.. <br>undefined.constructor // 报错:Cannot read.. <br>发现确实能校验一些typeof 不能校验的,但是 null和undefined没有'指针'啊,而且写继承的时候,指针是可以被改的,稍不注意就凉凉了...</p>
<ul><li>把这些都整合到一起基本也都够用了,可是并不优雅</li></ul>
<p>有请toString登场....<br>华丽分割线</p>
<hr>
<p>toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。完美~~</p>
<pre><code>Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]</code></pre>
<p>但是我觉得除了obj比较特殊之外,其他类型,typeof都可以判断,不需要再多调用一次toString方法,所以最终封装 =></p>
<pre><code>function typeDetection (n) {
if (typeof n === 'object') {
return Object.prototype.toString.call(n)
} else {
return typeof n
}
}</code></pre>
<p>直接调用typeDetection('') // string</p>
<h3>五:千分位的实现</h3>
<p><strong>Q:字符:1234567890.12 转换为:1,234,567,890.12</strong></p>
<p><strong>R:</strong><br>个人用了while循环的方式</p>
<pre><code>function strFilter (str) {
let newStr = String(str).split('.') // 切分原始字符,兼容传入Number类型
let transStr = newStr[0],resStr = []
while (transStr/1000 > 1) { // 判断是否大于1000
let whildStr = String((transStr/1000).toFixed(3)).split('.') // 这里一定要保留三位小数,否则正数部分末位的0就会丢失,又转为String,因为Number没有split方法
transStr = whildStr[0] // 每次都取小数点以前的(正数部分)
resStr.unshift(whildStr[1]) // 向前插入小数点后的数字()
}
// 除以1000剩下的数 + 每次除以1000后的数 + 原来小数
let res2 = newStr[1]?('.' + newStr[1]):''
let resComma = resStr.length?(',' + resStr.join(',')): ''
return transStr + resComma + res2
}</code></pre>
<p>虽然实现代码很多,但个人觉得还算易懂</p>
<p>网上看到用正则的,确实简短:</p>
<pre><code>function strFilter2 (n) {
let s = String(n)
let re = /\d{1,3}(?=(\d{3})+$)/g
let n1 = s.replace(/^(\d+)((\.\d+)?)$/, function (s, s1, s2) { return s1.replace(re, '$&,') + s2 })
return n1
}</code></pre>
<p>其实正则在好多场景都体现出优势,只是不能被轻易想到</p>
<p><strong>Q:以下this指向</strong></p>
<pre><code>(function () {
"use strict";
console.log(this) // undefined
})()
(function () {
"use strict";
console.log(this) // window
})()</code></pre>
<p><strong>R:严格模式下,除构造函数、对象内置函数外,this均指向 undefined</strong></p>
<p><strong>Q:script标签的 async、defer啥区别?</strong><br><strong>R:别说那没用的,上图</strong></p>
<p><img src="/img/bV97Nc?w=787&h=800" alt="clipboard.png" title="clipboard.png"><br>啥也不加:script读取和解析脚本阶段都会阻断页面执行,<br>加async : script解析脚本阶段会阻断页面执行<br>加defer : scriptj脚本将在页面完成解析时执行</p>
<p>Q:[1,2,3].map(parseInt)的结果?<br>R:之前用到parseInt,只知道是向下取整,翻了下w3c的parseInt定义: Crazy</p>
<p><img src="/img/bVbpCI2?w=1622&h=332" alt="clipboard.png" title="clipboard.png"><br>再看map,</p>
<p><img src="/img/bVbpCLW?w=1400&h=618" alt="clipboard.png" title="clipboard.png"><br>parseInt就是回调函数,map会传给parseInt三个参数,parseInt只识别前两个,<br>那也就是得到了</p>
<pre><code>function parseInt1 (item, index) {
return parseInt(item,index)
}</code></pre>
<p>得到 <code>parseInt(1,0) parseInt(2,1) parseInt(3,2)</code></p>
<pre><code>parseInt(1,0),parseInt定义radix不传或者0,按10进制,也就得到了1
parseInt(2,1),parseInt又定义第二个参数 radix 位于 2-36(除了0),否则返回NaN,所以得到NaN
parseInt(3,2),这个据说(网上)是 3不是 2的合法进制数 (只有0和1),但是个人试了试
parseInt(10,2) => 3,parseInt(20, 2) => 6,parseInt(30, 2) => NaN
,发现只要是字符首字符小于 radix都是可以的,但是一旦首字符 >= radix,就会返回NaN
</code></pre>
<p>参考文章:<a href="https://link.segmentfault.com/?enc=JqSvNLYdidpIRNgukV2EiQ%3D%3D.T1Gg5KgO0ysgWQaQuETVJLBZITY0Qf9Hnvjv9W8tLWR7c6rSNGDgUU56wIDd9AUF" rel="nofollow">判断js数据类型的四种方法</a></p>
完成需求很重要,优化好更重要
https://segmentfault.com/a/1190000016566169
2018-09-29T18:57:30+08:00
2018-09-29T18:57:30+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
37
<p><strong><em>总喜欢在前面啰嗦一些...</em></strong><br>如题,在日常紧急开发中,完成需求固然排在前面(毕竟产品会追着你打?),但是需求完成之后还是很有必要去优化我们的项目的!<br>花一两个月去做了一个项目,<strong>需求评审、原型评审、开发排期、测试用例评审、设计图评审</strong>(感觉不断的在开会)、<strong>开发、联调、提测、上线</strong>...大功告成,告辞!<br>当然这期间的流程每个公司都会不同,可能还会有需求变更什么的...<br>我们今天只谈上线之后...<br>上线之后,各种bug如期而至,正如你所料,需求所涉及的功能都做了,为什么还遭到用户投诉,这里不好那里又有问题?<br>对于用户而言,你的<strong>首页加载速度</strong>,很考验他(她)的耐心,so,首页加载优化得做<br>最简单易见的就是图片了,<strong>关于图片的优化:</strong></p>
<h2>图片优化(一)——大图片的优化</h2>
<p><a>压缩上传</a>,已附链接,还蛮好用的,去各大网站看一下,首页总共的资源<br>x宝<br><img src="/img/bVbhFcP?w=1113&h=635" alt="clipboard.png" title="clipboard.png"></p>
<p>x东<br><img src="/img/bVbhFc7?w=1133&h=628" alt="clipboard.png" title="clipboard.png"></p>
<p>可以看到,电商类的首页才只有不到2M的图片,还有你会发现,图片懒加载和预加载也起了至关重要的作用,预加载就是在网页全部加载之前提前加载图片。当用户需要查看时可直接从本地缓存中渲染以提供给用户更好的体验减少等待的时间,懒加载就是滑到可视区再加载(或满足条件再加载)</p>
<h2>图片优化(二)——小图片的优化(icon)</h2>
<blockquote>精灵图 (请css喝雪碧)</blockquote>
<p>老生常谈了,就是将很多的小图标集合到一个图片中去,目的是减少资源请求(网页的缓存机制是会略去本地已经有的资源),这个让设计师把一些简单的小图标集成到一张图给到前端就好了,前端设置</p>
<pre><code> background-image: url('../../img/jlt.png');
background-size: 4.34rem;//根据大图算出来的
background-position: 0.2rem 0.3rem;//位置拿photoshop量或者手动试也可以(?)
</code></pre>
<p>还有一些小icon,要会变色(不是根据眼睛变化那个..),根据网站主题色去变(或者就是迭代需求只是变色,图标不变),那再让设计师把带颜色的加到精灵图中,相信你俩都不爽,这时候可以考虑用</p>
<blockquote>svg</blockquote>
<hr>
<p>svg明显能满足变色需求,改fill属性就好了,不管你是主题色还是需求变更,而且svg是矢量级的图标,占位也很小,你要是变换3个颜色,精灵图里面就得给你三个不同颜色图标,所以从空间上也有优势</p>
<blockquote>@font-face</blockquote>
<p>还有一个<a href="https://link.segmentfault.com/?enc=2KVxD3bPutiXd64VSFr4ag%3D%3D.KjyFlHNOsmlLB7e6BerFCIM9S6g09ymtFrE02aHR2HoBlGLTqUuUrxBMRSQACL2q" rel="nofollow">比较棒的网站</a>,阿里矢量图标库,里面存放了很多小的icon,格式支持也很多,jpg、png、svg等等<br>让设计师建个项目,把对应的小图标上传到这里,然后会生成一个unicode,就是font-face的线上地址,复制到项目里就好<br>用法和本地下载好字体库是一样的,</p>
<pre><code>@font-face {
font-family: 'icon';
src: url('https://。。。.eot');
}
.icon{
font-family:"icon" !important;
font-style:normal;
}</code></pre>
<p>具体页面使用加上icon这个类名,改颜色,就是直接改color,也是灰常的好用</p>
<h2>静态资源的抽取</h2>
<p>项目中依赖的一些json、css、js等都抽取到cdn<br>浏览器是根据域(Domain)来缓存内容资源的,只要域不一样,即使是同一个资源,也需要重复下载,且使用同样的方式缓存起来,这需要需要占用带宽和本地缓存空间,<a href="https://link.segmentfault.com/?enc=BL4zimuoalx5qeV00UB3Lw%3D%3D.QoMDt1pTCKfZPXqO89nngtKdktu60Fvn5l4O%2B6AwW1PG%2BGgUdh1WMTou9mHbmsp%2FQERJND7RHQJ7ubYZMTTo%2FoRtwUJk%2B00PpIHWGOxhbgflYvCqu0W%2F26Ue89CawiAs" rel="nofollow">cdn详解</a></p>
<h2>路由懒加载(vue+webpack)</h2>
<p>前提是vue+webpack<br>都知道,如果不做,在首页会加载所有的页面资源,于是出了个解决方案:到哪个页面再去加载对应的资源<br>写法一:<code>const abc = r => require.ensure([], () => r(require('../pages/acb/abc.vue')), 'abc');</code><br>写法二:</p>
<pre><code>{
path: 'abc',
component: () => import('@/pages/abc/abc.vue'),
meta: {
title: 'abc'
}
},
</code></pre>
<p>把网速调慢,会发现,第一种写法会有很长的白屏时间(页面title变了,页面未变),第二种快了一些,而且第二种写法也更简洁</p>
<h2>公有组件抽取</h2>
<p>开发中,可能有点小摩擦,‘你的组件比较偏向于你,我要自己从新写一个’<br>组件的设计一定要满足大众口味,这样才能节省一些空间出来<br>比如load、弹框等等,主体结构复用,style在个人页面调整</p>
<p>还有一些大而广的东西:<br>减少请求(只能说尽量,业务需要该少的少不了)<br>减少DOM操作(现在都是虚拟DOM,还在讲DOM操作...)<br>避免使用CSS表达式(很少有人写吧,尽量用rem替换calc吧)<br>函数防抖和函数节流(小红书函数部分明确有些,load也可以替代功能)<br>首屏服务端渲染(还不错,接口直接返回page)<br>...</p>
<h2>总结:</h2>
<p>只有真正的在完成需求后,才会遇到种种问题,才能想到去优化哪些东西,所以实践真的是检验真理的唯一标准<br>有时间新建个demo实际操作一波还是很不错的选择,毕竟听别人叨叨似懂非懂的,换成自己的东西还能吹上一番</p>
js知识整理
https://segmentfault.com/a/1190000015650356
2018-07-16T16:35:05+08:00
2018-07-16T16:35:05+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
10
<blockquote>Jquery再熟悉不过,其中的$.ajax封装也不陌生,其中</blockquote>
<hr>
<pre><code>$.ajax({
url:url,
type:"GET",
data:data,
success:function(res){
},
error:function(err){
}
});</code></pre>
<p>以前比较喜欢success固定回调这种写法,很清晰<br><strong>接下来实现success和error这种语法</strong></p>
<pre><code>var $={
ajax(call){
if(call.bol){
call.success("成功的回调");
}else{
console.error("bol is undefined");//提示
}
}
}</code></pre>
<p>调用:</p>
<pre><code>$.ajax({
bol:false,//切换bol存在即可调用成功回调
success:function(res){
console.log(res);
},
error:function(err){
console.log(err);
}
});
</code></pre>
<blockquote><strong>类似物流信息,后台返回一个字符串,前端识别出连续的11位数字,并且可拨打电话</strong></blockquote>
<hr>
<p>做这里的时候,想要跟后台多要一个字段为电话,前端再a标签直接包起来。但是后台接的也是三方,三方返回就是一个整串......<br>后来仔细想想这个流程,即使后台多给一个字段,这需求一样做不完美<br>比如这个串是这样的:</p>
<pre><code>'您的快递正在配送,配送员电话:17600699305,某某快递持续为您服务'</code></pre>
<p>后台分了俩字段<br>'<code>str1=您的快递正在配送,配送员电话:,某某快递持续为您服务' str2='17600699305'</code><br>这个str2插到str1的哪里呢?显然也不好弄。<br>那还不如不麻烦后台,前端自己消化。<br>'识别连续的11位数字',第一想到的就是正则:</p>
<pre><code>var str='您的快递正在配送,配送员电话:17600699305,某某快递持续为您服务',strCon=str.match(/\d{11}/);</code></pre>
<p><a href="https://link.segmentfault.com/?enc=kSv3wblXItuCVGloX7yKJA%3D%3D.DDKkofd9iem0k%2FVlJT9%2B%2FEVXCllhN9%2BqI%2FxS92z46B6vMYgthTSkPgFXK5qdF3tw5em3%2Fe%2FiEMeVPmxKjGp9UA%3D%3D" rel="nofollow">match</a>符合条件会返回一个数组,包含 匹配到的内容,内容的开始下标,整个内容,还有个groups(求释义)<br>接下来封装实现:</p>
<pre><code>function continuFunc(str){
var strBol=str.match(/\d{11}/);
if(strBol){
return strBol.input.slice(0,strBol.index)+'<a href="tel:'+strBol[0]+'">'+strBol[0]+'</a>'+strBol.input.slice(strBol.index+strBol[0].length,str.length);
}
}</code></pre>
<p>就是分成三个部分返回,以匹配到的11位连续数字为节点,前后各为两部分,再进行拼接。<br>index就是匹配区的开始下标,所以第一部分从0取到下标,第二部分已经返回,第三部分取index+字符长度(11)——结尾</p>
<blockquote><strong>toFixed()方法如何实现(保留n位小数)</strong></blockquote>
<hr>
<p>刚开始看到是不是很慌,这方法调用的时候都是Number.toFixed(),而常规的函数封装都是</p>
<pre><code>function Func(argu){
return argu;
}
</code></pre>
<p>调用都是<code>Func('调用');</code>,很慌。<br>这种函数基本都是绑定在原型上的,不难发现,调用<code>toFixed</code>方法的都是Number类型,所以<code>toFixed</code>方法应该就是绑定在Number的prototype了,接下来分析内部实现问题</p>
<pre><code>Number.prototype.toDiyFixed=function(n){
return (Math.round(this * Math.pow( 10, n ))/ Math.pow( 10, n)).toString();
}</code></pre>
<p><strong>调用</strong>:<code>var num=100.123, a.toDiyFixed(2)</code>得到100.12。<br><code>toDiyFixed</code>方法就成功的copy了<code>toFixed</code>方法。<br>就<code>a=100.123</code>这个例子讲实现原理:把<code>原数 num</code>先乘以100(<strong>n是多少就乘以10的几次方</strong>),再进行四舍五入,(<strong>去除小数两位之后的小数</strong>,因为乘以100以后,<strong>前两位小数已经变为整数部分,四舍五入操作就不会影响了</strong>),至此完成保留2(n)位小数的操作。<br><strong>注意</strong>:但是,这个方法有个问题,就是不会自动向末尾<code>补0</code>,比如说<code>100.196</code>保留两位小数应该得到<code>100.20</code>,但是该方法只能拿到<code>100.2</code>,<code>100.201</code>应该得到<code>100.20</code>,但该方法得到<code>100.2</code>,目前还没想到解决办法,欢迎一起交流探讨,但是基础功能可以实现.<br>另外说个原生<code>toFixed</code>的<code>bug</code>:<br><code>0.7.toFixed(0)</code>发现得到的是<code>1(IE)</code><br><code>0.035</code>和<code>0.045</code>保留两位小数后得到的都是<code>0.04(Chrom)</code>,当然如果觉得这点误差无所谓,<code>10.35</code>、<code>10.45</code>保留两位小数得到<code>10.3</code>、<code>10.4</code>发现都是错的,这是不容小觑的数字了吧,后来查阅资料说,<code>js</code>的<code>toFixed</code>方法用的是<strong>”银行家算法“</strong>,实质就是 ”四舍六入无成双“,那啥意思呢,<strong>当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位。</strong><br>按照这个说法,在Chrom还是行不通的,10.35应该得到10.4,10.45得到10.4没问题,有些摸不到边了,所以自定义一下<code>toDiyFixed</code>这个方法以便备用</p>
<h2>总结:</h2>
<p>javaScript日益强大,各种语言逐步向它靠近。(✌️) 开发中一般时间很紧迫,好多东西都没工夫想,所以还有很多要优化的地方,有时候,放慢脚步,可能也是一种进步吧!</p>
vue+mint-ui地址三级or四级联动
https://segmentfault.com/a/1190000013718705
2018-03-14T11:00:00+08:00
2018-03-14T11:00:00+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
2
<blockquote>之前写了一篇前后端一起搞的地址选择。但是觉得每次滑动的时候都调用好几次接口很浪费,所以决定前端自己搞吧。</blockquote>
<hr>
<p>首先需要一份地址源啊,后端一次性返给我们或者本地存一份(在static下)</p>
<blockquote>
<p><template> <br><div class="addressFour"><br> <mt-picker :slots="addressSlots" @change="onAddressChange" :visible-item-count="5"</p>
<pre><code> v-show="valSelect==1"></mt-picker></code></pre>
<p><mt-picker :slots="streetSlots" ref="picker" @change="onStreetChange"</p>
<pre><code> :visible-item-count="5" v-show="valSelect==2" ></mt-picker></code></pre>
<p></template> <br> <script> </p>
<p>import address from<br>'../static/addressFour.json'; <br> export default{</p>
<pre><code>name: 'addressFour',
props: ['valSelect'],
data () {
return {
popupVisible: false,//省市区三级
popupVisible2: false,//街道
streetRender:true,//街道是否展示
addressSlots: [
{
flex: 1,
defaultIndex: 1,
values: Object.keys(address),
className: 'slot1',
textAlign: 'center'
}, {
divider: true,
content: '-',
className: 'slot2'
}, {
flex: 1,
values: [],
className: 'slot3',
textAlign: 'center'
}, {
divider: true,
content: '-',
className: 'slot4'
}, {
flex: 1,
values: [],
className: 'slot5',
textAlign: 'center'
}
],
streetSlots: [
{
flex: 1,
values: [],
className: 'slot1',
textAlign: 'center'
}
],
addressProvince: '省',
addressCity: '市',
county: '区',
addressStreet: '街道',
}
},
methods: {
onAddressChange(picker, values) {
let shi = Object.keys(address[values[0]]);
let index = shi.indexOf(values[1])
let xian = address[values[0]][shi[index]];
this.xianObj = xian;
picker.setSlotValues(1, shi);
this.addressProvince = values[0];
this.addressCity = values[1];
if (values[2]) {//有时为undefined
this.county = values[2];
}
this.$emit('addressProvince', values[0]);
this.$emit('addressCity', values[1]);
this.$emit('county', values[2]);
let address_full = picker.getValues();
if (address_full[address_full.length - 1] != undefined) {//会出现四组值,一组有效
this.$emit('addressFull', picker.getValues());
}
if (xian) {//xian报undefined,报错
picker.setSlotValues(2, Object.keys(xian));
}
},
onStreetChange(picker, values){
this.addressStreet = values[0];
this.$emit('addressStreet', values[0]);
},
},
watch: {
'county': {
handler(val, oval){
let street = this.xianObj[val];
if(street.length===0){
this.streetRender=false;
this.$emit('noPopup',false);
return;
}
this.streetRender=true;
this.streetSlots[0].values = street;
},
}
},
mounted(){
this.$nextTick(() => {
setTimeout(() => {
this.addressSlots[0].defaultIndex = 0;
}, 100);
});
} </code></pre>
<p>} <br></script></p>
</blockquote>
<hr>
<p><strong>一些参数说明:</strong></p>
<ul>
<li>addressFour.json:这个是四级地址库,网上随意搞一个比较全的就好。</li>
<li>valSelect这个参数控制三级地址还是街道展示</li>
<li>noPopup:是否有四级(直辖市有的只有三级)</li>
<li>Object.keys这个方法w3c也有,可以去看看</li>
</ul>
<p>至此,地址三级和四级就都可以用了,三级只需valSelect置为1即可</p>
<p>父组件引用很简单,每个人使用方式也不同,就不列举了<br>本文借鉴:<a href="https://link.segmentfault.com/?enc=S9tXT%2FNY2w155XxnVIbawg%3D%3D.6Zk%2BLP%2BmkzIHDWIYO0%2FvsUMQNxcSgMBvXp3igHGuIGpt7AlqK8nWXxy%2FotQ93mnX6sr6d0tlIW2FT%2FN%2F%2FajUaQ%3D%3D" rel="nofollow">https://www.cnblogs.com/cools...</a></p>
vue使用小结
https://segmentfault.com/a/1190000013645704
2018-03-10T17:05:09+08:00
2018-03-10T17:05:09+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
4
<h5>安装,webpack基本配置等基础官网够详细了,<a href="https://link.segmentfault.com/?enc=8I7Ufva5Z1pT7lzkxE21iA%3D%3D.ODxFPGz8CCghWosFeqskLM6wPyj46OGuTmNoPBqXlvcsgwp0kxtNadmiyb%2Bnju%2Bg" rel="nofollow">略</a>
</h5>
<h2>1.路由:</h2>
<p>——路由懒加载(按需加载):<br><code>const Home = r => require.ensure([], () => r(require('../pages/home/home.vue')), 'Home');</code></p>
<p><a href="https://link.segmentfault.com/?enc=20BnGF%2F2FIpZLjjSOvQ9Pw%3D%3D.O0MHOD0kfZO6Bbd6cnw1pU0jLKSKqA67sAHAdVGB2vUsVEjttdY062w10DZV4Q%2F6" rel="nofollow">推荐博文</a></p>
<p>好处就是到哪个页面加载对应的路由,节省资源,提高访问速度。</p>
<hr>
<p>——路由跳转:<br>router-link:面试时问过好多这个问题,组件和router-link上面想写点击事件怎么写,大多数都回答:直接写啊... 正确姿势:<code>@click.native="func"</code><br>刚用vue的时候,不太习惯所有东西都尽量使用它的语法。有一次有个页面需要从哪来,保存的时候还返回到哪里去。当时第一时间想的就是</p>
<pre><code>location.back();location.go(-1);</code></pre>
<p>幸好当时,一个实习的产品发现了bug。在IOS手机上面,列表页a编辑进入b,b页面点击保存后返回到列表页a,列表页a的数据并不更新,安卓都正常。百思不得姐,度娘了一波。使用了vue的</p>
<pre><code>this.$router.go(-1);this.$router.back();</code></pre>
<p>就OK了。<em>不清楚原因</em></p>
<hr>
<p>——监听来源路径及去向路径<br>用到两个钩子:<code>beforeRouteEnter</code>的<code>from.path</code>和<code>beforeRouteLeave</code>的<code>to.path</code><br> 场景:产品需求:下单时如果点击返回就<code>popup</code>提示,提示用户再想想,还是确定离开。此时<code>beforeRouteLeave</code>判断只要<code>to.path</code>不是下行页面就阻断,参数<code>next(false)</code>即可阻断。<br> ——有个问题就是,IOS的侧滑会有延迟,当在下单页面侧滑返回的时候,会先返回上个页面,延迟一会之后再回来。目前没发现可行办法(<strong>移动端touch事件禁用过,与侧滑是两码事,不在一个层级</strong>)。<br> ——京东、天猫的是,下单及下行页面,禁用侧滑,其他页面正常使用(毕竟我们不是app,禁用不了)<br> ——当然,这些都是为了用户体验。如果有合适办法,可讨论。<br> ——监听来源:刚刚说了<code>beforeRouteEnter</code>的<code>from.path</code>,但是外部链接直接访问vue项目,拿到的<code>from.path</code>是 “/”,<br> ——如果有很多渠道访问我们项目,就无法区分了。<br> ——解决办法:<br> 1.进入我们项目必须携带来源参数,每个渠道都必须传递不同参数。<br> 2.当然,如果公司本来就招商,别人又不愿意配合,那么不必太强求。JS提供了一个方法,<br><code>document.referrer</code>获取来源路径,前提是别人进入我们项目是通过,<code>href</code>属性进来的。比如a标签,<code>location.href</code></p>
<h2>2.webpack打包:</h2>
<p>查看各个模块的打包后的占用空间:<br>运行:</p>
<pre><code>npm run build -report
</code></pre>
<p>执行完之后会自动弹出模块占用空间页面,打包前及打包后的。发现相同模块多次渲染,那就去重咯.最常见的公共组件,多人引用,组件内部又引用其他资源,这时把此公共组件 按需加载即可。</p>
<h2>3.keep-alive缓存模式</h2>
<p>初次使用:解决列表页返回记录位置。搭配钩子:activated<br><a href="https://segmentfault.com/q/1010000010535368">之前讨论过这个</a></p>
<p>一些坑的补充说明:<br>产品设计要求:前进刷新数据,返回记录位置(刷新与否都可),那么在<br>beforeRouteLeave钩子里面要进行一些判断,判断需要记录位置的页面b的入口有哪些。</p>
<pre><code>
beforeRouteLeave(to, from, next){
this.isOk = false;
if (to.path == "/a" || to.path == "/c") {
this.isOk = true;
}
next();//这个一定要写
}</code></pre>
<p>activated钩子需要做的是把所有的数据初始化(跟第一次进入页面一样):</p>
<pre><code>activated(){
if (this.isOk) {
this.dataList = [];
this.pageNum = 1;
}
}</code></pre>
<p>原因就是该模式下,<code>created</code>,<code>mounted</code>只触发一次,数据都在缓存中,如果不手动清空,数据不会更新,用了<code>activated</code>无需这俩货了</p>
<h2>4.能用<code>computed</code>替代<code>watch</code>的,尽量都使用<code>computed</code>
</h2>
<p><a href="https://link.segmentfault.com/?enc=vpQlZSPjr%2FwXPtZ7CBEJ0w%3D%3D.dKBE9Caq%2BNaNzn%2FMae4hQbGsadg6wnxGH6SRgpIWZ17zvLtOpsQzO2VCQay3Mrd%2BAjJn4MYanRrUL6WqAnXFZWHe1tYYESs8LslvUp%2BZc20%3D" rel="nofollow">官网</a>也有说明</p>
<p>是尽量,不是一定。性能和代码方便computed还是挺有优势的.<br>场景:<br>购物车,价格计算(一般后端算,淘宝无网络前端算好,优惠在订单),<br>页面多个输入框,当所有的输入框输入符合条件,提交按钮高亮可点击</p>
<h2>5.某些场景下<code>$nextTick</code>代替<code>setTimeout</code>延时器</h2>
<p>释义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 <br>当然这个某些场景指的是修改数据后,DOM没有及时更新。 <br>受js影响很深啊,老是想用<code>setTimeout</code><br>顺便说下一下,有时候修改数组或者对象的数据,页面没有及时更新。建议使用this.$set方法,eg:<code>this.$set(data,name,'zhangsan')</code>;</p>
<h2>6.<code>@import</code>引入less文件重复渲染</h2>
<p>算是优化的内容了<br>解决办法:@import (reference) 'main.less';<br><a href="https://link.segmentfault.com/?enc=vtoJ7N%2FSH6X7wa9siJ%2FB7Q%3D%3D.HtHCnkaLDRReEGYEd3b7icOPFo6N0JzDtyO8AnyqZOE%2FUrs0%2FDOjIhXJQVj6gTN0p9kWunHk80R98Ughpnt1tw%3D%3D" rel="nofollow">less传送门</a></p>
<h2>7.安利一个三级联动,mint-ui的,下篇文章会单独讲</h2>
<p>之前<a href="https://segmentfault.com/a/1190000011307481">文章</a>数据从后端获取,下次使用的是纯前端本地或cdn一段json</p>
<h2>8.客户端的搜索</h2>
<p>——IOS键盘的 ‘换行’ 改为 ‘搜索’</p>
<pre><code><form action="javascript:void(0);">
<input type="search" placeholder="搜索">
</form>
</code></pre>
<h2>9.mint-ui的<code>popup</code>弹起,页面依旧可滑</h2>
<p>监听<code>popupVisible</code>,当弹起的时候,让整个页面外层设置</p>
<pre><code>document.getElementById('a').style.height="100%";
document.getElementById('a').style.overflow="hidden";
</code></pre>
<p>隐藏的时候:</p>
<pre><code>document.getElementById('a').style.height="";
document.getElementById('a').style.overflow="";
</code></pre>
<h2>10.键盘弹起时,IOS端页面多余空白</h2>
<p>原本不可滑动的页面,键盘弹起时,页面可滑了,底部还多了很多空白,这产品可忍不了啊<br>解决办法:当键盘弹起(获取焦点)的时候,绑定滚动事件<code>addEventListener</code>(延迟1s),该滚动事件做的是当滑动时就让对应的input触发blur(),也就是键盘隐藏<br>获取焦点的时候,把Index传递下来.方法很愚...</p>
<h2>总结</h2>
<p>不同想法,一起交流。<br>——不抱怨,每天更努力一点</p>
vue 上传图片到阿里云(前端直传:不推荐)
https://segmentfault.com/a/1190000013153127
2018-02-05T21:09:39+08:00
2018-02-05T21:09:39+08:00
蓝天海礁
https://segmentfault.com/u/lantianhaijiao
3
<p>为何要这样做:减轻后端数据库压力(个人觉得于前端没啥用,谁返回来都行)</p>
<hr>
<p>代码部分:</p>
<pre><code> <template>
<div class="upLoad">
<div class="file">上传图片
<input type="file" :accept="typeArr" @change="upload($event)">
</div>
</div>
</template>
<style lang="less" scoped>
.file {
position: relative;
left: .26rem;
top: .2rem;
display: inline-block;
background: #32d582;
border: 1px solid #99D3F5;
border-radius: 4px;
padding: 4px 12px;
overflow: hidden;
color: white;
text-decoration: none;
text-indent: 0;
line-height: 20px;
}
.file input {
position: absolute;
font-size: 100px;
right: 0;
top: 0;
opacity: 0;
}
</style>
<script>
export default{
props: ['typeArr', 'size'],
data(){
return {
client: '',
fileSize: 5000000
}
},
methods: {
getRight(){
if (this.size) {
this.fileSize = this.size;
}
this.client = new OSS.Wrapper({
region: "同endpoint",
secure: true,//https
endpoint: '运维会告诉你',
accessKeyId: "id和secret去阿里云控制台获得",
/*accessKeyId,accessKeySecret两者到阿里云控制台获得*/
accessKeySecret: "",
bucket: '' /*装图片的桶名*/
});
},
/**上传图片**/
upload(event){
var self = this;
var file = event.target.files[0];
var type = file.name.split('.')[1];
var storeAs = new Date().getTime() + '.' + type;
var boolean = true;
if (file.size > this.fileSize) {
Toast('亲,图片不能超过!' + this.fileSize / 1000 + 'kb');
return false;
}
if (this.typeArr && this.typeArr.indexOf(type) > 0) {
boolean = false;
}
if (boolean) {
Toast('上传图片格式不支持!请选择' + this.typeArr);
return false;
}
this.getRight();
this.client.multipartUpload(storeAs, file).then(function (result) {
console.log(result)//至此就拿到了返回的路径
self.data.url = result.res.requestUrls[0].split('?')[0];
}).catch(function (err) {
console.log(err);
});
},
}
}
</script>
</code></pre>
<p>父组件调用</p>
<pre><code><up-Load class="files" typeArr="image/png,image/jpg,image/gif,image/jpeg" size="500000">
</up-Load></code></pre>
<p>注:需引入官网推荐的oss对象的cdn</p>
<pre><code><script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-4.4.4.min.js"></script></code></pre>
<p><strong><em> 需再次强调的是</em></strong>:该代码为前端直传,accessKeyId,accessKeySecret都暴露在外面,更安全的方法可见官网的“服务端签名后上传”(貌似没示例)</p>