Vue提高18 更换打包文件路径

一个由更改Vue打包后路径引发的一系列问题。

引入字体文件

在Vue中,想要引入字体文件,需要,使用@font-face来引入本地的字体

1
2
3
4
5


h1 {
background: url('./assets/images/logo.png');
}

然后再App.vue中引入并使用

1
2
3
4
5
6
7
8
9
10
11
<style>
@font-face {
font-family: 'hello';
src: url('./assets/font/d.ttf');
font-weight: bold;
font-style: italic;
}
.hello-inner h1 {
font-family: hello;
}
</style>

这样就可以使用引入的字体了。

也可以单独建立一个CSS文件来统一管理引入的字体,然后通过import引入

1
2
3
4
5
6
<style>
@import 'font.css';
.hello-inner h1 {
font-family: hello;
}
</style>

url-loader

Webpack中的loader的目的是用来处理各种非JS之外的文件,可以使你在import或”加载”模块时预处理文件。

执行的顺序是从右至左,在Vue-cli2中,在webpack.base.conf.js中定义了一些基本的loader:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.pug$/,
loader: 'pug',
},
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader",
},
]
},

比较重要的是url-loader这个插件。我们在处理引入的各种资源文件时,Webpack最终会将各个模块打包成一个文件,因此我们样式中的URL路径是相对入口HTML页面的,而不是相对于原始CSS文件所在的路径的。这就会导致图片引入失败。这个问题可以使用file-loader解决。

url-loader中封装了file-loader,它不依赖于file-loader,它除了可以完成file-loader的作用,还可以将符合要求的图片进行编码生成DataURL,减少HTTP请求数目。url-loader选项中提供了limit参数,小于这个参数的文件才会转换为DataURL

路径

明确一下关于路径的表示,

1
2
3
./        表示当前目录,相对地址
../ 表示上层目录,绝对地址
/ 表示根目录,绝对地址

__dirname表示当前文件在系统中的绝对路径,比如我在D:\projects\vue-cli-learning文件夹下新建了一个test.js文件:

1
console.log(__dirname)

在Node环境下运行这个文件输出结果就是:

1
D:\projects\vue-cli-learning

path.resolve的目的是用来将相对路径转为绝对路径,接受多册参数,一次表示要进入的路径,直到最后一个参数为止。除了根目录,该方法的返回值都不带尾部的斜杠

1
2
// 实例
path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')

上面代码的实例,执行效果类似下面的命令。

1
2
3
4
5
$ cd foo/bar
$ cd /tmp/file/
$ cd ..
$ cd a/../subfile
$ pwd

比如,在刚才的test.js中打印:

1
2
3
const path = require('path');
console.log(path.resolve(__dirname, 'a'));
console.log(path.resolve(__dirname, '../a'));

输出的结果就是两个路径:

1
2
D:\projects\vue-cli-learning\a
D:\projects\a

打包路径的配置

在处理字体文件的url-loader的配置选项中:

1
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')

规定了Webpack打包后生成的资源名称和路径,其中,utils.assetsPath规定了跨系统平台输出文件路径,并且和环境变量相关:

1
2
3
4
5
6
7
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory

return path.posix.join(assetsSubDirectory, _path)
}

在生产环境下的config.build.assetsSubDirectory配置实在/config/index.js中:

1
2
3
4
5
6
7
8
9
10
11
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),

// Paths
assetsRoot: path.resolve(__dirname, '../dist/'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',

// ... 其他
}

其中,

index是生成的index.html在本机的地址,现在的配置下,生成的index文件会位于D:\projects\vue-cli-learning\dist\中(由path.resolve转换而来)

assetsRoot指明了构建后的资源的路径,Webpack会将所有打包后的资源(包括index.html在内都放到这个文件夹下

assetsSubDirectory是打包后的资源目录,除了index.html之外的所有模块都会放到这个文件夹中,当前配置后,所有的资源文件都会打包在D:\projects\vue-cli-learning\dist\static

assetsPublicPathstatic资源文件夹相对于HTTP服务器运行时的路径,比如我利用http模块构建了一个简单的静态服务器:

1
npx http-server -a 127.0.0.1 -p 7070

这个时候服务器运行的URL是localhost:7070/

当前目录的情况是这样:

捕获333.PNG

如果我在App.vue中引入的字体文件路径是./assets/font/d.ttf),在打包之后首先由对应的url-loader处理,生成的字体文件将位于/dist/static/fonts中,打包后静态服务器请求的字体地址是:http://127.0.0.1:7070/static/fonts/d.d30126a.ttf

明白这些选项都是干什么的后,就可以更改了

(1)更改打包后的文件路径,更改index选项assetsRoot选项,比如更改为:

1
2
3
4
5
6
7
8
9
10
11
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/test/index.html'),

// Paths
assetsRoot: path.resolve(__dirname, '../dist/test/'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',

// ... 其他
}

这时候打包后文件将是这样的结构:

捕获123.PNG

(2)更改打包后静态资源所在目录(同时对资源的请求也会更改,因为打包后内联的URL地址也由url-loader一并改变了

更改为:

1
2
3
4
5
6
7
8
9
10
11
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),

// Paths
assetsRoot: path.resolve(__dirname, '../dist/'),
assetsSubDirectory: 'static/hello/',
assetsPublicPath: '/',

// ... 其他
}

此时打包生成的目录:

捕获31.PNG

发出的获取字体的网络请求的URL为http://127.0.0.1:7070/static/hello/fonts/d.d30126a.ttf

(3) 更改HTTP服务器获取静态资源的路径

这个一直把我搞混了,在默认的配置情况下是去网站的根目录下去寻找assetsSubDirectory中规定的资源目录,比如http://127.0.0.1:7070/static/fonts/d.d30126a.ttf

但是如果资源不是在根目录下呢,比如需要通过http://127.0.0.1:7070/ok/static/fonts/d.d30126a.ttf来请求字体时,就需要改动assetsPublicPath了,此时的配置:

1
2
3
4
5
6
7
8
9
10
11
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),

// Paths
assetsRoot: path.resolve(__dirname, '../dist/'),
assetsSubDirectory: 'static/',
assetsPublicPath: '/ok/',

// ... 其他
}

这个时候打包后的目录没有变化,但是请求字体的URL就变成了http://127.0.0.1:7070/ok/static/fonts/d.d30126a.ttf

如果要将assetsPublicPath改为./会发生什么呢?请求字体的URL发生了变化

这是因为./是相对地址,相对发出请求的文件所在的木而言的,这个请求是谁发出的呢?是打包后的CSS文件发出的

捕获g.PNG

转换为绝对路径就是/static/css/,然后再这个目录下再去寻找资源目录,所以请求的URL是http://127.0.0.1:7070/static/css/static/fonts/d.d30126a.ttf,这肯定不是我们想要的

所以在更改assetsPublicPath时,尽量采用绝对地址,从服务器的根节点域名触发,定位资源文件,不容易发生错误。

总结

回到初衷,本意只是简单的更改打包后的文件路径,想要打包在/dist/project中,只需要简单的更改index选项和assertsRoot即可

1
2
3
4
build: {
index: path.resolve(__dirname, '../dist/project/index.html'),
assetsRoot: path.resolve(__dirname, '../dist/project/'),
}

这两项只会影响打包后在本地的路径,而assetsSubDirectory会影响请求和本地打包后路径,assetsPublicPath只会影响上线后发送请求的路径。

简单的一点改动,引出了一堆东西要学习。

参考