本文webpack是在Mac平臺下基於官方最新版本v3.10,對於webpack@v2會有小的差別,待全文完成後會補充webpack@v2與v3版本之間的差別
爲了方便以後本身更好的使用這個webpack_starter,引入git的支持,一是能夠把一些通用的東西放在主分支,二是能夠把後面不一樣的配置支持能夠經過branch
或者tag
的方式分門別類。javascript
webpack_starter
項目,以下圖所示,初始化.gitignore
支持Node
語言#clone項目到本地 git clone https://github.com/mpandar/webpack_starter.git cd webpack_starter
初始化過程按照提示完成便可,惟一注意的是entry point
,這是webpack進行打包時的入口文件,默認是根目錄下的index.js
,不過一般狀況下,咱們的源碼都是在src
目錄下,因此修改成src/index.js
> yarn init yarn init v1.3.2 question name (webpack_starter): question version (1.0.0): 0.1.0 question description: a webpack start project question entry point (index.js): src/index.js question repository url (https://github.com/mpandar/webpack_starter.git): question author (mpandar <mshp_****@126.com>): question license (MIT): question private: success Saved package.json ✨ Done in 53.55s.
單獨安裝與全局安裝對於webpack的使用並沒有太大差別,但推薦即便全局安裝之後,仍要在項目中進行單獨的安裝,方便項目移植。不然可能會致使全局安裝的webpack版本與項目中的配置文件可能存在不匹配。固然單獨安裝後,使用一些npm或yarn命令,它們會優先使用本地安裝的webpack
#全局安裝 yarn global add webpack #單項目使用 yarn add webpack
|--- |--dist //存放webpack打包後相關文件 |--src //存放項目源碼 |--index.js |--config //項目相關的配置文件 |--webpack.config.js //webpack默認讀取項目根目錄下的webpack.config.js文件做爲配置信息,爲了規範化移入到config目錄下 |--package.json
固然,即便沒有配置文件,直接運行webpack命令,一樣能夠直接對js文件完成打包工做,這裏是一篇分析webpack打包後的代碼的文章:簡要分析webpack打包後代碼,其中用到的一個新命令npx,很簡單,介紹點這裏php
#直接打包 npx webpack src/index.js dist/bundle.js
爲了應對更靈活的使用場景,webpack支持配置文件,而且默認狀況下,在項目根目錄下,若是存在webpack.config.js
文件,那麼webpack會主動讀取該文件做爲配置內容,不過Demo下,爲了更加符合咱們的目錄規範,咱們將config文件移到了config
目錄下css
//當配置文件內容爲空時,運行該命令會提示`Configuration file found but no entry configured` npx webpack src/index.js --config config/webpack.config.js
接下來,讓咱們看一下一個webpack配置文件,最簡單隻須要包含entry(定義入口文件)和output(定義打包輸出文件)這兩個部分:html
const path = require('path'); const base = path.join(__dirname, '..') module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') } };
注:使用path模塊只是爲了代碼清晰,你徹底能夠不用,直接用
__dirname+'/../src'
相似代碼拼接
//這樣就不須要在命令行定義輸入輸出文件啦 npx webpack --config config/webpack.config.js
除了entry
和output
,webpack中最多見的就是module
、resolve
、plugins
,大體結構以下:前端
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map', devServer: { contentBase: path.resolve(base, 'dist'), historyApiFallback: true, inline: true, proxy: { "/api": "http://localhost:8000" } }, module: { rules: [ ] }, resolve: { }, plugins: [ ] };
固然瞭解webpack最好的地方永遠是官方文檔,傳送門,接下來,天然是在目前配置的基礎上,增添更多使人興奮的特性java
開發離不開調試,但通過編碼後的代碼並不利於調試,很找到出錯的地方對應的你寫的代碼,而Source Maps就是來幫咱們解決這個問題的。
而webpack支持Source Maps僅僅是增長一行 devtool
配置選項,具體配置選項能夠看這裏的官方文檔,其中兩個選項 eval-source-map
與 source-map
是比較經常使用的選項,前一個選項推薦僅僅用在開發環境,然後一個一般在一些第三方庫中,提供給開發者調試使用。固然對於任何上線項目,實際上都推薦使用*.min.js並不使用Source Map以加快網絡加載。node
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map' }
做爲開發者,總不但願把時間浪費在執行命令和刷新頁面上,webpack提供一個單獨的組件webpack-dev-server
爲咱們提供一個基於nodejs的本地服務器、文件修改監控及編譯以及瀏覽器自動刷新等特性。webpack
首先是安裝:git
yarn add webpack-dev-server --dev
詳細配置參數可查閱官方文檔,常使用參數以下:es6
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map', devServer: { contentBase: path.resolve(base, 'dist'), historyApiFallback: true, inline: true, proxy: { "/api": "http://localhost:8000" } } }
小插曲,原本測試proxy的時候,本身利用php -S localhost:8000快速起了一個監聽進程,可是訪問前端的時候卻提示轉發去請求被拒絕,後來發現webpack-dev-server去轉發請求的時候是把localhost轉化爲了地址,即127.0.0.1:8000,因此在php啓動監聽進程時候,須要使用php -S 127.0.0.1:8000
或者php -S 0.0.0.0:8000
監聽全部網卡地址
webpack-dev-server的使用同webpack基本運行同樣,只是webpack-dev-server是一個不會退出的進程,並自動監控文件變化等(Ctrl+C退出)
npx webpack-dev-server --config config/webpack.config.js
使用npx webpack --config config/webpack.config.js
進行打包實際上已經很方便了,可是當咱們須要又有開發環境的配置,又有生產環境的配置,甚至還要爲命令增長其餘的環境變量的時候,這個命令簡直是又臭又長,咱們總不能每次都輸入這個繁瑣的命令,其實咱們能夠利用npm scripts,這裏是一篇阮一峯大神對npm腳本的介紹
//package.json { "scripts": { "build": "webpack --config config/webpack.config.js", "dev": "webpack-dev-server --config config/webpack.config.js" } }
npm run build npm run dev
須要注意的是在配置build&dev腳本的時候,咱們並無用
npx
命令,實際上,在npm腳本中的命令,npm默認都是優先查找本項目下node_modules下是否存在對應的模塊及命令,若是沒找到,纔會查找全局的命令
一般一個項目中,並非只有一個js文件,而entry默認支持多文件入口,修改output跟隨入口文件名字命名便可,簡單作以下修改:
module.exports = { entry: { index: path.resolve(base, 'src', 'index.js'), main: path.resolve(base, 'src', 'main.js') }, output: { filename: 'js/[name].js', path: path.resolve(base, 'dist') } }
同時建立一個簡單的main.js進行測試,再次編譯會發如今dist/js目錄下存在編譯後的index.js和main.js文件
webpack使人興奮的一個特性就是模塊化,易於擴展其功能。webpack支持大量的模塊導入方法,好比ES6(import)、CommonJS(require)、AMD等規範,而且加入了一些自由方法,具體的能夠看官方說明文檔。
用通俗的語言描述就是,webpack經過某個入口,去匹配各類模塊導入規範,發現一個模塊就根據對應配置尋找對應的loader去處理,如此往復,直處處理完畢全部依賴。
使用起來就是在modules字段中的rules(老版本名字爲loaders,爲保證兼容性,仍是支持這個字段的)中配置test,去匹配文件(js、css、圖片資源等等),而後把這個文件交給合適的loader處理便可,因此但凡新出的框架,若是用到了獨特的語法功能,都會配套提供對應的loader工具
目前雖然瀏覽器對ES6新特性的支持度都很是高,但還是有部分場景下,咱們只能運行ES5的代碼,這時候就須要利用到js轉碼屆的特斯拉Bebel及其插件了
yarn add babel-loader babel-core babel-preset-env
module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['env'] } } } ] },
顯然test
中匹配了全部的js文件,exclude
字段去除了項目中依賴庫裏的文件,use
則是配置對應的loader。其中對於options,是做爲參數傳遞給babel-loader的,babel的相關參數能夠參考babel的官方網站,其中presets做爲最主要的參數,告訴babel按照那種規則去解析代碼,固然env
是一個組合,包含es201五、es2016等,當presets參數包含多個值時,babel的處理規則是倒序的,"es2017","es2016"
,babel會先去匹配es2016
的規則。另外,對於babel的配置,也能夠經過在根目錄創建.babelrc
方式去配置:
//.babelrc { "presets": [ "env" ] }
固然除了擴展js的語法,有時候咱們還須要擴展js的功能,好比在某些低版本的瀏覽器上運行'Hello World'.includes('Hello')
,這可使用babel-polyfill
這個組件,點擊這裏能夠了解其使用方法。
固然,咱們能夠只讓webpack處理js文件,繼續在html文件中經過link
標籤引入css文件。但顯然webpack但願前端攻城獅們也能像模塊化編寫js同樣,進行css的編寫。咱們在src下建立css目錄,並建立main.css文件
/* src/css/main.css */ body{ background-color: deepskyblue; /* 我喜歡填空的藍色 */ }
//src/index.js import style from './css/main.css'
這時候使用npm run build
會發現編譯報錯,這是由於webpack沒法處理載入main.css後的代碼
ERROR in ./src/css/main.css Module parse failed: Unexpected token (1:4) You may need an appropriate loader to handle this file type. | body{ | background-color: red; | } @ ./src/index.js 3:12-37
webpack官方提供css-loader
專門處理導入的css模塊,固然css-loader
也僅僅是完成了模塊的導入處理,使webpack在編譯時候再也不報錯,實際上,還須要style-loader
處理導入後的css數據,自動添加到html的style標籤中。若是你在測試過程當中,僅僅添加css-loader
loader,你會發現body實際上並無變成背景藍
yarn add style-loader css-loader
//config webpack.config.js module->rules { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } } ] }
固然css-loader
還有一個重要配置選項,modules
參數,它支持導入的css中的類名自動重命名,這樣即便在不一樣組件使用了相同的css類命名,經處理後互相之間也不會出現影響。看下效果:
比較常見的預編譯工具也就是Sass、Less、Stylus。在使用這三個預編譯工具前,須要安裝其對應的專屬處理程序,好比Sass的處理工具node-sass。爲了配合與webpack使用,還要安裝對應的loader,好比sass-loader
yarn add sass-loader node-sass --dev
在webpack.config.js中添加對scss(Sass 3引入的新的語法格式,推薦新項目都用此)文件的支持,再起強調,webpack中loader執行順序是從右往左,從下往上。
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } }, { loader: "sass-loader" } }] }] } };
測試一下:
/* src/sass/main.scss */ body { background-color: blue; } .main { background-color: grey }
//src/index.js // import style from './css/main.css' import style from './sass/main.scss' let es6 = () => { console.log('run in es6') console.log(style) } es6();
其餘兩種預編譯器能夠參考各自官方文檔:less-loader、stylus-loader
PostCSS官網介紹是,利用js轉譯css的一個工具。對PostCSS的介紹,我比較承認這篇文章,PostCSS提供了一個解析器,把css轉換爲抽象語法樹(AST),固然這個AST是可以被js處理的,而後交給各類插件處理後,再將AST轉爲css代碼,因此關鍵是這些插件能完成哪些功能。
Autoprefixer 是一個流行的 PostCSS 插件,其做用是爲 CSS 中的屬性添加瀏覽器特定的前綴。
cssnext 插件容許開發人員在當前的項目中使用 CSS 未來版本中可能會加入的新特性。須要注意這個插件自己包含了Autoprefixer功能,因此若是使用了這個插件,則不須要Autoprefixer插件。
固然PostCSS中其實也包含支持Sass、Less等的插件,這個看我的喜愛,不過我本人卻是蠻期待嘗試下cssnext,畢竟大部分特性可能就是將來css支持的特性,提早熟悉下也算是預習。
更多插件能夠讀這篇文章,再也不敘述
安裝postcss-loader及其插件autoprefixer
yarn add postcss-loader autoprefixer --dev
在webpack.config.js
中添加postcss支持,注意postcss處理css文件的位置
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]', minimize: false } }, { loader: 'postcss-loader', options: { config: { path: path.resolve(base, 'config', 'postcss.config.js') } } }, { loader: "sass-loader" }] }] } };
如上面配置,postcss須要單獨的配置文件,建立config/postcss.config.js
,添加以下配置:
//config/postcss.config.js module.exports = { plugins: { 'autoprefixer': {} } }
file-loader提供了對圖片資源的loader功能,而且利用 publicPath
選項還能很好的支持cdn,配合url-loader還能對小圖片直接進行數字序列化(DataURL),減小網絡請求,提升加載速度。
yarn add url-loader file-loader --dev
增長相關配置;url-loader的默認 fallback
loader就是 file-loader
爲了更直觀就寫了出來,傳遞給 file-loader
的參數也只須要寫在options中便可。這樣background-image: url('../image/logo.jpg')
當咱們在css文件中使用這種方式引入圖片時,就會觸發url-loader去處理
//config/webpack.config.js module.exports = { ... module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192, fallback: 'file-loader', name: '[hash:5].[ext]', // publicPath: 'https://cdn.j2do.com/', outputPath: 'images/' } } ] } ] } }
Loader是專一於處理Webpack導入的資源模塊,而插件是對Webpack功能的擴展。除了Webpack內置的插件,開發社區提供了大量優秀的插件。固然插件也是解決問題的,咱們仍是以問題爲導向,去介紹幾款插件。
extract-text-webpack-plugin
分離css到獨立文件yarn add extract-text-webpack-plugin --dev
其中ExtractTextPlugin中fallback是指定了若是不須要提取到獨立css文件中的樣式文件,則交給 style-loade
處理,其餘loader配置,跟以前沒有差別。一樣若是是預編譯文件(Sass、Less等)的話,也只須要增長對應的loader便可
module.exports = { ... module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } }, { loader: 'postcss-loader', options: { config: { path: path.resolve(base, 'config', 'postcss.config.js') } } } ] }) } ] } ... plugins: [ new ExtractTextPlugin("css/[name].css") ] };
再次運行 yarn run build
會發現生成了獨立的css文件
html-webpack-plugin
自動生成 index.html
等入口文件隨着多入口以及css的分離,手動去寫html的入口文件也是一件麻煩事,這時候 html-webpack-plugin
就排上用途了, html-webpack-plugin
可以根據某個模板文件自動生成入口的html,包括多個入口,安裝及配置以下:
yarn add html-webpack-plugin --dev
module.exports = { ... plugins: [ new ExtractTextPlugin("[name].css"), new HtmlWebpackPlugin({ title: 'Index Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "index.html", chunks: ['index'] }), new HtmlWebpackPlugin({ title: 'Main Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "main.html", chunks: ['main'] }), ] };
建立模板文件,注意之因此命名爲 .tmpl
是爲了防止 .html 可能會被loader解析,<%= ... %>
將不會被插件識別,完成變量替換。以下圖,咱們的 title
是以變量形式在 webpack.config.js
中配置。該插件還支持多種模板文件,具體可見官方文檔
<!-- src/template/index.html.tmpl --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> <%= htmlWebpackPlugin.options.title %> </title> </head> <body> </body> </html>
前面已經將js、css等分離到單獨文件,接下來就是優化壓縮這些代碼。
對於css而言,只須要配置 css-loader
的 minimize
參數爲 true
便可;固然還能夠利用postcss的 cssnano
插件進行代碼的壓縮和優化,聽說 cssnano
是目前css壓縮優化中效果最好的工具。
對於js的壓縮,咱們藉助於 uglifyjs-webpack-plugin
插件
yarn add uglifyjs-webpack-plugin --dev
module.exports = { ... plugins: [ new ExtractTextPlugin("css/[name].css"), new HtmlWebpackPlugin({ title: 'Index Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "index.html", chunks: ['index'] }), new UglifyJsPlugin() ] };
Eslint再也不介紹,在webpack下使用Eslint須要以下依賴包:
yarn add eslint eslint-loader babel-eslint eslint-config-standard --dev
其中eslint是必須的,eslint-loader
是鏈接eslint與webpack的loader,babel-eslint
是一個eslint解析器,使其能支持es6等語法檢測,eslint-config-standard
是Airbnb的規範配置,目前最流行的js規範。
安裝過程當中若是有提示eslint-config-standard@11.0.0-beta.0" has unmet peer dependency "eslint-plugin-import@>=2.2.0"
等等,直接安裝對應的包便可,好比:yarn add eslint-plugin-import --dev
便可
eslint是規範js語法,因此他須要處理的是js文件,並且應該是先於全部loader去處理js文件,若是出錯或者不規範則糾正之,這裏能夠利用webpack的enforce
屬性,設置eslint檢查,先於其餘loader
module.exports = { ... module: { rules: [ { enforce: "pre", test: /\.js$/, exclude: /node_modules/,//注意不要檢測node_modules裏面的代碼 loader: "eslint-loader", options: { fix: true //自動修復不規範的代碼,並非全部代碼都能自動修復的,一些縮進,引號等能直接處理 } } ... ] } }
同時還要爲eslint增長對應的配置文件 .eslintrc
{ "parser": "babel-eslint", "extends": "standard", "rules": {} }
這時候再嘗試build吧,會發現一些不規範的代碼被自動修復,固然有些不規範的代碼,沒法自動修復的,會直接致使錯誤;好比,聲明瞭一個函數,卻沒有使用!
未完待續~~(近幾天更新)