一個月面試大廠,中廠,小廠的總結|2021 年中總結

推薦:兩年前端程序媛從0到18k的逆襲之路 | 2021年中總結css

image.png

求點贊+求分享+求評論,哦耶!!!(基本工資有一部分花在付費學習上)html

(文末有彩蛋。。。)。一個朋友,面試的小哥,天天早晨六點起牀學習,三本兩年多經驗拿了年包 50woffer。某大佬:前端

  • 你看不少,校招24k白菜價,誰又拿了大廠的offer。你會發現,他們不但學歷高,並且都十分的努力。我我的挺佩服的。vue

  • 學歷低的想拿大的offer,除了努力提高本身的技術,你別無選擇。捷徑有人能走,可是你想想這我的是否是你。node

1.Dear developers

6月徐來,年中總結,認識某人三本兩年多經驗拿了年包 50w 的 offer,經過相識,清晰了職業規劃,發展方向。2021年的時間進度條也已過半,咱們彼此或多或少經歷了不少不少不平凡的事發生。下面面試了一些大廠,中廠,小廠的經驗。webpack

每一個人都須要 有目標,有職業規劃,有夢想,有向上的心。(固然,若是你沒有,誰也不強求你)ios

image.png

在這裏感謝一下:冴羽大佬,字節強哥,浩月等大佬。git

簡歷發給他們後,只修改一次到達他們的認同(不過我卡學歷,除非某些方面很是優秀),我也發現了不少差距,因此接下來準備繼續學習(卷)。es6

2.簡歷總結

淘系:簡歷上,若有學歷硬傷,過不了簡歷評估;簡歷上不能廢話太多,你要是面試官你但願面試官問你哪些地方,你就往哪些地方寫;項目上也只是寫你作過的哪些事情,技術深度,實際的提高等;若是你有博客,文章網站等均可以寫一寫。github

字節:簡歷上,項目經驗不要大量寫業務描述,須要的是一些技術方面的亮點;區分與其餘人的優點,找個好的簡歷模板(這裏推薦 木及簡歷 百度一下就有),若是你是5年如下經驗的,若是一頁寫不完,要麼是大牛,要麼就是提煉不足,因此無論怎樣,提煉到一頁之內寫完。

一些普通的,你只須要在技能欄裏寫如:精通Vue技術棧便可,(面試官或hr就瞭解關於Vue項目你基本能掌握便可)。

image.png

image.png

簡歷:簡潔,主題鮮明。

簡歷細節:

  1. 性名,年齡,工做經驗,聯繫方式,學歷。
  2. 崗位,薪資面議,技能,工做經歷(倒敘書寫)。
  3. 技能就別寫普通的了,到必定技能可寫以下(只是個示例):
1. 熟練掌握JavaScript,TypeScript,Node,CSS以及ES6的新特性
2. 擅長Vue技術棧,以及其生態,某框架及其實現原理,源碼
3. 有良好的數據結構與算法,能熟練在項目中運用設計模式
4. 熟悉HTTP協議,網絡原理,webpack,babel等等
複製代碼
  1. 項目描述,如技術棧Vue+Vuex+axios+element ui+echarts+es6開發
  • (項目描述,時間點,遇到什麼問題,用什麼技術解決,取得了什麼效果)
  • (在項目中扮演的角色等,開源能力,知識筆記,博客)

3.細聊

  • 聊聊小程序,小遊戲,H5,app端,PC端(我作過)
  • 多端開發能力如何
  • 網站性能優化,瞭解瀏覽器實現原理
  • 瀏覽器緩存,計算機基礎知識,數據結構與算法
  • 瀏覽器事件循環
  • 前端工程化的:模塊化,組件化,規範化(eslint配置,文件,組件,函數,變量命,commit等),自動化
  • 項目目錄,規範,開發流程
  • HTTP:服務器的認識,三次握手/四次揮手,一個url輸入到瀏覽器產生界面發現了等
  • 單例模式
  • 閉包是什麼
  • 原型鏈是什麼
  • promise - api all,race,any等,async/await
  1. 編程能力解決單點難題
  2. 架構能力解決複雜,大項目的頂層設計
  3. 工程能力解決大規模人員參與的項目協做問題
  4. 領域知識解決特殊業務的系列問題
  • 問道: es6fill,includes,find,Object.keys()
  • 問道:防抖,節流
  • 問道:promise,let/const,axios,git flow,branch分支等
  • 問道: es5,es6的繼承
  • 問道:自動化包含:單元測試,ui測試,CI/CD,容器化,DockerFile,容器編排k8s監控sentry

HTML5的新標籤:header,sction,footer,aside,nav,main,article,figure,localStorage和sessionStorage,audio/video/svg等。

CSS3的:定位,static,relative,find,absoulte,sticky。

4.工資只是個大概估算(大概而已,別槓)

中小廠:

  • 初級:一線:7-20K;二線:5-15k
  • 中級:一線:15-25k;二線:12-23k
  • 高級:一線:20-40k;二線:15-35k

大廠:

  • 25-50k都有,看能力

image.png

水平(也只是個大概)

初級要:

  1. 負責業務系統前端模塊的設計與開發;
  2. 負責產品的需求分析,開發,測試,維護等各項工做;
  3. 承擔PC端和移動端的前端HTML5的開發任務;
  4. 總體頁面結構以及css樣式層的設計,優化;
  5. 完成頁面腳本程序編寫,實現各種頁面動態,交互效果;
  6. 可以理解後端架構,與後端工程師配合爲項目提供最優化的技術解決方案。

中級要:

  1. 負責所在項目需求實現與開發;
  2. 完成系統細節技術設計,完成核心代碼的編寫;
  3. 確保需求實現,知足項目設計規範,軟件編碼規範以及性能要求;
  4. 測試,系統測試等;
  5. 積極溝通,以確保功能實現按時,按質交付;
  6. 積極參與階段評審,知足項目過程質量需求,審覈和指導開發人員。

已知足,走向高級開發(我的帶領太小團隊):

  1. 負責大型系統的web前端開發
  2. 參與技術選型,推動應用和開發工做,支撐平臺架構設計與開發功能
  3. 提高系統的總體用戶體驗,推進前端技術的發展
  4. 爲提高團隊開發效率,提煉公共組件,創造實用工具
  5. 優化現有業務,開發流程
  6. 關注前端發展,應用行業新技術
  7. 團隊管理

筆記

模塊一:

ES6 Module和Commonjs區別:

  1. ES6 Module靜態引入,編譯時引入
  2. Commonjs動態引入,執行時引入
  3. 只有ES6 Module才能靜態分析,實現Tree-Shaking
let apiList = require('../config/api.js')
if(isDev) {
 // 動態也引入執行時引入
 apiList = require('../config/api.js')
}

import apiList form '../config/api.js'
if(isDev) {
 // 編譯時報錯,只能靜態引入
 import apiList from '../config/api_dev.js'
}
複製代碼

Scope Hosting:

// hello.js
export default 'hello'

// main.js
import str from './hello.js'
console.log(str)
複製代碼
  • webpack性能優化-產出代碼
  1. 小圖片base64編碼
  2. bundlehash
  3. 懶加載
  4. 提供公共代碼
  5. 使用CDN加速
  6. 使用production
  7. Scope Hosting
  • 代碼體積更小
  • 建立函數做用域更小
  • 代碼可讀性更好

babel:前端開發環境必備工具,同webpack,須要瞭解基本的配置和使用

環境搭建和基本配置:babel-polyfill,babel-runtime

環境搭建:.babelrc配置:presetsplugins

core-js,regenerator結合,如何按需引入babel-polyfillBabel 7.4以後棄用babel-polyfill,推薦直接使用core-jsregenerator

babel-polyfill按需引入:文件較大,只有一部分功能,無需所有引入,配置按需引入。

babel-polyfill的問題?

  1. 會污染全局環境;
  2. 若是作一個獨立的web系統,則無礙;
  3. 若是作一個第三方Lib,則會有問題;

babel-runtime不會污染全局環境:

  1. 基本配置
  2. 高級配置
  3. 優化打包效率
  4. 優化產生代碼
  5. 構建流程概述
  6. babel

細分:

拆分配置和merge;啓動本地服務;處理ES6;處理樣式;處理圖片;多入口;抽離css文件;抽離公共代碼;懶加載;處理JSX;處理Vue;webpack優化構建速度:優化babel-loaderIgnorePluginnoParsehappyPacKParalleIUglifyPlugin

前端爲什麼打包構建,好處?

  1. 體積更小(Tree-Shaking,壓縮,合併),加載更快
  2. 編譯高級語言或語法(TS,ES6+,模塊化,SCSS
  3. 兼容性和錯誤檢查(Polyfill,postcss,eslint

經過打包和構建,能夠統一,高效的開發環境;統一的構建流程和產出標準,集成公司構建規範(提測,上線等)。

模塊二:

module, chunk, bundle的區別?

  1. module各個源碼文件,webpack中一切皆模塊
  2. chunk多模塊合併成的,如entryimport()splitChunk
  3. bundle最終的輸出文件

loaderplugin的區別:loader模塊轉換器,如less-cssplugin擴展插件,如:HtmlWebpackPlugin

babelwebpack的區別?

  1. babel-js新語法編譯工具,不關心模塊化。
  2. webpack-打包構建工具,是多個loaderplugin的集合

如何產生一個lib?參考webpack.dll.js ouput library.

output: {
 // lib的文件名
 filename: 'lodash.js',
 // 輸出lib到dist目錄下
 path: disPath,
 library: 'lodash',
},
複製代碼

爲什麼Proxy不能被Polyfill?

  1. Class能夠用function模擬
  2. Promise能夠用callback來模擬
  3. Proxy的功能用Object.defineProperty沒法模擬

babel-polyfillbabel-runtime的區別

  1. babel-polyfill會污染全局
  2. babel-runtime不會污染全局
  3. 產生第三方lib要用babel-runtime

webpack如何實現懶加載

  • import()
  • 結合Vue React異步組件
  • 結合Vue-router``React-router異步加載路由

webpack優化構建速度(可用於生產環境)

  • 優化babel-loader
  • IgnorePlugin
  • noParse
  • happyPack
  • ParalleIUglifyPlugin

webpack優化構建速度(不用於生產環境)

  • 自動刷新
  • 熱更新
  • DIIPlugin

項目流程

  1. 項目分多人,多角色參與
  2. 項目分多階段
  3. 項目須要計劃和執行

需求分析:瞭解背景,質疑需求是否合理,需求是否閉環,開發難度如何,是否須要其餘支持,不要急於給排期。技術方案設計:1,求簡,不過渡設計;2,產出文檔,覆盤;3,找準設計重點,組件怎麼設計;4,組內評審;5,和RD,CRD溝通;6,發出會議結論。

完整項目流程:各個角色(需求分析),技術方案設計,開發,聯調,測試,上線。

如何保證代碼質量,開發,項目質量?

  1. 如何反饋排期
  2. 符合開發規範
  3. 寫出開發文檔
  4. 及時寫單元測試
  5. Mock API
  6. Code Review

聯調:1,和RD,CRD技術聯調;2,讓UE肯定視覺效果;3,讓PM肯定產品功能。

加需求:走需求變動流程,按規定走,發起項目組和leader的評審,從新評估排期。

測試:提測發郵件,抄送項目組,測試問題要詳細記錄。

有問題及時溝通,QA和FE天生信息不對稱,當面討論,讓QA幫你復現,須要特定設備才能復現。溝通,及時識別風險,及時彙報。

模塊三:

瞭解:CSS盒模型,DOM事件類,HTTP協議類,原型鏈類,面向對象類,通訊類,前端安全類,前端算法類;渲染機制類,JS運行機制,頁面性能,錯誤監控,業務能力,團隊協做能力,帶人能力。

佈局

浮動,絕對定位,flex-box,表格佈局,網格佈局。

浮動解決方案:
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
div {min-height: 100px;}
.left {float: left; width: 300px; background: red;}
.right {float: right;..}
.center { background: yellow; }

絕對定位: absolute
flex box
表格佈局
網格佈局
複製代碼

基本模型,content,padding, border, margin

height-content height, width-content width

標準模型,IE模型的區別:

  1. 計算的寬度和高度不一樣

IE模型:它的寬和高是計算:padding+border

CSS3的屬性,CSS如何設置這兩種模型,box-sizing:content-box;(標準模型) box-sizing:border-boxIE模型)

JS如何設置獲取盒模型的寬和高:dom.style.width/height,dom.currentStyle.width/height

兼容性更好一些:window.getComputeStyle(dom).width/heightdom.getBoundingClientRect().width/height

BFC(邊距重疊解決方案)塊級格式化上下文:BFC的原理,BFC的渲染規則:

  1. BFC這個元素的垂直方向的邊距會發生重疊
  2. BFC的區域不會與浮動元素的boxs重疊,清除浮動佈局
  3. BFC在頁面上是獨立的容器,外面的元素不會影響裏面的元素,裏面的元素也不會影響外面的元素
  4. 計算BFC高度的時候,浮動元素也會參與計算

如何建立BFC,給父元素添加:overflow: hidden;,overflow:auto;flow不爲none,設置浮動BFCposition:不是static/relative

使用BFC的場景:解決邊距重疊問題,解決清除浮動。BFC的元素不會與float元素相互重疊。

DOM事件:1.DOM事件的級別;2.DOM事件模型;3.DOM事件流;4.描述DOM事件捕獲的具體流程;5.Event對象的經常使用應用;6.自定義事件。

DOM事件類,事件級別:

DOM0 element.onclick = function(){}

DOM2 element.addEventListener('click',function(){}, false)

DOM3 element.addEventListener('keyup',function(){}, false)
複製代碼

DOM事件模型:捕獲,冒泡

事件流:瀏覽器在爲這個當前頁面與用戶作交互的過程當中,點擊屬標,傳到頁面上。

事件流:window對象,捕獲,目標元素(目標階段)-》冒泡,window對象。

描述DOM事件捕獲的具體流程:window-document-html-body-目標元素。(document.documentElement)

Event對象的經常使用:

event.preventDefault()阻止默認行爲;event.stopPropagation()阻止冒泡行爲;event.stopImmediatePropagation()event.currentTarget,當前所綁定的事件;event.target

自定義事件:

var eve = new Event('custome');
ev.addEventListener('custome',function(){
 console.log('custome');
});
ev.dispatchEvent(eve);

window.addEvenListener('click', function(){}, true);// 捕獲階段觸發
複製代碼

模塊四:

HTTP協議:

  1. HTTP協議的主要特色
  2. HTTP報文的組成部分
  3. HTTP方法
  4. POST和GET的區別
  5. HTTP狀態碼
  6. 什麼是持久鏈接
  7. 什麼是管線化

HTTP報文的組成部分

請求報文:請求行,請求頭,空行,請求體;響應報文:狀態行,響應頭,空行,響應體。請求行包含:http方法,頁面地址,http協議以及版本;請求頭包含:key-value值,告訴服務器端我要什麼內容。

HTTP協議類的主要特色:簡單快速,靈活,無鏈接,無狀態。

HTTP協議類,HTTP方法:GET,獲取資源,POST,傳輸資源,PUT,更新資源,DELETE,刪除資源,HEAD,得到報文首部。

HTTP協議類:POST和GET的區別:1.GET在瀏覽器回退時是無害的,而POST會再次提交請求;2.GET產生的URL地址能夠被收藏,而POST不能夠;3.GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置;4.GET請求只能進行url編碼,而POST支持多種編碼方式;5.GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會保留;6.GET請求在URL中傳送的參數是有長度限制的,而POST是沒有限制的;7.對參數的數據類型,GET只接受ASCII字符,而POST沒有限制;8.GET比POST更不安全,由於參數直接暴露在URL中,因此不是用來傳遞敏感信息的;9.GET參數經過URL傳遞的,POST放在Request body中。

HTTP狀態碼:

  • 1xx:指示信息,表示請求已接收,繼續處理;
  • 2xx:成功,表示請求已被成功接收;
  • 3xx:重定向,要完成請求必須進行更進一步的操做;
  • 4xx:客戶端錯誤,請求有語法錯誤或請求沒法實現;
  • 5xx:服務器錯誤,服務器未能實現合法的請求。
  • 200 ok: 客戶端請求成功
  • 206 Partial Content: 客戶發送了一個帶有Range頭的GET請求,服務器完成了它
  • 301 Moved Permanently: 所請求的頁面已經轉移至新的url
  • 302 Found: 全部請求的頁面已經臨時轉移至新的url
  • 304 Not Modified: 客戶端有緩衝的文檔併發出了一個條件性的請求

服務器告訴客戶,原來緩衝的文檔還能夠繼續使用

  • 400 客戶端請求有語法錯誤,不能被服務器所理解
  • 401 請求未經受權,這個狀態碼必須和www-Authenticate報頭域一塊兒使用
  • 403 對被請求頁面的訪問被禁止
  • 404 請求資源不存在
  • 505 服務器發送不可預期的錯誤,原來緩衝的文檔還能夠繼續使用
  • 503 請求未完成,服務器臨時過載或宕機,一段時間後可能恢復正常

持久鏈接:HTTP1.1版本 1.0不支持

HTTP協議採用「請求-應答」模式,當使用普通模式,即非keep-alive模式時,每一個請求/應答 客戶和服務器都要新鍵一個鏈接,完成以後當即斷開鏈接(HTTP協議爲無鏈接的協議)。

當使用keep-alive模式(又稱爲持久鏈接,鏈接重用)時,keep-alive功能使客戶端到服務器端的鏈接有效,當出現對服務器的後繼請求時,keep-alive功能避免了創建或從新創建鏈接。

管線化:

  1. 在使用非持久鏈接的狀況下,某個鏈接上消息的傳遞相似於請求1-響應1
  2. 使用持久鏈接狀況下:請求1-請求2-響應1-響應2

管線化:

  • 管線化機制經過持久鏈接完成,僅僅HTTP1.1支持此技術
  • 只有GET和HEAD請求能夠進行管線化,而post則有限制
  • 初次建立鏈接時不該啓動管線機制,由於對方(服務器)不必定支持HTTP/1.1 版本的協議
  • 管線化不影響響應到來的順序,響應返回的順序並未改變
  • HTTP/1.1要求服務器支持管線化,但並不要求服務端也對響應進行管理化處理,只要求對於管線化的請求不失敗便可
  • 因爲上面提到的服務器端問題,開啓管線化極可能並不會帶來大幅度的性能提高,並且不少服務器端和代理程序對管線化的支持並很差,所以現代瀏覽器如Chrome和FireFox默認並未開啓管線化支持

原型鏈類:建立對象幾種方法,原型,構造函數,實例,原型鏈,instanceof的原理。

建立對象的幾種方法:

// 字面量
var o1 = {name:'o1'};
var o2 = new Object({name:'01'});
// 經過構造函數
var m = function(){this.name='01'}
var m1 = new m();
// Object.create
var p = {name:'o3'};
var o3 = Object.create(p);
複製代碼

原型鏈:

構造函數,new,實例,構造函數-prototype-原型對象-__proto__原型對象,原型對象constructor構造函數,實例__proto__到原型對象。

原型鏈類:實例對象proto - instanceof - prototype - 原型,實例對象 -constructor - 構造函數。

模塊四:

new 運算符:

  1. 一個新對象被建立,它繼承自foo.protoype
  2. 構造函數foo被執行,執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定爲這個新實例,new foo等同於new.foo(),只能用在不傳遞任何參數的狀況。
  3. 若是構造函數返回了一個「對象」,那麼這個對象會取代整個new出來的結果。若是構造函數沒有返回對象,那麼new出來的結果爲步驟1建立的對象。

類與實例:類的聲明;生成實例。

類與繼承:如何實現繼承;繼承的幾種方式。

類的聲明:

function Animal() {
 this.name = 'name';
}

// es6 class
class Animal2 {
 constructor() {
  this.name = name;
 }
}
複製代碼

繼承的本質原型鏈:

  1. 藉助構造函數實現繼承
function Parent1() {
 this.name = 'parent';
}
function Child() {
 Parent.call(this);
 this.type = 'child';
}
複製代碼

缺點是獲取不到父類構造函數prototype上的方法。

  1. 藉助原型鏈實現繼承
function Parent2() {
 this.name = 'parent2';
}
function Child2() {
 this.type = 'child2';
}
child2.prototype=new Parent2();
複製代碼

原型鏈繼承方式的缺點:原型鏈中的原型對象,引用同一個對象,就是父類的實例對象。

第三種繼承方式:組合方式

function Parent3() {
 this.name = 'parent3';
 this.play = [1,2,3];
}
function Child3() {
 Parent3.call(this);
 this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child4();
s3.play.push(4);
複製代碼

解決1,2不足,缺點就是父級的構造函數執行了兩次。

第四種方式:結合繼承的優化,繼承父類的原型對象(原型式)

function Parent4() {
 this.name = 'parent4';
 this.play = [1,2,3];
}
function Child4() {
 Parent4.call(this);
 this.type = 'child4';
}
Child4.prototype = Parent4.prototype
// 數據類型:應用類型和值類型
// constructor指向
複製代碼

第5,組合繼承方式

function Parent5() {
 this.name = 'parent5';
 this.play = [1,2,3];
}
function Child5() {
 Parent5.call(this);
 this.play = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype); // __proto__
Child5.prototype.constructor = Child5;
// 構造函數指向Child5
複製代碼
  • DNS:根DNS,頂級DNS,權威DNS,本地DNS,負載均衡
  • 下層協議:可靠數據傳輸,四層與七層,TCP/IP,UNIX Domain Socket
  • 代理:正向代理,反向代理,Proxy協議。
  • URI/URL:協議名,查詢參數,編碼。
  • webSocket:全雙工,二進制幀,有狀態。
  • 編碼:Base64,chunked,壓縮(gzip,deflate)
  • 抓包工具:Wireshark,tcpdump
  • HTTP/3:基於UDP,QUIC,gRPC
  • HTTP/2:HPACK,Server Push,SPDY
  • HTTPS:對稱加密(AES,ChaCha),非對稱加密(RSA,DH),摘要算法(SHA-2),證書(X509,CA),SSL/TLS(SNI,OCSP,鏈接優化)。
  • CDN(負載均衡,就近訪問,Squid/Varaish/ATS
  • WAF:應用層防禦,訪問控制,審計

TCP三次握手,SYN,SYN.ACK,ACKTCP四次握手關閉鏈接:FIN,ACK/FIN,ACK

模塊五:

  1. 什麼是同源策略以及限制
  2. 先後端如何通訊
  3. 如何建立Ajax
  4. 跨域通訊的幾種方式

什麼是同源策略以及限制

同源策略限制從一個源加載的文檔或腳本如何與另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。

  1. Cookie, LocalStorage, IndexDB沒法讀取
  2. DOM沒法獲取
  3. AJAX請求不能發送

AJAX是同源下面的通訊方式,WebSocket不限制同源策略,CORS支持跨源通訊,也支持同源通訊。

如何建立Ajax

  1. XMLHttpRequest對象的工做流程
  2. 兼容性處理
  3. 事件的觸發條件
  4. 事件的觸發順序
  • Object.prototype.toString.call()
var util = {};
util.indexOf = function(array, item) {
 for(var i=0; i<array.length; i++) {
  if(array[i] === item) {
   return i;
  }
 }
 return -1;
};
// 判斷是否爲函數
util.isFunction = function(source) {
 return '[Object Function]` === Object.prototype.toString.call(source)
}
// 判斷是否是ie
util.isIE = function() {
 var myNav = navigator.userAgent.toLowerCase();
 return (myNav.indexOf('msie') !== -1) ? parseInt(myNav.split('mise')[1]) : false;
};
複製代碼

模擬ajax的功能:

util.json = function(options) {
 var opt = {
  utl: '',
  type: 'get',
  data: {},
  success: function() {}
  error: function() {}
 };
 util.extend(opt, options);
 if(opt.url) {
  var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
   var data = opt.data, url = opt.url,
   type = opt.type.toUpperCase(), dataArr=[];
  for(var k in data) {
   dataArr.push(K+'='+data[k]);
  }
  if(type === 'GET') {
   url = url + '?' + dataArr.join('&');
   xhr.open(type, url.replace(/\?$/g, ''), true);
   xhr.send();
  }
  if(type === 'POST'){
   xhr.open(type, url, true);
   xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
   xhr.send(dataArr.join('&'));
  }
  xhr.onload = function() {
   if(xhr.status === 200 || xhr.status === 304) {
    var res;
    if(opt.success && opt.success instanceof Function) {
     res = xhr.responseText;
     if(typeof res === 'string') {
      res = JSON.parse(res);
      opt.success.call(xhr.res);
     }
    }
   }else{
    if(opt.error && opt.error instanceof Function) {
     opt.error.call(xhr.res);
    }
   }
複製代碼

跨域通訊的幾種方式:

  • JSONP
  • Hash
  • PostMessage
  • WebSocket
  • CORS

jsonp的原理:

util.jsonp = function(url, onsuccess, onerror, charset) {
 var callbackName = util.getName('tt_player');
 window[callbackName] = function() {
  if(onSuccess && utils.isFunction(onSuccess)) {
   onSuccess(arguments[0]);
  }
 };
 var script = util.creatScript(url+'&callback='+callbaseName,charset);
 script.onload = script.onreadystatechange = function() {
  if(!script.readyState || /loader|complete/.test(script.readyState)){
   script.onload = script.onreadystatechange = null;
   // 移除該script的DOM對象
   if(script.parentNode){
    script.parentNode.removeChild(script);
   }
   // 刪除函數或變量
   window[callbackName] = null;
  }
 };
 script.onerror = function() {
  if(onerror && util.isFunction(onerror)) {
   onerror();
  }
 };
// script標籤的異步加載來實現的
<script src="http://xxx.com/?data = name & callback = 'jsonp'charset = 'utf-8'></script>
複製代碼
<script>
 jsonp({data:{}})
</script>
複製代碼

使用webSocket不受同源策略限制:

var ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = function(evt) {
 console.log('');
 ws.send('');
};
ws.onmessage = function(evt) {
 console.log('');
 ws.close();
};
ws.onclose = function(evt) {
 console.log('');
};
複製代碼

模塊五:

csrf, xss-CSRF:一般稱爲跨站請求僞造

Cross-site-request forgery縮寫CSRF,攻擊原理,防護措施。

CSRF防護措施:1.token驗證;2.Referer驗證;3.隱藏令牌

CSRF攻擊原理:依賴用戶點擊登陸,下發cookie,引誘點擊,訪問,指向的是網站A的api接口,特別是get類型。加token驗證,註冊成功之後,沒有手動上傳token,沒有帶token就避免了攻擊;Referer驗證,指頁面來源,是否來自我這個站點下的頁面,是的話執行動做,不是就攔截。

XSS-Cross-site scripting跨域腳本攻擊:XSS原理,向頁面注入腳本,防護措施,讓腳本不能執行

XSS和CSRF區別

  1. XSS是向你頁面注入JS去執行,而後JS函數體裏作它想作的事情
  2. CSRF是利用你自己的漏洞去幫助你主動執行那些接口,CSRF依賴於你這個用戶要登陸網站

算法:堆,棧,隊列,鏈表;JS的數組自己就具有堆,棧,隊列的特性,push, pop, shift, unshift

堆棧: 先進後出,先進先出,unshift進,pop出.

渲染機制

  1. 什麼是DOCTYPE及做用
  2. 瀏覽器渲染過程
  3. 重排Reflow
  4. 重繪Repaint
  5. 佈局Layout

DTD(document type definition,文檔類型定義)是一系列的語法規則,用來定義XML或(X)HTML的文件類型。瀏覽器會使用它來判斷文檔類型,決定使用何種協議來解析,以及切換瀏覽模式。

DOCTYPE是用來聲明文檔類型和DTD規範的,一個主要的用途即是文件的合法性驗證,若是文件代碼不合法,那麼瀏覽器解析時便會出一些差錯。

瀏覽器的渲染過程

HTML,HTML Parser,DOM Tree(DOM) (Style sheets-> CSS Parser-> Style Rules)->Attachment->Render Tree(Layout)->Painting->Display

Reflow重排

定義:DOM結構種的各個元素都有本身的盒子(模型),這些都須要瀏覽器根據各類樣式來計算並根據計算結果將元素放到它該出現的位置,這個過程稱爲reflow。

觸發reflow:

  1. 當你增長,刪除,修改DOM節點時,會致使Reflow或Repaint。
  2. 當你移動DOM的位置,或是搞個動畫的時候
  3. 當你修改css樣式的時候
  4. 當你Resize窗口的時候(移動端沒有這個問題),或是滾動的時候
  5. 當你修改網頁的默認字體時

重繪Repaint

當各類盒子的位置,大小以及其餘屬性,例如顏色,字體大小等會肯定下來後,瀏覽器因而把這些元素都各自的特性繪製了一遍,因而頁面的內容出現了,這個過程稱爲repaint。

運行機制:JS的單線程,一個時間以內,JS只能幹一件事;EventLoop事件循環。

任務隊列:同步任務,異步任務。

頁面性能

提高頁面性能的方法:

  1. 資源壓縮合並,減小HTTP請求
  2. 非核心代碼異步加載,異步加載的方式,
  3. 利用瀏覽器緩存
  4. 使用CDN
  5. 預解析DNS
<meta http-equiv='x-dns-prefetch-control` content='on'>
<link rel = 'dns-prefetch' href="//host_name_to_prefetch.com'>
複製代碼

CDN-讓網絡快速達到服務端,把文件下載下來,尤爲是當頁面第一次打開的時候,瀏覽器緩存是起不到任何做用的,那麼使用CDN是很是明顯的。

預解析DNS,若是你的頁面使用多個域名的時候,你的DNS解析是很是明顯的。

  • 頁面中的全部a標籤,在一些高級瀏覽器裏面是默認打開DNS預解析的,也就是說你不用加meta等這句話,也可使用DNS預解析的,可是若是你的頁面是https協議開頭的,不少瀏覽器默認是關閉DNS預解析的,須要強制打開DNS預解析。
  1. 非核心代碼異步加載
  2. 利用瀏覽器緩存

異步加載的方式:動態腳本加載;defer;async

異步加載的區別:

  • defer是在HTML解析完以後纔會執行,若是是多個,按照加載的順序依次執行。
  • async是在加載完以後當即執行,若是是多個,執行順序和加載順序無關。

模塊六:

瀏覽器緩存:

緩存的分類:強緩存/協商緩存

// 強緩存
Expires thXXX
Cache-Control: max-age
// 協商緩存
Last-Modified/If-Modified-Since
Etag/If-None-Match
複製代碼

緩存,瀏覽器緩存說的就是你個資源文件在瀏覽器中存在的這個備份,或說是副本。

強緩存就是我問都不問不直接請求,直接拿過來就用了;協商緩存就是瀏覽器發現我本地有這個副本,但我又不肯定用不用它,向服務器問一下,我這個文件要不要用,也就是協商和服務器協商一下,我這個能不能用,它是否是過時了,這個過程叫協商緩存。

強緩存:Expires:過時時間,值表示絕對時間;Cache-Control:Cache-Control:max-age=3600相對時間。

協商緩存:Last-Modified上次修改的時間,If-Modified-Since請求的時候我給服務器帶的,Etag解決:服務器下方的資源會給你Etag值,If-None-Match

1.0 缺點:雖然個人時間變了,可是個人內容沒有發生變化,內容沒有發生變化,我徹底能夠從副本中拿。

1.1 過了強緩存的時間,瀏覽器再去向服務器請求,問它,這個資源我可不能夠再用的時候,它會經過If-None-Matchhttp中會加一個這個If-None-Matchkey值,而後會加一個value,這個value就是服務器下方的Etag值。

緩存是爲了提升頁面性能優化的.

瞭解錯誤監控類:前端錯誤的分類,錯誤的捕獲方式,上報錯誤的基本原理。前端錯誤的分類:即時運行錯誤,代碼錯誤,資源加載錯誤。即時運行錯誤的捕獲方式:try...catch...window.onerror

資源加載錯誤:object.onerrorperformance.getEntries()Error事件

上報錯誤的基本原理:1. 採用ajax通訊的方式上報;2. 利用Image對象上報。

  • 業務能力
  • 思考能力
  • 學習能力
  • 付出

模塊七:

Vuex是一個專爲Vue服務,用於管理頁面的數據狀態,提供統一數據操做的生態系統。它集中於MVC模式中的Model層,規定全部的數據操做必須經過action-mutation-state,change的流程來進行,再結合Vue的數據視圖雙向綁定特性來實現頁面的展現更新。

統一的頁面狀態管理以及操做處理,可讓複雜的組件交互變得簡單清晰,同時可在調試模式下進行時光機般的倒退前進操做,查看數據改變過程,使code debug更加方便。

Vue Components-Dispath-Actions-commit-Mutations-Mutate-State-Render

  1. Vue Components,Vue組件,HTML頁面上,負責接收用戶操做等交互行動,執行dispath方法觸發對應action進行迴應。
  2. dispath,操做行爲觸發方法,是惟一能執行action的方法。
  3. actions,操做行爲處理方法,負責處理Vue Components接收到的全部交互行爲,包含同步/異步操做,支持多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操做就在這個模塊中進行,包括觸發其餘action以及提交mutation操做。該模塊提供了Promise的封裝,以支持action的鏈式觸發。
  4. commit,狀態改變提交操做的方法。對mutation進行提交,是惟一能執行mutation的方法。
  5. mutations:狀態改變操做方法,是Vuex修改state的惟一方法,其餘修改方式在嚴格模式下將會報錯,該方法只能進行同步操做,且方法名只能全局惟一。操做之中會有一些hook暴露出來,以進行state的監控等。
  6. state:頁面狀態管理容器對象。集中存儲Vue Components中data對象的零散數據,全局惟一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。
  7. getters:state對象讀取方法。被包含中render中,Vue Components經過該方法讀取全局state對象。

生命週期流程圖

new Vue新建Vue實例,初始化(事件&生命週期),beforeCreate,初始化(注入&校驗),created,是否指定'el'選項?是,是否指定'template'選項,是(將template編譯到render函數中),否(將el外部的HTML做爲template編譯);beforeMount,建立vm.$el,並用替換el,mounted,掛載完畢。(當data被修改時,beforeUpdate,虛擬DOM從新渲染並應用更新,updated),當調用vm.$destory函數時,beforeDestory,解除綁定銷燬子組件以及事件監聽器,destoryed

生命週期建立先後,掛載前/後,更新前/後,銷燬前/後。

  1. beforeCreate,在beforeCreate生命週期執行時,datamethods中的數據還未初始化,因此此時不能使用data中的數據和methods中的方法。
  2. createddatamethods初始化完畢,此時可使用methods中的方法和data中的數據。
  3. beforeMounttemplate模板已經編譯好,但還未掛載到頁面,此時頁面仍是上一個狀態。
  4. mounted,此時Vue實例初始化完成了,DOM掛載完畢,能夠直接操做dom或者使用第三方dom庫。
  5. beforeUpdate,此時data已更新,但還未同步頁面。
  6. updateddata和頁面都已經更新完成。
  7. beforeDestory,Vue實例進入銷燬階段,但全部的data和methods,指令,過濾器等都處於可用狀態。
  8. destoryed,此時組件已經被銷燬,data,methods等都不可用。

v-cloak主要是用來避免頁面加載時出現閃爍的問題,能夠結合CSS的[v-cloak]{display:none}方式解決這一問題。

組件之間,父子之間的通訊方案:

  • 經過事件總線bus,即經過發佈訂閱方式
  • vuex
  • 父組件經過prop向組件傳輸數據
  • 子組件綁定自定義事件,經過this.$emit(event.params)來調用自定義事件
  • 使用vue提供的parent children&refs方法來通訊

組件的按需加載是項目優化的一個環節,也能夠下降首屏渲染時間;使用()=>import();使用resolve=>require(['./componentA'], resolve)

keep-aliveVue的內置組件,能在組件切換過程當中將狀態保留在內存中,防止重複渲染DOM。chunkwebpack打包過程當中的Modules的集合,是打包過程當中的概念。

JSONP:ajax請求受同源策略影響,不容許進行跨域請求,而script標籤src屬性中的連接卻能夠訪問跨域的Js腳本,利用這個特性,服務端再也不返回JSON格式的數據,而是返回一段調用某個函數的js代碼,在src中進行進行了調用,這樣實現了跨域。

斐波那契數列:

function f(n) {
 if(n===0){
  return 0;
 }else if(n===1){
  return 1;
 }else{
  var fn1 = 0;
  var fn2 = 1;
  var fnx = 0;
  for(var i = 0; i< n-1; i++) {
   var newFn = fn2;
   fnx = fn1+fn2;
   fn1 = fn2;
   fn2 = fnx;
  }
  return fnx;
 }
}
複製代碼

遞歸算法:

function fib(count) {
 var count = parseInt(count);
 if(isNaN(count) || count<=0) {
  return 0;
 }
 function f(count) {
  if(count<=2) {
   return 1;
  }
  return f(count-1)+f(count-2);
 }
 return f(count);
}
複製代碼

求前20個數字:

var arr = [];
arr[1] = 1;
arr[2] = 2;
for(var i = 3; i<20; i++) {
 arr[i] = arr[i-1] + arr[i-2];
}
for(var i = 1; i<arr.length; i++) {
 console.log(arr[i]);
}
複製代碼

模塊八:

Vue實現數據雙向綁定的:

實現了一個監聽器Observer,對數據對象進行遍歷,包括了屬性對象,利用Object.defineProperty()屬性都加上settergetter。這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就監聽到了數據變化。

實現了一個解析器Compile,解析Vue模板指令,將模板中的遍歷都替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,調用更新函數進行數據更新。

實現一個訂閱者Watcher:Watch訂閱者是Observer和Compile之間通訊的橋樑,主要的任務是訂閱Observer中的屬性值變化的消息,當收到屬性值變化的消息時,觸發解析器Compile中的對應的更新函數。

實現一個訂閱器Dep,訂閱器採用發佈-訂閱設計模式,用來收集訂閱者Watcher,對監聽器Observer和訂閱者watcher進行統一管理。

算法

  • 棧,後進先出,push,pop
  • 隊列,先進先出,push,shift
  • 鏈表,結點,對元素的內存地址一無所知(每個結點的結構都包括了兩部分的內容,數據域和指針域)

示例:

{
 // 數據域
 val: 1,
 // 指針域,指向下一個結點
 next: { 
  val: 2,
  next:。。。
 }
}

// 鏈表
function ListNode(val) {
 this.val = val;
 this.next = null;
}
複製代碼
  • 二叉樹:根結點,子結點,葉子結點;葉子結點高度未1,等樹的高度;度表示一個結點開叉出去多少個子樹,被記爲結點的度;度爲0,表示爲葉子結點。
  • 二叉樹結構:1,它能夠沒有根結點做爲一顆空樹存在;2,若是它不是空樹,那麼必須由根結點,左子樹和右子樹組成且左右子樹度是二叉樹。3.二叉樹不能被簡單定義爲每一個結點的度度是2的樹。

二叉樹:在JS中,二叉樹使用對象來定義,它的結構分爲三塊:

  1. 數據域
  2. 左側子結點的引用
  3. 右側子結點的引用

二叉樹結點的構造函數:

function TreeNode(val) {
 this.val = val;
 this.left = this.right = null;
}
// 建立一個二叉樹結點時
const node = new TreeNode(1);
複製代碼

二叉樹遍歷:1.先序遍歷,2.中序遍歷,3.後序遍歷,4.層次遍歷(按照順序規則不一樣)

按照實現方式不一樣:遞歸遍歷;迭代遍歷(先中後序遍歷,層次遍歷)

建立一個由數據域,左右子樹組成的結點,每一顆二叉樹度應該由這三部分組成。

遞歸函數(遞歸式,遞歸邊界)

// 先序遍歷的遍歷實現
// 全部遍歷函數的入參都是根據結點對象
function preorder(root) {
 // 遞歸邊界,root爲空
 if(!root) {
  return
 }
 // 輸出當前遍歷的結點值
 console.log('當前遍歷的結點值' + root.val);
 // 遞歸遍歷左子樹:
 preorder(root.left);
 // 遞歸遍歷右子樹:
 preorder(root.right);
}

// 中序遍歷,左子樹,根結點,右子樹
function inorder(root) {
 if(!root) {
  return
 }
 // 遞歸遍歷左子樹
 inorder(root.left);
 // 輸出當前遍歷的結點值
 console.log('當前遍歷的結點是', root.val);
 // 遞歸遍歷右子樹
 inorder(root.right);
}

// 後序遍歷:左子樹,右子樹,根結點
function postorder(root) {
 if(!root) {
  return
 }
 // 遞歸遍歷左子樹
 postorder(root.left)
 // 遞歸遍歷右子樹
 postorder(root.right)
 // 輸出當前遍歷的結點值
 console.log('當前遍歷的結點值是:'+root.val)
}

時間複雜度/空間複雜度
複製代碼
  • 時間:算法對應的執行總次數的一個變化趨勢
  • 空間:內存增加的趨勢

求兩數求和問題

const twoSum = function(nums, target) {
 // 這裏我用對象來模擬map的能力
 const diffs = {}
 // 緩存數組的長度
 const len = nums.length
 // 遍歷數組
 for(let i = 0; i<len; i++) {
  // 判斷當前對應的target差值是否存在
  if(diffs[target-nums[i]] !== undefined) {
   // 如有對應差值,那麼答案get
   return [diffs[target-nums[i]], i]
  }
  // 若沒有對應差值,則記錄當前值
  diffs[nums[i]=i
 }
}
複製代碼

合併兩個有序數組:

const merge = function(nums1, m, nums2, n) {
// 初始化兩個指針的指向,初始化num1尾部索引k
let i = m-1, j = n-1, k = m+n-1;
// 當兩個數組都沒遍歷完時,指針同步移動
while(i>=0 && j>=0) {
 // 取較大的值,從末尾往前填補
 if(nums1[i] >= nums2[j]){
  num1[k] = nums1[i]
  i--;
  k--;
 }else{
  num1[k] = nums2[j]
  j--;
  k--;
 }
}
// nums2留下的狀況,特殊處理一下
while(j>=0){
 nums1[k] = num2[j];
 j--;
 k--;
}
};
複製代碼

字符串:反轉字符串:

const str = 'huang';
// 定義反轉後的字符串
const res = str.split('').reverse().join('');
console.log(res);
複製代碼

迴文字符串:正着讀和倒着讀都同樣

function isPalindrome(str) {
 // 先反轉字符串
 const reversedstr = str.split('').reverse().join('');
 return reversedstr === str
}
複製代碼

具備對稱性:

function isPalindrome(str){
 // 緩存字符串的長度
 const len = str.length
 // 遍歷前半部分,判斷和後半部分是否對稱
 for(let i = 0; i<len/2; i++) {
  if(str[i] !== str[len-i-1]){
   return false
  }
 }
 return true
}
複製代碼

鏈表的合併:

const mergeTwoLists = function(l1,l2) {
 // 定義結點,確保鏈表能夠被訪問到
 let head = new ListNode()
 let cur = head; // cur這裏就是我們那根「針」
 while(l1&&l2) {
  // 若是l1的結點值較小
  if(l1.val <= l2.val) {
   // 先串起來l1的結點
   cur.next = l1
   // l1指針向前一步
   l1 = l1.next
  }else{
   // l2較小時,串起l2的結點
   cur.next = l2
   // l2指針向前一步
   l2 = l2.next
  }
  // "針"在串起一個結點後,也會往前一步
  cur = cur.next
}
// 處理鏈表不等長的狀況
cur.next = l1 !== null ? l1 : l2
// 返回起始點
return head.next
}
複製代碼

單例模式

class SingleDog{
 show() {
  console.log('我是單例對象')
 }
 static getInstance() {
  // 判斷是否已經new過這個實例
  if(!SingleDog.instance) {
   SingleDog.instance = new SingleDog()
  }
  return SingleDog.instance
 }
}
複製代碼

閉包:

SingleDog.getInstance = (function() {
 // 定義自有變量instance
 let instance = null
 return function() {
  // 判斷自由變量是否爲null
  if(!instance) {
   instance = new SingleDog()
  }
  return instance
 }
})()
複製代碼

Vue原理:

  • 組件化和MVVM數據驅動視圖
  • 響應式原理
  • vdom和diff算法
  • 模板編譯/更新
  • 組件渲染過程
  • 前端路由

路由模式:hash/history; 路由配置:動態路由/懶加載

組件渲染過程:1. 初次渲染過程,2.更新過程,3.異步渲染過程。

解析模板爲render函數(或開發環境vue-loader),觸發響應式監聽data屬性,getter和setter,執行render函數生成vnode,path(elem, newNode);修改data,觸發setter,更新執行render函數生成newNode;$nextTick,彙總data的修改,一次性更新視圖,減小DOM操做次數,提升性能。

路由模式:hash變化會觸發網頁跳轉即瀏覽器的前進後退,hash變化不會刷新頁面,SPA必需的特色:hash永遠不會提交到server端。

history原理表現:用url規範的路由,但跳轉時,刷新頁面,history.pushState,window.onpopstate

組件化:組件化歷史,數據驅動視圖,MVVM。響應式:Object.defineProperty,監聽對象(深度),監聽數組,Object.defineProperty的缺點,Vue.set, Vue.delete,原生數組。

vdom和diffVnode結構,Snabbdom度。模板編譯:with語法,模板編譯爲render函數,執行render函數生成vnode

Vue組件如何通信:父子組件propsthis.$emit,自定義事件event.$on/event.$off/event.$emit/Vuex

雙向數據綁定v-model的實現原理:

  • input元素的value=this.name
  • 綁定input事件:this.name = $event.target.value
  • date更新觸發:re-render

computed的特色:緩存,data不變,提升性能。什麼時候使用異步組件:加載大組件,異步加載。什麼時候使用deforeDestory,解綁自定義事件:event.$off,清除定時器,解綁自定義事件。

vue優化:

  • 合理使用v-showv-if
  • 合理使用computed
  • v-for時加key,以免和v-if同時使用(v-for優先級更高一些,每次v-for都須要使用v-if,這些性能是一種消耗。)
  • 自定義事件,DOM事件及時銷燬
  • 合理使用異步組件,合理使用keep-alivedata層級不要太深
  • 使用vue-loader,在開發環境作編譯(預編譯),前端通用的性能優化,如圖片懶加載,使用SSR。

什麼是併發/並行

  • 併發是指一個處理器同時處理多個任務。
  • 並行是指多個處理器或者是多核處理器同時處理多個不一樣的任務。
  • 併發是邏輯上的同時發生,並行是物理上的同時發生。
  • 併發是一我的同時吃三個饅頭,而並行是三我的同時吃三個饅頭。

垃圾回收:1,引用計數垃圾收集;2,標記-清除。

找出數據中重複的元素:

function fun(arr) {
 var a = arr.sort();
 var result = [];
 for(var i = 0; i < arr.length; i++) {
  if(arr[i] === arr[i+1] && result.indexOf(arr[i]) == -1) {
   result.push(arr[i]);
  }
 }
 return result;
}
複製代碼

new操做:

var obj = new Da();
var obj = {};
obj.__proto__ = Da.prototype;
Da.call(obj);
複製代碼

深刻淺出防抖函數debounce,實現原理:

防抖函數debounce指的是某個函數在某段時間內,不管觸發了多少次回調,都只執行一次。假如咱們設置了一個等待時間3秒的函數,在這個3秒內若是遇到函數調用請求就從新計時3秒,直到新的3秒內沒有函數調用請求,此時執行函數,否則就以此類推從新計時。

實現原理就是利用定時器,函數第一次執行時設定一個定時器,以後調用時發現已經設定過定時器就清空以前的定時器,並從新設定一個新的定時器,若是存在沒有被清空的定時器,當定時器計時結束後觸發函數執行。

判斷:

  • instanceOf
  • Constructor
  • Object.prototype.toString.call()

繼承我總結了:有1,原型鏈繼承,2,借用構造函數繼承,3,組合繼承,4,原型式繼承,5,寄生式繼承,6,寄生組合式繼承。es6中新增的class和extends語法,用來定義類和實現繼承,底層採用了寄生組合式繼承。

this的指向問題:

  • 屬性事件的this,在標籤內調用事件函數,誰調用this所在的函數,那麼this就指向誰
  • 直接在fn函數中寫this(若是直接在fn函數中寫this,那麼this爲將根據其上下文來決定,通常指向window
  • onclick事件中的this

遍歷:

  • Object.keys(obj)
  • Object.values(obj)

你懂得越多,考慮的就越全面。

❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更好的文章

點贊、收藏和評論

我是Jeskson(達達前端),感謝各位人才的:點贊、收藏和評論,咱們下期見!(如本文內容有地方講解有誤,歡迎指出☞謝謝,一塊兒學習了)

我的網站:1024bibi.com/

咱們下期見!

github收錄,歡迎Stargithub.com/webVueBlog/…

特別注意:請在文末添加: 掘金年中主題活動 | 2021 個人半程成長之路徵文活動正在進行中......

相關文章
相關標籤/搜索