天天看點

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

今天我要進一步深入的了解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檔案

然後執行npm start,你應該能看到藍色的hello world字樣了

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

使用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,隻不過這次第一行變成了紅色,第二行變成了藍色

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

小圖檔優化

首先我們在我們的目錄下建立一個檔案夾 /app/images/ 用來存放我們的圖檔,這裡我選用了2張貓咪的圖檔,一張有270kb,另一張則是21.6kb.我們的目标是當浏覽器加載40KB以下的圖檔時,把圖檔轉化成dataurl,這樣可以減少請求數,提高網頁加載的速度.先看下沒有預處理器下,圖檔的處理情況

我們的檔案目錄結構:

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

我們在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

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

然而我們的需求是要把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格式
    },
    ...
  ]
},
...
           

然後讓我們再運作下看看結果

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

這個時候我們發現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的代碼已經成功輸出了!

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

支援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'},
],
...
           
Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化

我們可以在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 打包後的檔案更小了

Webpack入坑筆記(二) 小圖檔優化,引入CSS,使用ES6css預編譯使用Less小圖檔優化