一行一行手敲webpack4配置

9次阅读

共计 13376 个字符,预计需要花费 34 分钟才能阅读完成。

一、webpack4– 基本配置

这一部分通过 webpack 的基本配置,使用 loader 对图片和样式进行打包,从而了解 webpack4 简单的用方法,保证自己能够配置正确,提升学习动力。

1. 初始化配置

mkdir webpack4
cd webpack4
mkdir demo1
cd demo1
npm init -y 或 npm init

目录结构

webpack4
├── webpack4/demo1
│   └── webpack4/demo1/package.json
└── webpack4/README.md
  • 安装 webpack

npm install webpack --save-dev

  • 安装指定版本 webapck

npm install --save-dev webpack@<version>

  • webpack 4+ 版本,还需要安装 webpack-cli

npm install webpack-cli --save-dev

  • npx webpack -v:查看 webpack 版本
  • npx webpack-cli -v:查看 webpack-cli 版本

推荐本地安装 webpack 和 webpack-cli
写这篇博客的时候 webpack 最新版本为:4.30.0,也是这篇学习 webpack4 使用的版本

在 demo1 目录下新建 src 目录,在 src 目录下新建 index.js

mkdir src 
cd src
touch index.js

demo1 目录结构

demo1
├── demo1/package.json
├── demo1/package-lock.json
└── demo1/src
    └── demo1/src/index.js

在 index.js 中加写代码,例如:

//index.js

let demo='webpack4'
console.log(demo)

webpack4 可以零配置打包,webpack4 会默认对 src 目录下的 index.js 文件打包。
现在运行npx webapck,可以在 demo1 目录下看到 dist 目录,dist 目录下有一个 main.js 文件,这就是打包后的文件,打开查找可以看到 console.log(demo),说明 index.js 被打包到 main.js 中。

2.webpack4 的简单配置

demo1 目录下新建 webpack 配置文件webpack.config.js

配置webpack–webpack.config.js
const path = require('path')

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {index: './src/index.js'},
  // 打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    // 如果不设置 filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  }
}

运行 npx webpack 命令
npx webpack 等价于npx webpack --config webpack.config.js

webapck 配置文件命名为 webpack.config.js 时可以省略 --config *.js,直接执行npx webpack 即可,否则执行npx webpack --config 配置文件名

看到 dist 目录下有bundle.js,说明 webpack 配置正确。

package.json 中配置 ’script’

"scripts": {"build": "webpack"}

添加 "build": "webpack",运行npm run build 效果等价于执行 npx webpack 命令。

配置 webpack.config.js 的 modoule 对象

loader 的用法
file-loader的使用

安装file-loader
npm i file-loader --save-dev

webpack.config.js

const path = require('path')

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {index: './src/index.js'},
  // 打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    // 如果不设置 filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: {
    rules:[
      {test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'file-loader',
          options: {name: '[name].[ext]', // 对打包后的图片命名
            outputPath: 'images/' // 打包后图片输出的位置 dist\images
          }
        }
      }
    ]
  }
}

在 src 目录下新建 images 文件夹,存放图片

修改 index.js

//index.js

//import 导入图片
import image from './images/11.png'

let img=new Image()
img.src=image
document.body.append(img)

运行 npm run build 后的目录结构如下

demo1
├── demo1/dist
│   ├── demo1/dist/bundle.js
│   ├── demo1/dist/images
│   │   └── demo1/dist/images/11.png
│   └── demo1/dist/index.html
├── demo1/package.json
├── demo1/package-lock.json
├── demo1/src
│   ├── demo1/src/images
│   │   └── demo1/src/images/11.png
│   └── demo1/src/index.js
└── demo1/webpack.config.js

在 dist 目录下出现了 images 目录和图片,创建 index.html,引入 js 文件,在浏览器中打开就可以看到图片。

url-loader的使用

url-loader安装
npm i url-loader -D

url-loader的作用跟 ’file-loader’ 的作用很类似

webpack.config.js

  module: {
    rules:[
     {test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {name: '[name].[ext]', // 对打包后的图片命名
            outputPath: 'images/', // 打包后图片放的位置 dist\images
            limit: 20480
            //1024 == 1kb  
            // 小于 20kb 时打包成 base64 编码的图片否则单独打包成图片
          }
        }
      }
    ]
  }
}

limit属性:当图片大小大于属性值时打包成图片输出到 images 目录下,否则打包成 base64 编码的图片注入 bundle.js 中

因为 base64 编码的图片导致打包文件变大,所以图片比较小时打包成 base64 编码的图片,图片比较大时单独打包成一张图片。

cssscss的打包

安装相应的 loader
npm i css-loader style-loader -D
npm i node-sass sass-loader -D
npm i postcss-loader -D
npm i autoprefixer -D

postcss-loaderautoprefixer 配合使用可以在打包过程中自动添加前缀

在 demo1 根目录下新建postcss.config.js,配置如下

//postcss.config.js
module.exports={
  plugins: [require('autoprefixer')
  ]
}

在 webpack.config.js 文件的 `module.rules’ 数组中添加配置

module:{
  rules:[
    {
      test: /\.css$/,
       use:[
         'style-loader',
         'css-loader',
         'postcss-loader'  
         // 加前缀  npm i autoprefixer -D
         // 在项目根目录下配置 postcss.config.js 文件
       ]
     },
     {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              // 用于配置 css-loader 作用于 @import 的资源之前有多少个 loader 先作用于 @import 的资源
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}

demo1src下新建 css 文件夹,在 css 文件夹下新建 style.cssindex.scss文件。

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
}

style.css

body{border-radius: 10%;}

index.js

//index.js

import image from './images/11.png'
import './style.css'
import './index.scss'

let img=new Image()
img.src=image
document.body.append(img)

运行npm run build,在 dist 目录下新建 index.html,引入 js 文件,在浏览器中打开就可以看到效果,说明打包成功。

css 模块化

css 模块化,避免页面样式之间相互影响
webpack.config.js中的 css-loader 添加modules: true

//webpack.config.js

module:{
  rules: [
      {
        test: /\.css$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {modules: true}
          },
          'postcss-loader'  
          // 加前缀  npm i autoprefixer -D
          // 在项目根目录下配置 postcss.config.js 文件
        ]
      },
      {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              // 用于配置 css-loader 作用于 @import 的资源之前有多少个 loader 先作用于 @import 的资源
              modules: true // 加载 css 模块化打包,避免样式文件之间相互影响
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}

修改 index.js
.img是类名需要在样式文件中提前写好样式

//index.js

import image from './images/11.png'
import style from './css/style.css'
// import style from './css/index.scss'

let img=new Image()
img.src=image

//style.img .img 是 scss 文件中写好的类名
img.classList.add(style.img)

document.body.append(img)

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
  .img{border: 10px solid royalblue;}
}

style.css

body{border-radius: 10%;}
body .img{border: 10px solid yellow;}

结果

可以看到添加了一个 class,类名是一串比较复杂的字符串,从而避免这个样式对别的元素产生影响。


二、进一步配置 webpack4,使自己在学习 webpack4 的时候更方便

这一部分主要是学会使用 html-webpack-pluginclean-webpack-plugin插件,主要是学会配置 devServer 以及使用 webpack 的热模块替换功能。

首先,在 webpack4 目录下新建 demo2 文件夹将 demo1 目录下的所有东西复制到 demo2

在上一部分我们都是手动在 dist 目录下创建 index.html 引入 js 文件查看打包结果,这样会很麻烦。我们可以使用 html-webpack-plugin 来自动生产 index.html,并且能够自动引入打包好的文件,直接打开生产的 html 就可以看到打包结构。

1.html-webpack-plugin的使用

安装
npm i html-webpack-plugin -D

webpack.config.js 中配置 plugins 配置项

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

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {index: './src/index.js'},
  // 打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    // 如果不设置 filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({template: './index.html'})
  ]
}

在 demo2 目录下新建 index.html 作为模板

index.html

<!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> 模板 </title>
</head>
<body>
  <div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>

运行npm run build,可以看到 dist 目录下自动生产 index.html,并且还自动引入 js 文件

2.clean-webpack-plugin的使用

每次打包生成的 dist 目录,如果改一次代码,都得要删除一次 dist 目录,这样很麻烦,可以通过 clean-webpack-plugin 在每次打包的前自动清空 dist 目录。

安装
npm i clean-webpack-plugin -D

webpack.config.jsplugins中配置如下

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {index: './src/index.js'},
  // 打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    // 如果不设置 filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({template: './index.html'}),
   new cleanWebpackPlugin()]
}

运行npm run build,可以自己测试,每次打包前都会把 dist 目录下的文件删掉。

3.entryoutput 多入口配置

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js',
    main: './src/index.js'
  },
  // 打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    // 如果不设置 filename,则文件的名字跟入口文件路径的属性名一样
    // 占位符
    filename: '[name].js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
}

当有多入口的时候,需要修改 filename 的属性值为 '[name].js’

运行 npm run build,就会在 dist 目录下生产index.jsmain.js

4.配置devtool

devtool 决定源代码与打包后的代码之间的映射关系,方便对代码进行调试。

开发环境推荐: cheap-module-eval-source-map
生产环境推荐: cheap-module-source-map

devtool 具体内容请查阅:文档:devtool

module.exports={
  devtool: 'cheap-module-eval-source-map',
  // 开发环境推荐: cheap-module-eval-source-map
  // 生产环境推荐: cheap-module-source-map
}

5.配置devServer

文档:devServer

安装webpack-dev-server
npm i webpack-dev-server -D

在 webpack.config.js 中添加以下内容
module.exports={
  devServer: {
    contentBase: './dist',
    // open: true, // 自动打开浏览器
    // port: 8080, // 默认 8080
  }
}

修改 package.jsonscript,添加 "start": "webpack-dev-server"

 "scripts": {"start": "webpack-dev-server"},

执行 npm run start 后打开浏览器就可以看到效果,当我们修改代码的时候页面就会重新刷新。

有时我们希望页面只更新我们修改的那一部分就可以了,而并不是刷新页面,所以需要启用 webpack 的热模块替换功能。

6.启用 webpack 的热模块替换功能

首先修改 index.js
import './css/style.css'

var btn = document.createElement('button')
btn.innerHTML='新增'
document.body.appendChild(btn)

btn.onclick=function(){var div=document.createElement('div')
  div.innerHTML='items'
  document.body.appendChild(div)
}
修改 style.css,删掉 index.scss
//style.css
body{background: yellow;}
div:nth-of-type(odd){
  background: chartreuse;
  font-size: 18px;
}
在 webpack.config.js 中

引入 webpack:const webpack=require('webpack')
添加内容如下:

const webpack=require('webpack')
module.exports={
  plugins: [new webpack.HotModuleReplacementPlugin() // 启用 HMR
  ],
  devServer: {
    contentBase: './dist',
    // open: true, // 自动打开浏览器
    // port: 8080,
    hot: true, // 启用 webpack 的热模块替换功能
    hotOnly: true 
    //devServer.hot 在没有页面刷新的情况下启用热模块替换作为构建失败时的后备
  }
}

hot:true启用HotModuleReplacementPlugin(HMR)

执行npm run start,在浏览器打开以后,修改 div 的背景颜色,只有改变的地方才发生变化,但是页面并没有刷新。

在 demo2 的 src 目录下新建 number.js

number.js
var number=function(){var div=document.createElement('div')
  div.setAttribute("id","number")
  div.innerHTML=103
  document.body.appendChild(div)
}

export default number

修改 index.js

import number from './number'
number()

运行npm run start,在浏览器中打开看结果,然后在 number.js 中修改内容,但是页面并没有显示修改后的内容

这是因为在引入 js 文件的时候,热模块替换的实现方式有点区别。

js 要达到热模块替换的效果,得要 if(module.hot){}这一部分代码,否则就算改了代码,页面不刷新,修改的地方在页面上页面变化。

css 样式因为 css-loader 已经实现 if(module.hot){}这一部分,所以不需要单独实现这一部分。

再次修改 index.js

import number from './number'
number()

if(module.hot){module.hot.accept('./number.js',function(){number()
    document.body.removeChild(document.getElementById('number'))
  })
}

运行npm run start,在浏览器中打开看结果,然后在 number.js 中修改内容,发现页面显示修改后的内容


三、使用 Babel 处理 js 文件

Babel 是一个广泛使用的转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。

Babel 总共分为三个阶段:解析(parse),转换(transform),生成(generate)。

Babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 Babel 输出的代码和输入是相同的。

Babel 插件的使用

  1. 将插件的名字增加到配置文件中: 项目根目录下创建 .babelrc 配置文件或是 webapck.config.js 中配置,一般都是在 .babelrc 中配置。
  2. 使用 npm install xxx 进行安装

Babel 的配置文件是.babelrc,存放在项目的根目录下。使用 Babel 的第一步,就是配置这个文件。

该文件用来设置转码规则和插件,基本格式如下。

{"presets": [],
  "plugins": []}

Babel 简单介绍

preset

preset(预设)就是一系列插件的集合
@babel/preset-env 包含所有 ES6 转译为 ES5 的插件集合

core-js

转换一些内置类 (Promise, Symbols 等等) 和静态方法(Array.from 等)。

@babel/core

是作为 Babel 的核心存在,Babel 的核心 api 都在这个模块里面。

babel-loader

babel-loader在 webpack 中使用,是 webpack 和 Babel 之间的通讯桥梁

@babel/polyfill 介绍

@babel/preset-env默认只转译 js 语法,而不转译新的 API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法 (比如Object.assign) 都不会转译。这时就必须使用 @babel/polyfill(内部集成了core-jsregenerator)。

使用时,在所有代码运行之前增加import "@babel/polyfill"

或者是在 webpack.config.js 入口配置

module.exports = {entry: ["@babel/polyfill", "./app/js"],
}

因此必须把 @babel/polyfill 作为 dependencies 而不是devDependencies

@babel/polyfill 主要有两个缺点:

1. 使用 @babel/polyfill 需要做些额外配置,实现打包的时候按需引入,否则会把 @babel/polyfill 全部注入代码中会导致打出来的包非常大。

2.@babel/polyfill会污染全局变量。

Babel7的一个重大变化就是 npm package 名称的变化,把所有babel-* 重命名为@babel/*,例如:

  • babel-polyfill重命名为@babel/polyfill
  • babel-preset-env重命名为@babel/preset-env

Babel 在 webpack 中的用法

首先实现对 ES6 语法的转译

安装babel-loader、@babel/core、@babel/preset-env

  • npm i babel-loader -D
  • npm i @babel/core -D
  • npm i @babel/preset-env -D

babel-loader@8需要安装 @babel/core7.x 版本。

在 webpack.config.js 配置

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use:{
          loader: 'babel-loader',         
          options:{
            presets: [
              ["@babel/preset-env",{
                //targets:表示编译出的代码想要支持的浏览器版本
                targets: {chrome: "67"}
              }]
            ]
          }
        }
      }
    ]
  }
}

执行 npm run buildnpx webpack就可以看到 dist 目录下的打包文件,但是只是将 ES6 的语法进行转译,并没有对 ES6 新 API 进行转译,所以我们需要配置 @babel/polyfill 解决这个问题。

安装@babel/polyfill
npm i @babel/polyfill --save

index.js 中引入@babel/polyfill

index.js

//index.js

import '@babel/polyfill'

let arr=[new Promise(()=>{}),
  new Promise(()=>{}),
  2
]

arr.map((item)=>{console.log(item)
})

引入 @babel/polyfill 前,main.js 的大小为 29.5KB

引入 @babel/polyfill 后,main.js 的大小为 1MB

注意:以上对比都是在没有 targets 这个选项的情况下,因为有些浏览器几乎都支持 ES6,在这种情况下,@babel/preset-env将不会对代码进行处理。

这是因为把 @babel/polyfill 对所有 API 的实现都注入到打包文件中,但是里面很多的 API 我们在代码中并没有用到,所以需要修改配置,按需引入对应的 API。

修改 webpack.config.js 配置

添加 "useBuiltIns": "usage" 以后,需要安装 core-js@2,并且添加"corejs": 2 配置项,这时配置选项比较多,需要在项目根目录下新建 .babelrc 文件,在这个文件中配置。

.babelrc配置如下:

  • "useBuiltIns"属性值为 "usage" 时,会自动引入@babel/polyfill,必须保证已经安装了@babel/polyfill
  • "useBuiltIns"属性值为 "usage" 时,需要添加 "corejs": 2 配置项,否则报错,需要安装core-js

首先删掉 index.js 中的import '@babel/polyfill'

安装 core-js
npm i --save core-js@2npm i --save core-js@3

{
  "presets": [["@babel/preset-env",{
    "useBuiltIns": "usage", // 不需要把 polly 都打包到代码中,根据代码按需转译
    // core-js@3和 core-js@2二选一
    //"corejs": 3,  //npm i --save core-js@3
    "corejs": 2  //npm i --save core-js@2
  }]]
}

修改 webpack.config.js,删除options 对象

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

执行npm run build,打包后的文件大小为165KB

但是,在开发类库或是第三方模块时不适合使用 @babel/polyfill,所以接下来使用@babel/plugin-transform-runtime 来解决这个问题。

@babel/plugin-transform-runtime、@babel/runtime 和 @babel/runtime-corejs2 的用法

@babel/runtime-corejs2:是一个包含 Babel modular runtime helpersregenerator-runtime以及 core-js 的库。

@babel/runtime:是一个包含 Babel modular runtime helpersregenerator-runtime的库。

在配置项中 corejs 属性值为默认为 false,如果需要将PromiseAPI进行转译,则需要设置属性值为 2 时,并且安装@babel/runtime-corejs2

安装:

  • npm i @babel/plugin-transform-runtime -D
  • npm i --save @babel/runtime
  • npm i --save @babel/runtime-corejs2

修改.babelrc 文件

{
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}

我们把 presets 配置项去掉了,然后 npm run build 打包,打开打包后的 main.js 查看,虽然把转译了 Promise,但是 ES6 新语法并没被转译,例如:let 没有被转译为var

所以还是需要配置 presets,因为"@babel/preset-env" 包含了对所有 ES6 语法转译为 ES5 插件。

再次修改.babelrc 文件

{"presets": ["@babel/preset-env"],
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}

添加 presets 配置项,然后 npm run build 打包,打开打包后的 main.js 查看,可以看到 let 和箭头函数都被转译为 ES5 语法了。

未完待续 ……

正文完
 0