前面的流程完成后就可以开发项目了,项目开发完成后需要使用npm run build命令打包,为了正常运行需要稍做修改。

修改Build配置

找到package.json文件,修改build命令

package.json
"scripts": {
"dev": "vite",
"build": "vite build && esno ./build/scripts/postBuild.js",
"preview": "vite preview"
},

创建build/script/postBuild.js文件,内容如下:

build/script/postBuild.js
// #!/usr/bin/env node

import { runBuildConfig } from './buildConf';
import chalk from 'chalk';

import pkg from '../../package.json';

export const runBuild = async () => {
try {
const argvList = process.argv.splice(2);

// Generate configuration file
if (!argvList.includes('disabled-config')) {
await runBuildConfig();
}

console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};
runBuild();

创建build/script/buildConf.js文件,内容如下:

build/script/buildConf.js
/**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
// import { writeFileSync } from 'fs-extra';
import fs from 'fs-extra';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const fsa = require('fs');
import chalk from 'chalk';

import { getRootPath, getEnvConfig } from '../utils';
import { getConfigFileName } from '../getConfigFileName';

import pkg from '../../package.json';

function createConfig(
{
configName,
config,
configFileName = GLOB_CONFIG_FILE_NAME,
} = { configName: '', config: {} }
) {
try {
const windowConf = `window.${configName}`;
// Ensure that the variable will not be modified
const configStr = `${windowConf}=${JSON.stringify(config)};
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
fs.mkdirp(getRootPath(OUTPUT_DIR));
fsa.writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);

console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
} catch (error) {
console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
}
}

export function runBuildConfig() {
const config = getEnvConfig();
const configFileName = getConfigFileName(config);
createConfig({ config, configName: configFileName });
}

创建build/constant.js文件,内容如下:

build/constant.js
/**
* The name of the configuration file entered in the production environment
*/
export const GLOB_CONFIG_FILE_NAME = 'app.config.js';

export const OUTPUT_DIR = 'dist';

创建build/vite/plugin/compress.js文件,内容如下:

build/vite/plugin/compress.js
/** 
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* https://github.com/anncwb/vite-plugin-compression
*/

import compressPlugin from 'vite-plugin-compression';

export function configCompressPlugin(
compress,
deleteOriginFile = false
) {
const compressList = compress.split(',');

const plugins = [];

if (compressList.includes('gzip')) {
plugins.push(
compressPlugin({
ext: '.gz',
deleteOriginFile,
})
);
}
if (compressList.includes('brotli')) {
plugins.push(
compressPlugin({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile,
})
);
}
return plugins;
}

创建build/vite/plugin/html.js文件,内容如下:

build/vite/plugin/html.js
/**
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/

import { createHtmlPlugin } from 'vite-plugin-html';

import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';

export function configHtmlPlugin(env, isBuild) {
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;

const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;

const getAppConfigSrc = () => {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};

const htmlPlugin = createHtmlPlugin({
minify: isBuild,
inject: {
// Inject data into ejs template
data: {
title: VITE_GLOB_APP_TITLE,
},
// Embed the generated app.config.js file
tags: isBuild
? [
{
tag: 'script',
attrs: {
src: getAppConfigSrc(),
},
},
]
: [],
},
});
return htmlPlugin;
}

创建build/vite/plugin/mock.js文件,内容如下:

build/vite/plugin/mock.js
/**

* Mock plugin for development and production.
* https://github.com/anncwb/vite-plugin-mock
*/
import { viteMockServe } from 'vite-plugin-mock';

export function configMockPlugin(isBuild, prodMock) {
return viteMockServe({
ignore: /^\_/,
mockPath: 'mock',
localEnabled: !isBuild,
prodEnabled: isBuild && prodMock,
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer';

setupProdMockServer();
`,

});
}

创建build/vite/plugin/index.js文件,内容如下:

build/vite/plugin/index.js
import Components from 'unplugin-vue-components/vite';
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';

import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

import { configHtmlPlugin } from './html';
import { configMockPlugin } from './mock';
import { configCompressPlugin } from './compress';

export function createVitePlugins(viteEnv, isBuild, prodMock) {
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;

const vitePlugins = [
// have to
vue(),
// have to
vueJsx(),

// 按需引入NaiveUi且自动创建组件声明
Components({
dts: true,
resolvers: [NaiveUiResolver()],
}),
];

// vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));

// vite-plugin-mock
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock));

if (isBuild) {
// rollup-plugin-gzip
vitePlugins.push(
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE)
);
}

return vitePlugins;
}

修改Vite配置

找到vite.config.js文件,修改如下:

vite.config.js
import { loadEnv } from 'vite'
import { resolve } from 'path'
import { wrapperEnv } from './build/utils'
import { createProxy } from './build/vite/proxy'
import { createVitePlugins } from './build/vite/plugin'

function pathResolve(dir) {
return resolve(process.cwd(), '.', dir)
}

export default ({ command, mode }) => {
const root = process.cwd();
const env = loadEnv(mode, root);
const viteEnv = wrapperEnv(env);
const { VITE_PUBLIC_PATH, VITE_PORT, VITE_PROXY, VITE_GLOB_PROD_MOCK } = viteEnv;
const prodMock = VITE_GLOB_PROD_MOCK;
const isBuild = command === 'build';
return {
base: VITE_PUBLIC_PATH,
resolve: {
alias: [
{
find: /\/#\//,
replacement: pathResolve('types') + '/',
},
{
find: '@',
replacement: pathResolve('src') + '/',
},
],
dedupe: ['vue'],
},
plugins: createVitePlugins(viteEnv, isBuild, prodMock),
server: {
host: true,
port: VITE_PORT,
proxy: createProxy(VITE_PROXY)
}
}
}

修改.env.production,内容如下:

.env.production
# 网站根目录,Nginx二级目录需要修改此处
VITE_PUBLIC_PATH = /

# 网站前缀
VITE_BASE_URL = /

# API 接口地址
VITE_GLOB_API_URL =


# 是否启用gzip压缩或brotli压缩
# 可选: gzip | brotli | none
# 如果你需要多种形式,你可以用','来分隔
VITE_BUILD_COMPRESS = 'none'

# 图片上传地址
VITE_GLOB_UPLOAD_URL=

# 图片前缀地址
VITE_GLOB_IMG_URL=

# 接口前缀
VITE_GLOB_API_URL_PREFIX = /api

安装依赖

1、esno

sudo npm i -g esno

2、unplugin-vue-components

npm i unplugin-vue-components -D

3、@vitejs/plugin-vue-jsx

npm i @vitejs/plugin-vue-jsx -D

4、vite-plugin-html

npm i vite-plugin-html -D

5、vite-plugin-mock

npm i vite-plugin-mock -D

6、vite-plugin-compression

npm i vite-plugin-compression -D

7、fs-extra

npm i fs-extra @types/fs-extra -D

8、chalk

npm i chalk -D

构建项目

% npm run build

> hospital-web-admin@0.0.0 build
> vite build && esno ./build/script/postBuild.js

vite v4.5.0 building for production... 17:32:55

WARN <script src="/app.config.js?v=0.0.0-1716802375423"> in "/index.html" can't be bundled without type="module" attribute 17:32:55

✓ 3458 modules transformed. 17:33:01
dist/index.html 0.45 kB │ gzip: 0.31 kB
....

WARN 17:33:01
(!) Some chunks are larger than 500 kBs after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.

✓ built in 6.25s 17:33:01
✨ [hospital-web-admin] - configuration file is build successfully:
dist/app.config.js

✨ [hospital-web-admin] - build successfully!

测试

serve -d dist

出问题

如果你在Windows或者其他环境下出现下图错误:

更换使用 node 命令来执行 js 代码,更改文件如下:

1、修改package.json文件,内容如下:

"scripts": {
"dev": "vite",
"build": "vite build && node ./build/scripts/postBuild.js",
"preview": "vite preview"
},

2、修改build/script/postBuild.js文件,内容如下:

// #!/usr/bin/env node

import { runBuildConfig } from './buildConf.js';
import chalk from 'chalk';

import pkg from '../../package.json' assert { type: "json"};

export const runBuild = async () => {
try {
const argvList = process.argv.splice(2);

// Generate configuration file
if (!argvList.includes('disabled-config')) {
await runBuildConfig();
}

console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};
runBuild();

3、修改build/script/buildConf.js文件,内容如下:

/**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant.js';
// import { writeFileSync } from 'fs-extra';
import fs from 'fs-extra';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const fsa = require('fs');
import chalk from 'chalk';

import { getRootPath, getEnvConfig } from '../utils.js';
import { getConfigFileName } from '../getConfigFileName.js';

import pkg from '../../package.json' assert { type: "json" };

function createConfig(
{
configName,
config,
configFileName = GLOB_CONFIG_FILE_NAME,
} = { configName: '', config: {} }
) {
try {
const windowConf = `window.${configName}`;
// Ensure that the variable will not be modified
const configStr = `${windowConf}=${JSON.stringify(config)};
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
fs.mkdirp(getRootPath(OUTPUT_DIR));
fsa.writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);

console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
} catch (error) {
console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
}
}

export function runBuildConfig() {
const config = getEnvConfig();
const configFileName = getConfigFileName(config);
createConfig({ config, configName: configFileName });
}

4、执行npm run build命令重新打包,或者单独执行下面命令生成app.config.js

node ./build/scripts/postBuild.js