一线大厂高级前端编写,前端初中阶面试题,帮助初学者应聘,需要联系微信:javadudu

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

目录

  1. 前言
  2. 环境变量配置
  3. 支持css和less
  4. 添加css3前缀
  5. babel处理js兼容
  6. 复制public文件夹
  7. 支持引用图片文件
  8. 支持引用字体和媒体文件
  9. 参考

一. 前言

20201010日,webpack 升级至 5 版本到现在已经快两年,webpack5版本优化了很多原有的功能比如tree-shaking优化,也新增了很多新特性,具体变动可以看这篇文章阔别两年,webpack 5 正式发布了!

本系列文章将使用最新的webpack5一步一步从零搭建一个完整的vue3+ts开发和打包环境,配置完善构建速度构建结果的优化配置,以及配置完善的代码规范和git提交规范。完整代码已上传到webpack5-vue3-ts

本文是专栏【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目系列第二篇,会详细讲解进阶功能配置:环境变量,支持csslesscss3前缀babel兼容,图片和媒体资源等。

全系列概览

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

全系列文章:

  1. 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》
  2. 《进阶功能配置:环境变量,支持cssless,图片和媒体资源,css3前缀babel兼容》
  3. 优化构建速度:构建耗时分析,持久化缓存,多线程loaderdevtoolloader作用范围等。
  4. 优化化构建结果:构建结果分析,抽离css文件,压缩cssjs文件,hash合理配置,代码分割,tree-shaking清理cssjs,打包生成gzip
  5. 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化cssjs代码等。
  6. 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
  7. git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。

二. 配置环境变量

环境变量按作用来分分两种

  1. 区分是开发模式还是打包构建模式
  2. 区分项目业务环境,开发/测试/预测/正式环境

区分开发模式还是打包构建模式可以用process.env.NODE_ENV,因为很多第三方包里面判断都是采用的这个环境变量。

区分项目接口环境可以自定义一个环境变量process.env.BASE_ENV,设置环境变量可以借助cross-envwebpack.DefinePlugin来设置。

  • cross-env:兼容各系统的设置环境变量的包
  • webpack.DefinePluginwebpack内置的插件,可以为业务代码注入环境变量

安装cross-env

npm i cross-env -D

修改package.jsonscripts脚本字段,删除原先的devbuild,改为

"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字段来给业务代码注入对应的developmentprodction,这里在命令中再次设置环境变量NODE_ENV是为了在webpackbabel的配置文件中访问到。

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-loadercss插入到头部style标签中。

配置完成后再npm run build:dev打包,借助serve -s dist启动后在浏览器查看,可以看到样式生效了。

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

四. 支持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标签了了。

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

五. 处理css3前缀兼容(可省略)

使用了vue3了基本上就不用考虑低版本浏览器了,但有时候需要给css3加前缀,可以学一下,可以借助插件来自动加前缀, postcss-loader就是来给css3加浏览器前缀的,安装依赖:

npm i postcss-loader autoprefixer -D
  • postcss-loader:处理css时自动加前缀
  • autoprefixer:决定添加哪些浏览器前缀到css

修改webpack.base.js, 在解析cssless的规则中添加配置

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

以兼容到ie9chrome35版本为例,配置好后,执行npm run build:dev打包,可以看到打包后的css文件已经加上了ie和谷歌内核的前缀

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

上面可以看到解析cssless有很多重复配置,可以进行提取postcss-loader配置优化一下

postcss.config.jspostcss-loader的配置文件,会自动读取配置,根目录新建postcss.config.js

module.exports = {
  plugins: ['autoprefixer']
}

修改webpack.base.js, 取消postcss-loaderoptions配置

// 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.jsbabel-loaderoptions配置

// 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.htmldist文件下,所以不需要再复制该文件了。

测试一下,在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文件中了。

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

七. 处理图片文件

对于图片文件,webpack4使用file-loaderurl-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位格式的。

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

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位。

【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(二)

八. 处理字体和媒体文件

字体文件和媒体文件这两种资源处理方式和处理图片是一样的,只需要把匹配的路径和打包后放置的路径修改一下就可以了。修改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]', // 文件输出目录和命名
        },
      },
    ]
  }
}

文章系列:

  1. 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》
  2. 《进阶功能配置:环境变量,支持cssless,图片和媒体资源,css3前缀babel兼容》
  3. 优化构建速度:构建耗时分析,持久化缓存,多线程loaderdevtoolloader作用范围等。
  4. 优化化构建结果:构建结果分析,抽离css文件,压缩cssjs文件,hash合理配置,代码分割,tree-shaking清理cssjs,打包生成gzip
  5. 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化cssjs代码等。
  6. 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
  7. git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。

参考

  1. webpack官网
  2. babel官网
  3. 【万字】透过分析 webpack 面试题,构建 webpack5.x 知识体系
  4. Babel 那些事儿
  5. 阔别两年,webpack 5 正式发布了!

原文链接:https://juejin.cn/post/7246777426666864677 作者:Ausra无忧

(0)
上一篇 2023年6月21日 上午10:20
下一篇 2023年6月21日 上午10:31

相关推荐

发表评论

登录后才能评论