5

【webpack 系列】基础篇

 4 years ago
source link: http://www.cnblogs.com/alsy/p/12594946.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Webpack 基础篇

基本概念

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

四个核心概念

  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 进行项目初始化。

mkdir webpack-demo && cd webpack-demo
npm init -y

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

npm i -D webpack
npm i -D webpack@<version>

如果你使用 webpack 4+ 版本,你还需要安装 CLI

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

现在安装的 webpack 版本号是:

├── [email protected]
└── [email protected]

新建 src/index.js 文件:

// 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 命令,模式指定为 production

webpack 默认会将 ./src/index.js 作为入口文件,默认打包到 dist/main.js

// ...
"scripts": {
  "build": "webpack --mode=production"
}
// ...

通过 npm run build 可以执行我们定义的命令,这是可以多了 dist/main.js 文件,这就是打包之后的 js 代码。

webpack 配置文件

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

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

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 。启用 NamedChunksPluginNamedModulesPluginproduction 会将 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 也自动加上浏览器前缀了。

i6Rfyiu.png!web

需要注意的是 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 一下,可以看到图片地址已经被替换了

RRvmmef.png!web

npm run build 可以看到 dist/assets 有这个文件

UNV3aeR.png!web

注意此时如果需要在 html 文件中引用这个图片需要这样写

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

最终打包之后的路径是

F73ENre.png!web

打包前清空 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 系列的其他内容哦~

喜欢本文的话点个赞吧~

qiU7Vv3.jpg!web

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK