Skip to content

Latest commit

 

History

History
740 lines (517 loc) · 25.8 KB

blog.md

File metadata and controls

740 lines (517 loc) · 25.8 KB

title: Development Calendar author: azlar date: '2016-09-08 21:20:20' tags: [develop calendar] ignore: false


记录开发 blog 的过程。

todo (del 线代表已完成...这个 del 线有点蠢)

功能

  • 根据 Widget 宽度与 TagsArticles 的字体大小计算其最大显示字符数
  • List 样式 (page 的 comments 数量、样式)
  • Article 样式
  • Pagination
  • build.js 移动文件问题(现在需要 sudo 权限,由于有一个 .DS_STORE 文件存在)
  • build.js => new post(命令生成 yaml 配置文件)
  • 归档
  • SEO (sitemap)
  • !!! github pages static html map
  • 初次加载的时候,config.json 请求了两次:WidgetListComponent 同时发起了 ajax 请求
  • 列表筛选,最后更新时间(由于很多流水账会在不同时间续写)
  • time-line 与 archive
  • build 脚本新功能,自动部署到仓库
  • 表格问题,macdown 的表格,marked 貌似不支持,需要自己分析下
  • 逻辑优化。。。以前的代码看着太蛋疼了,就差重构了。。。(17.10.4)
  • /demos/ 文件夹,每次在 build 之后可能会丢失
  • 将目录、定位锚,改为该条字段的拼音,用数字会导致定位不准,非常不便于其他文章指向引用
  • 将 md 文件夹原目录下 (/files/) 抽出到了一个单独的仓库:https://github.com/azlarsin/blog-source

bugs

  • tootip of tags
  • 迁移项目目录,重构目录结构
  • css => scss 与样式重新整理、规范
  • 回到顶部小火箭的 hash
  • 列表页简介部分可能含有 markdown 语法
  • 文章内的 tags 不能有 - 否则转义失败(由于 tag 之前有空格,路由内将空格替换为 - ,故在解析的时候,转义后不能解析,列表页会过滤失败)

开发

16.9.9

折腾了一天,把代码高亮从 prism.js 换到了 highlight.js。其实使用上感觉并不明显,主要是后者高亮主题多(然并卵,还是自己边抄边调了一套高亮)。

后台编译(伪)程序 build.js,添加了一些处理细节,之后要开始写 tag-map 的生成。

自己在设计方面还是太差,设计出来的东西简直非人能看。

16.9.11

  • 左部挂件: tag-list
  • 挂件中的 tag-listlist 中的 tag 的选中状态联动。

16.9.12

文章预览

本来使用的方式是从 head 信息 后截取 140 字符,再向后寻找到第一个 “。”;使用之后发现效果并不好。

于是使用了在 md 文件 中添加一个 <!-- desc --> 这种方式来实现(参考 hexo 的预览文本方式)。这种方式可定制性更强。

P.S. 再次感慨很多时候不能纯凭机器,有时一个标识就可以解决的问题,用语言却可以派生出很多问题。

long tag

tag-list 中,会截取一部分(:hover 的时候提示显示),在文档列表页,不会截取: ![long tag hover hint](//blog.azlar.cc/images/long tag hover effect.gif)

文件移动

调用 build.js 的时候,会同时移动图片了,但是不能使用相对路径(由于 index.htmlbuild/ 的外面)。

正在考虑要不要将 index.html 移动到 build 内部。

建立关联文章 map

通过分析每篇文章的 tags,获取拥有相同 tag 的文章。本来是在前端分析的,前端比较麻烦,而目前我是充分相信后端数据的,故使用后端直接分析得出 index 比较方便简单。

//code in build.js
realListMap.map((article, index) => {
    let articleTags = article.tags;
    let articleRelated = [];
    for(let i = 0;i < articleTags.length;i++) {
        articleRelated = articleRelated.concat(tags[articleTags[i]].filter((item) => {
           return articleRelated.indexOf(item) < 0;
        }));
    }
    
    article.related = articleRelated;
});

16.9.13

tag-list 排序

按文章多少排序:

var sortedKeys = Object.keys(tags).sort(function(a,b){return tags[b].length - tags[a].length});

//sortedKeys.map((tag) => {...});

auto scroll tag.selected into viewpoint

自动将当前页面,tag-list 中被选中的 tag 滑动显示。一般在选中 tag 后,刷新页面,或者点击 :site/tag/xxx 直接进入页面时会用到。

//componentWillReceiveProps -> setState() -> callback()
let selectedTag = $(".tag-block.selected");
selectedTag[0].scrollIntoView({ behavior: "smooth" });

使用后发现,每次点击都会触发,体验很差,故需要判断其是否在视图 viewport 内;这个问题刚好之前研究过(两个 rect 是否相交)。之前有收藏一篇 how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport,从里面选取了一个方法(有点懒得看之前写的判断两矩形相交):

function isElementInViewport (el) {
    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

于是在视图内的 tag 被选中就不会自动滚动至顶部了,因为不会触发 scrollIntoView()

var selectedTag = $(".tag-block.selected");

if(selectedTag.length > 0 && !isElementInViewport(selectedTag)) {
    selectedTag[0].scrollIntoView({ behavior: "smooth" });
}

16.9.14

部署倒 github pages

今天把项目部署到了线上后才发现一个 2b 的事情:github pages 并不支持 rewrite

想了很多办法,甚至一度跑去开通了 amazonaws,可是在这个过程中,心里一直念叨着:我写这个不就是为了弄一个纯静态的么,上 vps 不是坑自己么。。。

github 说对 jekyll 有一个插件支持,但我没调试那个。

于是想通过 hack 办法来实现,发现 gp 所有找不到的页面都会定向到 404.html,所以直接将该页面写为类似 index.html 的入口文件。问题解决。。

但是这样做应该有明显的问题:

  • seo 问题
  • 资源引用路径必须都使用绝对路径

故这只是一个临时的办法,若想实现静态化,除了虚拟出所有的 html 文件,貌似没有其他办法。(这个会在 build.js 生成文件的时候,将所有的 html map 生成)

P.S. 404.html 真是好 low 的办法~

移动端样式

部署后顺手写了点样式,由于之前的一部分是在 PB 项目开始之前写的,发现之前的样式写的好 low,又提不起精神改,头痛。。。

将样式整理与 scss 重写放进 todo 吧 -。-

16.9.19

静态文件

终于把静态化的脚本写完了,文件目录变成了这样:

基本和 hexojeykll 一个样子了,并且更搓的地方是,我这个还得走请求去请求 md 文件然后解析,还不如直接做全静态得了。

不过考虑以后可能会迁移到服务器,到时候会用数据库,所以现在就暂时保持这个蛋疼的模式吧。

路由追加 trailing slash

为了配合静态路由,将所有的路由模式由 domain/about 改成了 domain/about/

16.9.20

new post

终于把这个给写了,会生成一个 .md 文件:

node build.js -new about
about-1.md (about.md exists.)
----
title: about-1
author: azlar
date: '2016-09-20 17:45:14'
tags: ''

---

<!-- desc -->

一些小功能:

  • yaml 信息写入
  • 检查文件名、更新文件名:
      `node build.js -new a` => `a.md`
      `node build.js -new a` => `a-1.md`
      `node build.js -new a` => `a-2.md`
      `node build.js -new a-1` => `a-1-1.md`
      `node build.js -new %a]` => `a-3.md`
  • 默认加了 desc 标识符

16.9.22

tag-widget、article-widget 字符数自动适配

思路:

  • 计算 widget-panel 的宽度与字符能使用的宽度(widgetPanelWidth - padding-left - padding-right
  • 计算最多能允许显示多少字符,截取
  • 通过 state 切换,动态控制字的截取
  • window 绑定 resize 事件

效果: ![tag-widget resize event](//blog.azlar.cc/images/tag-widget resize event.gif)

遇到的坑:

  • unicode 字符截取、长度的计算。
  • window.resize 监控 resize end 的事件。
    clearTimeout(window.resizedFinished);
    window.resizedFinished = setTimeout(() => {
    var widgetPanel = document.getElementById("widget-panel");
    if(null !== widgetPanel) {
    	
        this.setState({
            widgetPanelWidth: parseInt(window.getComputedStyle(widgetPanel).width)
        }, () => {
            console.log(this.state)
        });
    }
    	
    // alert('Resized finished.');
    }, 250);

16.11.7

live code 运行

对某些特殊的代码(一般是 javascriptcanvas),可以在线运行预览了。

.md 内代码块第一行添加:

#run: canvas	

解析时,程序会为该代码块分配一个写好的 canvas 运行环境(iframe)。

live code bugfix

描述

运行代码,是通过一个新组件 <LiveCode /> 实现的,是 <Content /> 的一个子组件。

由于需要通过 iframe 控制,而不巧的,<Content /> 的顶级 wrapper 使用了 transform 属性,故置于其中的元素不能使用 position: fixed; 浮起来了。

只能把 <LiveCode /> 作为一个实现层,编译好代码后,挂载到 <App /> 下的 live-code-wrapper 中。

<!--APPComponent render()-->

<div className="blog" id="blog">
	<!-- ... -->
	<div className="overlay live-code" onClick={ this.__closeLiveCodeLayer }>
	    <div id="live-code-wrapper">
	        <span className="close" onClick={ this.__closeLiveCodeLayer }>x</span>
	    </div>
	</div>
</div>

这个时候便会触发一个比较尴尬的问题,<Content /> 不知道 live-code-wrapper (overlay) 什么时候被关闭了,会不断的触发其子组件 <LiveCode />componentWillReceiveProps 方法。

造成关闭 overlay 后,点击文章中任意链接仍会触发 <LiveCode /> 中的编译挂载(上一个被关闭的运行结果不断浮出来)。

解决

<Content /> 传递给 <LiveCode /> 的参数中,追加一个唯一串(每次点击 run the code 的时候自动更新),用来区分此时(<LiveCode />)是否需要运行代码。

if(this.props.uuid !== props.uuid) {

	//generate the code & refresh the iframe 
	
}

16.12.16

增加了分页支持

目前还没支持刷新,有点懒得写呀 23333

后边重新增加了 404.html,可以刷新了。

17.2.7

为代码块增加了语言显示

在代码块右上角增加了代码语言显示。

17.10.3 - 17.10.4

自己从自己的中秋礼物,keep going~~

react react router 升级

放弃了 router 2.x,拥抱新变化吧,升级的过程还是蛮有意思的。

{
	"react": "^16.0.0",
	"react-dom": "^16.0.0",
	"react-router": "^4.2.0",
	"react-router-dom": "^4.2.2",
	"whatwg-fetch": "^2.0.3"
}

router change:

ReactDom.render(
    <Router history={ history } onUpdate={ logPageView } >
        <Route path="/" component={App} BaseOrigin={BaseOrigin}>

            <Route path='article/:articleName(/:ext)/' component={Content} />

            <Route path='about/' component={ Content } params={{ articleName: 'about' }} />

            <Route path='(tag/:tag/)(p/:p/)' component={ List } />

            <Route path="t" component={ Test } />

            <Route path="*" component={ List } onEnter={ enter }/> />

            <IndexRoute component={ List } />
        </Route>
    </Router>
    ,
    document.getElementById('qwe')
);

to:

// in AppComponent
<Route path="/" component={ this.props.logPageView } />
<Switch>
    <Route path='/article/:articleName/:ext?/' component={ Content } />
    <Route path='/about/' render={ (props) => <Content { ...props } articleName={'about'} /> } />

    <Route path="/p/:p/" component={ List } />

    <Route path="/tag/:tag/" component={ List } />

    <Route path="/tag/:tag/p/:p?" component={ List } />

    <Route path={ '/(tag/:tag/)?(p/:p/)?' } component={ List } />

    <Route path="*" component={ List } />
</Switch>

改写、优化

现在看以前的代码,真是设计的惨不忍睹,很多地方重复性太高、结构太复杂(乱);用 childContext 改写了一部分,设计了一小部分新逻辑。

另外使用了 fetch,具体的还得测试。

remove jquery

最近很长一段时间,精力都在原生 js 上,所以这次下笔写,还蛮快的,也没怎么查文档(AC...),包的体积又小了很多(笑...)

一点点心得

学习的过程是个积累的过程,尤其是思维逻辑上的学习、自我理解,只有用的时候才会感觉到,自己有没有变强。(喂,是在吐槽自己去年的代码太呕吐了么。。。。)

17.10.14

优化

改了下 content 页内,作者与日期、标题的样式。

生成了 manifest.js 文件,并将所有资源文件移入 'assets' 文件夹。

17.11.11

将 md 文件夹抽到了一个新仓库

由于新工作电脑使用内网,不方便将旧电脑整体迁移过来,但又想在工作电脑上写文章,所以将其提到了一个新仓库。

repo

https://github.com/azlarsin/blog-source

app

主要是在配置文件中增加了一个 origin: SOURCE_FILE_URL,在 ContentComponent 组件内获取文章时,使用这个 origin;其余逻辑不变。

build.js

以前是要将 /source/files 移动到 /build/files 以方便发布,现在这段逻辑整体废弃掉,可以通过维护新 repo 实现更新了。

这里要注意下 build 的时候要移除 repo 中的 README.md

18.1.30

archives

归档功能。

本来是用 node 处理文件生成 archives.md,后来想了下觉得没必要,直接分析 config.json 渲染组件即可。

auto push

在 build 脚本中增加了自动推送的功能,开发起来方便多了。

维护

16.9.8

由于顶部有 navigation-bar 的存在,导致页面自动定位 anchor 的时候,会被遮蔽,故需要

  • 方法1, css
.blog .wrapper .content *[id]:before {
    display: block;
    content: "";
    margin-top: -82px;
    height: 82px;
    visibility: hidden;
}
  • 方法二,js listen window.hasChange && locatin.href,then auto scroll the navigation-bar.height。

这种方法会有一个问题:点击同一个 hash 的时候,浏览器会认为当前位置并不是该 hash 所在的位置,然后重新定位,而此次定位,不会触发刚才写的 auto scoll 方法。

解决:为目录中的 <a> 添加一个 listener:该 hash 与当前 location.hash 相同的时候,preventDefault();

16.9.11

发现 切换 tag 时样式变化错误的 bug,尝试各种方式修复不能。

16.9.12

缩小了列表页,文章标题下 tag 的字体大小,由于有时候 tag 过多、过长,选中增大以后会比较难看。

Widget 组件中对 shouldComponentUpdate 做了一个单独处理:

shouldComponentUpdate(nextProps, nextState) {
    return nextState.nowTag != this.state.nowTag;
}

由于 shouldComponentUpdate 默认返回 true,故会造成一些不必要的更新(WidgetpropsApp(Root Component) 传入,又由 App 的某个子组建来更新,故 Widget 会被多次触发相同 tag 的 更新)。

16.9.13

瞎忙一天,就为了解决这个问题:css-absolute-element-hid-by-overflow-auto

目前发现的是 chrome 解析没有父级约束 .parent{ position: relative; } 的元素的时候,在 .grandpa {overflow: auto} 存在并且 触发滚动 的时候,判断独立的 absolute 元素会基于其原有位置。

这个问题留待解决啊。

P.S. 感冒了,脑子不是很清醒。

16.9.14

感谢 stackoverflow 的小哥给出的解决方案:jsfiddle,他的坚持不懈真是让我倍感羞愧。这个问题我还是准备先留一下,看看(不久的)将来有没有更好的解决方案。

16.9.18

移动文件需要 sodu 权限 解决

使用 fs-extra 的时候,由于没有权限操作 .DS_STORE,所以加了 sodu,后来觉得不方便,将 build 文件夹删除后,再删除 source 内的 .DS_STORE 就可以不用 sudocopy 了。

16.9.20

统一了 date 的格式

如果不是字符串(无引号):

---
date: 2016-09-08 21:20:20
---

会导致 yaml 读取的时候自动转换成 Date(),操作起来还得转成 UTC,于是直接将所有的时间写成字符串:

---
date: '2016-09-08 21:20:20'
---

这样就不用每次读的时候转换了。

16.9.21

优化了一下创建新文件的命名问题

美化了一下文章标题里含有 空格的情况,由于 \s-\s 我经常会使用:

bug
`node build.js -new 'a - b'` => `a---b.md`

fixed
`node build.js -new 'a - b'` => `a-b.md`
`node build.js -new 'a -        b'` => `a-b-1.md`
`node build.js -new 'a            -        b'` => `a-b-2.md`

16.9.26

markdown 格式错误

macdown 的格式与 marked 略有差异。写 list 的时候,代码块需要挨着上行内容,不能隔行写,否则转义出来的格式有问题。

手机显示文章日期显示错误

由于之前的显示方法是对 yaml 信息进行转义显示,手机转义失败(new Date(date) 失败),这次直接当做字符串处理了(因为之前保存的时候已经当做字符串处理而部当做 date 信息保存)。

左侧 Widget bug

描述

当浏览一篇文章时,点击 about 页,Widget 会由于之前隐藏了,在渲染 Tags 组件的时候,计算出来的宽度是 0;随着 scrollbar 自动滚动到最上,widget 重新获取到宽度,而这个时候左侧的 tags 已经渲染完毕,tag 显示全部被缩略。

解决

Tags 组件内,初次渲染时,宽度计算由 parseInt(window.getComputedStyle(widgetPanel).width) 变为 document.body.offsetWidth * 0.22 * 0.9(与 css 一致)。

16.10.17

border-radius

panel 增加了 4px 的圆角,不知道以后会不会抽风改成直角。。

plain 显示

转了一篇纯文本,增加了这个显示模式。

.md 内的头信息内,增加:

plain: true

会在文章处,获得一个段落缩进的效果。

16.11.18

重写了文件 build 时的生成规则

先来看几条命令: 之前一直使用 title 做判断,即生成同名文章的时候,title 亦会被修改。

现在在生成 file 的时候,由 title 生成了一个临时变量 fileName,临时存储在 yamlInfo,此变量即是文件名,会在判断重名后、生成文件前被销毁。

file 1:

node build.js -new 'wewe-ewasd asdas'

# wewe-ewasd asdas created.
# It will open after 2 second...
# opening ./source/files/wewe-ewasd-asdas.md

file 2:

bogon:code_b azlar$ node build.js -new 'wewe-ewasd asdas'

# wewe-ewasd-asdas-1 created.
# It will open after 2 second...
# opening ./source/files/wewe-ewasd-asdas-1.md

此时,两篇文章的 title 仍然是 wewe-ewasd asdas,而文件名不同(route 不同)。

16.11.22

添加了 https 支持

参照:

https://zhuanlan.zhihu.com/p/22667528

使用 cloudflare.com 为博客增加 https 支持。

为图片资源添加了 lazyload

采用了一个原生的库:https://github.com/verlok/lazyload

暂时先去掉了,效果并不好,一直加载图片资源的问题(disable_cache 下,如果打开某篇文章后再打开别的文章,此时会卡住,必须等到上一篇文章的资源加载完毕),是由于文章都使用同一个 Component 导致的,得想办法处理。

16.12.20

重写了左侧 widget 的 scroll 事件

原来

今天发现之前写的:

$(".tag-box.block").on( 'mousewheel DOMMouseScroll', function ( e ) {
    var event = e.originalEvent,
        d = event.wheelDelta || -event.detail;
    
    this.scrollTop += ( d < 0 ? 1 : (Math.abs(d) == 0 ? 0 : -1) ) * 15;
    e.preventDefault();
});

wheel event 支持的并不好,体验比较差。

重写

问题的关键在于,滚动此 div 的时候,会触发 bodyscroll ,导致 body 滚动。

Firefox 比较好,在 scroll 事件里,增加一个 e.stopPropagation(); 即可防止 scroll 扩散到 body

chrome 没啥用,只能手写阻止:

1. 找到当前的 scroll position
2. 在顶部(底部),继续向上(下)滚动时,阻止事件,防止触发 body

代码(使用 onWheel 绑定到 widget 上)

//end
if (
    e.currentTarget.scrollTop === (e.currentTarget.scrollHeight - parseInt(window.getComputedStyle(e.currentTarget).height))
    && e.deltaY > 0
) {
    e.stopPropagation();
    e.preventDefault();
}

//top
if (e.currentTarget.scrollTop === 0 && e.deltaY < 0) {
    e.stopPropagation();
    e.preventDefault();
}

这样会遇到的问题:滚动完毕后,移动鼠标的焦点其实还是在 widget 上,继续滚动,仍然会触发 onWheel,导致 body 不能滚动。

于是为 onWheel 增加了一个 if(isMouseOver) 判断,isMouseOver 会在 onMouseEnteronScroll 时设定为 trueonMouseLeave 的时候设定为 false

P.S. onScroll 修改 isMouseOver,是因为 mac 支持无焦点滚动,这时候当前页面没有获取到系统的焦点、mouseEnter 并不会触发。

17.9.22

改了很多东西。。

loading 重做

webpack 压缩重写

分段

文件分为 vendor.jsmain.js,另外把 css 文件也拆出去了。

文件版本号

由于之前没有版本控制(其实主要是面试被问到这个),于是突发奇想,为项目配一个版本号,使用 webpack 插件的形式书写:

// webpack config ...
plugins: {
    function() {
        this.plugin("done", function(statsData) {
            let stats = statsData.toJson();

            fs.writeFile("bundle-info.json", JSON.stringify(stats, null, 2), () => {});

            if (!stats.errors.length) {

                let publicPath = path.resolve(__dirname, "build"),
                    files = ["index.html", "404.html"],
                    css = stats.assetsByChunkName.app.find(assetName => path.extname(assetName) === ".css" ) || "main.css";

                files.map(fileName => {
                    let filePath = path.resolve(publicPath, fileName),
                        html = fs.readFileSync(filePath, "utf8");

                    if(html) {
                        let htmlOutput = html
                            .replace(/<script src="(.*)"><\/script>[\r\n]*<script src="(.*)"><\/script>/g, `<script src="//blog.azlar.cc/${stats.assetsByChunkName.vendor[0]}"></script>\r\n<script src="//blog.azlar.cc/${stats.assetsByChunkName.app[0]}"></script>`)
                            .replace(/link rel="stylesheet" href="(.*)"/g, `link rel="stylesheet" href="//blog.azlar.cc/${css}"`);

                        fs.writeFileSync(filePath, htmlOutput);
                    }
                });
            }


            //delete junk files
            let allFiles = new Set(fs.readdirSync(path.resolve(__dirname, "build"))),
                assetNames = new Set(stats.assets.map(asset => asset.name).concat([stats.assetsByChunkName.vendor]));

            let junkFiles = new Set([...allFiles].filter(x => !assetNames.has(x)));

            // console.log(junkFiles);

            for(let fileName of junkFiles) {
                if([".js", ".css", ".map"].indexOf(path.extname(fileName)) !== -1) {
                    fs.unlink(path.resolve(__dirname, "build", fileName), () => {});
                }
            }
        });
    }
}

这样就可以在打包后自动替换相关文件了,在 dev 模式中,可以直接输出到普通文件,这样就可以继续使用 debug 了。

webpack 压缩过大

主要原因是使用了 node-uuid 这个库,加载了过多依赖。

17.10.4

Articles 挂件中 Related Post 的链接有点问题,缺失最后的 '/'。

17.10.14

修正了 webpack 每次生成文件时,vendor.[chunkhash].js 的 hash 会改变的问题:

{
	output: {
		path: path.resolve(__dirname, "build", "assets"),
		// filename: 'main.[chunkhash:6].js'
		filename: "[name].[chunkhash:6].js"
	},
 
	plugins: [
		new webpack.optimize.CommonsChunkPlugin({
		    name: [ "vendor", "manifest" ],
		    path: path.resolve(__dirname, "build", "assets"),
		    // filename: "vendor.[chunkhash:6].js",
		    // minChunks: Infinity,
		}),   
	]
}

参考:webpack/webpack#1315

18.04.20

修改了 build.js 的自动推送,之前 bug:config.json 未推送(由于推送写在了写入 config.json 之前 :D)。

19.03.18

fix 了部分样式;to-top 增加了一个动画。