前端20個靈魂拷問,完全搞明白你就是中級前端工程師 上篇javascript
感受你們比較喜歡看這種類型的文章,之後會多一些。
歡迎加入咱們的前端交流二羣 目前一羣人數有點多 因此開放了二羣 ~ 歡迎加入前端
裏面不少小姐姐哦~~(包括思否小姐姐) 個人微信號在最後·~java
前端越往深度發展,越須要瞭解底層實現原理,借鑑他們的思想去實現業務需求,去實現性能優化,並且去學習新的東西時候也是在這些知識基礎上去學習~ 事半功倍node
爲何我會將這些問題放在中篇,本文會在介紹完這些問題後在後面給出理由react
移動端React開源項目,從零搭建的webpack腳手架jquery
前端模塊化出現是一定的,一個很複雜的應用不可能全部的內容都在一個文件中~webpack
模塊化的歷程:git
傳統的命令空間github
代碼實現:web
index.js (function(w){ w.a = 1 })(window)
原理: 在window
這個全局對象下面,掛載屬性,那麼全局均可以拿到這個屬性的值,原則上一個js
文件做爲一個模塊,就是一個IIFE
函數
-> require.js
基於AMD
規範
AMD規範採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。全部依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成以後,這個回調函數纔會運行。這裏介紹用require.js實現AMD規範的模塊化:用require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊。
代碼實現:
// 簡單的對象定義 define({ color: "black", size: "unisize" }); // 當你須要一些邏輯來作準備工做時能夠這樣定義: define(function () { //這裏能夠作一些準備工做 return { color: "black", size: "unisize" } }); // 依賴於某些模塊來定義屬於你本身的模塊 define(["./cart", "./inventory"], function(cart, inventory) { //經過返回一個對象來定義你本身的模塊 return { color: "blue", size: "large", addToCart: function() { inventory.decrement(this); cart.add(this); } } } );
-> sea.js
基於CMD
規範
CMD是另外一種js模塊化方案,它與AMD很相似,不一樣點在於:AMD 推崇依賴前置、提早執行,CMD推崇依賴就近、延遲執行。此規範實際上是在sea.js推廣過程當中產生的。
代碼實現:
define(function(require, exports, module) { var $ = require('jquery'); exports.sayHello = function() { $('#hello').toggle('slow'); }; }); seajs.config({ alias: { 'jquery': 'http://modules.seajs.org/jquery/1.7.2/jquery.js' } }); seajs.use(['./hello', 'jquery'], function(hello, $) { $('#beautiful-sea').click(hello.sayHello); });
原理:頂部引入sea.js
的源碼文件,運行時轉換代碼,一開始指定入口文件,根據入口文件定義的數組(或者引入的依賴),去繼續尋找對應的依賴。
-> commonJs
Node.js
原生環境支持commonJs
模塊化規範
先簡單實現一個require
:
function require(/* ... */) { const module = { exports: {} }; ((module, exports) => { // Module code here. In this example, define a function. // 模塊代碼在這裏,在這個例子中,咱們定義了一個函數 function someFunc() {} exports = someFunc; // At this point, exports is no longer a shortcut to module.exports, and // this module will still export an empty default object. // 當代碼運行到這裏時,exports 再也不是 module.exports 的引用,而且當前的 // module 仍舊會導出一個空對象(就像上面聲明的默認對象那樣) module.exports = someFunc; // At this point, the module will now export someFunc, instead of the // default object. // 當代碼運行到這時,當前 module 會導出 someFunc 而不是默認的對象 })(module, module.exports); return module.exports; }
require
就至關於把被引用的 module
拷貝了一份到當前 module
中
export
和module.exports
暴露出來接口
export
和module.exports
的區別:
export 是 module.exports 的引用。做爲一個引用,若是咱們修改它的值,實際上修改的是它對應的引用對象的值。
commonJS
用同步的方式加載模塊。在服務端,模塊文件都存在本地磁盤,讀取很是快,因此這樣作不會有問題。可是在瀏覽器端,限於網絡緣由,更合理的方案是使用異步加載。
一句話簡單總結就是,exports
-> {} <- module.exports
同時指向一個對象
-> ES6
模塊化
目前最經常使用的模塊化規範:
ES6
模塊化規範原生的瀏覽器環境和Node.js
環境都不識別,可是要使用,就必需要使用babel
編譯成瀏覽器或者Node.js
能夠識別的代碼,爲了節省時間,那麼就會出現自動化一鍵打包編譯代碼的工具, - webpack
.
ES6
最牛逼的地方,不只支持了靜態校驗,能夠同步異步加載,並且統一了先後端的模塊化規範,Node
和傳統前端,均可以用這套規範。
ES6
模塊與CommonJS
模塊的差別
CommonJS
模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用 (首次require
不一樣路徑的文件,會在require.cache
中保存一份緩存,下次讀取的時候就直接從緩存中讀取了)CommonJS
模塊是運行時加載,ES6 模塊是編譯時輸出接口。CommonJS
加載的是一個對象(即module.exports
屬性),該對象只有在腳本運行完纔會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成
這也是爲何TypeScript
支持靜態類型檢查的緣由 由於他使用的是ES6
模塊化方案
特別提示:如今Node
也能夠用ES6
模塊化方案的 用experimental
便可
看看commonJs
的
index.js const a = require('./test1.js'); const func = require('./test2'); a.a = 2; console.log(a.a,'test1'); func() test2.js const a = require('./test1') module.exports = function(){ console.log(a.a,'test2') } test1.js let a={ a:1 } module.exports=a
運行node index.js
輸出結果
看看ES6
的
// math.js export let val = 1 export function add () { val++ } // test.js import { val, add } from './math.js' console.log(val) // 1 add() console.log(val) // 2
React Vue
框架實現基本原理以及設計思想~設計思想和基本原理:
DOM
操做改爲了數據驅動的方式去間接替咱們操做DOM
。DOM
進行操做。 --diff
算法常見的diff
算法,有上一個虛擬dom
和此次更新後的虛擬dom
去對比,而後給真實dom
打補丁的方式,也有用真實dom
和虛擬dom
直接對比的方式。
從零本身編寫一個React框架 我這篇文章附帶了源碼,從零本身實現了一個React框架
常見的數據結構:棧,隊列,樹,圖,數組,單鏈表,雙鏈表,圖等...
冒泡排序
比較相鄰的兩個元素,若是前一個比後一個大,則交換位置。
第一輪的時候最後一個元素應該是最大的一個。
按照步驟一的方法進行相鄰兩個元素的比較,這個時候因爲最後一個元素已是最大的了,因此最後一個元素不用比較
function bubble_sort(arr){ for(var i=0;i<arr.length-1;i++){ for(var j=0;j<arr.length-i-1;j++){ if(arr[j]>arr[j+1]){ var swap=arr[j]; arr[j]=arr[j+1]; arr[j+1]=swap; } } } } var arr=[3,1,5,7,2,4,9,6,10,8]; bubble_sort(arr); console.log(arr);
快速排序
js代碼實現 解析:快速排序是對冒泡排序的一種改進,第一趟排序時將數據分紅兩部分,一部分比另外一部分的全部數據都要小。而後遞歸調用,在兩邊都實行快速排序。
function quick_sort(arr){ if(arr.length<=1){ return arr; } var pivotIndex=Math.floor(arr.length/2); var pivot=arr.splice(pivotIndex,1)[0]; var left=[]; var right=[]; for(var i=0;i<arr.length;i++){ if(arr[i]<pivot){ left.push(arr[i]); }else{ right.push(arr[i]); } } return quick_sort(left).concat([pivot],quick_sort(right)); } var arr=[5,6,2,1,3,8,7,1,2,3,4,7]; console.log(quick_sort(arr));
時間複雜度概念:
一個算法的時間複雜度反映了程序運行從開始到結束所須要的時間。
空間複雜度概念:
一個程序的空間複雜度是指運行完一個程序所需內存的大小。利用程序的空間複雜度,能夠對程序的運行所須要的內存多少有個預先估計。
具體能夠看這篇文章:
Node.js
的底層fs
,net
,path
,stream
等模塊以及express
框架使用和操做數據庫注意,Node.js
中不少回調函數的首個參數都是err
根據路徑同步讀取文件流:
// 在 macOS、Linux 和 Windows 上: fs.readFileSync('<目錄>'); // => [Error: EISDIR: illegal operation on a directory, read <目錄>]
異步地讀取文件的所有內容:
fs.readFile('路徑', (err, data) => { if (err) throw err; console.log(data); });
上面讀取到的data
都是buffer
數據 ,Buffer 類是一個全局變量,用於直接處理二進制數據。
若是路徑存在,則返回 true,不然返回 false。:
fs.existsSync(path)
Node.js
中通常同步的API
都是sync
結尾,不帶的通常是異步的,咱們通常都用異步API
Node.js 中有四種基本的流類型:
Writable
- 可寫入數據的流(例如 fs.createWriteStream())。Readable
- 可讀取數據的流(例如 fs.createReadStream())。Duplex
- 可讀又可寫的流(例如 net.Socket)。Transform
- 在讀寫過程當中能夠修改或轉換數據的 Duplex 流(例如 zlib.createDeflate()
)。
使用Node.js編寫的靜態資源服務器 這是個人本身編寫的靜態資源服務器
裏面有大量的Buffer
操做
Node
裏面這些經常使用的模塊,是走向全棧工程師的基礎。越是複雜的應用,對二進制操做會越多,好比本身定義的即時通信協議,你須要把數據一點點的從Buffer
裏切出來。若是是prob
協議,那麼還要反序列化。可是原理大都相似,還有涉及音視頻等。
Node.js
做爲中間件,同構服務端渲染單頁面應用,以及作轉發請求等操做爲了解決單頁面應用的SEO
問題
傳統的SSR
渲染是在服務端把代碼都運行好了而後經過字符串都形式傳給前端渲染
如今都單頁面應用是隻傳輸一個空的HTML
文件和不少個js
文件 給前端,而後拿到文件後動態生成頁面。這就致使搜索引擎的爬蟲沒法爬到網頁的信息,全部有了同構。
同構就是把單頁面應用,React和Vue
這樣框架寫的代碼,在服務端運行一遍(並非運行所有),而後返回字符串給前端渲染,這個時候搜索引擎就能夠爬取到關鍵字了。前端根據服務端返回的字符串渲染生成頁面後,js
文件接管後續的邏輯。這樣就是一套完整的同構
React服務端渲染源碼 這個是個人React
服務端渲染源碼
客戶端入口文件:
//client/index. js import React from 'react'; import ReactDom from 'react-dom'; import { BrowserRouter, Route } from 'react-router-dom'; import { Provider } from 'react-redux'; import { getClientStore } from '../containers/redux-file/store'; import {renderRoutes} from 'react-router-config' import routers from '../Router'; const store = getClientStore(); const App = () => { return ( <Provider store={store}> <BrowserRouter>{renderRoutes(routers)}</BrowserRouter> </Provider> ); }; ReactDom.hydrate(<App />, document.getElementById('root'));
同構的入口代碼:
// server/index.js import express from 'express'; import { render } from '../utils'; import { serverStore } from '../containers/redux-file/store'; const app = express(); app.use(express.static('public')); app.get('*', function(req, res) { if (req.path === '/favicon.ico') { res.send(); return; } const store = serverStore(); res.send(render(req, store)); }); const server = app.listen(3000, () => { var host = server.address().address; var port = server.address().port; console.log(host, port); console.log('啓動鏈接了'); });
render
函數:
import Routes from '../Router'; import { renderToString } from 'react-dom/server'; import { StaticRouter, Link, Route } from 'react-router-dom'; import React from 'react'; import { Provider } from 'react-redux'; import { renderRoutes } from 'react-router-config'; import routers from '../Router'; import { matchRoutes } from 'react-router-config'; export const render = (req, store) => { const matchedRoutes = matchRoutes(routers, req.path); matchedRoutes.forEach(item => { //若是這個路由對應的組件有loadData方法 if (item.route.loadData) { item.route.loadData(store); } }); console.log(store.getState(),Date.now()) const content = renderToString( <Provider store={store}> <StaticRouter location={req.path}>{renderRoutes(routers)}</StaticRouter> </Provider> );
看起來眼花繚亂 其實就是把代碼運行在服務端,而後拼接成字符串給前端
惟一有點特別的地方:
服務端代碼注水:
<script>window.context={state:${JSON.stringify(store.getState())}}</script>
客戶端代碼脫水:
store.js
import thunk from 'redux-thunk'; import { createStore, applyMiddleware } from 'redux'; import reducers from './reducers'; export const getClientStore = () => { //下面這行代碼就是代碼脫水,createStore是能夠傳入第二個參數的,去閱讀源碼能夠了解 const defaultState = window.context ? window.context.state : {}; return createStore(reducers, defaultState, applyMiddleware(thunk)); }; export const serverStore = () => { return createStore(reducers, applyMiddleware(thunk)); };
跟我一塊兒默唸:
同構的祕訣:
1.代碼如今服務端運行
2.返回字符串和注水後的數據給前端
3.前端拿到字符串和注水數據後,脫水渲染,而後js
文件接管,這時候又是單頁面應用的邏輯了~
爲何要挑選這五個問題
模塊化規範的學習,是爲了擁有改造舊輪子的能力
數據結構和算法是爲了擁有編寫輕量級框架和性能優化打基礎
Node.js
的使用是爲了向全棧發展打基礎
同構是爲了走向高併發場景打基礎
框架的實現原理,是爲了讓咱們學習這種設計思想,在平時業務代碼書寫時候,考慮時間複雜度和空間度的同時也要考慮框架底層實現。
以爲寫得不錯,能夠給個
star
。
歡迎加入咱們的二羣哦~
個人我的微信號:CALASFxiaotan