前言

本篇将从实际登程,搭建一个根底的反对模块化开发的我的项目,在第二章节《进阶配置》中应用 webpack 搭建一个 SASS + TS + React 的我的项目。

一、根底配置

接下来一起配置一个根底的 Webpack。

将反对以下性能:

  • 拆散开发环境、生产环境配置;
  • 模块化开发;
  • sourceMap 定位正告和谬误;
  • 动静生成引入 bundle.js 的 HTML5 文件;
  • 实时编译;
  • 封装编译、打包命令。

1. 新建我的项目

新建一个空我的项目:

// 新建 webpack-demo 文件夹mkdir webpack-demo// 进入 webpack-demo 目录cd ./webpack-demo// 初始化我的项目npm init -y

新建 2 个 js 文件,并进行模块化开发:

// 进入我的项目目录cd ./webpack-demo// 创立 src 文件夹mkdir src// 创立 js文件touch index.jstouch hello.js

index.js:

// index.jsimport './hello.js'console.log('index')

hello.js:

// hello.jsconsole.log('hello webpack')

我的项目构造如下:

- src    - index.js    - hello.js- package.json- node_modules

2. 装置

  1. 装置 Node

Node 须要是最新版本,举荐应用 nvm 来治理 Node 版本。

Node.js 更新到最新版本,也有助于进步性能。除此之外,将你的 package 管理工具(例如 npm 或者 yarn)更新到最新版本,也有助于进步性能。较新的版本可能建设更高效的模块树以及进步解析速度。

我装置的版本信息如下:

  • node v14.17.3
  • npm v6.14.13)
  • 装置 webpack
npm install webpack webpack-cli --save-dev

3. 新建配置文件

development(开发环境)production(生产环境) 这两个环境下的构建指标存在着微小差别。为代码清晰扼要,为每个环境编写彼此独立的 webpack 配置

// 进入我的项目目录cd ./webpack-demo// 创立 config 目录mkdir config// 进入 config 目录cd ./config// 创立通用环境配置文件touch webpack.common.js// 创立开发环境配置文件touch webpack.dev.js// 创立生产环境配置文件touch webpack.prod.js

webpack-merge

应用 webpack-marge 合并通用配置和特定环境配置。

装置 webpack-merge:

npm i webpack-merge -D

通用环境配置:

// webpack.common.jsmodule.exports = {} // 暂不增加配置

开发环境配置:

// webpack.dev.jsconst { merge } = require('webpack-merge')const common = require('./webpack.common')module.exports = merge(common, {}) // 暂不增加配置

生产环境配置:

// webpack.prod.jsconst { merge } = require('webpack-merge')const common = require('./webpack.common')module.exports = merge(common, {}) // 暂不增加配置

我的项目构造如下:

- config    - webpack.common.js    - webpack.dev.js    - webpack.prod.js- src    - index.js    - hello.js- package.json- node_modules

4. 入口(entry)

入口终点(entry point) 批示 webpack 应该应用哪个模块,来作为构建其外部 依赖图(dependency graph) 的开始。进入入口终点后,webpack 会找出有哪些模块和库是入口终点(间接和间接)依赖的。

在本例中,应用 src/index.js 作为我的项目入口,webpack 以 src/index.js 为终点,查找所有依赖的模块。

批改 webpack.commom.js:

module.exports = merge(common, {  // 入口  entry: {    index: './src/index.js',  },})

5. 输入(output)

output 属性通知 webpack 在哪里输入它所创立的 bundle,以及如何命名这些文件。

生产环境的 output 须要通过 contenthash 值来辨别版本和变动,可达到清缓存的成果,而本地环境为了构建效率,则不引人 contenthash

新增 paths.js,封装门路办法:

参考webpack视频解说:进入学习

const fs = require('fs')const path = require('path')const appDirectory = fs.realpathSync(process.cwd());const resolveApp = relativePath => path.resolve(appDirectory, relativePath);module.exports = {  resolveApp,  appPublic: resolveApp('public'),  appHtml: resolveApp('public/index.html'),  appSrc: resolveApp('src'),  appDist: resolveApp('dist'),  appTsConfig: resolveApp('tsconfig.json'),}

批改开发环境配置文件 webpack.dev.js:

module.exports =  merge(common, {  // 输入  output: {    // bundle 文件名称    filename: '[name].bundle.js',    // bundle 文件门路    path: resolveApp('dist'),    // 编译前革除目录    clean: true  },})

批改生产环境配置文件 webpack.prod.js:

module.exports =  merge(common, {  // 输入  output: {    // bundle 文件名称 【只有这里和开发环境不一样】    filename: '[name].[contenthash].bundle.js',    // bundle 文件门路    path: resolveApp('dist'),    // 编译前革除目录    clean: true  },})

上述 filename 的占位符解释如下:

  • [name] - chunk name(例如 [name].js -> app.js)。如果 chunk 没有名称,则会应用其 id 作为名称
  • [contenthash] - 输入文件内容的 md4-hash(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js

6. 模式(mode)

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

选项形容
development会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用无效的名。
production会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混同名称,FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPluginTerserPlugin

批改开发环境配置文件 webpack.dev.js:

module.exports =  merge(common, {  // 开发模式  mode: 'development',})

批改生产环境配置文件 webpack.prod.js:

module.exports =  merge(common, {  // 生产模式  mode: 'production',})

7. Source Map

当 webpack 打包源代码时,可能会很难追踪到 error 和 warning 在源代码中的原始地位。

为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 性能,能够将编译后的代码映射回原始源代码。

批改开发环境配置文件 webpack.dev.js:

module.exports =  merge(common, {  // 开发工具,开启 source map,编译调试  devtool: 'eval-cheap-module-source-map',})

source map 有许多 可用选项。本例抉择的是 eval-cheap-module-source-map

注:为放慢生产环境打包速度,不为生产环境配置 devtool。

实现上述配置后,能够通过 npx webpack --config config/webpack.prod.js 打包编译。

编译后,会生成这样的目录构造:

8. HtmlWebpackPlugin

npx webpack --config config/webpack.prod.js 后仅生成了 bundle.js,咱们还须要一个 HTML5 文件,用来动静引入打包生成的 bundle 文件。

引入 HtmlWebpackPlugin 插件,生成一个 HTML5 文件, 其中包含应用 script 标签的 body 中的所有 webpack 包。

装置:

npm install --save-dev html-webpack-plugin

批改通用环境配置文件 webpack.commom.js:

module.exports = {  plugins: [    // 生成html,主动引入所有bundle    new HtmlWebpackPlugin({      title: 'release_v0',    }),  ],}

从新 webpack 编译 npx webpack --config config/webpack.prod.js,生成的目录构造如下:

新生成了 index.html,动静引入了 bundle.js 文件:

<!DOCTYPE html><html> <head>  <meta charset="utf-8" />  <title>release_v0</title>  <meta name="viewport" content="width=device-width,initial-scale=1" />  <script defer="defer" src="index.468d741515bfc390d1ac.bundle.js"></script> </head> <body></body></html>

9. DevServer

在每次编译代码时,手动运行 npx webpack --config config/webpack.prod.js 会显得很麻烦,webpack-dev-server 帮忙咱们在代码发生变化后主动编译代码。

webpack-dev-server 提供了一个根本的 web server,并且具备实时从新加载性能。

webpack-dev-server 默认配置 conpress: true,为每个动态文件开启 gzip compression。

装置:

npm install --save-dev webpack-dev-server

批改开发环境配置文件 webpack.dev.js:

module.exports = merge(common, {  devServer: {    // 通知服务器从哪里提供内容,只有在你想要提供动态文件时才须要。    contentBase: './dist',  },})

实现上述配置后,能够通过 npx webpack serve --open --config config/webpack.dev.js 实时编译。

成果如图:

10.执行命令

上述配置文件实现后,优化 webpack 的实时编译、打包编译指令。

通过 cross-env 配置环境变量,辨别开发环境和生产环境。

装置:

npm install --save-dev cross-env

批改 package.json:

{    "scripts": {        "dev": "cross-env NODE_ENV=development webpack serve --open --config config/webpack.dev.js",        "build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"      },}

当初能够运行 webpack 指令:

  • npm run dev:本地构建;
  • npm run build:生产打包。

以上咱们实现了一个基于 webpack 编译的反对模块化开发的简略我的项目。

二、进阶配置

本章节将持续欠缺配置,在上述配置根底上,用 Webpack 搭建一个 SASS + TS + React 的我的项目。

将反对以下性能:

  • 加载图片;
  • 加载字体;
  • 加载 CSS;
  • 应用 SASS;
  • 应用 PostCSS,并主动为 CSS 规定增加前缀,解析最新的 CSS 语法,引入 css-modules 解决全局命名抵触问题;
  • 应用 React;
  • 应用 TypeScript。

1. 加载图片(Image)

在 webpack 5 中,能够应用内置的 Asset Modules,将 images 图像混入咱们的零碎中。

批改通用环境配置文件 webpack.commom.js:

const paths = require('./paths');module.exports = {    module: {        rules: [          {            test: /\.(png|svg|jpg|jpeg|gif)$/i,            include: paths.appSrc,            type: 'asset/resource',          },        ],      },}
在理论开发过程中,举荐将大图片上传至 CDN,进步加载速度。

2. 加载字体(Font)

应用 Asset Modules 接管字体文件。

批改通用环境配置文件 webpack.commom.js:

module.exports = {    module: {        rules: [            {               test: /.(woff|woff2|eot|ttf|otf)$/i,               include: [                  resolveApp('src'),                ],               type: 'asset/resource',             },         ]     } }

在这里我发现,我不新增对 ttf 文件的配置,也可能引入字体不报错,不晓得是什么问题,先记录一下,有晓得起因的大佬移步评论区。

在理论开发过程中,举荐将字体文件压缩上传至 CDN,进步加载速度。如配置字体的文字是固定的,还能够针对固定的文字生成字体文件,能够大幅放大字体文件体积。

3. 加载 CSS

为了在 JavaScript 模块中 import 一个 CSS 文件,须要装置并配置 style-loader 和 css-loader。

3.1 style-loader

style-loader 用于将 CSS 插入到 DOM 中,通过应用多个 <style></style> 主动把 styles 插入到 DOM 中.

3.2 css-loader

css-loader 对 @importurl() 进行解决,就像 js 解析 import/require() 一样,让 CSS 也能模块化开发。

3.3 装置配置

装置 CSS 相干依赖:

npm install --save-dev style-loader css-loader

批改通用环境配置文件 webpack.commom.js:

const paths = require('./paths');module.exports = {    module: {        rules: [            {                test: /\.css$/,                include: paths.appSrc,                use: [                  // 将 JS 字符串生成为 style 节点                  'style-loader',                  // 将 CSS 转化成 CommonJS 模块                  'css-loader',                ],              },          ]      }  }

4. 应用 SASS

4.1 Sass

Sass 是一款强化 CSS 的辅助工具,它在 CSS 语法的根底上减少了变量、嵌套、混合、导入等高级性能。

4.2 sass-loader

sass-loader 加载 Sass/SCSS 文件并将他们编译为 CSS。

4.3 装置配置

装置 SASS 相干依赖:

npm install --save-dev sass-loader sass 

批改通用环境配置文件 webpack.commom.js:

const paths = require('./paths');module.exports = {    module: {        rules: [        {        test: /.(scss|sass)$/,        include: paths.appSrc,        use: [          // 将 JS 字符串生成为 style 节点          'style-loader',          // 将 CSS 转化成 CommonJS 模块          'css-loader',          // 将 Sass 编译成 CSS          'sass-loader',        ],      },

5. 应用 PostCSS

5.1 PostCSS

PostCSS 是一个用 JavaScript 工具和插件转换 CSS 代码的工具。

  • 能够主动为 CSS 规定增加前缀;
  • 将最新的 CSS 语法转换成大多数浏览器都能了解的语法;
  • css-modules 解决全局命名抵触问题。

5.2 postcss-loader

postcss-loader 应用 PostCSS 解决 CSS 的 loader。

5.3 装置配置

装置 PostCSS 相干依赖:

npm install --save-dev postcss-loader postcss postcss-preset-env

批改通用环境配置文件 webpack.commom.js:

const paths = require('./paths');module.exports = {    module: {        rules: [          {            test: /\.module\.(scss|sass)$/,            include: paths.appSrc,            use: [              // 将 JS 字符串生成为 style 节点              'style-loader',              // 将 CSS 转化成 CommonJS 模块              {                loader: 'css-loader',                options: {                  // Enable CSS Modules features                  modules: true,                  importLoaders: 2,                  // 0 => no loaders (default);                  // 1 => postcss-loader;                  // 2 => postcss-loader, sass-loader                },              },              // 将 PostCSS 编译成 CSS              {                loader: 'postcss-loader',                options: {                  postcssOptions: {                    plugins: [                      [                        // postcss-preset-env 蕴含 autoprefixer                        'postcss-preset-env',                      ],                    ],                  },                },              },              // 将 Sass 编译成 CSS              'sass-loader',            ],          },        ],      },}
为晋升构建效率,为 loader 指定 include,通过应用 include 字段,仅将 loader 利用在理论须要将其转换的模块。

5. 应用 React + TypeScript

为了让我的项目的配置灵活性更高,不应用 create-reate-app 一键搭建我的项目,而是手动搭建 React 对应的配置项。

装置 React 相干:

npm i react react-dom @types/react @types/react-dom -D

装置 TypeScript 相干:

npm i -D typescript esbuild-loader
为进步性能,摒弃了传统的 ts-loader,抉择最新的 esbuild-loader。

批改通用环境配置文件 webpack.commom.js:

const paths = require('./paths'); module.exports = {    resolve: {        extensions: ['.tsx', '.ts', '.js'],    },    module: {        rules: [            {                test: /\.(js|ts|jsx|tsx)$/,                include: paths.appSrc,                use: [                  {                    loader: 'esbuild-loader',                    options: {                      loader: 'tsx',                      target: 'es2015',                    },                  }                ]              },         ]     } }

TypeScript 是 JavaScript 的超集,为其减少了类型零碎,能够编译为一般 JavaScript 代码。

为兼容 TypeScript 文件,新增 typescript 配置文件 tsconfig.json:

{    "compilerOptions": {        "outDir": "./dist/",        "noImplicitAny": true,        "module": "es6",        "target": "es5",        "jsx": "react",        "allowJs": true,        "moduleResolution": "node",        "allowSyntheticDefaultImports": true,        "esModuleInterop": true,      }}
如果想在 TypeScript 中保留如import _ from 'lodash';的语法被让它作为一种默认的导入形式,须要在文件 tsconfig.json 中设置 "allowSyntheticDefaultImports" : true"esModuleInterop" : true

留神:这儿有坑

1. "allowSyntheticDefaultImports": true 配置

TypeScript 配置文件 tsconfig.json 须要加 "allowSyntheticDefaultImports": true 配置,否则会提醒 can only be default-imported using the 'allowSyntheticDefaultImports' flag

{    "compilerOptions": {        "allowSyntheticDefaultImports": true    },}
2. tsx 和 jsx 不能混合应用

在 tsx 中引入 jsx 文件报错如下:

以上咱们实现了一个基于 webpack 编译的 SASS + TS + React 我的项目。

三、总结

本文从 Webpack 根底配置、Webpack 进阶配置 2 个角度进行讲述,从 Webpack 实际着手,和你一起理解 Webpack。

心愿能对你有所帮忙,感激浏览~