Setup
I've initialized a new project using vite on an Arch based operating system.
When I try to create the simple counter from the vue docs, the elemet doesn't render.
Code
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="counter">
Counter: {{ counter }}
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
main.js
import { createApp } from 'vue'
var CounterApp = {
data() {
return {
counter: 0
}
},
mounted() {
setInterval(() => {
this.counter++
}, 1000)
}
}
createApp(CounterApp).mount('#counter')
When I inspect the element it is commented out:
Question
Why is that? And how to resolve the error?
Doing that replaces the normal mounting process, and treats the root element like a template string for the App component. Since template strings require the runtime compiler, you would need to use a full build. There should be a console warning about that.
To avoid increasing the size of your app (by ~30%) with the full build, it's recommended to leave the mounting point untouched, and give the App component its own proper template:
index.html
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<template>
<div id="counter">
Counter: {{ counter }}
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
counter: 0
}
},
mounted() {
setInterval(() => {
this.counter++
}, 1000)
}
}
</script>
By default the runtime compiler is not included in the Vue build.
To include it, add the following resolve.alias configuration:
vite.config.js
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
},
},
})
Docs https://vitejs.dev/config/#resolve-alias
Related
I am new to Vue, Vue Router, and Vuex.
Please advise the best practice to change/update values in the component nested in the Vue Router, using Vuex store.
I wrote a simple code to change the number in the component by hitting the button but seems not working properly.
The following is the code I made:
// Trying to change the value in the component,
// but not updating the DOM, only the value in the state
function incrementValueInComponent(){
store.commit('increment');
alert("number in the store is: " + store.state.testNumber);
}
// *** Vuex store ***
const store = new Vuex.Store({
state: {
testNumber: 0
},
mutations: {
increment (state) {
state.testNumber++
}
}
});
// *** Component and Vue router ***
const Foo = {
store:store,
data(){
return {
myNumber:this.$store.state.testNumber
}
},
template: '<div>{{myNumber}}</div>' ,
}
const routes = [
{ path: '/', component: Foo }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- Vue, Vuex, Vue router-->
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/">Top Page</router-link>
</p>
<router-view></router-view>
<button class="btn" onclick="incrementValueInComponent()">Increment Number</button>
</div>
<script src="javascript.js"></script>
</body>
</html>
Am I doing something wrong or is this approach something against the concept of the Vue?
Any advice would be appreciated. Thanks in advance.
use computed
const Foo = {
store:store,
computed: {
myNumber () {
return this.$store.state.testNumber
}
}
template: '<div>{{myNumber}}</div>' ,
}
I'm trying to add a clucth.co widget to a Gatsby site, but it does not render. I've tried using react Helmet for the <script> part, but it still does not work.
Hopefully I'm missing something simple here, but looking at other solutions I can't find anything that works.
For reference: https://clutch.co/content/add-review-widget-your-website
<script type="text/javascript" src="https://widget.clutch.co/static/js/widget.js"></script>
<div className="clutch-widget" data-url="https://widget.clutch.co" data-widget-type="7" data-height="65" data-clutchcompany-id="XXXXXXX"></div>
You have multiple ways of inserting a third-party script in Gatsby. The problem you'll face in all of them is that you need to await that your div:
<div className="clutch-widget" data-url="https://widget.clutch.co" data-widget-type="7" data-height="65" data-clutchcompany-id="XXXXXXX"></div>
Needs to be rendered your script won't be able to load.
Using Script component (2022 update)
Since the release of the Script Gatsby component (powered by Partytown) it's much easier adding third-party scripts. Just:
import React from "react"
import { Script } from "gatsby"
function YourPage() {
return <Script src="https://my-example-script" />
}
export default YourPage
Using Helmet:
You said you already tried but it should. You may need to try the drop-in support that adds the gatsby-plugin-react-helmet. Then:
<Layout>
<SEO title="Live" />
<Helmet>
<script src="https://tlk.io/embed.js" type="text/javascript"/>
</Helmet>
</Layout>
Check the compatibility issues when used with hooks.
Using onRenderBody API from gatsby-ssr.js:
Gatsby exposes a setHeadComponents function in the onRenderBodyAPI that you can take advantage from:
import React from "react"
export const onRenderBody = ({ setHeadComponents }, pluginOptions) => {
setHeadComponents([
<script key="tracking"
src="https://widget.clutch.co/static/js/widget.js
type="text/javascript"
async
/>,
])
}
This snippet above will insert the <script> in the <head> tag of the compiled HTML.
Here you have another approach using dangerouslySetInnerHTML:
setHeadComponents([
<script dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])
Extracted from Unable to Inject 3rd Party Scripts in Gatsby
Modifying directly the html.js:
You can customize even more the output of the resultant HTML by modifying the html.js, the boilerplate that uses Gatsby to build your entire site.
Run:
cp .cache/default-html.js src/html.js
Or alternatively, copy the default-html.js from .cache folder into /src and rename it to html.js. When compiling, if the html.js is present, Gatsby will take it to build your site based on that skeleton.
You'll have something like:
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
</body>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
There you can add your <script> directly:
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<script type="text/javascript" src="https://widget.clutch.co/static/js/widget.js"></script>
{props.headComponents}
</head>
Using gatsby-plugin-load-script:
Just install and use the plugin:
{
resolve: 'gatsby-plugin-load-script',
options: {
src: 'https://widget.clutch.co/static/js/widget.js',
},
},
Hacking the gatsby-browser.js API:
If none of the above fits you, you can still use one of gatsby-browser.js APIs (onClientEntry) to manually add script given a source URL:
const addScript = url => {
const script = document.createElement("script")
script.src = url
script.async = true
document.body.appendChild(script)
}
export const onClientEntry = () => {
window.onload = () => {
addScript("https://widget.clutch.co/static/js/widget.js")
}
}
In order to not have the clutch widget disappear on route changes, I ended up running the Init and Destroy methods from window.CLUTCHCO myself in useEffect.
React.useEffect(() => {
// add widget to end of body and run it
const script = document.createElement("script")
script.type = "text/javascript"
script.src = "https://widget.clutch.co/static/js/widget.js"
script.async = true
document.body.appendChild(script)
// run script
script.onload = () => {
// #ts-expect-error Apparently we have to manually do this!! 🗑️
// eslint-disable-next-line #typescript-eslint/no-unsafe-member-access, #typescript-eslint/no-unsafe-call
window.CLUTCHCO.Init()
}
return () => {
// #ts-expect-error Apparently we have to manually do this!! 🗑️
// eslint-disable-next-line #typescript-eslint/no-unsafe-member-access, #typescript-eslint/no-unsafe-call
window.CLUTCHCO.Destroy()
document.body.removeChild(script)
}
}, [])
I have a basic Example Component setup which is binded to a Vue Instance like so
<template>
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
{{ msg }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data : function(){
return{
msg : "Hello"
}
},
mounted() {
console.log('Component mounted.')
}
}
</script>
And this is my app.js file
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('example-component', require('./components/ExampleComponent.vue'));
var firstComponent = new Vue({
el: '#app'
});
This is my HTML
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
</head>
<body>
<div id="app">
<example-component></example-component>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
How can I now change the value of msg? I don't have any reference to that component anywhere? How can I do that?
Is this possible?
var ex = Vue.component('example-component', require('./components/ExampleComponent.vue'));
ex.data = "new Value";
add props property to your component and set inside it the msg property :
<script>
export default {
props:{
msg:{
type:String,
default:"Hello"
}
},
data : function(){
},
mounted() {
console.log('Component mounted.')
}
}
</script>
and
<example-component msg="new Hello"></example-component>
UPDATE
after understanding your use case i recommend to use child component ref
const ExampleComponent = Vue.component('example-component', {
template: `
<div>
<h2>Example component</h2>
<div>{{msg}}</div>
</div>
`,
data() {
return {
msg: "Hello"
}
}
});
window.root = new Vue({
el: '#app',
components: {
ExampleComponent
}
});
root.$refs.example.msg = "new hello"
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Parent</h2>
<example-component ref="example"></example-component>
</div>
</body>
</html>
TL;DR Have a look at this example, it does what you want: https://codesandbox.io/s/88ojwpjq88
You need to distinguish between data (the components private data) and props (data passed to the child component). Read this section of the Vue.js guide: https://v2.vuejs.org/v2/guide/components.html
In your component, you'll need to declare the prop:
export default {
props : ['msg'],
mounted() {
console.log('Component mounted.')
}
}
Then you can bind a data to the child component like this:
<example-component :msg="myMessage"></example-component>, assuming the parent component has myMessage declared like this:
data : function(){
return {
myMessage : "Hello"
}
}
To change the value, you can bind it to some sort of input. Here's an example w/ a text field: <input v-model="myMessage">.
If you enter sth. into this field, you should see the bound value in your component update.
Do read the manual here: https://v2.vuejs.org/v2/guide/index.html. It's really good, your questions are covered entirely in there.
Add the property to your component, make it parent-to-child, that is, you will bind the property within the component, and you can pass data to that property.
Depending on the flow, you must identify how you will do this, whether it is from father to son or from child to father. To pass data to child, you pass through props, if you pass from child to parent, you will use $ emit.
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
If you use parent to child you will do it the following way.
Pai.vue
<template>
<div> {{msg}} </div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: 'Hello'
}
}
}
</script>
Ai Inside the child component you call the parameter
<template>
<div>
<parent :msg="valueMsg"/>
</div>
</template>
enter code here
son.vue
<script>
import pai from './pai'
export default {components: {parent},
data: () => ({
valueMsg = 'Hello Test'
})
}
https://jsfiddle.net/hamiltongabriel/3zr8jb7r/
I want to create basic sample of single file component in vue. I have configured webpack to compile my code and it works fine. Now i want to pass props to component and i get error that props is undefined.
index file
<head>
<meta charset="UTF-8">
<title>Vue Webpack Demo</title>
<script type="text/javascript" src="/dist/vue.js"></script>
</head>
<body>
<div id="mainContent">
<main-content post-title="hello!"></main-content>
</div>
</body>
<script src="/dist/index.js"></script>
index.js file
import Vue from 'vue';
import MainContent from './views/main-content';
let MainComponent = Vue.extend(MainContent);
new MainComponent().$mount("#mainContent");
main-content.vue
<template src="./main-content.html"></template>
<style scoped lang="scss" src="./main-content.scss"></style>
<script>
export default {
name: "main-content",
props: {
postTitle:{
type:String,
required:true
}
},
data: () => ({
webpack: 'Powered by webpack!?',
name:'name'
}),
}
</script>
The way you are setting up the application is awkward. There is no wrapper for the application. Following the following example to see how it can be orchestrated to finally have your component with the required prop
The index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>CodeSandbox Vue</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
The main.js where the vue application is created:
import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: "#app",
components: { App },
template: "<App/>"
});
Now the App uses your component MainContent and passes the prop:
<template>
<MainContent post-title="Hello!"/>
</template>
<script>
import MainContent from "./views/MainContent";
export default {
name: "App",
components: {
MainContent
}
};
</script>
Finally the component reads the prop:
<template>
<div class="hello">
post-title: {{ postTitle }}
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
postTitle: {
type: String,
required: true
}
},
};
</script>
You can see this example working here
I am trying to put together Webpack 4.5 and Vue components. The build is fine and I see on the screen the expected two components (details below).
Creating this basic SPA was a very iterative process, with information gathered from various sources (not always consistent). I finally end up with:
an HTML file which has one Vue component, a wrapper for the whole SPA
the Vue component above, which itself brings in the actual useful components
Is there a way to skip the wrapper component so that I can directly edit the HTML file (and link a CSS style file)? I would then define a CSS grid and place the components within.
Or is there an advantage to keep it this way I do not foresee?
The current project files:
the HTML file opened in the browser
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<infoscreen id="infoscreen"></infoscreen>
</body>
<script src="bundle.js"></script>
</html>
the webpack config file
'use strict'
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
mode: 'development',
entry: [
'./src/entry.js'
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new CopyWebpackPlugin([{
from: 'src/infoscreen.html',
to: 'infoscreen.html'
}])
]
}
the entry.js file
import infoscreen from "./infoscreen.vue"
import Vue from "vue/dist/vue.js"
Vue.component("infoscreen", infoscreen)
new Vue({ el: "infoscreen" })
the Vue wrapper component (infoscreen.vue)
This is the file I would like to get rid of and use <temperature-outside></temperature-outside> directly in the HTML file above
<template>
<div id="app">
<temperature-outside></temperature-outside>
<temperature-outside></temperature-outside>
</div>
</template>
<script>
import temperatureOutside from './temperatureOutside.vue'
export default {
name: 'infoscreen',
components: {
'temperature-outside': temperatureOutside
}
}
</script>
the Vue component (temperatureOutside.vue)
<template>
<div>
hello {{yo}}
</div>
</template>
<script>
export default {
name: 'temperatureOutside',
data: function () {
return {
yo: "world"
}
}
}
</script>
Vue components are recognized within the context of a Vue instance. It turns out you will need a mounting point in your html which the wrapping or parent Vue component will mount all child components registered.
Registering Components