【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)
目录
- 前言
- 环境变量配置
- 支持css和less
- 添加css3前缀
- babel处理js兼容
- 复制public文件夹
- 支持引用图片文件
- 支持引用字体和媒体文件
- 参考
一. 前言
从2020年10月10日,webpack 升级至 5 版本到现在已经快两年,webpack5版本优化了很多原有的功能比如tree-shaking优化,也新增了很多新特性,具体变动可以看这篇文章阔别两年,webpack 5 正式发布了!。
本系列文章将使用最新的webpack5一步一步从零搭建一个完整的vue3+ts开发和打包环境,配置完善构建速度和构建结果的优化配置,以及配置完善的代码规范和git提交规范。完整代码已上传到webpack5-vue3-ts。
本文是专栏【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目系列第二篇,会详细讲解进阶功能配置:环境变量,支持css,less,css3前缀,babel兼容,图片和媒体资源等。
全系列概览
全系列文章:
- 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》。
- 《进阶功能配置:环境变量,支持css,less,图片和媒体资源,css3前缀,babel兼容》。
- 优化构建速度:构建耗时分析,持久化缓存,多线程loader,devtool,loader作用范围等。
- 优化化构建结果:构建结果分析,抽离css文件,压缩css,js文件,hash合理配置,代码分割,tree-shaking清理css和js,打包生成gzip。
- 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化css和js代码等。
- 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
- git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。
二. 配置环境变量
环境变量按作用来分分两种
- 区分是开发模式还是打包构建模式
- 区分项目业务环境,开发/测试/预测/正式环境
区分开发模式还是打包构建模式可以用process.env.NODE_ENV,因为很多第三方包里面判断都是采用的这个环境变量。
区分项目接口环境可以自定义一个环境变量process.env.BASE_ENV,设置环境变量可以借助cross-env和webpack.DefinePlugin来设置。
- cross-env:兼容各系统的设置环境变量的包
- webpack.DefinePlugin:webpack内置的插件,可以为业务代码注入环境变量
安装cross-env
npm i cross-env -D
修改package.json的scripts脚本字段,删除原先的dev和build,改为
"scripts": {
"dev:dev": "cross-env NODE_ENV=development BASE_ENV=development webpack-dev-server -c build/webpack.dev.js",
"dev:test": "cross-env NODE_ENV=development BASE_ENV=test webpack-dev-server -c build/webpack.dev.js",
"dev:pre": "cross-env NODE_ENV=development BASE_ENV=pre webpack-dev-server -c build/webpack.dev.js",
"dev:prod": "cross-env NODE_ENV=development BASE_ENV=production webpack-dev-server -c build/webpack.dev.js",
"build:dev": "cross-env NODE_ENV=production BASE_ENV=development webpack -c build/webpack.prod.js",
"build:test": "cross-env NODE_ENV=production BASE_ENV=test webpack -c build/webpack.prod.js",
"build:pre": "cross-env NODE_ENV=production BASE_ENV=pre webpack -c build/webpack.prod.js",
"build:prod": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.prod.js"
}
dev开头是开发模式,build开头是打包模式,冒号后面对应的dev/test/pre/prod是对应的业务环境的开发/测试/预测/正式环境。
process.env.NODE_ENV环境变量webpack会自动根据设置的mode字段来给业务代码注入对应的development和prodction,这里在命令中再次设置环境变量NODE_ENV是为了在webpack和babel的配置文件中访问到。
在webpack.base.js中打印一下设置的环境变量
// webpack.base.js
// ...
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('BASE_ENV', process.env.BASE_ENV)
执行npm run build:dev,可以看到打印的信息
// NODE_ENV production
// BASE_ENV development
当前是打包模式,业务环境是开发环境,这里需要把process.env.BASE_ENV注入到业务代码里面,就可以通过该环境变量设置对应环境的接口地址和其他数据,要借助webpack.DefinePlugin插件。
修改webpack.base.js
// webpack.base.js
// ...
const webpack = require('webpack')
module.export = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
'process.env.BASE_ENV': JSON.stringify(process.env.BASE_ENV),
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]
}
配置后会把值注入到业务代码里面去,webpack解析代码匹配到process.env.BASE_ENV,就会设置到对应的值。测试一下,在src/index.vue打印一下两个环境变量
// src/index.vue
// ...
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('BASE_ENV', process.env.BASE_ENV)
执行npm run dev:test,可以在浏览器控制台看到打印的信息
// NODE_ENV development
// BASE_ENV test
当前是开发模式,业务环境是测试环境。
三 处理css和less文件
在src下新增app.css
h2 {
color: red;
transform: translateY(100px);
}
在src/App.vue中引入app.css
<template>
<h2>webpack5-vue3-ts</h2>
</template>
<script setup lang="ts">
import './app.css'
</script>
<style scoped>
</style>
执行打包命令npm run build:dev,会发现有报错, 因为webpack默认只认识js,是不识别css文件的,需要使用loader来解析css, 安装依赖
npm i style-loader css-loader -D
- style-loader: 把解析后的css代码从js中抽离,放到头部的style标签中(在运行时做的)
- css-loader: 解析css文件代码
因为解析css的配置开发和打包环境都会用到,所以加在公共配置webpack.base.js中
// webpack.base.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.css$/, //匹配 css 文件
use: ['style-loader','css-loader']
}
]
},
// ...
}
上面提到过,loader执行顺序是从右往左,从下往上的,匹配到css文件后先用css-loader解析css, 最后借助style-loader把css插入到头部style标签中。
配置完成后再npm run build:dev打包,借助serve -s dist启动后在浏览器查看,可以看到样式生效了。
四. 支持less或scss
项目开发中,为了更好的提升开发体验,一般会使用css超集less或者scss,对于这些超集也需要对应的loader来识别解析。以less为例,需要安装依赖:
npm i less-loader less -D
- less-loader: 解析less文件代码,把less编译为css
- less: less核心
实现支持less也很简单,只需要在rules中添加less文件解析,遇到less文件,使用less-loader解析为css,再进行css解析流程,修改webpack.base.js:
// webpack.base.js
module.exports = {
// ...
module: {
// ...
rules: [
// ...
{
test: /.(css|less)$/, //匹配 css和less 文件
use: ['style-loader','css-loader', 'less-loader']
}
]
},
// ...
}
测试一下,新增src/app.less
#root {
h2 {
font-size: 20px;
}
}
在App.vue中引入app.less,执行npm run build:dev打包,借助serve -s dist启动项目,可以看到less文件编写的样式编译css后也插入到style标签了了。
五. 处理css3前缀兼容(可省略)
使用了vue3了基本上就不用考虑低版本浏览器了,但有时候需要给css3加前缀,可以学一下,可以借助插件来自动加前缀, postcss-loader就是来给css3加浏览器前缀的,安装依赖:
npm i postcss-loader autoprefixer -D
- postcss-loader:处理css时自动加前缀
- autoprefixer:决定添加哪些浏览器前缀到css中
修改webpack.base.js, 在解析css和less的规则中添加配置
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /.(css|less)$/, //匹配 css和less 文件
use: [
'style-loader',
'css-loader',
// 新增
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer']
}
}
},
'less-loader'
]
}
]
},
// ...
}
配置完成后,需要有一份要兼容浏览器的清单,让postcss-loader知道要加哪些浏览器的前缀,在根目录创建 .browserslistrc文件
IE 9 # 兼容IE 9
chrome 35 # 兼容chrome 35
以兼容到ie9和chrome35版本为例,配置好后,执行npm run build:dev打包,可以看到打包后的css文件已经加上了ie和谷歌内核的前缀
上面可以看到解析css和less有很多重复配置,可以进行提取postcss-loader配置优化一下
postcss.config.js是postcss-loader的配置文件,会自动读取配置,根目录新建postcss.config.js:
module.exports = {
plugins: ['autoprefixer']
}
修改webpack.base.js, 取消postcss-loader的options配置
// webpack.base.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(css|less)$/, //匹配 css和less 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
},
// ...
}
提取postcss-loader配置后,再次打包,可以看到依然可以解析css, less文件, css3对应前缀依然存在。
五. babel预设处理js兼容
现在js不断新增很多方便好用的标准语法来方便开发,甚至还有非标准语法比如装饰器,都极大的提升了代码可读性和开发效率。但前者标准语法很多低版本浏览器不支持,后者非标准语法所有的浏览器都不支持。需要把最新的标准语法转换为低版本语法,把非标准语法转换为标准语法才能让浏览器识别解析,而babel就是来做这件事的,这里只讲配置,更详细的可以看Babel 那些事儿。
安装依赖
npm i babel-loader @babel/core @babel/preset-env core-js -D
- babel-loader: 使用 babel 加载最新js代码并将其转换为 ES5(上面已经安装过)
- @babel/corer: babel 编译的核心包
- @babel/preset-env: babel 编译的预设,可以转换目前最新的js标准语法
- core-js: 使用低版本js语法模拟高版本的库,也就是垫片
修改webpack.base.js
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'babel-loader',
options: {
// 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法
presets: [
[
"@babel/preset-env",
{
// 设置兼容目标浏览器版本,这里可以不写,babel-loader会自动寻找上面配置好的文件.browserslistrc
// "targets": {
// "chrome": 35,
// "ie": 9
// },
"useBuiltIns": "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加
"corejs": 3, // 配置使用core-js低版本
}
],
[
'@babel/preset-typescript',
{
allExtensions: true, //支持所有文件扩展名,很关键
},
]
]
}
}
}
]
}
}
此时再打包就会把语法转换为对应浏览器兼容的语法了。
为了避免webpack配置文件过于庞大,可以把babel-loader的配置抽离出来, 新建babel.config.js文件,使用js作为配置文件,是因为可以访问到process.env.NODE_ENV环境变量来区分是开发还是打包模式。
// babel.config.js
module.exports = {
// 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法
"presets": [
[
"@babel/preset-env",
{
// 设置兼容目标浏览器版本,这里可以不写,babel-loader会自动寻找上面配置好的文件.browserslistrc
// "targets": {
// "chrome": 35,
// "ie": 9
// },
"useBuiltIns": "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加
"corejs": 3 // 配置使用core-js使用的版本
}
],
[
"@babel/preset-typescript",
{
allExtensions: true, //支持所有文件扩展名,很关键
},
],
]
}
移除webpack.base.js中babel-loader的options配置
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.ts$/,
use: 'babel-loader'
},
// 如果使用到了js,可以把js文件配置加上
// {
// test: /.(js)$/,
// use: 'babel-loader'
// }
// ...
]
}
}
六. 复制public文件夹
一般public文件夹都会放一些静态资源,可以直接根据绝对路径引入,比如图片,css,js文件等,不需要webpack进行解析,只需要打包的时候把public下内容复制到构建出口文件夹中,可以借助copy-webpack-plugin插件,安装依赖
npm i copy-webpack-plugin -D
开发环境已经在devServer中配置了static托管了public文件夹,在开发环境使用绝对路径可以访问到public下的文件,但打包构建时不做处理会访问不到,所以现在需要在打包配置文件webpack.prod.js中新增copy插件配置。
// webpack.prod.js
// ..
const path = require('path')
const CopyPlugin = require('copy-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
plugins: [
// 复制文件插件
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'), // 复制public下文件
to: path.resolve(__dirname, '../dist'), // 复制到dist目录中
filter: source => {
return !source.includes('index.html') // 忽略index.html
}
},
],
}),
]
})
在上面的配置中,忽略了index.html,因为html-webpack-plugin会以public下的index.html为模板生成一个index.html到dist文件下,所以不需要再复制该文件了。
测试一下,在public中新增一个favicon.ico图标文件,在index.html中引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 绝对路径引入图标文件 -->
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5-vue3-ts</title>
</head>
<body>
<!-- 容器节点 -->
<div id="root"></div>
</body>
</html>
再执行npm run build:dev打包,就可以看到public下的favicon.ico图标文件被复制到dist文件中了。
七. 处理图片文件
对于图片文件,webpack4使用file-loader和url-loader来处理的,但webpack5不使用这两个loader了,而是采用自带的asset-module来处理
修改webpack.base.js,添加图片解析配置
module.exports = {
module: {
rules: [
// ...
{
test:/.(png|jpg|jpeg|gif|svg)$/, // 匹配图片文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/images/[name][ext]', // 文件输出目录和命名
},
},
]
}
}
测试一下,准备一张小于10kb的图片和大于10kb的图片,放在src/assets/imgs目录下, 修改App.vue:
<template>
<img :src="smallImg" alt="小于10kb的图片" />
<img :src="bigImg" alt="大于于10kb的图片" />
</template>
<script setup lang="ts">
import smallImg from './assets/imgs/5kb.png'
import bigImg from './assets/imgs/22kb.png'
import './app.css'
import './app.less'
</script>
<style scoped>
</style>
这个时候在引入图片的地方会报:找不到模块“./assets/imgs/22kb.png”或其相应的类型声明,需要添加一个图片的声明文件
新增src/images.d.ts文件,添加内容
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.less'
declare module '*.css'
declare module '*.vue'
添加图片声明文件后,就可以正常引入图片了, 然后执行npm run build:dev打包,借助serve -s dist查看效果,可以看到可以正常解析图片了,并且小于10kb的图片被转成了base64位格式的。
css中的背景图片一样也可以解析,修改app.vue。
<template>
<img :src="smallImg" alt="小于10kb的图片" />
<img :src="bigImg" alt="大于于10kb的图片" />
<!-- 小图片背景容器 -->
<div className='smallImg'></div>
<!-- 大图片背景容器 -->
<div className='bigImg'></div>
</template>
<script setup lang="ts">
import smallImg from './assets/imgs/5kb.png'
import bigImg from './assets/imgs/22kb.png'
import './app.css'
import './app.less'
</script>
<style scoped>
</style>
修改app.less
// app.less
#root {
.smallImg {
width: 69px;
height: 75px;
background: url('./assets/imgs/5kb.png') no-repeat;
}
.bigImg {
width: 232px;
height: 154px;
background: url('./assets/imgs/22kb.png') no-repeat;
}
}
可以看到背景图片也一样可以识别,小于10kb转为base64位。
八. 处理字体和媒体文件
字体文件和媒体文件这两种资源处理方式和处理图片是一样的,只需要把匹配的路径和打包后放置的路径修改一下就可以了。修改webpack.base.js文件:
// webpack.base.js
module.exports = {
module: {
rules: [
// ...
{
test:/.(woff2?|eot|ttf|otf)$/, // 匹配字体图标文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/fonts/[name][ext]', // 文件输出目录和命名
},
},
{
test:/.(mp4|webm|ogg|mp3|wav|flac|aac)$/, // 匹配媒体文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/media/[name][ext]', // 文件输出目录和命名
},
},
]
}
}
文章系列:
- 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》。
- 《进阶功能配置:环境变量,支持css,less,图片和媒体资源,css3前缀,babel兼容》。
- 优化构建速度:构建耗时分析,持久化缓存,多线程loader,devtool,loader作用范围等。
- 优化化构建结果:构建结果分析,抽离css文件,压缩css,js文件,hash合理配置,代码分割,tree-shaking清理css和js,打包生成gzip。
- 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化css和js代码等。
- 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
- git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。
参考
转载自:https://juejin.cn/post/7246777426666864677