愛搞事情的webpack
webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。
當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序需要的每個模塊,然後將所有這些模塊打包成一個或多個 bundle。
基本安裝
- 1. 局部安裝,官方也是推薦安裝到項目目錄下
mkdir webpack-demo-1cd webpack-demo-1npm init -y //生成package.json,並且一路同意,如果沒啥個性化的內容則省了你一路狂按enternpm install --save-dev webpack //安裝到開發環境裡面(devDependicies)
- 2. webpack基本配置文件
touch webpack.config.jsvi webpack.config.js
配置文件內容如下
const path = require(path);module.exports = { entry: ./src/index.js, // 這裡應用程序開始執行,webpack 開始打包 output: { // webpack 如何輸出結果的相關選項 filename: bundle.js,//輸出資源塊的名字(asset chunk) path: path.resolve(__dirname, dist) // 所有輸出文件的目標路徑,我的就是./dist/bundle.js }};
把當前目錄的src下的index.js打包到了dist目錄下,並且生成了(emmited)一個改頭換面的bundle.js
,裡面的代碼面目全非啊。
可以有多個入口(entry)
entry: { scss: ./src/css/main.scss, //對象的鍵名scss 就是輸出文件的name bundle: ./src/js/app.js }, output: { filename: "[name].js", path: path.resolve(__dirname, dist/js) // publicPath: "/output/" },
多個入口最好寫成對象的形式,官網說如果寫成了數組,輸出的內容會是數組的第一個。
上述代碼會在dist/js
目錄下生成scss.js和bundle.js
path
其中,配置文件的第一行代碼使用了Node的內置模塊path
,並且在它前面加上 __dirname
這個全局變數(也就是第七行代碼)。可以防止不同操作系統之間的文件路徑問題,並且可以使相對路徑按照預期工作。
- 3. 基本的使用
//第一種方法,使用當前目錄的node_modules裡面的webpack./node_modules/.bin/webpack //第二種方法使用npm腳本//首先在你的package.json裡面添加下列代碼{ ... "scripts": { "build": "webpack" }, ...}//然後,使用下列代碼即可npm run bulid//第三種方法,高版本的npm自帶了npxnpx webpack //npx會自動查找當前依賴包中的可執行文件,如果找不到,就會去 PATH 里找。如果依然找不到,就會幫你安裝!
所以說呢,我選擇了第三種使用方法。
babel-loader
ES6或其他版本js轉換成通用js代碼,毫無疑問應該使用babel
,不過在webpack
的世界裡面統一使用loader
,所以我們google webpack babel-loader
。
有個坑,很容易搞錯的坑
babel-loader
不同版本的安裝腳本、配置文件是不同的……
(@ο@) 哇~你搜出的最新的文檔用這麼小的文字告訴你,你用webpack 3.x babel-loader 7.x | babel 6.x
的去這個鏈接,webpack 3.x | babel-loader 8.x | babel 7.x
的去當前的這鏈接鏈接。
npm install --save-dev babel-loader babel-core babel-preset-env webpack
配置文件是
//依然屬於webpack.config.js配置的一部分,module: { //這是關於模塊的配置 rules: [ //模塊規則(配置 loader、解析器等選項) { test: /.js$/, //使用正則判斷後綴是js的文件 exclude: /(node_modules|bower_components)/, //除了這兩目錄下的node_modules|bower_components use: { loader: babel-loader, //用這個loader處理.js的文件 options: { presets: [env] //選項,還記得單獨使用babel的時候建立的那個.babelrc嘛,就是那個作用。 } } } ]}
藉此可以得到loader
的兩個作用:
在./src/js/
有module-1.js、module-2.js、app.js
三個文件,都是新的語法,用的模塊化寫法,有的瀏覽器不支持,所以需要轉化。
//module-1.js代碼function fn(){ console.log(1)}export default fn//module-2.jsfunction fn(){ console.log(2)}export default fn//app.jsimport x from ./module-1.jsimport y from ./module-2.jsx()y()
最終效果,打開的我的預覽鏈接,使用ctrl+shift+J
,會看到列印出1和2
sass-loader
如果使用了預編譯的scss語言,要把scss文件變成css並加入到html裡面,思路同上,google webpack scss
npm install sass-loader node-sass webpack --save-dev
模塊配置文件
// webpack.config.jsmodule.exports = { ... module: { rules: [{ test: /.scss$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "sass-loader" // compiles Sass to CSS }] }] }};
這個官方的就比較給力了,很清楚地用法
- 先用sass-loader把
./src/csa/main.scss
編譯成main.css - 再用css-loader把main.css變成符合CommonJS規範的
- 把main.css變成js字元串,並創建style節點,把它放進去,這樣html就可以顯示啦。
不過坑爹依舊?……用的時候報錯嘍~
第一次就說缺style-loader,好吧……自覺點把另一個一起安裝了把。
npm i --save-dev css-loader style-loader
當前的main.css效果圖
所以,打開我的預覽鏈接,會看到我的預覽的背景是灰色的。
此時的webpack.config.js最終代碼
const path = require(path);module.exports = { entry: ./src/js/app.js, output: { filename: bundle.js, path: path.resolve(__dirname, dist/js/) }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: babel-loader, options: { presets: [env] } } }, { test: /.scss$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "sass-loader" // compiles Sass to CSS }] } ]}};
所以藉助webpack強大的模塊化,通過其構建的依賴關係圖(dependency graph)把js、scss都搞到了bundle.js裡面,真是牛~
html-loader
本來搞了一個html-loader,優化html,把空格、注釋都給壓縮掉,提高性能,可是實際使用中也沒有報錯,也沒啥效果,比較尷尬……自己埋個坑後面補一補。
Copy Webpack Plugin
目前呢,前面的loader用的都很爽。在src目錄下修改完了代碼,一個npx webpack,刷新就可以看到效果了,體驗很棒。
但是今天坐在電腦前面,回想代碼,以前在前端工程話的道路上,scss、js、html都是被監視著(wacth),開四個命令行窗口,只要src下一有風吹草動,就會把修改後的代碼更新過去。- 目前使用的webpack可以完全自動化
scss、js
了,可我如果修改了src/index.html,dist/也無法獲知我的修改啊 - 然後我google一會,發現了這貨
Copy Webpack Plugin
哎呀,是個plugin
,終於webpack的四大基本概念都到齊了,前面搞了entry output loder
,今天用一下plugin
。
loader 被用於轉換某些類型的模塊,而插件則可以用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。
基本安裝
npm i -D copy-webpack-plugin
Copy Webpack Plugin配置文件(plugin的和loader的配置文件可不是一個套路。loader是在module.rules數組的每一個對象裡面(即rules數組的每一個value),而plugin是在module的plugins數組裡面)
//依然在webpack.config.jsconst CopyWebpackPlugin = require(copy-webpack-plugin)const config = { plugins: [ new CopyWebpackPlugin([ ...patterns ], options) ]}
Copy Webpack Plugin的github給的代碼,一開始把我搞蒙了,和webpack官網的代碼不大一樣啊。後來才發現原來用了module.exports = config;
plugins: [ new CopyWebpackPlugin([ { //原來一個plugin就是一個對象啊,使用的時候實例化對象即可 from: src/index.html, //從src/index.html目錄下複製 to: ../index.html, //到dist/index.html toType: file //複製類型是文件 }], { copyUnmodified: true }) //把未修改的部分也複製過去 ]
這個插件可以實現很多功能,具體的細節看這裡
關於目錄的一個小問題
上面代碼為什麼這麼寫呢 to: ../index.html,
,試了好幾遍發現沒有報錯,就是沒有結果,最後搞明白了是路徑的問題……
output
裡面的path嗎,回頭看一開始的pathoutput: { filename: bundle.js, path: path.resolve(__dirname, dist/js/)},
項目的path是dist/js
下,所以應該複製到上一級目錄下../
也就是dist/
目錄下了。
可以看到預覽鏈接裡面的文字啦
動態效果可以看下圖(知乎藥丸,圖片顯示這麼差)
![動態效果](
http://p3tha6q4v.bkt.clouddn.com/18-2-12/50816812.jpg)
postcss-loader
上述代碼有個小問題使用了display: flex
把ul>li
變成了橫排,但是這玩意有兼容性。當初我的一篇文章唯一的一個評論就是這麼說我的……
檢查兼容性(雖然這是嚴謹要求,我還是老忘),可以去caniuse 看一下,(@ο@) 哇~IE沒有綠的哎,支持太差了。( ⊙ o ⊙ )!萬一以後我項目搞大了,IE的用戶、老安卓的用戶想看我項目咋辦呢,只能加一下前綴優化一下啦。
有個挺牛的在線的autoprefixer,也可以去在線轉換。既然使用了webpack就Google webpack autoprefixer
,遺憾的發現autoprefixer
官方推薦使用postcss-loader
postcss-loader解決兼容性問題
先吐槽一下,這貨的文檔也是稀爛……
1. 官方安裝腳本npm i -D postcss-loader
2. 需要單獨配置文件postcss.config.js,官方的寫法是下面這個(最無語的就是這個……,下面的必錯,寫出來就是警告大家,官方的也不一定對)
module.exports = { parser: sugarss, // 鉿????解析器是sugarss??? plugins: { postcss-import: {}, postcss-cssnext: {}, cssnano: {} }}
在webpack.config.js的添加時還要注意下面的幾點
After setting up your postcss.config.js, add postcss-loader to your webpack.config.js. You can use it standalone or in conjunction with css-loader (recommended). Use it after css-loader and style-loader, but before other preprocessor loaders like e.g sass|less|stylus-loader, if you use any.
這段文檔的要點就是讓你注意postcss-loader
應該在css-loader style-loader
之後,但是一定要在其他的預處理器preprocessor loaders
之前,例如
sass|less|stylus-loader
。3. 官方給了一個推薦的配置代碼
//依然是webpack.config.jsmodule.exports = { module: { rules: [ { test: /.css$/, use: [ style-loader, { loader: css-loader, options: { importLoaders: 1 } }, postcss-loader ] } ] }}
本項目用的是.src/css/main.scss
,只能嘗試著將上述代碼加到相應的位置
rules: [ ... { test: /.scss$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader", options: { importLoaders: 1 }// translates CSS into CommonJS }, { loader: "postcss-loader" }, { loader: "sass-loader" // compiles Sass to CSS }] }, ... ]
下面的幾點可都是官網文檔沒寫的,只能自己踩一踩的坑……
- 運行
npx webpack
,連續報錯,不過是缺必備的module的錯誤,也就是缺postcss.config.js裡面的postcss-import postcss-cssnext cssnano sugarss
。沒辦法,先npm i -D 上面的四個模塊名字
,依然報錯,這次是語法錯誤
(⊙v⊙)嗯???它說我不必要的大括弧???我這標準的scss語法啊,又不是sass的語法(它省略了大括弧和分號),先Google一波這個錯誤。
終於在在postcss的issue裡面發現了蛛絲馬跡,問題果然出在那個令我疑惑的postcss.config.js
裡面
錯誤原因分析
- 錯誤的使用了sugarss的解析器(這貨和sass類似,沒有大括弧,所以它說我大括弧錯了,它的特點是Indent-based CSS syntax for PostCSS.SugarSS MIME-type is text/x-sugarss with .sss file extension.),而我寫的是scss語法。
postcss-loader
哪來的勇氣確定大家都是用的.sss
後綴的sugarss語法呢,還敢直接在文檔的醒目位置推薦稀爛的postcss.config.js
,O__O "…- 那麼多的預編譯的css語法,果然需要webpack打包工具啊,找到合適的loader去解析啊。
注釋掉parser: sugarss,
這句代碼,可以使用默認的解析器去解析了,正常運行了。
autoprefixer冗餘
仔細觀察命令行,發現有線索,一個警告
警告信息提示我說:postcss-cssnext發現有個冗餘的autoprefixer
插件在我的postcss插件裡面,這個可能有不良影響,我應該移除它,因為它已經包括在了postcss-cssnext裡面。
webpack的警告說的很明白,postcss-cssnext是無辜的,而且我確定按照官網代碼走的,沒有安裝autoprefixer
插件,錯誤必然在剩下的兩個插件裡面了。
//修改後的postcss.config.js只剩下這些了module.exports = { plugins: { postcss-import: {}, //1.它錯了? postcss-cssnext: {}, //webpack告訴我它是清白的 cssnano: {} //2.它錯了? }}
我選擇了排除法:
1. 先注釋postcss-import: {},
,發現無法轉換後的css代碼不對,說明它是無辜的。2. 那麼問題必然是最後一個插件,注釋掉cssnano: {}
,終於完美了,而且代碼很優美。 本著打破砂鍋問到底的精神,我搜了一下cssnano
,在其官網看到了真實的錯誤原因,webpack很明智啊,誠不欺我,果然冗餘插件了。
cssnano裡面有autoprefixer
導致了冗餘。
extract-text-webpack-plugin
webpack 把所有的資源都當成了一個模塊, CSS、JS 文件 都是資源, 都可以打包到一個 bundle.js 文件中.
但是有時候需要把樣式 單獨的打包成一個文件需要抽離出css文件到單獨的css/
下。使用extract-text-webpack-plugin插件可以做到。
- 安裝腳本
npm install extract-text-webpack-plugin --save-dev
2. 配置webpack.config.js,官方推薦的配置如下,但是需要結合自己的項目修改一下……
const ExtractTextPlugin = require("extract-text-webpack-plugin"); //插件的套路。都要require進來module.exports = { module: { rules: [ { test: /.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", //失敗了就用它解析 use: "css-loader" //是css文件,就用這個處理 }) } ] }, plugins: [ new ExtractTextPlugin("styles.css"), ]}
目前的問題
我如果使用的是scss,就很尷尬了
最開始由一個錯誤引起
Google一下,解決掉error,fallbak裡面使用了style-loader,use裡面不應該使用了,那麼問題又來了
- 問題一:無法使用style-loader把我的main.scss轉化後的css代碼插入到style標籤裡面。
詳見代碼注釋部分
{ test: /.scss$/, use: ExtractTextPlugin.extract({ fallback: style-loader, //如果在use裡面寫上style-loader,就報錯window未被定義,可是不寫的話,我的bundle.js裡面就無法把css放到style標籤裡面,只能手動把分離的css加到index.html,很無語。 use: [{ loader: "css-loader", options: { importLoaders: 1 }// translates CSS into CommonJS }, { loader: "postcss-loader" }, { loader: "sass-loader" // compiles Sass to CSS }] }) }
2. 問題2:可以提取出單獨的css文件,但是沒想到如何放到單獨的css/
下,很尷尬,代碼未提交,還在摸索中。
問題大約知道應該出現在下面這個函數裡面
new ExtractTextPlugin({ filename: (getPath) => { return getPath(../main.css).replace(../js, ../css); // 本意是生成在dist/css/main.css,結果只是在dist/main.css目錄下,沒有css/ }, allChunks: true })
所有的代碼都在我的demo裡面。
蛋疼的無力吐槽
算……是……搞定了webpack的基本使用了……吧,最簡單的符合我目前技術棧的各種loader,plugin都會安裝了。
當然,還有無數的webpack的loader、plugin在前方等著我去探索……各種稀奇古怪的配置文件……痛並快樂著?五花八門的配置文件挺讓我糟心的……幸虧有了node爸爸幫我啊,webpack爸爸雖然也是比較嚴厲的,但是省了你用四個命令行窗口的啊,還是很感人的啊。
工具這個東西嘛
- 配置出錯了怎麼辦,默念三句
- 如果真的搞蹦了怎麼辦。熊得,送你一句名言
沒有什麼bug不是一遍webpack解決不了額,如果有的話,那就來三遍webpaack。總有一天讓webpack叫你爸爸!!!
新的挑戰者parcel
現在的吐槽大概是沒有經歷過以前更蛋疼的日子吧,幸好有了新的後起之秀---parcel,它的官網老厲害了,智能提示我用了中文,真是貼心。
回顧一下webpack
的首頁
在對比一下parcel
的首頁
兩者的目的是一樣的,不過parcel不需要插件,而且速度快。
快速開始是真的快
快速開始
沒有配置,最好以html或者js為入口,直接npm init -y , parcel index.html
,可以實現index.js。
它會自動幫你打包到dist目錄下的一個js文件裡面,並複製index.html過去,而這一切只需要上面的一行代碼。
- 一開始我的項目的目錄結構
- 執行
parcel index.html
的目錄結構
而我當時搞webpack的時候的快速開始至少需要安裝webpack、webpack.config.js、修改配置內容、安裝插件才能實現上述的功能。
(垃圾的知乎吃圖,藥丸)
![快速開始](
http://p3tha6q4v.bkt.clouddn.com/18-2-13/44326202.jpg)
模塊化和scss的解析很方便
當我在parcel-demo
目錄下使用parcel index.html
的時候,它自動發現我引入了index.js。
<body> <script src="./index.js"></script></body>
- 裡面的內容是模塊化的內容,它自動幫我轉成了
dist/parcel-demo.js
,我在webpack的時候需要babel-loader - 我用的是scss,它也會自動發現,並且竟然
還幫我自動下載了node-sass
其他的特點都在官網去發掘吧~希望日後parcel
快速崛起吧
而現在我還是要用webpack
的……
推薦閱讀:
※FIS+Webpack在後端模板場景下的混合開發模式探索
※你的Tree-Shaking並沒什麼卵用
※require,import區別?
※Start React with Webpack