一位老程序員的心得分享,WEB前端菜鳥,感覺很迷茫,該怎麼做?

我們可以把學習路線比作遊戲中的段位上分,在不同的分段都有自己的定位和要鍛鍊的事情:

1、青銅 - 從零開始小學生:懷着滿腔的熱血,看到了這一個行業的希望和未來,準備開始學習 Web 開發知識。
a. 先通過 w3cschool 等免費學習資源把 HTML、CSS 和 JavaScript 的基本操作學會了
b. 寫一個簡單的表白頁面送給你的女/男朋友,展示一下自己努力的成果,如果沒有就當我沒說

2、白銀 - 懵懵懂懂初學者:懂得如何使用 HTML、CSS 和 JavaScript 三大件來實現基本頁面開發功能
a. 選擇一個可以覆蓋多種場景、可以隨自己意願調整難度的項目嘗試實現,如博客系統、記賬本、Markdown 編輯器等
b. 從 React 和 Vue 這兩個框架中選擇一個進行學習

3、黃金 - 輕車熟路新玩家:懂得使用框架來實現上面所舉例項目
a. 學習 Redux、Vuex 或者 MobX 等狀態管理工具,並將他們使用到前面的項目中
b. 思考狀態管理工具爲你的項目帶來了什麼好處

4、鉑金 V - 初出茅廬新司機: 懂得如何使用腳手架創建項目,並且能將代碼結構根據模塊化的思想進行安排
a. 學習 TypeScript,對前面的項目進行重寫,注重對數據結構和類型的控制
b. 學習 Node.js,試着配合數據庫實現一個比博客系統更爲複雜的 CMS(內容管理系統),如 圖書館管理系統、倉庫管理系統

5、鉑金 I - 基本上手好司機:如果是懂得如何利用 Node.js 或 TypeScript 編寫業務代碼的
a. 思考在前面使用框架開發的項目中,有哪些代碼是重複冗餘的,有哪些邏輯是可以在多個組件之間共用的
b. 學習利用 ES2015 或更新的 JavaScript 標準,逐步替換使用框架所編寫的代碼

6、鑽石 V - 淡定自然老司機:如果是對邏輯抽象、模塊封裝有了一定的理解和經驗的
a. 思考如何使用純 JavaScript 對業務組件中的非渲染、非 DOM 相關代碼進行抽象
b. 引入單元測試工具,對純邏輯代碼進行測試,爭取覆蓋率達到 80% 以上

7、鑽石 I - 賽道新手初學者:如果上面的條件你都已經滿足了
a. 思考不同的代碼哲學(OO、FP 等)、不同的代碼結構(MVC、MVVM 等)的區別
b. 思考不同的框架之間設計的初衷,思考不同的編程語言中對同一類問題不同解法的區別

到這裏我劃了一條從 0 到高級前端工程師級別的純技術路線。相信有不少有經驗的同學會發現中間我省略了不少內容,但也不難發現路線中從前半段的“學習”逐步變成後半段的“思考”。優秀的工程師除了需要有在純技術領域的沉澱以外,還需要更多對技術、團隊、ROI(投資回報率)的思考,當然這依然不足以支撐我們平穩地渡過“程序員 35 歲危機”,前面的路還有很長,鑽石往上還有王者呢,誰說程序員就是青春飯碗的?
回想起很多年前我也跟你一樣是一個完全的新手,從 0 開始慢慢自學摸索 Web 開發,甚至後來我也沒有進入科班學習計算機,那麼來聽聽我作爲一個“前人”是如何完全靠自學至今的故事吧。

本次給大家推薦一個免費的學習蔻qun,前面603 中間985最後993,
概括應用網站開發,css,html,JavaScript,jQuery,Vue、Ajax,node,
angular等。對web前端開發技術感興趣的同學,
不管你是小白還是大牛我都歡迎,每天技術分享。

我的從 0 開始

我是一個完全從自學開始的前端工程師,想起來第一次接觸前端就是初中那會特別流行合租 VPS 然後註冊一個 .tk 的免費域名。而作爲一個剛入門 Web 開發不久的小屁孩來說,用這種方式一探“大人的世界”屬實讓人興奮。而當時最流行的博客管理軟件就是用 PHP 寫的 WordPress,作爲一個十分成熟的 CMS 軟件來說 WordPress 當時就有了非常豐富的社區資源,比如主題、模板、插件等等。而作爲一個十分注重個性化的小屁孩來說,當然是要自己做一個主題的啊!於是我就從此踏上了 Web 開發的不歸路,在此之前我所接觸的都是 Visual Basic 這樣的 Native 的語言。
以 WordPress 主題作爲切入點,我開始學習 PHP 用於調用 WordPress 的 API 並輸出內容、學習 HTML 用於寫主題的模板、學習 CSS 用於“裝潢”我的博客、學習 jQuery 用於實現頁面動態效果。是的,那個時候基本上大部分人接觸的是 jQuery 而不是 JavaScript,一個 $ 函數就可以完成非常多的效果這讓我第一次感受到了“框架”所帶來的價值。於是便一步一步地發生了以下事情(不一定完全對,畢竟時間過太久了):

我發現頁面上的一些樣式效果無法在 IE 瀏覽器上正常顯示,於是我就開始到網上學習 CSS 在 IE 的各種特殊處理,包括 reset.css、normalize.css 等工具的使用;
每次點擊鏈接都要刷新頁面,在那個網速不怎麼好的年代體驗非常糟糕,於是乎就開始研究怎麼用 jQuery/JavaScript 實現不需要刷新頁面的情況下切換頁面的內容;
a. 通過查看文檔發現瀏覽器支持一種叫做 XMLHTTPRequest 的技術,可以讓我們不需要通過跳轉的方式從服務器獲取到信息,從這裏開始瞭解到 HTML、XML 和 JSON 三種不同格式的區別;
b. 第一次知道了可以通過服務器傳遞 JSON 格式的純數據,然後前端通過 JavaScript 對數據進行解析,並且結合前端的模板引擎渲染成完整的 HTML;
c. 從這裏又可以學習到如何通過 URL 中的 path、query、hash 以及 POST 和 PUT 請求正文等信息向服務器傳遞信息,服務器通過這些信息動態地對各種數據進行處理並返回結果;
d. SPA(Single Page Application)開發習慣初見雛形;

這樣我就來到了“白銀”階段了。

接觸 Node.js

當我正在愉快地設計着 WordPress 的自定義主題時,偶然間我在某前端網站上了解到了一個新的技術 —— Node.js。與它的相遇改變了我以後的學習路徑,影響至今。2009 年 Ryan Dahl 發佈了一個基於 Chrome JavaScript V8 引擎開發的程序運行環境 Node.js,它允許開發者在除了瀏覽器以外的地方運行 JavaScript 語言,並且提供一些標準庫允許 JavaScript 腳本啓動進行啓動一個 HTTP 服務端應用這種以前在瀏覽器無法完成的事情。

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

這一份代碼是 2010 年寫在 Node.js 官網的一段實例代碼,機緣巧合之下我被這麼一段簡單的代碼深深地吸引住了,雖然當時安裝它仍需要從 GitHub 上克隆整個項目代碼到本地並依次運行以下指令:

$ ./configure
$ make
$ make install

這一次編譯就得花上至少十分鐘,但完成安裝後運行上面的一段代碼,並在瀏覽器中打開 http://127.0.0.1:8124/,然後在瀏覽器上看到 Hello World 字樣時彷彿新世界的門打開了。因爲當時我所接觸過的服務端程序只有 PHP,而 PHP 本質上就是一個模板引擎,它並不能很直觀地處理請求本身而是藉助 CGI 進行響應。能做更多的事情,這件事情對剛學習編程不久的新手來說是具有很大誘惑力的。
從這裏開始,Node.js 配合 npm 便開始了長達 10 年的快速發展。從純服務端應用開發,到開發工具、工程工具,再到如今的 FaaS(Function as a Service,Serverless)開發方式。Node.js 已經成爲 Web 工程師不可或缺的一項技能,不管是用來開發服務端應用還是開發工具類應用,甚至是使用 Electron 開發桌面端應用還是配合 React Native 開發移動端 App,Node.js 能讓前端工程師瞭解更多系統級別的概念,如網絡、I/O、內存、文件系統等等,這些很多都是原本在瀏覽器端上看不到的。而學習這些知識對你理解前端開發背後的一些原理有非常好的價值,就跟學習算法一樣。

結論:請學習 Node.js 和其中涉及到的一些基本計算機原理。

框架時代

當我在做 WordPress 主題的時候,絕大部分的主題開發者都會在前端做一些簡單的效果,甚至有甚者會通過 JavaScript 實現一些原本只能通過後端來完成的事情,比如文章列表、文章內容的加載和渲染。而當年這些主題開發者基本上都會使用 jQuery 來進行這些 JavaScript 的操作,因爲純手寫 JavaScript 在當時來說非常的繁瑣(ES4時代,很多現在被廣泛使用的原生 API 都仍未具備)所以當時 jQuery 就是大家的首選方案。

從非常早的 PrototypeJS、後來的 jQuery、進入 MVC 時代的 Backbone,AngularJS 開啓 MVVM 模式,React 引入 FP 的概念,Vue 成功開啓了漸進式開發體驗的道路。一路下來一地的雞毛,被各路人馬詬病前端領域一個月開發一個新框架,“學不動了”。然而作爲一個也寫過框架、寫過工具類庫的開發者,我很喜歡用一個經常用於泛科技領域的例子來類比前端領域:

科技的終極目標,就是讓人民感覺不到科技。

jQuery 時代,前端開發者使用 JavaScript 的模式是從頁面中獲取 DOM 元素,添加事件,然後通過 class 和 style 對頁面進行動態地變更,以完成對用戶行爲的響應;

Backbone 時代,原本用在桌面端軟件開發中的 MVC 模式被引入到了前端開發中,前端開發者們發現 Web 開發的複雜度已經需要用這些更成熟的開發模式進行管理了;

AngularJS 時代,從這裏開始 Google 把數據雙向綁定模式帶到前端開發中,將原本需要通過 JavaScript 控制 DOM 元素這一繁瑣的操作變成了只需要關心 Model 層需要改動什麼內容即可。而 Vue 則將這種模式的開發成本降低到了一種相當可觀的程度,讓很多新手開發者也能很簡單地入手這種便捷的開發模式。

React 時代,Facebook 的科學家們把函數式編程的思想引入到前端開發中,注重的是數據鏈路的可跟蹤、可回溯、可管理,讓整個數據鏈路是儘可能以單鏈路流轉。
在這裏插入圖片描述
雖然前端領域常被說“一個月一個新框架”,但實際上每一個框架在迭代的過程中都是解決了它們所在業務場景的實際需求的,並不是“拍腦袋”地想要把每一個技術細節做出一個 break change。

而目前我目前推薦的學習的框架是 React 和 Vue:

• 同樣都是目前最流行的框架之一,而且可以預見未來 3~5 年內都是能滿足找工作的需求的;
• React:引入函數式編程(Functional Programming)的概念,使得寫代碼的思路更加嚴謹,更具有可維護性和邏輯可導性;
• Vue:將 MVVM 模式變得非常簡單易於入手,把 Progressive JavaScript Framework 的定位實踐得非常到位。且如今 Vue 3.0 的 Composition API 更是在某種程度上將 Hooks 的玩法實現得比 React Hooks 更優;

結論:請不要害怕學習!不要懼怕新技術!

工程之路

雖然我在接觸了框架和 Node.js 之後,發現 JavaScript 除了能實現一般只用於展示內容和呈現簡單交互以外還能做更多的事情。但本質上還是圍繞着多個頁面進行頁面上 DOM 元素的控制,而直到我打開了 Google 的一些網站時,我才發現原來網站除了能叫頁面以外,還能稱之爲“應用”。自從 Google 上線了一個完全不需要刷新頁面就能完成所有事情而且體驗很不錯的 GMail 之後,我們發現網頁原來也是可以承載那麼複雜的邏輯和應用場景的。大家的熱情異常地高漲,想着能不能讓自己所負責的項目也有這麼厲害高級的樣子。但隨着項目不斷地複雜,代碼規模也變得非常難以管理,而這個時候就需要工程化的引入。

工程化協作

對於企業來說除了研發效率要足夠高以外,研發鏈路的安全、合規也是同樣重要的。什麼叫安全合規?可管理的代碼版本、可控制的發佈流程、可管控的灰度機制,都是大廠用於保證項目流程穩定進行的必要工具。有很多初學者或者還沒有大公司經驗的同學在寫項目時都是單打獨鬥的,但更多的一線項目都需要至少 2~3 個甚至更多的人員一同參與開發的。而這種時候,因爲每個人的水平和開發習慣都是不一致的,而這些不一致就直接導致整體研發效率和項目進度受到極大的影響。所以就需要一種能夠讓大家在一個水平線上進行開發的模式,工程化需求便應運而生。
• 版本控制
Git:GitHub、GitLab、Coding……
SVN:BitBucket、Google Code……
• 代碼樣式檢查工具 JavaScript/TypeScript:ESLint
• 測試工具
單元測試:Karma、Jest、Mocha……
持續集成:CI
• ……

工程化開發工具

從直接將 JavaScript 代碼用 script 標籤,到需要將 jQuery 文件和主要程序文件分別引入,再到 Node.js 出現後使用 npm 進行依賴庫管理並使用 webpack 進行打包和壓縮。工程類工具的發展見證着前端工程近十年的發展歷史,對目前我們所常用的工程工具有更好的瞭解和實踐,絕對是通往優秀路上不可或缺的一步。
• 依賴包管理工具:npm、yarn
• 打包工具:webpack、rollup
• 腳手架工具:create-react-app……

工程化開發語言

相信很多同學都聽說過 JavaScript 誕生之初的一些軼事,比如根本沒有特別多的嚴謹思考,或者在非常多的場景中十分地晦澀,比如隱性轉換等。有人認爲 JavaScript 能發展到如今的地位跟它的這種“靈活度”或者“鬆散度”有關聯,雖然在某種程度上確實因爲這種特性造成的 JavaScript 學習門檻比較低而間接導致。但就如我上面所說,當項目規模和人員規模不斷髮展乃至膨脹過後,這些特性會逐漸表現出來非常糟糕的體驗:

團隊之間因爲沒有良好的技術文檔沉澱,信息不對等的情況直接導致代碼在沒有良好的單元測試時出現邏輯衝突;
第三方依賴庫的 API 在設計上大量使用了 JavaScript 鬆散的特性,導致使用方在引用時頻繁出現“迷惑”的狀態;
當需要使用 JavaScript 與其他語言(特別是強類型語言)進行交互時,JS 過於鬆散的習慣會讓對接方感到非常迷惑,對於雙方的實際接入成本會比前期預估的大得多;
爲了解決這種情況,來自不同編程領域的大牛們都紛紛開始想辦法,於是乎便誕生了非常多的“輪子”:

• Java 系:Scala.js、ClojureScript
• Go 系:GopherJS
• Microsoft:TypeScript
• Facebook:Flow、Reason
目前 TypeScript 已經影響了前端乃至整個 Web 領域的開發生態,TypeScript 之父 Anders Hejlsberg 創造過 Turbo Pascal、Delphi、C# 等在整個計算機科學領域都舉足輕重的語言,而 TypeScript 又再次創造出翻天覆地的變化:
• 強類型的引入能讓我們在寫代碼的時候從值優先的思維轉變成類型優先;
• 強類型的引入能幫助開發工具(IDE 等)更好地爲開發者提供便利性能力,如智能補全、類型檢測、編譯時檢查等等;
• TypeScript 可以讓 JavaScript 更好地與其他語言進行交互,甚至轉換爲其他語言;

工程化通用組件

當需求不斷變多後,“愛偷懶”的工程師們就會把經常用到的內容進行抽象,比如從很早以前就有的 ExtJS、Twitter 工程師發佈的 Bootstrap 再到今天的 Ant Design、Element UI 等,都幫助我們更快更好更穩定地完成一些通用頁面能力的開發。
• React:Ant Design、Fusion Design
• Vue:Element UI、iView、Ant Design of Vue

邏輯抽象能力

隨着我對 JavaScript 應用的編寫經驗不斷增加,我所嘗試的技術和場景也在不斷地變得更加複雜。而當邏輯代碼變得越來越複雜時我也漸漸發現一個新的問題,很多時候我所編寫的邏輯代碼是相似的,但相似之餘其中的一些細節不盡相同,而這些代碼往往是後期維護成本最高的。這就讓我感到十分困惑,如何讓我的代碼寫起來沒有那麼繁瑣的同時,又不丟失原本代碼的應有邏輯呢?這就讓我想起了之前學習的框架,它們的實現原理不就是把原本我們寫得非常繁瑣的邏輯代碼進行壓縮,讓我們寫起來更加簡潔直觀嗎?
這是我曾經面試過的一位校招候選人寫的代碼,其背景是用於快速判斷自走棋類遊戲中不同的增益能力(Buff)的成立狀態。但顯然這樣的代碼在實際開發中是絕對不允許存在的:

代碼邏輯過於冗餘;
一旦通用判斷邏輯出現變動,需要每一個都進行手動維護;
沒有良好的可維護性;

所以我便提出如何讓這些代碼寫得更加“優雅”和利於維護。

export default {
  beastBuff: (state) => {
        let arr = [];
        if (state.raceCount[0]['beast'] == 2 || state.raceCount[0]['beast'] == 3) {
            console.log(`you got 2 beast`)
            arr.push(state.racebuffdata[8])
        } else if (state.raceCount[0]['beast'] == 4 || state.raceCount[0]['beast'] == 5) {
            console.log(`you got 4 beast`)
            arr.pop()
            arr.push(state.racebuffdata[9])
        } else if (state.raceCount[0]['beast'] == 6) {
            console.log(`you got 6 beast`)
            arr.pop()
            arr.push(state.racebuffdata[10])
        } else if (state.raceCount[0]['beast'] < 2 && arr.length == 1) {
            arr.pop()
        }
        return arr;
    },
    caveclanBuff: (state) => {
        let arr = [];
        if (state.raceCount[1]['caveclan'] == 2 || state.raceCount[1]['caveclan'] == 3) {
            console.log(`you got 2 caveclan`)
            arr.push(state.racebuffdata[11])
        } else if (state.raceCount[1]['caveclan'] == 4) {
            console.log(`you got 4 caveclan`)
            arr.pop()
            arr.push(state.racebuffdata[12])
        } else if (state.raceCount[1]['caveclan'] < 2 && arr.length == 1) {
            arr.pop()
        }
        return arr;
    },
    demonBuff: (state) => {
        let arr = [];
        if (state.raceCount[2]['demon'] == 1) {
            console.log(`you got 1 demon`)
            arr.push(state.racebuffdata[5])
        } else if (state.raceCount[2]['demon'] < 1 && arr.length == 1) {
            arr.pop()
        }
        return arr;
  }
  // ...
}

我們不難發現這幾個 xxxBuff 函數中的邏輯都非常接近,但也各有不同。那麼如何能將這段代碼進行優化和抽象呢?我當時給 TA 提出了一份示例代碼:

const beastConfig = [
  [2, [2, 3], 8],
  [4, [4, 5], 9],
  [6, [6], 10],
  [2]
]

這份代碼中的每一個數字在上面的 beastBuff 函數中都可以一一找到,那麼要怎麼將它們複用到邏輯代碼中,實現與原本的代碼一樣的功能呢?
在這裏插入圖片描述
我同樣給他寫了一份參考答案:

const generateBuff = (rate, configArr) => {
  return state => {
    const arr = []
    for (const [ output, conditions, index ] of configArr) {
      if (conditions && index) {
        // Buff calculating
        const isHit = conditions.some(condition => state.raceCount[0][race] == condition)
        if (isHit) {
          console.log(`you got ${output} ${race}`)
          arr.pop()
          arr.push(state.racebuffdata[index])
          break
        }
      } else if (state.raceCount[0][race] < output && arr.length === 1) {
        // Last condition
        arr.pop()
      }
    }
    return arr
  }
}
export default {
  beastBuff: generateBuff('beast', [
    [2, [2, 3], 8],
    [4, [4, 5], 9],
    [6, [6], 10],
    [2]
  ]),
  caveclanBuff: generateBuff('caveclan', [
    [2, [2, 3], 11],
    [4, [4], 12],
    [2]
  ]),
  
  // ...
}

原本代碼裏面通過 hard code 實現的判斷邏輯,通過觀察其中的共同點,並思考能否通轉換爲可抽象部分,這同樣也是一名優秀的工程師所必須具備的能力。

Be D.R.Y.! (Don’t repeat yourself)

更高層次的思考能力

隨着我對不同業務、不同場景和不同代碼難度的不斷探索和研究,我發現在前端領域乃至整個編程領域裏,不同的框架和架構層出不窮地發展,其實在根本上就是各種實際業務場景在尋找更合適的 Better Practice(更好實踐)。就如前面的所說的那樣,不同的框架作者在開發的時候會採取不同的代碼結構甚至代碼哲學,這些不同的思維角度可能在框架的源碼中並不會直接表現出來。但我不會說研讀源碼完全沒有用!因爲研讀源碼最起碼可以學習其中的一些 trick 或者代碼習慣。
但更重要的是理解從 API、系統架構上進行思考,因爲只有多思考了,你才能逐漸變得比其他人更加對不同的技術遊刃有餘。

EOF

這一個流程並不是嚴謹的學習路線,更多的是我個人的一些經驗總結。當然除了我所提到的學習知識點以外,還有很多不同的分支對應着不同的實際業務和場景,比如配合 Electron 開發桌面端應用、配合 React Native/Flutter 開發移動端應用、配合 Node.js/QuickJS/FibJS 開發嵌入式應用、配合 TensorFlow.js 開發適用於前端甚至適用於邊緣計算的機器學習應用、配合 WebAssembly 將 Web 應用的使用體驗提升到接近原生應用的境界……

關於 JavaScript 有一個很有名的預言:
凡是能用 JavaScript 重寫的,終將會使用 JavaScript 重寫
無論這句話會不會最終完全實現,但目前我們已經能看到很多應用逐漸通過 Web 應用的形式雲端化,比如 Photoshop、音視頻編輯軟件、代碼編輯器甚至是大型遊戲等等原本我們完全沒想到可以運行在瀏覽器中。前端開發困難嗎?不困難、門檻相對比較低。簡單嗎?不簡單,通過相信看到這裏的你也已經有所體會了。當然實際要如何選擇路線和方向,還是你自己所遇到的經歷和機遇來決定的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章