webpack的一點介紹
Webpack 把任何一個文件都看成一個模塊,模塊間可以互相依賴(require or import),webpack 的功能是把相互依賴的文件打包在一起。webpack 本身只能處理原生的 JavaScript 模塊,但是 loader 轉換器可以將各種類型的資源轉換成 JavaScript 模塊。這樣,任何資源都可以成為 Webpack 可以處理的模塊。同時,webpack還有豐富的插件 plugin,可以完成例如js,css的壓縮,公共依賴模塊的提取和注入,甚至利用模板對 html 進行動態拼接等功能。
同時,webpack 使用commonjs規范(require),支持es6語法(import)的編譯,可以方便的抽離vue組件,這成為我們選擇它的重要理由。
對webpack工作方式直觀的理解(官網小示例)
import: es6 引入依賴的方式,還可以用commonjs 規范的require
var bar = require('./bar')
module.exports: commonjs規范中對外暴露本模塊接口的方式。每個模塊內部,都有一個module
對象,代表當前模塊,module對象包含module.id,module.filename,module.exports等信息
entry: 編譯的入口js文件,即需要處理的js文件(所有的其他模塊包括image,css,vue組件,html模板等都是通過js依賴引入進來的)
output: 編譯的出口js文件,即經過打包其他資源、合並、壓縮等處理之后生成的js文件
本示例中因為 app.js 依賴 bar.js ,所以打包之后的 bundle.js 可以理解為app.js和bar.js合並后的js
命令行工具中運行:wepack 即編譯成功
實際項目中的webpack解析
本項目已支持功能
1 對less編譯
2 對js es6語法支持 3 編譯.vue組件,並自動內聯組件樣式 4 圖片打包,包括對html內圖片處理(利用html-loader和es6字符串模板),對小圖片生成base64 5 利用htmlWebpackPlugin動態拼接html 的公共部分和內容部分,引入相應css/js資源,並構建到指定目錄, 對ejs模板支持 6 對js內依賴的css分離並壓縮 7 對js引用的公共模塊抽取分離成單獨文件 8 區分開發環境和生產環境 9 js 壓縮 10 靜態文件(css/js/img)hash版本支持 11 清除目標文件目錄 12 eslint支持並實現自動修復部分問題 13 vue接口請求axios支持 14 熱更新,自動編譯並刷新瀏覽器
項目目錄結構
|__ html
|__ dist
|__ income.html
|__ index.html
|__ src
|__ income
|__ income.ejs
|__ income.js
|__ index
|__ index.ejs
|__ index.js
|__ layouts
|__ footer.ejs
|__ header.ejs
|__ layout.ejs
|__ layout.js
|__ side-menu.e
|__ top-nav.ejs
|__ dist
|__ css
|__ img
|__ js
|__ income.js
|__ index.js
|__ manifest.js
|__ vendors.js
|__ src
|__ css
|__ img
|__ js
|__ component
|__ App.vue
|__ income.js
|__ index.js
|__ lib
|__ axios.min.js
|__ layer.js
|__ vue.js
|__ vue.min.js
|__ mock
|__ node_modules
|__ webpack-config
|__ .eslintrc.dev.js
|__ .eslintrc.js
|__ postcss.config.js
|__ resolve.config.js
|__ package.json
|__ .babelrc
|__ .eslintrc.js
一. entry介紹
var entries = getEntry('./src/**/*.js') // 獲得入口js文件 entries.vendors = ['vue','axios'] module.exports = { /* 輸入文件 */ entry: entries }
通常我們的項目中有大量的js入口文件,基本一個功能頁面有一個js,這時我們的 entry 文件為一個對象格式
entries 為:
{ income: './src/js/income.js', index: './src/js/index.js', vendors: [ 'vue', 'axios' ] }
key 值為對應模塊的別名,webpack會依次處理這些模塊。
vendors 為公共模塊,這里我們把vue, axios設置為公共模塊,供下面進行提取公共模塊操作。
之所以可以直接寫vue, axios,是因為我們在 alias 里設置了別名:
var path = require('path') module.exports = { // 模塊別名的配置,為了使用方便,一般來說所有模塊都是要配置一下別名的 alias: { 'vue': path.resolve(__dirname, '../src/js/lib/vue.min.js'), 'axios': path.resolve(__dirname, '../src/js/lib/axios.min.js') } }
這里的 path.resolve 作用是把相對路徑轉為絕對路徑,假設我項目建在d:/demo 目錄下,
_dirname: d:\demo
path.resolve(__dirname, '../src/js/lib/vue.min.js'): d:\demo\src\js\lib\vue.min.js
getEntry() 為獲取文件路徑的自定義函數:
/***** 獲取文件列表:輸出正確的js和html路徑 *****/ var glob = require('glob')
function getEntry(globPath) { var entries = {}, basename glob.sync(globPath).forEach(function (entry) { //排出layouts內的公共文件 if(entry.indexOf('layouts') == -1 && entry.indexOf('lib') == -1){ basename = path.basename(entry, path.extname(entry)) entries[basename] = entry } }) return entries }
通常webpack的entry入口文件,是功能性頁面的js,對於js庫等文件不需要列入入口文件進行處理。所以這里對lib文件夾進行了排除。layouts文件夾為獲取html路徑時需要排除的文件路徑。
二. output介紹
module.exports = { output: { /* 輸出目錄,沒有則新建 */ path: path.resolve(__dirname, './dist'), /* 靜態目錄,可以直接從這里取文件 */ publicPath: 'http://www.xxx.com/dist/', /* 文件名 */ filename: 'js/[name].js?v=[chunkhash:8]' } }
publicPath: 如果有這項,則html中的引用的js路徑會加上publicPath,即 http://www.xxx.com/dist/js/[name].js?v=[chunkhash:8]
filename: 這里可以自定義輸出后的文件名,加上版本號
[name] :輸入模塊的別名
[chunkhash] : 模塊的hash值,":8"代表保留8位hash值
[hash] : 整個編譯環境的hash值
三. module介紹
loaders: webpack利用各種loader來把不同格式的文件封裝成模塊加載到js內,比如css-loader, vue-loader
特別注意:webpack v1 和webpack v2 的區別
module: { - loaders: [ + rules: [ { test: /\.css$/, - loaders: [ - "style-loader", - "css-loader?modules=true" + use: [ + { + loader: "style-loader" + }, + { + loader: "css-loader", + options: { + modules: true + } + } ] }, { test: /\.jsx$/, loader: "babel-loader", // Do not use "use" here options: { // ... } } ] }
eslint-loader
module: { rules: [ { test: /\.js$/, enforce: 'pre', loader: 'eslint-loader', include: path.resolve(__dirname, './src/js/**/*.js'), exclude: ['./src/js/lib','./src/js/component'], options: { fix: true } } ] }
目的是 對js進行代碼風格和語法的校驗
enforce: 注意這是webpack v2的變動,v1是 preLoaders。設置為pre表示對js的校驗在編譯之前進行,我們只負責自己寫的js 語法和規范沒有問題即可,編譯后的代碼什么樣都不管。
通常我們只對自己寫的js進行校驗,類庫和包里的js無需校驗。include即聲明我們對哪些文件進行校驗,相反,exclude是排除校驗哪些文件。
fix:true 即在編譯時自動修復代碼風格和語法問題
babel-loader
{ test: /\.js$/, loader: 'babel-loader', exclude: ['node_modules','./src/js/lib','./src/js/component'] }
es6語法目前很多瀏覽器不支持,我們需要將其轉化為大部分瀏覽器支持的es5語法,這就需要babel-loader
css-loader style-loader post-loader less-loader
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] }, { test: /\.less$/, use: ExtractTextPlugin.extract(['css-loader','postcss-loader','less-loader']) }
對於有多個loader時,webpack v2也廢棄了v1的 " !" 連接,改為數組形式,且不能省略 " -loader " 以免造成名稱混亂意思模糊,執行順序為從右到左
postcss-loader為集合處理css各種問題的平台,其上面有各種插件來處理css,我們這里只用到了autoprefixer插件,后面插件部分會詳解
style-loader:可以將css以style內聯方式嵌入到html頁面
ExtractTextPlugin:提取css, 后面插件部分會進行詳解
file-loader url-loader 處理圖片
{ test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=5120&name=img/[name].[ext]?v=[hash:8]' }
webpack中處理圖片用file-loader,但url-loader有個好處,它可以把小圖片轉化成base64格式,其他的大圖片再用file-loader處理,這里的limit即為臨界值,這里定義小於5k圖片轉成base64格式,大於5k的用file-loader處理。
name: 可以重新定義處理后的圖片並加上版本值。
四. 插件plugin
module: {}, plugins: [ new ExtractTextPlugin('css/[name].css?v=[contenthash:6]'), new webpack.LoaderOptionsPlugin({ options: { eslint: require( './webpack-config/.eslintrc.js'), postcss: require( './webpack-config/postcss.config.js') }, }) ]
插件可以補充loader的功能,對其進行豐富完善,webpack聲明插件的方式可以像上面的寫法也可以如下方式:
module.exports.plugins.push(new htmlWebpackPlugin())
亦或
module.exports.plugins = module.exports.plugins.concat([ //壓縮css代碼 new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css/g, cssProcessor: require('cssnano'), cssProcessorOptions: { discardComments: {removeAll: true } }, canPrint: true }), //壓縮JS代碼 new webpack.optimize.UglifyJsPlugin({ output: { comments: false, // 去掉注釋內容 } }) ])
通常后兩種寫法用於動態的使用插件。
extract-text-webpack-plugin 插件
默認情況下,js依賴引入css,編譯后,css被加在js中,如果我們想把css提取出一個單獨的文件,可以使用這個插件,並可以對提取出的css進行自定義命名和加版本hash值
income.js中依賴income.less
如果不使用extractTextPlugin,編譯后的目錄結構為
查看income.js,發現css被引入在js之中,模塊id為16
再看下webpack sourcemap下的income.less,css-loader已經將此文件編譯成對外暴露的模塊形式,模塊id為16
使用extractTextPlugin插件后
income.js依賴的income.less被單獨提出,income.js中引入的模塊16發現提示 removed by extract-text-webpack-plugin,再看下此時的income.less
提示 removed by extract-text-webpack-plugin , 說明此插件已經順利的將js中的css提取成單文件形式
同時發現html中已插入income.css
這個是style-loader起的作用
LoaderOptionsPlugin插件
加載插件的配置項,比如eslint的語法配置,postcss的插件配置
optimize-css-assets-webpack-plugin 插件
壓縮css文件,對從js中提取出的css文件亦有效
注意:此插件是在css被提取出來加了hash值后進行處理,如果css文件提出來后被命名為 css/[name].css?v=[contenthash:8] 形式,插件的使用如下
new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css/g, //注意不要寫成 /\.css$/g, 否則匹配不到css文件會導致壓縮不成功 cssProcessor: require('cssnano'), cssProcessorOptions: { discardComments: {removeAll: true } }, canPrint: true })
html-webpack-plugin 插件
用於根據模板組合html各個部分,並插入對應引用的js,對前后端分離貢獻頗多,功能強大會有專門的篇章來介紹
browser-sync-webpack-plugin 插件
熱更新,自動刷新瀏覽器
var BrowserSyncPlugin = require('browser-sync-webpack-plugin') module.exports = { plugins: [ new BrowserSyncPlugin({ host: 'localhost', port: 3000, server: { baseDir: ['./'] } }) ] }
baseDir: localhost:3000 指定的目錄
結合webpack的watch,可以做到實時編譯並刷新瀏覽器
只需要 webpack --watch 即可
CommonsChunkPlugin 插件
提取js的公共模塊,此插件為webpack自帶的插件
// 提取公共模塊 new webpack.optimize.CommonsChunkPlugin({ names: ['vendors', 'manifest'], // 公共模塊的名稱 //filename: 'js/[name]-[chunkhash:6].js', // 公共模塊的名稱 chunks: 'vendors', // chunks是需要提取的模塊 minChunks: Infinity //公共模塊最小被引用的次數 })
通常我們的js可能會引入一些公共js文件,包括一些類庫,如果都打包在一個js中,這個js會變得非常龐大,而且一旦我們功能頁面的js有變化,會導致打包后的js版本號進行更新然后重新加載,這個代價有些大,所以我們會考慮把一些公共的js文件提取出一個單獨的文件,這樣在第一次訪問的時候會加載,之后就可以緩存下來,減少服務器請求的壓力並提高加載速度。
names: 定義公共js模塊的文件名,即vendors.js,manifest是為了存儲webpack編譯邏輯的一些信息,也會生成一個manifest.js並引入html。如果不生成manifest文件,這些webpack的編譯邏輯信息就會存儲在vendors中,當incomejs等頁面的功能js變化時,也會導致這個公共js的hash值變化,這樣又會導致重新加載100多k的vendor.js,這就失去了我們提取公共模塊的意義。
而如果多生成一個manifest.js文件,會發現,當incomejs等頁面頁面功能js變化時,只有manifest.js的hash值有更新,vendors.js的hash值不變,雖然這樣會導致重新請求manifest.js但它的體積不過幾kb,代價遠小於vendorjs,所以我們可以用這種方式來優化我們公共js模塊的加載方式。
官網如下解釋,可供參考:
To extract the webpack bootstrap logic into a separate file, use the CommonsChunkPlugin on a name which is not defined as entry. Commonly the name manifest is used. See the code splitting libraries guide for details.
new webpack.optimize.CommonsChunkPlugin({ name: "manifest", minChunks: Infinity })
filename: 我們可以重命名公共模塊的文件名格式,此舉會覆蓋output中的文件名命名方式,不定義這個屬性默認用output中的filename方式
chunks:需要提取的模塊,在entry中我們有如下定義 entries.vendors = ['vue','axios'] 所以定義 chunks: 'vendors' 就是我們會提取vuejs axiosjs到公共js中
minChunks: 公共模塊最小被引用的次數, 可以寫成2,3...。Infinity: with more entries, this ensures that no other module goes into the vendor chunk,推薦用infinity
CleanWebpackPlugin插件
清除目標文件
new CleanWebpackPlugin(['dist'])
源碼:https://github.com/saysmy/vue2-webpack2-demo
如有錯誤請指正
html模板拼接詳解請見:前后端分離之vue2.0+webpack2 實戰項目 -- html模板拼接
轉載請注明出處 https://i.cnblogs.com/EditPosts.aspx?postid=6635504