今天我要進一步深入的了解webpack的各種功能,webpack有着無數插件可以為項目提供各種支援,其中一部分對項目檔案進行預處理的插件叫做loader(預處理器),比較常用的有:
- css預編譯,less或sass預編譯 css-loader,style-loader,less-loader 把css預編譯并通過style标簽嵌入到頁面
- 小圖檔優化生成base64碼 url-loader 減少請求數
- 使用Jquery等類庫
- 支援ES6文法 babel-loader 裝逼(使用新特性提供開發效率)
- 代碼壓縮 節約流量
我們分别來嘗試一下以上提到的功能
css預編譯
在我們的app目錄下建立一個檔案夾css /app/css/ 用來存放我們的CSS檔案,建立一個main.css檔案
main.css
h1{
color:blue;
}
安裝預處理器 style-loader,css-loader
npm install style-loader css-loader --save-dev //一行指令要安裝多個插件,可以用空格隔開
安裝完成後,配置webpack.config,注意以下幾點:
- 所有的預處理器的處理順序是從後往前(從右到左)
- 在webpack 2.0版本後,預處理器不能省略-loader,例如:'style-loader'不能簡寫為'style'
...
module:{
loaders:[{
test: /\.css$/,
loaders:['style-loader','css-loader'],
include:APP_PATH
}]
}
...
不要忘了在index.js引用我們的css檔案
然後執行npm start,你應該能看到藍色的hello world字樣了
使用Less
讓我們接着上一步,如果我要使用sass或者less,應該怎麼辦呢,答案也非常簡單,隻需安裝less預處理器,并在配置檔案裡配置對less的預處理即可
npm installless-loader --save-dev
webpack.config配置
module:[
...
{
test:/\.less$/,
loader:['style-loader','css-loader','less-loader'] //是不是非常簡單,隻需要在預處理程式上加一道即可
},
...
]
在/app/下建立目錄 /app/less,建立檔案main.less
@base: red;
h1 {
color: @base;
};
h2 {
color:blue;
}
在index.js中修改引用
然後 npm start,編譯成功!又看到了熟悉的hello world,隻不過這次第一行變成了紅色,第二行變成了藍色
小圖檔優化
首先我們在我們的目錄下建立一個檔案夾 /app/images/ 用來存放我們的圖檔,這裡我選用了2張貓咪的圖檔,一張有270kb,另一張則是21.6kb.我們的目标是當浏覽器加載40KB以下的圖檔時,把圖檔轉化成dataurl,這樣可以減少請求數,提高網頁加載的速度.先看下沒有預處理器下,圖檔的處理情況
我們的檔案目錄結構:
我們在less中為h1,h2增加背景圖檔
@base: red;
@height: 300px;
h1 {
color: @base;
background: url('../images/p1.jpg') no-repeat;
background-size:;
height: @height;
};
h2 {
color:blue;
background: url('../images/p2.jpeg') no-repeat;
background-size: contain;
height: @height;
}
我們在html中用标簽也添加一張圖檔,這裡應該通過JS來生成到html中,我們添加一個元件images.js
function printIMG(){
var div=document.createElement('div');
div.innerHTML='Here is a picture from js<br>'
div.innerHTML+='<img src="/app/images/p1.JPG">';
return div;
}
module.exports=printIMG;
在index.js中引用它,注意我們這裡還有個同名的images目錄,但webpack自動識别了require中的images.js
require('./less/main.less');
var sub=require('./sub');
var img=require('./images');
var app=document.createElement('div');
app.innerHTML='<h1>Hello World 1111111</h1>';
app.appendChild(sub());
app.appendChild(img());
document.body.appendChild(app);
運作webpack後,我們看到頁面上多了3張圖檔,檢視HTML和CSS後,發現現在所有圖檔都是直接引用的url
然而我們的需求是要把40KB以下的圖檔全部轉化成base64的形式,現在我們使用url-loader來做這件事,
首先,安裝插件 url-loader
npm install url-loader --save-dev
然後在webpack.config中配置
...
module: {
loaders: [
...
{
test: /\.(png|jpg|jpeg)$/, //可以定義多種格式
loader: 'url-loader?limit=40000?name=images/[name].[ext]' //在預處理器後可以通過?增加參數,這裡?limit=40000的意思是把所有40000b以下的圖檔轉化成base64格式
},
...
]
},
...
然後讓我們再運作下看看結果
這個時候我們發現background裡的圖檔已經變成base64格式了(見框1),但框2中的img标簽裡同樣的圖檔卻沒有轉化.
這是由于url-loader不支援打包JS裡的src導緻的,為了打包時候不丢失圖檔,我們可以通過require的方式引用圖檔,這樣就可以通過url-loader來打包了,把images.js改一下:
var imgUrl= require('./images/p1.jpg');//注意!字尾名必須是小寫,大寫會報錯
var imgTemp='<img src="'+imgUrl+'" />';
function printIMG(){
var div=document.createElement('div');
div.innerHTML='Here is a picture from j11111111111s<br>'
div.innerHTML+=imgTemp;
return div;
}
module.exports=printIMG;
使用Jquery等類庫
如果要在項目中使用各種類庫,步驟也是和安裝插件一樣的
首先
npm install jquery --save
然後在需要使用jquery的頁面上require一下
var $=require('jquery');
...
$('body').append('<p>Jquery is work now</p>');
這裡筆者産生了一個問題,為什麼require('jquery')就能直接找到node_modules下的jquery檔案呢
這是因為如果require中的内容如果不包含'/'、'./'或'../'開頭的話,預設會先尋找node_modules目錄下的相關檔案,這裡的require('jquery')等價于 require('./node_modules/jquery')
參考:阮大神的文章
當 Node 遇到 require(X) 時,按下面的順序處理。
(1)如果 X 是内置子產品(比如 require('http'))
a. 傳回該子產品。
b. 不再繼續執行。
(2)如果 X 以 "./" 或者 "/" 或者 "../" 開頭
a. 根據 X 所在的父子產品,确定 X 的絕對路徑。
b. 将 X 當成檔案,依次查找下面檔案,隻要其中有一個存在,就傳回該檔案,不再繼續執行。
c. 将 X 當成目錄,依次查找下面檔案,隻要其中有一個存在,就傳回該檔案,不再繼續執行。X
X.js
X.json
X.node
X/package.json(main字段)
X/index.js
X/index.json
X/index.node
(3)如果 X 不帶路徑
a. 根據 X 所在的父子產品,确定 X 可能的安裝目錄。
b. 依次在每個目錄中,将 X 當成檔案名或目錄名加載。
(4) 抛出 "not found"
運作後,可以看到,Jquery的代碼已經成功輸出了!
支援ES6文法 babel-loader
如果想要使用es6,我們需要安裝babel編譯器,他可以把ES6文法解析成浏覽器能相容的JavaScript,我們還要安裝适用于babel的ES2015文法轉換器(babel-preset-es2015)
npm install babel-loader babel-preset-es2015 --save-dev
在webpack.config中配置
...
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /(node_modules|bower_components)/,
query: {
presets: ['es2015'] //表示使用es2015文法轉換器
}
},
...
關于babel-loader的設定可以參考 這裡
然後我們修改下原來的js檔案,可以使用ES6文法了
sub.js
export default function(){
var h2=document.createElement('h2');
h2.innerHTML='Hello World h2';
return h2;
}
images.js
import imgUrl from './images/p1.jpg';
let imgTemp='<img src="'+imgUrl+'" />';
export default function(){
let div=document.createElement('div');
div.innerHTML='Here is a picture from j11111111111s<br>'
div.innerHTML+=imgTemp;
return div;
}
index.js
//ES6風格
import './less/main.less';
import sub from './sub';
import images from './images'
import $ from 'jquery';
import moment from 'moment';
let app = document.createElement('div');
const myPromise = new Promise(function(resolve,reject){
setTimeout(function(){
console.log('執行完成');
resolve('101');
},5000);
});
myPromise.then((value)=>{
$('body').append('<p>promise result is ' + value + ' now is' + moment().format('YYYY-MM-DD hh:mm:sss')+'</p>');
});
app.innerHTML='<h1>Hello world H1</h1>';
document.body.appendChild(app);
app.appendChild(sub());
app.appendChild(images());
運作一下,依然是我們熟悉的hello world,并且promise的結果也print出來了.
代碼壓縮
現在我們對webpack基本的功能都有一定的了解了,最後生成項目的時候,頁面裡的js和css都是未經過壓縮的,并且我們所引用到的類庫都被打包到了bundle.js裡,導緻這個JS檔案非常大,在實際生成項目中,我們通常隻上線自己所寫的JS代碼,所有的類庫都會緩存到CDN上來分流,這個需求在webpack中也是可以實作的.首先我們來拆分項目中的Jquery和Moment 這2個庫
修改webpack.config,添加externals屬性可以在webpack打包時排除在externals下的依賴包
例如:我們排除jquery和moment,這樣在webpack打包時就不會把這2個庫打包到bundle.js下了
...
externals:[
{'jquery':'jQuery','moment':'moment'},
],
...
我們可以在template模闆中對未打包的檔案進行設定,比如設定CDN,我這裡由于沒有CDN,就用node_modules代替了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
hello this is from template
<script src="../node_modules/jquery/dist/jquery.min.js" charset="utf-8"></script>
<script src="../node_modules/moment/min/moment.min.js" charset="utf-8"></script>
</body>
</html>
另一種壓縮是對js本身進行的壓縮 可以通過webpack自帶的插件進行
...
plugins:[
new webpack.optimize.UglifyJsPlugin({//壓縮JS
compress:{
warnings:false
}
}),
]
...
運作weback 打包後的檔案更小了