以前寫了一篇媽媽不再用擔憂我不會webpack了,此次繼續對其進行補充。本文依舊是遵循直觀易懂的規則進行書寫。但願對你們有幫助。若是不太熟悉webpack能夠先看看以前的文章媽媽不再用擔憂我不會webpack了。下面咱們由淺入深來介紹webpack的使用javascript
import path = require('path');css
path實際上是node中的一個模塊,下面咱們就將一下這幾個常見的東西。html
path.join實際上是對路徑進行拼接。vue
const path = require('path');
let str1 = path.join('./path/./', './upload', '/file', '123.jpg');
console.log(str1); // path/upload/file/123.jpg
let str2 = path.join('path', 'upload', 'file', '123.jpg');
console.log(str2); // path/upload/file/123.jpg複製代碼
使用了它以後就獲得了一個拼接好的路徑java
它是絕對路徑的操做node
let myPath = path.resolve('path1', 'path2', 'a');
console.log(myPath); // E:/workspace/NodeJS/path1/path2/a複製代碼
它的結果是絕對路徑,不懂絕對路徑和相對路徑的同窗去查下相關知識哦。
這部分到這就結束了,下面可能涉及到這幾個東西的使用。react
使用__dirname變量獲取當前模塊文件所在目錄的完整絕對路徑
由於下面可能會碰到這幾個東西,因此咱們稍微簡單提了一下。題外話到此結束,咱們如今進入正題。webpack
咱們在編輯器上開發項目代碼,但這個傻慫編輯器IDE功能並非很強,每次import進來的文件都沒有後綴名。若是你使用的是腳手架工具,你可能會發現一個有趣的地方,就是當咱們引入js、jsx或者vue文件的時候,咱們不須要加後綴就可使用了。可是你寫了一些less或者sass引入到文件中,也沒有添加後綴,編譯直接報錯。這個其實就是resolve的問題。es6
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
}複製代碼
對resolve進行配置能設置模塊如何被解析。
這裏的extensions就是後綴的使用,我這裏默認是ts/tsx/js/json,這些文件在import時,不添加後綴是能夠的,只須要在數組中添加你想要省略的後綴名就能夠達到一樣的效果。好比下面這種web
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx', '.less']複製代碼
如今就算編輯器不給你添加後綴,你也不須要再加上後綴了,是否是省了不少事。
這裏是建立 import 或 require 的別名,來確保模塊引入變得更簡單
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Temp: path.resolve(__dirname, 'src/templates/')
}
}複製代碼
以前你引入src/template裏面的1文件多是這樣
import 1 from '../src/template/1';複製代碼
如今你能夠這樣寫了
import 1 from 'Temp/1';複製代碼
這裏的路徑仍是具體看你的文件路徑。不要照抄照搬哦。
這兩項是我我的以爲使用頻率比較多的,其餘狀況請去官方文檔查看下。
如今項目仍是在開發階段,你可能經過下面的接口獲取信息
http://www.xxx.com/test/v3 + 具體接口複製代碼
這個接口是放在測試服務器上的,但項目一旦上線要使用線上服務器
http://www.xxx.com/api/v4 + 具體接口複製代碼
你因而冒出了一個很傻X的想法,本地開發或者測試時使用上面的接口,等到上線的時候再將它改掉。鬼鬼,咱不能這麼秀。我給你提供一個好方法。
使用DefinePlugin插件來建立全局變量來解決這個問題
DefinePlugin容許你建立一個在編譯時能夠配置的全局常量,咱們下面建立一個名爲url的全局變量,若是你是將開發和生產環境的webpack配置文件分開,你能夠這樣寫
開發環境
plugins: [
new webpack.DefinePlugin({
url: JSON.stringify('http://www.xxx.com/test/v3')
})
]複製代碼
生產環境
plugins: [
new webpack.DefinePlugin({
url: JSON.stringify('http://www.xxx.com/api/v4')
})
]複製代碼
若是你只有一個webpack配置文件,你也能夠寫成這樣
plugins: [
new webpack.DefinePlugin({
url: process.env.NODE_ENV === 'production' ?
JSON.stringify('http://www.xxx.com/api/v4') :
JSON.stringify('http://www.xxx.com/test/v3')
})
// 這裏其實涉及到一個問題,就是你在生產環境的時候必須增長命令修改process.
// env.NODE_ENV = production,不然上面的代碼是不生效的
// 在package.json的scripts對象中可使用,使用方法見我上一篇webpack的文章
]複製代碼
因爲這個變量必須包含字符串引號,因此你要麼使用'"你的變量內容"', 或者使用 JSON.stringify('你的變量內容')這種形式。
如今你在項目中的接口url就能夠寫成這樣了
`${url}/接口信息` // es6的字符串模板應該都懂吧?複製代碼
你console.log(url)也是能夠的哦,如今咱們就解決了這個噁心的問題。
可是還有更噁心的問題等着咱們,哈哈。真滴煩!!!
若是你的項目是腳手架搭建,每每會有eslint,eslint不進行設置是沒辦法使用這個全局變量的,找到eslint配置文件,添加以下代碼
"globals": {
"go": true
}複製代碼
在不少狀況下,咱們都是用當下流行的框架進行web開發,好比vue、react。在開發一段時間事後,測試ok,咱們準備build項目了,可是打包以後文件是4.5MB,這玩意對pc或者移動來講都不是一個很好的體驗。若是咱們不去管它,那每次咱們改版或其餘的一些狀況,用戶都須要去從新加載4.5mb的文件,哪怕你只是修改了一行代碼。
使用多入口文件配合插件解決此問題
entry: {
vendor: ['react', 'react-dom'],
app: "./src/index.tsx"
}複製代碼
這裏咱們設置了兩個入口,一個是app,就是咱們傳統使用的入口文件。而vendor使用的是一個數組,咱們把react和react-dom單獨提取出來進行打包。這些庫咱們基本上是不會改動源代碼的,若是咱們把它們單獨打包出來,即使咱們修改了項目的代碼,react和react-dom的代碼都不須要變,這時瀏覽器都直接讀取vendor文件的緩存就能夠了,減小了每次加載資源的體積,加強了用戶體驗。
配合插件CommonsChunkPlugin使用
只是增長入口文件是無論用的,咱們須要使用插件把vendor文件從app文件中剝離出來
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor')
]複製代碼
如今咱們已經把vendor和app文件分割了。這裏只是舉了一個簡單的栗子,小夥伴能夠根據本身的需求本身進行配置。
咱們也能夠把公共組件進行一個單獨的打包,這裏再也不贅述,感興趣的小夥伴能夠本身試驗哦。
以前的文章其實已經介紹過這個插件了,但此次咱們稍微具體的說一下。咱們使用腳手架生成的index.html其實你是找不到script標籤引入js文件的,可能你也想這樣作,本身動手引入真的麻煩。html-webpack-plugin來幫你
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true, // 這個配置項爲true表示自動把打包出來的文件經過自動生成script標籤添加到html中
template: index.html, // 模板文件,其實若是沒有特殊要求,能夠考慮就是用本來的html文件,再也不單首創建模板。
minify: { // 壓縮的配置,感興趣的同窗意義本身查下意思
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
})
]複製代碼
你的模板html文件是這樣
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
</head>
<body>
<div id="example"></div>
<!-- Main -->
</body>
</html>複製代碼
經過使用上面的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>複製代碼
這樣其實就是和你用腳手架搭建的一毛同樣了。
咱們在使用webpack時,經常會用到webpack-dev-server。它給咱們提供了一個server,使咱們的項目能跑server上。
如今項目在本地正常運行,你看了下地址欄
localhost:8080/#複製代碼
leader說#是什麼鬼?必須去掉,你見過誰的網址帶#?
這個#實際上是hash路由進行路由跳轉的依託。它是能夠去掉的。react4是使用BroswerRouter替換HashRouter便可,vue的話同窗去查一下便可。去掉#以後咱們使用的就是h5的history模式進行路由跳轉了。
你覺得這樣就好了麼?
可是老闆又有新要求了,咱們的頁面不是放在根目錄下。網址是這樣www.xxx.com/xxx,這個時候你要想頁面放在服務器上能正常使用就須要在index.html添加base標籤。
你信心滿滿的進行試驗,發現,我擦,報錯了,連頁面都找不到了。
devServer: {
historyApiFallback: true
}複製代碼
這個東西就是告訴webpack-dev-server,再找不到文件的時候默認指向index.html,底層的東西我並無去深刻查詢,感興趣的同窗能夠去查下。
有些狀況下,咱們可能會起不止一個服務,這個時候端口每每就會衝突,添加port屬性,修改下端口便可解決衝突問題。
devServer: {
historyApiFallback: true,
port: 1234
}複製代碼
如今,地址就變成了localhost: 1234了。
devServer其實還有不少配置項,感興趣的同窗能夠去官網查看
熱替換俗稱HMR,即只更新你修改的局部內容,而不刷新整個頁面,大大提升開發效率。這個只能在開發時使用哦,下面簡單說一下用法。後面直接說react和vue的使用。
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
devServer: {
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]複製代碼
下面是index.js文件須要的配置(這裏以爲比較雞肋,由於若是你不把js全寫在一塊兒,你就要每一個文件都要添加這個東東。)
+ if (module.hot) {
+ module.hot.accept('./app.js', function() {
+ console.log('Accepting the updated printMe module!');
+ printMe();
+ })
+ }複製代碼
module.hot.accept 接受兩個參數,第一個參數是修改的文件,第二個是會掉函數。這裏只要app.js修改,就會觸發回調。
咱們大多數狀況下是使用react或者vue的,在這種狀況下,你使用webpack的HMR是不起做用的。由於它沒法保存這些框架的狀態。
react和vue給咱們提供瞭解決方案
同窗們能夠在npm中搜索這兩個東西的用法,都有對應的使用。
此部分webpack配置要去npm找到對應的loader看,很簡單。我主要講我當時很困惑的一點。
我主要講一下react的,就是在使用路由和redux的狀況下,咱們的index.js文件應該怎麼寫。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { AppContainer} from 'react-hot-loader'
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from "./reducer";
import "./index.css";
import App from "./App";
import './style/style';
// redux的配置,能夠忽略
const store = createStore(reducers, compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : ()=>{}
));
registerServiceWorker();
使用AppContainer包裹根組件便可
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component store={store}/>
</AppContainer>,
document.getElementById('root')
)
};
render(App);
// App做爲內容的根組件,redux和router所有放在裏面,只要有內容修改,就調用render函數
if (module.hot) {
module.hot.accept('./App', () => { render(App) })
}複製代碼
App文件
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import Second from "./second";
import Third from "./Third";
import Header from "./header";
import First from "./first";
class App extends Component {
render() {
return (
<Provider store={this.props.store}>
<BrowserRouter className="App">
<div>
<Header />
<Switch>
<Route exact path="/" component={First}/>
<Route path="/second" component={Second}/>
<Route path="/third" component={Third}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
);
}
}
export default App;複製代碼
我告你,如今你開發,根本不須要頁面刷新,超爽的。修改哪裏,哪裏變化。謝謝你們