为什么很多前端工程师喜欢用hexogithub hexo 搭建博客客

椰壳的个人博客,github+hexo+coding搭建个人的网站和前端web学习的笔记
椰壳的个人博客
能一行解决的事情干嘛还要啪啪啪写半天,从网上收集一些js一行代码,自己用来理解和学习,感谢大神的code,每一个code会注名出处:
function getConstellation(month,day){
var s=&魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯&;
var arr=[20,19,21,20,21,22,23,23,23,24,23,22];
return s.substr(month*2-(day&arr[month-1]?2:0),2)+&座&;
function getConstellation(m,d){
return &魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯&.substr(m*2-(d&&&[2*m-2]?2:0),2);
几乎所有处理对象的JS程序都是使用属性、原型和函数来完成的。甚至元素或声音节点对象的特殊行为也是通过调用继承自函数属性的方法来进行访问。
前端深度学习库deeplearn.js
webGL的学习
vue-cli写一个视频播放网站
canvas的学习
git的日常使用:(供自己上传文件时查看)
git修改已经提交过的注释:
git commit —amend
HEAD指向的是当前的版本,因此git允许我们在版本的历史之间穿梭,使用git
reset —hard commit_id穿梭前,用git可以查看提交的历史,以便确认要回到那个版本要返回未来
git reflog查看历史,以便确认要回到未来的哪个版本
git init 初始化git目录
git add –all 添加修改了的文件
git commit 进入编辑器内修改文件的提交注释
如果要取消git的初始化只需要进入要取消的目录下运行
rm -rf .git
git checkout –
丢弃添加在仓库里面的文件
用于比较两个文件的不同
git checkout -b
新建一个分支然后自动切换到新的分支上面去
git push -u origin [新分支的名字] 远程分支没有本地新建的分支,所以要把本地的分支和远程联系起来 -u
是–set-upstream 将远程联系起来origin 是远程分支新分支的名字 与本地的新建的分支的名字一样14. git checkout .将修改的不必要文件回复到以前的样子
git push -f
本地的版本和远程的版本不同步的时候需要将本地的覆盖到远程的时候
或者是git pull将远程的的一个版本拷贝下来,本地仓库增添了一个版本,然后再选择性的添加项目版本
cherry-pick 把已经提交的commit, 从一个分支放到另一个分支
vue-cli搭建vue.js环境
Linux传输文件操作的常用命令行
初次搭建个人博客还请看官门多多见谅,以下是本人的个人介绍以及在实习期所完成的一些小项目。
慕课在线平台
git clone /yk2web/graduate-creation.git
点击more查看详情
超出长度显示省略号
white-space:
ext-overflow: ellipsis
css3计算calc和vw实现滚动条出现不跳动
.swap-outer{
margin-left:calc(100vw-100%);
webapp在Andriod中点击链接的时候会有淡蓝色的遮罩层`body{
-webkit-tap-highlight-color:rgba(0,0,0,0);
模拟textarea实现文本输入给div设置contenteditable=”true”属性可以让div也具
有文本输入的能力 div 然后设置: min-height:minman-height:maxoverflow:scroll
`.postArea{
min-height: 17
max-height: 68
给ios嵌入的web网页滚动的速度更加的平滑
-webkit-overflow-scrolling:
Cmd Markdown 简明语法手册搭建博客用什么前端_
搭建博客用什么前端
以下内容已过滤百度推广
日&-&可以利用github-pages 和jekyll来搭建个人博客,不需要服务器,服务器托管在github上面...方案1的缺点在于不能自由的设计页面和交互,那么可不可以写一个纯前端的blog放...&&普通
日&-&关于博客的搭建与前端的学审察,有什么好的推荐吗? 对于博客的认知还属于小白级别,求大神指点,有什么问的不对的还请大神多多指教~ 对于博客系统用什么比...&&普通
日&-&本人现在从事前端开发的工作,没有接触过后端开发,现在想要创建一个个人博客网站,需要怎样起步呢。我上网看了很多文章,什么什么关于wordpress啦之类的...&&普通
日&-&提问 利用什么技术可以快速的搭建个人blog,博客源代码使用是markdown编写,也可以是静态的,初步使用github托管。 请有经验的帮忙分享一下使用到的工具,...&&普通
日&-&图片3中json文件夹是存储网站需要的json数据格式的,ashx文件夹中用于测试服务器数据,plugin文件夹里面放的是js的各种框架。 zoedylan是前端的数据处理层,无需任何...&&普通
最佳答案: 简单的建站,你会的这些已经够用了,可以找些简单的虚拟主机,把静态的html、css等用ftp丢上去就可以了 如果你想要真正的学审察其他的一些技术,建议你到国外...更多关于搭建博客用什么前端的问题&&&&专业问答网站
我的个人博客总共展示了三个版本,界面也经历了由“简单”到“复杂”再到“简单”,颜从“泽单一”到“五彩斑斓”再到“局部点缀”的过程。原来一年一个版本!...&&普通
日&-&前端开发 如何搭建个人博客? 显示全部 关注者 66 被浏览 6609 添加评论 分享邀请...我们另外的一个网站:就网店| 就网站 也仅仅用了大概一个小时搭建。jiufree 和...&&普通
日&-&本人现在从事前端开发的工作,没有接触过后端开发,现在想要创建一个个人博客网站,需要怎样起步呢。我上网看了很多文章,什么什么关于wordpress啦之类的...&&普通
日&-&o 利用github page 搭建个人博客网站 o 实用的 html 前端组件源代码总结 o 用jekyll + github pages搭建个人博客 o 使用jekyll+github搭建个人...&&普通
语义关联近似词猜&正规性45地理位置网址标题|网址|摘要F0略略略略略略略精确匹配1略略略略略略略精确匹配2略略略略略略略精确匹配3略略略略略略略同义词4略略略略略略略部分匹配5略略略略略略略部分匹配6略略略中略略略部分匹配7略略略略略略略部分匹配8略略略略略略略精确匹配9略略略略略略略部分匹配10
12时间限制猜&实时动态5相关检索词泛时效性8F1略略略略略略略略1略略略略略略略略2略略略略略略略略3略略略略略略略略4略略略略略略略略5略略略略略略略略6略略略略略略略略7略略略略略略略略8略略略略略略略略9略略略略略略略略10
url2345摘要前标题后标题F2略略略略略正文略1略略略略略正文略2略略略略略略略3略略略略略略略4略略略略略正文略5略略略略略略略6略略略略略正文略7略略略略略正文略8略略略略略略略9略略略略略正文略10
123原创猜&网址形式6相关词猜&相似度F3略略略略主页次优先&|&子页内容充实略略精确匹配1略略略略子页优先级较低略略精确匹配2略略略略子页优先级较低略略精确匹配3略略略略子页优先级较低略略近义词匹配4略略略略子页优先级较低略略D5略略略略子页优先级较低略略D6略略略略主页次优先&|&子页内容充实略召回D7略略略略主页次优先&|&子页内容充实略略D8略略略略子页优先级较低略略精确匹配9略略略略子页优先级较低略略D101. 环境环境
1.1 安装Git
请参考【1】
1.2 安装node.js
下载:http://nodejs.org/download/
可以下载&node-v0.10.33-x64.msi
安装时直接保持默认配置即可。
2. 配置Github
1.1 建立Repository
建立与你用户名对应的仓库,仓库名必须为【your_user_name.github.io】
1.2 配置SSH-Key
3. 安装Hexo
关于Hexo的安装配置过程,请以官方Hexo【2】给出的步骤为准。
3.1&Installation
打开Git命令行,执行如下命令
$ npm install -g hexo
3.2&Quick Start
&1.&Setup your blog
在电脑中建立一个名字叫「Hexo」的文件夹(比如我建在了D:\Hexo),然后在此文件夹中右键打开Git Bash。执行下面的命令
$ hexo init
[info] Copying data
[info] You are almost done! Don't forget to run `npm install` before you start b
logging with Hexo!
Hexo随后会自动在目标文件夹建立网站所需要的文件。然后按照提示,运行&npm install(在&/D/Hexo下)
npm install
会在D:\Hexo目录中安装&node_modules。
2.&Start the server
运行下面的命令(在&/D/Hexo下)
$ hexo server
[info] Hexo is running at http://localhost:4000/. Press Ctrl+C to stop.
表明Hexo Server已经启动了,在浏览器中打开&http://localhost:4000/,这时可以看到Hexo已为你生成了一篇blog。
你可以按Ctrl+C 停止Server。
3.&Create a new post
新打开一个git bash命令行窗口,cd到/D/Hexo下,执行下面的命令
$ hexo new "My New Post"
[info] File created at d:\Hexo\source\_posts\My-New-Post.md
刷新http://localhost:4000/,可以发现已生成了一篇新文章&"My New Post"。
有一个问题,发现&"My New Post" 被发了2遍,在Hexo server所在的git bash窗口也能看到create了2次。
$ hexo server
[info] Hexo is running at http://localhost:4000/. Press Ctrl+C to stop.
[create] d:\Hexo\source\_posts\My-New-Post.md
[create] d:\Hexo\source\_posts\My-New-Post.md
经验证,在hexo new "My New Post"&时,如果按Ctrl+C将hexo server停掉,就不会出现发2次的问题了。
所以,在hexo new文章时,需要stop server。
4.&Generate static files
执行下面的命令,将markdown文件生成静态网页。
$ hexo generate
该命令执行完后,会在&D:\Hexo\public\ 目录下生成一系列html,css等文件。
5. 编辑文章
hexo new "My New Post"会在D:\Hexo\source\_posts目录下生成一个markdown文件:My-New-Post.md
可以使用一个支持markdown语法的编辑器(比如 Sublime Text 2)来编辑该文件。
6. 部署到Github
部署到Github前需要配置_config.yml文件,首先找到下面的内容
# Deployment
## Docs: http://hexo.io/docs/deployment.html
然后将它们修改为
# Deployment
## Docs: http://hexo.io/docs/deployment.html
type: github
repository: :zhchnchn/zhchnchn.github.io.git
branch: master
Repository:必须是SSH形式的url(:zhchnchn/zhchnchn.github.io.git),而不能是HTTPS形式的url(/zhchnchn/zhchnchn.github.io.git),否则会出现错误:
$ hexo deploy
[info] Start deploying: github
[error] https:///zhchnchn/zhchnchn.github.io is not a valid repositor URL!
使用SSH url,如果电脑没有开放SSH 端口,会致部署失败。
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
如果你是为一个项目制作网站,那么需要把branch设置为gh-pages。
当部署完成后,在浏览器中打开http://zhchnchn.github.io/(https://zhchnchn.github.io/) ,正常显示网页,表明部署成功。
8. 总结:部署步骤
每次部署的步骤,可按以下三步来进行。
hexo clean
hexo generate
hexo deploy
9. 总结:本地调试
1. 在执行下面的命令后,
$ hexo g #生成
$ hexo s #启动本地服务,进行文章预览调试
浏览器输入http://localhost:4000,查看搭建效果。此后的每次变更_config.yml 文件或者新建文件都可以先用此命令调试,尤其是当你想调试新添加的主题时。
2. 可以用简化的一条命令
3.3 命令总结
3.3.1 常用命令
hexo new "postName" #新建文章
hexo new page "pageName" #新建页面
hexo generate #生成静态页面至public目录
hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy #将.deploy目录部署到GitHubhexo help
# 查看帮助hexo version
#查看Hexo的版本
3.3.2 复合命令
hexo deploy -g
hexo server -g
命令的简写为:
hexo n == hexo new
hexo g == hexo generate
hexo s == hexo server
hexo d == hexo deploy
4 配置Hexo
4.1 配置文件介绍
下面的各个部分的介绍,请直接参考【3】。
1. 默认目录结构介绍
2. _config.yml配置文件介绍
NOTE:在修改_config.yml配置文件时,按照【3】的介绍进行修改后,重新 hexo clean 或者hexo deploy时,可能会出现如下错误:
$ hexo clean
[error] { name: 'HexoError',
reason: 'can not read a a multiline key may not be an imp
licit key',
{ name: null,
buffer: '# Hexo Configuration\n## Docs: http://hexo.io/docs/configuration.h
tml\n## Source: https:///hexojs/hexo/\n\n# Site\ntitle: Zhchnchn\nsubt
itle: Coding on the way\ndescription: Zhchnchn\'s blog\nauthor: Zhchnchn\nemail:
@qq.com\nlanguage:zh-CN\n\n# URL\n## If your site is put in a subdirect
position: 249,
column: 0 },
message: 'Config file load failed',
{ domain: null,
_events: { error: [Function] },
_maxListeners: 10,
members: [ [Object] ] },
domainThrown: true,
stack: undefined }
我的_config.yml配置文件是一个空行,所以错误肯定在前面,经过对比发现,我前面修改了一下 # Site的各项设置,在冒号:后面没留空格导致了该问题,请对比一下下面的区别:
错误的设置:
author:Zhchnchn
email:XXX@qq.com
language:zh-CN
正确的设置:
author: Zhchnchn
email: XXX@qq.com
language: zh-CN
3. 各个主题下的目录介绍(hexo\themes\下的modernist主题为例)
4.2 安装主题
Hexo提供了很多主题,具体可参见Hexo Themes【4】。这里我选择使用Pacman主题。具体设置方法如下【5】
4.2.1 安装
1. 将Git Shell 切到/D/Hexo目录下,然后执行下面的命令,将pacman下载到&themes/pacman 目录下。
$ git clone https:///A-limon/pacman.git themes/pacman
2.&修改你的博客根目录/D/Hexo下的config.yml配置文件中的theme属性,将其设置为pacman。
3. 更新pacman主题
cd themes/pacman
NOTE:先备份_config.yml 文件后再升级
4.2.2 配置
如果pacman的默认设置不能满足需要的话,你可以修改 /themes/pacman/下的配置文件_config.yml来定制。
各个config的含义,请参考【5】中的介绍。
4.2.3 评论框
静态博客要使用第三方评论系统,pacman配置了多说评论系统(/themes/pacman/_config.yml),默认关闭,只要将其打开即可:false-&true。直接用你的微博/豆瓣/人人/百度/开心网帐号登录多说,即可发表平评论。
#### Comment
enable: true
short_name:
## duoshuo short name.
4.2.3 统计
1. &pacman配置了google analysis系统(/themes/pacman/_config.yml),默认关闭,将其打开。
2. 需要注册google analysis服务,以获得&跟踪 ID。
如果已有google账户的话,可以直接注册。注册时,需要正确填写 网站的URL。注册成功后,会得到一个跟踪ID,以及一段跟踪代码。
3.&pacman配置了google analysis系统,将其打开
#### Analytics
google_analytics:
enable: true
## e.g. UA-1766729-8 your google analytics ID.
site: auto ## e.g. yangjian.me your google analytics site or set the value as auto.
4. 在themes\pacman\layout\_partial\google_analytics.ejs 中,已经将google的跟踪代码添加进来了【3】。
&% if (theme.google_analytics.enable){ %&
&script type="text/javascript"&
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www./analytics.js','ga');
ga('create', '&%= theme.google_analytics.id %&', '&%= theme.google_analytics.site %&');
ga('send', 'pageview');
而且会将/themes/pacman/_config.yml中的id和site值读取进来。
5. 如果设置不起作用,请试试在\themes\pacman\layout\_partial\head.ejs文件中最后,&/head&之前,添加上下面的语句试试。
&%- partial('google_analytics') %&
4.3 Custom 404页面
1. 网上大多数教程都将其说的极其简单:&直接在根目录下创建自己的 404.html 就可以&。但我却在这儿废了不少时间,究其原因是大家觉得太简单而说的不够明白。&根目录下&指的不是Hexo目录下,而是Hexo/source目录下。
2. 404.html的内容可以设置为下面的内容【6】(NOTE:&_config.yml中的permalink_defaults属性不需要修改)。
layout: default
&meta charset="UTF-8" /&
&title&404&/title&
&script type="text/javascript" src="/404/search_children.js" charset="utf-8"&&/script&
也可以简化为这一行:
&script type="text/javascript" src="/404/search_children.js" charset="utf-8"&&/script&
4.4 安装插件
4.4.1&sitemap插件
1. 可以将你站点地图提交给搜索引擎,文件路径\sitemap.xml。
$ npm install hexo-generator-sitemap
3. 启用,修改Hexo\_config.yml,增加以下内容
# Extensions
- hexo-generator-sitemap
path: sitemap.xml
4. 使用方法
(1)访问 http://localhost:4000/sitemap.xml,即可看到站点地图。
(2)那么怎么将它显示在页面中呢【7】?
可以修改themes/pacman(也就是你正在使用的那个theme)下的 _config.yml,在 menu 节点下添加下面的内容(下面要介绍的RSS插件也同样)
Archives: /archives
Rss: /atom.xml
Sitemap: /sitemap.xml
修改后的效果如图所示:
5.&如何向google提交sitemap
Sitemap 可方便管理员通知搜索引擎他们网站上有哪些可供抓取的网页。向google提交自己hexo博客的sitemap,有助于让别人更好地通过google搜索到自己的博客。
如何向google提交sitemap,请参考【8】。
6. 升级插件
$ npm update
7. 卸载插件
$ npm uninstall hexo-generator-sitemap
4.4.2&feed插件
1. RSS的生成插件,你可以在配置显示你站点的RSS,文件路径\atom.xml。
$ npm install hexo-generator-feed
3. 启用,修改Hexo\_config.yml,增加以下内容
# Extensions
- hexo-generator-feed
- hexo-generator-sitemap
#Feed Atom
type: atom
path: atom.xml
4.使用方法
参见sitemap插件介绍
5. 优化Hexo
5.1 添加&Fork me on Github& ribbon
给blog主页添加一个&Fork me on Github&的绶带(ribbon)【9】,比如选择了红色的ribbon,将相应代码复制到Hexo正在使用的theme下layout.ejs中。比如我使用的pacman theme,那么将下面的代码(注意将you改为你自己的github上的注册名)
&a href="/zhchnchn"&&img style="position: top: 0; left: 0; border: 0;" src="/82b228a63ef44c62fccf2f7a6f6ef6d2f2fe732f666f726b6d655f6ce67" alt="Fork me on GitHub" data-canonical-src="/github/ribbons/forkme_left_red_aa0000.png"&&/a&
粘贴到&themes\pacman\layout\layout.ejs中,放置在 最后,标签&/body&之前即可。
6.1 中文乱码
在md 文件中写中文内容,发布出来后为乱码,原因是md的编码不对,将md文件另存为&UTF-8&编码的文件即可解决问题。
References
【1】Windows下Git安装指南(/zhcncn/p/3787849.html)
【2】Hexo (/hexojs/hexo)
【3】hexo你的博客(//hexo-your-blog/)
【4】Hexo&All Themes(/hexojs/hexo/wiki/Themes)
【5】Pacman主题介绍(http://yangjian.me/pacman/hello/introducing-pacman-theme/)
【6】hexo添加404页面(http://ruocaiwu.github.io//hexo%E6%B7%BB%E5%8A%A%B5%E9%9D%A2/)
【7】如何搭建一个独立博客&&简明Github Pages与Hexo教程(//-how-to-build-a-blog/)
【8】如何向google提交sitemap(详细)(http://fionat.github.io/blog//sitemap/)
【9】GitHub Ribbons(/blog/273-github-ribbons)
阅读(...) 评论()Hexo快速搭建a year agoHexo下新建一个app.js,写入下面代码:var spawn = require('child_process').
free = spawn('hexo', ['server', '-p 4000']);/* 其实就是等于执行hexo server -p 4000*/
free.stdout.on('data', function (data) {
console.log('standard output:\n' + data);
free.stderr.on('data', function (data) {
console.log('standard error output:\n' + data);
free.on('exit', function (code, signal) {
console.log('child process eixt ,exit:' + code);
其实思路也很简单,大致意思就是node启动一个子进程,用forever 守护 hexo sever -p 4000这条命令(4000代表端口),关于node的child_process的相关知识,请自行baidu、google,或者去查nodejs的文档。执行forever命令:$ forever --minUptime 10000 --spinSleepTime 26000 start app.js
至于后面这几个minUptime、spinSleepTime可填可不填,不填默认也会有,参数的意思可以直接去上查询。停止服务这里值得注意的是你拿forever启动的服务,通过forever stopall是根本停不掉的,因为其实你执行的是hexo sever,可以通过下面的办法:$ lsof -i:[port]
$ ps aux|grep hexo
$ kill pid(进程的id)
完...13收藏分享举报文章被以下专栏收录推荐阅读{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&isPending&:false,&contributes&:[{&sourceColumn&:{&lastUpdated&:,&description&:&&,&permission&:&COLUMN_PUBLIC&,&memberId&:7437751,&contributePermission&:&COLUMN_PUBLIC&,&translatedCommentPermission&:&all&,&canManage&:true,&intro&:&&,&urlToken&:&meituanwaimai&,&id&:16406,&imagePath&:&7aa622b74d29da5cded981dfada0b7f3.jpeg&,&slug&:&meituanwaimai&,&applyReason&:&0&,&name&:&前端技术分享站&,&title&:&前端技术分享站&,&url&:&https:\u002F\\u002Fmeituanwaimai&,&commentPermission&:&COLUMN_ALL_CAN_COMMENT&,&canPost&:true,&created&:,&state&:&COLUMN_NORMAL&,&followers&:1522,&avatar&:{&id&:&7aa622b74d29da5cded981dfada0b7f3&,&template&:&https:\u002F\\u002F{id}_{size}.jpeg&},&activateAuthorRequested&:false,&following&:false,&imageUrl&:&https:\u002F\\u002F7aa622b74d29da5cded981dfada0b7f3_l.jpeg&,&articlesCount&:7},&state&:&accepted&,&targetPost&:{&titleImage&:&https:\u002F\\u002F89f3b59ff8e133cd6ca7cb6fe9057238_r.jpg&,&lastUpdated&:,&imagePath&:&89f3b59ff8e133cd6ca7cb6fe9057238.jpg&,&permission&:&ARTICLE_PUBLIC&,&topics&:[9,6445],&summary&:&Hexo简介 Hexo是一个采用nodejs的静态博客,市面上的类似的博客也有很多,比较有名的Jekyll,Octopress等,到最早的wordPress,我最后选Hexo纯是因为它用的是nodejs而已。其实Jekyll也很好用啊,用Jekyll+Pages一起就能随便搞一个博客,而且还不用自己买服…&,&copyPermission&:&ARTICLE_COPYABLE&,&translatedCommentPermission&:&all&,&likes&:0,&origAuthorId&:0,&publishedTime&:&T11:28:16+08:00&,&sourceUrl&:&&,&urlToken&:,&id&:791777,&withContent&:false,&slug&:,&bigTitleImage&:false,&title&:&Hexo快速搭建&,&url&:&\u002Fp\u002F&,&commentPermission&:&ARTICLE_ALL_CAN_COMMENT&,&snapshotUrl&:&&,&created&:,&comments&:0,&columnId&:16406,&content&:&&,&parentId&:0,&state&:&ARTICLE_PUBLISHED&,&imageUrl&:&https:\u002F\\u002F89f3b59ff8e133cd6ca7cb6fe9057238_r.jpg&,&author&:{&bio&:&IT互联网web开发&,&isFollowing&:false,&hash&:&f3fd9bfff9fdf635d6e55b30a6f518b1&,&uid&:76,&isOrg&:false,&slug&:&liu-ye-19&,&isFollowed&:false,&description&:&&,&name&:&刘烨&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fliu-ye-19&,&avatar&:{&id&:&&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&memberId&:355644,&excerptTitle&:&&,&voteType&:&ARTICLE_VOTE_CLEAR&},&id&:390824}],&title&:&Hexo快速搭建&,&author&:&liu-ye-19&,&content&:&\u003Ch2\u003EHexo简介\u003C\u002Fh2\u003E\u003Cp\u003E
Hexo是一个采用nodejs的静态博客,市面上的类似的博客也有很多,比较有名的Jekyll,Octopress等,到最早的wordPress,我最后选Hexo纯是因为它用的是nodejs而已。其实Jekyll也很好用啊,用Jekyll+Pages一起就能随便搞一个博客,而且还不用自己买服务器,托管到github上还无需备案,无非就是国内访问github有时候有些慢你懂得,闲的无聊也弄了个\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\u002Ftobyreynold.github.io\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003Etobyreynold\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,搞起来其实上手比hexo还快,你还可以自己的github账号下面直接建个XXX.github.io的git仓库然后直接一关联就可以了,你也可以买个域名直接指过去,不用他白给你的XXX.github.io也行,随意..\u003C\u002Fp\u003E\u003Ch2\u003EHexo搭建\u003C\u002Fh2\u003E\u003Cp\u003E
进入 \u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\u002Fhexo.io\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EHexo\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E官网,按照教程npm install 各种之后,生成Hexo工程,Hexo sever就启动起来了,同样你在服务器端也可以直接这样搞,或者本地搞定了直接rsync到服务器上去也行,又或者直接github上建一个git仓库,本地搞定了推到github,服务器端pull github上的代码就可以了。\u003C\u002Fp\u003E\u003Ch2\u003EHexo进程守护\u003C\u002Fh2\u003E\u003Cp\u003E
Node进程守护有很多工具,\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\\u002Fpackage\u002Fforever\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EForever\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,\u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\u002Fpm2.keymetrics.io\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPM2\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\\u002Fpackage\u002Fpm25\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPM2.5\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E blah blah.解决的问题也是很容易讲清楚,比如:ssh登陆服务器,启动node服务,然后ssh断开连接,服务中断,网站无法访问。这里讲一下用forever 解决 Hexo 进程守护的问题,安装Forever:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E$ [sudo] npm install forever -g\n$ cd \u002Fpath\u002Fto\u002Fyour\u002Fproject\n$ [sudo] npm install forever-monitor\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003EHexo下新建一个app.js,写入下面代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Evar spawn = require('child_process').\nfree = spawn('hexo', ['server', '-p 4000']);\u002F* 其实就是等于执行hexo server -p 4000*\u002F\n\nfree.stdout.on('data', function (data) {\nconsole.log('standard output:\\n' + data);\n});\n\nfree.stderr.on('data', function (data) { \nconsole.log('standard error output:\\n' + data);\n});\n\nfree.on('exit', function (code, signal) {\nconsole.log('child process eixt ,exit:' + code);\n});\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E
其实思路也很简单,大致意思就是node启动一个子进程,用forever 守护 hexo sever -p 4000这条命令(4000代表端口),关于node的child_process的相关知识,请自行baidu、google,或者去查nodejs的文档。\u003Ch3\u003E执行forever命令:\u003C\u002Fh3\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E$ forever --minUptime 10000 --spinSleepTime 26000 start app.js\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E至于后面这几个minUptime、spinSleepTime可填可不填,不填默认也会有,参数的意思可以直接去\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\\u002Fpackage\u002Fforever\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003Eforever\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E上查询。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch3\u003E停止服务\u003C\u002Fh3\u003E\u003Cp\u003E这里值得注意的是你拿forever启动的服务,通过forever stopall是根本停不掉的,因为其实你执行的是hexo sever,可以通过下面的办法:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E$ lsof -i:[port]\n$ ps aux|grep hexo\n$ kill pid(进程的id)\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E完...\u003C\u002Fp\u003E&,&updated&:new Date(&T03:28:16.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:3,&collapsedCount&:0,&likeCount&:13,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\\u002F89f3b59ff8e133cd6ca7cb6fe9057238_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&reviewers&:[],&topics&:[{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&Hexo&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&轻博客&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&Node.js&}],&adminClosedComment&:false,&titleImageSize&:{&width&:1166,&height&:930},&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&column&:{&slug&:&meituanwaimai&,&name&:&前端技术分享站&},&tipjarState&:&inactivated&,&annotationAction&:[],&sourceUrl&:&&,&pageCommentsCount&:3,&hasPublishingDraft&:false,&snapshotUrl&:&&,&publishedTime&:&T11:28:16+08:00&,&url&:&\u002Fp\u002F&,&lastestLikers&:[{&bio&:&东北大学本科毕业 银河证券工作 爱好占星术&,&isFollowing&:false,&hash&:&bbc1dba20d851&,&uid&:48,&isOrg&:false,&slug&:&foreverun&,&isFollowed&:false,&description&:&&,&name&:&常明宇&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fforeverun&,&avatar&:{&id&:&a9ea&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&聞過則喜,見賢思齊&,&isFollowing&:false,&hash&:&33b1f491a95d72b7f283fa9809e03bdd&,&uid&:04,&isOrg&:false,&slug&:&bewho&,&isFollowed&:false,&description&:&真心是道場&,&name&:&米拉之落&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fbewho&,&avatar&:{&id&:&fb71a4995&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&往事并不如烟...&,&isFollowing&:false,&hash&:&9bcfa83e639a18fe338351&,&uid&:16,&isOrg&:false,&slug&:&frobisher27149&,&isFollowed&:false,&description&:&http:\u002F\u002Ffrobisher.me&,&name&:&没有色彩的阿Q&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Ffrobisher27149&,&avatar&:{&id&:&0ba3da30ca71&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&前端/计算机科学&,&isFollowing&:false,&hash&:&f96ad42a9f3972b8cac253&,&uid&:40,&isOrg&:false,&slug&:&wk633&,&isFollowed&:false,&description&:&&,&name&:&王凯&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fwk633&,&avatar&:{&id&:&45ad8c64a&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&&,&isFollowing&:false,&hash&:&07b2f599f4feaa64c30dd04&,&uid&:64,&isOrg&:false,&slug&:&zhe-ye-12-1&,&isFollowed&:false,&description&:&&,&name&:&者也&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fzhe-ye-12-1&,&avatar&:{&id&:&da8e974dc&,&template&:&https:\u002F\\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}],&summary&:&Hexo简介 Hexo是一个采用nodejs的静态博客,市面上的类似的博客也有很多,比较有名的Jekyll,Octopress等,到最早的wordPress,我最后选Hexo纯是因为它用的是nodejs而已。其实Jekyll也很好用啊,用Jekyll+Pages一起就能随便搞一个博客,而且还不用自己买服…&,&reviewingCommentsCount&:0,&meta&:{&previous&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\\u002F50\u002F46cbec6238dcb4e2464aab65f2547c77_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&前端开发框架和库&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&前端性能&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&前端性能优化&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&I am agent of CHAOS&,&isFollowing&:false,&hash&:&9add23fac81&,&uid&:04,&isOrg&:false,&slug&:&toukang&,&isFollowed&:false,&description&:&新浪\u002F美团外卖前端工程师&,&name&:&toukang&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Ftoukang&,&avatar&:{&id&:&f6a1b7c5b&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&column&:{&slug&:&meituanwaimai&,&name&:&前端技术分享站&},&content&:&Demo git仓库: \u003Ca href=\&https:\u002F\\u002F?target=https%3A\\u002Fsexdevil\u002FLSLoader\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EGitHub - sexdevil\u002FLSLoader: localStorage loader to increase mobile webapp speed\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E 欢迎clone\u003Cp\u003E1、浏览器原生缓存,PC时代我们怎么做的\u003C\u002Fp\u003E\u003Cp\u003E对于浏览器的缓存机制,我们传统用expire时间控制。当页面缓存没有到期时,浏览器会读取本地文件的js\u002Fcss\u002F图片;如果我们强制刷新,浏览器会发起304协议请求。\u003C\u002Fp\u003E\u003Cp\u003E对于js构建,对于小型页面,有原始的全局变量模式,中型以上的模块化有AMD模式,React,Vue使用的WebPack模式。\u003C\u002Fp\u003E\u003Cp\u003E这些模式在加载的时候一般有两种:\u003C\u002Fp\u003E\u003Cp\u003E(1)并行加载多个模块,利用RequireJS,webPack的回调函数进行模块依赖处理;\u003C\u002Fp\u003E\u003Cp\u003E优点:按需加载,每个模块文件浏览器都会根据文件名进行缓存\u003C\u002Fp\u003E\u003Cp\u003E缺点:http请求过多,耗费在延迟的性能大,对服务器的并发高\u003C\u002Fp\u003E\u003Cp\u003E(2)相对常用的,线下把所有依赖打包成一个大文件,一次请求全部。\u003C\u002Fp\u003E\u003Cp\u003E优点:http少,一次握手全部加载完成\u003C\u002Fp\u003E\u003Cp\u003E缺点:大文件间可能模块有重复加载,浪费时间和流量\u003C\u002Fp\u003E\u003Cp\u003E2、移动端的新坑\u003C\u002Fp\u003E\u003Cp\u003E移动浏览器环境,相对于PC环境又有如下新问题:\u003C\u002Fp\u003E\u003Cp\u003E(1)非Wi-Fi情况下网络延迟很大,达到200-400ms,直接结果就是304或者200情况下css,js加载很慢,加大白屏时间\u003C\u002Fp\u003E\u003Cp\u003E(2)IOS webview 大坑:IOS webview资源缓存存储在内存,如果IOS退出程序后台,所有图片\u002Fcss\u002Fjs全部缓存消失,再访问页面全部200请求。\u003C\u002Fp\u003E\u003Cp\u003E(3)各种客户端的WebView,第三方浏览器行为不一致,经常expire没有失效却触发了304协议\u003C\u002Fp\u003E\u003Cp\u003E综上,移动端传统线下打包+304缓存即无法满足移动web性能需求。\u003C\u002Fp\u003E\u003Cp\u003E3、业内解决方案 ——LocalStorage本地缓存\u003C\u002Fp\u003E\u003Cp\u003E目前业内有\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fscrat.io\u002F%23%21\u002Findex\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EScrat - webapp模块化开发体系\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,基于FIS的整套前端架构,有美团用的truckJS解决方案。基本原理就是使用md5版本号+localStorage,把静态文件存储在浏览器本地数据,利用js控制js的加载运行。\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\\u002Fquestion\u002F\& class=\&internal\&\u003E静态资源(JS\u002FCSS)存储在localStorage有什么缺点?为什么没有被广泛应用? - 互联网\u003C\u002Fa\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E基本原理高票答案张云龙大神的回答已经解释了,大概如下\u003C\u002Fp\u003E\u003Cp\u003E(1)配合线上combo,实现模块文件粒度的缓存,多页面间共用模块文件\u003C\u002Fp\u003E\u003Cp\u003E(2)上线灰度时不用打一个大文件包一起更新,缓存组件会按需更新模块文件,减少流量,提升时间\u003C\u002Fp\u003E\u003Cp\u003E(3)既然是js控制,所以可以避免刷新,地址栏重新输入造成的304请求\u003C\u002Fp\u003E\u003Cp\u003E(4)LocalStorage在IOS webview里面是文件存储,解决了退进程缓存失效的bug\u003C\u002Fp\u003E\u003Cp\u003E4、Why LsLoader ,这个东西有什么不同\u003C\u002Fp\u003E\u003Cp\u003E目前主流的解决\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fscart.io\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\u003Escart.io\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E是利用重新定义require\u002Fdefine的方式,把缓存作为整个体系的一个部分揉入工作流。缺点在于只支持AMD模式开发的js程序,对于首屏css,非AMD模式例如webPack,原生模块化,CMD等并不支持,是一个全家桶形式的解决方案。\u003C\u002Fp\u003E\u003Cp\u003E而LsLoader 构成相对简单,她只有两个部分:\u003C\u002Fp\u003E\u003Cp\u003E1, LsLoader.js,运行在客户端,异步加载所有css,js,css通过出现顺序的正确排列达到执行效果等同于原生link标签。同时css成功有回调函数,利用回调可以让页面先全部display:none,当首屏css加载完成后再显示,防止异步css造成的页面错乱的问题。JS的执行原理则是异步加载,顺序执行。意思是所有的js根据页面中出现的顺序压入一个执行队列,异步加载完成后根据队列的顺序执行js文件。\u003C\u002Fp\u003E\u003Cp\u003E图1 本地缓存的代码结构\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&err\&\u003E\u002Fstatic\u002Fjs\u002Flib\u002Fwm_lib.js:\u003C\u002Fspan\u003E\n\u003Cspan class=\&err\&\u003E\&\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ehttp\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E\u003Cspan class=\&c1\&\u003E\u002F\u002Fxs01.meituan.net\u002Fwaimai_c_activity_web\u002Fjs\u002Flib\u002Fwm_lib.d162d9dc.js\u002F*codestartv1*\u002Fvar Zepto=function(){function t(t){r...\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E第一项,相对路径为存在localStorage中的唯一key,value中有带md5版本号的线上路径,用来标示版本&加载资源。如果LsLoader发现缓存中程序版本有效,启用本地,否则远程请求资源执行并缓存。\u003C\u002Fp\u003E\u003Cp\u003E图2 LsLoader 缓存\u002F加载流程图\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fe9d7d7a43cbc9fb09dc3_b.png\& data-rawwidth=\&596\& data-rawheight=\&728\& class=\&origin_image zh-lightbox-thumb\& width=\&596\& data-original=\&https:\u002F\\u002F50\u002Fe9d7d7a43cbc9fb09dc3_r.png\&\u003E\u003Cbr\u003E\u003Cp\u003E加载资源时,LsLoader会用ajax请求并吧代码结构连同代码版本号存入localStorage,这样二次访问时如果版本号不变LsLoader会直接从localStorage取出代码执行\u003C\u002Fp\u003E\u003Cp\u003E2,templateBuild.js,基于gulp的编译组件,运行在上线前编译过程。通过模版扫描,AST语法分析,templateBuild.js能把页面中用注释标记的资源文件转换成为LsLoader接口的形式.AMD入口程序会把依赖的模块按顺序全部传入LsLoader接口,再配合almond.js,顺序执行define模块后,require函数就能使用之前运行过的模块。同理,原生js按照顺序执行,依赖顺序按照出现顺序。Webpack模块文件则是一系列comonJS的文件,通过模版分析顺序执行依赖即可让程序执行。\u003C\u002Fp\u003E\u003Cp\u003E他是基于gulp的,开发时我们用各种注释来标示要编译的动作,gulp编译后开发环境代码即可编译为线上缓存模式。基本标注格式为&!--任务名 build--&&!--任务名 endbuild--&\u003C\u002Fp\u003E\u003Cp\u003E如下:编译前的业务代码,一段script同步外引,一段内联脚本\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&c\&\u003E&!--\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ejs\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Els\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ebuild\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E--&\u003C\u002Fspan\u003E\n\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Escript\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Esrc\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E\u003Cspan class=\&s2\&\u003E\&\u002Fstatic\u002Fjs\u002Fpage\u002Fhome\u002Fhome.js\&\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E&&\u003C\u002Fspan\u003E\u003Cspan class=\&err\&\u003E\u002Fscript&\u003C\u002Fspan\u003E\n\u003Cspan class=\&c\&\u003E&!--\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ejs\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Els\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eendbuild\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E--&\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&c\&\u003E&!--\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ejs\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Einline\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ebuild\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E--&\u003C\u002Fspan\u003E\n\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Escript\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Erequire\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E([\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'page\u002Fhome\u002Fhome'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E],\u003C\u002Fspan\u003E \u003Cspan class=\&kd\&\u003Efunction\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003EhomePage\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003EhomePage\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Einit\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E({\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Ebaseurl\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'${_baseurl_}'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Echannel\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'${global_channel_id!}'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Eterminal\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'${__wm_terminal__}'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003EpageCount\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'${(data_obj.poi_total_num\u002Fdata_obj.page_size)?ceiling}'\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E});\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E});\u003C\u002Fspan\u003E\n\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\u003Cspan class=\&err\&\u003E\u002Fscript&\u003C\u002Fspan\u003E\n\u003Cspan class=\&c\&\u003E&!--\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ejs\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Einline\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eendbuild\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E--&\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E编译后的代码:外引js改为异步加载\u002FlocalStorage缓存,内联脚本用textarea包裹,用lsLoader的js执行队列延迟处理保证执行顺序的正确\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.load(\&\u002Fstatic\u002Fjs\u002Flib\u002Fwm_lib.js\&,\&http:\u002F\u002Fxs01.meituan.net\u002Fwaimai_i\u002Fjs\u002Flib\u002Fwm_lib.c75cec25.js\& )&\u002Fscript&\n\n&textarea style=\&display:none\& id=\&ls-loader-inlinecode11\&&\n
require(['page\u002Fhome\u002Fhome'], function (homePage) {\n
homePage.init({\n
baseurl: '',\n
channel: '1033',\n
terminal: 'i',\n
pageCount: '63'\n
});\n&\u002Ftextarea&&script&lsloader.runInlineScript(\&ls-loader-inlinerun11\&,\&ls-loader-inlinecode11\&)&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E首屏CSS的缓存,高级功能\u003C\u002Fp\u003E\u003Cp\u003E编译前,就是一段普通的加了onload的link标签,css加载前让html根节点踢出文档流,加载成功后再恢复。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&document.documentElement.style.display='none'&\u002Fscript&
\n&!--css ls build--&\n
&link href=\&\u002Fstatic\u002Fcss\u002Fpage\u002Fhome\u002Fhome.css\& onload=\&document.documentElement.style.display='';\& rel=\&stylesheet\&\u002F&\n
&!--css ls endbuild--&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E编译后,LsLoader会传入回调函数并在从本地缓存运行css\u002F远程加载运行css成功后执行回调,防止异步加载css非阻塞形式造成文档树先加载显示,css没有就绪造成的页面错乱和repaint\u002Freflow性能开销\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E &style id=\&\u002Fstatic\u002Fcss\u002Fpage\u002Fhome\u002Fhome.css\&&&\u002Fstyle&&script&lsloader.load(\&\u002Fstatic\u002Fcss\u002Fpage\u002Fhome\u002Fhome.css\&,\&http:\u002F\u002Fxs01.meituan.net\u002Fwaimai_i\u002Fcss\u002Fpage\u002Fhome\u002Fhome.cc82edd3.css\&,function(){document.documentElement.style.display='';} )&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E综上,LsLoader对于业务开发来说兼容各种形式的模块化方式,需要使用者适配的只是templateBuild,模版编译部分。通过自定义这个gulp组件,我们可以把各种形式的js编译成LsLoader可以加载\u002F缓存的格式。\u003C\u002Fp\u003E\u003Cp\u003E6、进阶功能,combo服务和模块化缓存\u003C\u002Fp\u003E\u003Cp\u003E我们都知道,本地打包合并css\u002Fjs模块,单文件上线可以减少http请求,减少等待网络延迟的时间,亦可减少服务器的并发。但是这种形式对模块文件的复用性较差。\u003C\u002Fp\u003E\u003Cp\u003E例如,页面A 页面B两个主js 文件a.js,b.js,都依赖了c.js,我们采用线下打包的时候线上a.js,b.js就会包含两份c.js,重复下载。代码更新时,如果c.js发生变化,a.js,b.js也都会缓存失效,重新发起一个200请求把整个打包后的大文件下载。这个对于移动端的高网络延迟环境性能损耗明显。\u003C\u002Fp\u003E\u003Cp\u003E为了解决这种问题,我们LsLoader支持线上combo服务来提升性能。\u003C\u002Fp\u003E\u003Cp\u003E何为线上combo?简单说就是传入一连串css\u002Fjs文件的路径,服务端把对应的文件按顺序拼接成一个reponse返回客户端。\u003C\u002Fp\u003E\u003Cp\u003E举个例子:\u003C\u002Fp\u003E\u003Cp\u003E我们发出一个请求,请求如下3个文件\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u002Fcombo?combo=js\u002Futil\u002Futil-17b72c16ec.js\u002Fcommon\u002FcommonDialog-2ddfc1cdfc.js\u002Fcommon\u002Ftest-9c740d7b0d.\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E服务端根据参数查找这3个文件,按顺序拼接成一个response返回\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u002F*combojs*\u002Fdefine(\&util\u002Futil\&,function(){return{utilNumber:2}});\n\n\u002F*combojs*\u002Fdefine(\&common\u002FcommonDialog\&,[\&util\u002Futil\&],function(m){return{commonNumber:m.utilNumber+2}});\n\n\u002F*combojs*\u002Fdefine(\&common\u002Ftest\&,[\&common\u002FcommonDialog\&],function(m){return{monNumber+2}});\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E其中,\u002F*combojs*\u002F分隔符是用来分隔每个文件的,lsLoader得到combo后的返回后按照分隔符把代码分隔成数组,再根据对应的请求文件名顺序保证解析对应的js内容。\u003C\u002Fp\u003E\u003Cp\u003EAMD形式的线上代码,参见demo \u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Flocalhost%3AF\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\u003Elocalhost:F\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.loadCombo(\n[\n{name:\&..\u002Fjs\u002Futil\u002Futil.js\&,path:\&js\u002Futil\u002Futil-17b72c16ec.js\&},{name:\&..\u002Fjs\u002Fcommon\u002FcommonDialog.js\&,path:\&js\u002Fcommon\u002FcommonDialog-2ddfc1cdfc.js\&},\n{name:\&..\u002Fjs\u002Fcommon\u002Ftest.js\&,path:\&js\u002Fcommon\u002Ftest-9c740d7b0d.js\&},{name:\&..\u002Fjs\u002FAmdModule.js\&,path:\&js\u002FAmdModule-9c.js\&},{name:\&..\u002Fjs\u002Findex.js\&,path:\&js\u002Findex-cd.js\&}\n])\n&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E页面中编译后模块化文件被拆分成了一个数组,这个数组内的所有文件会按照顺序执行。如果数组内文件有本地版本,直接加入队列,否则所有的待更新模块会被统一请求。\u003C\u002Fp\u003E\u003Cp\u003E对于这个编译过程,用到编译知识比较复杂,我会后面介绍。\u003C\u002Fp\u003E\u003Cp\u003E例子中,我使用的是AMD模式分块,LsLoader只负责把所有define模块按顺序执行出来,define,require函数我们交给您自己来选择。我的业务中选择的就是almond.js ,简化版的require.js,只负责模块define,require依赖关系,不负责远程加载模块。\u003C\u002Fp\u003E\u003Cp\u003E同理,如果我们使用非模块化js,那就是原始的按照顺序来执行即可保证文件关系正确。\u003C\u002Fp\u003E\u003Cp\u003E原生依赖方式的代码,参见demo \u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Flocalhost%3AFhtml\u002FnoAMD.html\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\u003Elocalhost:Fhtml\u002FnoA\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003EMD.html\u003C\u002Fspan\u003E\u003Cspan class=\&ellipsis\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.loadCombo\n([{name:\&..\u002Fjs\u002Fjquery.js\&,path:\&js\u002Fjquery-aca795763d.js\&},{name:\&..\u002Fjs\u002FnoAMD\u002F1.js\&,path:\&js\u002FnoAMD\u002F1-c41a23fafd.js\&},{name:\&..\u002Fjs\u002FnoAMD\u002F2.js\&,path:\&js\u002FnoAMD\u002F2-.js\&}])\n&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E非 AMD模块很简单,就是按顺序执行js.\u003C\u002Fp\u003E\u003Cp\u003E对于webPack模式,他提供了一个monsChunkPlugin函数,熟悉webPack的同学应该知道这是一个提取webPack公共函数的功能,他能把webPack依赖的公共文件拆出来放到主文件前面,只要保证执行顺序正确即可。由于LsLoader.loadCombo顺序执行的特性,webPack亦可接入我么的缓存系统。\u003C\u002Fp\u003E\u003Cp\u003EwebPack的加载代码,参见demo中的\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Flocalhost%3AFhtml\u002Fwebpack.html\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\u003Elocalhost:Fhtml\u002Fweb\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003Epack.html\u003C\u002Fspan\u003E\u003Cspan class=\&ellipsis\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.loadCombo(\n[{name:\&..\u002Fjs\u002Fwebpack\u002Fpicker.js\&,path:\&js\u002Fwebpack\u002Fpicker-f49e85ff50.js\&},{name:\&..\u002Fjs\u002Fwebpack\u002Ftimepicker.js\&,path:\&js\u002Fwebpack\u002Ftimepicker-af98294a63.js\&}])\n&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E综上,所有模块化系统转变为顺序执行的一堆js文件后,都可以使用combo服务实现异步加载\u002F顺序执行\u002F本地缓存\u002F模块化粒度更新。\u003C\u002Fp\u003E\u003Cp\u003E7、模块化文件怎么编译切分,有现成工具么?\u003C\u002Fp\u003E\u003Cp\u003E有~ 为了方便生产环境使用,我利用AST语法树分析开发了一套解析AMD文件依赖关系的组件,gulp组件ASTBuild.js。\u003C\u002Fp\u003E\u003Cp\u003E什么是AST语法树?\u003C\u002Fp\u003E\u003Cp\u003E这个话题说开了篇幅较大,详细的请参见团厂的文章\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fabstract-syntax-tree.html\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\\u002Fabstra\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003Ect-syntax-tree.html\u003C\u002Fspan\u003E\u003Cspan class=\&ellipsis\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E简单来说,AST --抽象语法树分析就是把js源代码按照语法抽象成一棵树,每个节点都是他的语义词。利用AST,我们能方便的找出js源文件中的define节点,require节点,然后找出里面的参数。形如define(id,[a,b],function(){})的模块,语法树解析后我们即可知道他依赖a,b两个文件,然后用ASTBuild.js 递归搜索a.js,b.js两文件的代码,当查到没有依赖的树叶为止。过程中所有依赖的列表我们逆序排列,即可得到一个从树叶到树根的依赖数组,去除重复,即可得到我们想要的唯一正确顺序的依赖树。\u003C\u002Fp\u003E\u003Cp\u003E编译前,我们用AMD build注释包裹这个AMD入口文件,告诉编译程序它是我们要分析的AMD格式的文件。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&!--js ls AMDModule build--&\n&script src='..\u002Fjs\u002Findex.js'&&\u002Fscript&\n&!--js ls AMDModule endbuild--&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E编译后,所有的依赖从树叶到树枝是一个数组,没有重复。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.loadCombo([\n{name:\&..\u002Fjs\u002Futil\u002Futil.js\&,path:\&js\u002Futil\u002Futil-17b72c16ec.js\&},{name:\&..\u002Fjs\u002Fcommon\u002FcommonDialog.js\&,path:\&js\u002Fcommon\u002FcommonDialog-2ddfc1cdfc.js\&},\n{name:\&..\u002Fjs\u002Fcommon\u002Ftest.js\&,path:\&js\u002Fcommon\u002Ftest-9c740d7b0d.js\&},{name:\&..\u002Fjs\u002FAmdModule.js\&,path:\&js\u002FAmdModule-9c.js\&},{name:\&..\u002Fjs\u002Findex.js\&,path:\&js\u002Findex-cd.js\&}])\n&\u002Fscript&\n\n&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E对于其他格式的依赖,我们有combo build编译任务处理\u003C\u002Fp\u003E\u003Cp\u003E形如\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&!--js combo build--&\n&script src=\&..\u002Fjs\u002Fjquery.js\&&&\u002Fscript&\n&script src=\&..\u002Fjs\u002FnoAMD\u002F1.js\&&&\u002Fscript&\n&script src=\&..\u002Fjs\u002FnoAMD\u002F2.js\&&&\u002Fscript&\n&!--js combo endbuild--&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E编译后就是顺序执行的js\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&script&lsloader.loadCombo\n([\n{name:\&..\u002Fjs\u002Fjquery.js\&,path:\&js\u002Fjquery-aca795763d.js\&},{name:\&..\u002Fjs\u002FnoAMD\u002F1.js\&,path:\&js\u002FnoAMD\u002F1-c41a23fafd.js\&},{name:\&..\u002Fjs\u002FnoAMD\u002F2.js\&,path:\&js\u002FnoAMD\u002F2-.js\&}\n])\n&\u002Fscript&\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E8、说的好多,有没有例子?\u003C\u002Fp\u003E\u003Cp\u003E有,gitHub上公开了我提出来的LsLoader源项目,附带一个node的演示服务,全功能,有缓存,有combo加载。下面我来一一演示\u003C\u002Fp\u003E\u003Cp\u003E首先上\u003Ca href=\&https:\u002F\\u002F?target=https%3A\\u002Fsexdevil\u002FLSLoader\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EGitHub - sexdevil\u002FLSLoader: localStorage loader to increase mobile webapp speed\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,\u003C\u002Fp\u003E\u003Cp\u003E1、clone代码到本地。\u003C\u002Fp\u003E\u003Cp\u003E2、切换到根目录下,运行npm install. 没有node的同学自行安装node先~\u003C\u002Fp\u003E\u003Cp\u003E3、运行gulp ,可以看到编译开始\u003C\u002Fp\u003E\u003Cp\u003E4、完成后运行node app.js 启动后端\u003C\u002Fp\u003E\u003Cp\u003E5、浏览器输入localhost:3000 即可看到效果\u003C\u002Fp\u003E\u003Cp\u003Echrome我们开发者工具查看,console输入localStorage,我们看到了首页的资源都被缓存了。\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fca3ef8bd199e2ad30f99475_b.png\& data-rawwidth=\&1424\& data-rawheight=\&586\& class=\&origin_image zh-lightbox-thumb\& width=\&1424\& data-original=\&https:\u002F\\u002F50\u002Fca3ef8bd199e2ad30f99475_r.png\&\u003E\u003Cbr\u003E\u003Cp\u003E输入localStorage.clear(); 清空本地缓存,再刷新,我们看network\u003C\u002Fp\u003E\u003Cp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002F90b2f1f977c_b.png\& data-rawwidth=\&2522\& data-rawheight=\&532\& class=\&origin_image zh-lightbox-thumb\& width=\&2522\& data-original=\&https:\u002F\\u002F50\u002F90b2f1f977c_r.png\&\u003E加载瀑布是异步,同时利用css onload我们也阻止了讨厌的页面错乱问题。\u003C\u002Fp\u003E\u003Cp\u003E这是,我们再模拟一个业务开发中常见的问题,index.js中依赖一个模块test.js,我们修改他保存,然后gulp编译。传统模式线下combo会整个index.js大包全部加载,这里我们试试重新打开页面如何。\u003Cimg src=\&https:\u002F\\u002F50\u002F84daaf0eb7cb4dc02718_b.png\& data-rawwidth=\&2570\& data-rawheight=\&152\& class=\&origin_image zh-lightbox-thumb\& width=\&2570\& data-original=\&https:\u002F\\u002F50\u002F84daaf0eb7cb4dc02718_r.png\&\u003E\u003C\u002Fp\u003E\u003Cp\u003E页面只去请求了被修改的模块~ 省流量,省时间,同时结合combo也不增加请求,业务快速迭代情况下亦可保证用户性能,节省服务端流量。\u003C\u002Fp\u003E\u003Cp\u003E9、我的项目适合这个缓存方案么?有什么坑么\u003C\u002Fp\u003E\u003Cp\u003E没有最好的解决方案,只有最适合你的方案。\u003C\u002Fp\u003E\u003Cp\u003E实现这套缓存机制需要的技术成本如下:\u003C\u002Fp\u003E\u003Cp\u003E1、静态资源要配置为可以跨域访问,因为缓存资源都是ajax请求,如果跨域了LsLoader会用script标签作个fallback,但是就得不尝失了。\u003C\u002Fp\u003E\u003Cp\u003E2、项目编译需要使用gulp\u002Fgrunt工具,编译过程是gulp插件,基于nodejs语法。\u003C\u002Fp\u003E\u003Cp\u003E3、线上有combo服务的站点可以优化实现性能提升,如果没有,亦可对IOS webview有很大收益。\u003C\u002Fp\u003E\u003Cp\u003E这套缓存机制不适用的情况:\u003C\u002Fp\u003E\u003Cp\u003E1、PC由于IE问题,对LocalStorage兼容不一致。\u003C\u002Fp\u003E\u003Cp\u003E2、PC带宽较高,拆分模块加载收益可以忽略。\u003C\u002Fp\u003E\u003Cp\u003E3、移动端,如果您的页面只是在浏览器环境,没有IOS webView的坑,本地缓存失效风险较低。这时部署这套方案的价值就在线上combo实现的模块级别更新发布,如果您没有线上combo,就不建议花费时间在这个上面\u003C\u002Fp\u003E&,&state&:&published&,&sourceUrl&:&&,&pageCommentsCount&:0,&canComment&:false,&snapshotUrl&:&&,&slug&:,&publishedTime&:&T12:44:13+08:00&,&url&:&\u002Fp\u002F&,&title&:&LsLoader 移动WEB工程化缓存方案&,&summary&:&Demo git仓库: \u003Ca href=\&https:\\u002Fsexdevil\u002FLSLoader\& data-editable=\&true\& data-title=\&GitHub - sexdevil\u002FLSLoader: localStorage loader to increase mobile webapp speed\& class=\&\&\u003EGitHub - sexdevil\u002FLSLoader: localStorage loader to increase mobile webapp speed\u003C\u002Fa\u003E 欢迎clone1、浏览器原生缓存,PC时代我们怎么做的对于浏览器的缓存机制,我们传统用expire时间控制。当页面缓存没有到期时,浏览器会读取本地文件的js\u002Fcss…&,&reviewingCommentsCount&:0,&meta&:{&previous&:null,&next&:null},&commentPermission&:&anyone&,&commentsCount&:6,&likesCount&:44},&next&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\\u002F50\u002F8ca8b07f1a3cd20c723d7ee_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&JavaScript&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&Web 开发&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&I am agent of CHAOS&,&isFollowing&:false,&hash&:&9add23fac81&,&uid&:04,&isOrg&:false,&slug&:&toukang&,&isFollowed&:false,&description&:&新浪\u002F美团外卖前端工程师&,&name&:&toukang&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Ftoukang&,&avatar&:{&id&:&f6a1b7c5b&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&column&:{&slug&:&meituanwaimai&,&name&:&前端技术分享站&},&content&:&\u003Cp\u003Egithub介绍首页 \u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\u002Fustbhuangyi.github.io\u002Fpicker\u002F\& class=\& external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u003Cspan class=\&invisible\&\u003Ehttp:\u002F\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&visible\&\u003Eustbhuangyi.github.io\u002Fp\u003C\u002Fspan\u003E\u003Cspan class=\&invisible\&\u003Eicker\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&ellipsis\&\u003E\u003C\u002Fspan\u003E\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E 手机直接打开看demo~\u003C\u002Fp\u003E我们在做webApp 开发的时候,经常会碰到下拉菜单,二级甚至三级菜单联动的需求。通常我们会用iscroll模拟个可以弹性滚动的选择框,然后每次根据选择自己写一些回调逻辑。但是对于类似IOS原生UIPickerView的滚动选择效果没有直接的组件。如果用原生的DatePicker,流畅度可以保证但是IOS和安卓UI不统一风格,而且选择内容固定为时间。\u003Cp\u003EPicker.js是一个纯用js+css3 transition特性构建的纯h5滚动选择器,它能实现近似原生IOS datePicker的滚动选择效果,同时利用js回调函数捕捉常用的几个自定义事件来实现几列菜单级联效果。而且它可以让你自定义列数,支持1-3列列表,一个picker搞定各种菜单栏。\u003C\u002Fp\u003E\u003Cp\u003EPicker.js 总体介绍如下:\u003C\u002Fp\u003E\u003Cp\u003E1、引入方式\u003C\u002Fp\u003E\u003Cp\u003E依赖zepto.js 和gmu.js ,在两个库文件加载成功后引入picker.js文件。picker.js本身用webpack打包,样式逻辑集中在一个文件中。可以根据你的项目直接引入或者包装成AMD模块。引入后,即可在你的业务代码中绑定picker.js和你要初始化的节点,当你选择\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&kd\&\u003Evar\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003E$name\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003E$\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'#name'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E \u003Cspan class=\&c1\&\u003E\u002F\u002F你要初始化的dom节点\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&kd\&\u003Evar\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Edata1\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Etext\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'小美'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Evalue\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}...\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E];\u003C\u002Fspan\u003E
\n\u003Cspan class=\&c1\&\u003E\u002F\u002F第一列的text\u002Fvalue列表\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&kd\&\u003Evar\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Edata2\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Etext\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'张三'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Evalue\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}...\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E];\u003C\u002Fspan\u003E\n\u003Cspan class=\&c1\&\u003E\u002F\u002F第二列的text\u002Fvalue列表\u003C\u002Fspan\u003E\n\u003Cspan class=\&kd\&\u003Evar\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Edata3\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Etext\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'开心'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Evalue\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}...\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E];\u003C\u002Fspan\u003E\n\u003Cspan class=\&c1\&\u003E\u002F\u002F第三列的text\u002Fvalue列表\u003C\u002Fspan\u003E\n\u003Cspan class=\&nx\&\u003E$name\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Epicker\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E({\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Edata\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Edata1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Edata2\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Edata3\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E],\u003C\u002Fspan\u003E \u003Cspan class=\&c1\&\u003E\u002F\u002F初始化三列数据,想要两列就传入两列\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003EselectIndex\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E2\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E],\u003C\u002Fspan\u003E
\u003Cspan class=\&c1\&\u003E\u002F\u002F三列数据各自选中的下标\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003Etitle\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&s1\&\u003E'我们都是小学生'\u003C\u002Fspan\u003E
\u003Cspan class=\&c1\&\u003E\u002F\u002Fpicker题目\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}).\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eon\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'picker.select'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&kd\&\u003Efunction\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ee\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003EselectVal\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003EselectIndex\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&c1\&\u003E\u002F\u002F 当用户点击确定的时候,会派发picker.select事件,同时会传递每列选择的值数组selectVal和每列选择的序号数组selectIndex。\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}).\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eon\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'picker.change'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&kd\&\u003Efunction\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ee\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eindex\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003EselectIndex\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&c1\&\u003E\u002F\u002F当一列滚动停止的时候,会派发picker.change事件,同时会传递列序号index及滚动停止的位置selectIndex。\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}).\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eon\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'picker.valuechange'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\u003Cspan class=\&kd\&\u003Efunction\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ee\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003EselectVal\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003EselectIndex\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E){\u003C\u002Fspan\u003E\n
\u003Cspan class=\&c1\&\u003E\u002F\u002F当用户点击确定的时候,如果本次选择的数据和上一次不一致,会派发picker.valuechange事件,同时会传递每列选择的值数组selectVal和每列选择的序号数组selectIndex。\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E});\u003C\u002Fspan\u003E\n\u003Cspan class=\&nx\&\u003E$name\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eon\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s1\&\u003E'click'\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&kd\&\u003Efunction\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\

我要回帖

更多关于 hexo coding 搭建博客 的文章

 

随机推荐