webpack 是目前使用最为火热的打包工具,各大知名的框架类库都用其打包,国内使用最近也火热起来。它在单页应用和类库打包上帮助许多人从代码管理中解脱了出来,成为了当下风靡一时的打包工具。css
可是坑也不少,好比说图片,字体等文件的路径。html
刚开始用webpack的同窗很容易掉进图片打包这个坑里,好比打包出来的图片地址不对或者有的图片并不能打包进咱们的目标文件夹里。明明在开发阶段都是好好的,一但发布到线上,就出现各类404,vue
通常来讲,webpack打包的SPA程序,发布到网站的根目录下都不会出现太多问题,可是发布到网站的子目下,就会出现各类资源文件找不到的状况。node
这种文件,我在如下这个知乎的帖子里曾经作过详细的回答webpack
知乎用户:vuejs怎么在服务器部署?web
彷佛全部问题都解决了,但有一个问题没有解决,就是若是我在css里引了了图片资源,webpack并不能很好的处理这里面的资源路径vue-cli
好比我把spa部署在 https://www.wx2share.com/m/
“m”文件夹是网站下的一个子目录,express
若是我在开发的时候,写了以下css代码api
.content { background: url('/static/img/1.jpg') }
首先,咱们修改(tips:个人配置文件是经过vue-cli生成的)浏览器
build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/m/', //这里指定publicPath 的路径为我子目录文件名,通常默认为'/' productionSourceMap: true, },
webpack publicPath 参数是用来,帮助你为项目中的全部资源指定一个基础路径,一但设定值之后,全部代码中经过requrie 或import 方式引入的资源文件,在build之后,都回指向相似 '/m/static/xxx.xx'
好比:
logo: require('@/assets/v.png') <img :src="logo">
编译之后,
<div class="logo-warp"> <img src="/m/static/img/logo_small.f457a3f.png"> </div>
能够看到,webpack 已经正确的把资源文件的路径里 加上了/m ,保证了资源文件引用正确,这里顺便提一点,publicpath 还能够设置在cdn的url
若是 publicPath: 'https://cdn.youdomin.com/' 这样编译之后,src=''https://cdn.youdomin.com//static/img/logo_small.f457a3f.png
这样你只要把static整个文件夹托管的你的cdn上就能够了很是方便。可是publicPath 必定用绝对路径,绝对路径,绝对路径(重要的事说3遍) 千万不要用相对路径,若是你把publicPath设为'./',哪你启用路由之后,https://www.wx2share.com/m/ 这样的url进入程序的,不会有问题,由于这个时候,static文件正好在当前目录下,可是当你相似用这样的网址来访问的时候 ,https://www.wx2share.com/m/sh... 资源文件又找不到了,由于这个时候./的指向的目录是
/m/show/static/ 很明显你的资源文件全在 /m/static下,因此又404了。
上面的方法基本解决了大部分的问题,惟一不能解决的就是文章开头提到的,css中引入的资源文件的问题了
经过publicpath的设置,并不到改变css中引用资源文件的路径,上面实例代码中的css编译后,仍是
.content { background: url('/static/img/1.jpg') }
是乎webpack 并不处理css中的文件路径,这样的结果就是发布之后页面上全部经过css引入的资源文件所有不能正常显示了。前段时间我采用了一个简单又粗爆的方法来解决这个问题,哪就是,绝对不用css引入任何文件。固然你也能够手动修改webpack编译过的文件,把经过查找替换 把/static 替换为/m/static 若是你不嫌累的慌。
最近又开始了一个新的SPA应用,引入了一个第三方css,里面好多经过css引入的图片文件 ,让我不得不下定决心来解决这个问题了
TIPS: 如下内容的配置文件修改所有是基于由vue-cli生成的配置文件,若是你是用本身写的配置文件请注意区别!
我首先想到,若是开发的时候我就是指定在“m/”子目录下,不就能解决大部分问题了,这样和线上的根目录文件对应了,buid之后就不会有哪么多问题 ,
第一步:修改dev模式上的publicPath
dev: { env: require('./dev.env'), port: 8082, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/m/', //把dev模式下的publicPath也设为 m proxyTable: {}, cssSourceMap: false } yarn run dev
之后,访问 http://localhost:8082/m/ 结果好么,直接给我来了个 can not get /m/
可是若是我直接访问http://localhost:8082/m/index.html 是能够的,并且功能基本正常,是就vue的router不起做用了,在浏览器地址栏里直接敲入 http://loalhost:8082/m/view/1 而后回车,这样的地址就所有404了,看来是dev sever没有把,全部连接重新定向到 /m/index.html上,可是publicPath设置是正确的了,如今要解决的问题就是,不管在浏览器地址栏里输入什么网址,都让它重定向到 /m/index.html 就能够解决全部问题了,
webpack 的dev sever 是什么,用什么来实现的,能过查看buid/dev-server.js
发现是用express 来实现web server,经过加载webpack-dev-middleware 来实现实时编译,全部请求都转发给它了,因此,在node_modules下找到它的源代码,看了老半天,看不出因此然来,只能debug了,看它究竟是怎么运做的,电脑上没有调试的工具,只好,经过打log的方法来调试了。
function webpackDevMiddleware(req, res, next) { function goNext() { if(!context.options.serverSideRender) return next(); return new Promise(function(resolve) { shared.ready(function() { res.locals.webpackStats = context.webpackStats; resolve(next()); }, req); }); } if(req.method !== "GET") { return goNext(); } var filename = getFilenameFromUrl(context.options.publicPath, context.compiler, req.url); console.log(filename) //关键就是这里,只有filename不等于 false的时候才进入真正的处理阶段 if(filename === false) return goNext(); //下面还有好多代码,不粘贴了, 既然这里返回 false
咱们进入 getFileNameFromUrl 这个函数看看,为何会false
function getFilenameFromUrl(publicPath, outputPath, url) { var filename; console.log(publicPath, outputPath, url) //我在这里打了个log // localPrefix is the folder our bundle should be in var localPrefix = urlParse(publicPath || "/", false, true); var urlObject = urlParse(url); // publicPath has the hostname that is not the same as request url's, should fail if(localPrefix.hostname !== null && urlObject.hostname !== null && localPrefix.hostname !== urlObject.hostname) { return false; } // publicPath is not in url, so it should fail if(publicPath && localPrefix.hostname === urlObject.hostname && url.indexOf(publicPath) !== 0) { return false; //就这里return false了 } // strip localPrefix from the start of url if(urlObject.pathname.indexOf(localPrefix.pathname) === 0) { filename = urlObject.pathname.substr(localPrefix.pathname.length); } if(!urlObject.hostname && localPrefix.hostname && url.indexOf(localPrefix.path) !== 0) { return false; } // and if not match, use outputPath as filename return querystring.unescape(filename ? pathJoin(outputPath, filename) : outputPath); }
看上面代码,我在进入函数的头部打了一个log,看看传入的参数究竟是什么
当我访问 http://localhost:8082/m/ 的时候 控制台里输出
发现 publicPath, outputPath, url,三个参数的值分别为:
/m/ F:workspacewx2share-pwadist /index.html
终于发现
if(publicPath && localPrefix.hostname === urlObject.hostname && url.indexOf(publicPath) !== 0) {
return false; //就这里return false了 }
url.index.of(publicPath) !== 0 这一个条件成立了 至关于 '/index.html/'.indexOf('/m/') 确定不会===0 啊,
可是这个‘/index.html' 这个参数的值哪里来的,回到上一段代码中,发现是req.url里传过来的,
但我明明访问的是/m/ 哪,req.url 应该等于 '/m/'啊,这是何时给重定向的呢,看来在这个以前,已经有过一次重定向了
回到dev-server.js文件,发如今,use devMiddlewarre 以前,还引入了一个connect-history-api-fallback的中间件,看来惟一能重定向的地方只有这里了,
app.use(require('connect-history-api-fallback')()); // 服务器部署 webpack 打包的静态资源 app.use(devMiddleware); // 使用热更新, 若是编译出现错误会实时展现编译错误 app.use(hotMiddleware);
打开connect-history-api-fallback的代码,终于发现了
rewriteTarget = options.index || '/index.html'; logger('Rewriting', req.method, req.url, 'to', rewriteTarget); req.url = rewriteTarget; next(); 这个的代码,当你不做配置的时候,默认重定向到根目录的/index.html上,找到缘由了,修改就简单了 // 处理 history API 的回退状况(若是在线上环境中,也须要服务器作相应处理) app.use(require('connect-history-api-fallback')({ index: '/m/index.html' }));
把options.index的值设为 /m/index.html 不就能够了吧,这样全部的请求,都会转发到/m/index.html了
再次打开http://localhost:8082/m/ 一切所有正常了,build之后,发如今线上,也彻底正常,不再会找不到css中引入的资源文件了
一个重点差点忘记提了
就是如今写css代码里,引入static文件中的文件,直接要写加上publicPath的路径,
就是原来写成
.content { background: url('/static/img/1.jpg') }
要在编码阶段所有写成
.content { background: url('/m/static/img/1.jpg') //直接带上发布是的绝对路径 }