1. 程式人生 > >Webpack入坑筆記(二) 小圖片優化,引入CSS,使用ES6

Webpack入坑筆記(二) 小圖片優化,引入CSS,使用ES6

今天我要進一步深入的瞭解webpack的各種功能,webpack有著無數外掛可以為專案提供各種支援,其中一部分對專案檔案進行預處理的外掛叫做loader(前處理器),比較常用的有:

  1. css預編譯,less或sass預編譯 css-loader,style-loader,less-loader 把css預編譯並通過style標籤嵌入到頁面
  2. 小圖片優化生成base64碼 url-loader 減少請求數
  3. 使用Jquery等類庫
  4. 支援ES6語法 babel-loader裝逼(使用新特性提供開發效率)
  5. 程式碼壓縮 節約流量

我們分別來嘗試一下以上提到的功能

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,注意以下幾點:

  1. 所有的前處理器的處理順序是從後往前(從右到左)
  2. 在webpack 2.0版本後,前處理器不能省略-loader,例如:'style-loader'不能簡寫為'style'
...
module:{
loaders:[{
test: /\.css$/,
loaders:['style-loader',
'css-loader'], include:APP_PATH }] } ...

不要忘了在index.js引用我們的css檔案

require('./css/main.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中修改引用

require('./less/main.less');

然後 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 當成檔案,依次查詢下面檔案,只要其中有一個存在,就返回該檔案,不再繼續執行。

X
X.js
X.json
X.node

c. 將 X 當成目錄,依次查詢下面檔案,只要其中有一個存在,就返回該檔案,不再繼續執行。

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 打包後的檔案更小了