還在爲webpack的配置而煩惱嗎?這裏有一份webpack從簡易到高級版本的配置。還附贈配置地址,你想要嗎?不,你不想。老老實實本身配置去吧。css
壓箱底的筆記而已,你們看看樂樂就行了,這是筆者爲了練習webpack而嘗試了不一樣的配置方式,參考了create-react-app的webpack配置。以及學習瞭如何本身寫一個簡易的proxy。html
Loader | 做用 |
---|---|
html-loader | --- |
html-webpack-plugin | --- |
style-loader | --- |
mini-css-extract-plugin | 劃重點,webpack4.0以後再也不使用extract-text-webpack-plugin |
css-loader | 一個將CSS變成JS的loader,筆者認爲它的modules模塊化是一個很實用的功能,大愛 |
sass-loader | 一個SASS的處理器,先將scss編譯成css,而後css再作進一步的處理 |
node-sass | 編譯scss依賴的包 |
postcss-loader | 一款配合autoprefix,autoprefixer.github.io/ 自動給CSS加惱人的前綴 |
ts-loader | 若是不用babel編譯ts,則須要ts-loader |
file-loader | 導入文件,好比json,變成js的格式 |
url-loader | 相似於file-loader,不過比file-loader智能,在文件過大的狀況下能夠只加載一個地址,而不用將文件載入 |
babel-loader | 別說了,es6就須要他編譯 |
webpack | 你們都懂的,核心 |
webpack-cli | 有了啓動編譯變得簡單 |
如今二進制能夠直接編譯jsnode
npx babel src --out-dir lib
複製代碼
大聲告訴我Babel是幹什麼的? ——由於JS語法一直在修訂進步,而用戶使用的瀏覽器更新頻率不如JS語法更新的快,所以須要一個編譯JS語法,使兼容支持不一樣時期JS語法的瀏覽器。react
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
npm install --save-dev bable-loader
複製代碼
@babel/polyfill | vs | @babel/preset-env |
---|---|---|
無視語法,直接require缺失api | 救得了各類新穎寫法(如箭頭函數),救不了api | |
核心包core/js |
babel-preset-es2015
babel-preset-es2016
babel-preset-es2017
babel-preset-latest
A combination of the above ^
....
複製代碼
噩夢般的配置,剛學習babel的時候,看到這些配置,隱約以爲本身學不會了。可是如今babel-preset-env
就能夠搞定全部了!。webpack
@babel/plugin-proposal-class-properties babeljs.io/docs/en/bab…git
這個包已經被babel給移除了,它僅僅是core-js的別名。es6
多安裝一個babel便可github
npm install --save-dev @babel/preset-react
npm install --save-dev react react-dom
複製代碼
npm install --save-dev typescript
複製代碼
ts能夠用babel編譯web
npm install --save-dev @babel/preset-typescript
複製代碼
ts也能夠用ts-loader編譯,須要配置tsconfig.jstypescript
npm install --save-dev ts-loader
複製代碼
CSS loader能夠很簡單,也能夠至關複雜,通常的需求有如下幾點:
參考create-react-app的配置文件,寫的一個一本知足的css loader大餐:
// mini-css-extract-plugin,有了他能夠代替style-loader,不只壓縮了文件,還能夠幫助咱們將CSS從js中剝離出來
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//loaders的主體,按照loader的順序應該是先是預處理,好比Scss,而後是加前綴prefix,到此爲止你們仍是CSS的樣子,等到了css-loader,css就變成了js,最後style-loader或者minicss將css模塊再變成js生成文件或者內聯。
const getStyleLoaders = (cssOptions, preProcessor,env) => {
const loaders = [
// 此處是最後一步,將CSS提取出或者內聯
{loader:env==="development"?require.resolve('style-loader') : MiniCssExtractPlugin.loader},
// 此處將CSS變成標準的JS模塊,若是有css模塊化的需求,是在此處理
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
// 此處將CSS預處理處,通常是給css加上惱人的前綴,這裏能夠設置瀏覽器的版本,你須要哪些瀏覽器的支持
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
},
},
];
// 此處若是有sass等預處理的需求,須要在此配置
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};
// style files regexes,這裏是爲了不重複配置css的各類類型
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// 導出給webpack配置使用
module.exports={getStyleLoaders,cssRegex,cssModuleRegex,sassRegex,sassModuleRegex}
複製代碼
npm install --save-dev webpack-dev-server
複製代碼
devServer: {
contentBase: path.join(__dirname, 'dist1'),
compress: true,
hot: true,
port: 9000
},
entry: [
require.resolve('webpack-dev-server/client') + '?/',
require.resolve('webpack/hot/dev-server'),
path.resolve(__dirname,'src/index')
],
plugins: [
new webpack.HotModuleReplacementPlugin()
]
複製代碼
devServer: {
proxy: {
'/slider': {
target: 'http://www.cherryvenus.com/',
secure: false,
changeOrigin: true//important 能解決大多數404沒法訪問的問題
}
}
},
複製代碼
npm install --save-dev express webpack-hot-middleware webpack-dev-middleware
複製代碼
client,webpack,此處不能忘,其實就是一個連接websocket的一個相似於插件的程序,記得production的時候將此處移除,否則bundle會很大呢。
entry: [
require.resolve('webpack-hot-middleware/client') + '?path=/__what&timeout=2000&overlay=false',
path.resolve(__dirname,'src/index')
],
plugins: [
new webpack.HotModuleReplacementPlugin()
]
複製代碼
server,此處的path配置須要和客戶端的path一致。
app.use(require("webpack-hot-middleware")(compiler,{
log: false,
path: "/__what",
heartbeat: 2000
}));
複製代碼
entry入口的client添加模塊,不accept,程序沒法自動刷新。
if(module.hot){
module.hot.accept()
}
複製代碼
npm install --save-dev http-proxy-middleware
複製代碼
var proxyMiddleWare = require("http-proxy-middleware");
var proxyPath = "http://www.cherryvenus.com/";
var proxyOption ={target:proxyPath,changeOrigoin:true};
app.use(proxyMiddleWare("/slider",proxyOption))
複製代碼
不管是http-proxy-middleware仍是webpack-dev-server的proxy都是基於http-proxy-middleware,而http-proxy-middleware是基於node-http-proxy。
參考網址:
var proxyPath = "http://www.cherryvenus.com/";
var proxyOption ={target:proxyPath,changeOrigin:true,selfHandleResponse : true,ignorePath:true};
const proxy = httpProxy.createProxyServer({})
//若是selfHandleResponse爲true就是能夠本身修改得到的內容,若是不須要修改就是獲取轉發就無需設置了。
// 若是想要自定義獲取到的內容,則能夠經過觸發這個事件`proxyRes`來截獲內容並修改
proxy.on('proxyRes', function (proxyRes, req, res) {
var body = new Buffer('');
// 這裏筆者複製了header信息,省去了一波配置
for(let i in proxyRes.headers){
res.setHeader(i, proxyRes.headers[i]);
}
proxyRes.on('data', function (data) {
body = Buffer.concat([body, data]);
});
proxyRes.on('end', function () {
body = Buffer.from(body);
res.write(body);
res.end()
res.rs()
});
proxyRes.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
})
// 此處僅僅是配置了一個連接而已,真正觸發代理,是下方的proxy.web(req, res, proxyOption);
app.use("/slider/",(req, res, next)=>{
// 當地址地位到
let p= new Promise((rs,rj)=>{
// 先關閉以前的代理,若是有
proxy.close();
res.rs=rs
// 請求一個新的連接
proxy.web(req, res, proxyOption);
})
p.then(()=>{
next()
})
return p
})
複製代碼
看到上面的解決方法,忽然靈光一閃,那麼我是否是能夠本身寫一個呢?這個流程並不複雜,其實代理就是抓取網頁而後轉發的過程,既然如此,寫一個簡易的proxy並非什麼難事。
const http=require("http")
// 建立服務器
http.createServer({ host : 'localhost'}, (req, res) => {
// 打開一個請求
http.get('http://www.baidu.com', (res1) => {
// 抓取內容
var body = new Buffer('');
res1.on('data', (chunk) => {
body = Buffer.concat([body, chunk]);
});
res1.on('end', () => {
body = Buffer.from(body);
// 複製頭部信息
for(let i in res1.headers){
res.setHeader(i, res1.headers[i]);
}
// 返回內容,代理成功
res.write(body);
res.end();
});
})
}).listen(8000);
複製代碼
代理就是如此簡單,固然這只是一個原理,一種實現方法,就是抓取網頁再打印到頁面上。
而後筆者在查閱node中http發現一個驚爲天人的寫法(也許是筆者見識淺薄),一個利用了Http的method爲CONNECT的方法,將當前連接處於連接狀態,也就是不會斷,而後用 net.connect這個方法,連接到了請求連接,創建了互讀互寫的管道。
const http=require("http")
const net=require("net")
const url=require("url")
const proxy = http.createServer((req, res) => {
const options = {
port: 1337,
host: 'localhost',
method: 'CONNECT',//會觸發"connect" https://nodejs.org/api/http.html#http_event_connect
path: 'www.baidu.com:80'
};
const req1 = http.request(options);
req1.end();
req1.on('connect', (res1, socket, head) => {
let context=new Buffer("")
// make a request over an HTTP tunnel
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.baidu.com:80\r\n' +
'Connection: close\r\n' +
'\r\n');
socket.on('data', (chunk) => {
context = Buffer.concat([context, chunk]);
});
socket.on('end', () => {
for(let i in req.headers){
res.setHeader(i, req.headers[i]);
}
context=context.toString().split("\n\r\n")
context.shift()
res.end(context.join(""))
});
});
}).listen(1337);
proxy.on('connect', (req, cltSocket, head) => {
const srvUrl = url.parse(`http://${req.url}`);
const srvSocket = net.connect(srvUrl.port, srvUrl.hostname)
cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
srvSocket.write(head);
//管道創建了互讀互寫
srvSocket.pipe(cltSocket).pipe(srvSocket)
});
複製代碼
這個方法的簡易原理是這樣的,當你訪問http server的時候,http server調起了一個訪問自身連接,而且設置method爲CONNECT,也就是不間斷的意思,並建立了一個clientSocket,而後此http server也監聽了connect的事件,當有method爲CONNECT的連接的時候,會觸發http server的connect。此時連接打通以後,http server就會建立一個net的連接,去訪問須要代理的網站,返回一個serverSocket,而後serverSocket負責讀取內容,而後寫入clientSocket,clientSocket再告訴serverSocket寫入成功,直至代理成功。
真拆包,參考連接
module.exports = {
//...
optimization: {
splitChunks: {
// ...
minChunks: 2,// 這個屬性配置了當前模塊在不一樣的模塊中出現的次數,若是出現了引用兩次的狀況,則複用打包出來,這個是真拆包,拆的本身包。
// ...
}
}
};
複製代碼
只要注意root的問題,root在web下至關於window,所以是window.React,prop-type更不用說了。
externals:{
react:{
root: 'React',
amd: 'react',
commonjs: 'react',
commonjs2: 'react'
},
"prop-types":{
root: 'PropTypes',
amd: 'prop-types',
commonjs: 'prop-types',
commonjs2: 'prop-types'
}
}
複製代碼
exteranl是剔除別人的包。
dll是建立本身的工具庫。
const library = '[name]_lib'
module.exports = {
mode:"production",
entry: {
vendors: ['react', 'react-dom']
},
output: {
filename: '[name].dll.js',
path: path.join(__dirname,"dist/vendor"),
//libraryTarget: "umd",
library
},
plugins: [
new webpack.DefinePlugin({
'process.env': { //用於打包react中的東西
NODE_ENV: JSON.stringify("production")
}
}),
new webpack.DllPlugin({
path: path.join(__dirname, 'dist/[name]-manifest.json'),
// This must match the output.library option above
name: library
}),
]
}
複製代碼
production中使用動態連接庫,此處不要配置external。
new webpack.DllReferencePlugin({
context: __dirname,
manifest: path.join(__dirname, 'dist/vendors-manifest.json'),
})
複製代碼
個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:cloud.tencent.com/developer/s…