概述 map() 方法返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。 语法 array.map(callback[, thisArg]) 参数 callback 原数组中的元素经过该方法后返回一个新的元素。 currentValue callback 的第一个参数,数组中当前被传递的元素。 index callback 的第二个参数,数组中当前被传递的元素的索引。 array callback 的第三个参数,调用 map 方法的数组。 thisArg 执行 callback 函数时 this 指向的对象。 例子:将数组中的单词转换成对应的复数形式. 下面的代码将一个数组中的所有单词转换成对应的复数形式.
1 2 3 4 5 6 7 8 9 10 11 12 |
function fuzzyPlural(single) { var result = single.replace(/o/g, 'e'); if( single === 'kangaroo'){ result += 'se'; } return result; } var words = ["foot", "goose", "moose", "kangaroo"]; console.log(words.map(fuzzyPlural)); // ["feet", "geese", "meese", "kangareese"] |
例子:求数组中每个元素的平方根 下面的代码创建了一个新数组,值为原数组中对应数字的平方根。
1 2 3 |
var numbers = [1, 4, 9]; var roots = numbers.map(Math.sqrt); /* roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9] */ |
例子:在字符串上使用 map 方法 下面的例子演示如在在一个 String 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:
1 2 3 |
var map = Array.prototype.map var a = map.call("Hello World", function(x) { return x.charCodeAt(0); }) // a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100] |
使用技巧案例 通常情况下,map 方法中的 callback 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 下面的语句返回什么呢: ["1", "2", "3"].map(parseInt); // 你可能觉的会是[1, 2, 3] // 但实际的结果是 [1, NaN, NaN] // 通常使用parseInt时,只需要传递一个参数.但实际上,parseInt可以有两个参数.第二个参数是进制数.可以通过语句"alert(parseInt.length)===2"来验证. // map方法在调用callback函数时,会给它传递三个参数:当前正在遍历的元素, 元素索引, 原数组本身. // 第三个参数parseInt会忽视, 但第二个参数不会,也就是说,parseInt把传过来的索引值当成进制数来使用.从而返回了NaN. /* //应该使用如下的用户函数returnInt function returnInt(element){ return parseInt(element,10); } ["1", "2", "3"].map(returnInt); // 返回[1,2,3] */ |
兼容旧环境(Polyfill) map 是在最近的 ECMA-262 标准中新添加的方法;所以一些旧版本的浏览器可能没有实现该方法。在那些没有原生支持 map 方法的浏览器中,你可以使用下面的 Javascript 代码来实现它。所使用的算法正是 ECMA-262,第 5 版规定的。假定Object, TypeError, 和 Array 有他们的原始值。而且 callback.call 的原始值也是 Function.prototype.call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// 实现 ECMA-262, Edition 5, 15.4.4.19 // 参考: http://es5.github.com/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback, thisArg) { var T, A, k; if (this == null) { throw new TypeError(" this is null or not defined"); } // 1. 将O赋值为调用map方法的数组. var O = Object(this); // 2.将len赋值为数组O的长度. var len = O.length >>> 0; // 3.如果callback不是函数,则抛出TypeError异常. if (Object.prototype.toString.call(callback) != "[object Function]") { throw new TypeError(callback + " is not a function"); } // 4. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined. if (thisArg) { T = thisArg; } // 5. 创建新数组A,长度为原数组O长度len A = new Array(len); // 6. 将k赋值为0 k = 0; // 7. 当 k < len 时,执行循环. while(k < len) { var kValue, mappedValue; //遍历O,k为原数组索引 if (k in O) { //kValue为索引k对应的值. kValue = O[ k ]; // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组. mappedValue = callback.call(T, kValue, k, O); // 返回值添加到新数组A中. A[ k ] = mappedValue; } // k自增1 k++; } // 8. 返回新数组A return A; }; } |
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map from:https://www.cnblogs.com/leejersey/p/5462427.html
View Details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<el-table :data="tableData" style="width: 100%"> <el-table-column v-for="col in cols" :prop="col.prop" :label="col.label" > </el-table-column> </el-table> <el-button raw-type="button" @click="addCol"> 添加一列 </el-button> data() { return { tableData: [{ date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' }, { date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄' }, { date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }, { date: '2016-05-03', name: '王小虎', address: '上海市普陀区金沙江路 1516 弄' }], cols: [ {prop: 'date', label: '日期'}, {prop: 'name', label: '姓名'}, ] } }, methods: { addCol(){ this.cols.push({prop: 'address', label: '地址'}) } |
from:https://blog.csdn.net/baidu_34692401/article/details/83348130
View Details1.切换进入当前路由之前的钩子函数 beforeRouteEnter
1 2 3 4 5 6 7 |
<script> export default { beforeRouteEnter(to, form, next) { next() } } </script> |
2.离开当前路由之前的钩子函数 beforeRouteLeave
1 2 3 4 5 6 7 |
<script> export default { beforeRouteLeave(to, form, next) { next() } } </script> |
from:https://www.cnblogs.com/gqx-html/p/11233014.html
View Details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
<!doctype html> <html> <head> <title>弹窗</title> <meta charset="utf-8"> <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <style> body{margin:0;padding:0;} .barrage{position:fixed;display:block;top:0;} .barrage_name{width:70px;height:70px;background:-webkit-gradient(linear,0 0,100% 100%,from(#f00), to(#0f0));border-radius:50%;} .barrage_name_hover{width:70px;height:70px;background:-webkit-gradient(linear,0 0,100% 100%,from(#ff0), to(#00f));border-radius:50%;} .col1{color:#fff;display: block;padding: 17px;text-align: center;} </style> </head> <body> <div class="barrage" id="barrage"> <div class="barrage_name" id="barrage_name"> <span class="col1">打开弹幕</span> </div> </div> <div> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> <p>我是来打酱油的</p> </div> </body> <script type="text/javascript"> $(function(){ var cont=$("#barrage"); var contW=$("#barrage").width(); var contH=$("#barrage").height(); var startX,startY,sX,sY,moveX,moveY; var winW=$(window).width(); var winH=$(window).height(); var barrage_name=$("#barrage_name"); var barrage_frame=$("#barrage_frame"); var body=$("body"); cont.on({//绑定事件 touchstart:function(e){ startX = e.originalEvent.targetTouches[0].pageX; //获取点击点的X坐标 startY = e.originalEvent.targetTouches[0].pageY; //获取点击点的Y坐标 //console.log("startX="+startX+"************startY="+startY); sX=$(this).offset().left;//相对于当前窗口X轴的偏移量 sY=$(this).offset().top;//相对于当前窗口Y轴的偏移量 //console.log("sX="+sX+"***************sY="+sY); leftX=startX-sX;//鼠标所能移动的最左端是当前鼠标距div左边距的位置 rightX=winW-contW+leftX;//鼠标所能移动的最右端是当前窗口距离减去鼠标距div最右端位置 topY=startY-sY;//鼠标所能移动最上端是当前鼠标距div上边距的位置 bottomY=winH-contH+topY;//鼠标所能移动最下端是当前窗口距离减去鼠标距div最下端位置 }, touchmove:function(e){ e.preventDefault(); moveX=e.originalEvent.targetTouches[0].pageX;//移动过程中X轴的坐标 moveY=e.originalEvent.targetTouches[0].pageY;//移动过程中Y轴的坐标 //console.log("moveX="+moveX+"************moveY="+moveY); if(moveX<leftX){moveX=leftX;} if(moveX>rightX){moveX=rightX;} if(moveY<topY){moveY=topY;} if(moveY>bottomY){moveY=bottomY;} $(this).css({ "left":moveX+sX-startX, "top":moveY+sY-startY, }) }, }) }) </script> </html> |
为了兼容PC和移动端,想出了以下办法: 拖动时候用到的三个事件: mousedown 、 mousemove 、 mouseup 在移动端都不起任何作用。毕竟移动端是没有鼠标的,查资料后发现,在移动端与之相对应的分别是: touchstart 、 touchmove 、 touchend 事件。还有一点要注意的是在PC端获取当前鼠标的坐标是: event.clientX 和 event.clientY ,在移动端获取坐标位置则是: event.touches[0].clientX 和 event.touches[0].clientY 。下面就来说说怎么实现这个效果吧,先看一下效果: PC端 : 移动端 : 先来分析一个拖动的流程,以PC端为例,首先是鼠标按下( mousedown 事件),然后移动( mousemove 事件),最后释放鼠标( mouseup 事件),首先要设置一个变量记录鼠标是否按下,在鼠标按下的时候,我们做一个标记,然后需要记录一下鼠标当前的坐标,还有这个div当前的偏移量,当鼠标开始移动的时候,记录下鼠标当前的坐标,用鼠标当前的坐标减去鼠标按下时的坐标再加上鼠标按下时div的偏移量就是现在div距离父辈元素的距离,当鼠标释放的时候将标记改为鼠标已经释放。下面来看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var flag = false; //是否按下鼠标的标记 var cur = { //记录鼠标按下时的坐标 x:0, y:0 } var nx,ny,dx,dy,x,y ; //鼠标按下时的函数 function down(){ flag = true; //确认鼠标按下 cur.x = event.clientX; //记录当前鼠标的x坐标 cur.y = event.clientY; //记录当前鼠标的y坐标 dx = div2.offsetLeft; //记录div当时的左偏移量 dy = div2.offsetTop; //记录div的上偏移量 } //鼠标移动时的函数 function move(){ if(flag){ //如果是鼠标按下则继续执行 nx = event.clientX - cur.x; //记录鼠标在x轴移动的数据 ny = event.clientY - cur.y; //记录鼠标在y轴移动的数据 x = dx+nx; //div在x轴的偏移量加上鼠标在x轴移动的距离 y = dy+ny; //div在y轴的偏移量加上鼠标在y轴移动的距离 div2.style.left = x+"px"; div2.style.top = y +"px"; } } //鼠标释放时候的函数 function end(){ flag = false; //鼠标释放 } |
然后在将事件加入到这个div中即可,下面再来看一个在移动端需要做些什么,首先是事件不同,只需要在添加移动端的 touchatart 、 touchmove 、 touchend 就可以了,还有一个不同的时移动端获取坐标是 event.touches[0].clientX 和 event.touches[0].clientY ,这也很简单,只要加上判断就可以了,如果是PC端就使用event,如果是移动端就使用 event.touches :
1 2 3 4 5 6 |
var touch ; if(event.touches){ touch = event.touches[0]; }else { touch = event; } |
还有一点要注意,在移动端拖动div的时候移动端的页面会自动产生滑动效果,所以还需要在 touchmove 的是给页面添加一个阻止默认事件的函数。 下面是整个代码,可以在Chrome下模拟移动端测试,点击这里查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>适配移动端的拖动效果</title> <style> #div1{ height: 1000px; } #div2{ position: absolute; top:0; left:0; width: 100px; height: 100px; background: #bbbbbb; } </style> </head> <body> <div id="div1"> <div id="div2"></div> </div> <script> var flag = false; var cur = { x:0, y:0 } var nx,ny,dx,dy,x,y ; function down(){ flag = true; var touch ; if(event.touches){ touch = event.touches[0]; }else { touch = event; } cur.x = touch.clientX; cur.y = touch.clientY; dx = div2.offsetLeft; dy = div2.offsetTop; } function move(){ if(flag){ var touch ; if(event.touches){ touch = event.touches[0]; }else { touch = event; } nx = touch.clientX - cur.x; ny = touch.clientY - cur.y; x = dx+nx; y = dy+ny; div2.style.left = x+"px"; div2.style.top = y +"px"; //阻止页面的滑动默认事件 document.addEventListener("touchmove",function(){ event.preventDefault(); },false); } } //鼠标释放时候的函数 function end(){ flag = false; } var div2 = document.getElementById("div2"); div2.addEventListener("mousedown",function(){ down(); },false); div2.addEventListener("touchstart",function(){ down(); },false) div2.addEventListener("mousemove",function(){ move(); },false); div2.addEventListener("touchmove",function(){ move(); },false) document.body.addEventListener("mouseup",function(){ end(); },false); div2.addEventListener("touchend",function(){ end(); },false); </script> </body> </html> |
from:https://www.cnblogs.com/joyco773/p/6519668.html
View Details有时候,依赖 Vue 响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据。或者,我们可能只想抛开当前的DOM,重新开始。那么,如何让Vue以正确的方式重新呈现组件呢? 强制 Vue 重新渲染组件的最佳方法是在组件上设置:key。 当我们需要重新渲染组件时,只需更 key 的值,Vue 就会重新渲染组件。 这是一个非常简单的解决方案。 当然,你可能会对其他方式会更感兴趣: 简单粗暴的方式:重新加载整个页面 不妥的方式:使用 v-if 较好的方法:使用Vue的内置forceUpdate方法 最好的方法:在组件上进行 key 更改 简单粗暴的方式:重新加载整个页面 这相当于每次你想关闭应用程序时都要重新启动你的电脑。 这种方式或许有用,但这是一个非常糟糕的解决方案,不要这样做,我们来看看更好的方法。 不妥的方式:使用 v-if v-if指令,该指令仅在组件为true时才渲染。 如果为false,则该组件在DOM中不存在。 来看看,v-if 是怎么工作的,在template中,添加v-if指令:
1 2 3 |
<template> <my-component v-if="renderComponent" /> </template> |
在script 中,使用nextTick的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script> export default { data() { return { renderComponent: true, }; }, methods: { forceRerender() { // 从 DOM 中删除 my-component 组件 this.renderComponent = false; this.$nextTick(() => { // 在 DOM 中添加 my-component 组件 this.renderComponent = true; }); } } }; </script> |
上面的过程大致如下: 刚开始 renderComponent设置为true,因此渲染 my-component 组件 当我们调用forceRerender时,我们立即将renderComponent设置为false 我们停止渲染my-component,因为v-if指令现在计算结果为false 在nextTick方法中将renderComponent设置回true 当v-if指令的计算结果为true时,再次渲染my-component 在这个过程中,有两个部分比较重要 首先,我们必须等到nextTick,否则我们不会看到任何变化。 在Vue中,一个 tick 是一个DOM更新周期。Vue将收集在同一 tick 中进行的所有更新,在 tick 结束时,它将根据这些更新来渲染 DOM 中的内容。如果我们不等到next tick,我们对renderComponent的更新就会自动取消,什么也不会改变。 其次,当我们第二次渲染时,Vue将创建一个全新的组件。 Vue 将销毁第一个,并创建一个新的,这意味着我们的新my-component将像正常情况一样经历其所有生命周期-created,mounted等。 另外,nextTick 可以与 promise 一起使用:
1 2 3 4 5 6 7 8 |
forceRerender() { // 从 DOM 中删除 my-component 组件 this.renderComponent = false; this.$nextTick().then(() => { this.renderComponent = true; }); } |
不过,这并不是一个很好的解决方案,所以,让我们做 Vue 想让我们做的 较好的方法:forceUpdate 方法 这是解决这个问题的两种最佳方法之一,这两种方法都得到了Vue的官方支持。 通常情况下,Vue 会通过更新视图来响应依赖项中的更改。然而,当我们调用forceUpdate时,也可以强制执行更新,即使所有依赖项实际上都没有改变。 下面是大多数人使用这种方法时所犯的最大错误。 如果 Vue 在事情发生变化时自动更新,为什么我们需要强制更新呢? 原因是有时候 Vue 的响应系统会让人感到困惑,我们认为Vue会对某个属性或变量的变化做出响应,但实际上并不是这样。在某些情况下,Vue的响应系统根本检测不到任何变化。 所以就像上一个方法,如果你需要这个来重新渲染你的组件,可能有一个更好的方法。 有两种不同的方法可以在组件实例本身和全局调用forceUpdate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 全局 import Vue from 'vue'; Vue.forceUpdate(); // 使用组件实例 export default { methods: { methodThatForcesUpdate() { // ... this.$forceUpdate(); // ... } } } |
重要提示:这不会更新任何计算属性,调用forceUpdate仅仅强制重新渲染视图。 最好的方法:在组件上进行 key 更改 在许多情况下,我们需要重新渲染组件。 要正确地做到这一点,我们将提供一个key属性,以便 Vue 知道特定的组件与特定的数据片段相关联。如果key保持不变,则不会更改组件,但是如果key发生更改,Vue 就会知道应该删除旧组件并创建新组件。 正是我们需要的! 但是首先,我们需要绕一小段路来理解为什么在Vue中使用key。 为什么我们需要在 Vue 中使用 key 一旦你理解了这一点,那么这是了解如何以正确方式强制重新渲染的很小的一步。 假设我们要渲染具有以下一项或多项内容的组件列表: 有本地的状态 某种初始化过程,通常在created或mounted钩子中 […]
View Details1.配合使用调用app原生的方法(h5页面不需要回调和数据) 实例1
1 2 3 4 5 6 7 8 9 |
// 通知客户端,token失效 callTokenLostToApp(){ let boswer = vm.config.getBrowser() if(boswer == 'isiOS'){ window.webkit.messageHandlers.tokenExpiredTransmit.postMessage(1); }else if(boswer == 'isAndroid'){ window.tokenExpiredTransmit.jsMethod(1) } }, |
实例2
1 2 3 4 5 6 7 8 9 |
// 关闭页面 closePageApp(cb){ let boswer = vm.config.getBrowser() // IOS 关闭页面 if(boswer == 'isiOS'){ // 这段代码是固定的,必须要放到js中 window.webkit.messageHandlers.closePage.postMessage(1); // 安卓关闭页面 }else if(boswer == 'isAndroid'){ window.closePage.jsMethod(1) } }, |
2.配合使用调用app原生的方法(h5页面需要回调和数据) // 从App获取UUID(手机唯一标识)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 从App获取UUID(手机唯一标识) getSystemInfoFromApp(cb){ let boswer = vm.config.getBrowser() // IOS 获取UUID if(boswer == 'isiOS'){ // 这段代码是固定的,必须要放到js中 function setupWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement('iframe'); WVJBIframe.style.display = 'none'; WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0) } /*与OC交互的所有JS方法都要放在此处注册,才能调用通过JS调用OC或者让OC调用这里的JS*/ setupWebViewJavascriptBridge(function(bridge) { /*JS给ObjC提供公开的API,ObjC端通过注册,就可以在JS端调用此API时,得到回调。ObjC端可以在处理完成后,反馈给JS,这样写就是在载入页面完成时就先调用*/ bridge.callHandler('getSystemInfoFromApp', function(responseData) { if(cb && typeof cb == 'function'){ cb(responseData) }else{ return responseData } }) }) // 安卓获取UUID }else if(boswer == 'isAndroid'){ let systemInfo = window.AndroidWebView.getSystemInfoFromApp(); if(cb && typeof cb == 'function'){ cb(systemInfo) }else{ return systemInfo } } }, |
标红字段为调用的app端定义的方法名,需要app端定义方法配合 from:https://www.cnblogs.com/wendyAndJken/p/9318501.html
View Details先写前面 俗话说 欲先善其事必先利器,作为一个21新世纪的打工人,怎么可以没有一个趁手的编辑器工具呢。今天的主角就是 VS Code ,一个炒鸡强大的编辑器,我们先来介绍一下这个编辑器。 VS Code 全称 Visual Studio Code 是由微软开发的一款免费、跨平台的轻量级代码编辑器。以功能强大、提示友好、不错的性能和颜值俘获了大量开发者的青睐,对 JavaScript 和 NodeJS 的支持非常好,自带很多功能,例如代码格式化,代码智能提示补全等。 再强大的IDE那也不可能面面俱到什么功能都塞进去,那样只会导致IDE本身太臃肿。功能嘛,按需索取,所以,vscode的很多强大功能都是基于插件实现的,IDE只提供一个最基本的框子和最基本功能,由插件来丰富和扩展它的功能。 美化插件 一个美美的编辑器,在我们的开发过程中是非常友好的,所以我们先来介绍一下用于美化的插件 1. Material Theme 插件 Material Theme 插件是一款用于美化主题的图标的插件,该插件包含两个子插件,分别是 Community Material Theme 该插件用于美化主题,还有一个 Material Theme Icons 用于美化图标的。 image-20201116212608537 2. VSCode Great Icons 插件 VSCode Great Icons 插件是 VS Code 图标插件,可以控制 VS Code 中的文件管理的树目录显示图标。不过如果安装上面那个插件这个插件也就不需要了。 image-20201116213037910 代码高亮插件 1. Color Highlight 插件 Color Highlight 插件是 VS Code 中的颜色高亮插件,可以在编辑器中看出其背景颜色。 image-20201116213858966 2. vscode-pigments 插件 该插件也是一款颜色高亮插件,同上一个插件类似,是我现在使用的一个颜色高亮的插件 image-20201116214057772 3. Bracket Pair Colorizer 插件 为不同的括号拥有不同的颜色,可以使嵌套结构表现特别明显,这个插件是我非常喜欢的一个插件,给我安排它。 image-20201116225424675 高效插件 1. Search node_modules 插件 Search node_modules 插件是一款高效的查找插件,当我们的文件太多时,需要找到某个定义的方法时,可以通过该插件在当前文件夹进行搜索内用 image-20201116214435807 2. Path Intellisense […]
View DetailsHTTPS页面里动态的引入HTTP资源,比如引入一个js文件,会被直接block掉的.在HTTPS页面里通过AJAX的方式请求HTTP资源,也会被直接block掉的。 Mixed Content: The page at 'xxx' was loaded over HTTPS, but requested an insecure resource 'xxx'. This request has been blocked; the content must be served over HTTPS. 解决办法: 页面的head中加入: <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> 意思是自动将http的不安全请求升级为https from:https://www.cnblogs.com/cici1989/p/11229810.html
View Details一.问题背景 一个二维平面上有一群NPC,每一回合可以随机向上/下/左/右任一方向走1步,有单位碰撞体积(NPC位置不能重合) 规则就这么简单,初始情况下这群NPC是被人工均匀分布在二维平面上的,运行N个回合后发现所有NPC都集中在了左下角。。怎么会这样,说好的随机呢? 二.分析 现有的实现是这样的: 根据NPC的当前位置判断得到可以去的位置,把结果存放在一维数组arr里P.S.上/下/左/右最多4个点(周围空荡荡的),最少0个点(被围起来了) 生成[0, arr.length – 1]内的一个随机数,作为目标位置索引值index
1 2 3 4 5 |
// 生成随机数[min, max] w.Util.rand = function(min, max) { return Math.round(Math.random() * (max - min) + min); } |
控制NPC移动到arr[index]的位置 逻辑应该是没有问题的,可是为什么运行结果是NPC都跑到左下角开会去了呢?等等,为什么是左下而不是其它角角? 因为实现第一步的时候是按照上 -> 下 -> 左 -> 右的顺序判断的,最后都去了左下角,说明向上和向右的概率太小了(生成随机数的函数rand是没问题的,确实能得到[min, max]的数) 问题的根源是Math.random()不给力,生成[0, 1)之间的小数,取到0和靠近1的值概率很小,所以rand()函数生成的随机数取到min和mix的概率也很小,向上/向右的可能性也就小了 三.解决方案 既然取到min和max的概率很小,中间概率比较均匀,那好办,切掉这两个值就好了。具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function randEx(min, max) { var num; var maxEx = max + 2; // 扩大范围到[min, max + 2],引入两个多余值替换min/max do{ num = Math.round(Math.random() * (maxEx - min) + min); num--; } while (num < min || num > max); // 范围不对,继续循环 return num; } |
值得一提的是上面的加2减1比较巧妙,能够恰好排除min和max 四.运行结果 JS中Math.random()返回值的概率差异可能比你想象的要大些,在实际应用中是不可接受的 测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// 生成随机数[min, max] w.Util.rand = function(min, max) { return Math.round(Math.random() * (max - min) + min); } // 生成更随机的随机数[min, max] function randEx(min, max) { var num; var maxEx = max + 2; // 扩大范围到[min, max + 2],引入两个多余值替换min/max do{ num = Math.round(Math.random() * (maxEx - min) + min); num--; } while (num < min || num > max); // 范围不对,继续循环 return num; } function testRand(times, fun) { var arr = [1, 2, 3, 4]; var count = []; var randVal; for (var i = 0; i < times; i++) { // 获取[0, 3]的随机数 randVal = fun(0, arr.length - 1); // 记录次数 if (typeof count[randVal] !== "number") { count[randVal] = 0; } count[randVal]++; } console.log(count); } console.log("100 times"); testRand(100, w.Util.rand); // 之前的实现 testRand(100, randEx); // 改进过的实现 console.log("1000 times"); testRand(1000, w.Util.rand); // 之前的实现 testRand(1000, randEx); // 改进过的实现 console.log("10000 times"); testRand(10000, w.Util.rand); // 之前的实现 testRand(10000, randEx); // 改进过的实现 console.log("100000 times"); testRand(100000, w.Util.rand); // 之前的实现 testRand(100000, randEx); // 改进过的实现 |
后话 理论上Java的Math.random(),C#的next可能也存在这个问题,这里就没有必要验证了,因为randEx函数的思想(加2减1,嫌效果不好还可以加4减2、加6减3……直到满意为止)是通用的 from:https://www.cnblogs.com/ayqy/p/4511565.html?utm_source=tuicool
View Details更多文章请戳VSCode插件开发全攻略系列目录导航。 发布方式 插件开发完了,如何发布出去分享给他人呢?主要有3种方法: 方法一:直接把文件夹发给别人,让别人找到vscode的插件存放目录并放进去,然后重启vscode,一般不推荐; 方法二:打包成vsix插件,然后发送给别人安装,如果你的插件涉及机密不方便发布到应用市场,可以尝试采用这种方式; 方法三:注册开发者账号,发布到官网应用市场,这个发布和npm一样是不需要审核的。 本地打包 无论是本地打包还是发布到应用市场都需要借助vsce这个工具。 安装:
1 2 |
npm i vsce -g |
打包成vsix文件:
1 2 |
vsce package |
打包的时候如果没有设置repository会有提示,所以最好设置一下。 生成好的vsix文件不能直接拖入安装,只能从扩展的右上角选择Install from VSIX安装: 发布应用市场 Visual Studio Code的应用市场基于微软自己的Azure DevOps,插件的身份验证、托管和管理都是在这里。 要发布到应用市场首先得有应用市场的publisher账号; 而要有发布账号首先得有Azure DevOps组织; 而创建组织之前,首先得创建Azure账号; 创建Azure账号首先得有Microsoft账号; 是不是有点晕,梳理一下: 一个Microsoft账号可以创建多个Azure组织; 一个组织可以创建多个publisher账号; 同时一个组织可以创建多个PAT(Personal Access Token,个人访问令牌); 3.1. 注册账号 首先访问 https://login.live.com/ 登录你的Microsoft账号,没有的先注册一个: 然后访问: https://aka.ms/SignupAzureDevOps ,如果你从来没有使用过Azure,那么会看到如下提示: 点击继续,默认会创建一个以邮箱前缀为名的组织。 3.2. 创建令牌 默认进入组织的主页后,点击右上角的Security: 点击创建新的个人访问令牌,这里特别要注意Organization要选择all accessible organizations,Scopes要选择Full access,否则后面发布会失败。 创建令牌成功后你需要本地记下来,因为网站是不会帮你保存的。 3.3. 创建发布账号 获得个人访问令牌后,使用vsce以下命令创建新的发布者:
1 2 |
vsce create-publisher your-publisher-name |
your-publisher-name必须是字母数字下划线,这是全网唯一的账号,然后会依次要求输入昵称、邮箱、令牌: 创建成功后会默认登录这个账号,接下来你可以直接发布了,当然,如果你是在其它地方创建的,可以试用vsce login your-publisher-name来登录。 除了用命令之外,你还可以使用网页版创建发布账号:https://marketplace.visualstudio.com/manage 3.4. 发布 发布很简单:
1 2 |
vsce publish |
发布成功后大概需要过几分钟才能在应用市场搜到。过几分钟就可以访问网页版的插件主页:https://marketplace.visualstudio.com/items?itemName=sxei.vscode-plugin-demo vscode里面也能搜到了: 3.4.1. 发布注意事项 README.md文件默认会显示在插件主页; README.md中的资源必须全部是HTTPS的,如果是HTTP会发布失败; CHANGELOG.md会显示在变更选项卡; 如果代码是放在git仓库并且设置了repository字段,发布前必须先提交git,否则会提示Git working directory not clean; 另外,如前面所说,如果Organization没有选择all accessible organizations,或者Scopes没有选择Full access,发布的时候可能会报如下错误:
1 2 3 4 |
Error: Failed Request: Unauthorized(401) - https://marketplace.visualstudio.com/_apis/gallery Be sure to use a Personal Access Token <span class="hljs-built_in">which</span> has access to **all accessible accounts**. See https://code.visualstudio.com/docs/tools/vscecli<span class="hljs-comment">#_common-questions for more information.</span> |
3.4.2. 增量发布 版本号:major.minor.patch 如果想让发布之后版本号的patch自增,例如:1.0.2 -> 1.0.3,可以这样:
1 2 |
vsce publish patch |
执行这个命令后会自动修改package.json里面的版本号。同理,vsce publish minor也是可以的。 3.5. 取消发布
1 2 |
vsce unpublish (publisher name).(extension name) |
3.6. 更新 如果修改了插件代码想要重新发布,只需要修改版本号然后重新执行vsce publish即可。 插件升级 4.1. 发布到了应用市场 如果发布到了应用市场,那么一般来说会自动检测有没有新版本,有的话会自动无感知升级,但具体什么时候会去检测我还没有研究过,已经确定的是在扩展面板搜索插件名字会自动检测,重启vscode也会检测。 4.2. 如果是本地打包 如果是打包成vsix,那么只能自己实现升级检测功能呢,通过对比服务器上某个文件的版本号,具体我就不细讲了。 […]
View Details