1.為什么要使用webpack
現今的很多網頁其實可以看做是功能豐富的應用,它們擁有著復雜的JavaScript代碼和一大堆依賴包。為了簡化開發的復雜度,前端社區涌現出了很多好的實踐方法。
1.模塊化,讓我們可以把復雜的程序細化為小的文件;
2.Scss,less等CSS預處理器。
3.轉換es6語法的處理器等等。
這些改進確實大大的提高了我們的開發效率,但是利用它們開發的文件往往需要進行額外的處理才能讓瀏覽器識別,而手動處理又是非常繁瑣的,這就為WebPack類的自動化工具的出現提供了需求。
2.什么是webpack
WebPack可以看做是模塊打包機。
在 webpack 里,所有類型的文件都可以是模塊,包含我們最常見的 JavaScript,以及 css 文件、圖片、json 文件等等。通過 webpack 的各種加載器,我們可以更有效地管理這些文件。它做的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),并將其打包為合適的格式以供瀏覽器使用。
3.WebPack和Grunt以及Gulp相比有什么特性
其實Webpack和另外兩個并沒有太多的可比性,Gulp/Grunt是一種能夠優化前端的開發流程的工具,而WebPack是一種模塊化的解決方案,模塊化是其他構件工具沒有的。gulp/grunt能做的,webpack也可以做。所以Webpack的優點(模塊化)使得Webpack可以替代Gulp/Grunt類的工具。
Grunt和Gulp的工作方式是:在一個配置文件中,指明對某些文件進行類似編譯,組合,壓縮等任務的具體步驟,這個工具之后可以自動替你完成這些任務。
Webpack的工作方式是:把你的項目當做一個整體,通過一個給定的主文件(如:index.js),Webpack將從這個文件開始找到你的項目的所有依賴文件,使用loaders處理它們,最后打包為一個瀏覽器可識別的JavaScript文件。
4.使用webpack
//全局安裝
npm install -g webpack
//安裝到你的項目目錄
npm install --save-dev webpack
正式使用Webpack前的準備
1.建立新文件夾(本例命名為testwebpack)
2.在文件夾里面使用npm init 創建package.json
{
"name": "webpackee",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
}
注:name值不能和所安裝的包重名,否則安裝失敗。所以本例用在webpack后面隨便加了兩個字母ee
3.使用npm install --save-dev命令安裝webpack,安裝完成后如下
{
"name": "webpackee",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.6.1"
}
}
4.在testwebpack文件中創建兩個文件夾(src文件夾和public文件夾),
1.src文件夾放原始數據和我們將寫的JavaScript模塊、路由模塊等等
2.public文件夾用來存放準備給瀏覽器讀取的數據(包括使用webpack生成的打包后的js文件以及一個index.html文件)
5.創建三個文件,index.html 文件放在public文件夾中,兩個js文件(Greeter.js和main.js)放在src文件夾中。
index.html文件只有最基礎的html代碼,它唯一的目的就是加載打包后的js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
<script src="bundle.js"></script>
</body>
</html>
greeter.js只包括一個用來返回包含問候信息的html元素的函數。
// greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
main.js用來把Greeter模塊返回的節點插入頁面。
//main.js
var greeter = require('./Greeter.js');
document.getElementById('root').appendChild(greeter());
通過配置文件來使用Webpack
1.在testwebpack文件中新建一個名為webpack.config.js的文件,并在其中進行最最簡單的配置,如下所示,它包含入口文件路徑和存放打包后文件的地方的路徑。
module.exports = {
entry: __dirname + "/src/main.js",//唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方供src引入加載
filename: "bundle.js"http://打包后輸出文件的文件名
}
}
2.通過webpack 命令將文件打包到public文件夾中
$ webpack
3.也可以在package.json中配置腳本命令取代默認的webpack命令。
"scripts": {
"start": "webpack" //配置的地方就是這里啦,相當于把npm的start命令指向webpack命令
},
注:npm的start是一個特殊的腳本名稱,它的特殊性表現在,在命令行中使用npm start就可以執行相關命令,如果對應的此腳本名稱不是start,想要在命令行中運行時,需要這樣用npm run scriptname如npm run build。
5.生成Source Maps(使調試更容易)
開發總是離不開調試,如果可以更加方便的調試當然就能提高開發效率,不過網頁預覽的代碼一般和開發的代碼不一樣,比如scss文件網頁預覽的為css文件。為了可以直接預覽scss等文件,方便調試的Source Maps便出現了。
在webpack的配置文件中配置source maps,需要開啟谷歌的devtool工具,f12-setting-Enable JavaScript source maps/Enable CSS source maps.
在本例中,隨便寫錯一處代碼,如
// greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greete;
};
本來應該返回greet,我加了一個e,肯定會報錯。但要是不用source maps,調試時候會顯示如下
上面只能定位到bundle這個js,不能定位到greeter.js這個文件,但使用了source maps,調試的時候顯示如下:
點擊greeter.js可直接跳到錯誤處。
webpack配置source maps可在webpack.config.js添加devtool選項即可
module.exports = {
entry: __dirname + "/src/main.js",//唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方供src引入加載
filename: "bundle.js"http://打包后輸出文件的文件名
},
devtool: 'source-map',//配置生成Source Maps,選擇合適的選項
}
具體配置如下:
6.使用webpack構建本地服務器
想不想讓你的瀏覽器監測你的代碼的修改,并自動刷新修改后的結果,其實Webpack提供一個可選的本地開發服務器,這個本地服務器基于node.js構建,可以實現你想要的這些功能,不過它是一個單獨的組件,在webpack中進行配置之前需要單獨安裝它作為項目開發依賴。
npm install --save-dev webpack-dev-server
然后在腳本命令行添加快捷命令:
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start":"webpack",
"server": "webpack-dev-server" //啟動命令
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.6.1",
"webpack-dev-server": "^2.4.5"
}
}
在webpack.config.js中配置服務器
devServer: {
contentBase: "./public", //本地服務器所加載的頁面所在的目錄
open : true, //打開瀏覽器
port: 9000 //端口
}
其他配置可以參考官網 https://doc.webpack-china.org/configuration/dev-server/
7.Loaders
Loaders是webpack中最讓人激動人心的功能之一了。通過使用不同的loader,webpack通過調用外部的腳本或工具可以對各種各樣的格式的文件進行處理,比如說分析JSON文件并把它轉換為JavaScript文件,或者說把下一代的JS文件(ES6,ES7)轉換為現代瀏覽器可以識別的JS文件。或者說對React的開發而言,合適的Loaders可以把React的JSX文件轉換為JS文件。
Loaders需要單獨安裝并且需要在webpack.config.js下的modules關鍵字下進行配置.
注:webpack2.0之前的版本是按如下方式配置的
module: {
loaders: {...}
}
2.0之后的版本是按在rule配置的,不多目前兩種方式都支持,以后會逐漸用第二種。
module: {
rules:[]
}
Loaders的配置選項包括以下幾方面:
1.test:接收一個匹配loaders所處理的文件的拓展名的正則表達式(必須),格式為
test: /\.json$/ //匹配json格式文件
2.loader:應用loader的名稱(必須),格式為:
module: {
rules:[
{
test: /\.vue$/,
loader: "json-loader"
},
{
test: /\.js$/,
loader: ['babel-loader'],
},
]
}
webpack2以后為了更清晰支持 -loader省略的。但測試不能省略
應用多個 loader 和選項時候可以用use屬性(數組)
module: {
rules: [{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000
}
}]
}
]
},
1.babel
Babel其實是幾個模塊化的包,其核心功能位于稱為babel-core的npm包中。對于每一個你需要的功能或拓展,你都需要安裝單獨的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)
我們先來一次性安裝這些依賴包
// npm一次性安裝多個依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
然后在webpack中配置
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'], //配置babel
}
]
},
然后在和webpack.config.js同級的目錄中建立 .babelrc 文件,命令如下
touch .babelrc
webpack會自動調用.babelrc里的babel配置選項。babelrc里面的內容如下:
{
"presets": ["es2015"]
}
現在就可以使用es6語法了,更改greeter.js文件內容
// greeter.js
var img = require('./assets/cat01.png');
var config = require('./config.json');
//es5
// module.exports = function() {
// var greet = document.createElement('div');
// greet.innerHTML = "Hi there and greetings![圖片上傳失敗...(image-e75b6-1519957547350)]" + config.greetText + "";
// return greet;
// };
//es6
function fn() {
let greet = document.createElement('div');
greet.innerHTML = "Hi there and greetings![圖片上傳失敗...(image-a3fe33-1519957547350)]" + config.greetText + "";
return greet;
};
export {fn}; //導出
更改main.js文件內容
//main.js
// var greeter = require('./greeter.js'); //es5
import { fn } from './greeter.js' //es6 導入
document.getElementById('root').appendChild(fn());
注:import和export必須配對使用,不能在greeter里面使用import,main里面還使用require導入,同理module.exports和require一起使用
開啟服務器
即可看到編譯成功。
注:主流瀏覽器支持大部分es6語法(比如本例的import和export),所以不用babel時候也可以編譯成功,經本地測試ie10不支持let,所以本例用此命令編譯,ie10瀏覽器不顯示此js返回的內容,但使用了babel,ie10就可以看見此js返回的內容
8.萬物皆模塊
Webpack有一個不可不說的優點,它把所有的文件都可以當做模塊處理,包括你的JavaScript代碼,也包括CSS和fonts以及圖片等等等,只有通過合適的loaders,它們都可以被當做模塊被處理。
css加載
webpack提供兩個工具處理樣式表,css-loader 和 style-loader,二者處理的任務不同。
css-loader:將css文件打包到js文件中,這樣才可以使用require或者import導入。
style-loader:將js文件中的css渲染到頁面中,在head標簽里面。
先安裝這兩個loader
//安裝
npm install --save-dev style-loader css-loader
配置
//使用
{
test: /\.css$/,
use: ['style-loader','css-loader'],
}
注:loader的加載順序都是從右向左的,即先加載css-loader,再加載style-loader,不能顛倒,先加載進入js再渲染頁面。顛倒會報錯
結果如下
sass使用
先安裝sass-loader和node-sass
npm install --save-dev sass-loader node-sass
因為sass-loader依賴于node-sass,所以需要安裝node-sass,不安裝會報錯。
配置
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析時排除的文件夾(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
use: ['style-loader','css-loader','sass-loader'],
}
]
},
main.js引入scss文件
import './index.scss'
編譯,sass-loader會自動把scss文件編譯成css,最后style-loader將其插入style里面
打包css
使用上面的loader,最后css會打包在js里面從而渲染在頁面中,但要是不想打包在js中,想把css分離出來。就需要插件了。
安裝extract-text-webpack-plugin插件
npm install --save-dev extract-text-webpack-plugin
在webpack.config.js中先引入后插件然再配置
頁面頂端定義:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
在loader里面定義
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析時排除的文件夾(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
// use: ['style-loader','css-loader','sass-loader'], //打包進js
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader",'sass-loader']
})
}
]
},
配置插件
plugins: [
new ExtractTextPlugin("css/index.css"),
]
注:css/index.css 表示css的路徑,名字可以隨便取,這里取名為index.css。啟動服務器的時候并不會在本地生成css文件夾和index.css,而是在服務器新生成的html中自動插入link標簽引入此路徑下的css
本地的頁面結構如下
開啟服務器后的預覽頁面
現在開始測試打包
當輸入打包命令后,如果在項目中有public文件夾,會在生成的html添加link連接樣式表,并且生成上面自己的路徑(css/index)。如果沒有public文件夾(文件出口),會自動生成一個public文件夾
打包html
默認情況下,我們在public文件夾中新建一個index.html文件,這樣打包后引入js。但要是不想在public建頁面,在其他文件夾(比如src文件夾)新建頁面,打包時再生成打包后的html。所以
安裝插件
npm install --save-dev html-webpack-plugin
配置
plugins: [
//這里開始寫
new HtmlWebpackPlugin({
template: 'src/index.html', //文件路徑
inject: 'body' //打包之后的js插入文檔的位置 (body表示script標簽的位置在body里面的最下面即</body>上面)
}),
]
在src文件夾新建index.html
啟動服務器
同css一樣,本地頁面無js添加,預覽頁面會自動添加js
打包
同css一樣,無public,生成public文件夾新增index.html,內容自動添加script標簽,有public文件夾,直接新增index.html,內容自動添加script標簽
引入并打包img
安裝url-loader或者file-loader
url-loader和file-loader 都是用于打包文件和圖片,但是這2個加載器區別如下
一般限制小圖片轉 base64 可以用 url-loader,其他情況都用 file-loader。
url-loader應該是file-loader上加了一層過濾。本例中使用url-loader,但兩者存在依賴關系,需全部安裝才可使用。
安裝
install url-loader file-loader --save-dev
配置
module: {
rules: [
{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}
]
}
test 屬性代表可以匹配的圖片類型,除了 png、jpg 之外也可以添加 gif 等,以豎線隔開即開。
limit 字段代表圖片打包限制,指當圖片大小小于限制時會自動轉成 base64 碼引用。
name 字段指定了打包后,在打包根目錄(output.path)下生成名為 images 的文件夾,并在原圖片名前加上8位 hash 值。
在greeter.js中引用
var imgurl = require('./assets/cat01.png');
然后就可以將字符串進行拼接使用了
所有配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: __dirname + "/src/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方供src引入加載
filename: "bundle.js" //打包后輸出文件的文件名
},
devtool: 'eval-source-map', //配置生成Source Maps,選擇合適的選項
devServer: {
contentBase: "./src", //本地服務器所加載的頁面所在的目錄
open: true,
compress: true
// colors: true, //終端中輸出結果為彩色
// historyApiFallback: true, //不跳轉
// inline: true, //實時刷新
// port: 9000
},
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析時排除的文件夾(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
// use: ['style-loader','css-loader','sass-loader'],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader",'sass-loader']
})
}
]
},
plugins: [
//這里開始寫
new HtmlWebpackPlugin({
template: 'src/index.html', //文件路徑
inject: 'body' //打包之后的js插入文檔的位置
}),
new ExtractTextPlugin("css/index.css"),
]
}