webpack

  • 全局安装webpack 和 webpack-cli
npm install -g webpack webpack-cli
  • 创建并进入项目文件
mkdir webpack-demo && cd $_
  • 创建package.json
npm init

我这里 webpack 版本为 4.20.2webpack-cli 版本为 3.1.1

一、命令行打包

假设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, 导出一个函数:

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

根目录下 index.js, 使用require加载了hello.js,并执行了hello()函数

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

执行命令 webpack index.js --mode=development, 打包结果如下:

打包结果.png

(4)一些参数

--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, 然后再将处理结果交给style-loader处理。css-loader 的作用是使得webpack可以处理css文件,而style-loader是将处理过后的css通过新建style标签的方式插入html中

//  index.js
require("style-loader!css-loader!./style.css")  // 注意两个loader的顺序

执行打包命令 webpack index.js --mode=development 即可成功打包css文件。这种方式的缺点是require 每个css文件都要加上loader, 不免麻烦。

方式二:

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

执行webpack index.js --mode=development --module-bind "css=style-loader!css-loader"
这种方式是通过--module-bind指定打包css文件时需要的 loader,打包结果和方式一一样。

二、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参数指定配置文件:

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 即可完成对应配置的打包。

(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]
  }
}

打包结果如下:


打包结果.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, 若设为false,则生成的html中不会自动插入script标签
      minify:{
        removeComments:true,     // 删除 html 中的注释
        collapseWhitespace:true,   // 删除 html 中空格
      }
    })
  ]
}

以上配置的打包结果是,以 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包链接到全局执行环境) 。

(2)生成多页面

可以在 plugins 中 new 若干个htmlWebpackPlugin实例以生成多页面,htmlWebpackPlugin还支持ejs语法自定义一些内容插入到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为项目中的所有资源指定一个基础路径
  },
  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>

以上配置的打包结果是,生成了a.html 、 b.html 和 c.html 文件,a.html的<body></body>标签中外链了index-[hash].js 和 c-[hash].js, 如下图所示:

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

inline方式引入脚本可以减少http请求,以下演示如何实现部分脚本inline方式引入,部分脚本外链方式引入。
在“生成多页面”的例子上做修改,我们现在的目的是,让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,      // 设为 false,表示不自动插入脚本
      chunks:["index","c"],      
    }) ,
    new htmlWebpackPlugin({
      filename:"b.html",
      template:"index.html" ,
      title:"this is b.html",   
      inject:false,      // 设为 false
      chunks:["index"],         // 因为要演示inline方式引入index-[hash].js脚本,所以这里chunks中必须包含index
    }),
    new htmlWebpackPlugin({
      filename:"c.html",
      template:"index.html" ,
      title:"this is c.html",   
      inject:false,      // 设为 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 拿到的是对应页面的publicPath,chunks等信息,以下是a.html页面的htmlWebpackPlugin.files数据,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循环htmlWebpackPlugin.files.chunks,排除chuankName为index的chunk就可以了。

下图为最终生成 的a.html(inline的script内容过长,已折叠):

a.html外链方式和inline方式同时存在.png

四、 loader的配置和使用

webpack本身只能打包Javascript文件,对于其他资源例如 css,图片,或者其他的语法集比如jsx,是没有办法加载的。 这就需要对应的loader将资源转化后再加载进来。

(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内容(其中又引入了common.css):

// src/style/base.css

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

common.css内容:

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

由于css中包含了一些可能导致浏览器样式差异的属性,除了安装依赖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"]})
            ]
          }
        }],
      },
    ]
  }
}

打包完成后审查元素,发现base.css中的flex已自动加上前缀,但是通过@import方式引入的common.css中的flex并未加上前缀,如下图所示:

审查元素结果.png

解决方法,给css-loader添加额外配置 importLoaders

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [  
      {
        test: /\.css$/,
        use:["style-loader",
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,   // 表示在css-loader处理之前,指定相应数量的loader来先处理import进来的资源,即需要先经过postcss-loader的处理
            }
          },{
          loader: 'postcss-loader',
          options: {
            plugins: [
              require("autoprefixer")({browsers: ["last 2 versions"]})
            ]
          }
        }], 
      },
    ],
  }
}

若整个项目中使用的都是预编译sass或less,则这里css-loader 不需要配置importLoaders=1,因为less和sass都会自动补全前缀,接下来会讲到。

(2)less 和sass loader

安装less-loader,如果没有less, 还需要安装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中相应配置即可:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [  
      {
        test: /\.less$/,
        // 由于less会自动补全前缀,故而这里的 css-loader 不需要配置importLoaders参数
        use:["style-loader","css-loader",{
            loader: 'postcss-loader',
            options: {
              plugins: [
                require("autoprefixer")({browsers: ["last 2 versions"]})
              ]
            }
          },"less-loader"],  // 若用的是sass, 这里对应修改为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"],
      },
    ],
  }
}

打包结果如下图所示,可见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"],
      },
    ],
  }
}

打包结果如下图所示:


ejs-loader打包结果.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容