緣起
之前我用nwjs做過一個博客園文章編輯器的客戶端
發了好幾個版本,最后一個版本到5.0.0了
其實第一個版本已經很好了,不知足,后來自己又做了兼容markdown的,結果用來用去,發現不是自己想要的
也一直沒動手做新的,寫博客的頻率也降下去了。
最近搞了個APP(還沒上線),打算把心得寫到博客里,發現沒有趁手的編輯器
於是動手重新搞這個東西
先把搞這個東西的心得寫出來,希望大家不吝賜教。
界面
截幾個圖給大家看看,
列表界面:

文章編輯的界面:

功能
博客登錄(引入博客園的登錄畫面,使用官方提供的登錄機制)
文章列表(可以檢索到你博客園內所有的隨筆文章,支持分頁,支持文章分類)
修改文章(你博客園內所有的歷史隨筆文章,都可以用此工具編輯修改)
新增文章(從無到有撰寫一篇新的隨筆文章,支持保存為草稿)
截圖工具截圖后直接黏貼到編輯器中(QQ截圖、微信截圖,直接黏貼到編輯器中)
圖片拖拽到編輯器中自動上傳
插入公式、插入地圖、插入表情、插入表格、插入音樂、插入代碼...一應俱全;
純客戶端,直接與博客園服務端通信(沒有任何自建服務,不采集用戶任何信息)
新版本升級提醒功能;
開源,開源地址:https://gitee.com/xland/cnblogs
操作系統兼容蘋果和windows,下載地址:https://gitee.com/xland/cnblogs/releases
搭建環境
這個程序用到了electron和vue
鏈接這兩個東西的橋梁是electron-vue
建議大家給個star,有這么個工具真的能省很多力氣(有錢的捧個💰場,沒錢的捧個✨場)
用npm工具初始化環境的命令為:
npm install -g vue-cli vue init simulatedgreg/electron-vue cnblogs51 cd cnblogs51 npm install npm run dev
幾行命令執行完,一個默認的模板畫面就啟動起來了;
我們簡單介紹一下electron-vue
我們知道vue有hot-reload的能力,
就是你改了什么代碼,馬上能在界面上表現出來,非常有利於調試;
electron-vue也有這個能力
為了做到這一點,他在你的開發機上起了http的服務;
然而你發布出來的程序,啟動的時候,並沒有在客戶機上也開一個http的服務;
也就是說,開發機上調試程序,界面訪問的是http://..........
客戶機上,運行程序界面加載的事:file://.........
工程簡述
他src目錄下,有兩個子目錄,一個是main;一個是renderer
之所以這樣分目錄,跟electron的工作原理有關系
electron與nwjs不同,
nwjs把nodejs里的v8和chrome瀏覽器里的v8整合到一起了,可以直接在網頁JS里訪問nodejs的庫
electron並沒有這么做,它分兩個主要進程,一個主進程,一個渲染進程;
對應起來就是剛才說的main目錄和renderer目錄
main目錄放主進程相關的東西
renderer目錄放界面相關的東西
如果你開發過vue的程序,你不會對renderer目錄下的東西陌生,就是一個純粹的vue項目應有的東西
main目錄下,一個index.js,一個index.dev.js
如果你的程序處於調試狀態,那么這兩個文件都會起作用(通過npm run dev命令啟動的程序);
如果你的程序處於發布狀態,那么只有index.js文件起作用(通過npm run build命令編譯打包后被安裝的程序);
如果你看了package.json文件
會發現如下配置:
"main":"./dist/electron/main.js"
這是electron程序啟動的入口,
這里的main.js就是index.js和index.dev.js合並打包出來的
注:nwjs入口程序是一個.html的文件;electron的入口程序是一個.js的文件
electron通過js入口程序加載畫面
在咱們這個工程下,他加載的事src目錄下的index.ejs畫面
這其實也是vue程序的宿主頁面
根目錄下,還有一個build子目錄,
如果沒有,你執行一下npm run build命令就有了,
你產品開發完,打包出來的東西,就放在這個目錄下;
這個目錄下,有一個icons的子目錄
打包的時候,從這個子目錄里取圖標,
打包出來的可執行文件的圖標,就是從這里得來的;
還有一個非常重要的目錄是根目錄下的static目錄
前面我們說了electron-vue開發環境和生產環境的不同;
在這個目錄里放的,就是一些靜態的資源
無論是開發環境,還是生產環境,
你只需要通過下面這樣的路徑訪問方式,就能加載到相應的資源
src="static/your/path/file.js"
程序的啟動與關閉
electron程序的入口是一個js文件
在js文件里打開一個窗口,讓這個窗口加載指定的畫面
functioncreateWindow () {
mainWindow = newBrowserWindow({
height:800,
useContentSize:true,
width:1200})
mainWindow.loadURL(winURL)
mainWindow.on('closed', ()=>{mainWindow =null})
}
app.on('ready', createWindow);
app在ready的時候,創建了一個窗口,並加載了一個路徑
app和窗口都是通過下面的代碼得來的:
import{ app, BrowserWindow } from 'electron'
路徑是通過下面的代碼得來的:
constwinURL = process.env.NODE_ENV === 'development'?
`http://localhost:9080`:
`file://${__dirname}/index.html`
electron允許打開子窗口,也允許開多標簽頁
在蘋果端,當所有窗口都關閉后,就要退出程序,代碼如下:
app.on('window-all-closed', ()=>{
if(process.platform !== 'darwin') {
app.quit();
}
})
繞不開的博客園登錄
其實並不是繞不開,而是繞開的成本太高了,不值得。
就像我們做IT系統,永遠沒有一個系統是絕對安全的,
如果破壞一個系統的成本足夠高,那么我們就認為這個系統是足夠安全的;
我們想自己寫畫面,在自己的畫面上顯示博客分類、文章列表;
在自己的畫面上新建、編輯、保存文章;
這就涉及到跨域的問題
一開始我考慮,自己寫模擬登錄的代碼,登錄成功之后自己保存會話信息;
后來發現博客園還有這個東西

要想繞開這個東西,成本一下子高很多
因為這個東西的目的,就是為了防止被繞開的
(其實我有思路,但並未做嘗試)
后來想到,就算繞開又如何呢?
難道我去把博客園的服務端接口都分析一遍
真的用自己的代碼,訪問博客園的接口嗎?
我為什么不通過一個隱藏的iframe,來控制博客園的界面
通過它獲取我想呈現在自己界面上的數據;
也通過它,提交我自己界面上的用戶數據;
這樣就只要用博客園前端頁面內置的jquery獲取文章數據;
然后再用jquery操作提交、草稿按鈕就可以了。
在nwjs環境里,我們真的就是用的一個iframe,代碼如下:
var iframeTag = 'nwUserAgent="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.3538.400 QQBrowser/9.6.12501.400"'; var articleDiv = '<iframe id="articleFrame" src="[your url]" nwdisable nwfaketop '+bky.iframeTag+'></iframe>';
這里涉及到三個特殊的屬性
nwdisable:
是為了防止這個iframe加載的頁面去調用nwjs的一些功能,比如說格盤,因為畢竟我們加載的頁面不是我們自己提供的,要為我們用戶的安全着想;
nwfaketop:
如果沒有這個,在iframe頁面里調用window.top,就能訪問到我們的頁面的window對象,很多廠商都是通過這個來判斷自己是不是被別人iframe了,有了這個標簽,window.top就訪問不到我們
nwUserAgent:
我們通過這個標簽來設置iframe加載頁面時使用的user-agent,進一步隱藏自己,不被服務提供方發現;
在electron里,不是直接用iframe,而是用了electron內置的一個webview的標簽
<webview ref="frame" :preload="preloadJsPath" :src="enterUrl" :useragent="$root.agent"></webview>
(注意:上面這行代碼用到了vue的一些特性)
它也有useragent屬性,跟nwjs里的nwUserAgent一樣
它默認就是不允許被加載頁面訪問electron的底層API的,
因為根本就不是iframe,所以window.top也訪問不到它的宿主頁面;
我的目的是讓我的用戶使用我開發的畫面
我的客戶在不需要的時候,不需要看到博客園的任何界面
所以這個webview默認是隱藏的;
在用戶第一次打開應用的時候,這個webview會直接加載博客園后台的頁面:
enterUrl:"https://i.cnblogs.com/posts"
如果你第一次使用這個應用,你本地肯定沒有會話信息,所以你訪問后台頁面,博客園會給你跳轉到登錄頁面;
我們可以在webview的dom-ready事件里監控webview內部的url變化,代碼如下:
var self = this;
var frame = this.$refs.frame;
frame.addEventListener("dom-ready",function(e){
if(frame.src.search('https://passport.cnblogs.com/user/signin') == 0) {
frame.style.zIndex ="99";
}else{
frame.style.zIndex = "1";
//other code...
}
});
當url變成登錄頁面時,我們就讓webview顯示出來,
這時我們的應用跟一個普通的瀏覽器沒有任何區別,
我們的客戶也是在與博客園的登錄頁面交互,
當用戶登錄成功之后,url又會變成https://i.cnblogs.com/posts,
此時,我又讓webview隱藏起來,把我自己的界面呈現給用戶
這個時候用戶已經登錄成功了
雖然我自己的界面還是沒有博客園的會話信息
但是這個webview已經具備了合法的身份
可以隨意訪問博客園后台的任意API
接下來,我們的界面就會以這個webview為橋梁,與博客園后台服務進行交互。
小結
想通了這些,我發現我手里多了一把錘子🔨
有很多事情可以做呀
寫個自動下單工具、秒殺工具、搶票工具之類的,都是易如反掌;
不會在糾結怎么模擬登陸,就算真要模擬登陸,也不是完全沒辦法;
因為在這個環境下,目標網站的前端代碼,完全暴露在你眼前,隨你想怎么控制就怎么控制
具體怎么搞,我們一下篇再說
,有着急的朋友,可以先看我開源出來的代碼
不過,我想要做的就是讓這個工具兼容更多的博客
現在博客園已經做差不多了,接下來兼容什么呢?CSDN?知乎?簡書?segmentfault?歡迎大家留言;
另外,安裝程序的下載地址為:https://gitee.com/xland/cnblogs/releases
大家發現什么問題可以直接提issue,如果大家期待什么新功能,也可以提issue(qq群:51021155)
喜歡的朋友,請幫忙點個贊,給個星


