Vue CLI 3 搭建的项目中路径相关问题

这是开头

最近在试水 Vue CLI 3,而且尝试配置一个多页面(多应用)项目出来,期间又遇到各类路径问题,因而...因而有了下面的唠叨。
如下都是基于 Vue CLI 3 来举例说明的,使用 2.x 版本的其实也相似

首先,参考 官方文档对静态资源处理的说明,并经过本身的实践,能够总结出如下内容css

静态资源能够经过两种方式进行处理:html

  1. 如下状况下,资源不会被 webpack 处理,而是被直接拷贝:vue

    • 放置在 public 目录下,即便未被使用。
    • 经过绝对路径被引用,即以 / 开头的路径。
  2. 如下状况下,资源会被 webpack 处理(URL的resolve、minify、uglify、转 base64 等):webpack

    • 使用 JavaScript 导入。
    • template/CSS 中经过相对路径(即以 . 开头或直接以文件(夹)名开头)被引用。
    • URL 以 ~ 开头,其后的任何内容都会做为一个模块请求被解析。
    • URL 以 @ 开头,它也会做为一个模块请求被解析(@ 是在 webpack 设置的 alias)。

咱们应该根据实际状况去选择咱们要引用的资源是否要被处理,而后用对应的、正确的方式去引用它们以达到目的。如下对使用绝对路径和相对路径的方法和注意事项进行描述。ios

使用绝对路径

默认状况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上(对应选项 baseUrl: '/'),例如 https://www.my-app.com/。若是应用被部署在一个子路径上,你就须要用这个选项指定这个子路径。例如,若是你的应用被部署在 https://www.my-app.com/my-app/,则设置 baseUrl/my-app/。正由于以上的可能状况,咱们应该在打算引用纯静态资源(那些不被webpack处理的资源,通常就是 public 目录下的资源)的时候,都确保使用 baseUrl 做为 URL 的开头,如下列举在不一样文件中配合 baseUrl 选项写绝对路径的使用方法和注意事项:git

在入口html文件中使用

咱们可使用 lodash template 语法插入 baseUrlgithub

<link rel="icon" href="<%= BASE_URL %>favicon.ico">

*.vue 中使用

咱们能够经过 Vue CLI 提供的客户端环境变量 process.env.BASE_URL 来获取 baseUrlweb

/* 在须要的组件中定义 baseUrl,而后在 <template> 下使用 */
<template>
    <div id="app">
        <img :src="imgUrl">
        <img :src="`${baseUrl}imgs/my_image.png`">
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            baseUrl: process.env.BASE_URL,
            isBigImg: Math.random() > 0.5
        }
    }
    computed: {
        // 动态地获取不一样的静态资源
        imgUrl() {
            if (this.isBigImg) {
                return `${baseUrl}imgs/my_image_big.png`
            } else {
                return `${baseUrl}imgs/my_image.png`
            }
        }
    }
};
</script>

/* 我的建议能够在全局定义,减去在每一个组件内定义的麻烦
Vue.prototype.$baseUrl = process.env.BASE_URL
// 在 <template> 下使用
<img :src="`${$baseUrl}imgs/my_image.png`">

在其余 js 模块中使用

import axios from 'axios';

const baseUrl = process.env.BASE_URL;

axios.defaults.baseURL = `http://www.example.com${baseUrl}api/`

在样式文件中使用(以 sass/scss 为例)

由于 sass 文件中没法获取环境变量或 webpack 内的配置,因而最直接的方法就是自定义一个变量,而后在每一个须要使用到它的文件引用它。bootstrap

// config.scss
$baseUrl: "/";

// icon.scss

@import "config"

.icon-test {
    display: inline-block;
    background: url($baseUrl + 'imgs/icon_test.png') no-repeat;
    width: 10px;
    height: 10px;
}

这样作仍是有比较大的麻烦:axios

  • 若是生产环境和开发环境的 baseUrl 不一样,每次转换环境去编译都要去手动修改这个变量,十分之麻烦并且可能出现错误;
  • 两处地方相同的定义,不方便代码的维护;
  • 在后续讲到的关于 相对路径 的坑会涉及到,每次引用 config.scss 的路径并不必定是同样的,且很容易出现编译错误;

那么,有没有什么办法能避免人工操做、避免屡次的定义而且避免使用可能潜在错误的引用呢?幸好的确是有的! sass-loader 提供了一个 data 选项,能够为全局注入变量或样式文件;

// vue.config.js

const baseUrl = process.env.NODE_ENV === 'production' ? '/sub/' : '/';

module.exports = {
    baseUrl,

    css: {
        loaderOptions: {
            sass: {
                data: `$baseUrl: "${baseUrl}";`
            }
        }
    }
}
这样咱们就能够在全局的 `sass` 文件中使用 `$baseUrl` 这个变量了,并且在只定义一次的状况下,能根据编译环境变化而变化。

使用相对路径

使用相对路径也会存在一些坑,接下来会列举常见的关于相对路径的坑与解决方法:

JavaScript 动态引用资源,编译没报错,但页面上请求返回 404

有时候咱们须要使用 JavaScript 动态的引用某些资源,且但愿这些资源被 webpack 一同打包,咱们先看这种作法:

computed: {
    background () {
        return `./bgs/${this.id}.jpg`
    }
}

咱们会发现打包没报错,可是在页面上能够发现这些资源的请求都是 404。这是由于相似 ./bgs/${this.id}.jpg 这样的动态字符串在打包阶段不会被 webpack 识别为依赖,资源也就不会被打包了。为了让 webpack 识别这些依赖,咱们能够这样作:

computed: {
    background () {
        return require('./bgs/' + this.id + '.jpg')
    }
}

经过使用 require() 让 webpack 将括号内的 URL 识别为一个依赖并传入对应的 loader 进行处理。

要特别注意,以上的例子中, ./bgs/ 目录下的全部图片都会被打包,由于 webpack 没法得知页面在运行时会使用哪张图片,因此 webpack 会把全部的图片都打包了。

在 sass 中使用相对路径引用图片或字体文件,编译报错

先来看一个例子:

//  文件目录
//  src
//  |--assets
//  |   |
//  |   |-fonts
//  |   |   |- iconfont.eot
//  |   |
//  |   |-css
//  |       |
//  |       |-iconfont.scss
//  |
//  |--app.vue

// iconfont.scss
@font-face {
    font-family: "iconfont";
    src: url("../fonts/iconfont.eot");
    ...
}

// app.vue
<style lang="scss">
@import './assets/css/iconfont.scss'
</style>

每每咱们在打包的时候会报错(以上例子会报错),说找不到 iconfont.eotsass-loader 文档中有对 url() 进行了单独的说明

Since Sass/libsass does not provide url rewriting, all linked assets must be relative to the output.

  • If you're just generating CSS without passing it to the css-loader, it must be relative to your web root.
  • If you pass the generated CSS on to the css-loader, all urls must be relative to the entry-file (e.g. main.scss).

大体意思就是, sass-loader 并不提供 url 的重写,全部的 scss 文件被 sass-loader 处理成最终的 CSS 后(编译过程当中 url 不会被重写即保持原样),再传递给 css-loader 处理。也就是说,全部的 url 都是相对于输出的!在 Vue CLI 搭建的项目中,它们都是相对于使用这些 scss 文件的 vue 文件的。对于上例,是相对于 app.vue 的,所以报错。咱们会很天然的会但愿路径的引用是相对于 scss 文件自己的,sass-loader 文档中也给出了解决方案:

  • Add the missing url rewriting using the resolve-url-loader. Place it before the sass-loader in the loader chain.
  • Library authors usually provide a variable to modify the asset path. bootstrap-sass for example has an $icon-font-path. Check out this working bootstrap example.

第一个方法:使用 resolve-url-loader 来弥补 sass-loader 缺失的 url 重写功能,注意要放到 sass-loader 之前调用。
第二个方法:Library 做者通常都会提供变量,用来设置资源路径,如 bootstrap-sass 能够经过 $icon-font-path 来设置。参见this working bootstrap example

这样看来解决的思路有两种:

  • 写 url 的时候就写 vue 文件相对于资源的路径。这种方法较为暴力,当项目层级复杂了以后容易写错路径(加上现有的编辑器、IDE应该认为你写的路径是错误的)。当同个 scss 文件被多个不一样层级的 vue 文件引用的时候,这种暴力的方法就行不通了!
  • 使用第三方库补充 sass-loader 的路径重写功能,让路径的引用是相对于当前 scss 文件自己的。这个方法能较好的解决问题。

在这里提供一下我喜欢的方法。与其考虑 让路径的引用是相对于 scss 文件自己让路径直接相对于 vue 文件,咱们能够换个思路,让全部路径都是以根目录往下找,并让 webpack 对路径进行重写,可是直接用 /src/ 这种绝对路径的写法会让这些资源不被 webpack 打包。在前文说起到的,webpack 有个强大的机制,也就是 ~,经过在 url 前面添加 ~ 能够告诉 webpack 要把它当作一个模块来处理,也就是会被 webpack 打包。配合 webpack 提供的别名 @/src),咱们能够对上例作修改:

// iconfont.scss
@font-face {
    font-family: "iconfont";
    src: url("~@/assets/fonts/iconfont.eot");
    ...
}

这样子,经过 webpack 对模块的处理,能够正确经过编译!这样作的好处是可大大避免书写相对路径可能产生的错误,每次只需“无脑”从根目录往下找就是了,又能够减少依赖、减小配置项。


若是有什么地方我理解错误欢迎你们指出!

相关文章
相关标签/搜索