无论在面试还是内部晋升,webpack 构建优化方案 一直都是非常重要的部分。
webpack5构建加持
- 一、项目完成目标
- 二、搭建项目
- 1. 安装koa、@koa/router (如果已经配置可路过)
- 2. 创建入口文件
- 3. 安装构建依赖
- 4. 在项目根目录添加 `.babelrc` 文件
- 5. 添加测试接口
- 6. 启动服务
- 7. 请求接口进行测试
- 三、配置 webpack
- 1. 核心概念
- 2. 在项目根目录创建 webpack.config.js 文件
- 3. 测试构建
- 4. 优化构建配置
- 四、路由自动注册
- 1. 使用 require-directory
- 2. 使用 require.context(webpack)功能
- 五、配置别名
- 1. 使用 webpack 的别名功能
- 2. 使用 module-alias
一、项目完成目标
- 支持 es6+语法
- 开发热更新
- webpack5 构建
- 接口搭建
- 路由合并,路由自动注册
- 添加项目规范
- 配置自定义别名
二、搭建项目
# 创建项目目录
mkdir webpack5-node# 进入webpack5-node文件夹
cd webpack5-node# 初始化package.json
npm init -y# 创建源码目录
mkdir src
1. 安装koa、@koa/router (如果已经配置可路过)
yarn add koa @koa/router
2. 创建入口文件
touch src/app.js
3. 安装构建依赖
yarn add -D webpack webpack-cli @babel/node @babel/core @babel/preset-env babel-loader clean-webpack-plugin nodemon webpack-node-externals webpack-merge rimraf
4. 在项目根目录添加 .babelrc
文件
// .babelrc
{"presets": [["@babel/preset-env",{"targets": {"node": "current"}}]]
}
5. 添加测试接口
在 app.js 中添加测试接口,由于已经配置了 babel
解析,所以可以直接在 app.js 中写 es6+语法
// app.js
import Koa from 'koa'
import Router from '@koa/router'const app = new Koa()
const router = new Router()router.get('/', async ctx => {ctx.body = {status: 200,message: 'success',data: {nickname: 'Simon',title: '前端工程师',content: 'webpack5构建node应用'}}
})app.use(router.routes()).use(router.allowedMethods())const port = 3000
app.listen(port, () => console.log(`服务启动在 ${port} 端口`))
6. 启动服务
npx babel-node src/app.js
7. 请求接口进行测试
三、配置 webpack
中文文档
1. 核心概念
entry
:入口;指示webpack
应该使用哪个模块,默认值是./src/index.js
;output
:输出;output
属性告诉webpack
在哪里输出它所创建的bundle
,默认值是./dist/main.js
;loader
:loader
负责完成项目中各种各样资源模块的加载;plugins
:插件;用来解决项目中除了资源模块打包以外的其他自动化工作。包括:打包优化,资源管理,注入环境变量;mode
:模式;通过选择development
,production
或none
之中的一个,来设置mode
参数,你可以启用webpack
内置在相应环境下的优化。其默认值为production
;
2. 在项目根目录创建 webpack.config.js 文件
// webpack.config.js
const { DefinePlugin } = require('webpack')
const nodeExternals = require('webpack-node-externals')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = {// 打包编译为某一端侧的可使用代码 默认值:web https://webpack.docschina.org/configuration/target/target: 'node',// 打包模式,可选择值:development、productionmode: 'development',// 控制是否生成,以及如何生成 source map。 https://webpack.docschina.org/configuration/devtool/#rootdevtool: 'eval-cheap-source-map',// 打包模块入口文件entry: {server: `${process.cwd()}/src/app.js`},// 打包后的输入文件output: {filename: '[name].bundle.js',path: `${process.cwd()}/dist`},// 匹配解析规则module: {rules: [{test: /\.(js|jsx)$/,use: {loader: 'babel-loader'},exclude: [`${process.cwd()}/node_modules`]}]},// 构建过程中使用的插件plugins: [new CleanWebpackPlugin(),new DefinePlugin({'process.env': {NODE_ENV: JSON.stringify(process.env.NODE_ENV === 'production' ||process.env.NODE_ENV === 'prod'? 'production': 'development')}})],// 防止第三方依赖被打包externals: [nodeExternals()]
}
3. 测试构建
npx webpack
构建成功!
🤔❓
在实际开发中可能会存在开发环境和生产环境的构建,所以单凭一个配置还不能达到实际的需求,接下来对开发环境和生产环境分别配置。
在项目根目录创建 config 文件,并创建三个文件分别是:
webpack.config.base.js
文件存放开发环境和生产环境都是需要的构建配置webpack.config.dev.js
文件存放开发环境的构建配置webpack.config.prod.js
存放生产环境的构建配置
4. 优化构建配置
mode
独立于构建环境,开发环境为(development
)、生产环境为(production
)devtool
只有在开发环境下才会存在stats
属性让你更精确地控制打包后的信息该怎么显示 【Stats 对象】
Tips 🤔
由于每个开发环境和生产环境都是独立的构建配置,所以要在构建时要合并基础配置;
安装webpack-merge
合并构建配置
npm i -D webpack-merge
-
优化基础构建配置
webpack.config.base.js
// config/webpack.config.base.js const { DefinePlugin } = require('webpack') const nodeExternals = require('webpack-node-externals') const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = {// 打包编译为某一端侧的可使用代码 默认值:web https://webpack.docschina.org/configuration/target/target: 'node',// 打包模式,可选择值:development、production// mode: "development",// 控制是否生成,以及如何生成 source map。 https://webpack.docschina.org/configuration/devtool/#root// devtool: "eval-cheap-source-map",// 打包模块入口文件entry: {server: `${process.cwd()}/src/app.js`},// 打包后的输入文件output: {filename: '[name].bundle.js',path: `${process.cwd()}/dist`},// 匹配解析规则module: {rules: [{test: /\.(js|jsx)$/,use: {loader: 'babel-loader'},exclude: [`${process.cwd()}/node_modules`]}]},// 构建过程中使用的插件plugins: [new CleanWebpackPlugin(),new DefinePlugin({'process.env': {// 设置环境变量 NODE_ENVNODE_ENV: JSON.stringify(process.env.NODE_ENV === 'production' ||process.env.NODE_ENV === 'prod'? 'production': 'development')}})],// 防止第三方依赖被打包externals: [nodeExternals()] }
-
开发环境的构建配置
webpack.config.dev.js
// config/webpack.config.dev.js const { merge } = require('webpack-merge') const baseWebpackConfig = require('./webpack.config.base')const webpackConfig = merge(baseWebpackConfig, {devtool: 'eval-cheap-source-map',mode: 'development',// 是否添加关于子模块的信息。stats: { children: false } })module.exports = webpackConfig
-
生产环境的构建配置
webpack.config.prod.js
生产环境构建时要进行代码压缩,需要安装
terser-webpack-plugin
命令:npm i -D terser-webpack-plugin
// config/webpack.config.prod.js const { merge } = require('webpack-merge') const TersetWebpackPlugin = require('terser-webpack-plugin') const baseWebpackConfig = require('./webpack.config.base')const webpackConfig = merge(baseWebpackConfig, {devtool: 'eval-cheap-source-map',mode: 'production',stats: { children: false },// 优化配置optimization: {// 压缩配置minimize: true,minimizer: [new TersetWebpackPlugin()],// 分块策略splitChunks: {// 缓存组 https://webpack.docschina.org/plugins/split-chunks-plugin/#splitchunkscachegroupscacheGroups: {commens: {name: 'commons',chunks: 'initial',minChunks: 3,enforce: true}}}} })module.exports = webpackConfig
-
添加构建脚本命令
-
设置环境变量
NODE_ENV
,由于各环境配置的差异问题,cross-env
可以有效的解决跨平台设置环境变量的问题; -
它是运行跨平台设置和使用环境变量(Node 中的环境变量)的脚本。
-
安装命令:
npm i -D cross-env
安装成功后配置构建命令 -
在 package.json 的
scripts
中添加如下命令:"build": "cross-env NODE_ENV=prod webpack --config config/webpack.config.prod.js", "dev": "cross-env NODE_ENV=dev nodemon --exec babel-node --inspect src/app.js",
-
-
启动开发环境服务
npm run dev
- 运行之后的效果图如下
-
启动编译构建命令
npm run build
- 运行效果如下图:
- 查看 dist 文件夹下被编译后的文件:
代码被压缩成了一整行!
- 运行效果如下图:
四、路由自动注册
1. 使用 require-directory
在 src 文件夹下新建 routes 和 api 两文件夹;
routes 是集成当前项目的所有路由
api 文件是存放项目的所有接口文件
-
安装
require-directory
,这个包的作用可以将一个目录下的所有模块来自动注册Koa应用的路由,从而避免手动导入和注册每个路由文件。npm i require-dirctory
-
创建
src/api/v1
下创建 demo.js 和 test.js 文件// src/api/v1/demo.js import Router from '@koa/router'const router = new Router({ prefix: '/api/v1' })router.get('/demo', async ctx => {ctx.body = {status: 200,message: 'message',data: {file: 'demo.js',title: 'webpack 5 构建node应用',content: 'koa + @koa/router + require-dirctory'}} })export default router
// src/api/v1/test.js import Router from '@koa/router'const router = new Router({ prefix: '/api/v1' })router.get('/test', async ctx => {ctx.body = {status: 200,message: 'message',data: {file: 'test.js',title: 'webpack 5 构建node应用',content: 'koa + @koa/router + require-dirctory'}} })export default router
-
配置
src/routes/index.js
// src/routes/index.js import Router from '@koa/router' import requireDirectory from 'require-directory'// 接口存放目录路径 const apiDirectory = `${process.cwd()}/src/api`function initLoadRoutes(app) {requireDirectory(module, apiDirectory, {visit({ default: router }) {if (router instanceof Router) {app.use(router.routes())}}}) }export default initLoadRoutes
-
修改
src/app.js
文件// src/app.js import Koa from 'koa' import initLoadRoutes from './routes/index'const app = new Koa()// 在入口文件中执行 initLoadRoutes(app)const port = 3002 app.listen(port, () => console.log(`服务启动在${port}端口`))
-
测试请求
🎉 至此,自动注册路由就大功告成了,后面我们定义接口的时候就用手动一个一个的引入,只管往 api 文件夹里写接口就好了。
2. 使用 require.context(webpack)功能
require 依赖管理
eg.:
.
├── modules
│ ├── adminRouter.js
│ ├── commentsRouter.js
│ ├── contentRouter.js
│ ├── loginRouter.js
│ ├── publicRouter.js
│ ├── userRouter.js
│ └── wxRouter.js
└── routes.js
目标:使用 routes.js
来动态加载 modules
目录中的 .js
的路由文件,其他的比如:vuex、vue-router 等场景,都适合。
上代码
// routes.jsimport combineRoutes from 'koa-combine-routers'// 加载目录中的Router中间件
const moduleFiles = require.context('./modules', true, /\.js$/)// reduce方法去拼接 koa-combine-router所需的数据结构 Object[]
const modules = moduleFiles.keys().reduce((items, path) => {const value = moduleFiles(path)items.push(value.default)return items
}, [])export default combineRoutes(modules)
使用方法
在 index.js 入口文件中:
import router from './routes/routes'app.use(router())
🧀🧀 两个小知识:
- 使用
koa-combine-routers
可以合并多个路由- 使用
require.context
可以动态引入多个文件
五、配置别名
在日常开发中我们引入一些封装好的方法或者模块总是写很长很长的文件路径;比如:
require('../../../../some/very/deep/module')
import format from '../../../../utils/format'
为了告别这种又臭又长的路径我们就可以使用一些解放生产力的方法了(哈哈哈哈,不会偷懒的程序员不是好程序员 🤭)
配置别名有两种方式,一种是 webpack
,另一种是通过 module-alias
包
1. 使用 webpack 的别名功能
官方文档: resolve.alias
配置方式,非常的简单方便:
const path = require('path');module.exports = {//...resolve: {alias: {'@': path.resolve(__dirname, 'src/'),// ...},},
};
2. 使用 module-alias
-
安装依赖
npm i module-alias
-
在
package.json
中添加自定义别名"_moduleAliases": {"@": "./src","@controller": "./src/controller" }
-
在入口文件的顶部引入
module-alias/register
,也就是在 app.js 的顶部引入// src/app.js require('module-alias/register') ...
-
配置成功后,将
/src/api/v1
内的逻辑全部提到src/controller
中,使用别名引入 controller 中文件,修改后如下:// src/api/v1/demo.js import Router from '@koa/router' import DemoController from '@controller/demo/'const router = new Router({ prefix: '/api/v1' })router.get('/demo', DemoController.demo)export default router
// src/api/v1/test.js import Router from '@koa/router' import TestController from '@controller/test'const router = new Router({ prefix: '/api/v1' })router.get('/test', TestController.test)export default router
// src/controller/v1/demo.js class DemoController {constructor() {}async demo(ctx) {ctx.body = {status: 200,message: 'message',data: {file: 'test.js',title: 'webpack 5 构建node应用',content: 'koa + @koa/router + require-dirctory'}}} }export default new DemoController()
// src/controller/v1/test.js class TestController {constructor() {}async test(ctx) {ctx.body = {status: 200,message: 'message',data: {file: 'test.js',title: 'webpack 5 构建node应用',content: 'koa + @koa/router + require-dirctory'}}} }export default new TestController()
-
测试接口
🙋 🙋
如果 git commit 时lint-staged
没有通过:
上述问题是因为 eslint
发现 @controller/*
开头的在 node_modules
中没有找到,所以配置 eslint
就好了:
// src/eslintrc.js
module.exports = {//...rules: {'import/no-unresolved': [2, { ignore: ['^@/', '@controller'] }] // @和@controller 是设置的路径别名}
}
这个问题是由于
constructor
构造函数为空引起的,在eslintrc.js
添加配置即可:
'no-empty-function': ['error', { allow: ['constructors'] }]
希望上面的内容对你的工作学习有所帮助!欢迎各位一键三连哦~
各位 加油!
✨ 原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!