VueJS Performance Questions - performance

I'm working on a browser extension that uses Vue Cli with Vue Bootstrap. I've already optimized my Vue Bootstrap imports to only load the components and icons I use in the project. I also have lazy loaded route components, but I still see a long time to get to the created hook of my first component. Here's a code extract:
Main entry point
console.info("Loaded in " + (new Date().getTime() - global.start) + "ms")
require("#/App.js")
App.js
import Vue from "vue"
import * as Sentry from "#sentry/vue"
import { Integrations } from "#sentry/tracing"
import App from "#/App.vue"
import router from "#/common/router"
import store from "#/common/store"
import { get } from "#/common/api"
...
import {
ModalPlugin,
ButtonPlugin,
TabsPlugin,
DropdownPlugin,
AlertPlugin,
ToastPlugin,
FormInputPlugin,
FormRadioPlugin,
...
BIconArrowRightShort,
BIconArrowDownSquareFill,
} from "bootstrap-vue"
Vue.use(ModalPlugin)
Vue.use(ButtonPlugin)
Vue.use(TabsPlugin)
...
Vue.component("BIcon", BIcon)
Vue.component("BIconX", BIconX)
Vue.component("BIconArrowLeft", BIconArrowLeft)
Vue.component("BIconMailbox", BIconMailbox)
Vue.component("BIconFolderPlus", BIconFolderPlus)
Vue.component("BIconEnvelope", BIconEnvelope)
...
global.vm = new Vue({
router,
store,
render: h => h(App),
created() {
this.$router.push({ name: "Responses" })
...
})
}
And here's my component file that gets loaded first:
<template>
<div>
<div>
...
</div>
</div>
</template>
<script>
let now = new Date().getTime()
console.info("SFC file loaded in " + (now - global.start) + "ms")
import ... from "#/common/components/..."
export default {
...
mounted() {
let now = new Date().getTime()
...
</script>
<style lang="scss">
...
</style>
When I benchmark times, this is what I get:
SFC file loaded at 46ms (at the top of the script section)
Created Hook starts a 177ms
Mounted Hook starts at 308ms
I'm wondering what takes so long in the created hook (I don't do much, just checking the $route parameters). 150ms to just go through the created hook seems like a lot?
Here's the created hook:
console.info("Created Hook in " + (new Date().getTime() - global.start) + "ms")
if (this.$route.params.xx {
this.... = this.$store.state.xxxx.find(e => {
return e.uuid == .......
})
}
Performance loading the extension is important for the user experience, and it always feels a little sluggish when opening the extension popup.
Any idea on what could delay the loading like that?
Thanks!

The first thing that I notice is that you are doing a route.push on App created hook, that means that the router already solve the first route (probably '/') and after that you are adding another route (but not immediately) and then the router is solving that new route.
For a faster boot why don't you add a redirect to the route:
//...routes
{
path: '/',
redirect: {name: 'Responses'}
}
If you have the opportunity to change to Vue3 then maybe you could also perceive a performance boost since Vue2 has an always present GlobalAPI and Vue3 is doing a tree shaking and ignoring the unused stuff after building.
Note: Make sure you are testing it with a production environment, because if you are using the vue-cli to serve the content then the startup will include a lot of overhead

Thanks guys! Actually the default route is already redirecting to Responses, and removing the push doesn't change much.
Unfortunately I can't really migrate to Vue 3 as I rely on dependencies that do not fully support Vue 3 (Vue BS being an important one).
I'm guessing that's as much as I can do at this point. Just wondering if there's any way with Vue Cli to open the window browser extension popup immediately and load Vue afterwards (right now, it's waiting for the whole thing to be loaded and then opens the popup which gives a 300ms delay between the click and the window actually opening).

There is much more what happens between created and mounted hooks - it's not just the code in all created hooks what is running. Check the Vue component lifecycle
As you are using Vue SFC'c and probably Webpack, template compilation is out of the question but still, after created Vue is executing render functions of all components (producing VDOM) and creating real DOM elements based on VDOM (rendering whole app). So depending on number of components and elements, 150ms is not that bad...

Related

Some vue scripts in a blade page

I would like to add some dynamism to my blade pages. For example if I select a certain value in a select, then make certain inputs of the form appear or disappear.
I told myself that this is an opportunity to put some "vue" functionnalities in my pages, to learn.
To do this I followed what is indicated in the doc here: https://laravel.com/docs/8.x/mix#vue
I installed vue in my project : npm install vue#next
I modified my "webpack.mix.js" in which I specify that I am using vue :
mix.js('resources/js/main_js.js', 'public/js')
.vue()
When I run npm watch : all is OK, I have no errors. Perfect !
Naively I just created a simple script to see if it worked, and before going any further. In a blade page I just added that :
<script>
Vue.createApp({
data() {
return { count: 1 }
},
created() {
// `this` points to the vm instance
console.log('count is: ' + this.count) // => "count is: 1"
}
})
And when I load the page I have this error :
Uncaught ReferenceError: Vue is not defined
This is probably too simplistic an approach. But what am I missing for this little piece of code to work? When I understand the mechanics, I can go further. I'm sure I'm not far away!

Using React Hooks to Show Component Render Time and Date

My application contains a view that is a functional react component. I'm trying to add a timestamp at the bottom of the view displaying the date and time when the user navigated to this view. I'm wondering what would be appropriate way to implement this using hooks. I guess one option would be to use const [date] = useState(new Date()) and simply provide no setter for the state since it would never change. However, this makes me wonder if useState is the appropriate hook to begin with. Should I just do const date = new Date() in the component body or would that have some unexpected side effects?
For what you intend to accomplish, the code below would work just fine:
// solution # 1
function MyComponent() {
const [time, setTime] = useState(new Date());
return (
<div>
View created at {time.toLocaleTimeString()}
</div>
)
}
However, it is important to know that it is not following React rules. That is because we are calling new Date() from inside the component, making it impure. React expects the components to be pure functions. This means that it should return the same thing for the same input. But React cannot guarantee that a function component doesn't have side effects, and that is why that first solution would work just fine.
According to this gist, a better approach for this task would be:
// solution # 2
function MyComponent() {
const [time, setTime] = useState<Date>();
useEffect(() => {
setTime(new Date());
}}, [])
return (
<div>
View created at {time && time.toLocaleTimeString()}
</div>
)
}
Roughly speaking, all side effects should live inside useEffect. So, things like calling an external API or calling impure functions (such as Math.random() or Date.now()) should be there. From my understanding, this solution would be more appropriate.
If we are not obeying React guidelines, the library might not work as advertised. Before version 18, React had only synchronous rendering. This means that the moment the render phase is kicked off (either by a initial render or by a state update), nothing could interrupt it from committing those updates to the browser. In that situation, I don't think the first solution I presented would be problematic. But, in React 18, we have the concurrent mode features by which rendering can be interrupted before the diffs are committed to the screen. With that, React can be more intelligent about how it breaks up all the work it needs to do (for example, we can now define high-priority updates).
In order for these new APIs to work properly, React assumes that the components are pure functions, which means that it has no side effects. These APIs are very new and there are still many more additional features coming in. I am not quite sure about how sensitive they are to function impurity, but I believe our second solution (with all side effects living inside the useEffect) is a better bet for this task.
The following seems to work. Still not sure if this is optimal though.
import React from 'react'
export function View(_props) {
const [viewCreated] = React.useState(Date.now())
return (
<div>
View created {String(new Date(viewCreated))}
</div>
)
}
useState(new Date()) is better. Here is complete component
import React, { useState , useEffect } from 'react'
export const DateTime = () => {
var [date,setDate] = useState(new Date());
useEffect(() => {
var timer = setInterval(()=>setDate(new Date()), 1000 )
return function cleanup() {
clearInterval(timer)
}
});
return(
<div>
<p> Time : {date.toLocaleTimeString()}</p>
<p> Date : {date.toLocaleDateString()}</p>
</div>
)
}
export default DateTime

How to properly add multiple Vue components in app.js in Laravel

So, hi. I've been struggling for 4 hours to add at least two different Vue components to my app.js file which is located in js folder along with these vue components in my Laravel project.
I couldn't find any real solution on the internet since it didn't fix my problem.
I have tried something like
Vue.component('signature-element', require('./components/Signature.vue').default);
under the other component which works perfectly, but the problem is I can't make the app work with 2 or more components.
I've tried installing vue-router via npm and configuring a router but it didn't work too, somehow whole JS stopped without any errors in the command log in browser or in Mix.
I've also tried calling the import function instead of the first one I've mentioned, for example:
const componentVar = import ...;
inside vue:
new Vue({
components: { first, componentVar },
mounted() {}
});
But this also did not work unfortunately.
In the second example you're trying to registrate locally your components.
If you want to do this you can do it like this:
import first from './components/first'
import componentVar from './components/componentVar'
new Vue({
el: '#app',
components: {
'first': first,
'componentVar': componentVar
}
})
Instead if you want to use the first example this means you want to register your components globally. That means they can be used in the template of any root Vue instance (new Vue) created after registration
Example:
Vue.component('signature-element', { /* ... */ })
new Vue({ el: '#app' })
Then in your view
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>

React Admin page not rendering correctly

I am new to UI coding and started using react-admin for putting some simple pages. Everything went well and we are able to host pages correctly. But we have noticed random issues where the background image is filling up the entire screen or sometimes the whole page gets reduced to the hamburger menu. I have disabled the registerServiceWorker to stop having my pages in cache. Not sure if this is causing the weird UI behavior.
I don't know why you get those issues, the description is way too generic and it seems you don't have any idea what the problem can be, probably due to being new to the area. Either way the kind of problem you appear to have is probably related to CSS which is a way give style to your page. But React Admin doesn't use CSS directly, you can use it that way, but for more dynamic way to style the page the Material-ui library uses a thing called JSS to apply the styles.
There are many libraries that are being used together in order to produce React Admin, you should have an understanding of the most important ones in order to do something fancy. My advice to you since you are new, and you pretend to use React Admin, first use what React Admin offers and when you feel comfortable using that components and have a general grasp how the framework works, after that start implementing your own components that don't have a direct relation to React Admin but use some of the same libraries of React Admin.
Also check if you are creating a React Admin app using the <Admin> component or are embedding React Admin in another app since the second is more probable to produce bugs.
After some debugging, I think i figured out the cause of this issue. I had a custom button to duplicate a row (basically post a create and route to edit page on the new id). For some reason, the rendering of that button seems to have caused this issue inconsistently. The actual button works fine but causes this inconsistent behavior. Below is the code for that button. Is there any issue with the below?:
export default class DuplicateButton extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = ({ redirect: false });
var redirectPath = '';
}
handleClick = (props) => {
var
{
push, record, resourceName
} = this.props;
let tempRecord = record;
var result = '';
console.log(this.props);
var p = restDataProvider(CREATE, this.props.resource + "/" + tempRecord.id, { data: tempRecord }).then(resp => {
result = resp.data;
let routePath = '/' + this.props.resource + '/' + result.id;
console.log(routePath);
this.redirectPath = routePath;
this.setState({ redirect: true });
return result;
});
}
render() {
if (this.state.redirect) {
console.log('Redirect to Edit page');
return <Redirect push to={this.redirectPath} />;
}
return <Button variant="flat" color="primary" label="Duplicate Entry" onClick={this.handleClick}><DuplicateIcon /></Button>;
}
}

Inconsistent image move behavior in quilljs with react

i have encountered an issue, when making a text editor with support of image based tags. There is a need to move those tags around freely in the text, which is being made impractical by this issue.
Basically when I start dragging an image, and then drop it on desired location, one of two results can happen: A) it works as intended and B) the image is dropped to the end/beginning of the sentence. You can see the behaviour in attached gif. Resulting behavior
I'm using react and typescript combination for creating the page with quill being inserted in a component.
// TextEditor/index.tsx
import * as React from 'react';
import * as Quill from 'quill';
import { TextEditorState, TextEditorProps } from '../#types';
import { generateDelta } from '../#utils/generateDelta';
const formats = [
'image'
];
class TextEditor extends React.Component<TextEditorProps, TextEditorState> {
constructor(props: TextEditorProps) {
super(props);
this.state = {
Editor: undefined
}
}
componentDidMount() {
const self = this;
this.setState({Editor: new Quill('#editor-container', {formats: formats, debug: 'warn'})});
}
changeText(text: string) {
if(typeof(this.state.Editor) !== 'undefined') {
this.state.Editor.setContents(generateDelta(text), 'api');
}
}
render() {
return (
<div id="editor-container"></div>
);
}
}
export default TextEditor;
And the usage of this component in another component is just
// editor.tsx
import TextEditor from '../QuillEditor/TextEditor';
...
onUpdate(text: string) {
this.refs.targetEditor.changeText(text);
}
...
render() {
return (
...
<TextEditor
ref={'targetEditor'}
/>
...
)
}
I have tried to change the text editor to just contentEditable div and that worked flawlessly, so it shouldn't be because of some css glitch.
Has anyone some idea of what could be causing this?
EDIT Feb 6:
I have found out, that this issue is manifesting only in Chrome, as IE and MS Edge did not encountered this issue. I have tried to switch off all extensions, yet the issue is still there. Private mode also didn't help.
After few days of research I have figured out what is causing the issue.
The combination of Quill and React won't work, because of the way React 'steals' input events, while creating the shadow DOM. Basically, because it tries to process my input in contenteditable div created by Quill, it causes some actions to not fire, resulting in the weird behaviour. And because Quill tries to do it by itself, outside of React DOM.
This I have proved in my simple testing project, where adding a simple input tag anywhere on the page broke down the Quill editor.
Possible solution would be to use react-quill or some other component container, however I haven't managed to make it successfully work, or write some yourself, which would incorporate Quill to React in its DOM compatible way.

Resources