Delayed loading of css on page refresh (Webpack) - css

I have splitted webpack configurations, common, development and production configs. I load also jQuery via webpack. The problem is that, on page refresh, there is short delay for about 200-300ms before css is loaded, so my page is firstly shown without it, and then reloads with css. What could be the problem?
In common, under plugins, I have:
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
In development config I have:
{
test: /\.scss$|\.css$/,
use: [
{
loader: "style-loader",
options: {sourceMap: true}
},
{
loader: "css-loader",
options: {sourceMap: true}
},
{
loader: "sass-loader",
options: {sourceMap: true}
}
]
},
and for production mode:
optimization: {
minimizer: [
new UglifyJSPlugin({
sourceMap: true,
uglifyOptions: {
mangle: false,
compress: {
inline: false
}
}
}),
new OptimizeCssAssetsPlugin({
cssProcessor: require('cssnano'),
cssProcessorOptions: {parser: _safe, discardComments: {removeAll: true}},
canPrint: true
})
]
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
]
}
EDIT
Here is compiled html file:
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=UTF-8>
<meta name=viewport content="width=device-width">
<meta http-equiv=X-UA-Compatible content="ie=edge">
<link rel="shortcut icon" href="favicon.png">
</head>
<body>
<div id=app></div>
<script type="text/javascript" src="main.bundle.js?32313079e3d03749d814"></script>
</body>
</html>

Maybe it is too late to answer this question, however, that is not related to Webpack. You have attached your bundle at the end of the page. Therefore before loading the bundle the page content comes up and after that bundles loaded the style. Therefore until it loads the completely there is a little time that takes time for the browser to run the bundle and attach the style. It can be fixed by putting the bundle in the <head> tag. You may need to run your jquery codes inside $(document).ready() function by passing a callback as an argument. Pay attention that it can affect the load time of your page as well.
Another solution can be configuring Webpack to load the styles first and then your js codes.

Related

Vue component not showing

I followed the series codetube "youtube clone" and I did everything like the Alex but the Vue component not working. I am not working on localhost but on server. I would be very glad for any suggestions.
My app.js
require('./bootstrap');
Vue.component('videoup', require('./components/VideoUpload.vue'));
const app = new Vue({
el: '#app'
});
My VideoUpload.vue file:
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Upload</div>
<div class="panel-body">
...
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
}
}
</script>
My blade file:
#extends('layouts.app')
#section('content')
<videoup></videoup>
#endsection
My app.blade file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<!-- <link href="/css/app.css" rel="stylesheet">-->
<link rel="stylesheet" href="/css/app.css">
<!-- Scripts -->
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
</head>
<body>
<div id="app">
#include('layouts.partials._navigation')
#yield('content')
</div>
<script src="/js/app.js"></script>
</body>
</html>
My gulfpile.js:
const elixir = require('laravel-elixir');
require('laravel-elixir-vue-2');
require('laravel-elixir-webpack-official');
elixir((mix) => {
mix.sass('app.scss')
.webpack('app.js');
});
My webpack.config.js:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map'
};
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map',
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
};
It's difficult to debug your setup because I have no idea what tutorial you followed, how you are bundling your code (webpack or browserify) or what build tools you are using (gulp, elixir etc), but I think the most important thing is to understand how Vue works, and then you will be able to better understand how to solve this yourself.
Firstly, vue has two builds - a standalone build and a runtime-only build. The difference between these is that the standalone build includes a template compiler and the runtime-only build does not.
Render Functions
Vue compiles templates in to render functions to work (which are just javascript functions), it doesn't use HTML at all, so if you haven't written a render function or you haven't pre-compiled your components (using .vue files and a bundler like browserify or webpack) then you must use the standalone build; and this includes the base component itself, so the important thing to know is:
If you are trying to use a component inside anything other than a .vue file you need to use the standalone build.
because you need the compiler to turn the HTML into a render function.
So, looking at your code, you are trying to use your component inside a .blade.php file, which isn't a single file component so you will need the standalone build in your project.
When using npm, vue imports the runtime-only build by default:
// ES6
import `Vue` from `vue` // this imports the runtime-only build
// ES5
var Vue = require('vue'); // this requires the runtime-only build
But you need to make sure you are using the standalone build, and how you do this depends on whether you use webpack or browserify. If you are using webpack, you need to add the following to your webpack config:
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
}
If you are using browserify you will need to add the following to your package.json:
"browser": {
"vue": "vue/dist/vue.common"
},
And also make sure that resources/assets/views/layouts/app.blade.php wraps everything in a div with the id app:
...
<body>
<div id="app">
...
</div>
</body>
...
Update
Based on your webpack config it looks like your issue is here:
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
This says you are compiling main.js in the src folder and outputting it to the dist folder as build.js.
Laravel uses a different structure, so you will need to change this to:
entry: './resources/assets/js/app.js',
output: {
path: path.resolve(__dirname, './public/js'),
publicPath: '/public/',
filename: 'app.js'
},
This now says, compile resources/assets/js/app.js and output the file to public/js/app.js. I don't use webpack myself so that may need a little tweaking, but that should get your project up and running.
I had the same issue. the following solved it for me..
I changed:
require('./components/mycomponent.vue')
to:
import MyComponent from "./components/mycomponent.vue";

How to stop FOUC when using css loaded by webpack

I am getting FOUC when loading css inside of my entry point when using webpack. If I remove my css from being loaded by webpack and just include it in my html file as a normal link then the problem with FOUC goes away.
Note: This not just with bootstrap framework, I have tested with
Foundation and Materialize with the same results
The code posted below is just a test example of my problem using Bootstrap.
Html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Navbar example</h1>
</div>
</div> <!-- /container -->
<script src="build/bundle.js"></script>
</body>
</html>
bootstrap.js main entry point
import "../node_modules/bootstrap/dist/css/bootstrap.css";
import bootstrap from 'bootstrap'
$(document).ready(function () {
console.log('bootstrap loaded')
});
webpack.config.js
var path = require('path');
const ProvidePlugin = require('webpack/lib/ProvidePlugin');
const webpack = require("webpack");
module.exports = {
entry: './src/bootstrap.js',
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js'
},
resolve: {
extensions: ['', '.js']
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
'window.jQuery': 'jquery'
})
],
devtool: 'inline-source-map',
module: {
resolve: {
modulesDirectories: ['node_modules']
},
loaders: [
{
test: path.join(__dirname, 'src'),
loader: 'babel-loader',
query: {
presets: ['es2015']
}
},
{ test: /\.css?$/, loader: 'style!css'},
{ test: /\.html$/, loader: 'html' },
{ test: /\.(png|gif|jpg)$/, loader: 'url', query: { limit: 8192 } },
{ test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url', query: { limit: 10000, mimetype: 'application/font-woff2' } },
{ test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url', query: { limit: 10000, mimetype: 'application/font-woff' } },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' },
]
}
};
ExtractTextWebpackPlugin will allow you to output your CSS as a separate file rather than having it embedded in your JS bundle. You can then include this file in your HTML, which as you said, prevents the flash of unstyled content.
I'd recommend only using this in production environments, as it stops hot-loading from working and makes your compile take longer. I have my webpack.config.js set up to only apply the plugin when process.env.NODE_ENV === "production"; you still get the FOUC when you're doing a development build/running the dev server, but I feel like this is a fair trade off.
For more information on how to set this up, take a look at SurviveJS's guide.
Update: As noted in the comments, ExtractTextWebpackPlugin has now been superceded by mini-css-extract-plugin - you should use that instead.
A bit late to the party, but here's how I do it.
While I recognize the merits of extract-text-plugin, it's plagued by a rebuild bug that messes up css order, and is a pain to set up. And setting timeouts in js is not something anyone should be doing (it's ugly and is not guaranteed 100% to prevent fouc)...
So my index.html is:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<style>
#app { display: none }
</style>
<title></title>
</head>
<body>
<div id="app"></div>
<script src="scripts/bundle.js"></script>
</body>
</html>
Then, in client.js at the very end I add:
include "./unhide.css";
...and unhide.css contains a single line:
#app { display: block }
Voila, you see nothing until the whole app is loaded.
It's janky, but I wrap ReactDom.render() in a setTimeout() in my root index.js file.
setTimeout(ReactDOM.render(...), 0)

Gulp Critical CSS and Twig Files

I'm trying to use this for applying critical css into my twig files.
I've had this working perfectly before but i've updated to gulp 4 and restructured my gulp file(s). i'm trying to also include multiple dimensions.
Any help would be much appreciated.
var critical = require( 'critical' ).stream;
function critical() {
return critical( plugins.path.join( project.paths.base ) )
.pipe( critical.generate({ inline: true, base: project.paths.views, dest: plugins.path.join( project.paths.views, 'html-header.twig' ), src: plugins.path.join( project.paths.build, 'css/style.min.css' ),
dimensions: [{
height: 200,
width: 500
}, {
height: 900,
width: 1200
}] }) )
.pipe( gulp.dest( project.paths.views ) );
}
gulp.task( 'critical-css', critical );
Here is my gulpfile.js
'use strict';
/**
* Project Setup
*
* Setting up variables for project name and directories
*/
/******************************************************************************
| > PLUGINS
******************************************************************************/
global.gulp = require('gulp');
var HubRegistry = require('gulp-hub');
var hub = new HubRegistry(['tasks/{,**/}*.js']);
global.plugins = {
path: require('path'),
rename: require('gulp-rename'),
changed: require('gulp-changed'),
concat: require('gulp-concat'),
browserSync: require('browser-sync'),
sourcemaps: require('gulp-sourcemaps')
};
/******************************************************************************
| > PROJECT VARIABLES
******************************************************************************/
global.project = {
name: 'base',
url: 'localhost:8888/base/',
phpSource: ['**/*.php', 'page-templates/**/*.php', '!library/**/*', '!wpcs/**/*', '!node_modules/**/*', '!vendor/**/*', '!assets/bower_components/**/*', '!**/*-min.css', '!assets/js/vendor/*', '!assets/css/*', '!**/*-min.js', '!assets/js/production.js'],
paths: {
base: __dirname,
build: plugins.path.join( __dirname, 'build' ),
src: plugins.path.join( __dirname, 'src' ),
vendors: plugins.path.join( __dirname, 'vendor' ),
tasks: plugins.path.join( __dirname, 'tasks' ),
views: plugins.path.join( __dirname, 'views' ),
assets: plugins.path.join( __dirname, 'assets' ),
assets: {
sass: plugins.path.join( __dirname, 'src/sass' ),
images: plugins.path.join( __dirname, 'assets/img' ),
js: plugins.path.join( __dirname, 'assets/js' ),
css: plugins.path.join( __dirname, 'assets/css' )
}
}
};
gulp.registry(hub);
gulp.task('images', gulp.series('image-fallback', 'image-optimise', 'image-responsive'));
The critical css needs to go into html-header.twig
<!doctype html>
<!--[if lt IE 7]><html class="no-js ie ie6 lt-ie9 lt-ie8 lt-ie7" {{site.language_attributes}}> <![endif]-->
<!--[if IE 7]><html class="no-js ie ie7 lt-ie9 lt-ie8" {{site.language_attributes}}> <![endif]-->
<!--[if IE 8]><html class="no-js ie ie8 lt-ie9" {{site.language_attributes}}> <![endif]-->
<!--[if gt IE 8]><!--><html class="no-js" {{site.language_attributes}}> <!--<![endif]-->
<html id="csstyle">
<head>
<meta charset="{{site.charset}}" />
<title>
{% if wp_title %}
{{ wp_title }} - {{ site.name }}
{% else %}
{{ site.name }}
{% endif %}
</title>
<meta name="description" content="{{site.description}}">
<link rel="stylesheet" href="{{site.theme.link}}/style.css" type="text/css" media="screen" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{function('wp_head')}}
I can't post images because of my small reputation.
Here is my file structure:
File Structure
When I run gulp critical-css in the terminal i get this error
[10:47:17] The following tasks did not complete: critical-css
[10:47:17] Did you forget to signal async completion?
I'm using version 0.7.1 of Critical
Thanks Jake.
Looking at the first four lines:
var critical = require( 'critical' ).stream;
function critical() {
return critical( plugins.path.join( project.paths.base ) )
.pipe( critical.generate(
You define a function and a var both named critical. Name you function criticalCss or anything else.
You're using path.join with a single argument. Probably missing something here.
critical.stream can only take an options object, not a path. You want return gulp.src instead of return critical.
critical.generate doesn't return a stream.Transform, so you can't pass it to stream.pipe(). You want critical({}) like in this example.

Build to style tag using webpack

In my webpack project, I want to inject styles as a <style> tag into the page, using JavaScript, rather than referencing an external file via a <link> tag. I am using style-loader for styles currently, and it outputs a bunch of link tags in the head.
Rather than this:
<html>
<head>
<link rel="stylesheet" type="text/css" href="/things">
<link rel="stylesheet" type="text/css" href="/things">
<link rel="stylesheet" type="text/css" href="/things">
</head>
<body>
I want this:
<html>
<head>
</head>
<style>
/* things here */
</style>
<body>
Is there any way of doing this? I had a read of the documentation but it only mentions building CSS output files, not outputting to a style tag in this way.
Reasons:
I am publishing a module to be included via NPM/Bower, and I would rather keep the build as a single package, rather than having to revert to
It is faster. There is a lot of literature around minimising the number of requests, including from webpack: https://github.com/christianalfoni/react-webpack-cookbook/wiki/Inlining-images
It turns out that adding the loaders to the imports manually does the trick. Removing this loaders definition from the webpack config:
module: {
loaders: [{
test: /\.scss$/,
loaders: ['style!css!sass!']
}]
}
and adding manually to each style import:
import React from 'react';
import 'style!css!sass!./fieldList.scss'; // -> this line
class FieldList extends React.Component
This causes the styles to be injected in my preferred way.
Try to use second entry point for styles.
All styles will be loaded first and then added to <style> tags in <head> section.
webpack.config.js:
export default {
entry: {
app: ['./app'],
styles: ['./styles']
},
loaders: [{
test: /\.(scss|css)$/,
loaders: [
'style',
'css',
'autoprefixer?browsers=last 3 versions',
'sass'
]
}]
}
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="styles.js"/>
</head>
<body>
<script src="app.js"/>
</body>
</html>

How to get rid of grunt-processhtml "<link rel=....>" tags

I'm trying to dynamically load some css files from a Javascript.
Snippet:
if (theme === 'testtheme' || theme === 'testtheme/') {
css =
<!-- build:css({.tmp,app}) styles/main_testtheme.css -->
'<link rel="stylesheet" href="styles/css/main_testtheme.css" type="text/css" />'
<!-- endbuild -->
;
} else {
css =
<!-- build:css({.tmp,app}) styles/main.css -->
'<link rel="stylesheet" href="styles/css/main.css" type="text/css" />'
<!-- endbuild -->
;
}
However, the grunt-build task replaces all the text between the build comments with something like:
<link rel="stylesheet" href="styles/e59b065a.main.css">
thus removing the string quotes and rendering the code invalid.
How I would like to to run:
<!-- build:css({.tmp,app}) styles/main.css -->
css = 'styles/css/main.css';
<!-- endbuild -->
should result in:
css = 'styles/e59b065a.main.css';
That would allow testing both the unminified (unbuilt) and the minified versions. Grunt build takes around 5 minutes for me so I'm trying to avoid that while developing.
Edit:
I can probably override the default blockReplacement for css (see https://github.com/yeoman/grunt-usemin#blockreplacements ) but it will make it a pain for anyone coming afterwards to try and figure out why their stylesheet is not embedded properly.
I still was not able to find an acceptable solution for this, but here's one that works, for now:
Gruntfile.js snippet:
useminPrepare: {
html: ['<%= yeoman.app %>/index.html', '<%= yeoman.app %>/includes.html'],
options: {
dest: '<%= yeoman.dist %>',
flow: {
// i'm using this config for all targets, not only 'html'
steps: {
// Here you define your flow for your custom block
cssQuoted: ['concat', 'cssmin'],
// use the option below where you have minified files that you just want to concatenate
concatjs: ['concat'],
// Note that you NEED to redefine flow for default blocks...
// These below is default flow.
js: ['concat', 'uglifyjs'],
css: ['concat', 'cssmin']
},
// also you MUST define 'post' field to something not null
post: {}
}
}
},
usemin: {
//css_name: ['<%= yeoman.dist %>/{,*/}*.html'],
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
options: {
//patterns: {
// html: [
// [ /cssHrefString\s+=\s+['"]([^"']+)["']/gm, 'Update the HTML to reference our revved/min CSS and add quotes' ]
// ]
//},
blockReplacements: {
cssQuoted: function(block){
return '\'<link rel="stylesheet" href="' + block.dest + '">\'';
},
concatjs: function (block) {
return '<script src="' + block.dest + '"></script>';
}
}
}
},
script.js snippet:
var theme = getUrlParameter('theme');
var cssHrefString;
// we need fully qualified css tags below otherwise grunt build will not be able to pick them up
if (theme === 'testtheme' || theme === 'testtheme/') {
cssHrefString =
<!-- build:cssQuoted({.tmp,app}) styles/main_testtheme.css -->
'<link rel="stylesheet" href="styles/css/main_testtheme.css">'
<!-- endbuild -->
;
} else {
cssHrefString =
<!-- build:cssQuoted({.tmp,app}) styles/main.css -->
'<link rel="stylesheet" href="styles/css/main.css">'
<!-- endbuild -->
;
}
$('head').append(cssHrefString);
The grunt config file adds a definition for concatjs - a tasks which only concatenates minified files, does not run the uglifier on them. cssQuoted which takes the string between the blocks and replaces it with a quoted "link rel=..." tag.
Make sure your grunt-usemin plugin version is up-to-date - I lost several hours trying out options with an old version (~0.1.3). The code above works with 3.0.0.

Resources