在Vue中动态引入图片,为什么要用require

案例1(Vue3 + webpack)编译<img :src="'../assets' + '/11.png'" />的结果:

js 复制代码
<img :src="'../assets/' + imageName" />

编译后的render函数:

js 复制代码
const _hoisted_1 = ["src"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
    return ((0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)("img", {
        src: '../assets/' + $setup.imageName
    }, null, 8 /* PROPS */, _hoisted_1));
}

案例2(vue3 + webpack)编译<img src="../assets/11.jpg" />的结果:

js 复制代码
<img src="../assets/11.jpg" />

编译后的render函数:

js 复制代码
__webpack_require__.r(__webpack_exports__);
/* harmony export */
__webpack_require__.d(__webpack_exports__, {
    /* harmony export */
    render: ()=>(/* binding */
    render)/* harmony export */
});
/* harmony import */
var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */
"./node_modules/.pnpm/vue@3.4.25_typescript@5.4.5/node_modules/vue/dist/vue.runtime.esm-bundler.js");
/* harmony import */
var _assets_11_jpg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../assets/11.jpg */
"./src/assets/11.jpg");

const _withScopeId = n=>((0,
vue__WEBPACK_IMPORTED_MODULE_0__.pushScopeId)("data-v-103335b0"),
n = n(),
(0,
vue__WEBPACK_IMPORTED_MODULE_0__.popScopeId)(),
n);
const _hoisted_1 = {
    src: _assets_11_jpg__WEBPACK_IMPORTED_MODULE_1__
};
function render(_ctx, _cache, $props, $setup, $data, $options) {
    return ((0,
    vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(),
    (0,
    vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)("img", _hoisted_1));
}

案例3 (Vue3 + Vite)编译<img src="../assets/11.png" />的结果:

Vue的编译过程

  1. webpack/lib/NormalModule.js

_doBuild------->processResult----->runLoaders

  1. loader-runner/lib/LoaderRunner.js
  2. vue-loader/dist/index.js
  3. @vue/compiler-sfc/dist/compiler-sfc.cjs.js

compileTemplate------>doCompileTemplate

  1. @vue/compiler-dom/dist/compiler-dom.cjs.js

compile

  1. @vue/compiler-core/dist/compiler-core.cjs.js

baseCompile(parse--->transform---->generate) generate(genNode)

vue处理<img src="../assets/11.jpg" />的逻辑源码如下

@vue/compiler-sfc/dist/compiler-sfc.cjs.js

js 复制代码
const defaultAssetUrlOptions = {
  base: null,
  includeAbsolute: false,
  tags: {
    video: ["src", "poster"],
    source: ["src"],
    img: ["src"],
    image: ["xlink:href", "href"],
    use: ["xlink:href", "href"]
  }
};

export enum NodeTypes {
  ROOT,
  ELEMENT,
  TEXT,
  COMMENT,
  SIMPLE_EXPRESSION,
  INTERPOLATION,
  ATTRIBUTE, // 6 属性
  DIRECTIVE, // 7 指令
  // containers
  COMPOUND_EXPRESSION,
  IF,
  IF_BRANCH,
  FOR,
  TEXT_CALL,
  // codegen
  VNODE_CALL,
  JS_CALL_EXPRESSION,
  JS_OBJECT_EXPRESSION,
  JS_PROPERTY,
  JS_ARRAY_EXPRESSION,
  JS_FUNCTION_EXPRESSION,
  JS_CONDITIONAL_EXPRESSION,
  JS_CACHE_EXPRESSION,

  // ssr codegen
  JS_BLOCK_STATEMENT,
  JS_TEMPLATE_LITERAL,
  JS_IF_STATEMENT,
  JS_ASSIGNMENT_EXPRESSION,
  JS_SEQUENCE_EXPRESSION,
  JS_RETURN_STATEMENT,
}


/**
 * A `@vue/compiler-core` plugin that transforms relative asset urls into
 * either imports or absolute urls.
 *
 * ``` js
 * // Before
 * createVNode('img', { src: './logo.png' })
 *
 * // After
 * import _imports_0 from './logo.png'
 * createVNode('img', { src: _imports_0 })
 * ```
 */
export const transformAssetUrl: NodeTransform = (
  node,
  context,
  options: AssetURLOptions = defaultAssetUrlOptions,
) => {
  if (node.type === NodeTypes.ELEMENT) {
    if (!node.props.length) {
      return
    }

    const tags = options.tags || defaultAssetUrlOptions.tags
    const attrs = tags[node.tag]
    const wildCardAttrs = tags['*']
    if (!attrs && !wildCardAttrs) {
      return
    }

    const assetAttrs = (attrs || []).concat(wildCardAttrs || [])
    node.props.forEach((attr, index) => {
      if (
        attr.type !== NodeTypes.ATTRIBUTE || // 当前节点的属性类型不是属性
        !assetAttrs.includes(attr.name) ||
        !attr.value ||
        isExternalUrl(attr.value.content) ||
        isDataUrl(attr.value.content) ||
        attr.value.content[0] === '#' ||
        (!options.includeAbsolute && !isRelativeUrl(attr.value.content))
      ) {
        return
      }

      const url = parseUrl(attr.value.content)
      if (options.base && attr.value.content[0] === '.') {
        // explicit base - directly rewrite relative urls into absolute url
        // to avoid generating extra imports
        // Allow for full hostnames provided in options.base
        const base = parseUrl(options.base)
        const protocol = base.protocol || ''
        const host = base.host ? protocol + '//' + base.host : ''
        const basePath = base.path || '/'

        // when packaged in the browser, path will be using the posix-
        // only version provided by rollup-plugin-node-builtins.
        attr.value.content =
          host +
          (path.posix || path).join(basePath, url.path + (url.hash || ''))
        return
      }

      // otherwise, transform the url into an import.
      // this assumes a bundler will resolve the import into the correct
      // absolute url (e.g. webpack file-loader)
      const exp = getImportsExpressionExp(url.path, url.hash, attr.loc, context)
      node.props[index] = {
        type: NodeTypes.DIRECTIVE,
        name: 'bind',
        arg: createSimpleExpression(attr.name, true, attr.loc),
        exp,
        modifiers: [],
        loc: attr.loc,
      }
    })
  }
}

调用栈如下:

Vue处理<img src="../assets/11.png" />的官方文档:

vue-loader.vuejs.org/zh/guide/as...

从源码发现,节点属性类型不为NodeTypes.ELEMENT(6),则不会按上述逻辑处理。<img :src="" /> :src是指令(7),而不是属性(6),所以被return了

webpack5处理图像

import MyImage from './my-image.png' 将会处理图像,将其添加到 output 目录,并且 MyImage 变量将包含该图像在处理后的最终的 url。

js 复制代码
<img :src="'../assets/' + imageName" />
<img :src="require('../assets/' + imageName)" />
<img src="../assets/11.jpg" />

最后的html如下:

相关推荐
u0111026751 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
雪芽蓝域zzs6 小时前
第 7 节:多边形点击弹窗 + 清空绘制按钮
vue.js
溪语流沙7 小时前
【Web全栈进阶】Composition API与组件通信:props深入、emit、插槽
前端·css·vue.js
顽疲10 小时前
Java vue 养老系统源码详解:入住核算单 live-check 缴费入账与预交金收据实践
java·vue.js·python·java 养老·springboot 养老·vue养老
志尊宝11 小时前
Vue3 零基础每日笔记(088·完结):从零到一——一个完整 Vue3 项目的成长复盘
前端·javascript·vue.js·笔记·ecmascript·#vue.js
桔筐11 小时前
Vue中DOM的data-*自定义属性(dataset)与Vue data响应式变量区别详解
前端·javascript·vue.js
用户17335980753713 小时前
纯前端 PDF 按书签拆分避坑指南:拆分后章节对不上?
前端·javascript·vue.js
雪芽蓝域zzs14 小时前
Webstorm打开uniapp uvue文件报红处理方案
vue.js
撸串-研究生14 小时前
面向景区的综合服务管理平台设计——以稻城亚丁景区为例【附源码】
java·开发语言·vue.js·前后端分离·稻城亚丁景区
志尊宝1 天前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js