Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<%= BASE_URL %> 是哪来的? #112

Open
yubaoquan opened this issue May 9, 2024 · 0 comments
Open

<%= BASE_URL %> 是哪来的? #112

yubaoquan opened this issue May 9, 2024 · 0 comments

Comments

@yubaoquan
Copy link
Owner

vue-cli 生成的工程代码中, public/index.html 通常会是这样:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

注意这两句:

<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>

我们知道, <%= %> 中包裹的是 HtmlWebpackPlugin 注入进来的变量. 那么BASE_URL htmlWebpackPlugin.options.title 这两个变量是在哪里配置的呢?

请看 HtmlWebpackPlugin 生成模板中变量的源码 如下

getTemplateParameters(compilation, assetsInformationByGroups, assetTags) {
    const templateParameters = this.options.templateParameters;

    if (templateParameters === false) {
      return Promise.resolve({});
    }

    if (
      typeof templateParameters !== "function" &&
      typeof templateParameters !== "object"
    ) {
      throw new Error(
        "templateParameters has to be either a function or an object",
      );
    }

    const templateParameterFunction =
      typeof templateParameters === "function"
        ? // A custom function can overwrite the entire template parameter preparation
          templateParameters
        : // If the template parameters is an object merge it with the default values
          (compilation, assetsInformationByGroups, assetTags, options) =>
            Object.assign(
              {},
              templateParametersGenerator(
                compilation,
                assetsInformationByGroups,
                assetTags,
                options,
              ),
              templateParameters,
            );
    const preparedAssetTags = {
      headTags: this.prepareAssetTagGroupForRendering(assetTags.headTags),
      bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags),
    };
    return Promise.resolve().then(() =>
      templateParameterFunction(
        compilation,
        assetsInformationByGroups,
        preparedAssetTags,
        this.options,
      ),
    );
  }

在生成变量时, 它会找到配置项中的 templateParameters, 如果这个东西是对象, 那么就在生成一堆默认变量后把这个对象合并到生成结果中, 如果这个东西是一个函数, 那么就直接调用这个函数;

如果用户没有配置 templateParameters 呢? 走默认配置, 如下

const defaultOptions = {
      template: "auto",
      templateContent: false,
      templateParameters: templateParametersGenerator, // 看这里
      filename: "index.html",
      publicPath:
        this.userOptions.publicPath === undefined
          ? "auto"
          : this.userOptions.publicPath,
      hash: false,
      inject: this.userOptions.scriptLoading === "blocking" ? "body" : "head",
      scriptLoading: "defer",
      compile: true,
      favicon: false,
      minify: "auto",
      cache: true,
      showErrors: true,
      chunks: "all",
      excludeChunks: [],
      chunksSortMode: "auto",
      meta: {},
      base: false,
      title: "Webpack App",
      xhtml: false,
    };

默认配置是什么呢? 如下:

function templateParametersGenerator(compilation, assets, assetTags, options) {
  return {
    compilation: compilation,
    webpackConfig: compilation.options,
    htmlWebpackPlugin: {
      tags: assetTags,
      files: assets,
      options: options,
    },
  };
}

所以默认情况下, 用户可以从模板中获取到这三个变量: compilation, webpackConfig, htmlWebpackPlugin
如果你传了自定义的 templateParameters, 那么在 html 模板中就只能拿到你的 templateParameters 返回的内容

但是 BASE_URL 又是哪里来的呢?

根据 AI 的回答, 这个变量的值是在 vue.config.js 文件中的 publicPath 配置项中设置的。

也就是说, vue-cli 内部偷偷改了 HtmlWebpackPlugin 的 templateParameters 配置, 将 BASE_URL 这个变量合并进了 HtmlWebpackPlugin 默认发回的变量列表中.

请看 vue-cli 相关代码 如下:

    // HTML plugin
    const resolveClientEnv = require('../util/resolveClientEnv')

    const htmlOptions = {
      title: api.service.pkg.name,
      scriptLoading: 'defer',
      templateParameters: (compilation, assets, assetTags, pluginOptions) => {
        // enhance html-webpack-plugin's built in template params
        return Object.assign({
          compilation: compilation,
          webpackConfig: compilation.options,
          htmlWebpackPlugin: {
            tags: assetTags,
            files: assets,
            options: pluginOptions
          }
        }, resolveClientEnv(options, true /* raw */))
      }
    }

可以看到, vue-cli 把 resolveClientEnv(options, true /* raw */)) 方法的返回值合并进了默认变量列表中

再来看 resolveClientEnv 的代码:

const getBaseUrl = require('./getBaseUrl')
const prefixRE = /^VUE_APP_/

module.exports = function resolveClientEnv (options, raw) {
  const env = {}
  Object.keys(process.env).forEach(key => {
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = process.env[key]
    }
  })
  env.BASE_URL = getBaseUrl(options) // 看这里

  if (raw) {
    return env
  }

  for (const key in env) {
    env[key] = JSON.stringify(env[key])
  }
  return {
    'process.env': env
  }
}

现在真相大白了, 在 vue-cli 生成的项目中, 如果对 HtmlWebpackPlugin 什么都不配, 那么在 index.html 中可用的变量包括 HtmlWebpackPlugin 默认给出的一些变量(包含 htmlWebpackPlugin.options.xxx)和 vue-cli 给出的一些变量(包括 BASE_URL).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant