標籤:

webpack必知必會

一些你不知道是什麼意思的東西(題外話)

webpack 應用編譯優化之路

webpack穩定moduleid和chunkid以實現持久化緩存的梳理 - 掘金

import path = require(path);

  • path.join
  • path.resolve
  • __dirname

path其實是node中的一個模塊,下面我們就將一下這幾個常見的東西。

path.join

path.join其實是對路徑進行拼接。

const path = require(path);nnlet str1 = path.join(./path/./, ./upload, /file, 123.jpg);nconsole.log(str1); // path/upload/file/123.jpgnnlet str2 = path.join(path, upload, file, 123.jpg);nconsole.log(str2); // path/upload/file/123.jpgn

使用了它之後就得到了一個拼接好的路徑

path.resolve

它是絕對路徑的操作

let myPath = path.resolve(path1, path2, a);nconsole.log(myPath); // E:/workspace/NodeJS/path1/path2/an

它的結果是絕對路徑,不懂絕對路徑和相對路徑的同學去查下相關知識哦。

這部分到這就結束了,下面可能涉及到這幾個東西的使用。

__dirname

使用__dirname變數獲取當前模塊文件所在目錄的完整絕對路徑

因為下面可能會碰到這幾個東西,所以我們稍微簡單提了一下。題外話到此結束,我們現在進入正題。

resolve配置

resolve.extensions

我們在編輯器上開發項目代碼,但這個傻慫編輯器IDE功能並不是很強,每次import進來的文件都沒有後綴名。如果你使用的是腳手架工具,你可能會發現一個有趣的地方,就是當我們引入js、jsx或者vue文件的時候,我們不需要加後綴就可以使用了。但是你寫了一些less或者sass引入到文件中,也沒有添加後綴,編譯直接報錯。這個其實就是resolve的問題。

resolve: {n // Add .ts and .tsx as resolvable extensions.n extensions: [".ts", ".tsx", ".js", ".json"]n}n

對resolve進行配置能設置模塊如何被解析。

這裡的extensions就是後綴的使用,我這裡默認是ts/tsx/js/json,這些文件在import時,不添加後綴是可以的,只需要在數組中添加你想要省略的後綴名就可以達到同樣的效果。比如下面這種

extensions: [.web.js, .mjs, .js, .json, .web.jsx, .jsx, .less]n

現在就算編輯器不給你添加後綴,你也不需要再加上後綴了,是不是省了很多事。

resolve.alias

這裡是創建 import 或 require 的別名,來確保模塊引入變得更簡單

resolve: {n alias: {n Utilities: path.resolve(__dirname, src/utilities/),n Temp: path.resolve(__dirname, src/templates/)n }n}n

之前你引入src/template裡面的1文件可能是這樣

import 1 from ../src/template/1;n

現在你可以這樣寫了

import 1 from Temp/1;n

這裡的路徑還是具體看你的文件路徑。不要照抄照搬哦。

這兩項是我個人覺得使用頻率比較多的,其他情況請去官方文檔查看下。

全局變數的使用

使用全局變數進行url的替換

現在項目還是在開發階段,你可能通過下面的介面獲取信息

http://www.xxx.com/test/v3 + 具體介面n

這個介面是放在測試伺服器上的,但項目一旦上線要使用線上伺服器

http://www.xxx.com/api/v4 + 具體介面n

你於是冒出了一個很傻X的想法,本地開發或者測試時使用上面的介面,等到上線的時候再將它改掉。鬼鬼,咱不能這麼秀。我給你提供一個好方法。

使用DefinePlugin插件來創建全局變數來解決這個問題

DefinePlugin允許你創建一個在編譯時可以配置的全局常量,我們下面創建一個名為url的全局變數,如果你是將開發和生產環境的webpack配置文件分開,你可以這樣寫

開發環境nplugins: [n new webpack.DefinePlugin({n url: JSON.stringify(http://www.xxx.com/test/v3)n })n]n生產環境nplugins: [n new webpack.DefinePlugin({n url: JSON.stringify(http://www.xxx.com/api/v4)n })n]n

如果你只有一個webpack配置文件,你也可以寫成這樣

plugins: [n new webpack.DefinePlugin({n url: process.env.NODE_ENV === production ?n JSON.stringify(http://www.xxx.com/api/v4) :n JSON.stringify(http://www.xxx.com/test/v3)n })n // 這裡其實涉及到一個問題,就是你在生產環境的時候必須增加命令修改process.n // env.NODE_ENV = production,否則上面的代碼是不生效的n // 在package.json的scripts對象中可以使用,使用方法見我上一篇webpack的文章n]n

由於這個變數必須包含字元串引號,所以你要麼使用"你的變數內容", 或者使用 JSON.stringify(你的變數內容)這種形式。

現在你在項目中的介面url就可以寫成這樣了

`${url}/介面信息` // es6的字元串模板應該都懂吧?n

你console.log(url)也是可以的哦,現在我們就解決了這個噁心的問題。

但是還有更噁心的問題等著我們,哈哈。真滴煩!!!

如果你的項目是腳手架搭建,往往會有eslint,eslint不進行設置是沒辦法使用這個全局變數的,找到eslint配置文件,添加如下代碼

"globals": {n "go": truen}n

多入口文件打包

為什麼要設置多文件入口打包

在很多情況下,我們都是用當下流行的框架進行web開發,比如vue、react。在開發一段時間過後,測試ok,我們準備build項目了,但是打包之後文件是4.5MB,這玩意對pc或者移動來說都不是一個很好的體驗。如果我們不去管它,那每次我們改版或其他的一些情況,用戶都需要去重新載入4.5mb的文件,哪怕你只是修改了一行代碼。

多入口實現第三方庫文件的單獨打包

使用多入口文件配合插件解決此問題

entry: {n vendor: [react, react-dom],n app: "./src/index.tsx"n}n

這裡我們設置了兩個入口,一個是app,就是我們傳統使用的入口文件。而vendor使用的是一個數組,我們把react和react-dom單獨提取出來進行打包。這些庫我們基本上是不會改動源代碼的,如果我們把它們單獨打包出來,即便我們修改了項目的代碼,react和react-dom的代碼都不需要變,這時瀏覽器都直接讀取vendor文件的緩存就可以了,減少了每次載入資源的體積,增強了用戶體驗。

配合插件CommonsChunkPlugin使用

只是增加入口文件是不管用的,我們需要使用插件把vendor文件從app文件中剝離出來

plugins: [n new webpack.optimize.CommonsChunkPlugin(vendor)n]n

現在我們已經把vendor和app文件分割了。這裡只是舉了一個簡單的栗子,小夥伴可以根據自己的需求自己進行配置。

我們也可以把公共組件進行一個單獨的打包,這裡不再贅述,感興趣的小夥伴可以自己試驗哦。

html-webpack-plugin

之前的文章其實已經介紹過這個插件了,但這次我們稍微具體的說一下。我們使用腳手架生成的index.html其實你是找不到script標籤引入js文件的,可能你也想這樣做,自己動手引入真的麻煩。html-webpack-plugin來幫你

plugins: [n // Generates an `index.html` file with the <script> injected.n new HtmlWebpackPlugin({n inject: true, // 這個配置項為true表示自動把打包出來的文件通過自動生成script標籤添加到html中n template: index.html, // 模板文件,其實如果沒有特殊要求,可以考慮就是用原本的html文件,不再單獨創建模板。n minify: { // 壓縮的配置,感興趣的同學意義自己查下意思n removeComments: true,n collapseWhitespace: true,n removeRedundantAttributes: true,n useShortDoctype: true,n removeEmptyAttributes: true,n removeStyleLinkTypeAttributes: true,n keepClosingSlash: true,n minifyJS: true,n minifyCSS: true,n minifyURLs: true,n },n })n]n

你的模板html文件是這樣

<!DOCTYPE html>n<html>n<head>n <meta charset="UTF-8" />n <title>Hello React!</title>n</head>n<body>n<div id="example"></div>nn<!-- Main -->n</body>n</html>n

通過使用上面的html-webpack-plugin配置它就變成了這樣

<!DOCTYPE html><html><head><meta charset="UTF-8"/><title>Hello React!</title></head><body><div id="example"></div><script type="text/javascript" src="vendor.19786c9df38012fdca96.js"></script><script type="text/javascript" src="app.19786c9df38012fdca96.js"></script></body></html>n

這樣其實就是和你用腳手架搭建的一毛一樣了。

講一些項目中會出現的問題--devServer的使用

我們在使用webpack時,常常會用到webpack-dev-server。它給我們提供了一個server,使我們的項目能跑server上。

historyApiFallback

現在項目在本地正常運行,你看了下地址欄

localhost:8080/#n

leader說#是什麼鬼?必須去掉,你見過誰的網址帶#?

這個#其實是hash路由進行路由跳轉的依託。它是可以去掉的。react4是使用BroswerRouter替換HashRouter即可,vue的話同學去查一下即可。去掉#之後我們使用的就是h5的history模式進行路由跳轉了。

你以為這樣就行了么?

但是老闆又有新要求了,我們的頁面不是放在根目錄下。網址是這樣xxx.com/xxx,這個時候你要想頁面放在伺服器上能正常使用就需要在index.html添加base標籤。

你信心滿滿的進行試驗,發現,我擦,報錯了,連頁面都找不到了。

這個原因是因為現在在這個路徑下我們是找不到資源文件,會報404的問題,這個時候我們必須要設置

devServer: {n historyApiFallback: truen }n

這個東西就是告訴webpack-dev-server,再找不到文件的時候默認指向index.html,底層的東西我並沒有去深入查詢,感興趣的同學可以去查下。

有些情況下,我們可能會起不止一個服務,這個時候埠往往就會衝突,添加port屬性,修改下埠即可解決衝突問題。

devServer: {n historyApiFallback: true,n port: 1234n }n

現在,地址就變成了localhost: 1234了。

devServer其實還有很多配置項,感興趣的同學可以去官網查看

webpack的熱替換問題

熱替換俗稱HMR,即只更新你修改的局部內容,而不刷新整個頁面,大大提高開發效率。這個只能在開發時使用哦,下面簡單說一下用法。後面直接說react和vue的使用。

const webpack = require(webpack);nn module.exports = {n entry: {n app: ./src/index.jsn },n devServer: {n hot: truen },n plugins: [n new webpack.HotModuleReplacementPlugin()n ]n

下面是index.js文件需要的配置(這裡覺得比較雞肋,因為如果你不把js全寫在一起,你就要每個文件都要添加這個東東。)

+ if (module.hot) {n+ module.hot.accept(./app.js, function() {n+ console.log(Accepting the updated printMe module!);n+ printMe();n+ })n+ }n

module.hot.accept 接受兩個參數,第一個參數是修改的文件,第二個是會掉函數。這裡只要app.js修改,就會觸發回調。

我們大多數情況下是使用react或者vue的,在這種情況下,你使用webpack的HMR是不起作用的。因為它無法保存這些框架的狀態。

react和vue給我們提供了解決方案

  • react-hot-loader
  • vue-loader 同學們可以在npm中搜索這兩個東西的用法,都有對應的使用。

此部分webpack配置要去npm找到對應的loader看,很簡單。我主要講我當時很困惑的一點。

我主要講一下react的,就是在使用路由和redux的情況下,我們的index.js文件應該怎麼寫。

import React from react;nimport ReactDOM from react-dom;nimport ./index.css;nimport { AppContainer} from react-hot-loadernimport registerServiceWorker from ./registerServiceWorker;nimport { createStore, applyMiddleware, compose } from redux;nimport thunk from redux-thunk;nimport reducers from "./reducer";nimport "./index.css";nimport App from "./App";nimport ./style/style;nn// redux的配置,可以忽略nconst store = createStore(reducers, compose(n applyMiddleware(thunk),n window.devToolsExtension ? window.devToolsExtension() : ()=>{}n));nnregisterServiceWorker();nn使用AppContainer包裹根組件即可nnconst render = Component => {n ReactDOM.render(n <AppContainer>n <Component store={store}/> n </AppContainer>,n document.getElementById(root)n )n};nrender(App);n// App作為內容的根組件,redux和router全部放在裡面,只要有內容修改,就調用render函數nif (module.hot) {n module.hot.accept(./App, () => { render(App) })n}n

App文件

import React, { Component } from react;nimport logo from ./logo.svg;nimport ./App.css;nimport { BrowserRouter, Switch, Route } from react-router-dom;nimport { Provider } from react-redux;nnimport Second from "./second";nimport Third from "./Third";nimport Header from "./header";nimport First from "./first";nclass App extends Component {n render() {n return (n <Provider store={this.props.store}>n <BrowserRouter className="App">n <div>n <Header />n <Switch>n <Route exact path="/" component={First}/>n <Route path="/second" component={Second}/>n <Route path="/third" component={Third}/>n </Switch>n </div>n </BrowserRouter>n </Provider>n );n }n}nnexport default App;n

我告你,現在你開發,根本不需要頁面刷新,超爽的。修改哪裡,哪裡變化。

推薦閱讀:

用 webpack 構建 node 後端代碼,使其支持 js 新特性並實現熱重載
Start React with Webpack
移動端網頁調試神器Eruda使用技巧

TAG:webpack |