通过调试Vite解决动态配置问题

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

背景

按照vue3文档使用Vite模板的方式初始化了一个项目(如下图1),但是在配置动态配置项时(如下图2)发现不生效。于是使用vscode调试vite的执行过程,发现是vite版本过低,不支持最新文档中的动态配置的特性。

图1

image.png

图2

image.png

开始

首先,使用yarn create vite-app xxx这条命令生成的项目是不带vite默认配置文件的,此时启动vite会使用默认配置。如果想显式配置vite,需要手动在项目根目录创建vite.config.js,如下:

vite.config.js

export default ({ command, mode }) => {
  if (command === 'build') {
    return {
      base: '/github_cleaner/'
    }
  }
}
复制代码

看一下此时的package.json文件,如下

{
  "name": "github_cleaner",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "vue": "^3.0.0-beta.14"
  },
  "devDependencies": {
    "vite": "^0.16.6",
    "@vue/compiler-sfc": "^3.0.0-beta.14"
  }
}
复制代码

然后使用上面的配置执行yarn build,也就是vite build,发现编译出来的index.html文件(如下)并没有按照预期修改部署目录为/github_cleaner/

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/_assets/favicon.59ab3a34.ico" />
  <title>Vite App</title>
<link rel="stylesheet" href="/_assets/style.9e6be708.css">
</head>
<body>
  <div id="app"></div>
  
<script type="module" src="/_assets/index.2f8e9066.js"></script>
</body>
</html>

复制代码

调试

那就调试进vite的执行过程中看一下为什么我们的动态配置没有生效。先试着在vite.config.js中我们导出的函数中增加一处断点,然后启动调试,发现断点处并没有停下。

于是找进node_modulesvite的入口文件打断点,看一下node_modules中的vite包的package.json,如下

{
  "name": "vite",
  "version": "0.16.12",
  "license": "MIT",
  "author": "Evan You",
  "bin": {
    "vite": "bin/vite.js"
  },
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "bin",
    "dist/**/*.js",
    "dist/**/*.d.ts",
    "hmr.d.ts"
  ],
  ...
}
复制代码

因为我们在启动vite执行使用的是vite可执行文件,所以对应bin属性中的bin/vite.js。找到该文件,源码如下

#!/usr/bin/env node
require('../dist/cli')
复制代码

可见对于vite可执行文件的执行,其实就是在使用当前环境中的node执行node_modules/vite/dist/cli.js文件。

其中关键初始化如下

const argv = require('minimist')(process.argv.slice(2));
...
const config_1 = require("./config");
const command = argv._[0];
const defaultMode = command === 'build' ? 'production' : 'development';
复制代码

找到其中第一个立即执行函数源码如下,我们将在这里设置第一个断点

(async () => {
    const { help, h, mode, m, version, v } = argv;
    if (help || h) {
        logHelp();
        return;
    }
    else if (version || v) {
        // noop, already logged
        return;
    }
    const envMode = mode || m || defaultMode;
    const options = await resolveOptions(envMode);
    if (!options.command || options.command === 'serve') {
        runServe(options);
    }
    else if (options.command === 'build') {
        runBuild(options);
    }
    else if (options.command === 'optimize') {
        runOptimize(options);
    }
    else {
        console.error(chalk_1.default.red(`unknown command: ${options.command}`));
        process.exit(1);
    }
})();
复制代码

其中resolveOptions函数很明显是解析vite执行配置的,源码也不多,看一下其中的执行逻辑

async function resolveOptions(mode) {
    // specify env mode
    argv.mode = mode;
    // shorthand for serviceWorker option
    if (argv['sw']) {
        argv.serviceWorker = argv['sw'];
    }
    // map jsx args
    if (argv['jsx-factory']) {
        ;
        (argv.jsx || (argv.jsx = {})).factory = argv['jsx-factory'];
    }
    if (argv['jsx-fragment']) {
        ;
        (argv.jsx || (argv.jsx = {})).fragment = argv['jsx-fragment'];
    }
    // cast xxx=true | false into actual booleans
    Object.keys(argv).forEach((key) => {
        if (argv[key] === 'false') {
            argv[key] = false;
        }
        if (argv[key] === 'true') {
            argv[key] = true;
        }
    });
    // command
    if (argv._[0]) {
        argv.command = argv._[0];
    }
    // normalize root
    // assumes all commands are in the form of `vite [command] [root]`
    if (!argv.root && argv._[1]) {
        argv.root = argv._[1];
    }
    if (argv.root) {
        argv.root = path_1.default.isAbsolute(argv.root) ? argv.root : path_1.default.resolve(argv.root);
    }
    const userConfig = await config_1.resolveConfig(mode, argv.config || argv.c);
    if (userConfig) {
        return {
            ...userConfig,
            ...argv // cli options take higher priority
        };
    }
    return argv;
}
复制代码

const userConfig = await config_1.resolveConfig(mode, argv.config || argv.c);这一行很关键

找到config_1是文件一开始通过const config_1 = require("./config");引入的

这里我们理一下进入resolveConfig之前的逻辑:

  1. 执行vite build
  2. build作为command参数被vite可执行文件解析
  3. 内部设置mode属性,因为在执行vite时没有显式声明mode,因此使用build命令默认对应的production
  4. 带着mode=production参数执行config.js中的resolveConfig函数

由于上面const argv = require('minimist')(process.argv.slice(2));这一句可知,argv其实是解析执行vite时第二个及以后的参数。

由于我们执行vite build时只带了build一个参数,所以可知argv其实是没有任何值的,所以在读取argv.configargv.c时都是undefined

这里的设计是为了执行vite时,可以通过携带参数--config=xxx--c=xxx来传递vite配置

深入配置解析

首先node_modules/vite/dist/config.js中的关于resolveConfig函数的实现如下:

async function resolveConfig(mode, configPath) {
    const start = Date.now();
    const cwd = process.cwd();
    let config;
    let resolvedPath;
    let isTS = false;
    if (configPath) {
        resolvedPath = path_1.default.resolve(cwd, configPath);
    }
    else {
        const jsConfigPath = path_1.default.resolve(cwd, 'vite.config.js');
        if (fs_extra_1.default.existsSync(jsConfigPath)) {
            resolvedPath = jsConfigPath;
        }
        else {
            const tsConfigPath = path_1.default.resolve(cwd, 'vite.config.ts');
            if (fs_extra_1.default.existsSync(tsConfigPath)) {
                isTS = true;
                resolvedPath = tsConfigPath;
            }
        }
    }
    if (!resolvedPath) {
        return;
    }
    try {
        if (!isTS) {
            try {
                config = require(resolvedPath);
            }
            catch (e) {
                if (!/Cannot use import statement|Unexpected token 'export'/.test(e.message)) {
                    throw e;
                }
            }
        }
        if (!config) {
            // 2. if we reach here, the file is ts or using es import syntax.
            // transpile es import syntax to require syntax using rollup.
            const rollup = require('rollup');
            const esbuilPlugin = await buildPluginEsbuild_1.createEsbuildPlugin(false, {});
            const bundle = await rollup.rollup({
                external: (id) => (id[0] !== '.' && !path_1.default.isAbsolute(id)) ||
                    id.slice(-5, id.length) === '.json',
                input: resolvedPath,
                treeshake: false,
                plugins: [esbuilPlugin]
            });
            const { output: [{ code }] } = await bundle.generate({
                exports: 'named',
                format: 'cjs'
            });
            config = await loadConfigFromBundledFile(resolvedPath, code);
        }
        // normalize config root to absolute
        if (config.root && !path_1.default.isAbsolute(config.root)) {
            config.root = path_1.default.resolve(path_1.default.dirname(resolvedPath), config.root);
        }
        // resolve plugins
        if (config.plugins) {
            for (const plugin of config.plugins) {
                config = resolvePlugin(config, plugin);
            }
        }
        // load environment variables
        const env = loadEnv(mode, config.root || cwd);
        debug(`env: %O`, env);
        config.env = env;
        debug(`config resolved in ${Date.now() - start}ms`);
        config.__path = resolvedPath;
        return config;
    }
    catch (e) {
        console.error(chalk_1.default.red(`[vite] failed to load config from ${resolvedPath}:`));
        console.error(e);
        process.exit(1);
    }
}
exports.resolveConfig = resolveConfig;
复制代码

我们从源码实现中可以看出,对于vite配置的解析,会先从vite命令行上读取是否有配置文件。如果有,就会使用该配置,否则,会从当前目录读取vite.config.js文件作为配置信息。

如果是本地声明vite.config.js的方式,vite会首先通过commonjs的规范引入该模块。但是由于我们在vite.config.js中是以ESM规范书写的,所以这里会捕获对于该模块引入的报错。

但是vite会兜底处理非commonjs规范的情况,内部使用rollup工具将vite.config.js配置文件编译为commonjs可引入的格式。具体实现如下

async function loadConfigFromBundledFile(fileName, bundledCode) {
    const extension = path_1.default.extname(fileName);
    const defaultLoader = require.extensions[extension];
    require.extensions[extension] = (module, filename) => {
        if (filename === fileName) {
            ;
            module._compile(bundledCode, filename);
        }
        else {
            defaultLoader(module, filename);
        }
    };
    delete require.cache[fileName];
    const raw = require(fileName);
    const config = raw.__esModule ? raw.default : raw;
    require.extensions[extension] = defaultLoader;
    return config;
}
复制代码

对于config的解析到这里就结束了,我们回到cli.js中的执行主流程。在拿到解析后的config,会根据command执行对应目录下的文件,我们执行的是build命令,所以会走入以下逻辑

node_modules/vite/dist/cli.js

async function runBuild(options) {
    try {
        await require('./build').build(options);
        process.exit(0);
    }
    catch (err) {
        console.error(chalk_1.default.red(`[vite] Build errored out.`));
        console.error(err);
        process.exit(1);
    }
}
复制代码

build实现

build的核心完整逻辑如下

/**
 * Bundles the app for production.
 * Returns a Promise containing the build result.
 */
async function build(options) {
    const { root = process.cwd(), base = '/', outDir = path_1.default.resolve(root, 'dist'), assetsDir = '_assets', assetsInlineLimit = 4096, cssCodeSplit = true, alias = {}, resolvers = [], rollupInputOptions = {}, rollupOutputOptions = {}, emitIndex = true, emitAssets = true, write = true, minify = true, silent = false, sourcemap = false, shouldPreload = null, env = {}, mode } = options;
    const isTest = process.env.NODE_ENV === 'test';
    process.env.NODE_ENV = mode;
    const start = Date.now();
    let spinner;
    const msg = 'Building for production...';
    if (!silent) {
        if (process.env.DEBUG || isTest) {
            console.log(msg);
        }
        else {
            spinner = require('ora')(msg + '\n').start();
        }
    }
    const indexPath = path_1.default.resolve(root, 'index.html');
    const publicBasePath = base.replace(/([^/])$/, '$1/'); // ensure ending slash
    const resolvedAssetsPath = path_1.default.join(outDir, assetsDir);
    const resolver = resolver_1.createResolver(root, resolvers, alias);
    const { htmlPlugin, renderIndex } = await buildPluginHtml_1.createBuildHtmlPlugin(root, indexPath, publicBasePath, assetsDir, assetsInlineLimit, resolver, shouldPreload);
    const basePlugins = await createBaseRollupPlugins(root, resolver, options);
    env.NODE_ENV = mode;
    const envReplacements = Object.keys(env).reduce((replacements, key) => {
        replacements[`process.env.${key}`] = JSON.stringify(env[key]);
        return replacements;
    }, {});
    // lazy require rollup so that we don't load it when only using the dev server
    // importing it just for the types
    const rollup = require('rollup').rollup;
    const bundle = await rollup({
        input: path_1.default.resolve(root, 'index.html'),
        preserveEntrySignatures: false,
        treeshake: { moduleSideEffects: 'no-external' },
        onwarn: exports.onRollupWarning,
        ...rollupInputOptions,
        plugins: [
            ...basePlugins,
            ...
        ]
    });
    const { output } = await bundle.generate({
        format: 'es',
        sourcemap,
        entryFileNames: `[name].[hash].js`,
        chunkFileNames: `[name].[hash].js`,
        ...rollupOutputOptions
    });
    spinner && spinner.stop();
    const cssFileName = output.find((a) => a.type === 'asset' && a.fileName.endsWith('.css')).fileName;
    const indexHtml = emitIndex ? renderIndex(output, cssFileName) : '';
    if (write) {
        const cwd = process.cwd();
        const writeFile = async (filepath, content, type) => {
            await fs_extra_1.default.ensureDir(path_1.default.dirname(filepath));
            await fs_extra_1.default.writeFile(filepath, content);
            if (!silent) {
                console.log(`${chalk_1.default.gray(`[write]`)} ${writeColors[type](path_1.default.relative(cwd, filepath))} ${(content.length / 1024).toFixed(2)}kb, brotli: ${(require('brotli-size').sync(content) / 1024).toFixed(2)}kb`);
            }
        };
        await fs_extra_1.default.remove(outDir);
        await fs_extra_1.default.ensureDir(outDir);
        // write js chunks and assets
        for (const chunk of output) {
            if (chunk.type === 'chunk') {
                // write chunk
                const filepath = path_1.default.join(resolvedAssetsPath, chunk.fileName);
                let code = chunk.code;
                if (chunk.map) {
                    code += `\n//# sourceMappingURL=${path_1.default.basename(filepath)}.map`;
                }
                await writeFile(filepath, code, 0 /* JS */);
                if (chunk.map) {
                    await writeFile(filepath + '.map', chunk.map.toString(), 4 /* SOURCE_MAP */);
                }
            }
            else if (emitAssets) {
                // write asset
                const filepath = path_1.default.join(resolvedAssetsPath, chunk.fileName);
                await writeFile(filepath, chunk.source, chunk.fileName.endsWith('.css') ? 1 /* CSS */ : 2 /* ASSET */);
            }
        }
        // write html
        if (indexHtml && emitIndex) {
            await writeFile(path_1.default.join(outDir, 'index.html'), indexHtml, 3 /* HTML */);
        }
        // copy over /public if it exists
        if (emitAssets) {
            const publicDir = path_1.default.resolve(root, 'public');
            if (fs_extra_1.default.existsSync(publicDir)) {
                for (const file of await fs_extra_1.default.readdir(publicDir)) {
                    await fs_extra_1.default.copy(path_1.default.join(publicDir, file), path_1.default.resolve(outDir, file));
                }
            }
        }
    }
    if (!silent) {
        console.log(`Build completed in ${((Date.now() - start) / 1000).toFixed(2)}s.\n`);
    }
    // stop the esbuild service after each build
    esbuildService_1.stopService();
    return {
        assets: output,
        html: indexHtml
    };
}
exports.build = build;
复制代码

可以看出,这里内容仍然是使用rollup进行编译,编译的配置就是刚才解析过的config对象,输出的内容则是dist目录中的静态文件。

整个过程都没有发现有对vite.config.js文件中的配置做函数执行的逻辑,所以我们可以断定至少在当下所看到的逻辑中,是不支持vite.config.js通过导出函数动态配置的。

寻求帮助

那么这种时候,我们应该去哪寻求帮助呢。首先想到是通过谷歌搜索一下网上有没有人遇到类似情况。

image.png

发现和这个有关的答案只有vite官方文档和viteissues,还有stackoverflow中有人问关于如何设置项目根目录的问题。

先说stackoverflow中的这个问答,它遇到的情况和我们不太一样,不是动态配置,而是静态配置为什么不生效。

所以再来看viteissues中有没有类似的提问,发现搜索也无果。

于是迟疑了一会。。。

转机

于是我想到,是不是我们用的vite版本压根就的确不支持动态配置这一特性呢。所以去到vitegithub首页看了一眼vite的最新版本

image.png

v2.4.4 !!!

再看我们项目中的vite版本

"devDependencies": {
    "vite": "^0.16.6"
}
复制代码

0.16.6 ~~~

啊,原来如此。

使用npm info vite查看一下vite包的版本信息

vite@2.4.4 | MIT | deps: 4 | versions: 215
Native-ESM powered web dev build tool
https://github.com/vitejs/vite/tree/main/#readme

bin: vite

dist
.tarball: https://registry.npmjs.org/vite/-/vite-2.4.4.tgz
.shasum: 8c402a07ad45f168f6eb5428bead38f3e4363e47
.integrity: sha512-m1wK6pFJKmaYA6AeZIUXyiAgUAAJzVXhIMYCdZUpCaFMGps0v0IlNJtbmPvkUhVEyautalajmnW5X6NboUPsnw==
.unpackedSize: 17.9 MB

dependencies:
esbuild: ^0.12.8 postcss: ^8.3.6  resolve: ^1.20.0 rollup: ^2.38.5  

maintainers:
- yyx990803 <yyx990803@gmail.com>
- antfu <anthonyfu117@hotmail.com>
- patak <matias.capeletto@gmail.com>

dist-tags:
beta: 2.5.0-beta.1  latest: 2.4.4 
复制代码

最新版本的确已经到v2.4.4了,至此已经基本揭开迷雾了。

更新

使用yarn add vite@2.4.4安装最新版本vite包,再重新执行yarn build

你以为到这里就结束了吗?一开始我也这么以为的emmmm

再报错

更新vite版本后的build,报出如下错误

yarn run v1.22.10
$ vite build
vite v2.4.4 building for production...
✓ 2 modules transformed.
[rollup-plugin-dynamic-import-variables] Unexpected token (1:0)
file: /Users/wangxin/code/temp/github_cleaner/App.vue:1:0
error during build:
SyntaxError: Unexpected token (1:0)
    at Parser.pp$4.raise (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16965:13)
    at Parser.pp.unexpected (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:14473:8)
    at Parser.pp$3.parseExprAtom (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16342:10)
    at Parser.pp$3.parseExprSubscripts (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16137:19)
    at Parser.pp$3.parseMaybeUnary (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16103:17)
    at Parser.pp$3.parseExprOps (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16036:19)
    at Parser.pp$3.parseMaybeConditional (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:16019:19)
    at Parser.pp$3.parseMaybeAssign (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:15987:19)
    at Parser.pp$3.parseExpression (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:15951:19)
    at Parser.pp$1.parseStatement (/Users/wangxin/code/temp/github_cleaner/node_modules/rollup/dist/shared/rollup.js:14663:45)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
复制代码

看不出什么问题,执行yarn dev看一下开发模式下的情况,一开始没什么问题,但当浏览器访问本地页面后,报出如下错误

image.png

不得不说,有React内味了,这种在浏览器报错的方式也许是为了迎合React用户?

看一下控制台报错信息

yarn run v1.22.10
$ vite

  vite v2.4.4 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

  ready in 310ms.

下午11:39:53 [vite] Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. Install @vitejs/plugin-vue to handle .vue files.
  Plugin: vite:import-analysis
  File: /Users/wangxin/code/temp/github_cleaner/App.vue
  3  |      <img alt="Vue logo" src="https://juejin.cn/post/logo.png" />
  4  |      <HelloWorld msg="Hello Vue 3.0 + Vite" />
  5  |    </div>
     |          ^
  6  |  </template>
  7  |  
      at formatError (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:50738:46)
      at TransformContext.error (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:50734:19)
      at TransformContext.transform (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:74194:22)
      at async Object.transform (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:50939:30)
      at async transformRequest (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:66763:29)
      at async viteTransformMiddleware (/Users/wangxin/code/temp/github_cleaner/node_modules/vite/dist/node/chunks/dep-c1a9de64.js:66904:32)
复制代码

和浏览器提示的内容当然是一致的,具体错误意思就是vite在处理模块解析时不认识vue文件的语法。

控制台也给出了相应的解决方案,就是要安装@vitejs/plugin-vue这个vite的插件来解析以.vue为后缀的文件。

那就手动安装这个插件呗

yarn add @vitejs/plugin-vue
复制代码

然后再重新启动项目yarn dev,依然报这个错。后来得知,安装这个插件,还需要在vite.config.js中引入这个插件并初始化,才能让vite知道以何种规则解析以.vue为后缀的文件语法。

这里仅仅安装了插件包而没做其他事情属实有点天真了,不过也因此发现了vite的插件生态。因为vite不单单是为Vue设计的,同时也支持React等其他框架使用,所以这里没有默认支持Vue语法,而是通过插件的方式来支持每种框架的特殊语法。这种思路从较新版本的@vue/cli就开始出现了,如果关注Vue的生态就会发现。

推倒重来

这里我没有选择继续耗下去研究vite插件生态的配置方式,而是转而看了vite官方文档,其内部有着和vue3不同的项目初始方式yarn create vite xxx --template vue

使用上述命令初始化的项目,相比vue3文档中提供的示例,合理更多。其package.json中对于相关包的依赖版本较新,而且默认提供了支持本地以production模式预览的运行命令(这个我在使用vue3方式初始化的项目中执行vite preview也是提示不存在该命令的,也是因为那个模板初始化的项目中vite版本过低导致)

package.json

{
  "name": "github_cleaner2",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "vue": "^3.0.5"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.3.0",
    "@vue/compiler-sfc": "^3.0.5",
    "vite": "^2.4.4"
  }
}
复制代码

另外,该项目默认显式提供了vite.config.js的配置方式及使用插件支持vue语法的配置方式,如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()]
})
复制代码

尾声

至此,我们终于可以顺利使用vite+vue3按条件配置本地运行和编译构建,输出如下:

vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default ({ command, mode }) => {
  if (command === 'build') {
    return defineConfig({
      plugins: [vue()],
      base: '/github_cleaner/'
    })
  }
  return defineConfig({
    plugins: [vue()]
  })
}

复制代码

dist/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/github_cleaner/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
    <script type="module" crossorigin src="/github_cleaner/assets/index.2f5dd7dc.js"></script>
    <link rel="modulepreload" href="/github_cleaner/assets/vendor.e68e32f2.js">
    <link rel="stylesheet" href="/github_cleaner/assets/index.f498eb83.css">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
复制代码

复盘

这一过程中经历了很多探索新技术中的常见问题,所以有必要在此复盘总结一下本次的踩坑经历。

关于Vue生态

Vue的作者 Evan You 近两年早就提到过,他近期的重心会主要放在Vite 上,甚至高于Vue3

知道这个信息很重要,这也是为什么我们使用vite文档中的示例初始化项目会很顺利,而vue3中的方式很明显存在版本老旧的问题。

关于vite

在我们搜索condition config这一条目时,出现的有效搜索结果并不多。可见vite目前的确还处于非常新的状态,使用者遇到的问题和抛出的问题还不够多。

所以这就注定了现在使用vite的人,算是第一批吃螃蟹的人,要具有同vite一同发展、实践和试错的准备。同时也意味着这不一定适合新手使用,因为你遇到问题不一定能在网上找到有效的解决方法。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享