摘要:往往純的單頁(yè)面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。
React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦予了它一次編寫(xiě),生成多種平臺(tái)小程序和React-native應(yīng)用的能力,這里特意說(shuō)下 Taro,它是國(guó)產(chǎn),文檔寫(xiě)得比較不錯(cuò),而且它的升級(jí)速度比較快,有issue我看也會(huì)及時(shí)解決,他們的維護(hù)人員還是非常敬業(yè)的!
,
Tips:本文某些知識(shí)點(diǎn)如果介紹不對(duì)或者不全的地方歡迎指出,本文可能內(nèi)容比較多,閱讀時(shí)間花費(fèi)比較長(zhǎng),但是希望你可以認(rèn)真看下去,可以的話最好手把手去實(shí)現(xiàn)一些code,本文所有代碼均手寫(xiě)。
本文會(huì)從原生瀏覽器環(huán)境,到跨平臺(tái)開(kāi)發(fā)逐漸去深入介紹,先給一些資料手寫(xiě)React優(yōu)化腳手架帶項(xiàng)目
react-ssr的源碼
手寫(xiě)Node.js原生靜態(tài)資源服務(wù)器
跨平臺(tái)Electron的demo
原生瀏覽器環(huán)境:原生瀏覽器環(huán)境其實(shí)是最考驗(yàn)前端工程師能力的編程環(huán)境,因?yàn)槲覀兦岸舜蟛糠忠婚_(kāi)始面向?yàn)g覽器編程,現(xiàn)在很多很多工作5-10年的前端,性能面板API都不知道用,怎么看調(diào)用函數(shù)分析耗時(shí)都不知道,這也是最近面試的情況,覺(jué)得有人說(shuō)35歲失業(yè)的情況,是普遍存在,但是很大部分是你在混啊兄弟。
純CSR渲染(客戶端渲染)
純SSR渲染(服務(wù)端渲染)
混合渲染(預(yù)渲染,webpack的插件預(yù)渲染,Next.js的約定式路由SSR,或者使用Node.js做中間件,做部分SSR,加快首屏渲染,或者指定路由SSR.)
客戶端請(qǐng)求RestFul接口,接口吐回靜態(tài)資源文件
Node.js實(shí)現(xiàn)代碼
const express = require("express") const app = express() app.use(express.static("pulic"))//這里的public就是靜態(tài)資源的文件夾,讓客戶端拉取的,這里的代碼是前端的代碼已經(jīng)構(gòu)建完畢的代碼 app.get("/",(req,res)=>{ //do something }) app.listen(3000,err=>{ if(!err)=>{ console.log("監(jiān)聽(tīng)端口號(hào)3000成功") } })
客戶端收到一個(gè)HTML文件,和若干個(gè)CSS文件,以及多個(gè)javaScript文件
用戶輸入了url地址欄然后客戶端返回靜態(tài)文件,客戶端開(kāi)始解析
客戶端解析文件,js代碼動(dòng)態(tài)生成頁(yè)面。(這也是為什么說(shuō)單頁(yè)面應(yīng)用的SEO不友好的原因,初始它只是一個(gè)空的div標(biāo)簽的HTML文件)
判斷一個(gè)頁(yè)面是不是CSR,很大程度上可以根據(jù)右鍵點(diǎn)開(kāi)查看頁(yè)面元素,如果只有一個(gè)空的div標(biāo)簽,那么大概率可以說(shuō)是單頁(yè)面,CSR,客戶端渲染的網(wǎng)頁(yè)。
單一數(shù)據(jù)來(lái)源決定組件是否刷新是精細(xì)化最重要的方向。
class app extends React.PureComponent{ /////// } export default connect( (({xx,xxx,xxxx,xxxxx})) //// )(app)
一旦業(yè)務(wù)邏輯非常復(fù)雜的情況下,假設(shè)我們使用的是dva集中狀態(tài)管理,同時(shí)連接這么多的狀態(tài)樹(shù)模塊,那么可能會(huì)造成狀態(tài)樹(shù)模塊中任意的數(shù)據(jù)刷新導(dǎo)致這個(gè)組件被刷新,但是其實(shí)這個(gè)組件此時(shí)是不需要刷新的。
這里可以將需要的狀態(tài)通過(guò)根組件用props傳入,精確刷新的來(lái)源,單一可變數(shù)據(jù)來(lái)源追溯性強(qiáng),也更方便debug
單向數(shù)據(jù)流不可變數(shù)據(jù),通過(guò)immutable.js這個(gè)庫(kù)實(shí)現(xiàn)
import Immutable from require("immutable"); var map1: Immutable.Map; map1 = Immutable.Map({ a: 1, b: 2, c: 3 }); var map2 = map1.set("b", 50); map1.get("b"); // 2 map2.get("b"); // 50
不可變數(shù)據(jù),數(shù)據(jù)共享,持久化存儲(chǔ),通過(guò)is比較,每次map生成的都是唯一的 ,它們比較的是codehash的值,性能比通過(guò)遞歸或者直接比較強(qiáng)很多。在PureComponent淺比較不好用的時(shí)候
一般的組件,使用PureComponent減少重復(fù)渲染即可
PureComponent,平時(shí)我們創(chuàng)建 React 組件一般是繼承于 Component,而 PureComponent 相當(dāng)于是一個(gè)更純凈的 Component,對(duì)更新前后的數(shù)據(jù)進(jìn)行了一次淺比較。只有在數(shù)據(jù)真正發(fā)生改變時(shí),才會(huì)對(duì)組件重新進(jìn)行 render。因此可以大大提高組件的性能。
PureComponent部分源碼,其實(shí)就是淺比較,只不過(guò)對(duì)一些特殊值進(jìn)行了判斷:
function is(x: any, y: any) { return ( (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) ); }
新,會(huì)自上而下逐漸刷新整個(gè)子孫組件,這樣性能損耗重復(fù)渲染就會(huì)多出很多,所以我們不僅要單一數(shù)據(jù)來(lái)源控制組件刷新,偶爾還需要在shouldComponentUpdate中對(duì)比nextProps和this.props 以及this.state以及nextState.
路由懶加載+code-spliting,加快首屏渲染,也可以減輕服務(wù)器壓力,因?yàn)楹芏嗳丝赡茉L問(wèn)你的網(wǎng)頁(yè)并不會(huì)看某些路由的內(nèi)容
使用react-loadable,支持SSR,非常推薦,官方的lazy不支持SSR,這是一個(gè)遺憾,這里需要配合wepback4的optimization配置,進(jìn)行代碼分割
Tips:這里需要下載支持動(dòng)態(tài)import的babel預(yù)設(shè)包 @babel/plugin-syntax-dynamic-import ,它支持動(dòng)態(tài)倒入組件
webpack配置: optimization: { runtimeChunk: true, splitChunks: { chunks: "all" } }
import React from "react" import Loading from "./loading-window"http://占位的那個(gè)組件,初始加載 import Loadable from "react-loadable" const LoadableComponent = Loadable({ loader: () => import("./sessionWindow"),//真正需要加載的組件 loading: Loading, }); export default LoadableComponent
好了,現(xiàn)在路由懶加載組件以及代碼分割已經(jīng)做好了,而且它支持SSR。非常棒
由于純CSR的網(wǎng)頁(yè)一般不是很復(fù)雜,這里再介紹一個(gè)方面,那就是,能不用redux,dva等集中狀態(tài)管理的狀態(tài)就不上狀態(tài)樹(shù),實(shí)踐證明,頻繁更新?tīng)顟B(tài)樹(shù)對(duì)用戶體驗(yàn)來(lái)說(shuō)是影響非常大的。這個(gè)異步的過(guò)程,更耗時(shí)。遠(yuǎn)不如支持通過(guò)props等方式進(jìn)行組件間通信,原則上除了很多組件共享的數(shù)據(jù)才上狀態(tài)樹(shù),否則都采用其他方式進(jìn)行通信。
SSR,服務(wù)端渲染:這里也使用Node.js+express框架
const express= require("express") const app =express() const jade = require("jade") const result = *** const url path = *** const html = jade.renderFile(url, { data: result, urlPath })//傳入數(shù)據(jù)給模板引擎 app.get("/",(req,res)=>{ res.send(html)//直接吐渲染好的`html`文件拼接成字符串返回給客戶端 }) //RestFul接口 app.listen(3000,err=>{ //do something })
const PrerenderSPAPlugin = require("prerender-spa-plugin") new PrerenderSPAPlugin({ routes: ["/","/home","/shop"], staticDir: resolve(__dirname, "../dist"), }),
我覺(jué)得掘金上的神三元那篇文章就寫(xiě)得很好,后面我自己去逐步實(shí)現(xiàn)了一次,感覺(jué)對(duì)SSR對(duì)理解更為透徹,加上本來(lái)就每天在寫(xiě)Node.js,還會(huì)一點(diǎn)Next,Nuxt,服務(wù)端渲染,覺(jué)得大同小異。
服務(wù)端渲染本質(zhì),在服務(wù)端把代碼運(yùn)行一次,將數(shù)據(jù)提前請(qǐng)求回來(lái),返回運(yùn)行后的html文件,客戶端接到文件后,拉取js代碼,代碼注水,然后顯示,脫水,js接管頁(yè)面。
同構(gòu)直出代碼,可以大大降低首屏渲染時(shí)間,經(jīng)過(guò)實(shí)踐,根據(jù)不同的內(nèi)容和配置可以縮短40%-65%時(shí)間,但是服務(wù)端渲染會(huì)給服務(wù)器帶來(lái)壓力,所以折中根據(jù)情況使用。
以下是一個(gè)最簡(jiǎn)單的服務(wù)端渲染,服務(wù)端直接吐拼接后的html結(jié)構(gòu)字符串:
var express = require("express") var app = express() app.get("/", (req, res) => { res.send( `hello hello world
` ) }) app.listen(3000, () => { if(!err)=>{ console.log("3000監(jiān)聽(tīng)")? } })
只要客戶端訪問(wèn)localhost:3000就可以拿到數(shù)據(jù)頁(yè)面訪問(wèn)
//server.js // 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(); return ` {renderRoutes(routers)} ssr123 ${content}`; };
數(shù)據(jù)注水,脫水,保持客戶端和服務(wù)端store的一致性。
上面返回的script標(biāo)簽,里面已經(jīng)注水,將在服務(wù)端獲取到的數(shù)據(jù)給到了全局window下的context屬性,在初始化客戶端store時(shí)候我們給它脫水。初始化渲染使用服務(wù)端獲取的數(shù)據(jù)~
import thunk from "redux-thunk"; import { createStore, applyMiddleware } from "redux"; import reducers from "./reducers"; export const getClientStore = () => { const defaultState = window.context ? window.context.state : {}; return createStore(reducers, defaultState, applyMiddleware(thunk)); }; export const serverStore = () => { return createStore(reducers, applyMiddleware(thunk)); };
這里注意,在組件的componentDidMount生命周期中發(fā)送ajax等獲取數(shù)據(jù)時(shí)候,先判斷下?tīng)顟B(tài)樹(shù)中有沒(méi)有數(shù)據(jù),如果有數(shù)據(jù),那么就不要重復(fù)發(fā)送請(qǐng)求,導(dǎo)致資源浪費(fèi)。
多層級(jí)路由SSR
//路由配置文件,改成這種方式 import Home from "./containers/Home"; import Login from "./containers/Login"; import App from "./containers/app"; export default [ { component: App, routes: [ { path: "/", component: Home, exact: true, loadData: Home.loadData }, { path: "/login", component: Login, exact: true } ] } ];
入口文件路由部分改成:
server.js const content = renderToString(); client.js {renderRoutes(routers)} {renderRoutes(routers)}
后續(xù)可能有利用loader進(jìn)行CSS的服務(wù)端渲染以及helmet的動(dòng)態(tài)meta, title標(biāo)簽進(jìn)行SEO優(yōu)化等,今天時(shí)間緊促,就不繼續(xù)寫(xiě)SSR了。
構(gòu)建Electron極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。第一個(gè)提到的是sqlite,嵌入式關(guān)系型數(shù)據(jù)庫(kù),輕量型無(wú)入侵性,標(biāo)準(zhǔn)的sql語(yǔ)句,這里不做過(guò)多介紹。
PWA,漸進(jìn)性式web應(yīng)用,這里使用webpack4的插件,進(jìn)行快速使用,對(duì)于一些數(shù)據(jù)內(nèi)容不需要存儲(chǔ)數(shù)據(jù)庫(kù)的,但是卻想要一次拉取,多次復(fù)用,那么可以使用這個(gè)配置
serverce work也有它的一套生命周期通常我們?nèi)绻褂?Service Worker 基本就是以下幾個(gè)步驟:
首先我們需要在頁(yè)面的 JavaScript 主線程中使用 serviceWorkerContainer.register() 來(lái)注冊(cè) Service Worker ,在注冊(cè)的過(guò)程中,瀏覽器會(huì)在后臺(tái)啟動(dòng)嘗試 Service Worker 的安裝步驟。
如果注冊(cè)成功,Service Worker 在 ServiceWorkerGlobalScope 環(huán)境中運(yùn)行; 這是一個(gè)特殊的 worker context,與主腳本的運(yùn)行線程相獨(dú)立,同時(shí)也沒(méi)有訪問(wèn) DOM 的能力。
后臺(tái)開(kāi)始安裝步驟, 通常在安裝的過(guò)程中需要緩存一些靜態(tài)資源。如果所有的資源成功緩存則安裝成功,如果有任何靜態(tài)資源緩存失敗則安裝失敗,在這里失敗的不要緊,會(huì)自動(dòng)繼續(xù)安裝直到安裝成功,如果安裝不成功無(wú)法進(jìn)行下一步 — 激活 Service Worker。
開(kāi)始激活 Service Worker,必須要在 Service Worker 安裝成功之后,才能開(kāi)始激活步驟,當(dāng) Service Worker 安裝完成后,會(huì)接收到一個(gè)激活事件(activate event)。激活事件的處理函數(shù)中,主要操作是清理舊版本的 Service Worker 腳本中使用資源。
激活成功后 Service Worker 可以控制頁(yè)面了,但是只針對(duì)在成功注冊(cè)了 Service Worker 后打開(kāi)的頁(yè)面。也就是說(shuō),頁(yè)面打開(kāi)時(shí)有沒(méi)有 Service Worker,決定了接下來(lái)頁(yè)面的生命周期內(nèi)受不受 Service Worker 控制。所以,只有當(dāng)頁(yè)面刷新后,之前不受 Service Worker 控制的頁(yè)面才有可能被控制起來(lái)。
直接上代碼,存儲(chǔ)所有js文件和圖片 //實(shí)際的存儲(chǔ)根據(jù)自身需要,并不是越多越好。
const WorkboxPlugin = require("workbox-webpack-plugin") new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true, importWorkboxFrom: "local", include: [/.js$/, /.css$/, /.html$/, /.jpg/, /.jpeg/, /.svg/, /.webp/, /.png/], }),
PWA并不僅僅這些功能,它的功能非常強(qiáng)大,有興趣的可以去lavas看看,PWA技術(shù)對(duì)于經(jīng)常訪問(wèn)的老客戶來(lái)說(shuō),首屏渲染提升非常大,特別在移動(dòng)端,可以添加到桌面保存。666啊~,在pc端更多的是緩存處理文件~
使用react-lazyload,懶加載你的視窗初始看不見(jiàn)的組件或者圖片。
/開(kāi)箱即用的懶加載圖片 import LazyLoad from "react-lazyload"//這里配置表示占位符的樣式~。 記得在移動(dòng)端的滑動(dòng)屏幕或者PC端的調(diào)用forceCheck,動(dòng)態(tài)計(jì)算元素距離視窗的位置然后決定是否顯示真的圖片~ import { forceCheck } from "react-lazyload"; forceCheck()
懶加載組件
import { lazyload } from "react-lazyload"; //跟上面同理,不過(guò)是一個(gè)裝飾器,高階函數(shù)而已。一樣需要forcecheck() @lazyload({ height: 200, once: true, offset: 100 }) class MyComponent extends React.Component { render() { return大數(shù)據(jù)React渲染,擁有讓?xiě)?yīng)用擁有60FPS -非常核心的一點(diǎn)優(yōu)化this component is lazyloaded by default!; } }
List長(zhǎng)列表
]
react-virtualized-auto-sizer和windowScroll配合一起使用,達(dá)到頁(yè)面復(fù)雜效果+大數(shù)據(jù)渲染保持60FPS。上面的官網(wǎng)里有介紹這些組件~
高計(jì)算量的工作交給web wrok線程var myWorker = new Worker("worker.js"); first.onchange = function() { myWorker.postMessage([first.value,second.value]); console.log("Message posted to worker"); } second.onchange = function() { myWorker.postMessage([first.value,second.value]); console.log("Message posted to worker"); }
這段代碼中變量first和second代表2個(gè)元素;它們當(dāng)中任意一個(gè)的值發(fā)生改變時(shí),myWorker.postMessage([first.value,second.value])會(huì)將這2個(gè)值組成數(shù)組發(fā)送給worker。你可以在消息中發(fā)送許多你想發(fā)送的東西。
在worker中接收到消息后,我們可以寫(xiě)這樣一個(gè)事件處理函數(shù)代碼作為響應(yīng)(worker.js):
onmessage = function(e) { console.log("Message received from main script"); var workerResult = "Result: " + (e.data[0] * e.data[1]); console.log("Posting message back to main script"); postMessage(workerResult); }
onmessage處理函數(shù)允許我們?cè)谌魏螘r(shí)刻,一旦接收到消息就可以執(zhí)行一些代碼,代碼中消息本身作為事件的data屬性進(jìn)行使用。這里我們簡(jiǎn)單的對(duì)這2個(gè)數(shù)字作乘法處理并再次使用postMessage()方法,將結(jié)果回傳給主線程。
回到主線程,我們?cè)俅问褂胦nmessage以響應(yīng)worker回傳的消息:
myWorker.onmessage = function(e) { result.textContent = e.data; console.log("Message received from worker"); }
在這里我們獲取消息事件的data,并且將它設(shè)置為result的textContent,所以用戶可以直接看到運(yùn)算的結(jié)果。
注意: 在主線程中使用時(shí),onmessage和postMessage() 必須掛在worker對(duì)象上,而在worker中使用時(shí)不用這樣做。原因是,在worker內(nèi)部,worker是有效的全局作用域。
注意: 當(dāng)一個(gè)消息在主線程和worker之間傳遞時(shí),它被復(fù)制或者轉(zhuǎn)移了,而不是共享。
開(kāi)啟web work線程,其實(shí)也會(huì)損耗一定的主線程的性能,但是大量計(jì)算的工作交給它也未嘗不可,其實(shí)Node.js和javaScript都不適合做大量計(jì)算工作,這點(diǎn)有目共睹,尤其是js引擎和GUI渲染線程互斥的情況存在。充分合理利用React的Feber架構(gòu)diff算法優(yōu)化項(xiàng)目
requestAnimationFrame調(diào)用高優(yōu)先級(jí)任務(wù),中斷調(diào)度階段的遍歷,由于React的新版本調(diào)度階段是擁有三根指針的可中斷的鏈表遍歷,所以這樣既不影響下面的遍歷,也不影響用戶交互等行為。
使用requestAnimationFrame,當(dāng)頁(yè)面處于未激活的狀態(tài)下,該頁(yè)面的屏幕刷新任務(wù)會(huì)被系統(tǒng)暫停,由于requestAnimationFrame保持和屏幕刷新同步執(zhí)行,所以也會(huì)被暫停。當(dāng)頁(yè)面被激活時(shí),動(dòng)畫(huà)從上次停留的地方繼續(xù)執(zhí)行,節(jié)約 CPU 開(kāi)銷。
一個(gè)刷新間隔內(nèi)函數(shù)執(zhí)行多次時(shí)沒(méi)有意義的,因?yàn)轱@示器每 16.7ms 刷新一次,多次繪制并不會(huì)在屏幕上體現(xiàn)出來(lái)
在高頻事件(resize,scroll等)中,使用requestAnimationFrame可以防止在一個(gè)刷新間隔內(nèi)發(fā)生多次函數(shù)執(zhí)行,這樣保證了流暢性,也節(jié)省了函數(shù)執(zhí)行的開(kāi)銷
某些情況下可以直接使用requestAnimationFrame替代 Throttle 函數(shù),都是限制回調(diào)函數(shù)執(zhí)行的頻率
使用requestAnimationFrame也可以更好的讓瀏覽器保持60幀的動(dòng)畫(huà)
requestIdleCallback,這個(gè)API目前兼容性不太好,但是在Electron開(kāi)發(fā)中,可以使用,兩者還是有區(qū)別的,而且這兩個(gè)api用好了可以解決很多復(fù)雜情況下的問(wèn)題~。當(dāng)然你也可以用上面的api封裝這個(gè)api,也并不是很復(fù)雜。
當(dāng)關(guān)注用戶體驗(yàn),不希望因?yàn)橐恍┎恢匾娜蝿?wù)(如統(tǒng)計(jì)上報(bào))導(dǎo)致用戶感覺(jué)到卡頓的話,就應(yīng)該考慮使用requestIdleCallback。因?yàn)閞equestIdleCallback回調(diào)的執(zhí)行的前提條件是當(dāng)前瀏覽器處于空閑狀態(tài)。
圖中一幀包含了用戶的交互、js的執(zhí)行、以及requestAnimationFrame的調(diào)用,布局計(jì)算以及頁(yè)面的重繪等工作。
假如某一幀里面要執(zhí)行的任務(wù)不多,在不到16ms(1000/60)的時(shí)間內(nèi)就完成了上述任務(wù)的話,那么這一幀就會(huì)有一定的空閑時(shí)間,這段時(shí)間就恰好可以用來(lái)執(zhí)行requestIdleCallback的回調(diào),如下圖所示:
使用preload,prefetch,dns-prefetch等指定提前請(qǐng)求指定文件,或者根據(jù)情況,瀏覽器自行決定是否提前dns預(yù)解析或者按需請(qǐng)求某些資源。這里也可以webpack4插件實(shí)現(xiàn),目前京東在使用這個(gè)方案~
const PreloadWebpackPlugin = require("preload-webpack-plugin") new PreloadWebpackPlugin({ rel: "preload", as(entry) { if (/.css$/.test(entry)) return "style"; if (/.woff$/.test(entry)) return "font"; if (/.png$/.test(entry)) return "image"; return "script"; }, include:"allChunks" //include: ["app"] }),對(duì)指定js文件延遲加載~
普通的腳本
給script標(biāo)簽,加上async標(biāo)簽,遇到此標(biāo)簽,先去請(qǐng)求,但是不阻塞解析html等文件~,請(qǐng)求回來(lái)就立馬加載
給script標(biāo)簽,加上defer標(biāo)簽,延遲加載,但是必須在所有腳本加載完畢后才會(huì)加載它,但是這個(gè)標(biāo)簽有bug,不確定能否準(zhǔn)時(shí)加載。一般只給一個(gè)
寫(xiě)這篇時(shí)間太耗時(shí)間,而且論壇的在線編輯器到了內(nèi)容很多的時(shí)候,非???,React-native的以及一些細(xì)節(jié),后面再補(bǔ)充下面給出一些源碼和資料地址:
手寫(xiě)React優(yōu)化腳手架帶項(xiàng)目
react-ssr的源碼
手寫(xiě)Node.js原生靜態(tài)資源服務(wù)器
跨平臺(tái)Electron的demo
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105905.html
摘要:往往純的單頁(yè)面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...
摘要:往往純的單頁(yè)面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...
showImg(https://segmentfault.com/img/bVbw3tK?w=1240&h=827); 前端工程師這個(gè)崗位,真的是反人性的 我們來(lái)思考一個(gè)問(wèn)題: 一個(gè)6年左右經(jīng)驗(yàn)的前端工程師: 前面兩年在用jQuery 期間一直在用React-native(一步一步踩坑過(guò)來(lái)的那種) 最近兩年還在寫(xiě)微信小程序 下面一個(gè)2年經(jīng)驗(yàn)的前端工程師: 并不會(huì)跨平臺(tái)技術(shù),他的兩年工作都是Reac...
摘要:目前我們的業(yè)務(wù)項(xiàng)目采用的來(lái)進(jìn)行優(yōu)化和首屏性能提升。可變性需要讓開(kāi)發(fā)人員降低開(kāi)發(fā)時(shí)的基準(zhǔn)線,來(lái)保證每一個(gè)用戶的體驗(yàn)。對(duì)于路由的切分以及庫(kù)的引入來(lái)說(shuō),這一個(gè)原則至關(guān)重要??焖偕梢环菡军c(diǎn)的性能審查報(bào)告。 The Cost Of JavaScript 2018 關(guān)于原文 原文是在Medium上面看到的,Chrome工程師Addy Osmani發(fā)布的一篇文章,這位的Medium上面的自我介紹里...
閱讀 2301·2021-10-13 09:39
閱讀 3426·2021-09-30 09:52
閱讀 811·2021-09-26 09:55
閱讀 2782·2019-08-30 13:19
閱讀 1902·2019-08-26 10:42
閱讀 3198·2019-08-26 10:17
閱讀 552·2019-08-23 14:52
閱讀 3647·2019-08-23 14:39