最近有个 Vue 项目,是使用 Vue-cli 构建起来的。Vue-Router 是使用的 HTML5 History 模式。这个模式是使用浏览器提供的 History 接口实现的,所以如果是在页面上通过点击链接往里面 push 一个新地址是没问题的,但是如果直接请求这个地址则会 404 (因为这个地址并不是真实存在的,只是我们通过代码模拟出来的)。

所以 Vue-Router 的手册中也给出了解决方式:通过配置重写规则把 404 统一重定向到 index.html 文件。但是懒得自己配环境,所以直接用的 Netlify 提供的免费服务。

当然 Netlify 很强大,提供了 redirect 的功能,是通过添加 _redirects 规则文件到根目录下来实现的。具体可以参照:https://www.netlify.com/docs/redirects/

问题就是如何在 npm run build 的时候自动把 _redirects 文件添加到根目录呢?

01. 尝试

.
├── build
├── config
├── node_modules
├── src
└── static

5 directories

上面的是由 vue-cli 所构建出来的项目的目录结构。起初我尝试把 _redirects 放到 static 目录中,但是失败了。是因为 npm run buildstatic 目录中的文件并不是放到了根目录(这里是 dist)中,而是在根目录中的 static 目录内。

dist
├── index.html
└── static
    ├── _redirects  <-- 在这里
    ├── css
    ├── fonts
    └── js

4 directories, 10 files

02. 查资料

于是我 Google 了一圈,找到了这个 issues :https://github.com/vuejs/vue-cli/issues/285,但是这貌似也不是我想要的。所以没办法了,只能自己动手了。

03. 瞎改

看了眼 package.json 文件里的 build 是执行了 node build/build.js ,打开这个文件一直跟踪到了 build/webpack.prod.conf.js 文件里,在 90 行左右有这样的代码:

// copy custom static assets
new CopyWebpackPlugin([
  {
    from: path.resolve(__dirname, '../static'),
    to: config.build.assetsSubDirectory,
    ignore: ['.*'],
  },
])

使用了 CopyWebpackPlugin 这个插件把 static 中的文件拷贝到了 config.build.assetsSubDirectory 下。

assetsSubDirectory 的定义在 config/index.js 文件中,应该就是指向了 dist/static 这个目录。

module.exports = {
  build: {
    env: require('./prod.env'),
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',  // <-- 在这里
    assetsPublicPath: '/',
    productionSourceMap: true,
    // ....

所以我就改了一下 build/webpack.prod.conf.js 的代码:

+    // copy _redirects file
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static/_redirects'),
+        to: config.build.assetsSubDirectory + '/..'
+      }
+    ]),
     // copy custom static assets
     new CopyWebpackPlugin([
       {
         from: path.resolve(__dirname, '../static'),
         to: config.build.assetsSubDirectory,
-        ignore: ['.*']
+        ignore: ['.*', '_redirects']
       }
     ])

_redirects 文件复制到 dist/static 的上级目录 /.. 中。完成啦~

04. 总结

这种修改文件的方法可能是最简便的了,但是随便修改这些文件显得不是很专业嘛。所以大家如果有别的方法请告诉我。