前言
又到了金三銀四的找工作季啦。我是一個入行一年多的前端菜鳥,去年夏天開始考慮換工作,陸續面試了幾家中小公司。面試過程我一般會錄音,方便結束后進行復盤。整理了幾次面試復盤的筆記,希望對相似情況的小伙伴有所幫助「 也是方便自己以后回憶」,願大家都能找到心儀的工作。
概況
- 公司:坐標上海,教育行業,前端團隊 20人+。
- 面試官:前端負責人,前阿里P7大佬,碩士畢業轉行進入阿里。
- 技術棧:團隊目前主要使用 vue,后期打算采用 react。
- 面試結果:未通過 o(╥﹏╥)o 。
- 面試感受:面試官和善,遇到不會的問題會適度引導,最后還給了提高建議,感覺收獲滿滿。明明被虐得很慘,但還是很開心 。
面試題
說明:問題末尾的數字代表對自己當時回答的打分,✔ 代表后面有關於該問題的重新整理。
- 轉行原因
- vue 和 react 用起來有什么區別?2 ✔
- vue雙向數據綁定的原理? 數組的更新。8 ✔
- 組件間通信的方式?vuex實現原理?state改變,怎么促使視圖改變?6 ✔
- 為什么選用uni-app?8
- (搜索查看電商小程序后)你做的哪些部分?圖片加載問題,橫向為什么沒有也用intersection observer呢? Intersection Observer是瀏覽器自帶的。7 ✔
- 為什么要做直播插件?8
- 做過哪些項目優化?5 ✔
- webpack配置,loader和plugin區別。如何去掉冗余的代碼? tree-shaking實現原理。5 ✔
- 一個新的路由,怎么知道下載對應的文件?(擴展: 動態插入js的方式) 2 ✔
- 按需加載?有哪些方式?最后選擇了什么?5
- 緩存,強緩存是誰設置的?html文件會緩存嗎?4 ✔
- cdn內容分發系統?為什么你們項目的靜態資源文件沒有放到CDN上。5 ✔
- 使用騰訊雲的移動直播、即時通訊sdk,有什么困難的地方?4 ✔
- sentry錯誤監控,錯誤是如何收集上報到平台的?(http, websoket, 跨域)2 ✔
- 某某項目(簡歷中的),具體講解一下?8
- 項目里遇到難點、挑戰。 4
問題2 Vue vs React
相同點:
- 使用virtural DOM + Diff算法。
- 組件化思想。
不同點:
- 模板語法的不同:react通過JSX渲染模板,vue通過拓展的html語法進行渲染。比如react中插值、條件、循環都通過JS語法實現,vue是通過指令v-bind、v-if、v-for實現的。
- 監聽數據變化原理不同:vue通過getter、setter劫持通知數據變化,react通過比較引用的方式進行。vue使用的響應式數據,而react是不可變數據。vue改變數據直接賦值,react需要調用setState方法(用新的state替換舊的state)。
問題3 vue響應式原理
level1: vue2.0中,響應式實現的核心就是 ES5的Object.defineProperty(obj, prop, descriptor). 通過Object.defineProperty()劫持data和props各個屬性的getter和setter,getter做依賴收集,setter派發更新。整體來說是一個 數據劫持 + 發布-訂閱者模式。
level2: 具體來說, ① vue初始化階段(beforeCreate之后create之前),遍歷data/props,調用Object.defineProperty給每個屬性加上getter、setter。② 每個組件、每個computed都會實例化一個watcher(當然也包括每個自定義watcher),訂閱渲染/計算所用到的所用data/props/computed,一旦數據發生變化,setter被調用,會通知渲染watcher重新計算、更新組件。
問題4 vue組件通信
level1: props+events 父子組件通信(parent/parent/parent/children),vuex 任何組件通信,事件中心emit/emit / emit/on 任何組件的通信, attrs/attrs/attrs/listeners 后代通信(provide / inject)。
level2: vuex運行機制:vuex的state作為一個倉庫,提供數據驅動vue component渲染。視圖通過dispach派發actions,actions中可以做一些異步操作。actions或者視圖通過commit提交給mutations,mutation去改變state。
level3: 源碼分析:vuex其實是一個Vue.js插件,插件都需要提供一個install方法,install方法調用會將Vue作為參數傳入。Vue.user(plugin)安裝插件,也就是執行插件的install方法。會在全局混入一個beforeCreate鈎子函數,把示例化的Store保存到所有組件的this.$store中。
level4: mutation改變state, 會觸發視圖改變的原因?通過vue實現的,[實例化vue,把state作為一個data屬性。] ↔️ 核心
let Vue
function install(_Vue) {
Vue = _Vue
function vuexInit() {
const options = this.$options
console.log('vuexInit -> this.$options', this.$options)
if (options.store) {
// // 根實例 this --> Vue
this.$store =
typeof options.store === 'function' ? options.store() : options.store
} else if (options.parent && options.parent.$store) {
// 組件實例 this --> VueComponent, 如 APP, Home, About...
this.$store = options.parent.$store
}
}
Vue.mixin({ beforeCreate: vuexInit })
}
class Store {
constructor(options = {}) {
const { state = {}, mutations = {}, getters = {} } = options
this._mutations = mutations
// getter實現原理
const computed = {}
this.getters = {}
for (let [key, fn] of Object.entries(getters)) {
computed[key] = () => fn(this.state)
Object.defineProperty(this.getters, key, {
get: () => this._vm[key]
})
}
this._vm = new Vue({
data: { $$state: state }, // 核心原理
computed
})
}
commit(type, payload) {
if (this._mutations[type]) {
this._mutations[type](this.state, payload)
}
}
get state() {
return this._vm._data.$$state
}
}
export default { Store, install }
問題6 Intersection Observer
level1: 推斷節點是否為用戶可見,以及多少比例可見,交叉觀察器。web API中也有。
level2: 小程序中的IntersectionObserver在Web的基礎上,做了一些封裝,通過createIntersectionObserver返回一個IntersectionObserver實例。實例方法包括relativeTo、relativeToViewPort、observer、disconnect。relativeTo和relativeToViewPort,相對指定元素或視口的指定位置。observer(selector, cb),觀察指定節點,可見性發生變化時觸發回調。
level3: Web API, new IntersectionObserver(cb, options)。options里root、rootMargin的配置,可實現小程序中relativeTo、relativeToViewPort方法的效果。另外 thresholds 屬性也蠻有趣的,在指定的幾個相交比例時觸發回調。
問題8 項目優化
- 移除生產環境的控制台打印。方案很多,esling+pre-commit、使用插件自動去除,插件包括babel-plugin-transform-remove-console、uglifyjs-webpack-plugin、terser-webpack-plugin。最后選擇了terser-webpack-plugin,腳手架vue-cli用這個插件來開啟緩存和多線程打包,無需安裝額外的插件,僅需在configureWebpack中設置terser插件的drop_console為true即可。最好還是養成良好的代碼習慣,在開發基本完成后去掉無用的console,vscode中的turbo console就蠻好的。
- 第三方庫的按需加載。echarts,官方文檔里是使用配置文件指定使用的模塊,另一種使用babel-plugin-equire實現按需加載。element-ui使用babel-plugin-component實現按需引入。
- 公有樣式,比如對element-ui部分組件(如彈框、表格、下拉選框等)樣式的統一調整。公共組件,比如date-picker、upload-file等在element-ui提供的組件基本上做進一步的封裝。自定義組件包括preview-file、搜索框等。
// babel.config.js配置如下:
plugins: ['equire']
// echarts.js
const echarts = equire(['line', 'tooltip', 'legend', 'dataZoom', 'grid']);
export default echarts;
前后端數據交換方面,推動項目組使用藍湖、接口文檔,與后端同學協商,規范后台數據返回。
雅虎軍規提到的,避免css表達式、濾鏡,較少DOM操作,優化圖片、精靈圖,避免圖片空鏈接等。
性能問題:頁面加載性能、動畫性能、操作性能。Performance API,記錄性能數據。
winter重學前端 優化技術方案:
- 緩存:客戶端控制的強緩存策略。
- 降低請求成本:DNS 由客戶端控制,隔一段時間主動請求獲取域名IP,不走系統DNS(完全看不懂)。TCP/TLS連接復用,服務器升級到HTTP2,盡量合並域名。
- 減少請求數:JS、CSS打包到HTML。JS控制圖片異步加載、懶加載。小型圖片使用data-uri。
- 較少傳輸體積:盡量使用SVG\gradient代替圖片。根據機型和網絡狀況控制圖片清晰度。對低清晰度圖片使用銳化來提升體驗。設計上避免大型背景圖。
問題9 loader、plugin、tree shaking
loader
對模塊的源代碼進行轉換,將不同的語言轉換為JS,或將內聯圖像轉換為data url。如:文件,url-loader、file-loader。轉換編譯,babel-loader、ts-loader。模板,html-loader。樣式,style-loader、css-loader、less-loader。清理,eslint-loader。框架,vue-loader。
plugin
解決loader無法實現的其他事兒。比如 HtmlWebpackPlugin、CleanWebpackPlugin、webpack-bundle-analyzer、DllPlugin、HotModuleReplacementPlugin。
tree shaking
消除無用的js代碼(剔除模塊中沒有導出或引用的部分)。僅支持ES Module靜態引入方式,不支持require運行時動態引入方式。
ES6模塊引入是靜態分析的,故而可在編譯時正確判斷加載哪些代碼。
可剔除的內容有限。webpack配合uglifyJS打包文件,只能shaking部分代碼,像模塊代碼存在副作用,立即執行函數等都不能shaking。uglifyJS不進行程序流分析,只簡單判斷變量后續是否被引用、修改,不去排除有可能有副作用的代碼。(rollup會)還有,比如項目中router.js引用了頁面組件,但是在路由渲染中沒有用到,也無法shaking掉。
webpack-deep-scope-analysis-plugin:利用webpack解析出來模塊的AST,利用scoped分析工具解析引用關系,排除掉沒有用到的模塊。
1. babel.config.js 配置,分析文件模塊依賴關系,生成AST時,保持ES6不動。
{
"presets": [ ["env", { "modules": false }] ]
}
2. 方式1,import {Button} from 'antd';
方式2,import {Buttion} from 'antd/lib/button';
import 'antd/lib/style';
這兩種方式,tree-shaking效果差別很大。(副作用范圍不同)
babel-plugin-import-fix 插件,遍歷AST找出類似import {Button} from 'antd'的結構,進行轉換重新生成代碼。
3. CSS tree-shaking <https://juejin.im/post/6844903808397475847>
方式1:mini-css-extract-plugin + purifycss-webpack
方式2:webpack-css-treeshaking-plugin。
利用postCSS提供的解析器,將CSS解析成AST,遍歷獲取選擇器與js、html代碼匹配,刪除匹配不到的,返回AST,重新生成代碼。
問題10 懶加載路由
考點,vue-router實現原理。
level1:
- 將需要懶加載的子模塊,打包成單獨的文件。ES6的import()。
- hashChange時,根據hash變化執行特定的函數,加載子模塊。
level2: 實現的三種方式,location.hash + hashChange(),HTML5規范的pushState(IE10) + popState事件監聽,abstract nodejs默認值。
level3:源碼分析。路由安裝,利用mixin給每個組件注入beforeCreated和destory鈎子函數,在Vue原型上定義route和route和route和router,並進行響應式處理,定義全局的roter-link和router-view組件。根據路由配置創建映射關系。根據傳入路徑計算出新的路徑,在路勁切換過程中,執行一系列的導航守衛函數,更改Url,渲染對應組件。
問題12 緩存
html文件也會緩存。項目中使用index.php,后端返回html內容,不會被緩存。
瀏覽器緩存策略:
-
強制緩存:(在指定時間內,瀏覽器直接使用強緩存的內容)
Expires:Thu.21 Jan 2019 23:59:59 GMT; (HTTP1.0)
Cache-Control:max-age=3600(HTTP1.1,優先級更高)
【緩存指令:no-cache需要協商緩存來驗證是否過期;no-store不緩存;public客戶端代理服務器都可以緩存;private客戶端可緩存】
-
協商緩存:(與服務器協商,確定資源是否更新)
Last-Modified(服務器下發時間):Thu.21 Jan 2018 23:59:59 GMT;(HTTP1.0)
If-Modified-Since(瀏覽器詢問) 【可能時間變了,內容沒變】
Etag(服務器下發); (HTTP1.1)
If-None-Match(瀏覽器詢問)
問題13 CDN
CDN,內容分發網絡,是建立再承載網基礎上的虛擬分布式網絡,能夠將源站內容緩存到全國或全球的節點服務器上。用戶就近獲取內容,提高了資源的訪問速度,分擔源站壓力。
使用DNS域名解析引導用戶來訪問cache服務器。
問題14 騰訊雲SDK難點
封裝IM類,在IM類上定義創建SDK實例,登錄/退出,群組加入/退出,IM事件監聽和移除。
- 移動直播SDK。最開始沒有做小程序主播端,使用APP推流。主要做PC和H5拉流這塊內容。
- 直播協議有以下幾種...。
- 最開始使用的HLS協議,使用騰訊雲提供的TC-Player作為播放器。遇到的問題,延遲高。
- 考慮更換協議,但是移動端瀏覽器不支持flv、rtmp。
- 騰訊雲web端拉流提供了TC-Player插件,TC-Player本質上就是利用瀏覽器自帶的video標簽來做的。
- 使用flv作為直播協議,但不直接使用flv格式播放。github兩種解決方案,一種是bilibili flv.js,將flv文件流轉碼復用成MPE隨便,通過Media Source Extensions API實現視頻的播放。另一種解決方案,是將視頻的每一幀畫面使用CANVAS繪制。
問題15 sentry
sentry錯誤日志通過https發送到sentry的web站點。使用cors實現跨域。
- 應用程序(Sentry客戶端SDK) 消息上報 web端
- 消息處理 放入消息隊列(Redis/Rabbitmq)
- worker從消息隊列中取數據進行處理
- 最后由postgresql完成消息存儲
**request headers:**
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross site
**response headers:**
access-control-allow-origin: <http://localhost:8080>
access-control-expose-headers: x-sentry-error, retry-after, x-sentry-rate-limits
面試被虐之后我開始猛刷面試題,並且做了一整理,免費分享給大家,希望能帶來好運吧。