网站内文章标题格式,成都网站备案,前端开发能干到多少岁,html中文网标签#xff1a; NodeJS 0 一个星期没更新了 一直在忙着重构代码#xff0c;以及解决重构后出现的各种bug 现在CSS也有一点了#xff0c;是时候把遇到的各种坑盘点一下了 1 听歌排行 API 修复与重构 1.1 修复 在加载云音乐听歌排行的时候#xff0c;有时会出现一个奇怪的…标签 NodeJS 0 一个星期没更新了 一直在忙着重构代码以及解决重构后出现的各种bug 现在CSS也有一点了是时候把遇到的各种坑盘点一下了 1 听歌排行 API 修复与重构 1.1 修复 在加载云音乐听歌排行的时候有时会出现一个奇怪的bugjson数据无法被解析。如下图 在刷新页面后问题就会得到解决。此后无论怎么刷新问题也不会出现。 过一段时间再次打开页面会出现相同的问题刷新之后也可以解决。此时换用其他各种浏览器都不会出现问题但一段时间之后仍会重现一次。。。 那肯定不是浏览器的锅了。把Response的内容复制出来看看。 粘贴格式化。VSCode报出了4个警告和一个错误再仔细看一眼哎怎么中途截断了难道是收到的请求不全 返回去看看接收请求收到的JSON文件没错啊是全的。当然了因为接下来刷新几次之后就不会在遇到此问题了。在本地测试中也发现只有服务器启动之后的第一次访问才会出现这个问题。 找到输出的位置在这里下断点开始调试。 从server.js进来的时候文件还没有被创建到36行建立请求38行绑定事件回调49行发送。 接收到数据触发response事件命中断点。 解压缩输出这时候检查一下输出的文件0 KB。跑到下一步callback传出文件名这时候检查输出文件0 KB。 等下怎么会是0 KB这时文件还没有写入完成就已经把文件名传给回调函数然后开始读取了 然后就进入了各种不明所以的内部库调用跳出之后检查输出文件37KB。这里才刚刚写入完成自然浏览器那边还是没法解析传出来的数据还是不完整即使输出文件已经是完整的了。 有没有联想到一些东西是IO效率的问题或者说文件操作也是异步的需要等待一个事件 好马上去查一下Stream的API文档找到了Stream.Writable的finish事件。这个事件在所有数据写入完成之后被触发。好要的就是你。 将代码修改如下 response.pipe(zlib.createGunzip()).pipe(output);
// wait for file operation
output.on(finish,() {fs.readFile(outputFileName, (err, data) {var buf JSON.parse(data.toString())[/api/user/detail/76980626].listenedSongs;bufJSON new Array();buf.forEach((value, index) {if (index 9) return;bufJSON.push({ id: value.id, name: value.name, artistName: value.artists[0].name });});});
}); 在等待文件操作完成之后才读取数据而且读到数据后只取出自己需要用到的部分存在全局数组bufJSON中当作缓存顺便提高一下API响应速度。 1.2 重构 之前API获取的听歌排行目标用户是写死在代码里的。可以写一个init()函数初始化它的获取目标用户。 function init(id) {userId id;outputFileName netease_music_record_${id}.json;
} 在写入请求body的时候要把请求数据转化成QueryString的格式。Node.js提供的QueryString模块可以接受一个Obejct作为参数输出字符串不过可变值的多行字符串并不能作为对象的属性名。也就是说 var postData {/api/user/detail/${id}: {\all\:true}
} 是会报错的对象属性名非法。这下我们就需要引入Map这个数据类型了只要是合法的字符串就可以当作数据的键和值。像这样 var req http.request(options);
var qString new Map();
qString[/api/user/detail/${userId}] {\all\:true};
req.write(qs.stringify(qString));嗯API的优化就说到这里了代码都在文章最下方的Git仓库里我也会时不时进行一些抽风似的重构不可能一一讲述了。 2 服务器端页面渲染 说到动态页面直接用JS在浏览器里操作不就行了还关服务器什么事这样虽然很方便不过有一个弊端不利于搜索引擎爬虫的索引。自己博客里写了这么多文章当然希望更多的人可以通过搜索引擎找到而不是整天放在那里无人问津吧。 好那就来动态的构建一个404页面可以显示当然服务器正在运行的Node版本。 之前我们的404页面是这样的。可现在Node.js的current版本已经到6.4.0了就先从这里下手吧。 通过Node.jsAPI文档了解到要获取当前node版本号只需要使用porcess.version。如何吧这个版本号替换进404页面的html文件中去呢我想到的方法是把html中的版本号改成一段特殊的字符串然后用正则表达式去唯一的匹配他。比如这样 pNode.js - ${process.version}/p 然后我们建立正则表达式去匹配那个字符串。但千万不要在html文档的其他地方使用这个“占位符”它会被全部替换成版本号。也可以再在后面加一些其他无意义内容反正要避免正常的代码或文字与它重复。 fs.readFile(path.join(root, /page/404.html), (err, data) {var versionRegex /\$\{process\.version\}/;var nodeVersion process.version;var current404 data.toString().replace(versionRegex, nodeVersion);var page404 fs.createWriteStream(path.join(root, /page/current404.html));page404.end(current404, utf8);
}); 读取文件转换字符串然后生成了新的current404.html文件。之后发送404页面的响应也要改成发送刚刚生成的current404.html。 把这段代码放在server.js靠前的部分相当于变量初始化的位置然后运行测试吧 好的效果达到了。 3 使用 history.pushState()改变 URL 并局部刷新页面 Ajax都很熟悉吧Asynchronous Javascript And XML再加上pushState就变成了Pjax。 没什么神秘的history.pushState()的作用就是改变页面的URL并将一个state对象储存起来。这个state对象是自己定义的。在事件window.onpopstate的回调函数中传入的参数的state属性是之前储存起来的state对象。 简单来说使用history.pushState()会改变当前页面的URL但仅仅是改变浏览器并不尝试去加载他只是摆在那里同时会将URL与传入的state对象一起压入历史纪录栈中。当用户操作浏览器前进或后退时如果操作后当前页面的URL是由history.pushState()方法压入栈中的那么页面将不会被重新加载window.onpopstate的回调函数会被执行。 有关更详细的介绍请看操纵浏览器的历史记录 - DOM | MDN。 我的目的是在用户单击了首页的标题文章标题时URL改变但以Ajax的方法从服务器加载文章内容显示在页面上。而当用户直接访问这个URL时又能提供完整文章浏览的页面。 为此先要在主页上动动手脚使得点击文章之后让他看起来像一个浏览页面 !DOCTYPE html
html langenheadmeta charsetUTF-8titleRockas Node Blog/title
/headbodyh1Rockas Node Blog/h1hrh3 idindex-article-title styledisplay:none;Title should be shown here./h3blockquote idindex-article-content styledisplay:none;Article should be shown here./blockquoteh3 idindex-article-headerBlog Archive/h3ul idindex-article-list/ulh3Rcecntly Listened/h3ul idindex-music-record/ul
/body/html 新加入的元素被设置为了不显示我们总不能在一个主页上就显示文章内容吧。在用户点击文章之后再改变历史记录同时变更页面的样式让它看起来像一个文章浏览页面。于是在loadArticleContent的success回调中我们这样写 function success(response) {history.pushState({originTitle: articleTitle,type: archive,originPathName: window.location.pathname},articleTitle,/archive/${articleTitle});// switch element visibilityshowArticleContnet();document.getElementById(index-article-title).innerText articleTitle;document.getElementById(index-article-content).innerText response;
} showAtricleContent函数用来切换各种元素可见性把#index-article-header和#index-article-list隐藏#index-article-title和#index-article-contnet显示这里就不展开写了。el.sytle.displayblock或者none就好。之后还会有一个showIndex函数都懂这个意思看看就好。 还有就是history.pushState()的三个参数第一个是要压入的state对象第二个是名称可以传入空字符串或者当前文章名称因为这个属性在现在并没由什么用处MDN是这么说的。第三个就是要变成的URL了规定好自己的URL地址。我这里用的是与文章文件相同位置的地址。 然后看看效果 URL被改变了内容也成功加载出来。可是如果现在后退的话虽然URL会变回去但却不会产生任何效果。这时要给window.onpopstate绑定回调函数 window.onpopstate (e) {if (e.state) {loadArticleContent(e.state.originTitle);} else {showIndex();}
} 这个e.state是我们之前pushState的时候压入历史记录栈中的里面存储的是跳转到的标题。同样如果没有state应该是后退到了主页上显示主页。 现在测试点击跳转了后退正常前进正常后退后退。。。。哎不对啊怎么退不回主页了还记得loadArticleContent吗我们调用它的时候直接使用了pushState。但在window.onpopstate的回调函数中也是调用了它。这也就意味着当我们操作页面前进时又会有一条历史记录被压入栈中然后再后退又多了一条每次后退又会多一条。虽然我们的位置后退了但在我们前面又增加了一条记录这样永远也回不到主页。 所以在加载文章内容时做出判断如果此次加载来自历史记录操作加一个参数就好那么不再增加历史记录 function loadArticleContent(articleTitle, fromState) {function success(response) {if (!fromState) {history.pushState({originTitle: articleTitle,type: archive,originPathName: window.location.pathname},articleTitle,/archive/${articleTitle});}showArticleContent();document.getElementById(index-article-title).innerText articleTitle;document.getElementById(index-article-content).innerText response;}// other more operations......// ......
}window.onpopstate (e) {if (!e.state) {showIndex();} else {loadArticleContent(e.state.originTitle, true);}
} 至此在不刷新的前提下主页的操作正常了。 4 动态构建文章阅读页面 借助pushState我们时可以改变URL了可是这个页面实际上是不存在的一刷新就没了。如果别人想要收藏你的博客文章不就很尴尬了。。。所以我们要动态的构建一个阅读页面出来。 刚才在处理首页的时候把元素隐藏了一下就变成阅读界面了。这里先把首页复制一份稍加改动就变成了文章阅读页面view.html !DOCTYPE html
html langenheadmeta charsetUTF-8titleRockas Node Blog/title
/headbodyh1Rockas Node Blog/h1hrh3 idindex-article-title${article.title}/h3blockquote idindex-article-content${article.contnet}/blockquoteh3 idindex-article-header styledisplay:none;Blog Archive/h3ul idindex-article-list styledisplay:none;/ulh3Rcecntly Listened/h3ul idindex-music-record/ul
/body/html 这里我把对应元素的内容也都换成了“占位符”方便匹配。接下来当用户请求文章页面的时候就像生成404页面一样先读取模板然后将占位符用相应的数据替换。唯一不同的一点是不要把输出后的文件缓存到当前目录否则加载文章列表要读取文件的时候会多出一些奇怪的东西。 在服务器启动监听端口之前先把原始的文章阅读页面存入全局变量也是相当于变量初始化吧 fs.readFile(path.join(root, /page/view.html), (err, data) {// read origin page in advanceplainViewPage data.toString();
}); 之后每次请求时只要复制存在全局变量里的字符串然后修改副本 fs.stat(filePath, (err, stats) {// no error occured, read fileif (!err stats.isFile()) {if (pathName.indexOf(/archive/) 0) {var archiveRegex /archive\/(.)/;var titleRegex /\$\{archive\.title\}/;var contentRegex /\$\{archive\.content\}/;var title archiveRegex.exec(pathName)[1];fs.readFile(path.join(root, pathName), (err, data) {var page plainViewPage;var page page.replace(titleRegex, title);var page page.replace(contentRegex, data.toString());response.end(page);});} else {// normal file read}} else {// file not found}
}); 现在问题来了上一步pjax的时候请求文章内容的URL已经是文章的“真实”URL了。如果再把这个URL分给文章页面是否会产生冲突 当然会了不过我们有办法避免。在我们异步请求文章内容的时候是一个GET请求浏览器刷新页面时也是。但在创建XMLHttpRequest的时候可以给它设置一个特殊的请求头比如pushstate-ajax之类的用于区分动态加载和页面获取。值得注意的是只有在请求open之后send之前才能设置请求头 var request new XMLHttpRequest();request.onreadystatechange () {if (request.readyState 4) {if (request.status 200) {// do sth with resopnse} else {// oops~~}}
}request.open(GET, /archive/${articleTitle});
// set special request header
request.setRequestHeader(pushstate-ajax, true);
request.send(); 同样在服务器端也需要进行一些判断 如果是正常的页面请求没有特殊请求头就要返回替换了文章内容的查看页面否则只需要返回文章内容if (request.method GET) {if (pathName.indexOf(/api/) 0) {// api request} else if (request.headers[pushstate-ajax]) {// return article coontent only} else {fs.stat(filePath, (err, stats) {if (!err stats.isFile()) {if (pathName.indexOf(/archive/) 0) {// return mixed view.html} else {// normal file}} else if (!err pathName /) {// goto index} else {// return currnet404.html}});}
} 5 好了今天就写到这里。其实我还落下了一次更新现在的实际进度已经达到了额还是点开下面的App地址看一下吧我也不好形容。我会抓紧把剩下的坑都填好的 仓库地址 GitHub仓库BlogNode 主仓库以后的代码都在这里更新。 HerokuApprocka-blog-node 上面GitHub仓库的实时构建结果。 转载于:https://www.cnblogs.com/rocket1184/p/nodejs-heroku-blog-4.html