src 文件夹下添加 shims-vue.d.ts 文件,解决 vue 类型报错
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
npm i html-webpack-plugin -D
安装 typescript 及解析插件
npm i typescript ts-loader --save-dev
处理图片url-loader
npm i url-loader -D
处理css,less
cnpm i mini-css-extract-plugin css-loader less less-loader postcss postcss-loader -D
配置如webpack.base.config.js
所示
npm i typescript ts-loader --save-dev
ts-loader
为单进程执行类型检查和转译,因此效率有些慢,可以用多进程方案:即关闭ts-loader
的类型检查,类型检查由 fork-ts-checker-webpack-plugin
插件执行。npm i fork-ts-checker-webpack-plugin --save-dev
合并插件配置webpack-merge
删除build生成的dist包clean-webpack-plugin
跨平台cross-env
创建配置文件,并写入配置(webpack4与webpack5配置基本一样)
创建build文件夹,并依次创建公共配置文件webpack.base.config.js
,开发配置文件webpack.dev.config.js
,生产配置文件webpack.prod.config.js
。
webpack.base.config.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {VueLoaderPlugin} = require('vue-loader')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const path = require('path')
const Dotenv = require('dotenv-webpack')
function getConfigPath(mode) {
return path.resolve(process.cwd(), `.env.${mode}`)
module.exports = {
entry: './src/main.ts',
cache: {
type: 'filesystem'
output: {
filename: 'js/[name].[chunkhash:5].js',
path: path.resolve(__dirname, "../dist")
module: {
rules: [
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: [
loader: 'babel-loader',
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: [
loader: 'ts-loader',
options: {
configFile: path.resolve(__dirname, '../tsconfig.json'),
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'postcss-loader',
'css-loader'
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
use: [
loader: 'url-loader',
options: {
limit: 1024,
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
minChunks: 1,
cacheGroups: {
commons: {
chunks: "all",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
name: "commons"
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
title: 'webpack5+Vue3'
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "style/[name].[hash:8].css",
chunkFilename: "style/[hash:8].css"
new ForkTsCheckerWebpackPlugin(),
webpack.dev.config.js
const { merge } = require('webpack-merge')
const common = require('./webpack.base.config')
module.exports = merge(common,{
mode: 'development',
devtool: 'source-map',
devServer: {
port: 9091,
disableHostCheck: true
webpack.prod.config.js
const { merge } = require('webpack-merge')
const common = require('./webpack.base.config')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = merge(common,{
mode: 'production',
plugins: [
new CleanWebpackPlugin()
package.json
"scripts": {
"server": "cross-env NODE_ENV=development webpack serve --progress --hot --inline --config build/webpack.dev.config.js",
"build": "cross-env NODE_ENV=production webpack --mode=production --config build/webpack.prod.config.js"
4.配置tsconfig.json
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"node"
"paths": {
"@/*": [
"src/*"
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue"
"exclude": [
"node_modules"
到这里配置的项目已经可以运行了,但是还有几个问题: Ts 可以编译为指定版本的 js,那么还需要 babel 么?
tsc 的 target 只转译语法,不集成 polyfill,所以还是得要 babel。
比如把箭头函数转成普通 function、aysnc + await 变成 Promise.then,这是语法转译;
但你运行环境里如果没有 Promise.prototype.finally,那没有就还是没有。
因此我们项目里还是需要 babel.
Webpack 转译 Typescript 现有方案:
方案 | 1 | 2 | 3 |
---|
单进程方案(类型检查和转译在同一个进程) | ts-loader(transplieOnly为false) | awesome-typescript-loader | |
多进程方案 | ts-loader(transplieOnly为true+fork-ts-checker-webpack-plugin) | awesome-typescript-loader+自带的Checkplugin | babel+fork-ts-checker-webpack-plugin |
综合考虑性能和扩展性,目前比较推荐的是 babel+fork-ts-checker-webpack-plugin
方案。
在 babel7 之前,是需要同时使用 ts-loader 和 babel-loader 的,其编译过程 TS > TS 编译器 > JS > Babel > JS 。可见编译了两次js,效率有些低下。但是 babel7 出来之后有了解析 typescript 的能力,有了这一层面的支持,我们就可以只使用 babel,而不用再加一轮 ts 的编译流程了。
在 babel 7 中,我们使用新的 @babel/preset-typescript 预设,结合一些插件便可以解析大部分的 ts 语法。
那么,Babel 是如何处理 TypeScript 代码的呢?
Babel 删除了所有 TypeScript,将其转换为常规的 JavaScript,并继续以它自己的方式处理。删除了 typescript 则不需要进行类型检查,不会有烦人的类型错误提醒,因此编译速度提升.
添加 babel 解析 typescript
# 安装以下依赖 --save-dev
# webpack loader
babel-loader
# babel 核心
@babel/core
# 智能转换成目标运行环境代码
@babel/preset-env
# 解析 typescript 的 babel 预设
@babel/preset-typescript
# polyfill
@babel/plugin-transform-runtime
# 支持 ts 类的写法
@babel/plugin-proposal-class-properties
# 安装以下依赖 --save
@babel/runtime
@babel/runtime-corejs3
"core-js": "^3.11.0"
删除 ts-loader, 添加 babel-loader
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: [
loader: 'babel-loader',
项目根目录添加 babel 配置文件 babel.config.js
module.exports = {
presets: [
'@babel/preset-env',
useBuiltIns: 'usage',
corejs: 3,
'@babel/preset-typescript',
allExtensions: true,
plugins: [
'@babel/plugin-transform-runtime',
corejs: 3,
'@babel/proposal-class-properties'
思考:vue-cli自动创建的脚手架项目可以根据.env.production,.env.development读取配置,应该如何实现?
思路:可以使用dotenv-webpack
,并在执行脚本前指定其运行环境cross-env NODE_ENV=development
,使用process.env.NODE_ENV
获取即可实现。
其他插件:
speed-measure-webpack-plugin 打包速度分析
webpack-bundle-analyzer 打包结果分析
当然还有很多其他的插件。
最后附上代码:地址