第二期:前端九條bug分享

本期

此次9個並不都是bug, 其中有幾個小優化, 雖然一個月的時間遇到不少bug, 但並非每一個都有參考價值, 讓咱們看看此次我遇到的有趣問題吧.css

1: git 報錯WARNING: POSSIBLE DNS SPOOFING DETECTED!

bug現象:
一個平凡的清晨, 放下書包喝了口白開水, 習慣性的git pull了一下, 這個不速之客就出如今了命令行裏面, 腦殼中居然第一反應是但願這個bug有趣一點, 這樣就能夠寫文章了, 這個bug是忽然出現的自己確定與個人操做無關, 當時問了下其餘同事也遇到了這個問題, 可是我須要查一下如何解決它,以及它的危害與預防.前端

bug追查:
WARNING: POSSIBLE DNS SPOOFING DETECTED! 翻譯成中文 警告:檢測到可能的DNS欺騙!哦哦原來是"信任"方面的問題, 那我能想到的就是"身份驗證方面", 有關身份我只作過全局的gitlab的帳號郵箱設置, 權限設置, 還有就是ssh的設置, 那麼怎麼看都是ssh嫌疑最大, 最後仍是去網上查到的解決方法.vue

bug解決方案:
刪除 known_hosts 文件
一臺主機上有多個Linux系統,會常常切換,那麼這些系統使用同一ip,登陸過一次後就會把ssh信息記錄在本地的~/.ssh/known_hsots文件中,
切換該系統後再用ssh訪問這臺主機就會出現衝突警告,須要手動刪除修改known_hsots裏面的內容。
有如下兩個解決方案:node

  1. 手動刪除修改known_hsots裏面的內容;
  2. 修改配置文件「~/.ssh/config」,加上這兩行,重啓服務器。
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

優缺點:webpack

  1. 須要每次手動刪除文件內容,一些自動化腳本的沒法運行(在SSH登錄時失敗),可是安全性高;
  2. SSH登錄時會忽略known_hsots的訪問,可是安全性低;

2:地圖geojson 繪製地圖的具體操做(附上地圖的代碼)

需求:
項目內須要使用地圖展現資產在全球的分佈, 須要繪製'全球地圖',有閃爍提示有懸停展現詳情, 本功能只是一個小模塊, 因此不準有明顯的加載感, 後端會返回給我每一個點對應的經緯度.ios

分析與準備:nginx

本次並無採用咱們公司內部孵化的地圖庫, 由於它並不夠輕量, 最後選用的是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>
效果以下圖(參考了網上你們的作法, 比官網還靠譜)

sj.png

3:樣式同樣的新老項目共用一個域名.

背景:
一個展轉了三個團隊, 維護了快三年的老vue項目, 代碼簡直亂到使人髮指的程度, 好比請求報錯了 /api/home/list/ip這樣的地址報401, 那麼我去查找這個地址在哪裏發出的請求, 全局也沒搜索到這個地址, 一個小時後我才知道 這個地址被分紅了相似這種-> , const a = 'api', const b = 'home', const c = 'list', 請求的時候 axios.get(/${a}/${b}/${c}/ip), 不只如此, 這個請求還被放在的vuex裏面展轉反側三個配置頁面而後被各類從新命名以後不知所蹤, 關鍵是連個文檔也沒有, 這個項目在我來到公司以前就已是一我的人不肯碰的存在了.git

我剛加入就接到了改造這個項目的任務
通過討論這個項目是"救不活了", 可是咱們能夠另起一個如出一轍的項目, 而後全部的新需求都放在新項目裏面, 每次需求都把老項目的頁面遷移過來一個, 這樣一年半載以後就能夠完全使用新項目代替老項目

須要解決的問題:程序員

  1. 兩個項目域名相同
  2. 切換時儘可能作到用戶無感
  3. 共用一套登陸系統
  4. 平穩遷移, 最終淘汰老項目

解決方案:web

  1. 老項目地址xxx/home, 新項目地址xxx/new, 這個須要後端同窗配置nginx代理, 監測到xxx後面的地址進行分配, 這樣兩個工程的靜態資源也要作好代理的區分, 咱們須要配置好publicPath.
  2. 倆個工程的一級二級導航欄作到徹底同樣, 甚至懸停出現的彈框也要作到徹底一致(視覺騙子), 還好這個項目導航方面並非很複雜.
  3. 添加一個專門的router攔截函數, 老項目: 監測到new字段就跳轉到新項目, 新項目檢測到home路徑就跳到老項目, 這樣寫對於404有bug最後我說解決的辦法.
  4. 後臺代碼也跟咱們一期一塊兒重寫, 這就要兼容新老後端代碼.

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
能否作一個小插件把這兩種文件類型與數據相互的轉換, 我分別看了下這兩個文件的格式以下
npm
yarn

文件類型各不相同, 還有一個問題就是這兩個工具的源也有差別, 可能會出現某個插件某個版本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種數據類型, 每種類型前面要有一個‘彩色的點’來快速代表他是什麼類型以下圖:
g.png

要解決的問題

  1. 這種修飾性的結構通常第一想法都是用after和before作。
  2. 圓內的文字須要動態傳入
  3. 顏色能動態傳入就完美了
  4. 由於這個數據可能上百條,因此不用真實dom頗有必要。
  5. 不能夠用js來修改, 由於不想把簡單問題弄得複雜,致使代碼工程化被破壞。
  6. 模塊化,最好其餘地方加上一個class名就有了這個效果。

解決問題

  1. 這個要作成模塊,中間的文字就要可配,我想到了attr()函數。
  2. 顏色實在沒想到動態的傳入方式, 只能退而求其次用scss老哥的@each助陣一下了, 還好最多隻有6種配色。
  3. 一個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

本次的分享就是這樣,我深入的感覺到‘好的解決方法’不少, 最缺乏的是好的問題,歡迎交流, 祝天天進步

相關文章
相關標籤/搜索