Notion页面公开以及分享
本篇将介绍如何将 Notion 页面公开以及分享,在进阶里会介绍配合 Cloudflare 将已公开的 Notion 页面换上个人域名。
Notion 的介绍
Notion 是一個將筆記、任務管理和知識庫相結合的跨平台協作工具,並具有 Markdown 支持,也可以通過大多數瀏覽器進行訪問。
https://zh.wikipedia.org/zh-tw/Notion?oldformat=true

Notion 虽然有各个平台的 APP,但是与 Evernote 以及 OneNote 不同的地方是 Notion 没有离线功能,也就是因为这样几乎所有功能都必须在有网路的环境进行,这对部分人来说也许是缺点,也因为一开始是朝着云端协作工具的角度开发,不管用户是在手机端或是在游览器的方式使用的都能有统一的体验。
那么直接进入主题
如何将 Notion 页面公开分享呢?
基础
只要在到你所准备好的 Notion 文件里,选择分享 Share,就能看到如下图一样选择开启 Public Access,在这里的示范里我选择公开 Everblogger 的看板范本。

进阶
Notion 官方暂时还没提供 custom domain 的服务,所以现在只能搭配 Cloudflare 的 Workflow 进行。要注意的是 Cloudflare 免费用户在使用次数上有限制。
打个比方,我想自订到 notion.everblogger.cc,我就需要到 cloudflare 里配合的配合,相信不只是 Notion,其他网站也能应用类似的方法。
首先使用 Cloudflare 托管 Domain,这里 DNS

这里的示范是notion.everblogger.cc,用 CNAME

接着到 Workers


在选择 Lauch Editor,接着在 Add script 里粘贴下列的程式码

const MY_DOMAIN = "agodrich.com"
const START_PAGE = "https://www.notion.so/gatsby-starter-notion-2c5e3d685aa341088d4cd8daca52fcc2"
const DISQUS_SHORTNAME = "agodrich"
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
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)
let response
if (url.pathname.startsWith("/app") && url.pathname.endsWith("js")) {
// skip validation in app.js
response = await fetch(`https://www.notion.so${url.pathname}`)
let body = await response.text()
try {
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")
console.log("get rewrite app.js")
} catch (err) {
console.log(err)
}
} else if ((url.pathname.startsWith("/api"))) {
// Forward API
response = await fetch(`https://www.notion.so${url.pathname}`, {
body: request.body, // must match 'Content-Type' header
headers: {
'content-type': 'application/json;charset=UTF-8',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
},
method: "POST", // *GET, POST, PUT, DELETE, etc.
})
response = new Response(response.body, response)
response.headers.set('Access-Control-Allow-Origin', "*")
} else if (url.pathname === `/`) {
// 301 redrict
let pageUrlList = START_PAGE.split("/")
let redrictUrl = `https://${MY_DOMAIN}/${pageUrlList[pageUrlList.length - 1]}`
return Response.redirect(redrictUrl, 301)
} else {
response = await fetch(`https://www.notion.so${url.pathname}`, {
body: request.body, // must match 'Content-Type' header
headers: request.headers,
method: request.method, // *GET, POST, PUT, DELETE, etc.
})
response = new Response(response.body, response)
if (DISQUS_SHORTNAME) {
// Delete CSP to load disqus content
response.headers.delete("Content-Security-Policy")
// add disqus comment component for every notion page
return new HTMLRewriter().on('body', new ElementHandler()).transform(response)
}
}
return response
}
class ElementHandler {
element(element) {
// An incoming element, such as `div`
element.append(`
<script>
let disqus = document.createElement("div")
disqus.id = "disqus_thread"
disqus.style.width = "100%"
var disqus_config = function () {
let pathList = window.location.pathname.split("-")
let pageID = pathList[pathList.length - 1]
this.page.url = window.location.href;
if (/^[\w]{32}$/.test(pageID)) {
this.page.identifier = pageID;
}else{
this.page.identifier = undefined;
}
};
(function () {
var d = document, s = d.createElement('script');
s.src = 'https://${DISQUS_SHORTNAME}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
// if you want to hide some element, add the selector to hideEle Array
const hideEle = [
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)",
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(5)",
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(4)",
]
// if you want to replace some element, add the selector and innerHTML to replaceEle Object
const replaceEle = {
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)": "<span>agodrich<span>"
}
function hideElement(qs) {
let eles = document.querySelectorAll(qs)
eles && eles.forEach(ele => ele.style.display = "none")
}
function replaceElement(qs, _html) {
let ele = document.querySelector(qs)
if (ele) {
ele.innerHTML = _html
}
}
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
let body = document.querySelector('body');
let observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
let pageContent = document.querySelector("#notion-app div.notion-page-content")
if (pageContent) {
if (pageContent.lastChild && pageContent.lastChild.id !== "disqus_thread") {
pageContent.append(disqus)
DISQUS.reset({ reload: true })
console.log(+new Date())
}
}
hideEle.forEach( hideE => hideElement(hideE) )
Object.entries(replaceEle).forEach( item => {
let [qs,_html] = item;
replaceEle(qs,_html)
})
});
});
observer.observe(body, { subtree: true, childList: true });
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
`, { html: Boolean })
console.log(`Incoming element: ${element.tagName}`)
}
comments(comment) {
// An incoming comment
}
text(text) {
// An incoming piece of text
}
}
view raw
就像我将一下更改const MY_DOMAIN = "notion.everblogger.cc"
const START_PAGE = "https://www.notion.so/366ac8b8488e43dcb2c4fe1cdfed079d?v=73b58e7553f24adfbd0746030407684a" const DISQUS_SHORTNAME = "notion"
最后在 Add route

欢迎来到 everbogger 的 notion 看板!

本篇也同时在公开 Notion 页面发布
https://notion.everblogger.cc/Everblogger-Notion-1a0d26a63789404eafb25a8720f70862
来源:https://medium.com/@TarasPyoneer/how-to-set-up-a-custom-domain-for-your-homepage-in-notion-53fa3d54f848
感想
接触了 Notion 的 Block 编辑器,觉得与 WordPress 后台的 Block 编辑器相比之下并没有多大的不同,甚至有过之而无不及。使用着 WordPress 的朋友相信不会对 Notion 的使用逻辑一头雾水,真希望下个版本的 WordPress 能吸收 Notion 的优点,尤其是 Code,以及网站链接的书签方式。
对 WordPress 有兴趣?物色好用的 WordPress 主机?这里推荐SiteGroud,一键设置,省时省力。
对微软产品有兴趣也能到微软官方商城