本期
这次9个并不都是bug, 其中有几个小优化, 虽然一个月的时间遇到很多bug, 但并不是每个都有参考价值, 让我们看看这次我遇到的有趣问题吧.
1: git 报错WARNING: POSSIBLE DNS SPOOFING DETECTED!
bug现象:
一个平凡的清晨, 放下书包喝了口白开水, 习惯性的git pull了一下, 这个不速之客就出现在了命令行里面, 脑袋中竟然第一反应是希望这个bug有趣一点, 这样就可以写文章了, 这个bug是突然出现的本身肯定与我的操作无关, 当时问了下其他同事也遇到了这个问题, 但是我需要查一下如何解决它,以及它的危害与预防.
bug追查:
WARNING: POSSIBLE DNS SPOOFING DETECTED! 翻译成中文 警告:检测到可能的DNS欺骗!哦哦原来是"信任"方面的问题, 那我能想到的就是"身份验证方面", 有关身份我只做过全局的gitlab的账号邮箱设置, 权限设置, 还有就是ssh的设置, 那么怎么看都是ssh嫌疑最大, 最后还是去网上查到的解决方法.
bug解决方案:
删除 known_hosts 文件
一台主机上有多个Linux系统,会经常切换,那么这些系统使用同一ip,登录过一次后就会把ssh信息记录在本地的~/.ssh/known_hsots文件中,
切换该系统后再用ssh访问这台主机就会出现冲突警告,需要手动删除修改known_hsots里面的内容。
有以下两个解决方案:
- 手动删除修改known_hsots里面的内容;
- 修改配置文件“~/.ssh/config”,加上这两行,重启服务器。
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
优缺点:
- 需要每次手动删除文件内容,一些自动化脚本的无法运行(在SSH登陆时失败),但是安全性高;
- SSH登陆时会忽略known_hsots的访问,但是安全性低;
2:地图geojson 绘制地图的具体操作(附上地图的代码)
需求:
项目内需要使用地图展示资产在全球的分布, 需要绘制'全球地图',有闪烁提示有悬停展示详情, 本功能只是一个小模块, 所以不许有明显的加载感, 后端会返回给我每个点对应的经纬度.
分析与准备:
本次并没有采用我们公司内部孵化的地图库, 因为它并不够轻量, 最后选用的是echarts, 我也是第一次用echarts画地图.
说实话官网的例子挺不好的, 很多地方也没有说明白反正我第一次去学习的体验挺差.
地图需要画出各个国家的区域线条, 那就需要一个描述文件而这个文件一般都叫做"geojson"文件, 而这个文件又分为pc(以中心点绘制)、以经纬度绘制两种比较常见, 而本次我们采用的是经纬度的定位, 那就需要用经纬度的geojson!! 这个非常重要, 具体的json这个网站里面挺全的, 但他都是pc绘制geojson文件库
下面是我简单做了下封装的组件
<template>
<div
id="main"
style="position: relative; width: 100%; height: 100%; padding: 0px; margin: 0px; border-width: 0px; cursor: default;"
/>
</template>
<script>
import echarts from 'echarts';
import geoJson from '../assets/geojson/countries.geo.json';
export default {
props: {
mapData: Array, // 里面只有三个值, 经度, 纬度, 数量 用于打点
},
data() {
return {
sanData: [],
myChart: null,
tooltip: { // 提示框
trigger: 'item',
formatter: (arg) => {
if (arg.value) {
return `${arg.name}: ${arg.value[2]}`;
}
return '';
},
},
geo: { // 地图的底色样式
map: 'all',
zoom: 1,
top: 30,
left: 30,
right: 30,
bottom: 30,
show: true,
roam: false,
label: {
normal: {
show: false,
},
emphasis: {
show: false,
},
},
itemStyle: {
normal: {
areaColor: '#47D1FF', // 板块颜色
borderColor: '#3B5077', // 边线
shadowColor: 'rgba(0, 0, 0, 0.2)',
shadowBlur: 10,
},
emphasis: {
areaColor: '#2B91B7', // 悬停
},
},
},
effect: { // 闪点样式
name: '数量',
type: 'effectScatter',
showEffectOn: 'render', // render emphasis
coordinateSystem: 'geo',
hoverAnimation: true,
legendHoverLink: true,
symbolSize: () => 20,
rippleEffect: {
brushType: 'stroke',
},
itemStyle: {
normal: {
color: '#ff8003',
},
},
label: {
normal: {
formatter(arg) {
return arg.value[2];
},
position: 'inside',
show: true,
},
emphasis: {
show: false,
},
},
},
map: { // 地图的绘制数据
type: 'map',
name: '工程数',
mapType: 'world', // 自定义扩展图表类型
geoIndex: 0,
itemStyle: {
normal: { label: { show: true } },
emphasis: { label: { show: true } },
},
},
};
},
methods: {
initMap() {
this.myChart = echarts.init(document.getElementById('main'));
const {
myChart, sanData, effect, tooltip, geo, map,
} = this;
echarts.registerMap('all', geoJson, {});
effect.data = sanData;
const option = {
geo,
tooltip,
series: [effect, map],
};
myChart.setOption(option);
},
// 组装成地图需要的打点数据
initDian() {
this.mapData.forEach((item) => {
this.sanData.push({
name: item.country,
value: [
item.lng,
item.lat,
item.value,
],
});
});
},
},
mounted() {
this.initDian();
this.initMap();
},
};
</script>
效果如下图(参考了网上大家的做法, 比官网还靠谱)
3:样式一样的新老项目共用一个域名.
背景:
一个辗转了三个团队, 维护了快三年的老vue项目, 代码简直乱到令人发指的程度, 比如请求报错了 /api/home/list/ip这样的地址报401, 那么我去查找这个地址在哪里发出的请求, 全局也没搜索到这个地址, 一个小时后我才知道 这个地址被分成了类似这种-> , const a = 'api', const b = 'home', const c = 'list', 请求的时候 axios.get(/${a}/${b}/${c}/ip
), 不仅如此, 这个请求还被放在的vuex里面辗转反侧三个配置页面然后被各种重新命名之后不知所踪, 关键是连个文档也没有, 这个项目在我来到公司之前就已经是一个人人不愿碰的存在了.
我刚加入就接到了改造这个项目的任务
经过讨论这个项目是"救不活了", 但是我们可以另起一个一模一样的项目, 然后所有的新需求都放在新项目里面, 每次需求都把老项目的页面迁移过来一个, 这样一年半载之后就可以彻底使用新项目代替老项目
需要解决的问题:
- 两个项目域名相同
- 切换时尽量做到用户无感
- 共用一套登录系统
- 平稳迁移, 最终淘汰老项目
解决方案:
- 老项目地址xxx/home, 新项目地址xxx/new, 这个需要后端同学配置nginx代理, 监测到xxx后面的地址进行分配, 这样两个工程的静态资源也要做好代理的区分, 我们需要配置好publicPath.
- 俩个工程的一级二级导航栏做到完全一样, 甚至悬停出现的弹框也要做到完全一致(视觉骗子), 还好这个项目导航方面并不是很复杂.
- 添加一个专门的router拦截函数, 老项目: 监测到new字段就跳转到新项目, 新项目检测到home路径就跳到老项目, 这样写对于404有bug最后我说解决的办法.
- 后台代码也跟我们一期一起重写, 这就要兼容新老后端代码.
404问题:
如果监测到new字段就跳转到新项目, 检测到home路径就跳到老项目, 这个会有bug, 比如我乱打一个xxx/jjj那么这个就会留在原地, 我们就需要在'新老'两个项目里面都写一个'404页面', 这一点想到了那么如果是这样 xxx/new/jjj这又是一个'404页面'但是他带new字段, 那么它跳到新项目又变成了bug, 我想到的解决方案是 new这个新项目只要检测到不是自己路由表里的地址, 就把这个连接指向老项目, 老乡维护一套新项目的路由地址list, 只有在list内才会进行跳转新项目的操作, 这样bug就解决了, 可能你会有更好的方法哦可以一起交流.
401与303 前端 测试 后端:
这里是个交流沟通的问题
问题是这样的, 老项目接口未登录报303, 新项目接口未登录报401, 这个现象看起来很好办, 只要检测到 303或是401统一跳登录页面, 但是问题来了, 这两个状态码的判断标准不一样, 出现了'循环跳转'的bug.
比如说: 你在老项目里面不报303那么登录页面就把你转到'来时的'路径, 但是这个路径如果指向新项目, 但是新型项目里面报了401, 导致新项目又跳回登录页面, 日复一年年复一年的循环跳转.
这种问题是沟通问题
只要出现循环的跳转, 测试就会来找到我们, 因为这看起来绝壁是前端问题啊, 你死循环管后端什么事, bug提给我不管是不是我的问题, 有时间的话我都会帮着排查与修复, 我把这个原因搞清楚后去跟后端同学沟通, 这个过程挺慢的, 毕竟谁也不希望bug是自己的..., 可是这个问题到第一次上线他们也没有去解决, 线上又报循环跳转测试仍让我紧急修复, 这个虽然能够理解但也挺无语, 后来用的技巧就是把问题在群里抛出来, 用简短的话把问题说清楚, 不能"看着像前端问题就前端改", 当然了我跟后台同学了解下303余与401的判断标准, 在前端侧也增加了一层登录判断, 虽然没啥大用但是也能帮后端同学解决95%的差异了, 可是剩下5%的问题我已经帮他们分析出解决方案, 可是也放在下个版本做了.
通过这些问题能够感受到, 前端不光是写代码, 也有统筹整个项目的责任在身上, 积极整合大家也是一种锻炼
4:element表单隐藏input
故事是这样的
作者接到了一个离职者的遗留工程的新增需求,需求很简单:在一个表单中根据某一项的选择情况来决定显示与隐藏一个input,比如'类型下拉框'选择‘个人’就隐藏‘订单号’反之则显示并且为'必填', 这个需求简直简单到爆。
bug描述
当先不选择'个人', 然后再选择'个人'选项时,‘订单号的input’消失了但是报错提示信息'订单号为必填'还是存在。
bug初步分析
element-ui本身是否有缺陷, 我用v-if控制输入框的隐藏它没有检测到。
bug初步解决
在我隐藏这个输入框后, 100毫秒的时候调用一次表单的验证方法,虽然你能够解决但这个方法给人的感觉就是临时方案。
bug深入分析
建一个vue工程把这个bug情况在纯净的新工程里面还原一次,结果并未复现, 这就说明这bug跟人家element没有关系,定位在代码本身有问题就好了,问题的元凶就在@change这个事件里面, 上一位同学是在行间这样写的。 @change="() => { updateDesc(); updateYtsUserType(); updateMonitoring() }"
而在updateMonitoring方法里面有一个对表单的校验,这个updateMonitoring方法还有5处地方在调用,所以并不建议修改这里, 导致这一bug的原因是@change事件与vue的数据更新机制未衔接好, vue更新一个值是要经过diff算法的, 更新数据采用三种方案,1:事件的订阅发布 2: promise 3:settimeout 逐级兼容, change事件恰好在这之前就调用了表单的验证,所以把这个检验放在下一个宏任务就不会出现这个bug了, 也不会出现错误信息一闪而逝的情况。
想起了聚焦与失焦的问题(某些移动端有bug)
之前做移动端项目有三个input, 我需要监控当前用户从某个input框里面填完再跳到某个input框的操作路径, 并做一些相应的处理,具体的业务场景我记不清了, 但是当时我发现,比如我再inputa里面输入内容后跳到inputb里面,有时候是先执行的inputb的聚焦 然后 才是inputa的失焦 并且这个时间不是固定的, 因为当时我搞了个0秒延时器并不是100%奏效, 这个点大家可以注意一下。
正常因该是 a聚焦-> a失焦-> b->聚焦 pc端还挺好
5:上传打包文件的小优化, 小而美提升工作体验
这种事情我也认为属于小bug, 毕竟程序员是追求高效的, 而且减少操作步骤可以减少出错的概率。
公司项目组暂时由我们上传到服务器,自动化正在搭建。
之前上传代码
1: 打包yarn build
2:压缩
把打包好的dist文件压缩为zip格式
3:scp上传dist.zip文件
4:ssh连接服务器
5: 进入指定目录, unzip解压dist
现在传代码
1: 打包yarn build
2:scp上传代码scp -r dist/* root@1.1.1.1:/home/xxx/xxxt
反思
如果包并不是特别大的话或者网络很不好, 建议使用第二种, 不以优化小而不为,不以危害小而为之。
6: Cannot assign to read only property 'exports' of object '#<Object>'
这个又是接收了一个离职同学的老项目
bug描述
拉新项目-> 切分支-> yarn 一切正常,打开地址就报这个错.
中文意思就是这个项目里面混合使用了common的规范与es的规范, 需要我统一规范, 就比比如使用了 module.exports
也使用了 import
bug初步解决
使用插件让两者兼容就解决了npm install babel-plugin-transform-es2015-modules-commonjs
babelrc配置{ "plugins": ["transform-es2015-modules-commonjs"] }
思考
这个问题应该不会是个需要下载插件或者修改代码才能解决的问题, 毕竟开发了很久, 如果启动都有bug那怎么上线的, 所以问题应该就存在于流程上, 或者外部的环境上, 比如它使用了我本地的全局webpack的话那就有可能是我webpack版本问题, 但查了一下package.json文件并无这种情况,那问题就在操作流程上。
bug解决
原因很简单, 这个是个老工程使用npm管理版本, 而我习惯性的用了yarn, 导致yarn命令并不能读取package-lock.json文件,那么我下载的版本与之前同学使用的版本可能出现了不同, 删掉node_modules文件夹npm i
, 解决问题
反思:npm与yarn的锁文件可否用插件兼容
npm: package-lock.json
yarn:yarn.lock
可否做一个小插件把这两种文件类型与数据相互的转换, 我分别看了下这两个文件的格式如下
文件类型各不相同, 还有一个问题就是这两个工具的源也有差异, 可能会出现某个插件某个版本yarn可以安装, npm里面没有等等问题, 这就导致类似的兼容插件没有大的意义了。
总结
大家还是暂时老老实实使用过同一种包管理工具把。
7: 厉害的hover
hover思维的局限
刚入门的时候一个老师给我讲,标签的:hover只能修改这个元素本身与这个元素内部的元素, 所以当时写一个弹出菜单的话需要把这个'标题''弹出框'写在一个div里面, hover这个div的时候弹出框block, 但是其实hover可以选择兄弟元素, 可以选择远方的兄弟元素, 可以给兄弟元素增加条件
举例子
相隔的一个兄弟元素内部的span
<div class="home">
<div class="main">被hover的元素</div>
<div class="content">相邻的元素</div>
<div class="content">
<span>远处的元素</span>
<p>我不变色</p>
</div>
</div>
.main:hover +.content +.content >span {
color: red;
}
8:after变色变文案
需求描述
比如我现在有6种数据类型, 每种类型前面要有一个‘彩色的点’来快速表明他是什么类型如下图:
要解决的问题
- 这种修饰性的结构一般第一想法都是用after和before做。
- 圆内的文字需要动态传入
- 颜色能动态传入就完美了
- 因为这个数据可能上百条,所以不用真实dom很有必要。
- 不可以用js来修改, 因为不想把简单问题弄得复杂,导致代码工程化被破坏。
- 模块化,最好其他地方加上一个class名就有了这个效果。
解决问题
- 这个要做成模块,中间的文字就要可配,我想到了attr()函数。
- 颜色实在没想到动态的传入方式, 只能退而求其次用scss老哥的@each助阵一下了, 还好最多只有6种配色。
- 一个class还不够, 需要data-*属性来配合。
上才艺,咳咳不是,是上代码
<div data-color="red" data-title="1">动漫</div>
<div data-color="green" data-title="2">小说</div>
<div data-color="blue" data-title="3">狗狗</div>
*[data-color]::after {
content: attr(data-title);
color: white;
align-items: center;
display: inline-flex;
justify-content: center;
width: 20px;
height: 20px;
font-size: 12px;
margin-left: 10px;
border-radius: 50%;
}
@each $color in red, green, yellow, blue {
*[data-color="#{$color}"]::after {
background-color: $color;
}
}
注意
attr只能在content
中生效。
如果attr不是只能在content
里生效就好了,我们就可以完美解决这一问题,这方面现在也在提案中, 但是暂时没有浏览器支持。
受限于css的本性, 这里并不完美, 颜色方面还是要传,如果大家有好的方法可以私信讨论一下。
9: 复制也很有意思
问题描述
我们前端工程师无时无刻都在与json打交道(或是一个大对象),比如我们生成了一个很长的json或者是ajax获取到了一段很长的json, 我们想要把这个json拿出来单独作为一个文件本地引入, 或者是在控制台看着累要拿到某些工具中进行解析,为了提升性能会出现很多..., 除此之外还会出现对象类型需要手动打开等情况, 说实话复制出来不是太方便
起因是一个晨会的知识分享
用鼠标复制还是处于网民阶段, 成需要肯定是利用方法
同学a:
使用window.copy方法把数据复制到剪切板里面就可以拿到了, 对于开发环节来说又不用在乎兼容性, 这个方法我试了一下也并没有长度限制挺好用的。
同学b:
使用js直接把json数据生成在一个文件里面岂不是更方便, 听到这个的时候就是感觉js不是不可以操纵用户的文件系统么。。。 有点打破我的固有认知, 但是试了一下他提供的方法, 还真不错,下面我就介绍一下这个方法与原理:上才艺
(function(console){
console.save = function(data, filename){
if(!data) {
console.error('Console.save: No data')
return;
}
if(!filename) filename = 'console.json'
if(typeof data === 'object'){
data = JSON.stringify(data, undefined, 4)
}
var blob = new Blob([data], {type: 'text/json'}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a')
a.download = filename
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
a.dispatchEvent(e)
}
})(console)
console.save({a:1})
// 这里是解释版, 每个语句的意思都详细的解释
(function(console){
console.save = function(data, filename){
if(!data) { // 1:没东西没有意义, 大部分可能是复制错了
console.error('Console.save: No data')
return;
}
// 2:名字肯定要有个
if(!filename) filename = 'console.json'
if(typeof data === 'object'){ // 3:转成字符串
// 4: JSON.stringify
// 第一个参数:就是数据了
// 第二个参数:指定哪些key需要处理, undefined就是都处理
// 第三个参数: 层级之间的空格缩进数
data = JSON.stringify(data, undefined, 4)
}
// 这个构造函数厉害了: Blob
// Blob类型的对象表示不可变的类似文件对象的原始数据。
// Blob对象是二进制数据,但它是类似文件对象的二进制数据,因此可以像操作File对象一样操作Blob对象
// Blob 表示的不一定是JavaScript原生格式的数据(这句最关键)。File 接口基于Blob
// 第一个参数是数组, 就是个拼接,这个自己拼也行
// 第二个参数是配置, 这里我们指定为json文件
var blob = new Blob([data,data], {type: 'text/json'}),
// 自定义一个事件
e = document.createEvent('MouseEvents'),
// a标签没什么好说的
a = document.createElement('a')
// 必须定义, 否则变成了跳转
a.download = filename
// URL就是定义本地路径的api, 我们做一个上传组件 或是裁剪组件的时候, 会用这种方式展示本地的图片
a.href = window.URL.createObjectURL(blob)
// dataset就是获取属性的意思
// 格式->> text/json:文件名:blob数据
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
// 我们的自定义事件配置
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
// 触发我们的自定义事件
a.dispatchEvent(e)
}
})(console)
console.save({a:21},'来段静态文件')
虽然用处真的不大, 但是开阔了思路对未来的变成之路也是有好处的。
end
本次的分享就是这样,我深刻的感受到‘好的解决方法’很多, 最缺少的是好的问题,欢迎交流, 祝每天进步
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。