專案小白如何從0開始配置webpack
自己配置過webpack的人應該都知道,webpack真的好複雜,一開始做專案都是拿別人現成的做做小修改,但是別人的終究沒有自己配的舒服。所以我打算寫這篇文章,從我的配置中帶大家瞭解webpack配置,簡化讀webpack官方頁面的複雜度。
首先我們需要明確我們要求webpack做什麼?
1.所寫即所得,我們在編譯器中寫了程式碼能馬上呈現在偵錯程式上(熱更新服務)
2.本地開發獲取資料存在的跨域問題(代理,解決跨域)
3.使用es678,less,sass等(翻譯,讓瀏覽器懂得我們的程式碼)
4.提高專案效能,比如壓縮程式碼,壓縮圖片等(專案優化)
5.如果與輸入相關的需求,找entry(比如多頁面就有多個入口) 6.如果與輸出相關的需求,找output(比如你需要定義輸出檔案的路徑、名字等等) 7.如果與模組定址相關的需求,找resolve(比如定義別名alias) 8.如果與轉譯相關的需求,找loader(比如處理sass處理es678N) 9.如果與構建流程相關的需求,找plugin(比如我需要在打包完成後,將打包好的檔案複製到某個目錄,然後提交到git上) 接下來我們來看下webpack的一些基本配置
1.entry(專案入口)
2.output(出口檔案)
3.modules(模組處理)
4.plugin(外掛)
5.resolve
6.devserver
7.mode,
8devtool
1.專案入口設定(entry)
主要有三種方式
1.字串形式
entry: '.src/main.js'
複製程式碼
2.陣列形式
entry: [react, react-dom]
複製程式碼
3.物件形式
entry: {
main:'./src/index2.js',
second: './src/index2.js',
vendor:['react', 'react-dom']
}
複製程式碼
2.出口檔案設定及(output)
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
複製程式碼
在上面的示例中,我們通過 output.filename 和 output.path 屬性,來告訴 webpack bundle 的名稱,以及我們想要 bundle 生成(emit)到哪裡。 Path.resolve是什麼,引入的path模組是幹什麼用的
Nodejs
該path.resolve()方法將一系列路徑或路徑段解析為絕對路徑。
給定的路徑序列從右到左處理,隨後每個path路徑都被預先載入,直到構造出絕對路徑。例如,給定路徑段的序列:/foo,/bar,baz,呼叫path.resolve('/foo', '/bar', 'baz')將返回/bar/baz。
如果在處理path完所有給定段之後尚未生成絕對路徑,則使用當前工作目錄。 生成的路徑已規範化,並且除非將路徑解析為根目錄,否則將刪除尾部斜槓。 零長度path段被忽略。
如果沒有path傳遞段,path.resolve()將返回當前工作目錄的絕對路徑。
path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// If the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'
_dirname
複製程式碼
當前模組的目錄名稱。這是一樣 path.dirname()的__filename。
示例:node example.js從中執行/Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
複製程式碼
我們在配置output是常用的幾個
filename 這個是輸出檔案的名稱,字串型別,如果只有一個輸出檔案,可以寫成靜態名稱。例如
output:{
filename:'bundle.js'
}
複製程式碼
當然了,在我們日常工作中,一般情況下是不會有這種情況的,當專案很大的時候,如果不分塊打包,bundle.js會驚人的大,專案越大,bundle.js就會越大,這不是我們今天討論的重點,以後再說
多個chunk的時候怎麼辦呢
webpack會為每個生成的Chunk取一個名稱,Chunk的名稱和Entry的配置有關:
1. 如果entry是一個string或者array,就只會生成一個chunk,這個chunk的名稱是main;
2. 如果entry是一個object,就可能出現多個chunk,這時chunk的名稱是object鍵值對裡鍵的名稱
然而,當通過多個入口起點(entry point)、程式碼拆分(code splitting)或各種外掛(plugin)建立多個
bundle,應該使用以下一種替換方式,來賦予每個 bundle 一個唯一的名稱…… 使用入口名稱:
output:{
filename: "[name].bundle.js"
}
複製程式碼
使用內部 chunk id
output:{
filename: "[id].bundle.js"
}
複製程式碼
使用每次構建過程中,唯一的 hash 生成
output:{
filename: "[name].[hash].bundle.js"
}
複製程式碼
使用基於每個 chunk 內容的 hash:
output:{
filename: "[chunkhash].bundle.js"
}
複製程式碼
這裡多出來幾個陌生詞彙hash、chunkhash,它們是什麼? hash、chunkhash和contenthash三者的區別 hash
hash的值是相同的,如果都使用hash的話,因為這是工程級別的,即每次修改任何一個檔案,所有檔名的hash至都將改變。所以一旦修改了任何一個檔案,整個專案的檔案快取都將失效。所以對於沒有改變的模組而言,這樣做顯然不恰當,因為快取失效了嘛。此時,chunkhash的用途隨之而來。 chunkhash
只有被修改了的檔案的檔名,hash值修改
filename: '[name]-[chunkhash].js'
當我們使用mini-css-extract-plugin拆分css的時候,就需要使用chunkhash,我一個js檔案裡面引入了css檔案。這時要是我修改了js,但沒修改css,可以通過chunkhash快取css檔案
contenthash
對css使用了chunkhash之後,我們測試會發現,如果修改了js,css檔名的hash值確實沒變,但這時要是我們修改css檔案的話,我們就會發現css檔名的chunkhash值居然沒變化,這樣就導致我們的非覆蓋釋出css檔案失效了。所以這裡需要注意就是css檔案必須使用contenthash。
上面介紹的 id、name、hash、chunkhash等都是webpack內建變數, id是唯一標示,不會重複,從0開始, name 是模組名稱,是你自己起的,在配置路由懶載入的時候可以自己命名 官網介紹的很清楚,我就不再這裡囉嗦了, chunkFilename
官網解釋:此選項決定了非入口(non-entry) chunk 檔案的名稱, 什麼場景需要呢?
在按需載入(非同步)模組的時候,也就是路由懶載入,這樣的檔案是沒有被列在entry中的,
比如
{
entry: {
"index": "pages/index.jsx"
},
output: {
filename: "[name].min.js",
chunkFilename: "[name].min.js"
}
}
const myModel = r => require.ensure([], () => r(require('./myVue.vue')), 'myModel')
複製程式碼
上面的例子,通過filename輸出的是index.min.js 非同步載入的模組是要以檔案形式載入哦,所以這時生成的檔名是以chunkname配置的,通過chunkFilename輸出的是myModel.min.js 所以chunkFilename也很重要哦!!! path path是配置輸出檔案存放在本地的目錄,字串型別,是絕對路徑
output:{
path: path.resolve(__dirname, 'dist/assets')
}
複製程式碼
__dirname,這個昨天說過,可以回顧一下,就是當前檔案所在的資料夾的名字 publicPath 對構建出的資源進行非同步載入(圖片,檔案),該選項的值是以 runtime(執行時) 或 loader(載入時) 所建立的每個 URL 為字首。因此,在多數情況下,此選項的值都會以/結束。 預設值是一個空字串 "",即相對路徑,配置錯誤會導致404 簡單說,就是靜態檔案託管在cdn上 舉個栗子: 如果你這麼配置:
output:{
filename:'[name]_[chunkhash:8].js',
publicPath:'https://www.qdtalk.com/assets/'
}
複製程式碼
打包編譯後,html頁面就是這樣的
path 和publicPath都支援字串模板
- 配置 單出口 // webpack 配置
const path = require('path');
module.exports = {
entry: main: './src/main.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
複製程式碼
- 配置 多出口
// webpack 配置
const path = require('path');
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
複製程式碼
3.modules(模組處理)
模組處理主要是對loader的配置
此處引用官網對loader的定義
loader 讓 webpack 能夠去處理那些非 JavaScript 檔案(webpack 自身只理解 JavaScript)。loader 可以將所有型別的檔案轉換為webpack能夠處理的有效模組,然後你就可以利用webpack的打包能力,對它們進行處理。
本質上,webpack loader 將所有型別的檔案,轉換為應用程式的依賴圖(和最終的 bundle)可以直接引用的模組。 注意,loader 能夠 import 匯入任何型別的模組(例如 .css 檔案),這是 webpack 特有的功能,其他打包程式或任務執行器的可能並不支援。我們認為這種語言擴充套件是有很必要的,因為這可以使開發人員建立出更準確的依賴關係圖。 在更高層面,在 webpack 的配置中 loader 有兩個目標:
1. test 屬性,用於標識出應該被對應的 loader 進行轉換的某個或某些檔案。
2. use 屬性,表示進行轉換時,應該使用哪個 loader。
webpack.config.js
const path = require('path');
const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
module.exports = config;
複製程式碼
以上配置中,對一個單獨的 module 物件定義了 rules 屬性,裡面包含兩個必須屬性:test 和 use。這告訴 webpack 編譯器(compiler) 如下資訊:
“嘿,webpack 編譯器,當你碰到「在 require()/import 語句中被解析為 '.txt' 的路徑」時,在你對它打包之前,先使用 raw-loader 轉換一下。” 重要的是要記得,在 webpack 配置中定義 loader 時,要定義在 module.rules 中,而不是 rules。然而,在定義錯誤時 webpack 會給出嚴重的警告。為了使你受益於此,如果沒有按照正確方式去做,webpack 會“給出嚴重的警告”
在webpack中有許許多多的loader,此處我按照官方文件解釋下和我理解的用法 loader 特性
- 幾乎所有 loader 都 需要安裝, 但 不需要 在 webpack 配置檔案中通過 require 引入
- 逆向編譯,鏈式傳遞 檔案
- raw-loader 載入檔案原始內容(utf-8)
- val-loader 將程式碼作為模組執行,並將 exports 轉為 JS 程式碼
- url-loader 像 file loader 一樣工作,但如果檔案小於限制,可以返回 data URL
- file-loader 將檔案傳送到輸出資料夾,並返回(相對)URL raw-loader 可以讓你在程式碼中引入檔案
import txt from './file.txt';
複製程式碼
val-loader 載入的模組必須使用以下函式介面,將 default export 匯出為一個函式。
function answer () {
return {
code: 'module.exports = 42;'
}
};
module.exports = answer;
複製程式碼
url-loader
url-loader 功能類似於 file-loader,但是在檔案大小(單位 byte)低於指定的限制時,可以返回一個 DataURL。 file-loader 將檔案傳送到輸出資料夾,並返回(相對)URL(不會再對檔案做處理) JSON
- json-loader 載入 JSON 檔案(預設包含)
- json5-loader 載入和轉譯 JSON 5 檔案
- cson-loader 載入和轉譯 CSON 檔案 主要處理json檔案
轉換編譯(Transpiling)
- script-loader 在全域性上下文中執行一次 JavaScript 檔案(如在 script 標籤),不需要解析
- babel-loader 載入 ES2015+ 程式碼,然後使用 Babel 轉譯為 ES5
- buble-loader 使用 Bublé 載入 ES2015+ 程式碼,並且將程式碼轉譯為 ES5
- traceur-loader 載入 ES2015+ 程式碼,然後使用 Traceur 轉譯為 ES5
- ts-loader 或 awesome-typescript-loader 像 JavaScript 一樣載入 TypeScript 2.0+
- coffee-loader 像 JavaScript 一樣載入 CoffeeScript
React開發過程中我們需要將jsx或者es6程式碼轉譯成es5程式碼。 我們需要用到babel-loader
{
'test': /\.(js|jsx)$/, // babel 轉換為相容性的 js
'exclude': /node_modules/,
'loader': 'babel-loader',
'query': {
'presets': ['react', 'latest', 'stage-0', 'react-hmre']
},
'include': path.resolve(__dirname, '../client')
},
複製程式碼
注:exclude是不轉換node_modules的程式碼,query === options 此處注意babel版本6和7設定上有區別。 如果引用錯誤會報錯 模板(Templating)
- html-loader 匯出 HTML 為字串,需要引用靜態資源
- pug-loader 載入 Pug 模板並返回一個函式
- jade-loader 載入 Jade 模板並返回一個函式
- markdown-loader 將 Markdown 轉譯為 HTML
- react-markdown-loader 使用 markdown-parse parser(解析器) 將 Markdown 編譯為 React 元件
- posthtml-loader 使用 PostHTML 載入並轉換 HTML 檔案
- handlebars-loader 將 Handlebars 轉移為 HTML
- markup-inline-loader 將內聯的 SVG/MathML 檔案轉換為 HTML。在應用於圖示字型,或將 CSS 動畫應用於 SVG 時非常有用。
樣式
- style-loader 將模組的匯出作為樣式新增到 DOM 中
- css-loader 解析 CSS 檔案後,使用 import 載入,並且返回 CSS 程式碼
- less-loader 載入和轉譯 LESS 檔案
- sass-loader 載入和轉譯 SASS/SCSS 檔案
- postcss-loader 使用 PostCSS 載入和轉譯 CSS/SSS 檔案
- stylus-loader 載入和轉譯 Stylus 檔案 如果我們在開發中使用了less或者sass,我們需要先轉換成css在引入到專案中
{
'test': /\.less$/,
'loader': ['style-loader', 'css-loader', {
'loader': 'less-loader',
'options': {
'javascriptEnabled': true
}
}]
},
複製程式碼
此處的執行順序。less-loader -> css-loader -> style-loader 清理和測試(Linting && Testing)
- mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
- eslint-loader PreLoader,使用 ESLint 清理程式碼
- jshint-loader PreLoader,使用 JSHint 清理程式碼
- jscs-loader PreLoader,使用 JSCS 檢查程式碼樣式
- coverjs-loader PreLoader,使用 CoverJS 確定測試覆蓋率
框架(Frameworks)
- vue-loader 載入和轉譯 Vue 元件
- polymer-loader 使用選擇前處理器(preprocessor)處理,並且 require() 類似一等模組(first-class)的 Web 元件
- angular2-template-loader 載入和轉譯 Angular 元件
核心重點(敲黑板)
plugins(外掛)
作用:可以處理各種任務,從打包優化和壓縮,一直到重新定義環境中的變數 loader不需要require. plugin需要
loader 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。外掛介面功能極其強大,可以用來處理各種各樣的任務。
想要使用一個外掛,你只需要 require() 它,然後把它新增到 plugins 陣列中。多數外掛可以通過選項(option)自定義。你也可以在一個配置檔案中因為不同目的而多次使用同一個外掛,這時需要通過使用 new 操作符來建立它的一個例項。 下面是我配置的用於開發環境的plugins例項
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通過 npm 安裝
const webpack = require('webpack'); // 用於訪問內建外掛
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
複製程式碼
'plugins': [
new webpack.optimize.OccurrenceOrderPlugin(), // 調整模組的打包順序,用到次數更多的會出現在檔案的前面
new webpack.DefinePlugin({ // DefinePlugin 允許建立一個在編譯時可以配置的全域性常量。
'process.env.NODE_ENV': JSON.stringify('development')
}),
new HtmlWebpackPlugin({ // HtmlWebpackPlugin簡化了HTML檔案的建立,以便為你的webpack包提供服務。這對於在檔名中包含每次會隨著編譯而發生變化雜湊的 webpack bundle 尤其有用。 你可以讓外掛為你生成一個HTML檔案,使用lodash模板提供你自己的模板,或使用你自己的loader。
'hash': true,
'title': 'Demo',
'filename': 'index.html',
'template': path.resolve(__dirname, '../views/index.ejs'),
'inject': 'body'
}),
new webpack.HotModuleReplacementPlugin(), // 啟用熱更新
new webpack.NoEmitOnErrorsPlugin(), // 輸出階段遇到編譯錯誤跳過
new webpack.NamedModulesPlugin(), // 當開啟 HMR 的時候使用該外掛會顯示模組的相對路徑,建議用於開發環境。
new webpack.ProgressPlugin(), // 輸出構建進度
]
複製程式碼