用vue-cli开发的项目在开发完打包之后想在本地预览要怎么做呢?这里要根据你的路由模式来分两种情况 1. history 模式 2. hash 模式(默认情况下)
我们先来说问题场景和解决方法。
如何预览:
方法1 : 项目根目录dist 文件夹下的 index.html 文件直接用浏览器打开。
方法2 : 在本地搭建一个服务器,然后运行该打包后的静态资源。
方法1
方法1 预览遇到问题:
用方法1 预览时,如果可能会遇到打开页面为空白的情况。
如何解决:
情况 1: 路由模式 mode: hash
其实这跟配置资源的路径有关,打开项目根目录config文件夹下的index.js,定位到build下的assetsPublicPath(dev下也有一个assetsPublicPath,别搞错了),把assetsPublicPath: '/'
修改为相对路径 assetsPublicPath: './'
修改完之后重新打包,然后用方法1来预览
情况 2: 路由模式 mode: history
只能用方法2来预览。
方法2
用node 或 express 打开一个本地服务器端口来预览打包后的静态文件。
当用方法2来预览时 assetsPublicPath 为相对路径或绝对路径都没影响。
因为vue项目为脚手架搭建的已经有 node 和 express 依赖,无需重新安装。我们可以直接在根目录下新建一个 js 文件, 因为是开启服务器的配置,一般会习惯性的命名为server.js
,配置如下:
const express = require("express")
const app = express()
const port = 8081
app.use(express.static('./dist'))
app.listen(port, () => {
console.log('Listening at http://localhost:' + port)
})
根目录下用node命令开启一个 8081 的端口来监听该运行:node server.js
监听成功后再浏览器输入: http://localhost:8081
就能看到项目页面了。
方法2 预览遇到问题:
如果 路由模式 hash
就万事大吉了,但为 history
时你会发现
此时只要首页能访问,通过首页点进去其他路由也是可以的,但是如果在其他路由刷新就有错误了,必须回到首页重新刷新才能正常访问。
如何解决:
上面的server.js 配置需加多加 connect-history-api-fallback
该中间件的作用是当页面访问出现错误时重定向到 index.html 页面,既重新访问index.html。配置如下:
const express = require("express")
const app = express()
const port = 8081
let history = require('connect-history-api-fallback')
//重定向到index.html
history({
rewrites: [{
from: /^\/libs\/.*$/,
to: '/index.html'
}]
});
app.use(history());
app.use(express.static('./dist'))
app.listen(port, () => {
console.log('Listening at http://localhost:' + port)
})
修改完配置后,重新启动服务器(重新执行node server.js
) 命令,然后在浏览器访问,就不会出现上面访问错误的情况了。
此时如果你的路由有设置通配符到404页面的话,如下
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})
当url输入的为错误地址时,就会跳转到 NotFoundComponent 页面,然后输入正确的地址又可以访问。
我们执行的vue 命令 都是 npm run xx
的,那么启动我们的本地预览服务器能不能用这种类型的命令呢?当然是可以的。
先来看看根目录下的 package.json
文件 的 scripts
键
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
}
这里的 dev start build 是不是很熟悉,我们只想的 npm run xxx 命令就是根据这里的字段来运行的。
来看看 build 指向的是build/build.js
文件。
那我们想运行npm run server
这样的命令来启动本地预览服务器设置可以是:
1.在 scripts
下新增 "server": "node build/server.js"
2.把刚才创建在根目录下的 server.js 文件 移动到 build 文件夹下。
3.根目录下运行 npm run server
原理
上面说了问题及解决方法,这里来说说为什么会出现上面的问题及问题的所在和怎么去定位问题。
** hash 模式下静态资源访问错误问题:**
方法1直接访问dist下的index.html文件时静态资源错误 assetsPublicPath 的路径问题。
打开控制台会发现,script标签的引入路径不对,因为static文件夹和index.html是在同一个目录下的,这里却是从根目录引入static下的文件,正确的路径应该是./开头的相对路径:
src='./static/...'
或者 src='static/...'
问题已经确定,是打包后的文件路径有问题。
于是就去找webpack.base.conf.js文件,有个output选项,output是webpack中对输出文件的配置,最常用的有fileName、path属性,指定输出文件的名称和在项目中的位置,publicPath可以为项目中的所有资源指定一个基础路径,也是我们解决这个问题的关键。
可以看到 publicPath
属性的值是根据 process.env.NODE_ENV
来判断的。那 process.env.NODE_ENV 又是什么呢?
process对象 是一个全局对象,可以在应用程序中全局使用(包括在业务页面);
process.env 属性返回一个包含用户环境信息的对象;
process.env.NODE_ENV 用来定义环境变量,一般包括production(生产环境)、development(开发环境),webpack中各种配置都是根据环境变量的不同来做相应的处理的。
从三元中知道 打包后(production 生产环境) 的 publicPath 是由 config.build.assetsPublicPath 来决定的,那我们就找到 config 文件夹下的index.js文件,然后定位到 build 下的 assetsPublicPath。
既然是 加载的文件引入路径错误的原因,那把引入路径修改过来就可以啦,既把 assetsPublicPath: '/' 修改为 assetsPublicPath: './'
history 模式下本地服务器在除根路由外其他路由刷新页面后找不到路径
history 和 hash 模式有什么区别呢?
history 模式是h5 api 的 history.pushState ,相对于浏览器模拟了一条历史,而真正的服务器上没有这个路径资源,为什么 hash 模式没有这个问题?
hash 模式是带# 符号,服务器就不会解析,相对于还是返回html而已, index.html会根据vue路由去解析;
而在 history 模式下,只是动态的通过js 操作window.history 来改变有浏览器地址栏里的路径,并没有发起http请求,但当你直接在浏览器里输入这个地址的时候 就一定要先对服务器放起http请求,但是这个目标在服务器上又不存在所以就返回了404了,怎么解决呢,就是把所有的请求全部转发到 index.html,让 index.html 重新取匹配路由。
对于浏览器的这种机制 vue-router 官方也给出了多种解决方案。
上面方法2使用中间件 connect-history-api-fallback 的方案实质上当找不到路由时重定向回项目首页,既
在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
history 模式下项目放在服务器子目录下访问为空白页面问题。
经过上面的方法2 基本上是没什么问题了(上面server.js配置中项目是部署在服务器根目录下的),但是在实际开发中,项目可能是部署在子目录下的。
此时若开启了 history 模式,在dev开发阶段是没问题的,但是打包之后部署在服务器根目录下,即把打包后生成的dist文件夹(不要上传文件夹)里的内容上传到服务器上,访问路径为:
xxx.com
此时访问正常。
但如果是部署在服务器子路下呢?例如放在子目录 public下,访问路径为:
xxx.com/public/
此时访问为空白页,这也是引用资源路径问题。
此时服务器上的目录结构是
www/
+public/
+static/
+index.html
但是上面说过,index.html
中 app.js
的引用方式是
<script type="text/javascript" src="/static/js/app.js"></script>
即index.html
引用app.js
的路径是 http://www.xxx.com/static/js/app.js
而部署在 public 文件夹下时,app.js
的实际路径是 http://www.xxx.com/public/static/js/app.js
所以当你通过http://www.xxx.com/public/index.html
来访问的时候会出现找不到app.js
要解决的方法很简单把引用方式 改成如下两种方式都可以
<script type="text/javascript" src="./static/js/app.js"></script>
// 或者
<script type="text/javascript" src="/public/static/js/app.js"></script>
可以是 修改webpack
位置
assetsPublicPath: './'
或者
assetsPublicPath: '/public/',
或者配置路由 router
的 base 属性, 设置为子目录路径即可
export default new Router({
mode: 'history',
base: '/public/',
routes: []
})
重启服务器,就可以用 xxx.com/public/
访问了。