1、Loader寫法及執行順序javascript
從webpack2起,loader的格式以下:css
module: {
rules: [ {test: /\.css$/, use: ['style-loader','css-loader']}, ] }
webpack1中的寫法以下:
module: { loaders: [ { test: /\.css$/, loader: 'style-loaer!css-loader' } ] }
無論採用哪一種寫法,須要記住的是loader的執行順序是從右往左
2、根據模板生成html文件並自動插入腳本和樣式的地址html
使用html-webpack-plugin插件可達到這一目的,而且插入的文件地址會隨着output.publicPath的變動而變動。html-webpack-plugin插件與chunk概念緊密相聯vue
所謂的chunk無非就是代碼段,假設入口爲index.js,在index.js中導入了a.js和b.js,那麼打包出來就會造成一個name爲index的chunk,chunk的內容是這三個文件合起來的內容java
該插件不是webpack自帶的,須要先安裝到開發環境( npm html-webpack-plugin --save-dev),而後在webpack配置文件中有幾個關鍵的配置項:react
module.exports = { plugins:[ new htmlPlugin({ filename: '最終輸出的html文件地址', template: '模板文件地址', hash:false, chunks: ['index','common'] }) ] }
hash:爲true時在html中插入的文件地址格式會變爲xxx.js?a3c9073cfd20130af5e3,生成hash的主要做用是用來處理瀏覽器緩存jquery
chunks:可選屬性,要插入哪些chunk生成的文件,若是未指定,則把全部的chunk造成的文件都插入。webpack
在多頁面中一般每一個頁面都有一個js作爲入口文件,index.html使用index.js,user.html使用user.js,這裏有兩個入口,打包出來會造成兩個chunk,分別爲index和useres6
entry:{ index:rootPath+'/src/js/index.js', user:rootPath+'/src/js/user.js' }, output: { filename: 'js/[name].js', path: rootPath+'/dist/' },
生成index.html時,咱們顯然不但願把user.js也引用進去,因此這時就必需要指定使用哪些chunkweb
若是要生成多個html,則須要在plugins數組中建立多個htmlPlugin實例
使用ExtractTextPlugin插件提取的css文件會自動添加到html中
指定了chunk時,若是使用CommonsChunkPlugin提取了公共代碼,若是未在配置項中添加該chunk的名字,則生成的html不會插入公共代碼文件
假設在html模板中已經手工插入了一些css和js文件,生成的文件會插入到這些文件以後
3、未打包的樣式和腳本也想自動插入到html中
在上面例子中咱們把打包生成的文件自動插入到了html中,那麼不參與打包的文件該怎麼處理呢?若是咱們採用手動插入方式,那麼若是在開發環境時不使用cdn,在生產環境要使用cdn時,不是得手工去改html中的連接嗎。
new htmlIncludeAssetsPlugin({ assets: ['js/lib/jq.js'], append: false, //false,添加到html-webpack-plugin生成的連接以前,true爲以後 hash:true, //是否在生成的文件地址後加hash,默認爲false publicPath:'' //自定義publicPath,若是省略或爲空則使用output項中指定的publicPath })
該插件的實例會對全部的html-webpack-plugin實例起做用,也就是在全部生成的html中都插入相應的代碼,若是隻想在指定的html中追加,能夠指定一個file屬性
4、項目使用jquery及插件的最佳實踐
不論是咱們以往的多頁面仍是vue、react這種mvvm框架中,都建議直接按以下方式使用:
1.在html中直接引入jquery及相應的插件,這樣子在代碼中就能夠正常使用jquery以及相應的插件了
2.若是須要在代碼中使用es6的模塊導出功能import $ from 'jquery'或者cmd方式加載var $ = require('jquery'),那麼你須要把$變量暴露給webpack,按以下方式進行配置
externals: { jquery: 'window.$' }
externals的使用場景以下:當咱們想引用一個庫,而後想在代碼中使用cmd或es6的模塊引入,加可是又不想讓webpack打包,這時就可使用該配置項。
此步驟不是必須的,不使用es6 import或cmd require並不影響使用jquery及插件
3.若是是本身編寫的插件,可使用es6的import './jqPlugin/globalJq.js'方式導入,此方式會把globalJq.js的代碼打包進項目的js中
4.會把jquery打包到項目js中的幾種方式
{ test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' }
step3,在須要使用jquery的文件中導入模塊便可
import $ from 'expose?$!jquery' import 'jquery-ui' //插件可用
此種方式在使用layer.js這個彈層庫時會出錯
step3:接下來就是直接使用$了,不須要再導入模塊
import 'jquery-ui' //引入jquery-ui插件
$('#app').text()
該插件的做用是自動加載模塊。 當 使用未賦值的變量時, module 就會自動被加載,而且 identifier 會被這個 module 輸出的內容所賦值。上面的變量是$,對應的module是咱們安裝的jquery模塊。
缺點是沒有全局上下文,$變量沒有聲明就可使用,當使用eslint時沒法驗證語法錯誤
5. 爲何推薦直接在html中引入jquery及相應的插件?
採用上面第4條中的作法,在打包時會出現一系列的問題,具體表如今:
1.若是多個文件都使用了jquery,那麼打包時,每一個文件中都會插入jquery庫的代碼,代碼重複,而後你會想到第2條的抽取公共代碼方法
2.使用CommonsChunkPlugin插件把多份代碼中都使用到的代碼提取爲獨立文件,這種作法在每次打包時都會計算哪些是公共代碼,項目大時找包速度很慢。
3.爲了解決打包性能,得改用DllPlugin和DllReferencePlugin插件,打包速度解決了,可是仍是得手動把xxx.dll.js插入到html中。(看不明白沒關係,後續會講到該插件)
看到了嗎,使用第4條中的方法繞來繞去最終仍是得在html中插入提取到的公共代碼或xxx.dll.js,作了一系列的工做,實際上等於脫了褲子放屁
5、css相關處理
在webpack中全部的東西都是模塊,css也不例外,只有將css視爲模塊,才能在webpack中進行打包和壓縮,才能自動插入到相應的html中,才能將css轉成js對象方式來使用(vue中有該技術)。
1.css-loader和style-loader
css-loader 主要的做用是把css看成模塊使用,而後就能夠在js中使用 import 'xxx.css'導入樣式表,而後參與打包
style-loader 主要的做用是把js中導入的css模塊插入到html中造成一個<style>xxxx</style>
step1:安裝模塊
npm install style-loader css-loader --save-dev
step2:webpack中編寫loader
module: { rules: [ { test: /\.css$/, use: [
{ loader: 'style-loader'}, {loader: 'css-loader',options: { modules: true}} ] } ] }
2.使用post-loader+autoprefixer自動爲css加前綴
某些css屬性,要爲不一樣的瀏覽器加不一樣的前綴,手動添加相應的前綴是個苦差事,post-loader能夠很好的解決這個問題,咱們只需寫一個正常的樣式
div { transform:rotate(7deg); }
使用post-loader後會自動轉成
div { transform:rotate(7deg); -ms-transform:rotate(7deg); /* IE 9 */ -moz-transform:rotate(7deg); /* Firefox */ -webkit-transform:rotate(7deg); /* Safari 和 Chrome */ -o-transform:rotate(7deg); /* Opera */ }
網上不少文章比較坑,會告訴你用autoprefixer插件或用post-loader插件,實際上兩個插件都須要安裝!
npm install autoprefixer post-loader --save-dev
3.使用less-loader來編譯.less文件
4.使用extract-text-webpack-plugin插件來提取style-loader向頁面中插入的樣式
使用前須要先安裝該插件,而後使用const ExtractTextPlugin = require("extract-text-webpack-plugin");//導入模塊,最後在plugins數組中建立一個實例
new ExtractTextPlugin('css/[name].css'),
使用name屬性時,從入口中index.js中抽取出來後的文件是index.css,固然也能夠指定爲一個固定的名字好比page,css
當咱們指定css/xxx.css時,最終的生成路徑其實是webpack的output.path+'css/xxx.css'
5.相關的配置
注意use中的loader順序,不要寫反了
{
// 用正則去匹配要用該 loader 轉換的 CSS 文件
test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", // 編譯後用什麼loader來提取css文件 use: ['css-loader?minimize=true','postcss-loader'] }) },
6.css壓縮處理
須要使用optimize-css-assets-webpack-plugin插件來專門處理css。
注意:對於webpack v3或更低版本,請使用optimize-css-assets-webpack-plugin@3.2.0。 optimize-css-assets-webpack-plugin@4.0.0及以上版本支持webpack v4。
若是你搜網上那些教程,通常會告訴你css-loader有壓縮屬性,又或者告訴你你在pageage.json中建立一個打包的scrpits命令,配置以下:
"scripts": {
"build": "webpack -p", },
假如你按他們的方法來打包,最後你發現你的css文件中的代碼仍是原封不動的,根本就沒有刪除空格,也沒有刪除註釋!
new OptimizeCssAssetsPlugin({ cssProcessor: require('cssnano'), canPrint: true })
6、圖片資源的處理
主要使用url-loader和file-loader,網上看博文說url-loader是file-loader的升級版,能夠獨立使用,可是我在webpack2中使用時報錯了,安裝url-loader後仍是得安裝file-loader.
容易出錯的地方,請查看這篇
7、實時編譯並查看運行結果(HRM)
主要使用webpack-dev-server,安裝好該包後,需在package.json中和webpack.config.js中進行配置。
首先在package.json的scripts節點增長一個命令,我習慣叫dev,而後在命令行中就可使用 npm run dev來啓動調試服務器了
"scripts": { "dev": "webpack-dev-server --open --inline --hot },
open--參數表示啓動服務器後自動打開網頁
inline 和hot配合實現熱刷新,hot參數必須寫在這裏,在webpack.config中配置無效
接下來在webpack.config中增長一個devServer配置項:
devServer: { contentBase:'dist', //網站文件目錄,通常指向打包輸出目錄 historyApiFallback: true,//不跳轉 progress: true },
更詳細的配置,點我
8、如何處理sourcemap?
主要使用devTool選項,最經常使用的有如下幾種:
上述選項由上到下打包速度愈來愈快,不過同時也具備愈來愈多的負面做用,較快的打包速度的後果就是對打包後的文件的的執行有必定影響。
生成source map的目的是爲了方便調式,出錯時能定位到最原始的資源,無論該資源是react的jsx,仍是vue的單文件,所以使用eval選項並無意義,這也是在上表中沒有列出的緣由
那爲何也不使用inline-source-map選項呢?主要仍是由於打包速度慢,文件體積變大
總結:
在開發環境中咱們使用:cheap-module-eval-source-map
在生產環境中咱們使用:cheap-module-source-map。
9、區分開發環境和生產環境
const common = require('./webpack.common.js'); const merge = require('webpack-merge'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const webpack=require('webpack') const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const path=require('path') const rootPath=path.resolve(__dirname,'../') //const DllReferencePlugin = require('webpack/lib/DllReferencePlugin'); const prodConfig={ plugins:[ new webpack.DefinePlugin({ 'process.env': JSON.stringify({env:'development','BASE_API':'http://www.baidu.gov.cn'}) }), new CleanWebpackPlugin(['build'], { root: '', // An absolute path for the root of webpack.config.js verbose: true,// Write logs to console. dry: false // Do not delete anything, good for testing. }), /* new DllReferencePlugin({ manifest: require('../dist/vendor.manifest.json'), }), */ new UglifyJSPlugin(), new OptimizeCssAssetsPlugin({ cssProcessor: require('cssnano'), canPrint: true }) ] } module.exports=merge(common,prodConfig)
須要注意的是DefinePlugin中某個項的值的寫法,假如你想在代碼中訪問process.env裏存儲的內容,那麼在定義時必須使用Json.stringify('prod'),或者是'"prod"',而不是'production'
"scripts": { "build": "webpack --config webpack.prod.js", "dev": "webpack-dev-server webpack.dev.js", },
須要注意配置文件的路徑,若是不是在根目錄下,而是在config文件夾下,則應修改成