关于博客搭建:使用-github-和-Deno-Deploy-搭建一个博客网站

这个可能是目前最简略的搭建博客网站的形式了。你只须要有一个github账号就行了。 github 是一个用来放代码的网站,程序员大多应用此网站寄存本人的代码,和他人交换本人/他人的代码的bug…… Deno Deploy 是一个服务部署网站,你把代码交给它,它就会把你的代码跑起来了。相似于腾讯云、阿里云之类的,比他们更简略更易用,不必再本人买机器装零碎blabla等繁琐的操作了。只不过是只能部署用Deno 写的服务。能够应用 github 的账号登录。 简略概括一下步骤: 在 github 上创立一个代码仓库,用来寄存博客零碎的代码和内容。在 Deno Deploy 平台上创立一个我的项目,关联上一步的代码仓库。在github上编写博客内容,推送到github仓库后,等个10s左右,Deno Deploy 会主动部署实现。创立 github 仓库,寄存博客代码和内容0. 创立代码仓库,能够抉择公有的或公开我的项目依照你的须要填你本人起的名字。抉择Public的话,他人就能看到你的代码仓库外面的内容,留神不要放些敏感内容。 1. 配置博客相干信息在我的项目根目录下创立一个名为main.tsx的文件,文件内容如下(依据须要改成你的相干信息): import blog from "https://deno.land/x/blog/blog.tsx";blog({ author: "Dino", title: "My Blog", description: "The blog description.", avatar: "https://deno-avatar.deno.dev/avatar/83a531.svg", avatarClass: "rounded-full", links: [ { title: "Email", url: "mailto:bot@deno.com" }, { title: "GitHub", url: "https://github.com/denobot" }, { title: "Twitter", url: "https://twitter.com/denobot" }, ], lang: "zh",});更多具体设置,看 deno_blog 文档 2. 创立博客内容一个小技巧:在github 的仓库主页,按句号.对应的按键,能够用github的在线编辑器关上此仓库,能够间接在线编辑提交到代码仓库。创立文件夹posts,留神,这个文件夹名字固定是这个;而后在文件夹中创立第一篇博客,比方这里创立了一个名为hello_world.md,的文件作为第一篇博客(留神:文件名称不要有空格和中文),内容如下: ---title: 第一篇博客publish_date: 2022-11-20tags: ['hello-world']---这是我的第一篇博客!这里是博客内容将内容推送到 github 的代码仓库中。 ...

November 21, 2022 · 1 min · jiezi

关于博客搭建:Hexo搭建基操配置

初始化Hexo首先须要以下装置环境,间接在官网下载即可 Node.jsGit环境筹备好后,装置Hexo npm install -g hexo-cli装置后在指定文件夹创立我的项目 hexo init <folder>cd <folder>npm install抉择主题 Hexo初始化胜利后,而后须要抉择一个丑陋的主题,这个举荐间接在官网上找,主题 选中主题后下载到我的项目themes文件夹而后批改根目录下的_config.yml文件theme: '主题名,也就是themes文件夹里的名字'hexo最罕用的有以下几个命令 启动服务:hexo server 或 hexo s新建文章:hexo new "文章名字"新建动态页面:hexo new page "页面名字"生成public文件夹:hexo generate 或 hexo g革除public文件夹:hexo clean部署:hexo deploy 或 hexo d部署到GithubGithub有收费提供搭建动态网站的性能,所以hexo举荐间接放到GitHub Page,依照以下步骤进行部署。 首先新建一个源码repo,倡议抉择Private,这个用来放hexo我的项目代码,不对外开放本地仓库运行以下命令提交代码 git init:git初始化git add .: 将所有代码增加到暂存区git commit -m "first commit":提交本次commitgit remote add origin '近程仓库地址':增加近程仓库地址git push -u origin main:推送代码而后再新建一个仓库,名字为 github名字.github.io,这个仓库用于放部署后的代码装置Hexo部署插件 npm install hexo-deployer-git批改根目录下的 _config.yml deploy: type: git repo: 部署仓库地址 branch: main最初执行hexo deploy命令就能够在github名字.github.io看到博客了SSH + github Actions主动部署Github Actions是github专用的继续集成脚本,用于主动执行工作流程,SSH能够通过建设密钥免密登录,这两个联合就能达到主动部署的成果首先通过ssh-keygen生成密钥,ssh-keygen -t rsa -C "Github 的邮箱地址"而后github配置ssh,配置门路:github -> Settings –> SSH and GPG keys git仓库增加密钥公有变量,配置门路:repo -> Settings -> Secrets,点击New repository secret,name能够自定义,次要是接下来的deploy.yaml文件须要应用,Secret填写你生成的密钥而后根目录下新建.github/workflows/deploy.yaml,并填写以下内容 ...

September 7, 2022 · 1 min · jiezi

关于博客搭建:Hugo-even-GitHub-Pages-Utterances搭建个人博客

前言多年前基于Jekyll和GitHub Pages搭建的博客款式总是出问题,调研了下目前最风行的动态博客次要有3个: hexo:基于Node.js,开源地址:https://github.com/hexojs/hexoHugo:基于Go实现,开源地址:https://github.com/gohugoio/hugogridea:国产,集体保护,开源地址:https://github.com/getgridea/grideaHugo目前GitHub上star最多,我关注的几个技术博主根本都选用的Hugo,再加上Hugo基于Go实现,有bug我也能够思考本人解决,于是果决抉择了Hugo。 最终成果参考https://jincheng9.github.io/ 步骤Hugo装置Hugo(以Mac为例) $ brew install hugo创立本地文件夹,用于存储博客内容,假如文件夹叫blog $ hugo new site blogblog目录下的文件内容如下: drwxr-xr-x 9 zhangjincheng staff 288 12 21 14:46 .drwxr-xr-x 17 zhangjincheng staff 544 12 21 14:46 ..drwxr-xr-x 3 zhangjincheng staff 96 12 21 14:46 archetypes-rw-r--r-- 1 zhangjincheng staff 82 12 21 14:46 config.tomldrwxr-xr-x 2 zhangjincheng staff 64 12 21 14:46 contentdrwxr-xr-x 2 zhangjincheng staff 64 12 21 14:46 datadrwxr-xr-x 2 zhangjincheng staff 64 12 21 14:46 layoutsdrwxr-xr-x 2 zhangjincheng staff 64 12 21 14:46 staticdrwxr-xr-x 2 zhangjincheng staff 64 12 21 14:46 themeseven主题给Hugo配置even主题: ...

December 21, 2021 · 1 min · jiezi

关于博客搭建:使用Cloudflare和notion搭建自己的博客

欢送拜访我的集体博客ximikang.icu应用Cloudflare和notion搭建本人的博客应用过Nation的小伙伴肯定在某个时刻冒出过这样子的想法:这个笔记利用这么好用,而且是基于Web端开发的,那为什么不能够用他来作为我本人的博客呢?这样我是不是能够用记笔记的形式来疾速的实现和保护本人的博客了呢? 我搭建的集体博客,欢送大家拜访。https://notion.ximikang.icu/ 购买一个域名并将域名转移到Cloudflare首先咱们须要将域名的域名服务器设置为 Cloudflare 的域名服务器,这样就能够应用 Cloudflare 的 DNS 服务了。 注册Cloudflare ,而后在这里增加本人的域名: 将你的域名指向notion切换到 dns 页面,增加一条 CNAME 记录,如果你本人的二级域名为 blog.xxx.com,那么名称那就填 blog,指标填 Notion 的域名,保留。 或者须要将主域名设置为则为 留神须要将代理状态切换至已代理,这样能力用cloudflare的收费CDN。 增加后下方就会显示已增加的域名,点击已增加的域名进入设置页面。 配置Cloudflare的Web Worker配置workder使得咱们拜访咱们下面定义的http申请能够主动改写。 首先治理Workers,新建一个对应的notion的worker。 获取notion页面中的链接ID,如图中的id须要在worker配置中应用。 创立worker后编辑worker,将如下代码复制到worker中。 /* CONFIGURATION STARTS HERE */ /* 增加域名 notion.ximikang.icu*/ const MY_DOMAIN = ''; /* 增加方才的id 例如: '': 'bb1678c7e8fe47b29bf49aac08aebbb', */ const SLUG_TO_PAGE = { '': '', }; /* 增加主页面的题目和形容 */ const PAGE_TITLE = ''; const PAGE_DESCRIPTION = ''; /* 能够抉择显示的字体 https://fonts.google.com 例如:const GOOGLE_FONT = 'Noto Sans Simplified Chinese';*/ const GOOGLE_FONT = ''; /* 增加自定义脚本,能够增加Google Analytics */ const CUSTOM_SCRIPT = ``; /* CONFIGURATION ENDS HERE */ const PAGE_TO_SLUG = {}; const slugs = []; const pages = []; Object.keys(SLUG_TO_PAGE).forEach(slug => { const page = SLUG_TO_PAGE[slug]; slugs.push(slug); pages.push(page); PAGE_TO_SLUG[page] = slug; }); addEventListener('fetch', event => { event.respondWith(fetchAndApply(event.request)); }); function generateSitemap() { let sitemap = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'; slugs.forEach( (slug) => (sitemap += '<url><loc>https://' + MY_DOMAIN + '/' + slug + '</loc></url>') ); sitemap += '</urlset>'; return sitemap; } const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }; function handleOptions(request) { if (request.headers.get('Origin') !== null && request.headers.get('Access-Control-Request-Method') !== null && request.headers.get('Access-Control-Request-Headers') !== null) { // Handle CORS pre-flight request. return new Response(null, { headers: corsHeaders }); } else { // Handle standard OPTIONS request. return new Response(null, { headers: { 'Allow': 'GET, HEAD, POST, PUT, OPTIONS', } }); } } async function fetchAndApply(request) { if (request.method === 'OPTIONS') { return handleOptions(request); } let url = new URL(request.url); url.hostname = 'www.notion.so'; if (url.pathname === '/robots.txt') { return new Response('Sitemap: https://' + MY_DOMAIN + '/sitemap.xml'); } if (url.pathname === '/sitemap.xml') { let response = new Response(generateSitemap()); response.headers.set('content-type', 'application/xml'); return response; } let response; if (url.pathname.startsWith('/app') && url.pathname.endsWith('js')) { response = await fetch(url.toString()); let body = await response.text(); response = new Response(body.replace(/www.notion.so/g, MY_DOMAIN).replace(/notion.so/g, MY_DOMAIN), response); response.headers.set('Content-Type', 'application/x-javascript'); return response; } else if ((url.pathname.startsWith('/api'))) { // Forward API response = await fetch(url.toString(), { body: url.pathname.startsWith('/api/v3/getPublicPageData') ? null : request.body, headers: { 'content-type': 'application/json;charset=UTF-8', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36' }, method: 'POST', }); response = new Response(response.body, response); response.headers.set('Access-Control-Allow-Origin', '*'); return response; } else if (slugs.indexOf(url.pathname.slice(1)) > -1) { const pageId = SLUG_TO_PAGE[url.pathname.slice(1)]; return Response.redirect('https://' + MY_DOMAIN + '/' + pageId, 301); } else { response = await fetch(url.toString(), { body: request.body, headers: request.headers, method: request.method, }); response = new Response(response.body, response); response.headers.delete('Content-Security-Policy'); response.headers.delete('X-Content-Security-Policy'); } return appendJavascript(response, SLUG_TO_PAGE); } class MetaRewriter { element(element) { if (PAGE_TITLE !== '') { if (element.getAttribute('property') === 'og:title' || element.getAttribute('name') === 'twitter:title') { element.setAttribute('content', PAGE_TITLE); } if (element.tagName === 'title') { element.setInnerContent(PAGE_TITLE); } } if (PAGE_DESCRIPTION !== '') { if (element.getAttribute('name') === 'description' || element.getAttribute('property') === 'og:description' || element.getAttribute('name') === 'twitter:description') { element.setAttribute('content', PAGE_DESCRIPTION); } } if (element.getAttribute('property') === 'og:url' || element.getAttribute('name') === 'twitter:url') { element.setAttribute('content', MY_DOMAIN); } if (element.getAttribute('name') === 'apple-itunes-app') { element.remove(); } } } class HeadRewriter { element(element) { if (GOOGLE_FONT !== '') { element.append(`<link href="https://fonts.googleapis.com/css?family=${GOOGLE_FONT.replace(' ', '+')}:Regular,Bold,Italic&display=swap" rel="stylesheet"> <style>* { font-family: "${GOOGLE_FONT}" !important; }</style>`, { html: true }); } element.append(`<style> div.notion-topbar > div > div:nth-child(3) { display: none !important; } div.notion-topbar > div > div:nth-child(4) { display: none !important; } div.notion-topbar > div > div:nth-child(5) { display: none !important; } div.notion-topbar > div > div:nth-child(6) { display: none !important; } div.notion-topbar-mobile > div:nth-child(3) { display: none !important; } div.notion-topbar-mobile > div:nth-child(4) { display: none !important; } div.notion-topbar > div > div:nth-child(1n).toggle-mode { display: block !important; } div.notion-topbar-mobile > div:nth-child(1n).toggle-mode { display: block !important; } </style>`, { html: true }) } } class BodyRewriter { constructor(SLUG_TO_PAGE) { this.SLUG_TO_PAGE = SLUG_TO_PAGE; } element(element) { element.append(`<div style="display:none">Powered by <a href="http://fruitionsite.com">Fruition</a></div> <script> window.CONFIG.domainBaseUrl = 'https://${MY_DOMAIN}'; const SLUG_TO_PAGE = ${JSON.stringify(this.SLUG_TO_PAGE)}; const PAGE_TO_SLUG = {}; const slugs = []; const pages = []; const el = document.createElement('div'); let redirected = false; Object.keys(SLUG_TO_PAGE).forEach(slug => { const page = SLUG_TO_PAGE[slug]; slugs.push(slug); pages.push(page); PAGE_TO_SLUG[page] = slug; }); function getPage() { return location.pathname.slice(-32); } function getSlug() { return location.pathname.slice(1); } function updateSlug() { const slug = PAGE_TO_SLUG[getPage()]; if (slug != null) { history.replaceState(history.state, '', '/' + slug); } } function onDark() { el.innerHTML = '<div title="Change to Light Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgb(46, 170, 220); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(12px) translateY(0px);"></div></div></div></div>'; document.body.classList.add('dark'); __console.environment.ThemeStore.setState({ mode: 'dark' }); }; function onLight() { el.innerHTML = '<div title="Change to Dark Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgba(135, 131, 120, 0.3); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(0px) translateY(0px);"></div></div></div></div>'; document.body.classList.remove('dark'); __console.environment.ThemeStore.setState({ mode: 'light' }); } function toggle() { if (document.body.classList.contains('dark')) { onLight(); } else { onDark(); } } function addDarkModeButton(device) { const nav = device === 'web' ? document.querySelector('.notion-topbar').firstChild : document.querySelector('.notion-topbar-mobile'); el.className = 'toggle-mode'; el.addEventListener('click', toggle); nav.appendChild(el); onLight(); } const observer = new MutationObserver(function() { if (redirected) return; const nav = document.querySelector('.notion-topbar'); const mobileNav = document.querySelector('.notion-topbar-mobile'); if (nav && nav.firstChild && nav.firstChild.firstChild || mobileNav && mobileNav.firstChild) { redirected = true; updateSlug(); addDarkModeButton(nav ? 'web' : 'mobile'); const onpopstate = window.onpopstate; window.onpopstate = function() { if (slugs.includes(getSlug())) { const page = SLUG_TO_PAGE[getSlug()]; if (page) { history.replaceState(history.state, 'bypass', '/' + page); } } onpopstate.apply(this, [].slice.call(arguments)); updateSlug(); }; } }); observer.observe(document.querySelector('#notion-app'), { childList: true, subtree: true, }); const replaceState = window.history.replaceState; window.history.replaceState = function(state) { if (arguments[1] !== 'bypass' && slugs.includes(getSlug())) return; return replaceState.apply(window.history, arguments); }; const pushState = window.history.pushState; window.history.pushState = function(state) { const dest = new URL(location.protocol + location.host + arguments[2]); const id = dest.pathname.slice(-32); if (pages.includes(id)) { arguments[2] = '/' + PAGE_TO_SLUG[id]; } return pushState.apply(window.history, arguments); }; const open = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function() { arguments[1] = arguments[1].replace('${MY_DOMAIN}', 'www.notion.so'); return open.apply(this, [].slice.call(arguments)); }; </script>${CUSTOM_SCRIPT}`, { html: true }); } } async function appendJavascript(res, SLUG_TO_PAGE) { return new HTMLRewriter() .on('title', new MetaRewriter()) .on('meta', new MetaRewriter()) .on('head', new HeadRewriter()) .on('body', new BodyRewriter(SLUG_TO_PAGE)) .transform(res); }配置对应的路由,也就是拜访咱们的域名后,须要执行咱们定义的notion worker ...

September 16, 2021 · 5 min · jiezi

关于博客搭建:Mac-OS上搭建Hexo博客流程

Hexo 介绍https://hexo.iohttps://hexo.io/zh-cn/环境筹备装置nodejs下载地址:https://nodejs.org/en/以:node-v14.17.1.pkg 为例 关上终端,切换root用户比方:关上terminal或者iterms终端软件,执行以下命令 //切换到root用户sudo su 装置淘宝镜像//应用npm装置相干依赖包,速度很慢,倡议装置淘宝镜像,后续命令应用cnpmnpm install -g cnpm --registry=https://registry.npm.taobao.org 装置Hexo//装置hexocnpm install -g hexo-cli Hexo装置胜利 初始化博客网站新建我的项目文件夹,并进入文件夹内比方:Blog。后续如果我的项目呈现问题,或者想从新创立,只须要删除此文件夹,即可 //初始化我的项目(必须进入到我的项目文件夹外面后,执行此命令)sudo hexo init 启动hexo//启动hexohexo s 浏览器关上博客网站// 浏览器关上http://localhost:4000 创立博客,并写博客Vim命令的应用,能够参考这里 //创立文章hexo n "我的第一篇博客文章" //编辑文章vim 我的第一篇博客文章.md 项目管理执行清理我的项目缓存新增博客文章或者批改博客文章内容,须要执行此命令 // 清理之前生成的网站hexo clean 从新生成我的项目// 从新生成博客我的项目hexo g 再次启动我的项目// 再次启动博客网站hexo s 批改主题抉择主题大家能够依照本人的须要下载适宜的主题。能够从官网下载比方:抉择 butterfly 下载主题代码在我的项目根目录下,即"Blog“文件夹下执行以下命令, // clone主题代码到 themes/butterflygit clone https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly 批改我的项目配置文件,配置主题// 批改配置文件,主题主题vim _config.yml ...

June 28, 2021 · 1 min · jiezi

关于博客搭建:实战丨Halo轻松部署属于自己的博客系统

背景Halo 是一款现代化的集体独立博客零碎,给习惯写博客的同学多一个抉择。 云托管(Tencent CloudBase Run)是 云开发(Tencent CloudBase,TCB)提供的新一代云原生利用引擎(App Engine 2.0),反对托管任意容器化利用。 应用云托管部署博客零碎筹备工作开明 CloudBase 云开发环境留神: 包年包月环境无奈开明云托管。起源为“微信小程序”的环境暂不反对开明云托管。第 1 步:开明登录 云开发 CloudBase 控制台,抉择一个环境,进入云托管治理页面,单击「立刻开明」。 第 2 步:配置云托管所在地区云托管以后仅凋谢上海地区,更多地区将陆续凋谢。 云托管网络网络设置包含 公有网络 VPC 和 子网。以环境为维度,云托管中创立的所有服务,都将部署在这同一个 VPC 之中,且胜利开明云托管后不反对更换网络设置。 网络设置分为如下两种模式: 零碎默认配置云托管将为您主动新建一个 VPC 和子网,并将其绑定您以后的云开发环境。 后续您在以后环境下的云托管中创立的所有服务,都将被部署到这个 VPC 和子网中。 此过程无需您手动进行任何设置。后续您能够在 公有网络控制台 中查看并治理这个由云托管主动创立的 VPC 及子网,也能够将更多云资源(例如云服务器,云数据库等)部署在这个 VPC 之内,以便这些云资源进行互动。 自定义配置如果您之前曾经创立过公有网络 VPC 和子网,并在其中曾经部署有其余云资源(例如云服务器、云数据库等),且须要您的云托管利用与这些已存在的云资源进行互动,则您能够抉择「自定义配置」,而后抉择具体的 VPC 和子网。 VPC 只能抉择一个,子网反对抉择多个。 开明胜利单击「提交」,状态变为开明中,请期待数秒。 开明胜利后,您将主动跳转到云托管的服务列表页面。 至此您曾经胜利开明后云托管服务,您能够单击「新建服务」开始部署咱们的管理系统。 新建服务单击「新建服务」,填写服务名称、备注信息(选填)后,单击「提交」 封装halo博客零碎压缩包1、下载拉取halo博客零碎halo博客零碎:https://github.com/halo-dev/halo 依据状况批改mysql 和 redis等配置2、本地打包jar应用intellij idea 、eclipse 等工具打包工程。 若依属于多模块的我的项目,java的话,间接用生成的jar包 来做dockerfile吧。编译走本地,这样会不便点。PS:因为Java须要很多依赖,很多开发者习惯拉取到本地之类的。3、编写dockerfile文件# Use java 8 FROM java:8ADD target/*.jar /app.jar# 裸露端口EXPOSE 80ENTRYPOINT ["java","-jar","/app.jar"]4、文件夹或zip文件构造 ...

February 3, 2021 · 1 min · jiezi

VuePress一-10分钟开启你的个人博客生涯

有时想写个自己的博客又苦于需要一堆堆的前置技能:既要先完成优雅而具备良好兼容性的界面设计,又要购买服务器和部署后台,这严重阻碍了大众踏出搭建网站第一步的积极性,于是 VuePress 横空出世,按照教程操作,从构建网站(包括完成第一篇文章)到在 Github Page 只需要短短 10 分钟不到的时间。不信?打开Visual Code和浏览器跟着文章来一遍吧~ 本文基于 VuePress 1.1.0 文档编写.0.前置本地安装好 Node.js(版本>=8.0), 推荐安装 yarn.具备可运行命令的终端 Window: cmder、Git BashMac: Terminal、iTermLinux: 默认终端注册好 Github 账号1. 本地创建博客项目参(fan)考(yi): 官方教程-Getting Started1)打开终端mkdir blog # 本地创建 blog 项目cd blogyarn add -D vuepress # 或者 npm i -D vuepress, 安装 vuepress 依赖mkdir docsecho 'Hello, my first blog!' > docs/README.md # 编写第一篇文章(注意符号)2)在 package.json 中加入脚本命令{ "scripts": { "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, "devDependencies": { "vuepress": "^1.1.0" }}3)尝试本地运行项目运行 yarn docs:dev 或者 npm run docs:dev ...

October 15, 2019 · 1 min · jiezi

丢掉烂大街的Hexo吧用Vuepress搭建专属你的博客吧

一、先来聊聊搭建博客1.1. 什么人喜欢折腾博客通常如果不是爱折腾或者有特别需求的普通人,一般对于搭建博客的需求其实不大,通常主要一下几类人会搭建博客自己玩玩。 程序员 (喜欢写一些技术博客,分享一些问题)设计工作者 (需要一个平台暂时自己,通常的网站满足不了他们)技术爱好者 (不一定是干IT的,但是希望搞一个博客交流的)文艺青年 (喜欢写写文章分享给大家的)极客 (喜欢追求极致,喜欢做一些酷炫吊炸天的网站的异类)对于上述几类人群,我们再细分一下,他们能够搭建博客的方式。 博客平台 (CSDN、博客园、Farbox之类的一站式平台,写好文章网上丢就可以了)纯自己码 (需要超强的技术壁垒)搭建博客的工具 (下面会进行详细说明,目前也是最主流的方式) WordPressHexoHugoVuepress (本文主要说的)jekyll1.2. 简单对比一下几个吧!(不废话,直接说缺点)WordPress:第一个是麻烦,学习成本高,第二个必须要搞一个自己域名主机,操作很麻烦,新手门槛高。Hexo:好,好,好 ,轻量 、简单 、实用 、方便 —————but 烂大街了,现在随便百度还是谷歌搜一下都是Hexo搭建博客,NexT主题也被用烂大街了,导致搭一个博客一点新意都没,没有那种成就感。(当然你自己能另写主题就算了)Hugo:小众 文档少 优化少 主题少 坑多jekyll:跟Hugo也差不多Vuepress:哈哈哈,比Hugo/jekyll 文档还少,主题更少 (惊不惊喜,意不意外) ,教程也少,要不然都是重复的。--------but 真心好看,是尤雨溪大神的力作,优化更不要说了,整体风格极简却又非常适合文档阅读,而且流畅,最重要,使用的人少,个性化定制多,只要你会点vue,你的博客只属于你。1.3. 废话不多说,直接看效果预览效果:https://domyselfzy.github.io/ 首页 文章 时间线 关于我 二、正菜开始2.1 直接开干vuepress官网:https://vuepress.vuejs.org/zh/ 如果想搭建一个简单的vuepress博客,很简单。 项目安装 不推荐全局安装方式,因为一个新手很难把握文件层级 # 将 VuePress 作为一个本地依赖安装yarn add -D vuepress # 或者:npm install -D vuepress# 新建一个 docs 文件夹mkdir docs# 新建一个 markdown 文件echo '# Hello VuePress!' > docs/README.md# 开始写作npx vuepress dev docs目录如下: ...

July 11, 2019 · 1 min · jiezi

借助URLOS快速安装WordPress

简介WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统。WordPress具有插件架构和模板系统。截至2018年4月,排名前1000万的网站超过30.6%使用WordPress。WordPress是最受欢迎的网站内容管理系统。WordPress是当前因特网上最流行的博客系统。 今天我们介绍一种更快速的安装方法,那就是通过URLOS一键安装WordPress。urlos是什么? URLOS是一个云主机管理软件,基于Docker容器技术打包和运行应用,包含负载均衡和故障转移等高级功能,可自动识别机器和云应用的故障并将云应用转移至可用的机器上,单机故障并不影响业务开展。 你可以使用以下命令安装URLOS: curl -LO www.urlos.com/iu && sh iu在此不讨论URLOS的使用方法,感兴趣的朋友请自行搜索,我们直接来看URLOS如何快速安装WordPress: 安装流程1. 登录URLOS系统后台,在应用市场中搜索“WordPress”,找到之后,直接点击安装按钮 2.填写服务名称、选择运行节点、选择智能部署 3.填写域名:www.aaa.com(这里填写自己的域名) 4.设置数据库 然后点击“提交”按钮,等待部署完成;

July 10, 2019 · 1 min · jiezi

Pelican+Github搭建博客

Github设置注册后登录Github,点击“Creat a new repo”,版本库名使用'username.github.io'的格式,这里将username替换成自己的用户名即可。 安装Python、Pelican和Markdownyum install python pip install pelican pip install markdown http://wowubuntu.com/markdown/ 创建博客骨架搭建博客目录:mkdir blog; cd blog; pelican-quickstart。 进入output文件夹,把username.github.io版本库clone下来:cd output; git clone https://github.com/username/u...。 设置上传部署到Github,修改根目录下的Makefile文件: OUTPUTDIR=$(BASEDIR)/output/username.github.io publish: $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) github: publish cd OUTPUTDIR ; git add . ; git commit -am 'your comments' ; git push 定制博客设置pelicanconf.py。 导航目录项MENUITEMS = (("ITEM1","http://github.com"),("ITEM2",URL), ......)。 安装主题安装主题:git clone https://github.com/getpelican...;cd pelican-themes;pelican-themes -i bootstrap2。 设置主题:THEME = 'bootstrap2'。 推荐主题:Elegant http://oncrashreboot.com/eleg...。 ...

June 8, 2019 · 1 min · jiezi

Docsify快速搭建个人博客

平常写一些文档或者个人笔记时,Markdown 是我的第一个选择,因为它用起来真的很方便、简洁。那么今天要讲的 Docsify 是什么呢?Docsify简介Docsify 是一个动态生成文档网站的工具。不同于 GitBook、Hexo 的地方是它不会生成将 .md 转成 .html 文件,所有转换工作都是在运行时进行。 这将非常实用,如果只是需要快速的搭建一个小型的文档网站,或者不想因为生成的一堆 .html 文件“污染” commit 记录,只需要创建一个 index.html 就可以开始写文档而且直接部署在 GitHub Pages。 docsify 中文文档 全局安装安装脚手架工具 docsify-cli,安装过程中较慢的可以切换 npm 源为 cnpm $ npm i docsify-cli -gGithub 创建你的 Blog 项目如果你正在用 Markdown 写一些 Blog 项目,那么也可以用你现在的项目,如果你没有,那么建议你在 Github 新建一个属于你的 Blog 项目,开始我们接下来的学习 初始化文档注意这里的文件名约定为 docs 也是官方推荐,请按照规则设置,否则发到 Github 可能会出现一些问题 $ docsify init docsInitialization succeeded! Please run docsify serve docs执行完以上命令 docs 文件目录下会生成以下 3 个文件: index.html:入口文件README.md:会做为主页内容渲染.nojekyll:用于阻止 GitHub Pages 会忽略掉下划线开头的文件启动本地服务预览docs 同级目录下执行以下命令,打开本地服务器,默认地址为:http://localhost:3000 ...

May 26, 2019 · 1 min · jiezi

年轻人的第一篇博客

前言写这篇文章的前因是有位读者留言提到了相关的话题,其实在之前有一篇《如何成为一位「不那么差」的程序员》时有简要提到但没有细说;这次就借这个机会好好聊聊这个事情的前因后果。 <!--more--> 为什么要写博客为什么要写博客? 我觉得大部分人应该都知道标准答案。 翻了下记录,我从 16 年四月份至今写了三年的博客,产出了 100 多篇;现在让我回忆当初为啥要写博客,我还记得那时作为一个初入职场的小菜鸟有许多问题、资料都要在网上查找;那时候我就发现不少答案网上已经有现成的了,而且有些大牛还有着酷炫的个人网站。 这事对我冲击挺大的,主要有以下几点: 我能在别人博客上查到我想要的东西,那是否我也可以输出一些东西被被人搜到呢?这样是否对方便记录我自己的问题(高中时的错题本)甚至对今后面试有好处?酷炫的博客页面又是一个展示(装B)自己的机会。我相信大部分人无非也是这几个原因吧,具体是哪个原因我觉得大家首先要想清楚。 因为如果是前面两个原因,也许后面会因为博客内容帮助到自己的同时也帮助到他人,让自己更有坚持下去的动力。 如果是最后一个,大概率的会因为自己不想折腾而慢慢放弃。 我现在主要还是前面两个原因而继续坚持,毕竟我的博客外观已经几年没更新了????。 你适合写博客嘛?目的搞清楚之后再来看看你适合写博客嘛?不过我觉得这个问题改为 你会坚持写博客嘛? 可能更直接一些。 因为我认为写博客最大的阻碍就是【坚持】二字了。 我见过不少博客写的很棒的大牛突然之间停更,至今也没有恢复,其实挺可惜的。 再讨论这个问题之前首先看两个问题: 做什么事情自己坚持的最久?什么原因导致没有坚持下去?我还记得大学期间我和另外两个小伙伴组建了一个篮球小团伙,每天中午、晚上都会自觉的约上一起练球直到后来毕业。 现在想想当初为啥会坚持呢?我觉得核心要求有几个: 一个是【热爱】,当初我们是真的热爱这个运动,每天不打场球真的浑身不舒服。二是有【产出】,大学那阵由于每天我们都训练进步还是非常明显的,在学校球场基本上是横着走;以致于在球场的知名度也越来越高,当然在虚荣心的驱使下更会拼命练球。所以回归到写博客这件事情上来,我觉得只要你对这件事是喜欢的,有激情,同时写出的东西对你有正向激励就会让自己有坚持下去的理由。 第二个问题:什么原因导致没有坚持下去? 就像我去年立下的健身 flag 一样,现在也放弃了。 归根到底还是因为太懒了,锻炼多麻烦,站着不如坐着,坐着不如躺着;同时效果也不明显导致非常容易放弃。 换到写博客这事上来也是一样的。 写一篇不吹水的文章并没有想象那么容易,需要找资料、论证、写提纲、码字等一系列过程。 和健身一样也是一个长期同时效果不明显的工程,所以弃坑率很高。 鉴于这些,我觉得你具备这几点作为一个长更(长期更新)型选手是没有问题的。 热爱技术、文字表达,而不是短时间的头脑发热。写的过程中自己能得到积累提高,而不是每天记录流水账。要有毅力坚持下去,这个就比较玄学了;通常前面两项做的好这个毅力就会随之增强。所以自己是否适合写博客应该有自己的判断了。 会遇到哪些问题我还得要给大家泼盆凉水,现在来聊聊写博客过程中可能会碰到哪些问题,不一定都对但至少都是我遇到过的。 原创、抄袭首先第一个就是 抄袭 问题; 随着这几年互联网的发展,自媒体也越来越多,这也包括了大量的技术自媒体。 也许哪天你会收到某平台向你发出申请,想要转载你的文章;自己在得意之余还得要谨慎对待以下几件事情: 对方是否标明出处,包括但不限于二维码、用户ID、网站地址等。是否明显标注出处;不要小瞧这个,很多鸡贼的作者确实也会标明出处,但你不拿出老花镜来看是根本发现不了的。(这类撕 B 事件不止听说一两次)对于自己(这里的自己指申请者)需要声明原创的要求是否提供了稿费?写一篇原创文章是很费精力的,该拿的一定不能亏了自己。这些其实都还好,还有一些不知名的平台或作者会神不知鬼不觉的转载你的内容,完全不提出处这件事。 甚至有些还会修改你的部分内容,转换为他自己的一部分然后发表在一些类似于“头条号、百家号”之类的自媒体平台上;也就是大家常说的“洗搞”。 我就不止遇到过一次,刚开始还很气愤上去理论;态度好的会道个歉然后删除文章,态度不好的甚至还会和你互喷让你拿出证据证明这是你写的。 经过 N 多次的斗智斗勇后现在即便是有朋友给我说 XX 平台上好像有你的内容,我也非常淡定,啥大风大浪没有见过。 换个角度想,别人转载不注明出处、甚至抄袭不也侧面证明自己写的还不错嘛?开个玩笑,其实主要原因还是当前的写作环境复杂,没有某一个大的平台来约束所有的产出内容;在这样的客观条件下我们能做的也只有产出优质的内容扩大自己的影响力从而让某些抄袭者不敢轻举乱动。 产出瓶颈还有一个问题也挺突出,那就是产出问题。 有段时间我甚至能做到一个月 6 篇原创,当然也有一个月憋出一篇的尴尬。 这也是没办法的事情,内容产出不像是工地搬砖,花时间就会有效果。 这个问题我相信 99% 写原创的作者都会碰到,根据我目前的经验还是有几个小 tips 可以提供给大家: 平时一定要多积累,不管是工作中的案例还是业余学到的小技巧都可以;这样不用等到想写点什么的时候没有灵感的尴尬。实在不知道写啥时切莫强行输出,也许这一篇文章就能把你之前积累的口碑破坏。没有题材时不如多翻翻之前写的东西,也许就有了新的灵感。能赚钱嘛?问题讲了这么多来看看现实的问题,这事能赚钱嘛? 有这个想法也没错,毕竟要花费自己大量的业余时间及精力。 ...

May 24, 2019 · 1 min · jiezi

CODING Pages 服务全面升级,更快更稳更可靠!

尊敬的 CODING 用户: 您好! 由于原上游服务商无法满足 CODING Pages 日益增长的用户量以及访问速度需求,同时提供的 DDoS 解决方案无法支撑大型 DDoS 攻击,给 CODING 用户造成了使用上的不便,对此我们深表歉意。 为保障用户使用体验,经过无缝平稳迁移,CODING Pages 服务现已全面升级至腾讯云,为用户提供更加强大的网络资源,加速 Pages 访问,同时优化了防 DDoS 方案,稳定性大幅提升。 CODING Pages 拥有强大的页面托管服务,提供自定义域名、免费 SSL 证书、自动实时部署等功能,使用 CODING Pages 一键托管您的网站,通过实时自动发布您在腾讯云开发者平台中托管的代码,向世界介绍您与您的项目。 目前已有数万开发者、设计师、产品经理、团队与企业在使用 CODING Pages 托管他(她)们的个人网站、博客、企业与产品官网、在线文档等。 点击注册腾讯云开发者平台→ 创建一个项目 → 一键开启 CODING Pages 现在开始面向世界! 同时 CODING 研发管理系统(企业版)也将在近期支持 Pages 功能,点击体验 CODING 研发管理系统,一站式 DevOps,提升研发效能,五人以下团队免费。 如果在使用 Pages 服务的过程中遇到任何问题,欢迎查阅帮助文档获取相关信息: https://dev.tencent.com/help/doc/quick-start/creating-pages 或者通过以下方式随时向我们反馈: 在线反馈渠道:https://feedback.coding.net 官方邮箱:support@coding.net CODING 团队

April 22, 2019 · 1 min · jiezi

自建网站写博客,怎么被百度等搜索引擎搜到?

自从本站openGPS.cn增加博客板块,也有一段时间了,通过这段时间的学习积累,意识到自建网站发布博客,中间要经历的几件事情:1,百度搜索,360搜索,搜狗搜索等国内搜索引擎好久搜不到自己的网站。2,新写的文章,搜索引擎搜不到3,搜索引擎能搜到了,但是结果里显示的简介都是一样的这是本站首次发表一个SEO话题,遇到这些问题,都是取决于自己的操作不当。在接下来的话题之前,先普及一下公开的一种查询方法,那就是在搜索引擎直接搜 “site:www.opengps.cn”(不含引号)。这样就可以大致的看到该域名在当前搜索引擎下的收录量了首先回答问题1:百度搜索,360搜索,搜狗搜索等国内搜索引擎好久搜不到自己的网站对于新站,没有外部链接,没有主动提交。自己的网站就仿佛在“暗网”之中。因此第一步要解决的问题就是去各大搜索引擎的“站长平台”进行URL提交。列举几个国内常见搜索引擎的站长平台地址:百度站长平台:https://ziyuan.baidu.com/360站长平台:http://zhanzhang.so.com/搜狗站长平台:http://zhanzhang.sogou.com/通过验证网站,可以让搜索引擎知道这是你的网站,然后记得去“提交URL”,告诉搜索引擎,这里有个新的网站可以被搜索。最好是能够提供站点地图,来告诉搜索引擎都有哪些页面可以收录回答问题二:新写的文章,搜索引擎搜不到经过了问题一的解答,大家应该关注到,站长平台几乎都有个统一的模块:URL提交,这里要说的,就是需要手动提交每次的网址,或者看看这个站长是否提供自动推送代码。可以在文章页模板上增加这行代码,实现自动提交。回答下问题三:搜索引擎能搜到了,但是结果里显示的简介都是一样的还有一个情况就是收录的网页都是同一个标题,原因跟这个情况相同,都是代码处理不到位导致的。html标签里有几个针对搜索引擎的标签:title,keyword,description等等。这个问题是本站所遇到的。其实原因很简单,就是博客页面,在开发时候偷懒,没有 动态替换html标签的<description>标签。从而导致所有的文章只收录的不同的title,却收录了相同的description。现如今,虽然改了代码。但是似乎一时半会还没办法立刻更新“快照”原文地址: https://www.opengps.cn/Blog/View.aspx?id=243 文章的更新编辑依此链接为准。欢迎关注源站原创文章!

March 6, 2019 · 1 min · jiezi

Ghost配置6——首页太阳系动画效果

最近在逛知乎时,意外发现了一组CSS效果,其中一个太阳系运行的动画吸引了我。于是我决定把这个效果加到个人博客的首页头部中来。修改首页首页对应的文件是index.hbs,找到其中的header内容,并修改为:<header class=“site-header outer”> <div class=‘solar-syst’> <div class=‘sun’></div> <div class=‘mercury’></div> <div class=‘venus’></div> <div class=‘earth’></div> <div class=‘mars’></div> <div class=‘jupiter’></div> <div class=‘saturn’></div> <div class=‘uranus’></div> <div class=‘neptune’></div> <div class=‘pluto’></div> <div class=‘asteroids-belt’></div> </div> <div class=“inner”> {{> “site-nav”}} </div></header>编写CSScss代码在作者的codepen上有说明,注意选择编译后的css进行查看。我个人写了一个solar.css保存其内容。.solar-syst以上的css代码都可以删除,并且在该类中加入背景属性:.solar-syst { background: radial-gradient(ellipse at bottom, #1C2837 0%, #050608 100%); margin: 0 auto; width: 100%; height: 600px; position: relative;}…添加CSS部署css编辑好的solar.css文件,放置在ghost/content/themes/casper/assets/css下面。引入css修改default.hbs,在header中加入css引用。…<head> … <link rel=“stylesheet” type=“text/css” href="{{asset “css/solar.css”}}" /> …</head><body …>…</body>以上工作完成后,重启Ghost即可查看博客的新动画效果。

February 22, 2019 · 1 min · jiezi

目前可用的微博秀的嵌入方法大全(亲测2019年2月仍有效)

当今最全面可用的微博分享组件嵌入方法(亲测2019年2月仍有效)最近一直在找一种目前可用的微博分享组件的使用方法,发现有3个大坑:向网页嵌入微博秀时,需要的uid简单,但需要的verifier值有点难获取,原因在于原生成微博秀页面的 url 及其子链接均会被强制从http重定向跳转到https,此外其response中部分css引用失败导致页面无法完整显示;https页面是没法调用http下的js和css的;微博秀是需要用iframe来嵌入的,博客园默认不支持iframe标签,可通过构造字符串的方式添加iframe来解决,也可以直接用embed标签替换掉iframe。对于微博第5版(weibo v5),其相应的微博组件的网址为: https://open.weibo.com/widgets ,及其具体使用方法为: 微博秀-新浪微博JSSDK官方网站,而对于微博第4版(weibo v4),相应的微博组件的网址为: http://app.weibo.com/tool ,相比之下第5版的组件中丢失了第4版中很重要的"微博秀"组件.下面来介绍我解决向博客园中成功嵌入微博秀且在http/https下均能显示的方法:1.获取微博秀的参数uid和verifier使用Chrome打开微博登录页面 https://weibo.com, 然后打开微博秀页面 https://app.weibo.com/tool/we… ,接下来按F12,点击开发者工具导航栏中的Source。选择灰色的那个点开,就可以看到相应的html代码:然后另存为weiboshow.html放在本地,最后修改代码中光标处的https为http,接着使用Chrome浏览器打开本地的weiboshow.html,此时在左下角的框框中已出现uid和verifier。事实上不保存为本地的html文件也行,在第2张图对应的html代码中分别搜索"$uid", “$CONFIG.$checkKey”,取出=右边的值,即可知uid=‘2606405674’和verifier=‘d5cf5ffc’。2.对于第2个问题,为使得微博秀既能在http 和https形式(分别对应于https://www.cnblogs.com/enjoy233 和 http://www.cnblogs.com/enjoy2…,方法也很简单。将从网页左下角复制到的代码中的src=“http://” 改为src="//" 即可。3.解决问题3目前已知如下3种方法(以上述截图上微博的uid=2606405674&verifier=d5cf5ffc为例):a.复制左下角的代码,在其基础上 将iframe改为embed,删除 frameborder=“0”,贴进公告即可,相应代码为:<embed width=“100%” height=“550” class=“share_self” scrolling=“no” src="//widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=2&ptype=1&speed=0&skin=1&isTitle=1&noborder=1&isWeibo=1&isFans=1&uid=2606405674&verifier=d5cf5ffc&dpc=1"></embed>b.使用html5的另一个标签object.<object data="//widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=2&ptype=1&speed=0&skin=1&isTitle=1&noborder=1&isWeibo=1&isFans=1&uid=1763628267&verifier=a1171a80&dpc=1" width=“100%” height=“550” type=“text/html”> Embedded data failed to be displayed.</object>目前本人博客正是使用这种方法,在移动端也能正常显示c.使用JavaScript去动态拼接iframe,相应代码为:<div id=“weiboshow”><script type=“text/javascript”>var weibocode = ‘<if’weibocode += ‘rame width=“100%” height=“550” class=“share_self” frameborder=“0” scrolling=“no” src="//widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=2&ptype=1&speed=0&skin=1&isTitle=1&noborder=1&isWeibo=1&isFans=1&uid=2606405674&verifier=d5cf5ffc&dpc=1"></iframe>’;document.getElementById(‘weiboshow’).innerHTML = weibocode;</script></div>将其贴进公告即可。d.将微博v5的版本应用到微博秀上,直接使用微博官方提供的wb.js来解决,该方法微博v5的组件接口中"赞同"就是类似的(参看网页 https://open.weibo.com/widget… 末尾)。<html xmlns:wb="//open.weibo.com/wb"><script src="//tjs.sjs.sinajs.cn/open/api/js/wb.js" type=“text/javascript” charset=“utf-8”></script><wb:show uid=“2606405674” width=“850” verifier=“d5cf5ffc”></wb:show>最后一步还是将其贴进公告。亲测可知,后面这种官方推荐的方法在移动端也能正常显示,但iframe有些浏览器(比如: iPhone自带的Safari就不显示)不支持。ps: 点赞按钮的相应代码为:<html xmlns:wb="//open.weibo.com/wb"><script src="//tjs.sjs.sinajs.cn/open/api/js/wb.js" type=“text/javascript” charset=“utf-8”></script><div> <wb:follow-button uid=“1763628267” type=“red_3” width=“100%” height=“90”></wb:follow-button></div>对于上述几种方法,读者只需将uid=2606405674&verifier=d5cf5ffc换为自己微博的相应值即可。如果偶尔出现如下问题,是正常的,刷新一下就可以解决,原因是微博官方的API有时会出故障。好啦,此时所以的问题都解决了,希望对君有用。至于豆瓣秀就很简单了,打开豆瓣收藏秀 https://www.douban.com/servic…,将相应的js贴到公告中即可(同样需要src=“http://” 改为src="//")。关于微博API,今天还学到一招 - 微博未登陆时重定向提醒用户登录:https://passport.weibo.cn/sig…手机版passport.weibo.cn与PC版passport.weibo.com共用cookie喔原创不易,记得支持一下喔~<br/>本文首发于本人博客园博客:https://www.cnblogs.com/enjoy….

February 13, 2019 · 1 min · jiezi

前端小白也能快速学会的博客园博客美化全攻略

前端小白也能快速学会的博客园博客美化全攻略<font size=6>A呦V,博客园er的自我修养是什么?第一条,别只顾收藏和偷师呀,记得点"推荐"或关注本人喔~ </font>美化方法论简介一般而言,需要选一个默认的skin,然后在该基础上调整。官方介绍:博客皮肤模板 http://skintemplate.cnblogs.com/官方文档 - 【博客园skin开发文档 】:https://docs.qq.com/sheet/DZF…宽屏模版:SimpleMemoryMinyx2_LitelessIsMoreBlueSky博客园布局的组成及其对应关系(下方一图来自于网络):准备工作首先你得有个cnblogs博客申请js权限附该美化过程的github项目:yanglr/Beautify-cnblogs: Beautify-cnblogs欢迎fork或star本博客的所有代码在此github项目的src文件夹中源码使用步骤:打开 博客后台管理 → “设置”在博客皮肤选项卡中将博客皮肤设置为: LessIsMore将src文件夹下的页面定制.css 复制到 页面定制CSS代码 代码框内将同一文件夹下的 页首.html 复制到 页首Html代码 代码框内将同一文件夹下的 页尾.html 复制到 页脚Html代码 代码框内保存,即可见效。js权限申请登陆后依次点击“我的博客” → “管理” → “设置”,在下拉后找到“博客侧边栏公告”,后方有一个“申请js权限”。或者也可进博客园园子页面(https://home.cnblogs.com/feed…),发状态@博客园团队,申请开通js权限。也可发个邮件到contact@cnblogs.com申请js权限。申请时内容模板已为你备好:尊敬的博客园管理员:本人请求申请开通js权限,希望能够把博客修饰的漂亮点,点缀自定义js插件效果,希望管理员可以批准,多谢提交完申请,会弹出提示:JS权限申请已提交,待审核。剩下的就是耐心等待了,一般来说挺快就会通过。如果设置页面上公告栏标题右侧不存在“申请js权限”,说明已成功开通js权限。如何模仿一个博客园的自定义风格(样式css+动态效果js)?模仿一个cnblogs的全局css,只需打开Chrome浏览器,按下F12,找里面的skin css和custom css,例如:<link id=“MainCss” type=“text/css” rel=“stylesheet” href="/skins/LessIsMore/bundle-LessIsMore.css?…"><link type=“text/css” rel=“stylesheet” href="/blog/customcss/198996.css?v=…">补充完整前缀:http://www.cnblogs.com,使用ref将该两个css引用到自己的博客中,即可进行大概样子的模仿,其他部分需要细调。markdown样式自定义默认markdown状态下,代码中的字比较小。/* 文章标题样式(这个不是markdown里的标题) /#topics .postTitle a { / color: #169fe6; / font-family: Georgia,Times New Roman,Times,sans-serif, monospace; font-weight: bold;} / 普通文字样式 /#cnblogs_post_body p { margin: 18px auto; color: #000; font-family: Georgia,Times New Roman,Times,sans-serif, monospace; font-size: 16px; text-indent: 0;} / 标题样式 /#cnblogs_post_body h1 { font-family: Georgia,Times New Roman,Times,sans-serif, monospace; font-size: 32px; font-weight: bold; line-height: 1.5; margin: 10px 0;}#cnblogs_post_body h2 { font-family: Consolas, “Microsoft YaHei”, monospace; font-size: 26px; font-weight: bold; line-height: 1.5; margin: 20px 0;}#cnblogs_post_body h3 { font-family: Georgia,Times New Roman,Times,sans-serif, monospace; font-size: 20px; font-weight: bold; line-height: 1.5; margin: 10px 0;}#cnblogs_post_body h4 { font-family: Georgia,Times New Roman,Times,sans-serif, monospace; font-size: 18px; font-weight: bold; margin: 10px 0;}/ 标题样式设置结束 / / 去除双下划线斜体样式 /em { font-style: normal; color: #000;} / 无序列表 /#cnblogs_post_body ul li { font-family: Georgia,Times New Roman,Times,sans-serif, monospace; color: #000; font-size: 16px; list-style-type: disc;} / 有序列表 /#cnblogs_post_body ol li { font-family: Georgia,Times New Roman,Times,sans-serif, monospace; color: #000; font-size: 16px; list-style-type: decimal;} / 超链接 /#cnblogs_post_body a:link { text-decoration: none; color: #002C99;} / 引用背景 /#topics .postBody blockquote { background: #fff3d4; border: none; border-left: 5px solid #f6b73c; margin: 0; padding-left: 10px;} / 单行代码 /.cnblogs-markdown code { font-family: Consolas, “Microsoft YaHei”, monospace !important; font-size: 16px !important; line-height: 20px; background-color: #f5f5f5 !important; border: 1px solid #ccc !important; padding: 0 5px !important; border-radius: 3px !important; line-height: 1.8; margin: 1px 5px; vertical-align: middle; display: inline-block;} / 多行代码, 引用 */.cnblogs-markdown .hljs { font-family: Consolas, “Microsoft YaHei”, monospace !important; font-size: 16px !important; line-height: 1.5 !important; padding: 5px !important;}如果希望使用Sublime那样的主题,可参考:如何自定义博客园代码高亮主题,同时分享自己使用的黑色主题 - 我是小茗同学 - 博客园 .在页面顶部添加"自定义搜索"功能css部分:<style type=“text/css”> #auto_div { display: none; width: 257px; border: 1px #74c0f9 solid; background: #FFF; position: absolute; top: 24px; left: 0; margin-top: 15px; color: #323232; /设置显示在当前页面的上一层/ z-index: 1; } .side_search { float: left; position: relative; height: 31px; margin-left: 25px; display: inline-block; } .side_search:hover { -webkit-box-shadow: 0 0 3px #999; -moz-box-shadow: 0 0 3px #999 } .search_input { width: 210px; vertical-align: middle; height: 30px; line-height: 30px; border: 1px solid #999; border-radius: 2px 0 0 2px; padding: 4px 7px; background-color: #fbfbfb; } .delete_btn { background: #fbfbfb; margin-left: -6px; border: 1px solid #fbfbfb; border-radius: 0 3px 3px 0; cursor: pointer; display: inline-block; vertical-align: middle; color: red; font-weight: bold; width: 38px; font-size: 25px; height: 38px; padding-bottom: inherit; } .search_btn { border-radius: 3px 3px 3px 3px; background: #4d90fe; margin-left: -7px; border: 1px solid #4d90fe; cursor: pointer; display: inline-block; vertical-align: middle; color: #f3f7fc; font-weight: bold; width: 100px; font-size: 18px; height: 41px; } .search_btn:hover { background: #1874CD } .search_btn2 { border-radius: 3px 3px 3px 3px; background: #F0CB85; border: 1px solid #F0CB85; cursor: pointer; display: inline-block; vertical-align: middle; color: #DC143C; font-weight: bold; width: 100px; font-size: 18px; height: 41px; } .search_btn2:hover { background: #DEB887 } input, button, textarea, select, optgroup, option { font-family: inherit; font-size: inherit; font-style: inherit; font-weight: inherit; }</style>js部分:<script src=“https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js" type=“text/javascript”></script><script type=“text/javascript”> var availableTags = [ “C#”, “C++”, “算法”, “ASP”, “MySQL”, “Oracle”, “HTML”, “CSS”, “JavaScript”, “jQuery”, “AJAX”, “微软系列技术”, “Sublime”, “Git”, “Visual Studio Code”, “Eclipse”, “C#开发”, “C++开发”, “Java开发”, “PHP开发”, “Python开发”, “Web前端开发”, “Windows Forms”, “WPF”, “计算机数学”, “浏览器插件”, “软件推荐”, “算法实践”, “专业学习”, “Leetcode”, “知乎”, “Bravo Yeung”, “legege007”, “enjoy233”, “数学”, “内容太长,显示其中的一部分” ]; var old_value = “”; var highlightindex = -1; //高亮 //自动完成 function AutoComplete(auto, search, mylist) { if ($(”#" + search).val() != old_value || old_value == “”) { var autoNode = $("#" + auto); //缓存对象(弹出框) var carlist = new Array(); var n = 0; old_value = $("#" + search).val(); for (i in mylist) { if (mylist[i].indexOf(old_value) >= 0) { carlist[n++] = mylist[i]; } } if (carlist.length == 0) { autoNode.hide(); return; } autoNode.empty(); //清空上次的记录 for (i in carlist) { var wordNode = carlist[i]; //弹出框里的每一条内容 var newDivNode = $("<div>").attr(“id”, i); //设置每个节点的id值 newDivNode.attr(“style”, “font:14px/25px arial;height:25px;padding:0 8px;cursor: pointer;”); newDivNode.html(wordNode).appendTo(autoNode); //追加到弹出框 //鼠标移入高亮,移开不高亮 newDivNode.mouseover(function () { if (highlightindex != -1) { //原来高亮的节点要取消高亮(是-1就不需要了) autoNode.children(“div”).eq(highlightindex).css(“background-color”, “white”); } //记录新的高亮节点索引 highlightindex = $(this).attr(“id”); $(this).css(“background-color”, “#ebebeb”); }); newDivNode.mouseout(function () { $(this).css(“background-color”, “white”); }); //鼠标点击文字上屏 newDivNode.click(function () { //取出高亮节点的文本内容 var comText = autoNode.hide().children(“div”).eq(highlightindex).text(); highlightindex = -1; //文本框中的内容变成高亮节点的内容 $("#" + search).val(comText); }) if (carlist.length > 0) { //如果返回值有内容就显示出来 autoNode.show(); } else { //服务器端无内容返回 那么隐藏弹出框 autoNode.hide(); //弹出框隐藏的同时,高亮节点索引值也变成-1 highlightindex = -1; } } } //点击页面隐藏自动补全提示框 document.onclick = function (e) { var e = e ? e : window.event; var tar = e.srcElement || e.target; if (tar.id != search) { if ($("#" + auto).is(":visible")) { $("#" + auto).css(“display”, “none”) } } } } $(function () { old_value = $("#zzk_q").val(); $("#zzk_q").keyup(function () { AutoComplete(“auto_div”, “zzk_q”, availableTags); }); }); function quickdelete() { document.getElementById(“zzk_q”).value = “”; } function zzk_go() { var n = encodeURIComponent(document.getElementById(“zzk_q”).value); window.location = “http://zzk.cnblogs.com/s?w=blog%3AEnjoy233+" + n + “&t=” } function zzk_go2() { var n = encodeURIComponent(document.getElementById(“zzk_q”).value); window.location = “http://zzk.cnblogs.com/s?w=" + n } function zzk_go_enter(n) { if (n.keyCode == 13) return zzk_go(), !1 }</script>如果需要修改自动完成的下拉选项,修改变量availableTags的值即可见效。html部分:<h1 style=“margin-left: 570px;padding-top:15px;padding-bottom: 15px; color: #337ab7;font-size: 3em;font-weight: bold;font-style: italic;text-shadow: 1px 0px 0px #1E90FF;"> </h1><div class=“side_search”> <input type=“text” id=“zzk_q” class=“search_input” onkeydown=“return zzk_go_enter(event);” placeholder=“输入 回车搜索” tabindex=“1” autofocus x-webkit-speech> <div id=“auto_div”> </div>&nbsp; <input onclick=“quickdelete()” type=“button” class=“delete_btn” value="×” title=“清空”> <input onclick=“zzk_go()” type=“button” class=“search_btn” value=“本博搜索”> <input onclick=“zzk_go2()” type=“button” class=“search_btn2” value=“园内搜索”></div>效果图:在页面顶部添加"音乐播放器”(Flash)播放背景音乐先登录网易云音乐网页版,搜索到想要的音乐,然后点击"生成外链播放器"即可得到相应的html代码。表现形式一:单曲播放 (type = 1)<embed src=“http://music.163.com/outchain/player?type=0&amp;id=26237342&amp;auto=0&amp;height=430" width=“100%” height=“450” frameborder=“no” marginwidth=“0” marginheight=“0”></embed> 或<embed src=“https://music.163.com/style/swf/widget.swf?sid=26237342&type=2&auto=0&width=320&height=66" width=“340” height=“86” allowNetworking=“all” ></embed>参数说明:播放器可修改参数:width=100% #自适应宽度, 本博客使用了固定宽度320height=66 #根据自己需要来改sid=26237342 # 此数字是歌曲的ID http://music.163.com/#/song?i...auto=0 # 0表示不自动播放,1表示自动播放表现形式二:列表播放 (type = 0)<embed src=“https://music.163.com/outchain/player?type=0&amp;id=78413764&amp;auto=0&amp;height=430" width=“100%” height=“450” frameborder=“no” marginwidth=“0” marginheight=“0”></embed> 当然该url中的https:也可删掉。参数说明播放器可修改参数:width=100% # 自适应宽度height=450 # 根据自己的需要修改id=34238509 # 此数字是歌曲列表页的ID, 例如:http://music.163.com/#/playli…auto=0 # 0表示不自动播放,1表示自动播放将该代码贴进页首html即可见效(如果代码中含有iframe,需替换成embed)<div style=“display: table; text-align: center; width: 40%; height: 100%;"><embed src=“https://music.163.com/style/swf/widget.swf?sid=26237342&type=2&auto=0&width=320&height=66" width=“340” height=“86” allowNetworking=“all” ></embed></div>效果图: 在页面顶部添加"Fork me on Github"图标页首html需要添加<div><a href=“http://github.com/yanglr" target="_blank” style=“position: absolute;right: 0; top: 0; z-index: 100;"> <img src=“http://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_git-right-link.gif" alt=“Fork me on github”></a></div>效果见本页面右上角。如果想对该图标进行更多颜色或位置的设置,请参考:GitHub Ribbons - The GitHub Blog.为导航栏设置渐变背景色/* 头部 /#header { position: relative; height: 280px; margin: 0; background: #020031; background: -moz-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -webkit-gradient(linear,left bottom,right top,color-stop(0%,#020031),color-stop(100%,#6d3353)); background: -webkit-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -o-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -ms-linear-gradient(45deg,#020031 0,#6d3353 100%); background: linear-gradient(45deg,#020031 0,#6d3353 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=’#020031’, endColorstr=’#6d3353’, GradientType=1); -webkit-box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2); -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2); box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2);}在公告栏添加滚动文字使用marquee标签即能实现文字的滚动<marquee><a href=”#"><font color=“blue” size=“4”>You will make it!</marquee>效果图:在公告栏加入自己的社交网络账号 - 图片链接可以将自己不同社交网络的账号放在同一个span中,然后嵌入到div里,代码如下: <div nowrap align=center> <img src=“http://images.cnblogs.com/cnblogs_com/enjoy233/1389971/o_gzh.png" width=150 height=150> </div><br><div align=“center”> <a href=“http://stackoverflow.com/users/6075331/bravo-young" target="_blank” class=“mr5 “><strong>StackOverflow</strong></a><font style=“color:#BF7158”>(</font> <img src=“http://images.cnblogs.com/cnblogs_com/enjoy233/1389971/o_reputation-rp.png" class=“mr5”> <span style=“color:#BF7158” class=“mr10”>406 <font size=4 color=black>⬆</font>)</span></div><br><div class=“c-social” align=“center”><span> <a href=“https://github.com/yanglr" target="_blank”> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_github.png" class=“mr5” width = 40 height=40> </a> <a href=“http://blog.csdn.net/lzuacm" target="_blank”> <img src=“http://images.cnblogs.com/cnblogs_com/enjoy233/1389971/o_csdn.png" class=“mr5” width = 40 height=40> </a> <a href=“https://www.zhihu.com/people/legege007" target="_blank”> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_zhihu.png" class=“mr5” width = 40 height=40> </a> <a href=“http://weibo.com/546671991" target="_blank”> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_weibo.png" class=“mr5” width = 40 height=40> </a></span>效果图:对于知乎,可以这样写更详细些:<div valign=“middle” align=“left” bgcolor=’#F6F8FA’> <span> <a href=“https://www.zhihu.com/people/legege007" title=“知乎撩我” target="_blank”> <img src=“http://images.cnblogs.com/cnblogs_com/enjoy233/1389971/o_zhihu-dog.jpg" height=‘22’> </a> </span> <span valign=‘middle’> <font>(</font> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_vote.png" width=‘14’ height=‘14’ title=“赞同”> 42.8k <font color=black></font> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_fav.png" width=‘14’ height=‘14’ title=“收藏”> 90.7k <font color=black></font> <img src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_thank.png" width=‘14’ height=‘14’ title=“感谢”> 21.7k <font color=black></font>) </span></div>而对于stackoverflow,官方在StackExchange页面提供了flair,比如我的是: User Bravo Yeung - Stack Exchange ,页面中提供了可嵌入的html代码:<a href=“https://stackexchange.com/users/4637854/bravo-yeung"><img src=“https://stackexchange.com/users/flair/4637854.png" width=“208” height=“58” alt=“profile for Bravo Yeung on Stack Exchange, a network of free, community-driven Q&amp;A sites” title=“profile for Bravo Yeung on Stack Exchange, a network of free, community-driven Q&amp;A sites” /></a>将其用div包起来,放进公告.html即可见效。在公告栏添加一个能旋转的rss图标先将相应的css放入页面定制css或公告栏的css中,然后在后面使用。#feed_icon { border: #000 solid 2px; display: block; margin: 50px auto; border-radius: 50%; transition: all 2.0s;}#feed_icon:hover { transform: rotate(360deg);}然后将如下代码贴进公告中~<div id=“feed”> <a href=“https://www.cnblogs.com/enjoy233/rss" title=“订阅Feed” target="_blank”> <img id=“feed_icon” src=“https://www.cnblogs.com/images/cnblogs_com/enjoy233/1389971/o_rss.png" alt=”” style=“border: 0pt none;” width = 60 height=60> </a></div>效果图:在公共栏添加"人体时钟”(Flash)此人体时钟的原作者: 日本flash达人 Chabudai,初发表于2008年9月(2008-09-23)。只需在公告中贴入如下代码:<embed wmode=“transparent” src=“https://files.cnblogs.com/files/enjoy233/honehone_clock_tr.swf" quality=“high” bgcolor="#FDF6E3” width=“200” height=“120” name=“honehoneclock” align=“middle” allowscriptaccess=“always"type=“application/x-shockwave-flash” pluginspage=“http://www.macromedia.com/go/getflashplayer">效果图: 参考:人体造型时钟hone clock设置、博客侧栏代码 - 曹杰峰的博客ps: iOS移动端用默认浏览器打开看不到效果很正常,这个是flash做的,苹果的产品基本上都不直接支持flash了,iOS上安装一个Chrome浏览器倒是可以正常打开的~在公共栏添加"站点统计"功能打开网站:http://www.flagcounter.com/ ,无需注册,点击黄色按钮"Get Your Flag Counter”,即可生成嵌入该插件的html代码。生成的html代码如下:<div><a href=“https://info.flagcounter.com/LCgi"><img src=“https://s04.flagcounter.com/count2/LCgi/bg_FFFFFF/txt_000000/border_CCCCCC/columns_2/maxflags_10/viewers_0/labels_1/pageviews_1/flags_0/percent_0/" alt=“Flag Counter” border=“0”></a><div>最后将该代码加入到公告栏的html代码中即能生效。在公告栏中加入"自定义搜索”(PopUp弹窗)实现的基本原理:onclick = “window.open()”, target = popUpWindow此功能的特色在于用户搜索之后,搜索结果页面并不会影响到原博客页面,而是在弹出的独立窗口显示,而且很容易扩展出很多其他站点的搜索功能。在公告html中贴入如下代码:<div id=“sidebar_search_new” class=“mySearch”><h3 class=“catListTitle”>自定义搜索(PopUp窗口)</h3> <input type=“text” id=“zzk_q1” class=“input_my_zzk_new” placeholder=“Bravo Yeung”> <span> <input type=“button” class=“btn_my_zzk” value=“本博搜索” onclick=“window.open(‘https://zzk.cnblogs.com/my/s/blogpost-p?Keywords=' + document.getElementById(‘zzk_q1’).value,‘popUpWindow’,‘height=750,width=1000,left=10,top=10,scrollbars=yes,menubar=no’); return false;” />&emsp;<input type=“button” class=“btn_my_zzk” value=“站内搜索” onclick=“window.open(‘https://zzk.cnblogs.com/s/blogpost?w=' + document.getElementById(‘zzk_q1’).value,‘popUpWindow’,‘height=750,width=1000,left=10,top=10,scrollbars=yes,menubar=no’); return false;” /></div><span><span> <input type=“button” class=“btn_my_zzk” value=“知乎搜索” onclick=“window.open(‘https://www.zhihu.com/search?type=content&q=' + document.getElementById(‘zzk_q1’).value,‘popUpWindow’,‘height=750,width=1000,left=10,top=10,scrollbars=yes,menubar=no’); return false;” />&emsp;<input type=“button” class=“btn_my_zzk” value=“CSDN搜索” onclick=“window.open(‘https://so.csdn.net/so/search/s.do?t=blog&u=yanglr2010&q=' + document.getElementById(‘zzk_q1’).value,‘popUpWindow’,‘height=750,width=1000,left=10,top=10,scrollbars=yes,menubar=no’); return false;” /></div>效果图:参考:Javascript - Open a given URL in a new tab by clicking a button - Stack OverflowJavaScript Popup Windows Javascript window.open, also fullscreen and centered popup window « JavaScript DHTML Tutorials页面底部添加"回到顶部” + “收藏” + “快速评论"功能html部分:<div class=“scrollBtn” id=“scrollBtn”> <ul class=“clearfix”> <li class=“sB-home”> <a href=“http://www.cnblogs.com/enjoy233" class=“ff-t” title=“首页”></a> </li> <li class=“sB-comment”> <a href="#blog-comments-placeholder” onclick="$(’#tbCommentBody’).focus();” class=“ff-t” title=“添加评论”></a> </li> <li class=“sB-share”><a onclick=“if(cb_entryId !=undefined){AddToWz(cb_entryId)}” href=“javascript:void(0);” title=“收藏”></a></li> <li class=“sB-goTop” id=“goTop” style=“display: list-item;"> <a href="#top” title=“回顶部”></a> </li> </ul></div>效果图:“自动移动的目录"功能页脚html引入css文件nav.my.css和nav.my.js。<link href=”//blog-static.cnblogs.com/files/enjoy233/nav.my.css” rel=“stylesheet”><script type=“text/javascript” src=”//files.cnblogs.com/files/enjoy233/nav.my.js”></script>然后将下方代码贴进页脚html.JS部分:<script language=“javascript” type=“text/javascript”>//生成目录索引列表function GenerateContentList(){ var jquery_h3_list = $(’#cnblogs_post_body h4’);//如果你的章节标题不是h4,只需要将这里的h4换掉即可 if(jquery_h3_list.length>0) { var content = ‘<a name="_labelTop”></a>’; content += ‘<div id=“navCategory”>’; content += ‘<p style=“font-size:18px”><b>阅读目录</b></p>’; content += ‘<ul>’; for(var i =0;i<jquery_h3_list.length;i++) { var go_to_top = ‘<div style=“text-align: right”><a href=”#_labelTop”>回到顶部</a><a name="_label’ + i + ‘"></a></div>’; $(jquery_h3_list[i]).before(go_to_top); var li_content = ‘<li><a href=”#_label’ + i + ‘">’ + $(jquery_h3_list[i]).text() + ‘</a></li>’; content += li_content; } content += ‘</ul>’; content += ‘</div>’; if($(’#cnblogs_post_body’).length != 0 ) { $($(’#cnblogs_post_body’)[0]).prepend(content); } } }GenerateContentList();</script>效果图: 改进评论的显示样式这里我索性改成了熟悉的微信聊天的样式。纯css实现: .blog_comment_body { background: #B2E866; float: left; border-radius: 5px; position: relative; overflow: visible; margin-left: 33px; max-width: 700px; } .feedbackListSubtitle a.layer { background: #B2E866; color: #414141 !important; padding: 2px 4px; border-radius: 2px; }将上面的代码贴紧页面css文本框即可见效果。效果图:在公告栏添加"友情链接"cnblogs博客后台提供了"链接"功能,这个就是用来添加友情链接的。设置方法(见下图):编辑分类 -> 添加链接,设置好后公告栏上会显示相关内容,不过可能会有延时,需要等一会。效果图:“博客签名"功能虽然cnblogs博客后台提供了"博客签名"功能,测试发现该该方法实现的博客签名在IE中打开时不显示,只好改为用跨浏览器的JQuery来实现了。<script type=“text/javascript”>$(document).ready(function(){ $("#cnblogs_post_body”).append(’<br/><hr/><div style=“border: 2px dotted #4d90fe; padding: 2px; background-color: lightgray”> 作者:<a href=“http://www.cnblogs.com/enjoy233/" target="_blank” title=“Enjoy233的博客” style=“color: brown”>Bravo Yeung</a><br> 出处:<a href=“http://www.cnblogs.com/Enjoy233/" target="_blank” title=“http://www.cnblogs.com/Enjoy233/" style=“color: blue”>http://www.cnblogs.com/Enjoy233/</a> <br>如果您觉得阅读本文对您有帮助,请点击一下右下方的<b style=“color: blue; font-size: 16px”>推荐</b>按钮,您的<b style=“color: blue; font-size: 16px”>推荐</b>将是我写作的最大动力! <br>版权声明:本文为博主原创或转载文章,欢迎转载,<b>但转载文章之后必须在文章页面明显位置注明出处</b>,否则保留追究法律责任的权利。</div>’);});</script>禁用页面的"选中复制"功能在css中进行相应的设置,只需将下方代码贴入"页面css"文本框即可。/ 禁止页面,选中 复制 /html,body { moz-user-select: -moz-none; -moz-user-select: none; -o-user-select: none; -khtml-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none;}综合考虑后,这种处理方式并不太友好,于是采用了后文中的"复制博客内容时自动加版权说明”。不显示底部广告在css中进行相应的设置,只需将下方代码贴入"页面css"文本框即可。#ad_t2,#cnblogs_c1,#under_post_news,#cnblogs_c2,#under_post_kb { display:none; !important}修改导航栏(修改部分链接的文字 + 增加下拉菜单)css部分:/ 定制自己导航栏的样式 /#shwtop ul { margin: 0; padding: 0; list-style-type: none; /去除li前的标注/ background-color: #333; overflow: hidden; /隐藏溢出的部分,保持一行/}#shwtop li { float: left; /左浮动/}#shwtop li a, .dropbtn { display: inline-block; /设置成块/ color: white; text-align: center; text-decoration: none; padding: 14px 16px;}/鼠标移上去,改变背景颜色/#shwtop li a:hover, .dropdown:hover .dropbtn { / 当然颜色你可以自己改成自己喜欢的,我还是挺喜欢蓝色的 / background-color: blue;}#shwtop .dropdown { / display:inline-block将对象呈递为内联对象, 但是对象的内容作为块对象呈递。 旁边的内联对象会被呈递在同一行内,允许空格。 */ display: inline-block;}#shwtop .dropdown-content { display: none; position: absolute; background-color: #f9f9f9; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);}#shwtop .dropdown-content a { display: block; color: black; padding: 8px 10px; text-decoration:none;}#shwtop .dropdown-content a:hover { background-color: #a1a1a1;}#shwtop .dropdown:hover .dropdown-content{ display: block;}页脚html部分:<!– 更新导航栏的菜单–><script> $(function(){ $("#navigator”).append(’<div id=“shwtop” > <ul style=“margin-left:5px;margin-right: 5px;"> <div class=“dropdown”> <a href=”#” class=“dropbtn”>后台管理</a> <div class=“dropdown-content”> <!– <a class=“menu” href=“这里是你文章或随笔分类的链接地址,自己修改下面同理”> 这里更改下拉具体内容 </a> –> <a class=“menu” href=“http://i.cnblogs.com/Configure.aspx" target="_blank”>GUI配置</a> <a class=“menu” href=“http://i.cnblogs.com/posts" target="_blank”>博文列表</a> <a class=“menu” href=“http://wz.cnblogs.com/" target="_blank”>收藏</a> <a class=“menu” href=“http://i.cnblogs.com/Files.aspx" target="_blank”>文件</a> <a class=“menu” href=“https://i.cnblogs.com/EditGalleries.aspx" target="_blank”>相册</a> </div> </div> </ul></div>’); $("#navList”).append(’<li id=“nav_home”><a id=“enjoy233” rel=“nofollow” href=“https://ing.cnblogs.com/" target="_blank” title=“进入我的闪存”>闪存</a></li>’); $("#navList”).append(’<li id=“nav_follow”><a id=“enjoy233” rel=“nofollow” href=“http://home.cnblogs.com/followees/" target="_blank” title=“进入我的关注”>我关注</a></li>’); $(’#navList’)[0].children[“nav_contact”].innerHTML=’<a id=“nav_contact” class=“menu” rel=“nofollow” href=“https://msg.cnblogs.com/send/Enjoy233">私信</a>’; $(’#navList’)[0].children[“nav_rss”].innerHTML=’<a id=“nav_rss” class=“menu” rel=“nofollow” href=“https://www.cnblogs.com/enjoy233/rss">RSS订阅</a>’; //加载图片 var ponum1 = $(".postTitle”).length; var ponum2 = $(".entrylistPosttitle”).length; if(ponum1!=0) articleimg(ponum1); if(ponum2!=0) entrylistarticleimg(ponum2); });</script>效果图:如需调整请自行修改微博秀的嵌入(支持http/https访问)参看本人的另一篇文章 当今最全面可用的微博分享组件嵌入方法(亲测2019年2月仍有效) - Enjoy233 即可。效果图(见本博客左侧公告栏):分享组件的嵌入(支持http/https访问)由于官方的 Baidu Share 的ssl证书已过期,只好找到一个替代镜像 //static.dmzj.com/baidushare/static/js/shell_v2.js,使得通过https访问或http访问本博客都可以看到左下角的分享按钮在页脚.html中加入如下代码:<!– Baidu Share BEGIN –><div id=“bdshare” class=“bdshare_t bds_tools get-codes-bdshare”> <span class=“bds_more”>分享到:</span> <a href=”#” class=“bds_tsina” data-cmd=“tsina” title=“分享到新浪微博”></a><a href=”#” class=“bds_qzone” data-cmd=“qzone” title=“分享到QQ空间”></a><a href=”#” class=“bds_sqq” data-cmd=“sqq” title=“分享到QQ好友”></a><a href=”#” class=“bds_douban” data-cmd=“douban” title=“分享到豆瓣网”></a><a href=”#" class=“bds_youdao” data-cmd=“youdao” title=“分享到有道云笔记”></a><a href="#" class=“bds_renren” data-cmd=“renren” title=“分享到人人网”></a><a href="#" class=“bds_kaixin001” data-cmd=“kaixin001” title=“分享到开心网”></a><a href="#" class=“bds_mail” data-cmd=“mail” title=“分享到邮件分享”></a></div> <script type=“text/javascript” id=“bdshare_js” data=“type=tools&amp;uid=2883522” ></script> <script type=“text/javascript” id=“bdshell_js”></script> <script type=“text/javascript”> document.getElementById(“bdshell_js”).src = “//static.dmzj.com/baidushare/static/js/shell_v2.js?cdnversion=” + Math.ceil(new Date()/3600000) </script> <!– Baidu Share END –>官方demo:分享按钮-百度分享 (获取代码 -> 按向导操作,可查看网页侧边的动态使用效果,也可看到相应代码。)效果图:打赏功能本博客基于开源插件 tctip v1.0.3 来实现在页脚.html中插入如下代码即可:<!– tctip 支付赞赏/打赏 –><script type=“text/javascript” src="//files.cnblogs.com/files/enjoy233/tctip-1.0.3.min.js"></script> <!– js文件引入 –><script> new tctip({ top: ‘20%’, button: { id: 1, type: ‘zanzhu’, }, list: [ { type: ‘alipay’, qrImg: ‘https://files.cnblogs.com/files/enjoy233/Reward_Alipay_Charge.bmp’ //替换成自己的支付宝付款码 }, { type: ‘wechat’, qrImg: ‘https://files.cnblogs.com/files/enjoy233/Reward_WX_Charge.bmp’ //替换成自己的微信付款码 } ] }).init()</script>效果图:还看到过一个不错的方法,亲测有效:自制简易打赏功能 - EritPang .复制正文文字时自动加版权确保页面能成功引入JQuery.js后在页首html中加入如下代码:<script language=“javascript” type=“text/javascript”>jQuery(document).on(‘copy’, function(e){ var selected = window.getSelection(); var copyFooter = ‘<br>———————<br>著作权归作者所有。<br>’ + ‘商业转载请联系作者获得授权,非商业转载请注明出处。<br>’ + ‘作者:Bravo Yeung<br> 源地址:’ + document.location.href + ‘<br>来源:博客园cnblogs<br>© 版权声明:本文为博主原创文章,转载请附上博文链接!’; var copyHolder = $(’<div>’, {html: selected + copyFooter, style: {position: ‘absolute’, left: ‘-99999px’}}); $(‘body’).append(copyHolder); selected.selectAllChildren( copyHolder[0] ); window.setTimeout(function() { copyHolder.remove(); },0);});</script>当用户复制文中内容(ctrl+C或鼠标右击复制)时,会自动加上版权文字,csdn的代码复制功能以及知乎的内容复制都有此功能。效果图:对正文中的图片设置悬停放大不少平台有个关于图片的功能:当鼠标悬停在图片上时,图片会被放大。本人直接使用css来做了这件事情读者只需将下方代码贴进页面css即可~.post img { cursor: pointer; transition: all 0.5s;}.post img:hover { transform: scale(1.3);}隐藏博文右下角的"反对"按钮只需在页面css的文本框中加入如下代码:.buryit { display: none;}.comment_bury { display: none;}效果请见页面右下角。本文首发于本人的博客园: https://www.cnblogs.com/enjoy…. ...

February 12, 2019 · 9 min · jiezi

在个人服务器利用hexo搭建博客

准备1、 一台主机 我的是阿里云(CentOS系统)2、 node.js可以参照我这篇文章搭建环境https://segmentfault.com/a/11…3、 git 安装后查看版本正常输出代表安装成功$ sudo yum install git-core$ git –versiongit version 1.8.3.1Hexo 安装$ npm install -g hexo-cli$ hexo versionhexo-cli: 1.1.0os: Linux 3.10.0-693.2.2.el7.x86_64 linux x64http_parser: 2.7.0node: 9.3.0v8: 6.2.414.46-node.15uv: 1.18.0zlib: 1.2.11ares: 1.13.0modules: 59nghttp2: 1.25.0openssl: 1.0.2nicu: 60.1unicode: 10.0cldr: 32.0tz: 2017c新建博客1、 建立文件夹$ hexo init blog2、安装$ cd blog$ npm install3、 生成静态页面$ hexo g4、 启动hexo博客$ hexo s更换hexo主题1、 下载next主题$ cd /home/blog/theme$ git clone https://github.com/iissnan/hexo-theme-next themes/next2、 配置hexo主题(_config.yml)主要是配置theme选项,其他配置可以自行参考hexo官网theme: hexo-theme-next更换主题后重新生成静态文件然后启动$ hexo g$ hexo s

January 12, 2019 · 1 min · jiezi

使用 Gatsby.js 搭建静态博客 5 博客上线

这真的是最简单的一步啦~使用 netlify deploy 你的网站netlify 是一个可以帮助你自动部署网站的平台。你可以选择自己买服务器,运行 build 然后推送到自己服务器(或者直接使用 gh-pages),但是使用 netlify 你只需要正常推送项目到 GitHub,netlify 就会自动给你 deploy,这项服务是免费的。推荐直接用 GitHub 登陆netlify 获取权限设置指令都确定以后其实系统已经开始自动 build 你的项目了。你可以在 deploy 页面查看 build 进度。详细设置可以在 settings 查看,可以进行构建、环境变量、hook 等相关配置。除了 deploy 功能外,netlify 还有很多其他服务,例如可以为你提供登陆系统,或是表单服务。域名购买很多人推荐在 GoDaddy 购买域名,但是,其实你直接看到售价是很便宜,不过续费就完全比其他网站都贵了。稍微查了一下发现 namesilo 口碑貌似不错,最重要的是便宜,虽然这家网站的样式真的很老 ????netlify 自定义域名设置因为我已经加了域名所以回不去了,细节步骤记得不是很清楚…简单来说,点击那个大大的 Set up a custom domain,在 netlify 提交你已经购买的域名,然后 netlify 还提供了 DNS 服务,按指引操作即可。关于 DNS本来,你要访问一个网页是必须知道网页的 IP 地址,但是数字不好记,所以出现了域名。但是域名怎么跟 IP 关联呢?DNS 就像一个电话本,你可以拿着网站的域名问 DNS:这个网站的 IP 是什么?他告诉你了,便能访问了。(不负责任推测)印象中很久很久之前,改 DNS 可以访问谷歌,大概是因为那时候的墙只是破坏了域名服务这一步吧?所以,你拥有域名了,就能在 DNS 把你的域名“关联”到某个 IP,结果,访问你的域名,就能如愿访问你的服务器了。关于 TTLTTL(Time to live)是你修改域名服务器之后旧数据保留的时间,namesilo 的最低 TTL 也得是 3600,所以不要着急,等一小时左右就 OK 了。生成 sitemap有了自己的网站,你一定会想向谷歌等搜索引擎提交网址,但是总不能一条条手输吧?这个时候就需要用到 sitemap 了。Gatsby 当然是有生成 sitemap 的插件的——gatsby-plugin-sitemap安装,然后在配置文件加上即可,sitemap 会输出在根目录。// In your gatsby-config.jssiteMetadata: { siteUrl: https://www.example.com,},plugins: [gatsby-plugin-sitemap]下一步评论系统的实现,方法多样,任君选择。 ...

January 2, 2019 · 1 min · jiezi

使用 Gatsby.js 搭建静态博客 4 标签系统

原文链接:https://ssshooter.com/2018-12…回顾:使用 Gatsby.js 搭建静态博客 3 样式调整官方自带标签系统教程,英语过关可以直接阅读官方教程。以下说一下重点:提示:以下所有查询都可以在 localhost:8000/___graphql 测试建立标签系统只需要以下步骤:在 md 文件添加 tags—title: “A Trip To the Zoo"tags: [“animals”, “Chicago”, “zoos”]—用 graphQL 查询文章标签{ allMarkdownRemark( sort: { order: DESC, fields: [frontmatter___date] } limit: 1000 ) { edges { node { frontmatter { path tags // 也就添加了这部分 } } } }}制作标签页面模板(/tags/{tag})标签页面结构不难,与之前的文章页面差不多,区别在于标签的查询:// 注意 filterexport const pageQuery = graphql query($tag: String) { allMarkdownRemark( limit: 2000 sort: { fields: [frontmatter___date], order: DESC } filter: { frontmatter: { tags: { in: [$tag] } } } ) { totalCount edges { node { frontmatter { title path } } } } }修改 gatsby-node.js,渲染标签页模板const path = require(“path”)const _ = require(“lodash”)exports.createPages = ({ actions, graphql }) => { const { createPage } = actions const blogPostTemplate = path.resolve(“src/templates/blog.js”) const tagTemplate = path.resolve(“src/templates/tags.js”) return graphql( { allMarkdownRemark( sort: { order: DESC, fields: [frontmatter___date] } limit: 2000 ) { edges { node { frontmatter { path tags } } } } } ).then(result => { if (result.errors) { return Promise.reject(result.errors) } const posts = result.data.allMarkdownRemark.edges posts.forEach(({ node }) => { createPage({ path: node.frontmatter.path, component: blogPostTemplate, }) }) let tags = [] // 获取所有文章的 tags .each(posts, edge => { if (.get(edge, “node.frontmatter.tags”)) { tags = tags.concat(edge.node.frontmatter.tags) } }) // 去重 tags = .uniq(tags) // 创建标签页 tags.forEach(tag => { createPage({ path: /tags/${_.kebabCase(tag)}/, component: tagTemplate, context: { tag, }, }) }) })}如果你要把标签页也分页,多加一个循环就行,道理跟主页分页都是一样的:tags.forEach(tag => { const total = tag.totalCount const numPages = Math.ceil(total / postsPerPage) Array.from({ length: numPages }).forEach((, i) => { createPage({ path: i === 0 ? /tag/${tag.fieldValue} : /tag/${tag.fieldValue}/${i + 1}, component: tagTemplate, context: { tag: tag.fieldValue, currentPage: i + 1, totalPage: numPages, limit: postsPerPage, skip: i * postsPerPage, }, }) })})这里仅仅是把查询到的文章的所有标签都抽取出来,用以生成标签页,但是标签具体内容的获取依赖于标签页本身的查询。制作 /tags 页面展示所有标签重点同样是查询部分:export const pageQuery = graphql query { site { siteMetadata { title } } allMarkdownRemark( limit: 2000 filter: { frontmatter: { published: { ne: false } } } ) { group(field: frontmatter___tags) { fieldValue totalCount } } }fieldValue 是标签名,totalCount 是包含该标签的文章总数。在之前写好的文章页渲染标签就是查询的时候多一个标签字段,然后渲染上,完事。下一步再次提醒,对于数据结构模糊的话直接在 localhost:8000/___graphql 查一下就很清晰了。现在这个 blog 已经越来越完善,接下来添加的功能可以说都是非必须的了,下一步先说说页面部署。 ...

December 29, 2018 · 2 min · jiezi

后端小白的我,是如何成功搭建 express+mongodb 的简洁博客网站后端的

前言blog-node 是采用了主流的前后端分离思想的,主里只讲 后端。blog-node 项目是 node + express + mongodb 的进行开发的,项目已经开源,项目地址在 github 上。效果请看 http://乐趣区.cn/main.html1. 后端1.1 已经实现功能[x] 登录[x] 文章管理[x] 标签管理[x] 评论[x] 留言管理[x] 用户管理[x] 友情链接管理[x] 时间轴管理[x] 身份验证1.2 待实现功能[ ] 点赞、留言和评论 的通知管理[ ] 个人中心(用来设置博主的各种信息)[ ] 工作台( 接入百度统计接口,查看网站浏览量和用户访问等数据 )2. 技术nodecookie-parser : “~1.4.3"crypto : “^1.0.1"express: “~4.16.0"express-session : “^1.15.6”,http-errors : “~1.6.2”,mongodb : “^3.1.8”,mongoose : “^5.3.7”,mongoose-auto-increment : “^5.0.1”,yargs : “^12.0.2"3. 主文件 app.js// modulesconst createError = require(‘http-errors’);const express = require(’express’);const path = require(‘path’);const cookieParser = require(‘cookie-parser’);const logger = require(‘morgan’);const session = require(’express-session’);// import 等语法要用到 babel 支持require(‘babel-register’);const app = express();// view engine setupapp.set(‘views’, path.join(__dirname, ‘views’));app.set(‘view engine’, ’ejs’);app.use(logger(‘dev’));app.use(express.json());app.use(express.urlencoded({ extended: false }));app.use(express.static(path.join(__dirname, ‘public’)));app.use(cookieParser(‘blog_node_cookie’));app.use( session({ secret: ‘blog_node_cookie’, name: ‘session_id’, //# 在浏览器中生成cookie的名称key,默认是connect.sid resave: true, saveUninitialized: true, cookie: { maxAge: 60 * 1000 * 30, httpOnly: true }, //过期时间 }),);const mongodb = require(’./core/mongodb’);// data servermongodb.connect();//将路由文件引入const route = require(’./routes/index’);//初始化所有路由route(app);// catch 404 and forward to error handlerapp.use(function(req, res, next) { next(createError(404));});// error handlerapp.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get(’env’) === ‘development’ ? err : {}; // render the error page res.status(err.status || 500); res.render(’error’);});module.exports = app;4. 数据库 core/mongodb.js/** * Mongoose module. * @file 数据库模块 * @module core/mongoose * @author 乐趣区 <https://github.com/乐趣区> /const consola = require(‘consola’)const CONFIG = require(’../app.config.js’)const mongoose = require(‘mongoose’)const autoIncrement = require(‘mongoose-auto-increment’)// remove DeprecationWarningmongoose.set(‘useFindAndModify’, false)// mongoose Promisemongoose.Promise = global.Promise// mongooseexports.mongoose = mongoose// connectexports.connect = () => { // 连接数据库 mongoose.connect(CONFIG.MONGODB.uri, { useCreateIndex: true, useNewUrlParser: true, promiseLibrary: global.Promise }) // 连接错误 mongoose.connection.on(’error’, error => { consola.warn(‘数据库连接失败!’, error) }) // 连接成功 mongoose.connection.once(‘open’, () => { consola.ready(‘数据库连接成功!’) }) // 自增 ID 初始化 autoIncrement.initialize(mongoose.connection) // 返回实例 return mongoose}5. 数据模型 Model这里只介绍 用户、文章和评论 的模型。5.1 用户用户的字段都有设置类型 type,大多都设置了默认值 default ,邮箱设置了验证规则 validate,密码保存用了 crypto 来加密。用了中间件自增 ID 插件 mongoose-auto-increment。/* * User model module. * @file 权限和用户数据模型 * @module model/user * @author 乐趣区 <https://github.com/乐趣区> /const crypto = require(‘crypto’);const { argv } = require(‘yargs’);const { mongoose } = require(’../core/mongodb.js’);const autoIncrement = require(‘mongoose-auto-increment’);const adminSchema = new mongoose.Schema({ // 名字 name: { type: String, required: true, default: ’’ }, // 用户类型 0:博主 1:其他用户 type: { type: Number, default: 1 }, // 手机 phone: { type: String, default: ’’ }, //封面 img_url: { type: String, default: ’’ }, // 邮箱 email: { type: String, required: true, validate: /\w[-\w.+]@([A-Za-z0-9][-A-Za-z0-9]+.)+[A-Za-z]{2,14}/ }, // 个人介绍 introduce: { type: String, default: ’’ }, // 头像 avatar: { type: String, default: ‘user’ }, // 密码 password: { type: String, required: true, default: crypto .createHash(‘md5’) .update(argv.auth_default_password || ‘root’) .digest(‘hex’), }, // 创建日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now },});// 自增 ID 插件配置adminSchema.plugin(autoIncrement.plugin, { model: ‘User’, field: ‘id’, startAt: 1, incrementBy: 1,});module.exports = mongoose.model(‘User’, adminSchema);5.2 文章文章是分类型的:文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍而且简历和管理员介绍的文章只能是各自一篇(因为前台展示那里有个导航 关于我 ,就是请求管理员介绍这篇文章的,简历也是打算这样子用的),普通文章可以是无数篇。点赞的用户 like_users 那里应该只保存用户 id 的,这个后面修改一下。/** * Article model module. * @file 文章数据模型 * @module model/article * @author 乐趣区 <https://github.com/乐趣区> /const { mongoose } = require(’../core/mongodb.js’);const autoIncrement = require(‘mongoose-auto-increment’);// 文章模型const articleSchema = new mongoose.Schema({ // 文章标题 title: { type: String, required: true, validate: /\S+/ }, // 文章关键字(SEO) keyword: [{ type: String, default: ’’ }], // 作者 author: { type: String, required: true, validate: /\S+/ }, // 文章描述 desc: { type: String, default: ’’ }, // 文章内容 content: { type: String, required: true, validate: /\S+/ }, // 字数 numbers: { type: String, default: 0 }, // 封面图 img_url: { type: String, default: ‘https://upload-images.jianshu.io/upload_images/12890819-80fa7517ab3f2783.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240' }, // 文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍 type: { type: Number, default: 1 }, // 文章发布状态 => 0 草稿,1 已发布 state: { type: Number, default: 1 }, // 文章转载状态 => 0 原创,1 转载,2 混合 origin: { type: Number, default: 0 }, // 文章标签 tags: [{ type: mongoose.Schema.Types.ObjectId, ref: ‘Tag’, required: true }], comments: [{ type: mongoose.Schema.Types.ObjectId, ref: ‘Comment’, required: true }], // 文章分类 category: [{ type: mongoose.Schema.Types.ObjectId, ref: ‘Category’, required: true }], // 点赞的用户 like_users: [ { // 用户id id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: ’’ }, // 用户类型 0:博主 1:其他用户 type: { type: Number, default: 1 }, // 个人介绍 introduce: { type: String, default: ’’ }, // 头像 avatar: { type: String, default: ‘user’ }, // 创建日期 create_time: { type: Date, default: Date.now }, }, ], // 其他元信息 meta: { views: { type: Number, default: 0 }, likes: { type: Number, default: 0 }, comments: { type: Number, default: 0 }, }, // 创建日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now },});// 自增 ID 插件配置articleSchema.plugin(autoIncrement.plugin, { model: ‘Article’, field: ‘id’, startAt: 1, incrementBy: 1,});// 文章模型module.exports = mongoose.model(‘Article’, articleSchema);5.3 评论评论功能是实现了简单的三级评论的,第三者的评论(就是别人对一级评论进行再评论)放在 other_comments 里面。/* * Comment model module. * @file 评论数据模型 * @module model/comment * @author 乐趣区 <https://github.com/乐趣区> */const { mongoose } = require(’../core/mongodb.js’);const autoIncrement = require(‘mongoose-auto-increment’);// 评论模型const commentSchema = new mongoose.Schema({ // 评论所在的文章 id article_id: { type: mongoose.Schema.Types.ObjectId, required: true }, // content content: { type: String, required: true, validate: /\S+/ }, // 是否置顶 is_top: { type: Boolean, default: false }, // 被赞数 likes: { type: Number, default: 0 }, user_id: { type: mongoose.Schema.Types.ObjectId, ref: ‘User’, required: true }, // 父评论的用户信息 user: { // 用户id user_id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: ’’ }, // 用户类型 0:博主 1:其他用户 type: { type: Number, default: 1 }, // 头像 avatar: { type: String, default: ‘user’ }, }, // 第三者评论 other_comments: [ { user: { id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: ’’ }, // 用户类型 0:博主 1:其他用户 type: { type: Number, default: 1 }, }, // content content: { type: String, required: true, validate: /\S+/ }, // 状态 => 0 待审核 / 1 通过正常 / -1 已删除 / -2 垃圾评论 state: { type: Number, default: 1 }, // 创建日期 create_time: { type: Date, default: Date.now }, }, ], // 状态 => 0 待审核 / 1 通过正常 / -1 已删除 / -2 垃圾评论 state: { type: Number, default: 1 }, // 创建日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now },});// 自增 ID 插件配置commentSchema.plugin(autoIncrement.plugin, { model: ‘Comment’, field: ‘id’, startAt: 1, incrementBy: 1,});// 标签模型module.exports = mongoose.model(‘Comment’, commentSchema);其他模块的具体需求,都是些常用的逻辑可以实现的,也很简单,这里就不展开讲了。6. 路由接口 routes6.1 主文件/*所有的路由接口/const user = require(’./user’);const article = require(’./article’);const comment = require(’./comment’);const message = require(’./message’);const tag = require(’./tag’);const link = require(’./link’);const category = require(’./category’);const timeAxis = require(’./timeAxis’);module.exports = app => { app.post(’/login’, user.login); app.post(’/logout’, user.logout); app.post(’/loginAdmin’, user.loginAdmin); app.post(’/register’, user.register); app.post(’/delUser’, user.delUser); app.get(’/currentUser’, user.currentUser); app.get(’/getUserList’, user.getUserList); app.post(’/addComment’, comment.addComment); app.post(’/addThirdComment’, comment.addThirdComment); app.post(’/changeComment’, comment.changeComment); app.post(’/changeThirdComment’, comment.changeThirdComment); app.get(’/getCommentList’, comment.getCommentList); app.post(’/addArticle’, article.addArticle); app.post(’/updateArticle’, article.updateArticle); app.post(’/delArticle’, article.delArticle); app.get(’/getArticleList’, article.getArticleList); app.get(’/getArticleListAdmin’, article.getArticleListAdmin); app.post(’/getArticleDetail’, article.getArticleDetail); app.post(’/likeArticle’, article.likeArticle); app.post(’/addTag’, tag.addTag); app.post(’/delTag’, tag.delTag); app.get(’/getTagList’, tag.getTagList); app.post(’/addMessage’, message.addMessage); app.post(’/addReplyMessage’, message.addReplyMessage); app.post(’/delMessage’, message.delMessage); app.post(’/getMessageDetail’, message.getMessageDetail); app.get(’/getMessageList’, message.getMessageList); app.post(’/addLink’, link.addLink); app.post(’/updateLink’, link.updateLink); app.post(’/delLink’, link.delLink); app.get(’/getLinkList’, link.getLinkList); app.post(’/addCategory’, category.addCategory); app.post(’/delCategory’, category.delCategory); app.get(’/getCategoryList’, category.getCategoryList); app.post(’/addTimeAxis’, timeAxis.addTimeAxis); app.post(’/updateTimeAxis’, timeAxis.updateTimeAxis); app.post(’/delTimeAxis’, timeAxis.delTimeAxis); app.get(’/getTimeAxisList’, timeAxis.getTimeAxisList); app.post(’/getTimeAxisDetail’, timeAxis.getTimeAxisDetail);};6.2 文章各模块的列表都是用了分页的形式的。import Article from ‘../models/article’;import User from ‘../models/user’;import { responseClient, timestampToTime } from ‘../util/util’;exports.addArticle = (req, res) => { // if (!req.session.userInfo) { // responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); // return; // } const { title, author, keyword, content, desc, img_url, tags, category, state, type, origin } = req.body; let tempArticle = null if(img_url){ tempArticle = new Article({ title, author, keyword: keyword ? keyword.split(’,’) : [], content, numbers: content.length, desc, img_url, tags: tags ? tags.split(’,’) : [], category: category ? category.split(’,’) : [], state, type, origin, }); }else{ tempArticle = new Article({ title, author, keyword: keyword ? keyword.split(’,’) : [], content, numbers: content.length, desc, tags: tags ? tags.split(’,’) : [], category: category ? category.split(’,’) : [], state, type, origin, }); } tempArticle .save() .then(data => { responseClient(res, 200, 0, ‘保存成功’, data); }) .catch(err => { console.log(err); responseClient(res); });};exports.updateArticle = (req, res) => { // if (!req.session.userInfo) { // responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); // return; // } const { title, author, keyword, content, desc, img_url, tags, category, state, type, origin, id } = req.body; Article.update( { _id: id }, { title, author, keyword: keyword ? keyword.split(’,’): [], content, desc, img_url, tags: tags ? tags.split(’,’) : [], category:category ? category.split(’,’) : [], state, type, origin, }, ) .then(result => { responseClient(res, 200, 0, ‘操作成功’, result); }) .catch(err => { console.error(err); responseClient(res); });};exports.delArticle = (req, res) => { let { id } = req.body; Article.deleteMany({ _id: id }) .then(result => { if (result.n === 1) { responseClient(res, 200, 0, ‘删除成功!’); } else { responseClient(res, 200, 1, ‘文章不存在’); } }) .catch(err => { console.error(’err :’, err); responseClient(res); });};// 前台文章列表exports.getArticleList = (req, res) => { let keyword = req.query.keyword || null; let state = req.query.state || ‘’; let likes = req.query.likes || ‘’; let tag_id = req.query.tag_id || ‘’; let category_id = req.query.category_id || ‘’; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (!state) { if (keyword) { const reg = new RegExp(keyword, ‘i’); //不区分大小写 conditions = { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }], }; } } else if (state) { state = parseInt(state); if (keyword) { const reg = new RegExp(keyword, ‘i’); conditions = { $and: [ { $or: [{ state: state }] }, { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }, { keyword: { $regex: reg } }] }, ], }; } else { conditions = { state }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Article.countDocuments(conditions, (err, count) => { if (err) { console.log(‘Error:’ + err); } else { responseData.count = count; // 待返回的字段 let fields = { title: 1, author: 1, keyword: 1, content: 1, desc: 1, img_url: 1, tags: 1, category: 1, state: 1, type: 1, origin: 1, comments: 1, like_User_id: 1, meta: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Article.find(conditions, fields, options, (error, result) => { if (err) { console.error(‘Error:’ + error); // throw error; } else { let newList = []; if (likes) { // 根据热度 likes 返回数据 result.sort((a, b) => { return b.meta.likes - a.meta.likes; }); responseData.list = result; } else if (category_id) { // 根据 分类 id 返回数据 result.forEach(item => { if (item.category.indexOf(category_id) > -1) { newList.push(item); } }); let len = newList.length; responseData.count = len; responseData.list = newList; } else if (tag_id) { // 根据标签 id 返回数据 result.forEach(item => { if (item.tags.indexOf(tag_id) > -1) { newList.push(item); } }); let len = newList.length; responseData.count = len; responseData.list = newList; } else { responseData.list = result; } responseClient(res, 200, 0, ‘操作成功!’, responseData); } }); } });};// 后台文章列表exports.getArticleListAdmin = (req, res) => { let keyword = req.query.keyword || null; let state = req.query.state || ‘’; let likes = req.query.likes || ‘’; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (!state) { if (keyword) { const reg = new RegExp(keyword, ‘i’); //不区分大小写 conditions = { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }], }; } } else if (state) { state = parseInt(state); if (keyword) { const reg = new RegExp(keyword, ‘i’); conditions = { $and: [ { $or: [{ state: state }] }, { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }, { keyword: { $regex: reg } }] }, ], }; } else { conditions = { state }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Article.countDocuments(conditions, (err, count) => { if (err) { console.log(‘Error:’ + err); } else { responseData.count = count; // 待返回的字段 let fields = { title: 1, author: 1, keyword: 1, content: 1, desc: 1, img_url: 1, tags: 1, category: 1, state: 1, type: 1, origin: 1, comments: 1, like_User_id: 1, meta: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Article.find(conditions, fields, options, (error, result) => { if (err) { console.error(‘Error:’ + error); // throw error; } else { if (likes) { result.sort((a, b) => { return b.meta.likes - a.meta.likes; }); } responseData.list = result; responseClient(res, 200, 0, ‘操作成功!’, responseData); } }) .populate([ { path: ’tags’, }, { path: ‘comments’, }, { path: ‘category’, }, ]) .exec((err, doc) => {}); } });};// 文章点赞exports.likeArticle = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); return; } let { id, user_id } = req.body; Article.findOne({ _id: id }) .then(data => { let fields = {}; data.meta.likes = data.meta.likes + 1; fields.meta = data.meta; let like_users_arr = data.like_users.length ? data.like_users : []; User.findOne({ _id: user_id }) .then(user => { let new_like_user = {}; new_like_user.id = user._id; new_like_user.name = user.name; new_like_user.avatar = user.avatar; new_like_user.create_time = user.create_time; new_like_user.type = user.type; new_like_user.introduce = user.introduce; like_users_arr.push(new_like_user); fields.like_users = like_users_arr; Article.update({ _id: id }, fields) .then(result => { responseClient(res, 200, 0, ‘操作成功!’, result); }) .catch(err => { console.error(’err :’, err); throw err; }); }) .catch(err => { responseClient(res); console.error(’err 1:’, err); }); }) .catch(err => { responseClient(res); console.error(’err 2:’, err); });};// 文章详情exports.getArticleDetailByType = (req, res) => { let { type } = req.body; if (!type) { responseClient(res, 200, 1, ‘文章不存在 !’); return; } Article.findOne({ type: type }, (Error, data) => { if (Error) { console.error(‘Error:’ + Error); // throw error; } else { data.meta.views = data.meta.views + 1; Article.updateOne({ type: type }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, ‘操作成功 !’, data); }) .catch(err => { console.error(’err :’, err); throw err; }); } }) .populate([ { path: ’tags’, select: ‘-_id’ }, { path: ‘category’, select: ‘-_id’ }, { path: ‘comments’, select: ‘-_id’ }, ]) .exec((err, doc) => { // console.log(“doc:”); // aikin // console.log(“doc.tags:",doc.tags); // aikin // console.log(“doc.category:",doc.category); // undefined });};// 文章详情exports.getArticleDetail = (req, res) => { let { id } = req.body; let type = Number(req.body.type) || 1; //文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍 console.log(’type:’, type); if (type === 1) { if (!id) { responseClient(res, 200, 1, ‘文章不存在 !’); return; } Article.findOne({ _id: id }, (Error, data) => { if (Error) { console.error(‘Error:’ + Error); // throw error; } else { data.meta.views = data.meta.views + 1; Article.updateOne({ _id: id }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, ‘操作成功 !’, data); }) .catch(err => { console.error(’err :’, err); throw err; }); } }) .populate([ { path: ’tags’, }, { path: ‘category’, }, { path: ‘comments’, }, ]) .exec((err, doc) => { // console.log(“doc:”); // aikin // console.log(“doc.tags:",doc.tags); // aikin // console.log(“doc.category:",doc.category); // undefined }); } else { Article.findOne({ type: type }, (Error, data) => { if (Error) { console.log(‘Error:’ + Error); // throw error; } else { if (data) { data.meta.views = data.meta.views + 1; Article.updateOne({ type: type }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, ‘操作成功 !’, data); }) .catch(err => { console.error(’err :’, err); throw err; }); } else { responseClient(res, 200, 1, ‘文章不存在 !’); return; } } }) .populate([ { path: ’tags’, }, { path: ‘category’, }, { path: ‘comments’, }, ]) .exec((err, doc) => {}); }};6.3 评论评论是有状态的:状态 => 0 待审核 / 1 通过正常 / -1 已删除 / -2 垃圾评论。管理一级和三级评论是设置前台能不能展示的,默认是展示,如果管理员看了,是条垃圾评论就 设置为 -1 或者 -2 ,进行隐藏,前台就不会展现了。import { responseClient } from ‘../util/util’;import Comment from ‘../models/comment’;import User from ‘../models/user’;import Article from ‘../models/article’;//获取全部评论exports.getCommentList = (req, res) => { let keyword = req.query.keyword || null; let comment_id = req.query.comment_id || null; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (comment_id) { if (keyword) { const reg = new RegExp(keyword, ‘i’); //不区分大小写 conditions = { _id: comment_id, content: { $regex: reg }, }; } else { conditions = { _id: comment_id, }; } } else { if (keyword) { const reg = new RegExp(keyword, ‘i’); //不区分大小写 conditions = { content: { $regex: reg }, }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Comment.countDocuments(conditions, (err, count) => { if (err) { console.error(‘Error:’ + err); } else { responseData.count = count; // 待返回的字段 let fields = { article_id: 1, content: 1, is_top: 1, likes: 1, user_id: 1, user: 1, other_comments: 1, state: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Comment.find(conditions, fields, options, (error, result) => { if (err) { console.error(‘Error:’ + error); // throw error; } else { responseData.list = result; responseClient(res, 200, 0, ‘操作成功!’, responseData); } }); } });};// 添加一级评论exports.addComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); return; } let { article_id, user_id, content } = req.body; User.findById({ _id: user_id, }) .then(result => { // console.log(‘result :’, result); if (result) { let userInfo = { user_id: result._id, name: result.name, type: result.type, avatar: result.avatar, }; let comment = new Comment({ article_id: article_id, content: content, user_id: user_id, user: userInfo, }); comment .save() .then(commentResult => { Article.findOne({ _id: article_id }, (errors, data) => { if (errors) { console.error(‘Error:’ + errors); // throw errors; } else { data.comments.push(commentResult._id); data.meta.comments = data.meta.comments + 1; Article.updateOne({ _id: article_id }, { comments: data.comments, meta: data.meta }) .then(result => { responseClient(res, 200, 0, ‘操作成功 !’, commentResult); }) .catch(err => { console.error(’err :’, err); throw err; }); } }); }) .catch(err2 => { console.error(’err :’, err2); throw err2; }); } else { responseClient(res, 200, 1, ‘用户不存在’); } }) .catch(error => { console.error(’error :’, error); responseClient(res); });};// 添加第三者评论exports.addThirdComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); return; } let { article_id, comment_id, user_id, content } = req.body; Comment.findById({ _id: comment_id, }) .then(commentResult => { User.findById({ _id: user_id, }) .then(userResult => { if (userResult) { let userInfo = { user_id: userResult._id, name: userResult.name, type: userResult.type, avatar: userResult.avatar, }; let item = { user: userInfo, content: content, }; commentResult.other_comments.push(item); Comment.updateOne( { _id: comment_id }, { other_comments: commentResult, }, ) .then(result => { responseClient(res, 200, 0, ‘操作成功’, result); Article.findOne({ _id: article_id }, (errors, data) => { if (errors) { console.error(‘Error:’ + errors); // throw errors; } else { data.meta.comments = data.meta.comments + 1; Article.updateOne({ _id: article_id }, { meta: data.meta }) .then(result => { // console.log(‘result :’, result); responseClient(res, 200, 0, ‘操作成功 !’, result); }) .catch(err => { console.log(’err :’, err); throw err; }); } }); }) .catch(err1 => { console.error(’err1:’, err1); responseClient(res); }); } else { responseClient(res, 200, 1, ‘用户不存在’); } }) .catch(error => { console.error(’error :’, error); responseClient(res); }); }) .catch(error2 => { console.error(’error2 :’, error2); responseClient(res); });};// 管理一级评论exports.changeComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); return; } let { id, state } = req.body; Comment.updateOne( { _id: id }, { state: Number(state), }, ) .then(result => { responseClient(res, 200, 0, ‘操作成功’, result); }) .catch(err => { console.error(’err:’, err); responseClient(res); });};// 管理第三者评论exports.changeThirdComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, ‘您还没登录,或者登录信息已过期,请重新登录!’); return; } let { comment_id, state, index } = req.body; Comment.findById({ _id: comment_id, }) .then(commentResult => { let i = index ? Number(index) : 0; if (commentResult.other_comments.length) { commentResult.other_comments[i].state = Number(state); Comment.updateOne( { _id: comment_id }, { other_comments: commentResult, }, ) .then(result => { responseClient(res, 200, 0, ‘操作成功’, result); }) .catch(err1 => { console.error(’err1:’, err1); responseClient(res); }); } else { responseClient(res, 200, 1, ‘第三方评论不存在!’, result); } }) .catch(error2 => { console.log(’error2 :’, error2); responseClient(res); });};其他模块的具体需求,都是些常用的逻辑可以实现的,也很简单,这里就不展开讲了。7. Build Setup ( 构建安装 )# install dependenciesnpm install # serve with hot reload at localhost: 3000npm start # build for production with minification请使用 pm2 ,可以永久运行在服务器上,且不会一报错 node 程序就挂了。8. 项目地址如果觉得该项目不错或者对你有所帮助,欢迎到 github 上给个 star,谢谢。项目地址:前台展示: https://github.com/乐趣区/blog-react管理后台:https://github.com/乐趣区/blog-react-admin后端:https://github.com/乐趣区/blog-nodeblog:https://github.com/乐趣区/blog本博客系统的系列文章:react + node + express + ant + mongodb 的简洁兼时尚的博客网站react + Ant Design + 支持 markdown 的 blog-react 项目文档说明基于 node + express + mongodb 的 blog-node 项目文档说明服务器小白的我,是如何将node+mongodb项目部署在服务器上并进行性能优化的9. 最后小汪也是第一次搭建 node 后端项目,也参考了其他项目。参考项目:1. nodepress2. React-Express-Blog-Demo对 全栈开发 有兴趣的朋友,可以扫下方二维码,关注我的公众号,我会不定期更新有价值的内容。微信公众号:乐趣区分享 前端、后端开发 等相关的技术文章,热点资源,全栈程序员的成长之路。关注公众号并回复 福利 便免费送你视频资源,绝对干货。福利详情请点击: 免费资源分享–Python、Java、Linux、Go、node、vue、react、javaScript ...

November 25, 2018 · 16 min · jiezi