摘要:前端個靈魂拷問,徹底搞明白你就是中級前端工程師上篇感覺大家比較喜歡看這種類型的文章,以后會多一些。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。此規范其實是在推廣過程中產生的。
前端20個靈魂拷問,徹底搞明白你就是中級前端工程師 上篇
感覺大家比較喜歡看這種類型的文章,以后會多一些。
歡迎加入我們的前端交流二群 目前一群人數有點多 所以開放了二群 ~ 歡迎加入
里面很多小姐姐哦~~(包括思否小姐姐) 我的微信號在最后·~
前端越往深度發展,越需要了解底層實現原理,借鑒他們的思想去實現業務需求,去實現性能優化,而且去學習新的東西時候也是在這些知識基礎上去學習~ 事半功倍
為什么我會將這些問題放在中篇,本文會在介紹完這些問題后在后面給出理由
問題來了 1.為什么會出現模塊化,以及各種模塊化標準移動端React開源項目,從零搭建的webpack腳手架
前端模塊化出現是必定的,一個很復雜的應用不可能所有的內容都在一個文件中~
模塊化的歷程:
傳統的命令空間
代碼實現:
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) // 2React Vue框架實現基本原理以及設計思想~
設計思想和基本原理:
1.由傳統的直接DOM操作改成了數據驅動的方式去間接替我們操作DOM。
2.每次數據改變需要重新渲染時,只對存在差異對那個部分DOM進行操作。 --diff算法
有一系列對生命周期,其實就是代碼執行順序中給定了一部分的特定函數名稱進行執行,一種約定。
常見的diff算法,有上一個虛擬dom和這次更新后的虛擬dom去對比,然后給真實dom打補丁的方式,也有用真實dom和虛擬dom直接對比的方式。
從零自己編寫一個React框架 我這篇文章附帶了源碼,從零自己實現了一個React框架
前端需要了解的常見的算法和數據結構常見的數據結構:棧,隊列,樹,圖,數組,單鏈表,雙鏈表,圖等...
冒泡排序
比較相鄰的兩個元素,如果前一個比后一個大,則交換位置。
第一輪的時候最后一個元素應該是最大的一個。
按照步驟一的方法進行相鄰兩個元素的比較,這個時候由于最后一個元素已經是最大的了,所以最后一個元素不用比較
function bubble_sort(arr){ for(var i=0;iarr[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時間復雜度概念:
一個算法的時間復雜度反映了程序運行從開始到結束所需要的時間。
空間復雜度概念:
一個程序的空間復雜度是指運行完一個程序所需內存的大小。利用程序的空間復雜度,可以對程序的運行所需要的內存多少有個預先估計。
具體可以看這篇文章:
JavaScript 算法與數據結構
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結尾,不帶的一般是異步的,我們一般都用異步APINode.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 (); }; ReactDom.hydrate( {renderRoutes(routers)} , 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(); {renderRoutes(routers)} 看起來眼花繚亂 其實就是把代碼運行在服務端,然后拼接成字符串給前端唯一有點特別的地方:
服務端代碼注水:
客戶端代碼脫水:
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.代碼現在服務端運行
經過很久考慮才覺得應該寫這5個問題,接下來的5個問題會在下周更新。
2.返回字符串和注水后的數據給前端
3.前端拿到字符串和注水數據后,脫水渲染,然后js文件接管,這時候又是單頁面應用的邏輯了~為什么要挑選這五個問題模塊化規范的學習,是為了擁有改造舊輪子的能力
數據結構和算法是為了擁有編寫輕量級框架和性能優化打基礎
Node.js的使用是為了向全棧發展打基礎
同構是為了走向高并發場景打基礎
框架的實現原理,是為了讓我們學習這種設計思想,在平時業務代碼書寫時候,考慮時間復雜度和空間度的同時也要考慮框架底層實現。
覺得寫得不錯,可以給個star。歡迎加入我們的二群哦~
我的個人微信號:CALASFxiaotan
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/116307.html
摘要:前端個靈魂拷問,徹底搞明白你就是中級前端工程師上篇感覺大家比較喜歡看這種類型的文章,以后會多一些。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。此規范其實是在推廣過程中產生的。 showImg(https://segmentfault.com/img/bVbwAMU?w=700&h=394); 前端20個靈魂拷問,徹底搞明白你就是中級前端工程師...
摘要:安裝后已經完成了安裝,并且等待其他的線程被關閉。激活后在這個狀態會處理事件回調提供了更新緩存策略的機會。并可以處理功能性的事件請求后臺同步推送。廢棄狀態這個狀態表示一個的生命周期結束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺,已經來到了最后的下篇 其實我寫的東西你如果認真去看,跟著去寫,應該能有...
摘要:還是老規矩,從易到難吧傳統的定時器,異步編程等。分配對象時,先是在空間中進行分配。內存泄漏內存泄漏是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網上參差不棄的面試題,本文由淺入深,讓你在...
摘要:還是老規矩,從易到難吧傳統的定時器,異步編程等。分配對象時,先是在空間中進行分配。內存泄漏內存泄漏是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網上參差不棄的面試題,本文由淺入深,讓你在...
摘要:黑客技術點擊右側關注,了解黑客的世界開發進階點擊右側關注,掌握進階之路開發點擊右側關注,探討技術話題作者丨呼延十排版丨團長前言本文主要受眾為開發人員所以不涉及到的服務部署等操作且內容較多大家準備好耐心和瓜子礦泉水前一陣系統的學習了一下也有 ...
閱讀 2570·2021-09-06 15:02
閱讀 3199·2021-09-02 10:18
閱讀 2821·2019-08-30 15:44
閱讀 685·2019-08-30 15:43
閱讀 1948·2019-08-30 14:08
閱讀 2758·2019-08-30 13:16
閱讀 1397·2019-08-26 13:52
閱讀 930·2019-08-26 12:21