【webpack 系列】基础篇

Webpack 基础篇

基本概念

Webpack 是一个现代 JavaScript 应用程序的静态模块打包器。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 bundlecss

四个核心概念

  1. 入口(Entry): 构建 Webpack 内部依赖图的入口模块
  2. 输出(Output): 输出 Webpack 打包好的 Bundles
  3. Loader: 加载器,Webpack 原生只能解析 JavaScript 文件,Loaderwebpack 拥有了加载和解析非 JavaScript 文件的能力。
  4. 插件(Plugins): 扩展 Webpack 的功能,让 Webpack 具备更多的灵活性。在 Webpack 运行的生命周期中会广播出许多事件,Plugin 能够监听这些事件,在合适的时机经过 Webpack 提供的 API 改变输出结果。

Webpack 基础配置

初始化项目

新建一个文件夹 webpack-demo,在该目录中使用 npm init -y 进行项目初始化。html

mkdir webpack-demo && cd webpack-demo
npm init -y
复制代码

运行如下命令安装最新版本或特定版本node

npm i -D webpack
npm i -D webpack@<version>
复制代码

若是你使用 webpack 4+ 版本,你还须要安装 CLIwebpack

npm i -D webpack-cli
复制代码
  • npm i -Dnpm install --save-dev 的缩写,安装一个用于开发环境的安装包
  • npm i -Snpm install --save 的缩写,安装一个要打包到生产环境的安装包

如今安装的 webpack 版本号是:git

├── webpack@4.42.1
└── webpack-cli@3.3.11
复制代码

新建 src/index.js 文件:es6

// src/index.js

class HelloComponent {
  constructor (content = '') {
    this.content = content;
    this.render();
  }
  render () {
    const element = document.createElement('div');
    element.innerHTML = this.content;
    document.body.appendChild(element);
  }
}

new HelloComponent('hello webpack');
复制代码

如今能够直接执行 npx webpack,默认是 production 模式。 也能够在 package.json 中的 scripts 里配置一个 build 命令,模式指定为 productionwebpack 默认会将 ./src/index.js 做为入口文件,默认打包到 dist/main.jsgithub

// ...
"scripts": {
  "build": "webpack --mode=production"
}
// ...
复制代码

经过 npm run build 能够执行咱们定义的命令,这是能够多了 dist/main.js 文件,这就是打包以后的 js 代码。web

webpack 配置文件

上面例子中使用的是 webpack 的默认配置,下面咱们来定义更加丰富的自定义配置。npm

根目录下新建 webpack.config.js 文件json

const path = require('path');

module.exports = {
  mode: 'development', // 模式
  entry: path.resolve(__dirname, 'src/index.js'), // 入口文件
  output: {
    path: path.resolve(__dirname, 'dist'), // 输出目录
    filename: 'bundle.js' // 输出文件名
  }
}
复制代码

更改咱们的 build 命令,指定 webpack 按照咱们的配置文件来打包文件

"scripts": {
  "build": "webpack --config webpack.config.js"
}
复制代码

执行 npm run build 能够看到,dist 目录下新增了 bundle.js 文件。而且 bundle.js 是在开发模式下打包的,能够看到更多的信息。

html-webpack-plugin 插件

如今咱们已经有了打包好的 js 文件了,须要添加个 html 文件来引入这个 js 文件在浏览器查看效果了。

在实际开发中,为了不每次修改打包的 js 文件被浏览器缓存而看不到最新的代码,咱们会给打包文件加上 hash,至关于这个文件的版本号。这样每次修改后打包的 js 文件名都会不一样,若是人工去修改 html 中的 js 文件名就太麻烦了,咱们能够借助 html-webpack-plugin 插件来自动完成这些事情。

安装 html-webpack-plugin

npm i -D html-webpack-plugin
复制代码

新建 public/index.html 文件,修改咱们的 webpack.config.js

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'public/index.html'), // 指定模板文件,不指定会生成默认的 index.html 文件
      filename: 'index.html' // 打包后的文件名
    })
  ]
}
复制代码

执行 npm run build 能够看到 dist 目录下新增了 index.html 文件,而且自动将打包好的 bundle.js 文件经过 script 标签引入了。

webpack-dev-server 开发工具

如今能够经过浏览器直接打开 dist/index.html 查看了,可是这样每次改完代码咱们都须要手动 npm run build 一下,这样太麻烦了。

咱们能够借助 webpack-dev-server 来解决这个问题。webpack-dev-server 会提供了一个简单的 web 服务器,而且可以实时从新加载。

安装 webpack-dev-server

npm i -D webpack-dev-server
复制代码

修改 package.json 文件

// package.json
"scripts": {
  "dev": "webpack-dev-server --config webpack.config.js",
  "build": "webpack --config webpack.config.js"
}
复制代码

npm run dev 以后,默认会在 localhost:8080 下创建服务,经过访问这个地址能够访问到 dist 目录下的文件。

能够在 webpack.config.jsdevServer 进行配置

// webpack.config.js

module.exports = {
  // ...
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: '9000', // 指定端口,默认是8080
    compress: true // 是否启用 gzip 压缩
  }
  //...
}
复制代码

关于 webpack-dev-server 更多的配置能够点击查看

mode

咱们在 package.json 定义了两条命令,可是 mode 都为 development。咱们能够经过设置 process.env.NODE_ENV 的值来区分开发仍是生产环境。

咱们须要安装一下 cross-env, 来实现跨平台设置 NODE_ENV

npm i -D cross-env
复制代码
// package.json
"scripts": {
  "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js",
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
}
复制代码

修改 webpack.config.js

// webpack.config.js

const isProduction = process.env.NODE_ENV == 'production';

module.exports = {
  mode: isProduction ? 'production' : 'development', // 模式
  // ...
}
复制代码

设置 mode 的不一样值能够启用相应模式下的 webpack 内置的优化

选项 描述
development 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPluginNamedModulesPlugin
production 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin.

用 babel 向后兼容 js 语法

如今咱们的代码虽然已经完成了打包,可是并无被转义为低版本的代码。咱们须要经过 Babel 来将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便可以运行在当前和旧版本的浏览器或其余环境中。

安装 babel-loader

npm i -D babel-loader
复制代码

此外咱们还须要安装如下依赖

npm i -D @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm i -S @babel/runtime @babel/runtime-corejs3
复制代码

webpack.config.js 配置 babel-loader

// webpack.config.js 
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: ['babel-loader'],
        exclude: /node_modules/ // 排除 node_modules 目录
      }
    ]
  }
  // ...
}
复制代码

建议给 loader 指定 include 或是 exclude,排除一些不须要编译的目录能够提升编译效率,好比 node_modules 目录。

有两种方式配置 babel

  1. 经过 .babelrc 文件配置 根目录下新建一个 .babelrc 文件,配置以下:
// .babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3
      }
    ]
  ]
}
复制代码
  1. webpack 中配置 babel
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ["@babel/preset-env"],
            plugins: [
              [
                "@babel/plugin-transform-runtime",
                {
                  "corejs": 3
                }
              ]
            ]
          }
        },
        exclude: /node_modules/
      }
    ]
  }
}
复制代码

经过执行 npm run dev, 咱们查看 http://localhost:9000/bundle.js 发现已是转义以后的低版本代码了。

使用 source map

webpack 打包源代码时,会很难追踪到错误和警告在源代码中的原始位置。为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。

在开发环境中,能够设置 devtool 值为 inline-source-map,生产环境设置为 none 或者 source-map

// webpack.config.js
const isProduction = process.env.NODE_ENV == 'production';

module.exports = {
  // ...
  devtool: isProduction ? 'source-map' : 'inline-source-map',
}
复制代码

使用 source-map 最终会单独打包出一个 .map 文件,咱们能够根据报错信息和 map 文件定位到源代码。

可是通常不会直接将 .map 文件部署到 CDN,而是将 .map 文件传到错误监控系统,以便咱们能够解析到出错的源码位置。

处理样式文件

webpack 只能处理 js 文件,若是要处理 css 须要借助 loader

若是是 .css,咱们须要的 loader 有: style-loadercss-loader,考虑到兼容性问题,还须要 postcss-loaderautoprefixer

若是是 .less, 还须要 less-loaderless

若是是 .sass 的话,还须要 sass-loadernode-sass

安装相应的依赖

npm i -D style-loader css-loader postcss-loader autoprefixer less-loader less sass-loader node-sass
复制代码

webpack.config.js 添加 csslesssass loader

// webpack.config.js

module.exports = {
  // ..
  module: {
    rules: [
      // ...
      {
        test: /\.(c|le)ss$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
        exclude: /node_modules/ // 排除 node_modules 目录
      },
      {
        test: /\.sass$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
        exclude: /node_modules/ // 排除 node_modules 目录
      }
    ]
  }
}
复制代码

根目录新建 postcss.config.js

// postcss.config.js
module.exports = {
  plugins: [
    // 兼容浏览器,添加前缀
    require('autoprefixer')({
      overrideBrowserslist: [
        "Android 4.1",
        "iOS 7.1",
        "Chrome > 31",
        "ff > 31",
        "ie >= 8"
        // 'last 10 versions', // 全部主流浏览器最近10版本用
      ],
      grid: true
    })
  ]
}
复制代码

配置完成,新建几个文件测试一下

/* src/index.css */
div {
  width: 200px;
  height: 100px;
  display: flex;
}
复制代码
// src/index.less
@color: yellow;
body {
  background: @color;
  display: flex;
}
复制代码
// src/index.sass
$text-color: orange;
div 
  color: $text-color;
  display: flex;
复制代码

再在入口文件中引入三个文件

// src/index.css
import './index.css';
import './index.less';
import './index.sass';
复制代码

咱们能够看到样式生效而且 flex 也自动加上浏览器前缀了。

须要注意的是 loader 的执行顺序是从右向左执行的,执行顺序为:

less-loader/sass-loader => postcss-loader => css-loader => style-loader

  1. less-loader 处理编译 .less 文件,将其转为 css
  2. sass-loader 处理编译 .sass 文件,将其转为 css
  3. postcss-loaderautoprefixer,自动生成浏览器兼容性前缀
  4. css-loader 处理 css 中的 @importurl(...)等语句
  5. style-loader 动态建立 style 标签,将 css 插入到 head

处理图片、字体等媒体文件

咱们可使用 url-loader 或者 file-loader 来处理本地的资源文件。

file-loader 就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件 url),将文件移动到输出的目录中,同时在 require 文件的地方会返回文件的绝对路径。

url-loader通常与 file-loader 搭配使用,功能与 file-loader 相似,若是文件小于限制的大小,则会返回 base64 编码。

须要同时安装 file-loaderurl-loader

npm i -D file-loader url-loader 
复制代码

配置 webpack.config.js

// webpack.config.js

module.exports = {
  // ..
  module: {
    rules: [
      // ...
      {
        test: /\.(jpe?g|png|gif|webp|svg|eot|ttf|woff|woff2)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240, // 10K 资源大小小于 10K 时,将资源转换为 base64,超过 10K,将图片拷贝到 dist 目录
              name: '[name]_[hash:6].[ext]', // 设置文件名,默认状况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名
              outputPath: 'assets', // 输出目录
              esModule: false // 表示是否使用es6模块的导出,默认是启用的
            }
          }
        ],
        exclude: /node_modules/
      }
    ]
  }
}
复制代码

咱们修改 src/index.sass 文件

// src/index.sass
$text-color: orange;
div 
  color: $text-color;
  display: flex;
  background: url('../images/author.jpg');
复制代码

修改了配置文件,咱们从新 npm run dev 一下,能够看到图片地址已经被替换了

npm run build 能够看到 dist/assets 有这个文件

注意此时若是须要在 html 文件中引用这个图片须要这样写

<body>
  <img src="<%= require('../images/author.jpg') %>">
</body>
复制代码

最终打包以后的路径是

打包前清空 dist 目录

咱们修改文件打包以后,生成的 hash 值和以前 dist 中的不同,会致使 dist 下的文件愈来愈多,因此咱们须要在打包前先清空 dist 目录。

安装 clean-webpack-plugin

npm i -D clean-webpack-plugin
复制代码
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  //...
  plugins: [
    // ...
    new CleanWebpackPlugin() 
  ]
}

复制代码

这样每次打包前就会自动清除 dist 目录下的文件了。

最后

经过上面的实践,咱们对 webpack 的基础配置有了一个初步的了解。本文全部代码能够查看github

后续将会继续推出 webpack 系列的其余内容哦~

喜欢本文的话点个赞吧~

更多精彩内容,欢迎关注微信公众号~

相关文章
相关标签/搜索