webpack

  • 全局安裝webpack 和 webpack-cli
npm install -g webpack webpack-cli
  • 創(chuàng)建并進入項目文件
mkdir webpack-demo && cd $_
  • 創(chuàng)建package.json
npm init

我這里 webpack 版本為 4.20.2 ,webpack-cli 版本為 3.1.1

一、命令行打包

假設(shè)hello.js是我們需要打包的文件:

(1)不配置打包后文件的名稱和路徑,則默認在根目錄下生成dist/main.js,

--mode的可選值為developmentproduction,默認值為development。

webpack  hello.js --mode=development
(2)配置打包后文件的名稱和路徑
webpack hello.js -o ./dist/hello.min.js --mode=production
(3)wepack支持AMD,CommonJS,ES6

以下為CommonJS模塊示例:
根目錄下 hello.js, 導(dǎo)出一個函數(shù):

//  hello.js:
module.exports=function () {
  alert("hello")
}

根目錄下 index.js, 使用require加載了hello.js,并執(zhí)行了hello()函數(shù)

//  index.js
var hello=require("./hello")
hello()

執(zhí)行命令 webpack index.js --mode=development, 打包結(jié)果如下:

打包結(jié)果.png

(4)一些參數(shù)

--watch : 當文件變動時,自動打包
--progress: 看到打包的百分比進程
--colors : 有顏色
--display-modules: 列出引用的所有模塊
--display-reasons: 顯示打包原因(哪里引用了被打包的文件)

(5)如何打包css文件

若直接在 index.js中 require ("./style.css"),然后打包,會報錯 “You may need an appropriate loader to handle this file type.”, 提示我們需要有合適的loader來處理這種類型的文件。

所以,我們安裝兩個loader ——css-loaderstyle-loader :

// 安裝css-loader 和 style-loader
 npm install css-loader style-loader --save-dev

方式一:

在 index.js中 添加require("style-loader!css-loader!./style.css"),意思是說,先通過css-loader處理style.css, 然后再將處理結(jié)果交給style-loader處理。css-loader 的作用是使得webpack可以處理css文件,而style-loader是將處理過后的css通過新建style標簽的方式插入html中

//  index.js
require("style-loader!css-loader!./style.css")  // 注意兩個loader的順序

執(zhí)行打包命令 webpack index.js --mode=development 即可成功打包css文件。這種方式的缺點是require 每個css文件都要加上loader, 不免麻煩。

方式二:

//  index.js
require ("./style.css")

執(zhí)行webpack index.js --mode=development --module-bind "css=style-loader!css-loader"
這種方式是通過--module-bind指定打包css文件時需要的 loader,打包結(jié)果和方式一一樣。

二、webpack 配置文件

(1)簡單示例

一個最簡單的webpack 配置:

// webpack.config.js

const path = require('path');
module.exports={
 // mode:"development",     // 模式,放在package.json中配置更好
  entry: './index.js',      // 打包入口文件
  output:{
    path:path.resolve(__dirname, 'dist'),     // 打包輸出路徑
    filename:"dist.min.js"                    // 打包輸出文件名稱
  }
}

直接運行命令 webpack 即可打包, 這是因為 webpack默認會去查找名稱為webpack.config.js的配置文件。若我們的配置文件不叫webpack.config.js,比如叫 webpack.config.dev.js, 則需通過--config參數(shù)指定配置文件:

webpack --config webpack.dev.config.js

更簡單的方式是在 package.json 中配置我們的打包命令:

// package.json

"scripts":{
  "webpack-dev":"webpack --config webpack.config.js --mode=development --progress --colors --display-modules  --display-reasons",
  "webpack-prod":"webpack --config webpack.config.js --mode=production --progress --colors --display-modules  --display-reasons"
}

然后運行npm run webpack-devnpm run webpack-prod 即可完成對應(yīng)配置的打包。

(2)entry 的 3 種配置方式
  1. entry:"./index.js" , 簡單示例中的方式
  2. entry:["./entry1.js","./entry2.js"] , 適用于兩個互不依賴的文件想打包到一起
  3. 多chunk方式(會打包生成多個文件)
    以下為多chunk 配置示例:
// 文件目錄
webpack-demo
|- index.js
|- webpack.config.js
|- package.json
|- js
  |- a.js
  |- b.js
  |- c.js

//webpack.config.js 
const path = require('path');
module.exports={
  entry: {
    index:"./index.js",
    ab:["./js/a.js","./js/b.js"],
    c:"./js/c.js",
  },
  output:{
    path:path.resolve(__dirname, 'dist'),
    filename:"[name]-[hash].js",   // [name], [hash]為占位符, 還有[chunk-hash]
  }
}

打包結(jié)果如下:


打包結(jié)果.png

三、自動生成html頁面(html-webpack-plugin插件的使用)

若使用 [hash] 或 [chunk-hash] 占位符的方式生成文件名稱,每次生成的文件名稱都是不一樣的,那么在html中 如何通過外鏈的方式(<script src=" "></script>)引入打包后的文件呢?或者我們有時候需要在不同的頁面引入不同的 js文件,又該如何做呢?
我們可以使用 html-webpack-plugin 插件來做到這一切。

npm install html-webpack-plugin --save-dev
(1)按模板自動生成html頁面
// webpack.config.js
const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")

module.exports={
  entry: {
    index:"./index.js",
    ab:["./js/a.js","./js/b.js"],
    c:"./js/c.js",
  },
  output:{
    path:path.resolve(__dirname, 'dist'),
    filename:"[name]-[hash].js",   
  },
  plugins:[
    new htmlWebpackPlugin({
      filename:"index-[hash].html",    // 生成的html文件名稱
      template:"index.html",     // 以當前目錄下的的 index.html 為模板來生成html
      inject:"head",   // script標簽插入到 html中的位置,head/body/false, 默認為插入body, 若設(shè)為false,則生成的html中不會自動插入script標簽
      minify:{
        removeComments:true,     // 刪除 html 中的注釋
        collapseWhitespace:true,   // 刪除 html 中空格
      }
    })
  ]
}

以上配置的打包結(jié)果是,以 index.html為模板生成一個新的 index-[hash].html,新的html 的 <header></header> 標簽中以外鏈的方式引入了[index]-[hash].js, [ab]-[hash].js, [c]-[hash].js 三個打包后的js文件。

注:若 require("html-webpack-plugin") 時報錯 Cannot find module 'webpack/lib/node/NodeTemplatePlugin,嘗試本地安裝webpack 或者 運行npm link webpack --save-dev (該命令的作用是將一個任意位置的npm包鏈接到全局執(zhí)行環(huán)境) 。

(2)生成多頁面

可以在 plugins 中 new 若干個htmlWebpackPlugin實例以生成多頁面,htmlWebpackPlugin還支持ejs語法自定義一些內(nèi)容插入到html模板中。

webpack.config.js配置:

// webpack.config.js

const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
  entry: {
    index:"./index.js",
    ab:["./js/a.js","./js/b.js"],
    c:"./js/c.js",
  },
  output:{
    filename:"[name]-[hash].js", 
    path:path.resolve(__dirname, 'dist'),
    publicPath:"http://cdn",     // publicPath為項目中的所有資源指定一個基礎(chǔ)路徑
  },
  plugins:[
    new htmlWebpackPlugin({
      filename:"a.html",
      template:"index.html" ,
      title:"this is a.html",      // title 將替換到模板中
      chunks:["index","c"],        // a.html 引入打包后的index.js 和 c.js
    }) ,
    new htmlWebpackPlugin({
      filename:"b.html",
      template:"index.html" ,
      title:"this is b.html",    // title 將替換到模板中
      chunks:["ab"],             // b.html 引入打包后的 ab.js
    }),
    new htmlWebpackPlugin({
      filename:"c.html",
      template:"index.html" ,
      title:"this is c.html",    // title 將替換到模板中
      excludeChunks:["c"],       //  c.html 頁面引入除c.js之外的其它js
    })
  ]
}

模板index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- ejs 語法,渲染title -->
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>

以上配置的打包結(jié)果是,生成了a.html 、 b.html 和 c.html 文件,a.html的<body></body>標簽中外鏈了index-[hash].js 和 c-[hash].js, 如下圖所示:

a.html.png
(3)inline 的方式引入js

inline方式引入腳本可以減少http請求,以下演示如何實現(xiàn)部分腳本inline方式引入,部分腳本外鏈方式引入。
在“生成多頁面”的例子上做修改,我們現(xiàn)在的目的是,讓a.html, b.html,c.html 都以inline的方式引入index.js 腳本,其它腳本各頁面以外鏈的方式按需引入。

webpack.config.js配置:

// webpack.config.js

const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
  ...
  plugins:[
    new htmlWebpackPlugin({
      filename:"a.html",
      template:"index.html" ,
      title:"this is a.html",     
      inject:false,      // 設(shè)為 false,表示不自動插入腳本
      chunks:["index","c"],      
    }) ,
    new htmlWebpackPlugin({
      filename:"b.html",
      template:"index.html" ,
      title:"this is b.html",   
      inject:false,      // 設(shè)為 false
      chunks:["index"],         // 因為要演示inline方式引入index-[hash].js腳本,所以這里chunks中必須包含index
    }),
    new htmlWebpackPlugin({
      filename:"c.html",
      template:"index.html" ,
      title:"this is c.html",   
      inject:false,      // 設(shè)為 false
      excludeChunks:["c"],       
    })
  ]
}

模板index.html:

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title><%= htmlWebpackPlugin.options.title %></title>
  <script type="text/javascript">
    <%= compilation.assets[htmlWebpackPlugin.files.chunks.index.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %>
  </script>
</head>
<body>
    <% for (var k in htmlWebpackPlugin.files.chunks){%>
      <% if(k !=="index") { %>
        <script type="text/javascript" src="
          <%= htmlWebpackPlugin.files.chunks[k].entry %>
        "></script>
      <% }%>
    <% }%>
</body>
</html>

解釋一下上圖模板index.html的含義:
首先,compilation.assets拿到的是所有的chunks,如下圖所示:

compilation.assets.png

其次,htmlWebpackPlugin.files 拿到的是對應(yīng)頁面的publicPath,chunks等信息,以下是a.html頁面的htmlWebpackPlugin.files數(shù)據(jù),a.html 使用了index 和 c 兩個chunks:

// a.html頁面 htmlWebpackPlugin.files 取到的值
{
  "publicPath":"http://cdn/",
  "chunks":{
    "index":{
      "size":154,
      "entry":"http://cdn/index-6ab81e3153cfd7fb16c1.js",
      "hash":"630b3fd517102652c275",
      "css":[]
    },
    "c":{
      "size":35,
      "entry":"http://cdn/c-6ab81e3153cfd7fb16c1.js",
      "hash":"a172ef412ae0af3b89b9",
      "css":[]
    }
  },
  "js":["http://cdn/index-6ab81e3153cfd7fb16c1.js", "http://cdn/c-6ab81e3153cfd7fb16c1.js"],
  "css":[]
}

最后,通過.source()拿到chuankName 為 index的 js代碼,直接插入 <script>標簽中。而外鏈方式的腳本直接通過for循環(huán)htmlWebpackPlugin.files.chunks,排除chuankName為index的chunk就可以了。

下圖為最終生成 的a.html(inline的script內(nèi)容過長,已折疊):

a.html外鏈方式和inline方式同時存在.png

四、 loader的配置和使用

webpack本身只能打包Javascript文件,對于其他資源例如 css,圖片,或者其他的語法集比如jsx,是沒有辦法加載的。 這就需要對應(yīng)的loader將資源轉(zhuǎn)化后再加載進來。

(1)css loader
// 文件目錄
webpack-demo
|- app.js
|- webpack.config.js
|- package.json
|- src
  |- style
    |- base.css
    |- common.css

app.js中引入base.css:

// src/app.js
import "./style/base.css"

base.css內(nèi)容(其中又引入了common.css):

// src/style/base.css

@import "./common.css";
html{
  background: yellow;
}
.box1{
  display: flex;
}

common.css內(nèi)容:

// src/style/common.css
.box2{
  display: flex;
}

由于css中包含了一些可能導(dǎo)致瀏覽器樣式差異的屬性,除了安裝依賴css-loaderstyle-loader外,通常還需要安裝postcss-loaderautoprefixer,用于自動補充css前綴。

// 安裝 postcss-loader 和 autoprefixer
npm install postcss-loader autoprefixer --save-dev

webpack.config.js配置如下:

const path = require('path');
var htmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  entry: "./src/app.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  plugins: [
    new htmlWebpackPlugin({
      filename: "index.html",
      inject: "body",
    })
  ],
  module: {
    rules: [  
      {
        test: /\.css$/,
        use:["style-loader","css-loader",{
          loader: 'postcss-loader',
          options: {
            plugins: [
              require("autoprefixer")({browsers: ["last 2 versions"]})
            ]
          }
        }],
      },
    ]
  }
}

打包完成后審查元素,發(fā)現(xiàn)base.css中的flex已自動加上前綴,但是通過@import方式引入的common.css中的flex并未加上前綴,如下圖所示:

審查元素結(jié)果.png

解決方法,給css-loader添加額外配置 importLoaders

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [  
      {
        test: /\.css$/,
        use:["style-loader",
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,   // 表示在css-loader處理之前,指定相應(yīng)數(shù)量的loader來先處理import進來的資源,即需要先經(jīng)過postcss-loader的處理
            }
          },{
          loader: 'postcss-loader',
          options: {
            plugins: [
              require("autoprefixer")({browsers: ["last 2 versions"]})
            ]
          }
        }], 
      },
    ],
  }
}

若整個項目中使用的都是預(yù)編譯sass或less,則這里css-loader 不需要配置importLoaders=1,因為less和sass都會自動補全前綴,接下來會講到。

(2)less 和sass loader

安裝less-loader,如果沒有l(wèi)ess, 還需要安裝less:

// 安裝less-loader
npm install less-loader --save-dev

// 如果用的是sass,則安裝sass-loader
// npm install sass-loader --save-dev

app.js中引入less文件:

// app.js

import "./style/cover.less"   // 或者使用 require ("./style/cover.less")

webpack.config.js中相應(yīng)配置即可:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [  
      {
        test: /\.less$/,
        // 由于less會自動補全前綴,故而這里的 css-loader 不需要配置importLoaders參數(shù)
        use:["style-loader","css-loader",{
            loader: 'postcss-loader',
            options: {
              plugins: [
                require("autoprefixer")({browsers: ["last 2 versions"]})
              ]
            }
          },"less-loader"],  // 若用的是sass, 這里對應(yīng)修改為sass-loader
      },
    ],
  }
}
(3)處理模板文件

這里只講html和ejs模板文件的處理,更為詳細的可移步這里。

1. html-loader

// 安裝 html-loader
npm install html-loader --save-dev

模板文件 test.html:

<!-- test.html-->
<div>
  This is test.html
</div>

app.js:

// app.js
import layer from "./xxx/test.html"
document.getElementById("app")
app.innerHTML=layer
// console.log(layer)

webpack.config.js:

// webpack.config.js
module.exports = {  
  ...
  module: {
    rules: [
      {
        test: /\.html$/,
        use:["html-loader"],
      },
    ],
  }
}

打包結(jié)果如下圖所示,可見html-loader將html資源處理成了字符串。


html-loader將html資源處理成字符串.png

2. ejs-loader

模板文件 test2.ejs:

// 模板文件 test2.ejs
<div class="test2">
  <div>this is <%= name %></div>
  <% for(var i=0;i<arr.length;i++){ %>
    <%= arr[i] %>
   <% } %>
</div>

layer.js:

// layer.js
import tpl2 from "../xxx/test2.ejs"
export default function () {
  return {
    name:"Test",
    tpl:tpl2
  }
}

app.js:

// app.js
import Layer from "./js/layer"

var app=document.getElementById("app")
var layer=new Layer()
app.innerHTML=layer.tpl(
  {
    name:"ejs",
    arr:["apple","banana","pear"]
  }
)
// console.log(layer)

webpack.config.js

// webpack.config.js
module.exports = {  
  ...
  module: {
    rules: [
      {
        test: /\.ejs$/,
        use:["ejs-loader"],
      },
    ],
  }
}

打包結(jié)果如下圖所示:


ejs-loader打包結(jié)果.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容