2020-07 前端技術(shù)匯總

2020/07/30 周四

#什么是好的代碼识虚?

在web前端方面,什么是好的代碼婉商?好的代碼應該包含以下兩個特性

  • 高性能轧叽,低時延(性能優(yōu)化)
    • 熟悉數(shù)據(jù)結(jié)構(gòu)與算法蜜自,減少時間復雜度或空間復雜度
    • 熟悉瀏覽器渲染基本原理、熟悉HTTP請求與響應細節(jié)、熟悉前端框架源碼抒抬、減少不必要的渲染開銷复局,提高加載速度
  • 可讀性冲簿、可維護性、可擴展性
    • 熟悉設計模式亿昏,封裝變化峦剔。代碼高內(nèi)聚、低耦合角钩、指責單一吝沫、高度復用。寫出好維護递礼、好迭代惨险、好擴展的代碼
    • 化繁為簡,形成特定代碼規(guī)范脊髓,注意命名辫愉、注釋。寫出人能看懂的代碼将硝,不做騷操作恭朗。盡量保持簡單、易懂依疼,在可擴展性和簡單之間尋找平衡

前端只要不是寫框架痰腮,性能問題會很少遇到。簡單來講涛贯,在實現(xiàn)功能的基礎上诽嘉,代碼簡單、易懂弟翘、好維護迭代就很好了虫腋。技術(shù)始終是為業(yè)務需求服務的∠∮啵基礎建設是很重要的一個環(huán)節(jié)悦冀,這樣有利于快速迭代開發(fā)

#vue使用js顯示彈窗組件

重點是使用vue的render函數(shù),把單文件vue組件睛琳,再封裝為一個函數(shù)盒蟆,掛載到vue的實例屬性后踏烙,其他地方直接調(diào)用該函數(shù)即可調(diào)用組件。

// 在main.js里注冊實例屬性
import showDialog from '@/views/jsDialog/index.js'
Vue.prototype.$showDialog = showDialog

// 其他地方直接使用 this.$showDialog(options) 即可調(diào)用組件

下面來看看實現(xiàn)思路历等,關(guān)于render函數(shù)createElement的options的配置讨惩,參見 createElement 參數(shù) - 深入數(shù)據(jù)對象(opens new window)

import Vue from "vue";
import DialogComponent from '@/views/jsDialog/src/index.vue'

let TheDialog = null
export default function showDialog(options) {
  // 如果未移除,先移除
  TheDialog && TheDialog.remove()

  TheDialog = create(DialogComponent, {
    on: {
      // 單文件組件內(nèi)部可以emit該事件寒屯,銷毀TheDialog組件
      'close-dialog': () => {
        TheDialog.remove()
      }
    },
    props: {
      // 需要傳入的屬性荐捻,單文件組件需要使用props接收
      title: '標題',
      content: '內(nèi)容' 
    }
    // 其他參數(shù)
    ...options
  })

  function create(Component, options) {
    // 先創(chuàng)建實例
    const vm = new Vue({
      render(h) {
        // h就是createElement,它返回VNode
        return h(Component, options);
      }
    }).$mount();

    // 手動掛載
    document.body.appendChild(vm.$el);

    // 銷毀方法
    const comp = vm.$children[0];
    comp.remove = function() {
      document.body.removeChild(vm.$el);
      vm.$destroy();
    };
    return comp;
  }
}

#2020/07/26 周日

#echarts餅圖label兩端對齊label距離引導線距離

注意echarts版本要是 v4.6 +

// 兩端對齊 + 引導線距離
{
name: '訪問來源',
type: 'pie',
minAngle: 90, // label最小扇區(qū)大小
label: {
    normal: {
        alignTo: 'edge', // label兩端對稱布局
        //  ECharts v4.6.0 版本起寡夹,提供了 'labelLine' 與 'edge' 兩種新的布局方式
        margin: 90, // 布局為兩端對稱時候需要外邊距防止圖表變形 數(shù)值隨意不要太大
        distanceToLabelLine: 0, // label距離引導線距離
        formatter: function(param) {
            return '{a|' + param.name + '}\n{hr|}\n' + '{d|' + param.value + '}';
        },
        rich: {
            a: {
                padding: [4, 10, 0, 10],  // 4邊距是文字和hr間距处面,此處的邊距10用于解決label和引導線有間距問題
                color: 'blue'
            },
            d: {
                padding: [0, 10, 4, 10],
                color: 'purple'
            },
            hr: {
                borderWidth: 1,
                width: '100%',
                height: 0,
                borderColor: ' '
            }
        }
    },

}

// 分隔線上線顯示內(nèi)容 
label: {
    normal: {
        formatter: '{font|{c}}\n{hr|}\n{font|fxbbxk6%}',
        rich: {
            font: {
                fontSize: 20,
                padding: [5, 0],
                color: '#fff'
            },
            hr: {
                height: 0,
                borderWidth: 1,
                width: '100%',
                borderColor: '#fff'
            }
        }
    },
},
labelLine: {
    lineStyle: {
        color: '#fff'
    }
}

參考:

#element表單中,人數(shù)輸入框怎么限制只能輸入正整數(shù)

在人數(shù)這一欄菩掏,輸入時魂角,前端需要確保輸入的只能是正整數(shù),且不能是負數(shù)智绸,且自動校正野揪,來看看怎么實現(xiàn)

<template>
  <div>
    只能輸入正整數(shù): {{ peopleCount }}
    <el-input
      v-model="peopleCount"
      @keyup.native="keyUp"
      style="width:200px;margin:50px;"
    ></el-input>
  </div>
</template>

<script>
export default {
  data() {
    return {
      peopleCount: ""
    };
  },
  methods: {
    keyUp(e) {
      // 非數(shù)字全部轉(zhuǎn)換為''
      e.target.value = e.target.value.replace(/[^\d]/g, "");
      // 開始的0處理
      if ([0, "0"].includes(e.target.value)) {
        e.target.value = "";
      }
      this.peopleCount = e.target.value;
      return e.target.value;
    }
  }
};
</script>

有了上面的思路后,對于萬元輸入框怎么限制只能輸入最多保留兩位小數(shù)點的number類型數(shù)據(jù)瞧栗,可以思考下

#怎么開發(fā)vscode插件

在vue-cli項目中囱挑,每次修改vue.config.js都需要手動停止在運行,怎么一鍵就搞定呢沼溜?能不能開發(fā)個vscode插件

帶著這個問題,來看看vscode插件的開發(fā)游添。直接找vscode官方教程系草。按照文檔先來跑一個hello word

# Install Yeoman and VS Code Extension Generator with:
npm install -g yo generator-code

運行yo code,生成一個腳手架項目

guoqzuo-mac:vscodeExtension kevin$ yo code

     _-----_     ╭──────────────────────────╮
    |       |    │   Welcome to the Visual  │
    |--(o)--|    │   Studio Code Extension  │
   `---------′   │        generator!        │
    ( _′U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |     
   __'.___.'__   
 ′   `  |° ′ Y ` 

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? zuo-restart
? What's the identifier of your extension? zuo-restart
? What's the description of your extension? A plugin use to auto restart vue.con
fig.js
? Initialize a git repository? Yes
? Which package manager to use? npm

Your extension zuo-restart has been created!

To start editing with Visual Studio Code, use the following commands:

     cd zuo-restart
     code .

Open vsc-extension-quickstart.md inside the new extension for further instructions
on how to modify, test and publish your extension.

For more information, also visit http://code.visualstudio.com and follow us @code.

這樣會創(chuàng)建一個空的項目唆涝,只注冊了helloworld命令找都,我們按照 vsc-extension-quickstart.md 里的說明運行demo

按F5,進入如下頁面廊酣,但并沒有像官網(wǎng)上的視頻那樣彈一個新的插件調(diào)試窗口能耻,一直在運行中

vscode_plugin_1.png

網(wǎng)上說要裝一個 run code的 vscode插件,也裝了亡驰。后面發(fā)現(xiàn)還是不行晓猛,點擊正在生成,ctrl + c 就彈出一個名為 "擴展開發(fā)宿主" 的新窗口了凡辱,里面可以調(diào)試插件戒职,如下圖

vscode_plugin_2.png

生成項目的入口是 extension.ts,他默認注冊了一個helloword命令透乾,我們輸入命令就會顯示一個彈窗消息

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "zuo-restart" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('zuo-restart.helloWorld', () => {
        // The code you place here will be executed every time your command is executed

        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World from zuo-restart!');
    });

    context.subscriptions.push(disposable);
}

// this method is called when your extension is deactivated
export function deactivate() {}

我們在插件調(diào)試窗口跑下hello world命令

vscode_plugin_3.png

出現(xiàn)如下彈窗消息洪燥,就說明跑的沒問題了

vscode_plugin_4.png

這樣hellowrod就跑起來了磕秤,vsc-extension-quickstart.md 里面有構(gòu)架、發(fā)布插件的文檔說明

官網(wǎng)提供了一些簡單的demo捧韵,可以練練手市咆,vscode-extension-samples | github (opens new window),后續(xù)有時間了繼續(xù)研究

參考:

#loadash節(jié)流與防抖理解

理論上throttle節(jié)流一般用于像監(jiān)聽resize方法再来,想要減少執(zhí)行頻率時使用蒙兰。對于點擊按鈕提交,防止短時間內(nèi)多次點擊可以用防抖

但實際使用時可根據(jù)具體情況來看其弊,本質(zhì)上都是利用setTimeout來處理執(zhí)行頻率或執(zhí)行間隔癞己。下面是一個簡單的防抖示例

import { debounce } from 'loadsh'
export default {
  methods: {
    submitFormDebounce: debounce(function() {
      console.log('submit', +new Date())
      this.submitForm()
    }, 300, {trailing: true}),

    submitForm() {

    }
  }
}

#element源碼中節(jié)流與防抖的應用

在做input搜索時,由于input change后需要請求接口梭伐,這里el-autocomplete有個默認的300豪秒debounce痹雅,可以減少請求頻率。理論上這里減少頻率需要使用節(jié)流糊识,但為什么是防抖呢绩社?

我們把element源碼中對節(jié)流防抖的使用都找一找÷该纾可以看到element使用的節(jié)流防抖庫是 throttle-debounce

發(fā)現(xiàn)節(jié)流throttle用的比較少愉耙,只找到了三個地方:

// Backtop 回到頂部
// packages/backtop/src/main.vue  滾動監(jiān)聽時用到了節(jié)流
import throttle from 'throttle-debounce/throttle';
mounted() {
  this.init();
  this.throttledScrollHandler = throttle(300, this.onScroll);
  this.container.addEventListener('scroll', this.throttledScrollHandler);
},

// Carousel 走馬燈 
// packages/carouse/src/main.vue 鼠標hover,箭頭點擊使用了節(jié)流
import throttle from 'throttle-debounce/throttle';
created() {
  this.throttledArrowClick = throttle(300, true, index => {
    this.setActiveItem(index);
  });
  this.throttledIndicatorHover = throttle(300, index => {
    this.handleIndicatorHover(index);
  });
},

// Image 圖片 滾動到區(qū)域懶加載時拌滋,使用了節(jié)流
if (_scrollContainer) {
  this._scrollContainer = _scrollContainer;
  this._lazyLoadHandler = throttle(200, this.handleLazyLoad);
  on(_scrollContainer, 'scroll', this._lazyLoadHandler);
  this.handleLazyLoad();
}

再來看看防抖的地方

el_debounce.png

總結(jié):涉及到接口請求的基本都是防抖朴沿,對于不請求接口,防止多次執(zhí)行的情況才用節(jié)流败砂,其他請求一律防抖

#log2n對數(shù)在前端的應用場景:把文件大小或金額自動添加合適的單位

在寫下載/導出文件接口時赌渣,由于接口文件數(shù)據(jù)是流的形式而非buffer,導致total為0昌犹,無法獲取進度坚芜。只能通過loaded知道當前下載了多少字節(jié)。前端顯示時斜姥,怎么給出合適的單位鸿竖,是KB、MB铸敏,還是G缚忧?

// Math.pow(2, 0) // B
// > Math.pow(2, 10) // KB
// > Math.pow(2, 20) // MB
// > Math.pow(2, 30) // GB
// > Math.pow(2, 40) // TB
// > Math.pow(2, 50) // PB
// 以此類推...

可以通過對數(shù)來快速確定區(qū)間

/**
 * @description 格式化文件size
 * @param { Number } value 文件大小 B 字節(jié)
 * @returns 轉(zhuǎn)換后的文件大小及單位數(shù)組,保留兩位小數(shù)
 * @example
 * formatFileSize(100) =>  [100, "B"]
 * formatFileSize(10000) => [9.77, "KB"]
 * formatFileSize(100000000) => [95.37, "MB"]
 */
function formatFileSize(value) {
  let unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB','YB']
  let index = Math.floor(Math.log2(value) / 10) // 計算該value的值為2的多少次方搞坝,向下取整
  // 如果超出范圍取最大值
  if (index > unitArr.length - 1) {
    index = unitArr.length - 1
  }
  let result = value / Math.pow(2, index * 10) // 裝換為合適的單位
  result = (result * 100).toFixed() / 100 

  return [result, unitArr[index]]
}

依此類推搔谴,假設給定單位為元,將值轉(zhuǎn)換為合適的單位:元/萬/億/兆(萬億)桩撮,10的4次方 萬敦第,10的8次方 億峰弹,10的12次方 兆

/**
 * @description 格式化人民幣
 * @param { Number } value 元
 * @returns 轉(zhuǎn)換后的人民幣及單位數(shù)組,保留兩位小數(shù)
 * @example
 * formartMoney(1000) => [1000, "元"]
 * formartMoney(98000) => [9.8, "萬"]
 * formartMoney(100000000) => [1, "億"]
 */
function formartMoney(value) {
  let unitArr = ['元', '萬', '億', '兆'] 
  let index = Math.floor(Math.log10(value) / 4)
  // 如果超出范圍取最大值
  if (index > unitArr.length - 1) {
    index = unitArr.length - 1
  }
  let result = value / Math.pow(10, index * 4) // 裝換為合適的單位

  result = (result * 100).toFixed() / 100 

  return [result, unitArr[index]]
}

擴展:

#關(guān)于常用組件芜果、樣式鞠呈、工具函數(shù)封裝代碼快速復用的思考

怎么讓搬磚更有效率,整理常用工具庫utils右钾、搜集各種場景常用的代碼片段蚁吝,快速ctrl+c、ctrl+v

接口請求舀射、表單校驗(正則)窘茁、表單/表格/通用樣式、表格分頁脆烟、圖表等常用的碎片化代碼搜集整理山林,形成文檔,以便快速復用

#2020/07/25 周六

#el-input類型為textarea時不能使用v-model.trim

el-input如果type為textarea邢羔,不能使用.trim修飾符驼抹,否則輸入內(nèi)容時會無法換行,如果需要去掉收尾空格拜鹤,可以在提交數(shù)據(jù)時框冀,手動執(zhí)行.trim()去空格

<template>
  <div>
    <el-input
      type="textarea"
      v-model.trim="text"
      rows="5"
      style="width:200px;margin:100px;"
    ></el-input>
  </div>
</template>

<script>
export default {
  data() {
    return {
      text: ""
    };
  }
};
</script>

#2020/07/24 周四

#git修改上上次的commit備注信息

由于提交代碼時有鉤子函數(shù),信息里面沒有包含前置的code會無法提交敏簿。所以如果commit信息寫的有問題需要修改后才能提交

對于修改上一次commit備注信息明也,我們可以使用 --amend -m 來修改。但它無法修改上上次提交信息惯裕,這種情況我們可以使用rebase來做處理诡右,下面來做一個測試

本地做兩次提交,第一次提交信息為"測試第一次提交", 第二次提交信息為 "第二次提交"轻猖,先不push,我們需要修改上上次的提交信息域那,也就是修改"測試第一次提交"的內(nèi)容

# 查看git記錄
guoqzuo-mac:fedemo kevin$ git log
commit 3814855781da539d21e2072e42a53558587497c6 (HEAD -> master)
Author: guoqzuo <guoqzuo@gmail.com>
Date:   Mon Aug 10 22:28:15 2020 +0800

    第二次提交

commit e889c7ecbcb024037701eb48c9bfe3b9c22f9490
Author: guoqzuo <guoqzuo@gmail.com>
Date:   Mon Aug 10 20:04:27 2020 +0800

    測試第一次提交

commit d5c2f2f3193cf02d6ac1ae995ca00c4082e36cad (origin/master, origin/HEAD)
Author: guoqzuo <guoqzuo@gmail.com>
Date:   Mon Aug 10 00:56:34 2020 +0800

    update cookie研究,合并單元格研究demo
:

執(zhí)行 git rebase -i HEAD~2咙边,如下圖可以看到最近兩次提交,進入一個vim編輯頁面

edit_commit_rebase_1.png

按ESC, 再按a進入INSERT模式, 將上上次提交的信息前的pick改為edit次员,如下圖败许,按ESC, shift + : 進入命令模式,輸入x 或wq保存淑蔚,不熟悉vim操作的可以搜索下vim教程

edit_commit_rebase_2.png

保存后市殷,會看到下面log

guoqzuo-mac:fedemo kevin$ git rebase -i HEAD~2
Stopped at e889c7e...  測試第一次提交
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue
guoqzuo-mac:fedemo kevin$ 

運行 git commit --amend 會進入下面的修改頁面,可以修改上上次的信息

edit_commit_rebase_3.png

這里我們把上上次信息改為 "測試第一次提交,修改第一次提交的內(nèi)容"刹衫,保存后醋寝,結(jié)果如下

[detached HEAD 45f5911] 測試第一次提交,修改第一次提交的內(nèi)容
 Date: Mon Aug 10 20:04:27 2020 +0800
 1 file changed, 2 insertions(+)
guoqzuo-mac:fedemo kevin$ 

然后運行 git rebase --continue搞挣,這樣就修改好了

guoqzuo-mac:fedemo kevin$ git rebase --continue
Successfully rebased and updated refs/heads/master.
guoqzuo-mac:fedemo kevin$ 

再來git log 看看提交記錄,修改上上次的提交信息已ok

guoqzuo-mac:fedemo kevin$ git log
commit e498bcabf2d2e4c97f47320e1d72693cb82d9db8 (HEAD -> master)
Author: guoqzuo <guoqzuo@gmail.com>
Date:   Mon Aug 10 22:28:15 2020 +0800

    第二次提交

commit 45f591157be44100073de14f5808b816104a8f2b
Author: guoqzuo <guoqzuo@gmail.com>
Date:   Mon Aug 10 20:04:27 2020 +0800

    測試第一次提交,修改第一次提交的內(nèi)容

參考:修改上上次的commit信息(opens new window)

#2020/07/22

#echarts動態(tài)改變option里dataZoom的值沒有實時生效的問題

這里我們雖然修改了options的值音羞,但不會實時生效囱桨,需要手動調(diào)用下echarts實例的resize()方法

另外在做echarts時,對于自適應縮放的圖表嗅绰,一定要注意在窗口縮放時舍肠,重新調(diào)用resize()

#http請求有哪幾種傳參方式

在swagger文檔里,有一個傳參類型的描述 Parameter Type窘面,一般有四種

  • header 通過請求頭傳參翠语,也就是參數(shù)加到首部 headers 里
  • path 參數(shù)放到url路徑里,比如 /user/123 這里 123是用戶id
  • query 查詢參數(shù)财边,也就是url后面 ? 符號之后的傳參肌括,一般用于get請求傳參,比如 /user/123?a=xx&b=xx
  • body 參數(shù)放到請求體制圈,一般用于post請求们童,相對get請求來說,安全性好鲸鹦,可以傳的數(shù)據(jù)更多

#Object.assgin時是否會忽略null,undefined,空字符串

一般我們在需要設置某個對象的多個值時Object.assgin是一種很好的方法慧库,但又怕當某個屬性的值為空字符串、null或undefined時馋嗜,會自動跳過的情況齐板。這里來做一個簡單的測試

objA = {a: 'a', b: 'b', c: 'c', d: 'd'}
Object.assign(objA, {a: 1, b: undefined, c: null, d: ''})
objA // {a: 1, b: undefined, c: null, d: ""}

Object_assign.png

綜上,Object.assgin可以放心用

#git將遠程倉庫A分支合并到B分支

假設要將遠程分支的 A 分支合并到 B 分支葛菇,一般我會先在A分支將B分支merge甘磨,再切到B分支,merge A分支眯停。

以將遠程倉庫的 dev1.3.4 分支合并到遠程的 test1.3.4 分支為例济舆,下面是我一般的合并過程

# 1\. 本地切到 dev1.3.4 分支
# 2\. merge遠程的test1.3.4分支,命令如下
git merge origin/test1.3.4
# 3.如果有沖突(conflict)莺债,修改沖突文件
# 4.修改沖突后提交代碼到遠程倉庫滋觉,命令如下
git add 修改沖突相關(guān)的文件
git commit '修改沖突,fix conflict'
git push
# 5.切換到test1.3.4分支
# 6.merge本地的dev1.3.4齐邦,因為本地的dev1.3.4是最新的代碼椎侠,命令如下
git merge dev1.3.4 => git push

另外,養(yǎng)成習慣措拇,在git push前我纪,先git pull

#2020/07/17 周五

#前端修改cookie后,相關(guān)cookie改動會傳到后臺嗎

首先我們來捋一捋,什么是cookie浅悉?與cookie相關(guān)的知識點有兩個:

  1. 前端獲取/設置cookie趟据,使用 document.cookie
  2. HTTP請求與響應相關(guān)cookie

我們先下個結(jié)論:他們之間是相互關(guān)聯(lián)的,接口響應頭設置cookie仇冯,會對document.cookie的值產(chǎn)生影響之宿;前端設置docuemnt.cookie也會對請求頭cookie值產(chǎn)生影響,但如果后端寫到前端的cookie如果使用了HttpOnly屬性苛坚,前端是無法通過document.cookie做修改的

#根據(jù)功能點寫測試demo

紙上得來終覺淺比被,這里為了弄懂這里面的關(guān)系,我們來寫一個demo做測試泼舱,將涉及的知識點都串起來等缀,首先要有一個html頁面,有兩個用處

  1. 在該頁面打開F12娇昙,在console里通過命令查看或設置document.cookie信息
  2. 在該頁面中請求接口尺迂,在F12 Network里觀察請求頭,響應頭里cookie的信息

另外需要寫兩個接口冒掌,用戶觀察請求響應頭里cookie的信息噪裕,這里我們用koa來寫兩個簡單的接口。

下面是demo目錄層級

# demo 目錄層級
├── public
│   └── index.html # 靜態(tài)頁面
├── index.js # 接口
└── package.json # npm init 創(chuàng)建股毫,用于安裝index.js引入的koa等npm包

index.html代碼如下

<body>
  <h1>test測試</h1>
  <button id="userInfoBtn">獲取user信息</button>
  <button id="editInfoBtn">修改user信息</button>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    let userInfoBtn = document.querySelector('#userInfoBtn')
    let eidtInfoBtn = document.querySelector('#userInfoBtn')
    // 請求獲取用戶信息接口
    // 用于測試響應頭設置 'Set-Cookie' 對前端docuemnt.cookie的影響
    userInfoBtn.onclick = () => {
      axios.get('/user').then((res) => {
        console.log(res)
      }).catch((e) => {
        console.error(e.message)
      })
    }
    // 請求修改用戶信息接口
    // 用于測試document.cookie設置后或者第一次請求響應頭設置Set-Cookie后膳音,對下次接口請求頭的影響
    editInfoBtn.onclick = () => {
      axios.put('/user').then((res) => {
        console.log(res)
      }).catch((e) => {
        console.error(e.message)
      })
    }
  </script>
</body>

index.js接口代碼

const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()

// 靜態(tài)服務,用于使用 http://127.0.0.1:9000/ 訪問 public下的index.html頁面
app.use(new require('koa-static')('./public'))

// GET /user  接口铃诬,設置Set-Cookie響應頭測試
router.get('/user', ctx => {
  ctx.set({
    // 'Set-Cookie': 'token=123;path=/;max-age=100;HttpOnly',
    'Set-Cookie': ['token=123;path=/;max-age=100;HttpOnly','mark=9;path=/;']
  })
  ctx.body = {
    name: '張三',
    age: 20
  }
})

// PUT /user  接口祭陷,用于觀察接口請求頭
router.put('/user', ctx => {
  ctx.body = {
    code: 0,
    data: {},
    msg: '成功'
  }
})

app.use(router.routes())

// 開啟本地HTTP服務,9000端口
app.listen(9000, () => {
  console.log('server listen on 9000 port')
})

#document.cookie設置對請求頭的影響

首先我們可以先看 Document.cookie - Web API 接口參考 | MDN (opens new window)這個文檔趣席,對document.cookie有一個大概的了解兵志。

我們先運行demo,nodemon index.js宣肚,訪問 http://127.0.0.1:9000/ 進入頁面想罕,查看cookie信息,如下圖

what_cookie_1.png

這時霉涨,我們發(fā)送一個put請求弧呐,看接口請求頭信息。再通過 document.cookie = "a=1" 設置cookie嵌纲,然后再發(fā)送一個put請求,對比請求頭之前的區(qū)別腥沽,如下圖逮走,我們初步得出結(jié)論:前端設置cookie后,下次請求今阳,在請求頭里會攜帶這個cookie

what_cookie_2.png

#document.cookie操作方法

document.cookie API的設計不怎么友好师溅,一般會使用一個庫茅信,來操作docuemnt.cookie,需要注意

document.cookie = "a=1" // 如果cookie中沒有a這個key墓臭,可以添加key為a蘸鲸,值為1的cookie,否則修改對應的值為1
document.cookie = "" // 不會清空cookie窿锉,對cookie基本無影響
// 怎么刪除 a=1 的cookie呢酌摇?將該cookie的有效時間設置為過去的時間即可
document.cookie = "a=1;expires=" + new Date('1970-1-1')

MDN document.cookie文檔里有推薦的一個操作cookie的封裝方法,可以參考下

/*\
|*|
|*|  :: cookies.js ::
|*|
|*|  A complete cookies reader/writer framework with full unicode support.
|*|
|*|  https://developer.mozilla.org/en-US/docs/DOM/document.cookie
|*|
|*|  This framework is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*|  Syntaxes:
|*|
|*|  * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
|*|  * docCookies.getItem(name)
|*|  * docCookies.removeItem(name[, path], domain)
|*|  * docCookies.hasItem(name)
|*|  * docCookies.keys()
|*|
\*/

var docCookies = {
  getItem: function (sKey) {
    return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
  },
  setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
    if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
    var sExpires = "";
    if (vEnd) {
      switch (vEnd.constructor) {
        case Number:
          sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
          break;
        case String:
          sExpires = "; expires=" + vEnd;
          break;
        case Date:
          sExpires = "; expires=" + vEnd.toUTCString();
          break;
      }
    }
    document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
    return true;
  },
  removeItem: function (sKey, sPath, sDomain) {
    if (!sKey || !this.hasItem(sKey)) { return false; }
    document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : "");
    return true;
  },
  hasItem: function (sKey) {
    return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
  },
  keys: /* optional method: you can safely remove it! */ function () {
    var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
    for (var nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); }
    return aKeys;
  }
};

#HTTP cookies后端接口向前端頁面寫入cookie

可以先看下面兩個文檔嗡载,對http cookie有一個基本的了解

  1. http請求頭或響應頭參數(shù)cookie Cookie - HTTP Headers | MDN(opens new window)
  2. http cookies HTTP cookies - HTTP | MDN(opens new window)

An HTTP cookie (web cookie, browser cookie) is a small piece of data that a server sends to the user's web browser. The browser may store it and send it back with later requests to the same server. Typically, it's used to tell if two requests came from the same browser — keeping a user logged-in, for example. It remembers stateful information for the stateless HTTP protocol.(HTTP Cookie 是服務器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù)窑多,它會在瀏覽器下次向同一服務器再發(fā)起請求時被攜帶并發(fā)送到服務器上。通常洼滚,它用于告知服務端兩個請求是否來自同一瀏覽器埂息,如保持用戶的登錄狀態(tài)。Cookie 使基于無狀態(tài)的HTTP協(xié)議記錄穩(wěn)定的狀態(tài)信息成為了可能遥巴。)

Cookies are mainly used for three purposes(cookie主要由以下三個應用場景):

  • Session management (會話管理)
    • Logins, shopping carts, game scores, or anything else the server should remember(用戶登錄狀態(tài)千康、購物車、游戲分數(shù)或其它需要記錄的信息)
  • Personalization (個性化)
    • User preferences, themes, and other settings(用戶自定義配置铲掐,主題或其他設置)
  • Tracking (用戶行為跟蹤)
    • Recording and analyzing user behavior (用于記錄和分析用戶行為)

#cookie設置屬性詳解

服務端通過在接口響應頭設置 'Set-Cookie' 拾弃,將對應的cookie寫到前端,先來看看設置cookie的格式

Set-Cookie: “name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure;samesite”

屬性 說明 默認值
name 一個唯一確定的cookie名稱迹炼。如有特殊字符需要編碼(encodeURIComponent) /
value 存儲在cookie中的字符串值砸彬。如有特殊字符需要編碼 空字符串
domain cookie對于哪個域是有效,如果指定了一個域斯入,那么子域也包含在內(nèi)砂碉。.xx.com血筑,對于xx.com的所有子域都有效 當前url所在的域名
path 表示這個cookie影響到的路徑话浇,瀏覽器跟會根據(jù)這項配置,像指定域中匹配的路徑在請求頭發(fā)送cookie信息 當前url所在的path
expires 失效時間午乓,是一個具體的時間磅摹,這個值是GMT時間格式滋迈,expires=" + new Date('1970-1-1') 這種即可。如果客戶端和服務器端時間不一致户誓,使用expires就會存在偏差饼灿。注意兩點:1.設置一個過去的時間,可用于刪除該cookie 2.當Cookie的過期時間被設定時帝美,設定的日期和時間只與客戶端相關(guān)碍彭,而不是服務端。max-age也是如此 如果沒有定義,cookie會在對話結(jié)束時(關(guān)閉瀏覽器)過期
max-age 與expires作用相同庇忌,用來告訴瀏覽器此cookie多久過期(單位是秒)舞箍,而不是一個固定的時間點。正常情況下皆疹,max-age的優(yōu)先級高于expires疏橄。 同上面的expires
HttpOnly 告知瀏覽器不允許通過腳本document.cookie去更改這個值,同樣這個值在document.cookie中也不可見略就。但在http請求張仍然會攜帶這個cookie捎迫。注意這個值雖然在腳本中不可獲取,但仍然在瀏覽器安裝目錄中以文件形式存在残制。這項設置通常在服務器端設置立砸。有助于緩解跨站點腳本(XSS)攻擊。 不設置
secure 安全標志初茶,指定后颗祝,只有在使用SSL鏈接(https)時候才能發(fā)送到服務器,如果是http鏈接則不會傳遞該信息恼布。就算設置了secure 屬性也并不代表他人不能看到你機器本地保存的 cookie 信息螺戳,因此未加密的重要信息盡量不要放cookie了 不設置
samesite 當跨域請求是瀏覽器是否發(fā)送cookie,可以阻止跨站請求偽造攻擊CSRF折汞。1. 值為None: 瀏覽器會在同站請求倔幼、跨站請求下繼續(xù)發(fā)送 cookies,不區(qū)分大小寫爽待。2.Strict:瀏覽器將只在訪問相同站點時發(fā)送 3.Lax:與 Strict 類似损同,但用戶從外部站點導航至URL時(例如通過鏈接)除外。 Same-site cookies 將會為一些跨站子請求保留鸟款,如圖片加載或者 frames 的調(diào)用膏燃,但只有當用戶從外部站點導航到URL時才會發(fā)送。如 link 鏈接 以默認值都是None何什,現(xiàn)在基本新的瀏覽器基本都是Lax了

#后端設置'Set-Cookie'響應頭對前端的影響

我們在 GET /user 接口做了處理组哩,當訪問這個接口時,向前端設置cookie, 一個HttpOnly的处渣,一個非httpOnly伶贰,看看效果

ctx.set({
  // 單個cookie設置
  // 'Set-Cookie': 'token=123;domain=;path=/;max-age=100;HttpOnly', 
  // 多個cookie設置
  'Set-Cookie': ['token=123;path=/;max-age=100;HttpOnly','mark=9;path=/;max-age=60;']
})

先刪除之前的cookie,保證document.cookie為空

what_cookie_3.png
  1. 先請求獲取user信息接口罐栈,在F12里看響應頭黍衙,Respons e Headers可以看到HttpOnly的cookie
  2. 使用document.cookie看下前端cookie信息。這里獲取不到設置了HttpOnly的cookie
  3. 請求修改user信息接口荠诬,看請求頭琅翻。后端設置的cookie涯捻,下次請求會攜帶
  4. 100秒后,發(fā)現(xiàn)document.cookie以及application面板都沒有cookie值了望迎,因為我們設置了有效時間,再次發(fā)送修改user的put請求凌外,請求頭不會攜帶任何cookie信息
what_cookie_4.png
  1. 再次請求獲取user信息接口辩尊,然后用document.cookie修改token的值,發(fā)現(xiàn)HttpOnly屬性的cookie前端無法通過document.cookie來修改康辑,且再次請求put接口摄欲,token值還是原先后端設置的值,所以HttpOnly屬性的cookie前端無法修改
what_cookie_5.png

#參考文檔

#element合并單元格利用css自定義表格border

有個較為特殊的表格疮薇,需要合并單元格胸墙,且改變表格border,看看element el-table怎么實現(xiàn)這種表格

special_table.png
<template>
  <div class="table-test">
    <el-table
      :data="dataList"
      border
      size="mini"
      :span-method="arraySpanMethod"
      :header-cell-style="{ background: '#f7f7f7' }"
    >
      <el-table-column
        v-for="item in ['a', 'b', 'c', 'd']"
        :key="item"
        :prop="item"
        :label="item"
      ></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dataList: []
    };
  },
  mounted() {
    this.dataList = [1, 2, 3, 4].map(() => {
      return { a: "1", b: "2", c: "3", d: 4 };
    });
  },
  methods: {
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
      console.log(row, column, rowIndex, columnIndex);
      // 只是遍歷表格td內(nèi)容按咒,不包含th表頭
      // 對第一列迟隅,進行合并列
      if (columnIndex === 0) {
        if (rowIndex === 0) {
          // 第一列,第一行励七,默認
          return {
            rowspan: 1,
            colspan: 1
          };
        } else if (rowIndex === 1) {
          // 第一列智袭,第二行,合并掠抬,占this.dataList.length - 1行
          return {
            rowspan: this.dataList.length - 1,
            colspan: 1
          };
        } else if (rowIndex >= 2) {
          // 第一列吼野,剩余行,為空
          return {
            rowspan: 0,
            colspan: 0
          };
        }
      }
    }
  }
};
</script>

<style lang="less" scoped>
.table-test {
  width: 500px;
  margin: 100px;
  // border處理
  // 去掉表頭單元格th右邊框
  /deep/ .el-table th:not(:first-child) {
    border-right: 0;
  }
  // 去掉表格內(nèi)容單元格td的右側(cè)邊框两波、底部邊框
  /deep/ .el-table td {
    border-right: 0;
    border-bottom: 0;
  }
  // 為第一行td增加底部border
  /deep/ .el-table__row:first-child td {
    border-bottom: 1px solid #eaeaea;
  }
  // 為第一行第一列td增加右側(cè)border
  /deep/ .el-table__row:first-child td:first-child,
  // 為第二行(合并后的)第一列td設置右側(cè)border
  /deep/ .el-table__row:nth-child(2) td:first-child {
    border-right: 1px solid #eaeaea;
  }
}
</style>

#2020/07/12 周日

#github clone下載太慢怎么解決

以clone vue源碼為例瞳步,默認git clone下載非常慢,我們可以把github.com鏈接改為鏡像github.com.cnpmjs.org腰奋,這樣下載速度就很快了单起,過程如下

# git clone 下載vue源碼
guoqzuo-mac:source kevin$ git clone https://github.com/vuejs/vue.git
Cloning into 'vue'...
remote: Enumerating objects: 56366, done.
^Cceiving objects:   5% (2823/56366), 556.01 KiB | 10.00 KiB/s   
guoqzuo-mac:source kevin$ 
guoqzuo-mac:source kevin$ git clone https://github.com.cnpmjs.org/vuejs/vue.git
Cloning into 'vue'...
remote: Enumerating objects: 56366, done.
remote: Total 56366 (delta 0), reused 0 (delta 0), pack-reused 56366
Receiving objects: 100% (56366/56366), 26.75 MiB | 1.22 MiB/s, done.
Resolving deltas: 100% (39568/39568), done.
guoqzuo-mac:source kevin$ 

github_clone_slow.png

#js獲取location.href不真實的問題

macOS 修改host文件 /etc/hosts 后,本地訪問某個域名會按照host指定的ip去解析氛堕,就會造成前端location.href不準確的問題馏臭,下面來看看

# 默認沒有寫的權(quán)限,無法編輯
guoqzuo-mac:~ kevin$ ls -l /etc/hosts 
-rw-r--r--  1 root  wheel  244  3 24  2019 /etc/hosts
# 新增寫的權(quán)限
guoqzuo-mac:~ kevin$ sudo chmod 0666 /etc/hosts
Password:
# 再次查看權(quán)限
guoqzuo-mac:~ kevin$ ls -l /etc/hosts 
-rw-rw-rw-  1 root  wheel  244  3 24  2019 /etc/hosts
# 使用vi修改該文件
guoqzuo-mac:~ kevin$ vi /etc/hosts
# 新增a.com解析讼稚,在本地將a.com解析到47.107.190.93的服務器括儒,也就是zuo11.com解析到的服務器
47.107.190.93 a.com

macOS修改host是實時生效的,修改后锐想,本地瀏覽器訪問a.com帮寻,會訪問47.107.190.93的服務器,顯示的是zuo11.com的內(nèi)容赠摇。這時用location.href就是a.com固逗,而不是zuo11.com浅蚪。下面是百度統(tǒng)計的數(shù)據(jù),顯示的就是a.com

a_com_#png

#2020/07/09 周四

#表格怎么畫斜線

在最近的需求中烫罩,有個表格表頭里有斜線惜傲,我特意翻了HTML5權(quán)威指南的筆記,發(fā)現(xiàn)并沒有介紹怎么畫表頭的斜線贝攒。找了下網(wǎng)上的實現(xiàn)盗誊,一般都是通過css來實現(xiàn),效果如下隘弊,在線預覽地址: 表格畫斜線 | github(opens new window)

table_slash.png

基本實現(xiàn)都是純css哈踱,有的用到的偽元素。我這里直接用了三個元素來實現(xiàn)梨熙,如果想要完全理解开镣,需要用到一點點數(shù)學方面的知識。

  1. 比如計算斜線的長度咽扇。勾股定理 a2 + b2 = c2
/* 斜邊邊長 */
/* Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) */
/* Math.sqrt(Math.pow(150, 2) + Math.pow(80, 2)) = 170 */

  1. 根據(jù)th單元格的寬高計算斜線的rotate角度邪财。給定直角三角形的邊長,怎么計算角度? 這里我們知道寬高肌割,不知道斜邊邊長,假設角度A卧蜓,正切tanA = 對邊(高) / 鄰邊(寬),我們知道這個角度A的正切值把敞,怎么反向計算A的角度呢弥奸。就需要用到反正切函數(shù) Math.atan了,他會返回一個弧度值奋早。在JS中 180度對應的值為 Math.PI盛霎,計算出來的值乘以 (180 / Math.PI) 就是可以在css中使用的度數(shù)了,單位為 deg
/* 角度計算公式 */ 
/*  Math.atan(height / width) * 180 / Math.PI  */
/*  Math.atan(80 / 150) * 180 / Math.PI  = 28.072486935852954 */

完整代碼如下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    /* 基本表格元素 */
    table {
      border-collapse: collapse;
    }
    th,td {
      border: 1px solid #666;
      padding: 5px;
    }

    /* th單元格 */
    .slash-wrap {
      position: relative;
      box-sizing: border-box;
      width: 150px;
      height: 80px;
    }

    /* 斜線 */
    .slash {
      position: absolute;
      display: block;
      top: 0;
      left: 0;
      /* 斜邊邊長 */
      /* Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) */
      /* Math.sqrt(Math.pow(150, 2) + Math.pow(80, 2)) = 170 */
      width: 170px;
      height: 1px;
      background-color:#000;
      /* 旋轉(zhuǎn)角度計算公式 */ 
      /*  Math.atan(height / width) * 180 / Math.PI  */
      /*  Math.atan(80 / 150) * 180 / Math.PI  = 28.072486935852954 */
      transform: rotate(28.072486935852954deg); 
      transform-origin: top left;
    }

    /* 左下角文字 */
    .left {
      position: absolute;
      /* 左下角 left:0; bottom: 0; */
      left: 15px;
      bottom: 15px;
    }

    /* 右上角文字 */
    .right {
      position: absolute;
      /* 右上角 right:0; top: 0; */
      right: 15px;
      top: 15px;
    }
  </style>
</head>

<body>
  <div>
    <table>
      <tr>
        <th class="slash-wrap">
          <span class="left">姓名</span>
          <span class="slash"></span>
          <span class="right">科目</span>
        </th>
        <th>語文</th>
        <th>數(shù)學</th>
      </tr>
      <tr>
        <td>張三</td>
        <td>89</td>
        <td>80</td>
      </tr>
      <tr>
        <td>李四</td>
        <td>89</td>
        <td>80</td>
      </tr>
    </table>
  </div>
</body>

</html>

參考:

#權(quán)限code狀態(tài)管理設計

一般使用vuex狀態(tài)管理耽装,提供一個getter方法獲取對應的角色權(quán)限愤炸,假設getter名為roleMuster,通過mapGetters導入掉奄,然后使用

let { role_admin, role_a, role_b, role_c } = this.roleMuster
// 如果有對應的權(quán)限规个,值則為true

在getter的邏輯里可以對角色對應的code進行轉(zhuǎn)換,使用好理解變量代替姓建,消除魔術(shù)字符串

#2020/07/08 周三

#pc樣式自適應rem

pc端如果做官網(wǎng)诞仓、展示類的UI,就需要使用rem了速兔。為了好還原UI圖墅拭,1rem通常設計為100px。rem是相對于html元素font-size來計算的涣狗,我們可以通過動態(tài)的改變html元素的fontSize谍婉,來實現(xiàn)頁面自適應

怎么動態(tài)設置html元素的font-size呢舒憾?來看看

export default {
  created() {
    const recalc = () => {
      let designSize = 1920
      let minWidth = 1280
      let html = document.documentElement
      let w = html.clientWidth < minWidth ? minWidth : html.clientWidth
      let rem = ( w / designSize) * 100
      this.rem = rem // 當前頁面 使用時寬高使用 this.rem * (設計稿標記尺寸/100)
      // html.style.fontSize = `${rem}px`  // 會影響所有頁面
    }
    this.recalc = recalc
    recalc()
    // 窗口變更后,變更rem
    window.addEventListener('resize', recalc)
  },
  // 組件銷毀時移除resize事件的recalc
  beforeDestroy() {
    window.removeEventListener('resize', this.recalc, false)
  }
}

這里就凸顯出 window.addEventListener('resize') 相對于 window.onresize 的優(yōu)勢穗熬,假設項目很大镀迂,其他代碼已經(jīng)監(jiān)聽了window.onresize如果你再用window.onresize可能會覆蓋原來的方法,要特別小心唤蔗,而window.addEventListener就不用擔心這個問題招拙,你只需要注意remove的時候,只移除你自己的監(jiān)聽函數(shù)即可措译。

#background設置圖片背景相關(guān)

HTML5權(quán)威指南這本書對background的簡寫貌似有點不正確,使用起來會有問題饰序,這次讓圖片在某個區(qū)域完全顯示领虹,是分開寫的,如下:

div {
  background: #fff url('/images/xxx.png') no-repeat;
  background-size: cover;
}

后面仔細測試下這塊

#移動端rem自適應

設計圖750 * xx(iphone 6/7/8 或 全面屏)求豫,以100為基準

function initRem() {
  let html = document.documentElement
  let resizeEventName = 'orientationchange' in window ? 'orientationchange' : 'resize'
  let recalc = () => {
    let w = html.clientWidth < 320 ? 320 : html.clientWidth
    let fontSize = w > 750 ? 200 : ((w / 375) * 100)
    Object.assign(html.style, { fontSize })
  }
  recalc()
  [resizeEventName, 'DOMContentLoaded'].map(eventName => {
    document.addEventListener(eventName, recalc, false)
  })
}

#2020/07/05 周日

#vue實現(xiàn)一個tree組件

樹形組件主要是遞歸的問題塌衰,組件自己調(diào)用自己,來寫個簡單的例子

<template>
  <div>
    <z-tree :data="treeData"></z-tree>
  </div>
</template>

<script>
export default {
  components: {
    ZTree: () => import("./ZTree")
  },
  data() {
    return {
      treeData: [
        {
          label: "冰箱"
        },
        {
          label: "水果",
          list: [
            { label: "蘋果" },
            { label: "梨子" },
            { label: "葡萄" },
            {
              label: "喜歡的水果",
              list: [{ label: "水果1" }, { label: "水果2" }, { label: "水果3" }]
            },
            { label: "香蕉" }
          ]
        },
        {
          label: "茶葉",
          list: [
            { label: "鐵觀音" },
            { label: "西湖龍井" },
            { label: "毛尖" },
            {
              label: "紅茶",
              list: [{ label: "紅茶1" }, { label: "紅茶2" }, { label: "紅茶3" }]
            }
          ]
        }
      ]
    };
  }
};
</script>

ZTree.vue實現(xiàn)

<template>
  <!-- z-tree遞歸組件實現(xiàn) -->
  <div>
    <ul>
      <li v-for="item in data" :key="item.label">
        {{ item.label }}
        <i class="iconfont el-icon-arrow-right" v-if="item.list"></i>
        <z-tree v-if="item.list" :data="item.list"></z-tree>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  components: {
    ZTree: () => import("./ZTree")
  },
  props: {
    data: {
      type: Array,
      required: true
    }
  },
  data() {
    return {};
  }
};
</script>

#select渲染上萬條數(shù)據(jù)卡頓的問題

一般下拉選擇時會使用select組件蝠嘉,但如果select數(shù)據(jù)過萬最疆,可能會產(chǎn)生卡頓,其實這種數(shù)據(jù)大的情況使用select就體驗很差了蚤告。

可以做成彈窗選擇或搜索框努酸,那怎么保持select有數(shù)萬條數(shù)據(jù)而不卡呢?我的理解是杜恰,使用觸底刷新获诈,滾動到底部時加載后面的內(nèi)容

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市心褐,隨后出現(xiàn)的幾起案子舔涎,更是在濱河造成了極大的恐慌,老刑警劉巖逗爹,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亡嫌,死亡現(xiàn)場離奇詭異,居然都是意外死亡掘而,警方通過查閱死者的電腦和手機挟冠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镣屹,“玉大人圃郊,你說我怎么就攤上這事∨冢” “怎么了持舆?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵色瘩,是天一觀的道長。 經(jīng)常有香客問我逸寓,道長居兆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任竹伸,我火速辦了婚禮泥栖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勋篓。我一直安慰自己吧享,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布譬嚣。 她就那樣靜靜地躺著钢颂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拜银。 梳的紋絲不亂的頭發(fā)上殊鞭,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音尼桶,去河邊找鬼操灿。 笑死,一個胖子當著我的面吹牛泵督,可吹牛的內(nèi)容都是我干的趾盐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼小腊,長吁一口氣:“原來是場噩夢啊……” “哼谤碳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起溢豆,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜒简,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漩仙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搓茬,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年队他,在試婚紗的時候發(fā)現(xiàn)自己被綠了卷仑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡麸折,死狀恐怖锡凝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垢啼,我是刑警寧澤窜锯,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布张肾,位于F島的核電站,受9級特大地震影響锚扎,放射性物質(zhì)發(fā)生泄漏吞瞪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一驾孔、第九天 我趴在偏房一處隱蔽的房頂上張望芍秆。 院中可真熱鬧,春花似錦翠勉、人聲如沸妖啥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迹栓。三九已至,卻和暖如春俭缓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酥郭。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工华坦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人不从。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓惜姐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椿息。 傳聞我的和親對象是個殘疾皇子歹袁,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容