情景描述
- 客戶要求我們首次打開頁面的時候必須在1s之內(nèi)
- 可是項目很大,首頁加載模塊體積大首屏加載時間長
- 為了解決這個問題項目中使用
按需加載路由配置這種方式 - 將router寫成了如下的樣子
import {AppContainer} from 'containers'
export const routes = {
const errorLoading = _ => _ // console.error('Dynamic loading failed' + err)
const loadRoute = cb =>
module => cb(null, module.default)
export const routes = {
component: AppContainer,
path: '/',
indexRoute: {
getComponent(location, callback) {
import('./containers/User/LandingPageContainer')
.then((module) => {
callback(null, module.default)
})
.catch(err => errorLoading(err))
}
},
childRoutes: [
{
path: '/admin',
getChildRoutes(partialNextState, callback) {
import('./containers/Admin/routes')
.then(loadRoute(callback))
.catch(err => errorLoading(err))
},
onEnter: checkInvalidRoleAndRedirect
},
{
path: '/wms/create',
getComponent(nextState, callback) {
import('./containers/WMS/CreateOrderContainer')
.then(loadRoute(callback))
.catch(err => errorLoading(err))
}
}
]
}
- 導(dǎo)致我完全不能明白。。。。
解釋
- Q1:這種寫法和以前的使用標簽區(qū)別是啥呢?
<BrowserRouter>
<Route path="/tacos" component={Tacos}/>
</div>
</BrowserRouter>
-
A1:區(qū)別:
- 使用標簽的寫法webpack會把所有的文件全部打包在一個main.*****.js文件,也就是說這時候龐大的項目中所有js代碼全部都被打包到一個js文件中。會造成的問題
- 當用戶請求首頁,首頁的代碼以及項目中所有的代碼全部都在
main.****.js文件中。 - 為了將首頁render出來,必須將這個非常大的js文件請求回來
- 因此非常耗時
- 當用戶請求首頁,首頁的代碼以及項目中所有的代碼全部都在
- 使用項目中的寫法
- webpack會通過
import()這個方法作為code spliting的分割點,然后將龐大的項目根據(jù)分割點分成若干的chunk.js文件。 - 然后當你訪問首頁的時候
- 我們只需要請求首頁的chunk.js文件即可
- 這時候每次請求的內(nèi)容大大減少,時間就會減小
- webpack會通過
- 使用標簽的寫法webpack會把所有的文件全部打包在一個main.*****.js文件,也就是說這時候龐大的項目中所有js代碼全部都被打包到一個js文件中。會造成的問題
Q2: 按需加載又是什么樣的概念呢?
A2: 按需加載很好理解,需要什么加載什么,問題在于如何保證你需要的都能加載進來,你不需要的都不加載進來呢?重點在于我們通過webpack將文件打包成靜態(tài)文件,部署到服務(wù)器,要做到按需加載(按需請求),就必須要求webpack做到合適的分隔項目并且將他們打包到不同的文件(chunk文件中)所以要實現(xiàn)好的按需加載必須要要求webpack能夠做好將不同的代碼打包到不同的文件。
- Q3:那么webpack又是如何分割項目并打包到不同的文件中呢?
- A3:webpack提供了
code splitting特性,此特性能夠把代碼分離到不同的bundle文件實現(xiàn)按需加載。在webpack的官網(wǎng)介紹了三種實現(xiàn)code splitting,采用動態(tài)導(dǎo)入的方式,結(jié)合es6提出的import(和promise)作為code splitting分割點,將不同本部分分別打包在不同的chunk文件中。- ps:webpack主要有兩種代碼分割的分割點選擇方式:
- 1.import
- 2.require.ensure
- ps:webpack主要有兩種代碼分割的分割點選擇方式:
- Q4:import是什么和我們之前的模塊引入使用import不是一個東西嗎?
- A4:兩種import分別是同步和異步加載模塊(也可以看做靜態(tài)和動態(tài)的引入模塊),兩種引入方式的最大區(qū)別在:
-
import App from './component/app':編譯時加載模塊,因此必須寫在每個模塊的最上方,寫在編譯時候不會執(zhí)行的地方就會失效(比如寫在if里面),只能做靜態(tài)引入不能做動態(tài)引入 -
import('./component/app').then((module)=>{console.log('this is dynamic import module',module.default)}).catch(err=> 'dynamic import failed'):是運行時加載模塊(和require一樣),所以只有import()和require能夠做到動態(tài)引入- require和import之間的區(qū)別又是什么呢?
- require:是同步的
- import:返回的是promise是異步的
- require和import之間的區(qū)別又是什么呢?
-
- Q5:那么使用webpack做按需加載需要配置什么嗎? 代碼中又要做什么樣的改變呢?
- A5:改變需要分成兩個部分:
- webpack配置上的改變:配置需要做的改變基本沒有,頂多就是配置一下chunk文件的名字
}```js entry: { chunkFilename: '[name].[chunkhash:5].chunk.js', //給生成的chunk文件確定命名規(guī)范,name在沒有設(shè)置的時候默認使用id //chunkhash:5 5位hash碼- 代碼上的改變:這里主要針對react實現(xiàn)按需加載,如果想要對react實現(xiàn)按需加載,那么就需要webpack和react-router之間的配合。so react-router實現(xiàn)按需加載該怎么配置(主要分成兩個部分): - 實現(xiàn)組件的按需引入:react-router提供了方法代替相應(yīng)屬性實現(xiàn)對相應(yīng)路由組件的異步掛載 - getComponent: 代替Route標簽的component實現(xiàn)當訪問到當前路徑的時候再執(zhí)行該方法加載組件 - 參數(shù)有兩個 - location:是一個對象包含當前的所有路由中能獲取的信息 - callback:回調(diào)函數(shù),用于異步引入組件成功后調(diào)用 - 參數(shù)有兩個: - error(如果沒有error這個參數(shù)在調(diào)用的時候?qū)懗蒼ull,如果不寫成null,那么在當前路由下就會報出Require.ensure error的錯誤) - 組件:請注意如果你是用的是import引入組件那么promise返回的數(shù)據(jù)應(yīng)該是整個module對象,而不是你的組件 - 如果在module使用default暴露:那么callback的第二參數(shù)必須是module.default - 如果在module中使用export const a、export const那么第二個參數(shù)必須是module.a或者module.b - getChildRoutes:代替某個Route的子組件實現(xiàn)異步加載child組件(大有作用??! 下一篇簡書介紹) - getIndexRoute: 但是實踐過后發(fā)現(xiàn)使用失敗不知道為什么,所以換成了`indexRoute: { getComponent()}` - 寫明code splitting的分割點:之前提到過有兩種分割方式 - 使用webpack 提供的require.ensure實現(xiàn):用這個方法定義分割點獨立打包chunk - 使用es6的動態(tài)import實現(xiàn):參數(shù)是引入module的地址 方法返回promise 回調(diào)函數(shù)的參數(shù)中包含module ```js import AppContainer from './components/AppContainer' import LandingPageContainer from './containers/User/LandingPageContainer' import CreateOrderContainer from './containers/WMS/CreateOrderContainer' <Router> <Route path='/' component={AppContainer}> <IndexRoute component={LandingPageContainer} /> <Route path='/wms/creat' component={CreateOrderContainer} /> </Route> </Router> //原來的寫法 所有的組件都是靜態(tài)引入,也就意味這在編譯的時候就必須將所有引入的文件(也就是項目中所有的組件)打包在一個js文件中,當瀏覽器沒有緩存的時候,第一次請求就要把一個很大的js文件請求回來
- Q6:那么對于第三方模塊如何實現(xiàn)性能的提升呢?
- A6: 采用將所有的第三方庫的全部都打包到一個js文件,原因:
- 由于這一部分內(nèi)容不怎么需要變化,所以打包好之后就放在一個js文件中不動了
- 這樣既有利于緩存而且不需要重復(fù)打包。
- 但是剝離第三方庫主要對于開發(fā)的時候會加快打包速度
- 可以使用commonChunkPlugin來實現(xiàn)