I'm trying to create an Chrome extension with web accessible resources. Since my extension tries to create a modal with a list of available data, I decided to use VueJS to handle the dynamic nature of content.
However, When I create the sample files and initialize VueJS, it simply leads to all the DOM being commented out and my app not working.
Here's the code I'm using:
web_resources/vue.html
<html>
<head>
<script type="text/javascript" src="/web_resources/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script type="text/javascript" src="/web_resources/app.js"></script>
</body>
</html>
web_resources/app.js
window.app = new Vue({
el: '#app',
data: {
message: 'Hello World'
}
})
When I run this, the entire div is replaced with: <!----> and nothing works.
On searching online for VueJS inside iframe, I came across this post which has this fiddle which surprisingly produces a blank page for me with no content. On inspecting the result, I find that the div has been replaced with <!----> here as well.
Why is VueJS not initializing properly within an iframe?
The problem was Content Security Policy (CSP). Since I was using a local copy of vue.min.js, I couldn't see any of the error messages. It looked like everything was initializing as expected except it wasn't. Once I replaced this with a un-minified version, I saw an error on the lines of:
I simply had to go update the CSP in manifest.json which I was able to do by adding:
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
Related
This is a rookie question. I'm trying to take some baby-steps into Electron, Vue, Webpack, and Node. To that end, I've used electron-forge to spool out a boilerplate project as a starting point, like this:
npx create-electron-app my-project --template=typescript-webpack
After the project has been created everything (seemingly) works as expected. If I make any edits I can see webpack is invoked and reloading the content reveals my edits. So far so good.
My next step was to introduce the simplest Vue.js 'hello world' content I could. First, I install Vue.js using NPM, like this:
npm install "vue"
I then edit the boilerplate index.html to look like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<h1>💖 Hello World!</h1>
<p>Welcome to your Electron application.</p>
<div id="vue-app">
{{ message }}
</div>
<script>
var app = new Vue({
el: '#vue-app',
data: {
message: 'This message is from Vue!'
}
})
</script>
</body>
</html>
Which does not work. If I change the script tag to use the CDN for the Vue.js script (instead of the node_modules folder), everything works as expected.
My conclusion is that although I can reference Vue.js in my node_modules folder at design time that location does not exist in my output at run time. I'm not certain if that is due to how webpack is configured, or due to how electron works - but it strongly implies there must be something I need to do, either programmatically or via the webpack configuration to properly reference the script.
So what is the right way to 'reference' the local Vue.js script?
Thanks!
You need to reference vue via some bundler like webpack (otherwise, like you said, it is not available at runtime). Your method won't work because the generated file structure isn't the same as the one you have during 'design time'
Here's an example with vue-cli which sets up a starter project:
npm i -g #vue/cli
vue create project-name
cd project-name
vue add electron-builder
npm install
Done. Your project is operational.
With one little caveat you would encounter later, if you are using vue-router. Add the following in router.js (it changes router mode to hash instead of history so it works with electron).
export default new Router({
mode: process.env.IS_ELECTRON ? 'hash' : 'history',
})
As a sidenote, vue-cli abstracts away a lot of config. If you ever get lost, just print it out with
vue inspect > ./app/inspect.js.md
(The part after > means save to the named file, otherwise it would print it out in your console.) Check it out, that's the correct set up you're looking for, just auto created with vue-cli.
Also, check out the generated /public/index.html, it has no mention of importing vue ;)
Best of luck
After some tinkering, I came up with this solution:
The HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<h1>💖 Hello World!</h1>
<p>Welcome to your Electron application.</p>
<div id="vue-app">{{ message }}</div>
</body>
</html>
I then added an import statement to the electron-forge boilerplate renderer.ts, like this:
import './vueapp.js';
and finally, I created a new script file called vueapp.js like this:
import Vue from 'vue/dist/vue.js';
var app = new Vue({
el: '#vue-app',
data: {
message: 'Hello Vue!'
}
});
I welcome feedback and comments; this may or may not be the correct approach and would love to hear how others have done this.
I'd like to use Vue.js within one page of a large legacy application. The idea is to replace the old JS+jQuery hodge-podge within a single page -- but leave the rest of the app (many other pages) untouched. So, not interested in using NPM, Node, Vue CLI, Webpack, Babel, etc., just yet.
This is a proof-of-concept before we invest in refactoring the entire frontend of the application.
The approach we followed was to include vue.js via as explained here: https://v2.vuejs.org/v2/guide/installation.html#Direct-lt-script-gt-Include in that one page, and the use Vue only within that one page. This is the general page layout:
<html>
<head>
...
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
...
</head>
<body>
<div id="el">
... vue template ...
</div>
<script>
...
var vm = new Vue({
el : '#el',
data : {
config : <% config.json %> // this is server-rendered, using server templating
...
},
...
});
...
</script>
</body>
</html>
The page does work. However, I get the following error/warning within the Vue console:
Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed.
Although I'd rather not, I can certainly move the page-specific JS to its own separate file (and that does eliminate the warning/error). However, I wouldn't be able to set vm.config with server-provided data along with the loaded page by using server-side template, e.g. config : <% config.json %>. I know I could GET it using JS separately, after pageload, via an AJAX call directly from the server, but for practical reasons I'd like to avoid doing that here.
I'd greatly appreciate any suggestions on how to get this to work nicely. I'm also open to other suggestions with regard to this general pattern, that don't involve retooling the front-end just yet.
And perhaps the answer is to ignore the warning, or somehow disable it, given the page does work as intended...
Thank you!
One simple solution here is to write it to the global window object. IIRC SSR frameworks like Angular universal/Nuxt/Next/... all use this approach.
window.__STATE__ = <% config.json %>
In your JS file you can then refer to the window.__STATE__ object.
var vm = new Vue({
el: '#el',
data: {
config: window.__STATE__
}
})
Ofcourse the order is important here:
<body>
<script>
window.__STATE__ = <% config.json %>
</script>
<script src="app.js"></script>
</body>
Grrr, after several days after enduring this error, I discovered this:
<fieldset id="el">
...
<div id="el">
...
</div>
...
</fieldset>
So the issue was repeating #el within same page.
My mistake.
Just wish the error message emitted by Vue had been a bit more useful!
Bottom line: The pattern described in the origional question works just fine without NPM/CLI.
I'm trying to implement VueJS in one of my pages blade template. I just want to use Vue in that single template.
I tried the following:
default.blade.php
//...
#yield('js-for-layout-before')
<script src="{{ asset('js/vue.min.js') }}"></script>
// ...
view.blade.php
#extends('layouts.default')
//...
#section('content')
<div id="app">
<ol>
<todo-item></todo-item>
</ol>
<p>#{{ message }}</p>
</div>
<script type="module" src="{{ asset('js/pages/vue_component.js') }}"></script>
#stop
vue_component.js
Vue.component('todo-item', {
template: '<li>This is a list item</li>'
})
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
With this, I am only getting a blank page. There are no errors in the console, all files are loaded.
The inspector shows what I mean:
I do not want to implement a build step or anything, just a minimum setup with vue. Any ideas what I am missing here?
--UPDATE:
I replaced vue.min.js with the development version and finally got a clue:
vue.js:633 [Vue warn]: It seems you are using the standalone build of
Vue.js in an environment with Content Security Policy that prohibits
unsafe-eval. The template compiler cannot work in this environment.
Consider relaxing the policy to allow unsafe-eval or pre-compiling
your templates into render functions.
For reference, the issue was that my CSP didn't allow for unsafe-eval, which is required in this scenario without a build step. I didn't notice this until I included vue.js instead of the minified vue.min.js, which doesn't show errors.
To fix this for now, I did the following, though you should probably not do this in production: Check where your CSP is defined and add 'unsafe-eval' to "script-src".
In production, you probably want to use nonce on the script instead of unsafe-eval and something like https://github.com/spatie/laravel-csp to generate the nonce.
When pages with Vue components are translated via chrome's translate option, the vue components stops re rendering and updating the view.
Ex: Translate https://v2.vuejs.org/v2/guide/#Handling-User-Input on chrome using the translate option from chromes's context menu into a different language, the reverse message demo stops working.
Since Google translate plugin updates DOM outside of Vue's control, this is sort of expected. Looking for any work arounds that let both co-exist. The sections can be marked with "notranslate" class but that would mean it is no longer translatable.
React inspite of being based on virtual DOM, works even with DOM being modified by translate plugin.
A possible workaround is to use the Vue special attributes key (as described here) and ref (as described here).
Here it is an example I did, starting from the link you provide above:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue.js Reverse Example</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-5" class="demo">
<!-- Adding Vue attributes here -->
<p :key="message" ref="msg">{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
<script>
var vm = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
// vm.$refs.msg.innerText retrieves the translated content
this.message = vm.$refs.msg.innerText.split('').reverse().join('')
}
}
})
</script>
</body>
</html>
As you may notice, the DOM element where you'd like to maintain the Vue reactive behavior (i.e.: the reverse operation here), has been enriched with both a key attribute and a ref one.
The idea here is to use:
:key to force replacement of the element instead of reusing it;
ref to register a reference to the element: it is used in reverseMessage method in order to get the translated innerText content after the Google translation is performed.
For sure, this workaround would affect the performance, but at least it provides the expected behavior (i.e.: the reverse function properly working also after a page translation).
I have a test jsp with:
<head>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js" type="text/javascript">
</script>
<script type="text/javascript">
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeContextMenu");
</script>
</head>
<body>
<div dojoType="TreeSelector" widgetId="treeSelector"></div>
<div dojoType="Tree" widgetId="treeWidget" selector="treeSelector"toggler="wipe">
<div dojoType="TreeNode" widgetId="1" title="First node" isFolder="false"></div>
<div dojoType="TreeNode" widgetId="2" title="Second node">
<div dojoType="TreeNode" widgetId="2.1" title="Second node First Child"></div>
<div dojoType="TreeNode" widgetId="2.2" title="Second node Second Child"></div>
</div>
<div dojoType="TreeNode" widgetId="3" title="Third node" isFolder="false"></div>
</div>
This will not work in any browser.
I thought this would be easy, it seems the dojo library is not being downloaded/found?
Do I need to do anything else?
Also, my IDE, JDeveloper, reports that the attribute "dojoType" is not defined on element div.
I have to say, this example looks like it is taken from a very old version of dojo, but you're trying to run it against Dojo 1.5. That most likely won't work. dojo.widget hasn't existed since...0.4, 0.9 maybe.
You may be right in your comment to the previous answer in that no parseOnLoad: true was necessary in the original example, but I'd also assure you that that example was not running any version of Dojo anywhere near what you're running it with.
Based on what you're looking at there, you may want to start somewhere like here: http://www.dojotoolkit.org/reference-guide/dijit/Tree.html
I'm not sure what the default behavior is when it's not present, but you probably need to define a djConfig with parseOnLoad set to true (or call the parser directly). See the following links for more information:
http://docs.dojocampus.org/djConfig
http://dojocampus.org/content/2008/03/08/the-dojo-parser/
Follow the:
Google AJAX Libraries API Dev Guide,
and the Google API Loader's Guide.
You need to:
register for an API key (or use a direct link as you did),
if not using a direct link but google.load, you need to defer the execution of your code using an onload callback.
Personally, I would just do something like:
within the <head> section of my.html:
<script type="text/javascript" src="http://www.google.com/jsapi?key=MY_API_KEY_GOES_HERE"></script>
<script type="text/javascript" src="my.js"></script>
in my.js:
google.load("dojo", "1.5", {
uncompressed: true
});
function OnLoad() {
/* do stuff here */
}
google.setOnLoadCallback(OnLoad);