项目搭建之 Webpack 4 配置笔记

253 阅读4分钟

什么是 webpack?

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

webpack核心概念

  • 入口(entry)入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。

可以通过在 webpack.config.js 中配置 entry 属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src

  • 输出(output)output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程。
  • loaderloader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效 模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

在更高层面,在 webpack 的配置中 loader 有两个目标:

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use 属性,表示进行转换时,应该使用哪个 loader。
  • 插件(plugins):loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。

  • 模式

通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化。

用 webpack 搭建一个基于 React+TypeScript 的项目

  1. 初始化项目:创建一个项目文件夹,运行 npm init -y 进行初始化。
  2. 安装 webpackwebpack-cli
yarn add webpack webpack-cli --dev //(或者使用 npm)
  1. 新建 lib/index.tsx文件,随便写点什么:
console.log('hi');
  1. 新建 webpack.config.js,开始 webpack 配置,首先添加入口配置:
module.exports = {
    entry: {
        index: './lib/index.tsx'   
    },
}
  1. 告诉 webpack 如何理解 tsx 文件,添加 awesome-typescript-loader。首先安装:
yarn add awesome-typescript-loader --dev

webpack.config.js 中添加配置:

module.exports = {
    // ...
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
    },
    module: {
        rules: [
            {
                test: /.tsx?$/,
                loader: 'awesome-typescript-loader'
            }
        ]
    },
}
  1. 添加出口配置
const path = require('path')
module.exports = {
   // ...
    output: {
        path: path.resolve(__dirname, 'dist/lib'), // 必须是绝对路径
        library: 'MyLibrary',  // 模块名称
        libraryTarget: 'umd',  //将 library 暴露为所有的模块定义下都可运行的方式
    },
    module: {
       // ...
    },
}
  1. 添加 mode
module.exports = {
    //....
    mode: "production",
    module: {
        //...
    }
}

mode 配置项,告知 webpack 使用相应模式的内置优化。

mode 配置项,支持以下两个配置:

  • development:将 process.env.NODE_ENV 的值设置为 development,启用 NamedChunksPluginNamedModulesPlugin
  • production:将 process.env.NODE_ENV 的值设置为 production,启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin
  1. 配置 TypeScriptTSlint, 添加 tsconfig.jsontslint.json
// tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist",
    "declaration": true,
    "baseUrl": ".",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": ".",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "importHelpers": true,
    "strictNullChecks": true,
    "esModuleInterop": true,
    "noUnusedLocals": true
  },
  "include": [
    "lib/**/*"
  ],
  "exclude": [
    "node_modules",
    "build",
    "dist",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts",
    "*.js"
  ]
}
// tslint.json
{
  "extends": ["tslint:recommended", "tslint-react"],
  "rules": {
    "no-console": [false, "log", "error"],
    "jsx-no-multiline-js": false,
    "whitespace": false,
    "no-empty-interface": false,
    "space-before-function-paren": false,
    "no-namespace": false,
    "label-position": false,
    "quotemark": [true, "single", "jsx-double"],
    "member-access": false,
    "semicolon": [true, "always", "ignore-bound-class-methods"],
    "no-unused-expression": [true, "allow-fast-null-checks"],
    "member-ordering": false,
    "trailing-comma": false,
    "arrow-parens": false,
    "jsx-self-close": false,
    "max-line-length": false,
    "interface-name": false,
    "no-empty": false,
    "comment-format": false,
    "ordered-imports": false,
    "object-literal-sort-keys": false,
    "eofline": false,
    "jsx-no-lambda": false,
    "no-trailing-whitespace": false,
    "jsx-alignment": false,
    "jsx-wrap-multilines": false,
    "no-shadowed-variable": [
      false,
      {
        "class": true,
        "enum": true,
        "function": false,
        "interface": false,
        "namespace": true,
        "typeAlias": false,
        "typeParameter": false
      }
    ]
  },
  "linterOptions": {
    "exclude": [
      "config/**/*.js",
      "node_modules/**/*.ts",
      "coverage/lcov-report/*.js"
    ]
  }
}

现在,执行 npx webpack, 就可以在 dist/lib 文件夹下看到转义好的 index.js 文件了。

  1. 在浏览器中查看页面 查看页面需要有 html 文件,由于每次生成的 js 文件名可能会不同,总不能每次手动修改 html,所以使用 html-webpack-plugin 这个插件来帮我们自动插入 script 标签。

首先安装 html-webpack-plugin

yarn add html-webpack-plugin --dev

配置 webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入插件
module.exports = {
   // ...
    plugins: [
        new HtmlWebpackPlugin({
            title: 'React-app',
            template: 'index.html'
        })
    ]
}

在根目录新建 index.html

<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
  1. 使用 webpack-dev-server 实现自动打包 运行 yarn add webpack-dev-server --dev安装 webpack-dev-server

运行 npx webpack-dev-server 即可打开一个端口,该端口请求的 js 是最新编译后的源码字符串(存放在内存中)。

  1. 对开发环境和生产环境进行分别配置 创建 webpack.config.dev.jswebpack.config.prod.js

根据项目需求和使用方法,比如本项目是开发一个 React 组件库,那么使用时就不需要再额外下载 React 依赖,那么在生产环境对应的配置中,将 react 加入 externals,可以缩小打包后代码的体积。将生产环境和开发环境共有的配置保留在 webpack.config.js中,然后分别对webpack.config.dev.jswebpack.config.prod.js根据项目需求进行配置。

webpack.config.js

const path = require('path')
module.exports = {
  entry: {
    index: './lib/index.tsx'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
  output: {
    path: path.resolve(__dirname, 'dist/lib'),
    library: 'MyLibrary',
    libraryTarget: 'umd',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader'
      },
      {
        test: /\.svg$/,
        loader: 'svg-sprite-loader',
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader']
      }
    ]
  },
}

webpack.config.dev.js 用于开发环境

const base = require('./webpack.config')   //将共有的配置项合并
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = Object.assign({}, base, {
    mode: 'development',
    entry: {
        example: './example.tsx',
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'React-app',
            template: 'example.html'
        })
    ],
})

webpack.config.prod.js 用于生产环境

const base = require('./webpack.config')
module.exports = Object.assign({}, base, {
    mode: 'production',
    externals: {
        react: {
            commonjs: 'react',
            commonjs2: 'react',
            amd: 'react',
            root: 'React',
        },
        'react-dom': {
            commonjs: 'react-dom',
            commonjs2: 'react-dom',
            amd: 'react-dom',
            root: 'ReactDOM',
        },
    }
})

package.json文件中添加

  "scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
    "test": "cross-env NODE_ENV=test jest --config=jest.config.js --runInBand",
  },

就可以通过 yarn start 命令运行 npx webpack-dev-server且指定配置文件为 webpack.config.dev.js

通过 yarn build 命令运行 npx webpack 且指定配置文件为 webpack.config.prod.js。(cross-env是为了兼容Windows和Mac)。


此次项目的 webpack 配置基本完成,在后续的开发中,只能根据需求和报错随机应变添加相应的配置。只有理解了 webpack,才能根据自己的需求做出最合适的配置。

本文参考: