日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Chrome Extension in CLJS —— 搭建开发环境

發布時間:2025/6/15 javascript 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Chrome Extension in CLJS —— 搭建开发环境 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

?磨刀不誤砍柴工,本篇將介紹如何搭建Chrome插件的ClojureScript開發環境。
具體工具棧:vim(paredit,tslime,vim-clojure-static,vim-fireplace) + leiningen(lein-cljsbuild,lein-doo,lein-ancient) + com.cemerick/piggieback

寫得要爽

?首先拋開將cljs編譯為js、調試、測試和發布等問題,首先第一要務是寫得爽~
?cljs中最讓人心煩的就是括號(),過去我想能否改個語法以換行來代替括號呢?而paredit.vim正好解決這個問題。

安裝

在.vimrc中添加

Plugin 'paredit.vim'

在vim中運行

:source % :PluginInstall

設置<Leader>鍵

" 設置<Leader>鍵 let mapleader=',' let g:mapleader=','

用法

  • 輸入(、[、{和",會自動生成)、]、}和",并且光標位于其中,vim處于insert狀態;
  • normal模式時,輸入<Leader>+W會生成括號包裹住當前光標所在的表達式;
  • normal模式時,輸入<Leader>+w+[會生成[]包裹住當前光標所在的表達式;
  • normal模式時,輸入<Leader>+w+"會生成""包裹住當前光標所在的表達式。
  • 更多用法就通過:help paredit查看paredit的文檔即可。

    編譯環境

    ?cljs要被編譯為js后才能被運行,這里我采用leiningen。
    在shell中運行

    # 創建工程 $ lein new crx-demo $ cd crx-demo

    工程目錄中的project.clj就是工程文件,我們將其修改如下

    (defproject crx-demo "0.1.0-SNAPSHOT":description "crx-demo":urnl "http://fsjohnhuang.cnblogs.com":license {:name "Eclipse Public License":url "http://www.eclipse.org/legal/epl-v10.html"}:dependencies [[org.clojure/clojure "1.8.0"] ;; 通過dependencies聲明項目依賴項[org.clojure/clojurescript "1.9.908"]]:plugins [[lein-cljsbuild "1.1.7"]] ;; 通過plugins聲明leiningen的插件,然后就可以通過lein cljsbuild調用lein-cljsbuild這個插件了:jvm-opts ["-Xmx1g"] ;; 設置JVM的堆容量,有時編譯失敗是應為堆太小:cljsbuild {:builds[{:id "browser_action":source-paths ["src/browser_action"]:compiler {:main browser-action.core:output-to "resources/public/browser_action/js/ignoreme.js":output-dir "resources/public/browser_action/js/out":asset-path "browser_action/js/out":optimizations :none ;; 注意:為提高編譯效率,必須將優化項設置為:none:source-map true:source-map-timestamp true}}{:id "content_scripts":source-paths ["src/content_scripts"]:compiler {:main content-scripts.core:output-to "resources/public/content_scripts/js/content_scripts.js":output-dir "resources/public/content_scripts/js/out":asset-path "content_scripts/js/out":optimizations :whitespace:source-map true:source-map-timestamp true}}}]}:aliases {"build" ["cljsbuild" "auto" "browser_action" "content_scripts"] ;; 設置別名,那么通過lein build就可一次性編譯browser_action和content_scripts兩個子項目了。})

    ?上述配置很明顯我是將browser_action和content_scripts作為兩個獨立的子項目,其實Chrome插件中Browser ActionPage Action、Content ScriptsBackground等均是相對獨立的模塊相互并不依存,并且它們運行的方式和環境不盡相同,因此將它們作為獨立子項目配置、編譯和優化更適合。
    ? 然后新建resources/public/img目錄,并附上名為icon.jpg的圖標即可。
    &esmp;然后在resources/public下新建manifest.json文件,修改內容如下

    {"manifest_version": 2,"name": "crx-demo","version": "1.0.0","description": "crx-demo","icons":{"16": "img/icon.jpg","48": "img/icon.jpg","128": "img/icon.jpg"},"browser_action":{"default_icon": "img/icon.jpg","default_title": "crx-demo","default_popup": "browser_action.html"},"content_scripts":[{"matches": ["*://*/*"],"js": ["content_scripts/js/core.js"],"run_at": "document_start"}],"permissions": ["tabs", "storage"] }

    ?接下來新建resources/public/browser_action.html,并修改內容如下

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title></title> </head> <body><script src="browser_action/js/out/goog/base.js"></script><script src="browser_action/js/out/goog/deps.js"></script><script src="browser_action/js/out/cljs_deps.js"></script><script src="browser_action.js"></script> </body> </html>

    ?到這一步我們會發現哪來的browser_action.js啊?先別焦急,這里涉及到Browser Action的運行環境與google closure compiler輸出不兼容的問題。

    Browser Action/Popup運行環境

    ?這里有個限制,就是default_popup所指向頁面中不能存在內聯腳本,而optimizations :none時google closure compiler會輸出如下東東到ignoreme.js中

    var CLOSURE_UNCOMPILED_DEFINES = {}; var CLOSURE_NO_DEPS = true; if(typeof goog == "undefined") document.write('<script src="resources/public/browser_action/js/out/goog/base.js"></script>'); document.write('<script src="resources/public/browser_action/js/out/goog/deps.js"></script>'); document.write('<script src="resources/public/browser_action/js/out/cljs_deps.js"></script>'); document.write('<script>if (typeof goog == "undefined") console.warn("ClojureScript could not load :main, did you forget to specify :asset-path?");</script>'); document.write('<script>goog.require("process.env");</script>'); document.write('<script>goog.require("crx_demo.core");</script>');

    這很明顯就是加入內聯腳本嘛~~~所以我們要手工修改一下,新增一個resources/public/browser_action.js,然后添加如下內容

    goog.require("process.env") goog.require("crx_demo.core")

    這里我們就搞定Browser Action/Popup的編譯運行環境了^_^。大家有沒有發現goog.require("crx_demo.core")這一句呢?我們的命名空間名稱不是crx-demo.core嗎?注意了,編譯后不僅路徑上-會變成_,連在goog中聲明的命名空間名稱也會將-變成了_。

    Content Scripts運行環境

    ?由于content scripts是直接運行腳本,沒有頁面讓我們如popup那樣控制腳本加載方式和順序,因此只能通過optimizations :whitespace將所有依賴打包成一個js文件了。也就是說編譯起來會相對慢很多~很多~多~~~

    開發得爽

    ?到這里我們似乎可寫上一小段cljs,然后編譯運行了。且慢,沒有任何智能提示就算了,還時不時要上網查閱API DOC,你確定要這樣開發下去?

    在vim中查看API DOC

    ?通過vim-fireplace我們可以手不離vim,查閱API文檔,和查閱項目代碼定義哦!
    1.裝vim插件

    Plugin 'tpope/vim-fireplace'

    在vim中運行

    :source % :PluginInstall

    2.安裝nRepl中間件piggieback
    ?nRepl就是網絡repl,可以接收客戶端的腳本,然后將運行結果回顯到客戶端。我們可以通過lein repl啟動Clojure的nRepl。
    ?而fireplace則是集成到vim上連接nRepl的客戶端,但默認啟動的僅僅是Clojure的nRepl,所以要通過中間件附加cljs的nRepl。這是我們只需在project.clj中添加依賴即可。

    :dependencies [[com.cemerick/piggieback "0.2.2"]] :repl-options {:port 9000:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}

    ?在shell中更新依賴lein deps
    3.設置fireplace監聽端口
    ?在項目目錄下創建文件,echo 9000 > .nreplport
    4.啟動nRepl,lein repl
    ?這時在vim中輸入:Source map就會看到cljs.core/map的定義,若不行則按如下設置:

    :Connect Protocol: nrepl Host: localhost Port: 9000 Scope connection to: ~/crx-dome

    這樣就設置好fireplace和nrepl間的鏈接了。
    5.別開心太早
    ?不知道是什么原因我們只能用fireplace中部分的功能而已,如通過:Source <symbol>查看定義,:FindDoc <keyword>查看匹配的Docstring,但無法通過:Doc <symbol>來查看某標識的Docstring。
    ?另外若要通過:Source <symbol>查看當前項目的定義時,請先lein build將項目編譯好,否則無法查看。另外一個十分重要的信息是,在optimizations不為:none的項目下的文件是無法執行fireplace的指令的,所以在開發Content Scrpts時就十分痛苦了~~~

    ?那有什么其他辦法呢?不怕有tslime.vim幫我們啊!

    tslime.vim

    ?tslime.vim讓我們可以通過快捷鍵將vim中內容快速地復制到repl中執行
    1.安裝vim插件

    Plugin 'jgdavey/tslime.vim'

    在vim中運行

    :source % :PluginInstall

    2..vimrc配置

    " 設置復制的內容自動粘貼到tmux的當前session和當前window中 let g:tslime_always_current_session = 1 let g:tslime_always_current_window = 1vmap <C-c><C-c> <Plug>SendSelectionToTmux nmap <C-c><C-c> <Plug>NormalModeSendToTmux nmap <C-c>r <Plug>SetTmuxVars

    3.將clojure repl升級cljs repl
    ?通過lein repl我們建立了一個cljs nrepl供fireplace使用,但在終端中我們看到的是一個clojure的repl,而tslime恰好要用的就是這個終端的repl。那現在我們只要在clojure repl中執行(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))即可。
    然后就可以在vim中把光標移動到相應的表達式上按<C-c><C-c>,那么這個表達式就會自動復制粘貼到repl中執行了。

    美化輸出

    ?由于cljs擁有比js更為豐富的數據類型,也就是說直接將他們輸出到瀏覽器的console中時,顯示的格式會不太清晰。于是我們需要為瀏覽器安裝插件,但通過devtools我們就不用顯式為瀏覽器安裝插件也能達到效果(太神奇了!)
    在project.clj中加入

    :dependencies [[binaryage/devtools "0.9.4"]] ;; 在要格式化輸出的compiler中添加 :compiler {:preloads [devtools.preload]:external-config {:devtools/config {:features-to-install [:formatters :hints :async]}}}

    然后在代碼中通過js/console.log、js/console.info等輸出的內容就會被格式化的了。

    單元測試很重要

    ?為了保證開發的質量,單元測試怎么能少呢?在project.clj中加入

    :plugins [[lein-doo "0.1.7"]]

    然后在test/crx_demo下新建一個runner.cljs文件,并寫入如下內容

    (ns crx-demo.runner(:require-macros [doo.runners :refer [doo-tests]])(:require [crx-demo.content-scripts.util-test]));; 假設我們要對crx-demo.content-scripts.util下的函數作單元測試,而測試用例則寫在crx-demo.content-scripts.util-test中 (doo-tests 'crx-demo.content-scripts.util-test)

    然后創建crx-demo.content-scripts.util-test.cljs測試用例文件

    (ns crx-demo.content-scripts.util-test(:require-macros [cljs.test :refer [deftest is are testing async]](:require [crx-demo.content-scripts.util :as u]))(deftest test-all-upper-case?(testing "all-upper-case?"(are [x] (true? x)(u/all-upper-case? "abCd")(u/all-upper-case? "ABCD"))))(deftest test-all-lower-case?(testing "all-lower-case?"(is (true? (u/all-lower-case? "cinG")))))(deftest test-get-async(async done(u/get-async (fn [item](is (seq item))(done)))))

    然后再新增一個測試用的子項目

    {:id "test-proj":source-paths ["src/content_scripts" "test/crx_demo"]:compiler {:target :nodejs ;;運行目標環境是nodejs:main crx-demo.runner:output-to "out/test.js":output-dir "out/out":optimizations :none:source-map true:source-map-timestamp true}}

    然后在shell中輸入lein doo node test-proj

    發布前引入externs

    ?辛苦開發后我們將optimizations設置為advanced后編譯優化,將作品發布時發現類似于如下的報錯

    Uncaught TypeError: sa.B is not a function

    這究竟是什么回事呢?
    開發時最多就是將optimizations設置為simple,這時標識符并沒有被壓縮,所以如chrome.runtime.onMessage.addListener等外部定義標識符依然是原裝的。但啟用advanced編譯模式后,由于上述外部標識符的定義并不納入GCC的編譯范圍,因此GCC僅僅將調用部分代碼壓縮了,而定義部分還是原封不動,那么在運行時調用中自然而然就找不到相應的定義咯。Cljs早已為我們找到了解決辦法,那就是添加extern文件,extern文件中描述外部函數、變量等聲明,那么GCC根據extern中的聲明將不對調用代碼中同簽名的標識符作壓縮。
    示例:chrome的extern文件chrome.js片段

    /*** @constructor*/ function MessageSender(){} /** @type {!Tab|undefined} */ MessageSender.prototype.tab

    配置

    1.訪問https://github.com/google/closure-compiler/tree/master/contrib/externs,將chrome.js和chrome_extensions.js下載到項目中的externs目錄下
    2.配置project.clj文件

    :compiler {:externs ["externs/chrome.js" "externs/chrome_extensions.js"]}

    總結

    最后得到的project.clj為

    (defproject crx-demo "0.1.0-SNAPSHOT":description "crx-demo":urnl "http://fsjohnhuang.cnblogs.com":license {:name "Eclipse Public License":url "http://www.eclipse.org/legal/epl-v10.html"}:dependencies [[org.clojure/clojure "1.8.0"][org.clojure/clojurescript "1.9.908"][binaryage/devtools "0.9.4"][com.cemerick/piggieback "0.2.2"]]:plugins [[lein-cljsbuild "1.1.7"][lein-doo "0.1.7"][lein-ancient "0.6.12"]] ;; 通過`lein ancient upgrade` 或 `lein ancient upgrade:plugins`更新依賴項:clean-targets ^{:protect false} [:target-path "out" "resources/public/background" "resources/public/content_scripts" "resources/public/browser_action"]:jvm-opts ["-Xmx1g"]:repl-options {:port 9000:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}:cljsbuild {:builds[{:id "browser_action":source-paths ["src/browser_action"]:compiler {:main browser-action.core:output-to "resources/public/browser_action/js/ignoreme.js":output-dir "resources/public/browser_action/js/out":asset-path "browser_action/js/out":optimizations :none:source-map true:source-map-timestamp true:externs ["externs/chrome.js" "externs/chrome_extensions.js"]:preloads [devtools.preload]:external-config {:devtools/config {:features-to-install [:formatters :hints :async]}}}}{:id "content_scripts":source-paths ["src/content_scripts"]:compiler {:main content-scripts.core:output-to "resources/public/content_scripts/js/content_scripts.js":output-dir "resources/public/content_scripts/js/out":asset-path "content_scripts/js/out":optimizations :whitespace:source-map true:source-map-timestamp true:externs ["externs/chrome.js" "externs/chrome_extensions.js"]:preloads [devtools.preload]:external-config {:devtools/config {:features-to-install [:formatters :hints :async]}}}}]}:aliases {"build" ["cljsbuild" "auto" "browser_action" "content_scripts"]"test" ["doo" "node" "test-proj"]})

    隨著對cljs的應用的深入,我會逐步完善上述配置^_^
    尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

    參考

    http://astashov.github.io/blo...
    https://github.com/emezeske/l...
    https://nvbn.github.io/2014/1...
    https://github.com/binaryage/...
    https://clojurescript.org/too...
    https://github.com/google/clo...

    總結

    以上是生活随笔為你收集整理的Chrome Extension in CLJS —— 搭建开发环境的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。