{"id":43051356,"url":"https://github.com/subdavis/vuejs-browser-extensions","last_synced_at":"2026-01-31T10:36:59.153Z","repository":{"id":78496171,"uuid":"132672574","full_name":"subdavis/vuejs-browser-extensions","owner":"subdavis","description":"What to keep in mind if you want to use Vue.js for a WebExtension","archived":false,"fork":false,"pushed_at":"2018-05-16T03:26:33.000Z","size":292,"stargazers_count":20,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-06-11T23:38:54.109Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/subdavis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-08T22:38:44.000Z","updated_at":"2022-07-21T03:27:30.000Z","dependencies_parsed_at":null,"dependency_job_id":"96a666d7-5de1-448b-85d4-64f71ffb8501","html_url":"https://github.com/subdavis/vuejs-browser-extensions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/subdavis/vuejs-browser-extensions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/subdavis%2Fvuejs-browser-extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/subdavis%2Fvuejs-browser-extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/subdavis%2Fvuejs-browser-extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/subdavis%2Fvuejs-browser-extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/subdavis","download_url":"https://codeload.github.com/subdavis/vuejs-browser-extensions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/subdavis%2Fvuejs-browser-extensions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28938783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T10:18:23.202Z","status":"ssl_error","status_checked_at":"2026-01-31T10:18:22.693Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-31T10:36:55.842Z","updated_at":"2026-01-31T10:36:59.146Z","avatar_url":"https://github.com/subdavis.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vuejs-browser-extensions\n\nWhat to keep in mind if you want to use Vue.js for a WebExtension\n\n## Anatomy of a browser extension\n\nBrowser extensions consist of 4 parts which are mostly just regular web apps:\n\n* Popup page - the main application (restricted)\n* Options page - a config page (restricted)\n* Background page - an invisible tab that runs as long as your browser is open (restricted, no frontend)\n* Inject script(s) - scripts that can be optionally or automatically run within the context of any website you visit. (unrestricted)\n\nThe first three parts have some restrictions...\n\n* Content Security Policy defaults to `script-src 'self'; object-src 'self'`\n* `eval` (and friends) won't work (ooh... ahh...)\n* All inline JavaScript will not be executed.\n\nMore details: https://developer.chrome.com/extensions/contentSecurityPolicy\n\n...and some superpowers.  For example:\n\n* they can inject code and assets into any page you visit.\n* they can interact with some system hardware, like USB devices.\n* they can add new tabs and functions to the debug console.\n\nMANY more details: https://developer.chrome.com/extensions/api_index\n\n## Can we get around these restrictions?\n\nThe answer is *Yes, but don't*.\n\nBecause extensions have invasive access to modify browser behavior, XSS attacks are a greater threat than they would be in a regular browser tab.\n\nFor perspective, because electron has a node.js runtime, XSS attacks can directly impact your operating system.  Here's [A recent electron CVE](https://www.trustwave.com/Resources/SpiderLabs-Blog/CVE-2018-1000136---Electron-nodeIntegration-Bypass/) that demonstrates how XSS could result in Remote code execution (RCE).\n\n## Security best practices\n\n* Use the default CSP\n* Minimize third party library use\n* Avoid distributing un-invoked code\n\nWe also have to pick a \"stack\".  Here's mine:\n\n* Webpack \u0026 Yarn/NPM\n* Vue.js \u0026 vue-loader\n* SASS \u0026 sass-loader\n\n## Pitfalls: vue-cli\n\nYou may be tempted to `vue-cli init webpack my-extension` and try to shoehorn the result into a working browser extension.\n\nThis will result in:\n\n* A rat's nest of Webpack config you don't need\n* A dependency list heavier than a neutron star\n* A development server on `http://localhost:8080` we can't do anthing with.\n\n## A from-scratch approach\n\nUse the un-minified development version of `vue.js` from https://github.com/vuejs/vue/releases and create a simple hello-world vue.js app will look something like this:\n\n```JavaScript\n// index.html\n\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003cbody\u003e\n\t\n    \u003cdiv id=\"elm\"\u003e\n      \u003ch1\u003e{{ msg }}\u003c/h1\u003e\n    \u003c/div\u003e\n    \u003cscript src=\"vue.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"index.js\"\u003e\u003c/script\u003e\n\n  \u003c/body\u003e\n\u003c/html\u003e\n\n// index.js\n\nnew Vue({\n\tel: \"#elm\",\n\tdata() {\n\t  return {\n\t    msg: \"Hello World!\"\n\t  }\n\t}\n});\n\n// manifest.json\n\n{\n\t\"name\": \"Example\",\n\t\"short_name\": \"Example\",\n\t\"version\": \"2018.5.9\",\n\t\"manifest_version\": 2,\n\t\"minimum_chrome_version\": \"48\",\n\t\"description\": \"Example\",\n\t\"browser_action\": {\n\t\t\"default_popup\": \"index.html\",\n   \t\t\"default_title\": \"Example\"\n\t}\n}\n```\n\nIf you browse to `file:///path/to/index.html` everything looks peachy.\n\nIf you import the app as a browser extension you get a console error:\n\n```html\nError compiling template:\n\n\u003cdiv id=\"elm\"\u003e\n   \t\u003ch1\u003e{{ msg }}\u003c/h1\u003e\n\u003c/div\u003e\n\n- invalid expression: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"script-src 'self' blob: filesystem: chrome-extension-resource:\".\n in\n\n    _s(msg)\n\n  Raw expression: {{ msg }}\n\n(found in \u003cRoot\u003e)\n```\n\nThe offending lines from `vue.js` reveal themselves to be:\n\n```JavaScript\nfunction createFunction (code, errors) {\n  try {\n    return new Function(code)\n  } catch (err) {\n    errors.push({ err: err, code: code });\n    return noop\n  }\n}\n```\n\nSetting a breakpoint shows the `code` string arg:\n\n```JavaScript\n\"with(this){return _c('div',{attrs:{\"id\":\"elm\"}},[_c('h1',[_v(_s(msg))])])}\"\n```\n\n## Solving the eval problem\n\nHere, I'll borrow some points from [a great article on the subject](https://vuejsdevelopers.com/2017/05/08/vue-js-chrome-extension/).\n\n### Write your own render functions\n\n`vue-template-compiler` is actually a component seperable from the vue runtime.  It is invoked at runtime when you either:\n\n1. you use a template string in your vue source code; or\n2. you mount to a template using `el` à la example1\n\nYou can avoid invoking the template compiler by writing your own render functions, described in the [Vue.js docs](https://vuejs.org/v2/guide/render-function.html) but this won't scale.\n\nFrom the docs:\n\n\u003e Vue recommends using templates to build your HTML in the vast majority of cases. There are situations however, where you really need the full programmatic power of JavaScript. That’s where you can use the render function, a closer-to-the-compiler alternative to templates.\n\n**Render functions are impractical for writing your entire application.**\n\n```JavaScript\n// An example render function\n// No need to eval because this function explicitly creates my new element.\n\nrender: function (createElement) {\n  return createElement(\n    s'h1', \t\t\t\t// the element to creat,\n    {},\t\t\t\t\t// A data object corresponding to the attributess\n    [ this.blogTitle ] \t// the list of children to populate this new element\n  )\n}\n```\n\nYou'll notice this is exactly the same as the `code` argument from above.  The template compiler turned our template into a string representation of a render function and tried to `eval()` it.\n\n### Use Single File Components (SFCs)\n\nFrom [the same article](https://vuejsdevelopers.com/2017/05/08/vue-js-chrome-extension/):\n\n\u003e When you use vue-loader to process your .vue file, one of the things it does is use vue-template-compiler to turn your component’s template into a render function.\n\nYou can precompile your entire application.  For this we need.... **webpack!** (and Babel and vue-loader)\n\nLuckily, the webpack configuration much more terse than `vue-cli init` gives you.\n\n```plaintext\n.\n├── build\n│  └── index.build.js\n├── index.html\n├── index.js\n├── manifest.json\n├── package.json\n├── Popup.vue\n├── vue.js\n├── webpack.config.js\n├── yarn-error.log\n└── yarn.lock\n```\n\nThe important parts are:\n\n```JavaScript\n// index.html:\n\n\u003cdiv id=\"app\"\u003e\n    \u003cpopup\u003e\u003c/popup\u003e\n\u003c/div\u003e\n\u003cscript src=\"build/index.build.js\"\u003e\u003c/script\u003e\n\n// index.js:\n\nimport Popup from './Popup.vue'\nimport Vue from 'vue';\n\nnew Vue({\n    el: \"#app\",\n    components: {\n        Popup\n    }\n})\n\n// Popup.vue is boundary of wierdness, where all your current Vue code will work normally.\n```\n\nLoading `/path/to/index.html` as a static file produces a broken page.  In console, you can see:\n\n```\n[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.\n```\n\nIt turns out that `import Vue from 'vue'` grabs a runtime-only build by default.  While there are no templates to explicitly cause `new Function(stringn)` to be executed, vue wants to run `\u003cdiv id=\"app\"\u003e...\u003c/div\u003e` from `index.html` through the compiler and cannot find it.\n\nEven if the compiler were present, we would be right back to the `eval` problem.\n\n**Solution**: Use a render function for *only* the top-level template so `vue-template-loader` is never needed.\n\n`index.js` becomes:\n\n```JavaScript\nimport Popup from './Popup.vue' /* compiled by webpack */\nimport Vue from 'vue'           /* runtime only */\nnew Vue({\n    el: \"#app\", \n    render: createElement =\u003e createElement(Popup)\n})\n```\n\nViola, you're running a Vue.js app as a browser extension!\n\n## So how do I set up my development environment?\n\nWe can't use a development server, and we need to load fully compiled static assets from disk.\n\n`webpack --watch` is perfect for us because during development, the browser fetches all assets from disk every time the application is opened.  For example, when you click the Popup icon, all necessary files are fetched directly from disk.\n\n[Webpack Watch docs](https://webpack.js.org/configuration/watch/)\n\n`webpack --watch` is *very* fast because it only re-compiles the files that change.  The rest of the compilation is kept in process memory.\n\nYour development flow is now as easy as:\n\n1. Make a code change.\n2. Reload the popup (or options) window.  I do this with `window.location.reload()` in the debug console.\n\n## How come I've never had to do that render function jazz?\n\nYou might be using `vue.runtime.js` if you load vue from script tags.\n\nIt's more likely that you started a project with a tool like `vue-cli init`, and the `webpack.conf` it creates contains the following section, aliasing `vue` as the full build:\n\n```JavaScript\nresolve: {\n    alias: {\n        'vue$': 'vue/dist/vue.esm.js'\n    },\n    extensions: ['*', '.js', '.vue', '.json']\n},\n```\n\nThis is why I'm not a fan of boilerplate project generators.\n\n## Other problems\n\n1. Expensive(ish) source maps.  As with before, we cannot use `eval` in our source maps.  According to https://webpack.js.org/configuration/devtool/ `cheap-source-map` is the best we can do.\n2. Long initial build times.  Avoid compiling static assets with webpack if you can avoid it (images and fonts are the worst), and try `DLLPlugin`.\n3. Hot Reload is tricky but not impossible.  [Webpack Chrome Extension Loader](https://github.com/rubenspgcavalcante/webpack-chrome-extension-reloader) is brilliant middleware that translates WebSocket reload notifications to `chrome.runtime` events that your extension can listen for.\n\n## A living example project\n\n`example2_sfc` is great for understanding the basic setup.\n\n[My browser extension, Tusk](https://github.com/subdavis/Tusk) will provide guidance for the gritty details, such as handling static resources and using `DLLPlugin`.  It's also a decent example of how to organize a large browser extension project with Vue.js (maybe?)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubdavis%2Fvuejs-browser-extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsubdavis%2Fvuejs-browser-extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubdavis%2Fvuejs-browser-extensions/lists"}