最近修改一些老项目,好多组件里面引入图片的方式不太一样,总感觉自己没有好好总结过,今天有时间参考了几篇帖子,就总结一下;

在总结之前看了一下vue-cli的文档,突然感觉之前可能是我忽略它了基本没有看过,官方文档这块写的清清楚楚,还是应该多看文档,天天看掘金、简书这些碎片化知识也不太好;

Vue中静态资源引入机制


Vue.js关于静态资源的官方文档

静态资源可以通过两种方式进行处理:

  1. 在 JavaScript 被导入或在 template/CSS 中被引用。
    这类引用会被 webpack 处理。诸如<img src="...">background: url(...)CSS @import的资源, 都会被解析为一个模块依赖。
    例如,url(./image.png)会被翻译为require(’./image.png’)

  2. 放置在public目录下或通过绝对路径被引用。
    这类资源将会直接被拷贝,而不会经过 webpack 的处理,你需要通过绝对路径来引用它们。

webpack中的require解析

首先明确一点,在项目中的webpack.config.js等项目配置文件中使用的require属于nodejs范畴,而进入index.js后,加载的组件中的require都属于webpack的解析范畴。

先聊聊webpack中require的用法。

// 代码1 报错
let url = "@/assets/images/carousel/logo.svg"
require(url)    // require单纯传了一个变量

// 代码2 正确
let url = "logo.svg"
require("@/assets/images/carousel/"+url); // require里面虽然是变量但是增加了前缀,让计算机更快找到

页面修改webpack流程:

webpack进行编译
等待编译完
进行工程打包
打包正确
热加载运行
刷新页面

如果require中传入的是个变量,它有可能是计算机系统中的任何目录下的任何文件,那么在打包静态资源时它有可能会将你的电脑整个磁盘遍历一遍(它很傻); 如上代码1只传了个变量。
因此需要给出在大致路径(如上代码2),这样才能精确的将那个路径下的对应文件打包,然后在代码运行时,直接用对应文件名生成正则匹配(因为打包后的文件,可能有hash值。不能直接查文件名),找到后,加载到代码中。所以,尽可能详细的指定require中的路径,然后拼接变量。

webpack将项目中的静态资源编译打包后,生成的路径已经不是原来的那个路径了。

src/assets/image/logo.png
// 编译后可能变成 
dist/public/image/logo.1d997ea3.png

通过require("src/assets/image/logo.png"),
会自动找到并加载 dist/public/image/logo.1d997ea3.png文件

关于打包完不是原来的路径那是 file-loader 的作用,这里不细说可以看一下,file-loader和url-loader的讲解。

解析介绍

这里主要用到 vue-loader,vue-loader 在编译单文件组件中的 块时,它也会将所有遇到的资源 URL 转换为 webpack 模块请求。(这样我们就没必要手动调用require了,而是交给vue-loader处理了)
vue-loader默认可以处理的标签/特性的组合如下: (不包括style)

// 面对下面的标签组合,vue-loader会自动进行资源url的转换。
{
  video: ['src', 'poster'],
  img: 'src',   //即img元素上的src属性
  source: 'src',  //source元素上的src属性
  image: 'xlink:href'
}

转换规则(官方文档有介绍)

部分的路径处理

  1. 如果路径是绝对路径,会被原样保留。如 /src/assets/image/login/title.png
//代码
<template>
   <img src="/src/assets/image/login/title.png" alt="">
</template>

//渲染后html页面
<img data-v-70c98a68="" src="/src/assets/image/login/title.png" alt="">
// 当然这个图片是无法展示的,因为编译后title.png已不在src/assets/image/login下了 

这个图片是无法展示的,因为编译后title.png已不在src/assets/image/login下了 ,关于public文件夹在下文会有介绍

  1. 如果路径以 . 开头,将会被看作相对的模块依赖。如 ./title.png
//代码
<img src="./titlea.png" alt="">

//渲染后html页面
<img data-v-70c98a68="" src="/static/img/titlea.1e9fa570.png" alt="">

❌错误的引入方式,使用 :src 调用了 v-bind 指令处理其内容,相对路径不会被webpack的 file-loader 处理。

<img :src="'./assets/images/02.jpg'" alt=""> // ×
// 编译后:
<img src="./assets/images/02.jpg" alt="">
  1. 如果路径以 @ 开头,也会被看作模块依赖。如果你的 webpack 配置中给 @ 配置了 alias,这就很有用了。所有 vue-cli 创建的项目都默认配置了将 @ 指向 /src
//代码
<img src="@/assets/image/login/title.png" alt="">

//渲染后html页面
<img data-v-70c98a68="" src="/static/img/title.1e9fa570.png" alt="">
  1. 如果路径以 ~ 开头,其后的部分将会被看作模块依赖,既可以加载含有别名的静态资源,又可以加载node-modules中的资源。如
// 如果用别名的静态资源必须加@否则报一下错误。
//代码 必须有@否则报错,@是别名
<img src="~@/assets/image/login/title.png" alt="">
//渲染后html页面
<img data-v-70c98a68="" src="/static/img/title.1e9fa570.png" alt="">


//代码
<img src="~[npm包名]/xxx/logo.png" alt="">
//渲染后的html页面
<img data-v-70c98a68="" src="/static/img/logo.2f53e458.png" alt="">

<style>部分的路径处理

由于vue-loader在处理style时,采用的是style-loader,所以可能 和上面部分的转换规则不太一样。在vue-loader的内部使用了如下的配置(不一定配置,也有可能通过js直接给rules赋值):

//在vue-loader的内部使用css-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        loader: 'css-loader',
        options: {  
          url: true, //默认选项 url为true时,则意味着可以将url中的字符串通过require()加载进来。
        },
      },
    ],
  },
};
  1. 如果路径是绝对路径,会被原样保留。如 /src/assets/image/login/title.png
//代码
<style scoped>
.login {
  background-image: url("/src/assets/image/login/title.png");
}
</style>

//渲染后css
.login[data-v-70c98a68] {
  background-image: url(/src/assets/image/login/title.png);
}
// 同样不会显示,编译后的路径不是这个(要放在public下面)
  1. 如果路径以 . 开头,将会被看作相对的模块依赖。如 ./title.png
//代码
<style scoped>
.login {
  background-image: url("./title.png");
}
</style>

//渲染后css
.login[data-v-70c98a68] {
  background-image: url(/static/img/title.1e9fa570.png);
}
  1. 如果路径以 ~ 开头,其后的部分将会被看作模块依赖,即可以加载含有别名的静态资源,又可以加载node-modules中的资源。如
//代码
<style scoped>
.login {
  background-image: url("~[npm包名]/logo.png");
}
</style>

//渲染后css
.login-[data-v-70c98a68] {
  background-image: url(/static/img/logo.e05643fc.png);
}

//代码
<style scoped>
.login {
  background-image: url("~@/assets/image/login/bg.png");
  // 和上面的<template>相比,少了直接用@开头的方式url("@/assett/logo.png")
  // 错误写法
  // background-image: url("@/assets/image/login/bg.png");
}
</style>

//渲染后css
.login[data-v-70c98a68] {
  background-image: url(/static/img/bg.1d997ea3.png);
}

其他介绍

拼接路径

  1. 当路径的文件名需要拼接变量的时候,可使用 require() 引入,在 template:src 或者 scriptdata computed 中都可以进行 require 引入或拼接。
<img :src="require('./assets/images/03.jpg')" alt=""> // √
<img :src="require('./assets/images/'+ this.imgName +'.jpg')" alt=""> // √
<img :src="img3" alt=""> // √
<script>
export default:{
    data(){
        return {
          imgName: '03.jpg',
          img3: require('./assets/images/03.jpg'),
          // 或者 @是定义的别名,(注意这里可以直接用@,而在style中只能用~@)
          img3: require('@/assets/images/login-bg.png')
        }
      },
}
</script>
// 编译后:
<img src="/img/03.ea62525c.jpg" alt="">
<template>
   <img :src="imgUrl">
</template>

<script>
  import imgUrl from "../assets/test.png"   
</script>
<div :style="{ backgroundImage: `url(${img3})` }"></div>
<script>
export default:{
    data(){
        return {
          img3: require('./assets/images/03.jpg'),
          // 或者
          img3: require('@/assets/images/login-bg.png')
        }
      },
}
</script>

public文件夹

  1. 任何放置在public文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。

  2. 用绝对路径引入时,路径读取的是public文件夹中的资源,任何放置在 public 文件夹的静态资源都会被简单的复制到编译后的目录中,而不经过 webpack特殊处理。

  3. 当你的应用被部署在一个域名的根路径上时,比如 http://www.abc.com/,此时这种引入方式可以正常显示但是如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀 publicPath 是部署应用包时的基本 URL,在vue.config.js 中进行配置,详情参阅官方文档

// vue.config.js 应用没有部署到域名的根目录
// 引入publicPath并且将其拼接在路径中,实现引入路径的动态变动
module.exports = {
    publicPath:'/foo/',
    ...
}
<img :src="this.publicPath + 'images/05.jpg'" alt=""> // √
// 编译后:
<img src="/foo/images/05.jpg" alt="">
<script>
export default:{
    data(){
        return {
          publicPath: process.env.BASE_URL,
        }
    },
}
</script>

总结

URL 转换规则

  • 如果 URL 是一个绝对路径 (例如/images/foo.png),它将会被保留不变。
  • 如果 URL 以 . 开头,它会作为一个相对模块请求被解释且基于你的文件系统中的目录结构进行解析。
  • 如果 URL 以 ~ 开头,其后的任何内容都会作为一个模块请求被解析。这意味着你甚至可以引用 Node 模块中的资源:<img src="~some-npm-package/foo.png">
  • 如果 URL 以 @ 开头,它也会作为一个模块请求被解析。它的用处在于 Vue CLI 默认会设置一个指向/src的别名@。(仅作用于模版中)

推荐将资源作为你的模块依赖图的一部分

推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:

  • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
  • 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
  • 最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。
  • 综上静态资源不建议全部放在public文件夹

参考文件

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐