什麼是 webpack
webpack
可以看做是模塊打包機:它做的事情是,分析你的項目結構,找到 JavaScript
模塊以及其他的一些瀏覽器不能直接運行的擴展語言(Scss
,TypeScript
等),並將其轉換和打包爲合適的格式供瀏覽器使用
使用 webpack 前的準備
npm init
:創建一個package.json
文件。它是標準的npm
說明文件,包括當前項目的依賴模塊、自定義的腳本任務等cnpm install webpack --save-dev
:安裝webpack
文件目錄的構建方面:一般由兩個目錄構成:
app
(存放原始數據和JS
模塊),public
(存放供瀏覽器讀取的文件,即webpack
打包生成的文件,和html
源代碼)創建根目錄下
webpack.config.js
文件,這是webpack
的配置文件,基本的配置如下:
module.exports = {
entry: __dirname + "/app/main.js", // 已多次提及的唯一入口文件
output: {
path: __dirname + "/public", // 打包後的文件存放的地方
filename: "bundle.js" // 打包後輸出文件的文件名
}
};
‘__dirname’是 node.js 中的全局變量,它指向當前執行腳本所在的目錄
package.json
方面的配置:(配置好後,直接可以在命令行:npm start
即可進行打包編譯,需要注意的是,除了 "start"
之外,所有的自定義命令都需要通過 npm run 命令名稱
的形式進行)
"scripts": {
"start": "webpack"
}
webpack 提供了一種對應編譯文件和源文件的方法,使得編譯後的代碼可讀性更高,更容易調試。
module.exports = {
// 小到中型的項目中,eval-source-map 是一個很好的選擇
devtool: 'eval-source-map', // 注意:只應該在開發階段使用它
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
}
};
正式書寫代碼時,通過 module.exports
的方式進行模塊的導出,通過 require
的方式進行模塊的導入
同時建立多個入口文件:
module.exports = {
// 可以同時定義多個入口文件
entry: {
'index': __dirname + '/app/main.js',
'indexT': __dirname + '/app/main2.js'
},
output: {
path: __dirname + '/build',
filename: '[name]-[hash].js' // 定義多個出口文件時的命名
},
}
使用 webpack 構建本地服務器
cnpm install webpack-dev-server --save-dev
:安裝 webpack-dev-server
在 webpack.config.js
中進行配置
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public", // 本地服務器所加載的頁面所在的目錄
historyApiFallback: true, // 不跳轉
inline: true // 實時刷新
}
};
之後,在 package.json 中的 scripts 對象添加命令,方便開啓本地服務器:
"server": "webpack-dev-server --open"
此時,通過在終端輸入:npm run server
就可以本地的 8080 端口查看結果
Loaders
通過使用不同的 loader
,webpack
有能力調用外部的腳本或者工具,實現對不同格式的文件的處理
如分析轉換 scss
爲 css
,把 ES6
,ES7
轉爲現代瀏覽器兼容的 JS
文件等等
Loaders
需要單獨安裝並且需要在 webpack.config.js
中的 modules
關鍵字下進行配置,配置包括以下方面:
1. test
:一個用以匹配 loaders
所處理文件的擴展的正則表達式(必須!)
2. loader
:loader
的名稱(必須!)
3. include
/ exclude
:手動添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選)
4. query
:爲 loaders
提供額外的設置選項(可選)
Babel
Babel
:其實是一個編譯 JavaScript
的平臺,它可以編譯 ES6
,ES7
,基於 JavaScript
的擴展語言:React
的 JSX
,TypeScript
等等
Babel
是幾個模塊化的包,其核心功能位於稱爲 babel-core
的 npm
包中,對於每個我們需要的功能或擴展,都需要單獨安裝響應的包,如:
- 解析 ES6
的 babel-env-preset
包
- 解析 JSX
的 babel-preset-react
包
安裝他們:cnpm install babel-core babel-loader babel-preset-env babel-preset-react --save-dev
cnpm install react react-dom --save
在 webpack.config.js
中配置 Babel
:
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
配置完成後,就可以使用 ES6
以及 JSX
語法了
關於 Babel 的配置
babel
具有非常多的配置選項,可以將它的配置單獨放在 .babelrc
的配置文件中(webpack
會自動調用 .babelrc
裏的 babel
配置選項)
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
// 以下放到 .babelrc 中
// options: {
// presets: [
// "env", "react"
// ]
// }
},
exclude: /node_modules/
}
]
}
在 .babelrc
中:
{
"presets": ["react", "env"]
}
一切皆模塊
webpack
的特殊優點是,它把所有的文件都當做模塊處理,JavaScript
、CSS
、fonts
、圖片等等,通過合適的 loader
都可以被處理
1. CSS 的處理
css-loader
:能夠使用類似@import
和url(...)
的方法實現require()
的功能style-load
:將所有計算後的樣式加入到頁面中
兩者組合在一起能夠把樣式表嵌入到
webpack
打包後的JS
文件中
安裝他們:cnpm install style-loader css-loader --save-dev
在 webpack.config.js 中配置使用:
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
}
]
}
]
}
注意這裏對同一個文件引入多個 loader 的方法
之後,只需將對應的 CSS
文件,通過 require
導入到入口文件 main.js
中就可以使用共同打包
2. CSS module
CSS modules
意在把 JS
的模塊化思想帶入 CSS
中,通過 CSS
模塊,所有的類名,動畫名默認都只作用於當前模塊,這樣可以有效的避免全局污染,配置如下:(在 webpack.config.js
中進行配置)
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定啓用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的類名格式
}
}
]
}
// 使用cssModule添加類名的方法
<div className={styles.root}>
3. CSS 預處理器
對於 Sass、Less 之類的預處理器可以在 webpack 使用相關的 loaders 進行配置:
1. Less Loader
2. Sass Loader
3. Stylus Loader
不過其實也存在一個 CSS 的處理平臺 -PostCSS,它的其中一個功能是爲 CSS 代碼自動添加使用不同瀏覽器的 CSS 前綴
安裝他們:cnpm install postcss-loader autoprefixer --save-dev
其中,autoprefixer
是自動添加前綴的插件。之後再 webpack 配置文件中添加 postcss-loader:
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}, {
loader: "postcss-loader" // 添加 postcss-loader
}
]
}
4. 對圖片的打包
圖片需要使用 url-loader 來加載,安裝:cnpm install url-loader --save-dev
使用:
{
test: /(\.png|\.jpg)$/,
// 小於8kb的圖片不會被處理
use: 'url-loader?limit=8192&name=img/[hash:8].[name].[ext]'
}
插件(Plugins)
插件是用來擴展 webpack 功能的,他們會在整個構建過程中生效,執行相關的任務
Loaders 和 Plugins 的區別:
- loaders 是在打包構建過程中用來處理源文件的(JSX,Scss,Less…),一次處理一個
- 插件並不直接操作單個文件,它直接對整個構建過程起作用
1. 使用插件的方法
使用哪個插件,就通過 npm
來進行安裝,之後再 webpack.confign.js
中的 plugins
關鍵字部分添加該插件的一個實例
2. 常用的插件之:HtmlWebpackPlugin
該插件的作用:依據一個簡單的 html 模板,生成一個自動引用打包後的 JS 文件的新的 index.html,這在每次生成的 JS 文件名稱中帶有 hash 值時非常有用(因爲每次名稱不同)
安裝他們:cnpm install html-webpack-plugin --save-dev
之後,需要對項目進行一些更改:
刪除
public
文件夾,利用此插件html
文件會自動生成,前面操作的JS
文件也會自動關聯到生成的新html
文件中在 app 目錄下,創建的 html 模板以這種格式創建:
名稱.tmpl.html
,插件會根據這個模板生成最終的 html 頁面,並會進行自動的依賴更新 webpack 的配置文件,新建一個 build 文件夾用來存放生成的文件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + '/app/main.js', // 入口文件
output: {
path: __dirname + '/build', // 出口文件所在文件夾
filename: 'bundle.js' // 打包後的文件
},
// 啓動服務器:webpack-dev-server --open
devServer: {
contentBase: './public',//本地服務器所加載的頁面所在的目錄
historyApiFallback: true,//不跳轉
inline: true//實時刷新
},
// 關於 Babel 的配置,可以單獨在 .babelrc 中進行配置
module: {
rules: [
// Babel 的一些簡單配置
// 下載的依賴包:babel-core babel-loader babel-preset-env babel-preset-react react react-dom
// 配置好後就可以使用 react 語法了
{
test: /(\.jsx|\.js)$/, // 處理的文件
use: {
loader: 'babel-loader', // 使用的 loader
// 在 .babelrc 中進行配置
// options: {
// presets: [
// 'env', 'react'
// ]
// }
},
// 不需要處理的文件夾
exclude: /node_modules/
},
// 安裝 postcss-loader 和 autoprefixer
// PostCSS 代碼自動添加使用不同瀏覽器的 CSS 前綴
// autoprefixer 自動添加前綴的插件
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
}, {
loader: 'css-loader',
// css模塊:所有的類名,動畫名都只作用於當前模塊,避免全局污染
options: {
modules: true, // 指定啓用css modules
// 指定css的類名格式
// localIdentName: '[name]__[local]--[hash:base64:5]'
}
}, {
// 需要新建 postcss.config.js 文件進行其他配置
loader: 'postcss-loader'
}
]
}
]
},
plugins: [
// 添加版權的插件
new webpack.BannerPlugin('版權所有,翻版必究'),
//new 一個這個插件的實例,並傳入相關的參數
new HtmlWebpackPlugin({
template: __dirname + '/app/index.tmpl.html'
})
]
};
之後,正常的在終端執行:npm start
就可以進行新一輪的打包編譯了
3. 常用的插件之:Hot Module Replacement
Hot Module Replacement
(HMR
)插件允許在修改組件代碼後,自動刷新實時預覽修改後的效果
它是 webpack
裏的一個插件
如何配置:
1. 在 webpack.config.js
中添加 HMR
插件
2. 在 webpack Dev Server
中添加 “hot
” 參數
3. 配置結束後,在 JS
模塊中執行一個 webpack
提供的 API
才能實現熱加載
思路:
1. Babel
和 webpack
是兩個獨立的工具
2. 兩者可以一起工作的同時,都可以通過插件擴展功能
3. HMR
是一個 webpack
插件,想讓他工作,還需要對模塊進行額外的配置
4. Babel
中有一個 react-transform-hmr
插件,可以讓 HMR
正常工作,而且不產生對模塊的額外配置影響
在 webpack.config.js 中的配置:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + '/app/main.js', // 入口文件
output: {
path: __dirname + '/build', // 出口文件所在文件夾
filename: 'bundle.js' // 打包後的文件
},
// 啓動服務器:webpack-dev-server --open
devServer: {
contentBase: './public', // 本地服務器所加載的頁面所在的目錄
historyApiFallback: true, // 不跳轉
inline: true, // 實時刷新
hot: true // 配合 HMR 實現熱加載
},
plugins: [
// 熱加載插件,還需要在 .babelrc 中的 "env" 進行配置
new webpack.HotModuleReplacementPlugin()
]
};
安裝: cnpm install babel-plugin-react-transform react-transform-hmr --save-dev
在 Babel 中的配置:
{
"presets": ["react", "env"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
配置完成之後,就可以在使用 React 時,進行熱加載模塊了
產品階段的構建
產品階段:主要是對打包的文件進行額外處理,如壓縮、優化、緩存,分離 CSS
和 JS
等
創建一個 webapck.production.config.js 文件:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + '/app/main.js', //已多次提及的唯一入口文件
output: {
path: __dirname + '/build',
filename: 'bundle.js'
},
devtool: 'null', //注意修改了這裏,這能大大壓縮我們的打包代碼
devServer: {
contentBase: './public', //本地服務器所加載的頁面所在的目錄
historyApiFallback: true, //不跳轉
inline: true,
hot: true
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: 'babel-loader'
},
exclude: /node_modules/
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
modules: true
}
}, {
loader: 'postcss-loader'
}],
})
}]
},
plugins: [
new webpack.BannerPlugin('朋友們注意版權哦~~'),
new HtmlWebpackPlugin({
template: __dirname + '/app/index.tmpl.html' //new 一個這個插件的實例,並傳入相關的參數
}),
new webpack.HotModuleReplacementPlugin() //熱加載插件
],
};
package.json 文件也需要配置:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server --open",
"build": "set NODE_ENV=production && webpack --config ./webpack.production.config.js --progress"
},
優化插件
webpack 提供了一些優化插件,產品發佈階段所需的功能:
OccurenceOrderPlugin
:爲組件分配ID
,通過分析和優先考慮使用最多的模塊,並未他們分配最小ID
UglifyJsPlugin
:壓縮JS
代碼ExtractTextPlugin
:分離CSS
和JS
文件
上面的插件中,OccurenceOrderPlugin
和 UglifyJsPlugin
是內置插件,需要下載的只有:ExtractTextPlugin
:
cnpm install extract-text-webpack-plugin --save-dev
之後再 webpack.production.config.js 引用他們:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: __dirname + '/app/main.js', //已多次提及的唯一入口文件
output: {
path: __dirname + '/build',
filename: 'bundle.js'
},
devtool: 'null', //注意修改了這裏,這能大大壓縮我們的打包代碼
devServer: {
contentBase: './public', //本地服務器所加載的頁面所在的目錄
historyApiFallback: true, //不跳轉
inline: true,
hot: true
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: 'babel-loader'
},
exclude: /node_modules/
}, {
test: /\.css$/,
// 分離 CSS,成爲一個單獨的文件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader'
]
})
}]
},
plugins: [
new webpack.BannerPlugin('朋友們注意版權哦~~'),
new HtmlWebpackPlugin({
template: __dirname + '/app/index.tmpl.html', //new 一個這個插件的實例,並傳入相關的參數
// 壓縮HTML文件
minify: {
removeComments: true, // 移除HTML中的註釋
collapseWhitespace: true // 刪除空白符與換行符
}
}),
// 爲組件分配 ID,通過這個插件可以優先考慮使用最多的模塊,分配給他們最小的 ID
new webpack.optimize.OccurrenceOrderPlugin(),
// 壓縮 JS 文件
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
except: ['$super', '$', 'exports', 'require'] //排除關鍵字
}),
// 分離 CSS 和 JS 文件
new ExtractTextPlugin('style.css')
],
};
之後再終端執行:npm run build
就可以進行產品階段的打包了
緩存
使用這種方式:
output: {
path: __dirname + "/build",
filename: "bundle-[hash].js" // 添加 hash 值
},
添加 hash 之後,導致改變文件內容後的重新打包,文件名不同而文件越來越多,可以使用:clean-webpack-plugin
來進行清理
安裝他們:cnpm install clean-webpack-plugin --save-dev
在配置文件中進行響應的配置即可:
const CleanWebpackPlugin = require("clean-webpack-plugin");
plugins: [
...// 這裏是之前配置的其它各種插件
new CleanWebpackPlugin('build/*.*', {
root: __dirname,
verbose: true,
dry: false
})
]