摘要:前端個(gè)靈魂拷問(wèn),徹底搞明白你就是中級(jí)前端工程師上篇感覺(jué)大家比較喜歡看這種類型的文章,以后會(huì)多一些。所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。此規(guī)范其實(shí)是在推廣過(guò)程中產(chǎn)生的。
前端20個(gè)靈魂拷問(wèn),徹底搞明白你就是中級(jí)前端工程師 上篇
感覺(jué)大家比較喜歡看這種類型的文章,以后會(huì)多一些。
歡迎加入我們的前端交流二群 目前一群人數(shù)有點(diǎn)多 所以開(kāi)放了二群 ~ 歡迎加入
里面很多小姐姐哦~~(包括思否小姐姐) 我的微信號(hào)在最后·~
前端越往深度發(fā)展,越需要了解底層實(shí)現(xiàn)原理,借鑒他們的思想去實(shí)現(xiàn)業(yè)務(wù)需求,去實(shí)現(xiàn)性能優(yōu)化,而且去學(xué)習(xí)新的東西時(shí)候也是在這些知識(shí)基礎(chǔ)上去學(xué)習(xí)~ 事半功倍
為什么我會(huì)將這些問(wèn)題放在中篇,本文會(huì)在介紹完這些問(wèn)題后在后面給出理由
問(wèn)題來(lái)了 1.為什么會(huì)出現(xiàn)模塊化,以及各種模塊化標(biāo)準(zhǔn)移動(dòng)端React開(kāi)源項(xiàng)目,從零搭建的webpack腳手架
前端模塊化出現(xiàn)是必定的,一個(gè)很復(fù)雜的應(yīng)用不可能所有的內(nèi)容都在一個(gè)文件中~
模塊化的歷程:
傳統(tǒng)的命令空間
代碼實(shí)現(xiàn):
index.js (function(w){ w.a = 1 })(window)
原理: 在window這個(gè)全局對(duì)象下面,掛載屬性,那么全局都可以拿到這個(gè)屬性的值,原則上一個(gè)js文件作為一個(gè)模塊,就是一個(gè)IIFE函數(shù)
-> require.js 基于AMD規(guī)范
AMD規(guī)范采用異步方式加載模塊,模塊的加載不影響它后面語(yǔ)句的運(yùn)行。所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。這里介紹用require.js實(shí)現(xiàn)AMD規(guī)范的模塊化:用require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊。
代碼實(shí)現(xiàn):
// 簡(jiǎn)單的對(duì)象定義 define({ color: "black", size: "unisize" }); // 當(dāng)你需要一些邏輯來(lái)做準(zhǔn)備工作時(shí)可以這樣定義: define(function () { //這里可以做一些準(zhǔn)備工作 return { color: "black", size: "unisize" } }); // 依賴于某些模塊來(lái)定義屬于你自己的模塊 define(["./cart", "./inventory"], function(cart, inventory) { //通過(guò)返回一個(gè)對(duì)象來(lái)定義你自己的模塊 return { color: "blue", size: "large", addToCart: function() { inventory.decrement(this); cart.add(this); } } } );
-> sea.js基于CMD規(guī)范
CMD是另一種js模塊化方案,它與AMD很類似,不同點(diǎn)在于:AMD 推崇依賴前置、提前執(zhí)行,CMD推崇依賴就近、延遲執(zhí)行。此規(guī)范其實(shí)是在sea.js推廣過(guò)程中產(chǎn)生的。
代碼實(shí)現(xiàn):
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的源碼文件,運(yùn)行時(shí)轉(zhuǎn)換代碼,一開(kāi)始指定入口文件,根據(jù)入口文件定義的數(shù)組(或者引入的依賴),去繼續(xù)尋找對(duì)應(yīng)的依賴。
-> commonJs
Node.js原生環(huán)境支持commonJs模塊化規(guī)范
先簡(jiǎn)單實(shí)現(xiàn)一個(gè)require:
function require(/* ... */) { const module = { exports: {} }; ((module, exports) => { // Module code here. In this example, define a function. // 模塊代碼在這里,在這個(gè)例子中,我們定義了一個(gè)函數(shù) 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. // 當(dāng)代碼運(yùn)行到這里時(shí),exports 不再是 module.exports 的引用,并且當(dāng)前的 // module 仍舊會(huì)導(dǎo)出一個(gè)空對(duì)象(就像上面聲明的默認(rèn)對(duì)象那樣) module.exports = someFunc; // At this point, the module will now export someFunc, instead of the // default object. // 當(dāng)代碼運(yùn)行到這時(shí),當(dāng)前 module 會(huì)導(dǎo)出 someFunc 而不是默認(rèn)的對(duì)象 })(module, module.exports); return module.exports; }
require 就相當(dāng)于把被引用的 module 拷貝了一份到當(dāng)前 module 中
export和module.exports暴露出來(lái)接口
export和module.exports的區(qū)別:
export 是 module.exports 的引用。作為一個(gè)引用,如果我們修改它的值,實(shí)際上修改的是它對(duì)應(yīng)的引用對(duì)象的值。
commonJS用同步的方式加載模塊。在服務(wù)端,模塊文件都存在本地磁盤,讀取非常快,所以這樣做不會(huì)有問(wèn)題。但是在瀏覽器端,限于網(wǎng)絡(luò)原因,更合理的方案是使用異步加載。
一句話簡(jiǎn)單總結(jié)就是,exports-> {} <- module.exports同時(shí)指向一個(gè)對(duì)象
-> ES6模塊化
目前最常用的模塊化規(guī)范:
ES6模塊化規(guī)范原生的瀏覽器環(huán)境和Node.js環(huán)境都不識(shí)別,但是要使用,就必須要使用babel編譯成瀏覽器或者Node.js可以識(shí)別的代碼,為了節(jié)省時(shí)間,那么就會(huì)出現(xiàn)自動(dòng)化一鍵打包編譯代碼的工具, - webpack.
ES6最牛逼的地方,不僅支持了靜態(tài)校驗(yàn),可以同步異步加載,而且統(tǒng)一了前后端的模塊化規(guī)范,Node和傳統(tǒng)前端,都可以用這套規(guī)范。
ES6 模塊與 CommonJS 模塊的差異
CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用 (首次require不同路徑的文件,會(huì)在require.cache中保存一份緩存,下次讀取的時(shí)候就直接從緩存中讀取了)
CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。
CommonJS 加載的是一個(gè)對(duì)象(即module.exports屬性),該對(duì)象只有在腳本運(yùn)行完才會(huì)生成。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會(huì)生成
這也是為什么TypeScript 支持靜態(tài)類型檢查的原因 因?yàn)樗褂玫氖?b>ES6模塊化方案
特別提示:現(xiàn)在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
運(yùn)行node index.js
輸出結(jié)果
看看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框架實(shí)現(xiàn)基本原理以及設(shè)計(jì)思想~
設(shè)計(jì)思想和基本原理:
1.由傳統(tǒng)的直接DOM操作改成了數(shù)據(jù)驅(qū)動(dòng)的方式去間接替我們操作DOM。
2.每次數(shù)據(jù)改變需要重新渲染時(shí),只對(duì)存在差異對(duì)那個(gè)部分DOM進(jìn)行操作。 --diff算法
有一系列對(duì)生命周期,其實(shí)就是代碼執(zhí)行順序中給定了一部分的特定函數(shù)名稱進(jìn)行執(zhí)行,一種約定。
常見(jiàn)的diff算法,有上一個(gè)虛擬dom和這次更新后的虛擬dom去對(duì)比,然后給真實(shí)dom打補(bǔ)丁的方式,也有用真實(shí)dom和虛擬dom直接對(duì)比的方式。
從零自己編寫一個(gè)React框架 我這篇文章附帶了源碼,從零自己實(shí)現(xiàn)了一個(gè)React框架
前端需要了解的常見(jiàn)的算法和數(shù)據(jù)結(jié)構(gòu)常見(jiàn)的數(shù)據(jù)結(jié)構(gòu):棧,隊(duì)列,樹,圖,數(shù)組,單鏈表,雙鏈表,圖等...
冒泡排序
比較相鄰的兩個(gè)元素,如果前一個(gè)比后一個(gè)大,則交換位置。
第一輪的時(shí)候最后一個(gè)元素應(yīng)該是最大的一個(gè)。
按照步驟一的方法進(jìn)行相鄰兩個(gè)元素的比較,這個(gè)時(shí)候由于最后一個(gè)元素已經(jīng)是最大的了,所以最后一個(gè)元素不用比較
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代碼實(shí)現(xiàn) 解析:快速排序是對(duì)冒泡排序的一種改進(jìn),第一趟排序時(shí)將數(shù)據(jù)分成兩部分,一部分比另一部分的所有數(shù)據(jù)都要小。然后遞歸調(diào)用,在兩邊都實(shí)行快速排序。
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時(shí)間復(fù)雜度概念:
一個(gè)算法的時(shí)間復(fù)雜度反映了程序運(yùn)行從開(kāi)始到結(jié)束所需要的時(shí)間。
空間復(fù)雜度概念:
一個(gè)程序的空間復(fù)雜度是指運(yùn)行完一個(gè)程序所需內(nèi)存的大小。利用程序的空間復(fù)雜度,可以對(duì)程序的運(yùn)行所需要的內(nèi)存多少有個(gè)預(yù)先估計(jì)。
具體可以看這篇文章:
JavaScript 算法與數(shù)據(jù)結(jié)構(gòu)
Node.js的底層fs,net,path,stream等模塊以及express框架使用和操作數(shù)據(jù)庫(kù)注意,Node.js中很多回調(diào)函數(shù)的首個(gè)參數(shù)都是err根據(jù)路徑同步讀取文件流:
// 在 macOS、Linux 和 Windows 上: fs.readFileSync("<目錄>"); // => [Error: EISDIR: illegal operation on a directory, read <目錄>]異步地讀取文件的全部?jī)?nèi)容:
fs.readFile("路徑", (err, data) => { if (err) throw err; console.log(data); });上面讀取到的data都是buffer數(shù)據(jù) ,Buffer 類是一個(gè)全局變量,用于直接處理二進(jìn)制數(shù)據(jù)。如果路徑存在,則返回 true,否則返回 false。:
fs.existsSync(path)Node.js中一般同步的API都是sync結(jié)尾,不帶的一般是異步的,我們一般都用異步APINode.js 中有四種基本的流類型:
Writable - 可寫入數(shù)據(jù)的流(例如 fs.createWriteStream())。
Readable - 可讀取數(shù)據(jù)的流(例如 fs.createReadStream())。
Duplex - 可讀又可寫的流(例如 net.Socket)。
Transform - 在讀寫過(guò)程中可以修改或轉(zhuǎn)換數(shù)據(jù)的 Duplex 流(例如 zlib.createDeflate() )。使用Node.js編寫的靜態(tài)資源服務(wù)器 這是我的自己編寫的靜態(tài)資源服務(wù)器
里面有大量的Buffer操作
Node里面這些常用的模塊,是走向全棧工程師的基礎(chǔ)。越是復(fù)雜的應(yīng)用,對(duì)二進(jìn)制操作會(huì)越多,比如自己定義的即時(shí)通訊協(xié)議,你需要把數(shù)據(jù)一點(diǎn)點(diǎn)的從Buffer里切出來(lái)。如果是prob協(xié)議,那么還要反序列化。但是原理大都類似,還有涉及音視頻等。使用Node.js作為中間件,同構(gòu)服務(wù)端渲染單頁(yè)面應(yīng)用,以及做轉(zhuǎn)發(fā)請(qǐng)求等操作為了解決單頁(yè)面應(yīng)用的SEO問(wèn)題
傳統(tǒng)的SSR渲染是在服務(wù)端把代碼都運(yùn)行好了然后通過(guò)字符串都形式傳給前端渲染
現(xiàn)在都單頁(yè)面應(yīng)用是只傳輸一個(gè)空的HTML文件和很多個(gè)js文件 給前端,然后拿到文件后動(dòng)態(tài)生成頁(yè)面。這就導(dǎo)致搜索引擎的爬蟲無(wú)法爬到網(wǎng)頁(yè)的信息,所有有了同構(gòu)。
同構(gòu)就是把單頁(yè)面應(yīng)用,React和Vue這樣框架寫的代碼,在服務(wù)端運(yùn)行一遍(并不是運(yùn)行全部),然后返回字符串給前端渲染,這個(gè)時(shí)候搜索引擎就可以爬取到關(guān)鍵字了。前端根據(jù)服務(wù)端返回的字符串渲染生成頁(yè)面后,js文件接管后續(xù)的邏輯。這樣就是一套完整的同構(gòu)
React服務(wù)端渲染源碼 這個(gè)是我的React服務(wù)端渲染源碼
客戶端入口文件:
//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")); 同構(gòu)的入口代碼:
// 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("啟動(dòng)連接了"); });render函數(shù):
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 => { //如果這個(gè)路由對(duì)應(yīng)的組件有l(wèi)oadData方法 if (item.route.loadData) { item.route.loadData(store); } }); console.log(store.getState(),Date.now()) const content = renderToString(); {renderRoutes(routers)} 看起來(lái)眼花繚亂 其實(shí)就是把代碼運(yùn)行在服務(wù)端,然后拼接成字符串給前端唯一有點(diǎn)特別的地方:
服務(wù)端代碼注水:
客戶端代碼脫水:
store.js
import thunk from "redux-thunk"; import { createStore, applyMiddleware } from "redux"; import reducers from "./reducers"; export const getClientStore = () => { //下面這行代碼就是代碼脫水,createStore是可以傳入第二個(gè)參數(shù)的,去閱讀源碼可以了解 const defaultState = window.context ? window.context.state : {}; return createStore(reducers, defaultState, applyMiddleware(thunk)); }; export const serverStore = () => { return createStore(reducers, applyMiddleware(thunk)); };跟我一起默念:
同構(gòu)的秘訣:
1.代碼現(xiàn)在服務(wù)端運(yùn)行
經(jīng)過(guò)很久考慮才覺(jué)得應(yīng)該寫這5個(gè)問(wèn)題,接下來(lái)的5個(gè)問(wèn)題會(huì)在下周更新。
2.返回字符串和注水后的數(shù)據(jù)給前端
3.前端拿到字符串和注水?dāng)?shù)據(jù)后,脫水渲染,然后js文件接管,這時(shí)候又是單頁(yè)面應(yīng)用的邏輯了~為什么要挑選這五個(gè)問(wèn)題模塊化規(guī)范的學(xué)習(xí),是為了擁有改造舊輪子的能力
數(shù)據(jù)結(jié)構(gòu)和算法是為了擁有編寫輕量級(jí)框架和性能優(yōu)化打基礎(chǔ)
Node.js的使用是為了向全棧發(fā)展打基礎(chǔ)
同構(gòu)是為了走向高并發(fā)場(chǎng)景打基礎(chǔ)
框架的實(shí)現(xiàn)原理,是為了讓我們學(xué)習(xí)這種設(shè)計(jì)思想,在平時(shí)業(yè)務(wù)代碼書寫時(shí)候,考慮時(shí)間復(fù)雜度和空間度的同時(shí)也要考慮框架底層實(shí)現(xiàn)。
覺(jué)得寫得不錯(cuò),可以給個(gè)star。歡迎加入我們的二群哦~
我的個(gè)人微信號(hào):CALASFxiaotan
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/116307.html
摘要:前端個(gè)靈魂拷問(wèn),徹底搞明白你就是中級(jí)前端工程師上篇感覺(jué)大家比較喜歡看這種類型的文章,以后會(huì)多一些。所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。此規(guī)范其實(shí)是在推廣過(guò)程中產(chǎn)生的。 showImg(https://segmentfault.com/img/bVbwAMU?w=700&h=394); 前端20個(gè)靈魂拷問(wèn),徹底搞明白你就是中級(jí)前端工程師...
摘要:安裝后已經(jīng)完成了安裝,并且等待其他的線程被關(guān)閉。激活后在這個(gè)狀態(tài)會(huì)處理事件回調(diào)提供了更新緩存策略的機(jī)會(huì)。并可以處理功能性的事件請(qǐng)求后臺(tái)同步推送。廢棄狀態(tài)這個(gè)狀態(tài)表示一個(gè)的生命周期結(jié)束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺(jué),已經(jīng)來(lái)到了最后的下篇 其實(shí)我寫的東西你如果認(rèn)真去看,跟著去寫,應(yīng)該能有...
摘要:還是老規(guī)矩,從易到難吧傳統(tǒng)的定時(shí)器,異步編程等。分配對(duì)象時(shí),先是在空間中進(jìn)行分配。內(nèi)存泄漏內(nèi)存泄漏是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網(wǎng)上參差不棄的面試題,本文由淺入深,讓你在...
摘要:還是老規(guī)矩,從易到難吧傳統(tǒng)的定時(shí)器,異步編程等。分配對(duì)象時(shí),先是在空間中進(jìn)行分配。內(nèi)存泄漏內(nèi)存泄漏是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網(wǎng)上參差不棄的面試題,本文由淺入深,讓你在...
摘要:黑客技術(shù)點(diǎn)擊右側(cè)關(guān)注,了解黑客的世界開(kāi)發(fā)進(jìn)階點(diǎn)擊右側(cè)關(guān)注,掌握進(jìn)階之路開(kāi)發(fā)點(diǎn)擊右側(cè)關(guān)注,探討技術(shù)話題作者丨呼延十排版丨團(tuán)長(zhǎng)前言本文主要受眾為開(kāi)發(fā)人員所以不涉及到的服務(wù)部署等操作且內(nèi)容較多大家準(zhǔn)備好耐心和瓜子礦泉水前一陣系統(tǒng)的學(xué)習(xí)了一下也有 ...
閱讀 2681·2021-09-06 15:02
閱讀 3368·2021-09-02 10:18
閱讀 2925·2019-08-30 15:44
閱讀 826·2019-08-30 15:43
閱讀 2040·2019-08-30 14:08
閱讀 2849·2019-08-30 13:16
閱讀 1501·2019-08-26 13:52
閱讀 1010·2019-08-26 12:21