I'm having trouble figuring out how to have multiple pages in a Vue CLI project. Right now I have my home page with a few components and I want to create another page but I do not know how to do that. Am I supposed to create multiple html files where the index.html by default is? In a simple file structure with css js img folder and html files as pages I know that creating another html file means making another page. But I don't understand how this works with Vue CLI project.
I saw stuff like vue-router and "pages" in Vue documentation but I do not understand them very well. What are my alternatives? Is there a guide that explains that in detail, because I wasn't able to find any, let alone detailed. Would be very happy if you could help! Thank you!
First: always read the official documentation. With Vue you can build a SPA, and a MPA is also no problem. Just follow the guides:
https://cli.vuejs.org/
https://cli.vuejs.org/guide/html-and-static-assets.html#building-a-multi-page-app
https://cli.vuejs.org/config/#pages
You should create a new project with Vue CLI 3. Once you've created your project set it to be manually configured. Make sure you don't choose the SPA option. Vue will then create a nice "start" project using a MPA approach. After that, just repeat the config on vue.config.js.
Updated #1
It seems that some updates on Vue Cli, have changed the way to build a MPA app, so:
Create a new application vue create test
Choose Manual configuration
The boilerplate created will be for a SPA. So make the following changes:
Create a folder under src named pages (optional)
Into this folder create your own pages: Home, About, etc.
Copy and paste the App.vue and main.js from src, into your new folders - Home, etc.
Format the App.vue into this folders, to your liking.
Create a vue.config.js and set it like this: https://cli.vuejs.org/config/#pages
Below, I have three images demonstrating this:
First: a fresh new app
Second: this same app, with the changes I made above
Third: the vue.config.js from this app
You don't need to create the pages folder, this is just to get the idea.
Link to GitHub: Building a MPA App
EDIT: Vue has this built-in. Skip to the bottom for more.
Original answer:
There are two ways to interpret your question, and therefore to answer it.
The first interpretation is: "how can I support routing to different pages within the same single-page app, e.g. localhost:8080/about and localhost:8080/report etc?". The answer to this is to use the router. It's reasonably straightforward and works well.
The second interpretation is: "my app is complex, and I have multiple single-page applications, e.g. one app for the 'website' part, one app for consumers to log in and do work, one app for admins, etc - how can vue do this, without making three entirely separate repositories?"
The answer to the latter is a single repository with multiple single-page apps. This demo looks like exactly what you're after:
https://github.com/Plortinus/vue-multiple-pages/
Look in particular at: https://github.com/Plortinus/vue-multiple-pages/blob/master/vue.config.js
Updated answer:
It turns out that vuejs has the idea of multiple top-level pages built-in. I mean, it makes sense - it's going to be really common, despite what many incorrect answers are saying about "no, it's for single page apps"!
You want the pages option in the vue.config.js file:
https://cli.vuejs.org/config/#pages
If your project doesn't have that file in the root directory, create it and vuejs will discover it.
There is a long and a short way to define each page. I used the short form here:
module.exports = {
pages: {
index: 'src/pages/index/main.ts',
experiment: 'src/pages/experiment/main.ts'
}
}
You don't have to put your work under "pages". It could be "/src/apps/index/index.ts" or whatever.
After moving code around and changing some imports from:
import HelloWorld from './components/HelloWorld'
to
import HelloWorld from '#/components/HelloWorld'
The app works - but the "experiment" app in my repo had to be loaded like this:
http://localhost:8080/experiment.html
Pretty ugly, and even worse because it uses the router which resulted in URLs like:
http://localhost:8080/experiment.html/about
Ugh.
Fortunately, this stackoverflow answer solved it. Update the vue.config.js file to include devServer options (make sure this is at the top level of the exported object:
devServer: {
historyApiFallback: {
rewrites: [
{ from: /\/index/, to: '/index.html' },
{ from: /\/experiment/, to: '/experiment.html' }
]
}
}
Then also modify the router.ts file to append the extra path (in my case "experiment/":
export default new Router({
mode: 'history',
base: process.env.BASE_URL + 'experiment/',
...
Then URLs resolve nicely, e.g.: http://localhost:8080/experiment/about
This may not be relevant to the question, but bear with me, maybe my answer can help someone.
I use webpack+vue, and I have figured out how to build multiple pages applications. Here my webpack.config.js:
const path = require('path');
const fs = require('fs')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
entry: {
app: './src/app.js',
mgmt: ['./src/modules/mgmt/mgmt.js'],
login: './src/modules/login/login.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
// publicPath: '/ahezime/',
filename: (chunkData) => {
console.log('chuckData.chunk.name => ', chunkData.chunk.name)
return chunkData.chunk.name === 'app' ? './[name].bundle.js' : './[name]/[name].bundle.js';
}
},
optimization: {
minimizer: [
new TerserPlugin(),
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new CleanWebpackPlugin(['dist']),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: 'app',
template: './src/app.html',
// inject: false,
chunks: ['app'],
filename: './index.html'
}),
new HtmlWebpackPlugin({
title: 'mgmt',
template: './src/modules/mgmt/mgmt.html',
// inject: false,
chunks: ['mgmt'],
filename: './mgmt/index.html'
}),
new HtmlWebpackPlugin({
title: 'login',
template: './src/modules/login/login.html',
// inject: false,
chunks: ['login'],
filename: './login/index.html'
})
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
plugins: ['#babel/plugin-proposal-object-rest-spread']
}
}
}
],
rules: [
{
test: /\.vue$/,
exclude: /node_modules/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.scss?$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
}
};
And here's my directory structure:
https://i.stack.imgur.com/uFvKx.png
And you can jump pages:
<template>
<div>
<h1>App</h1>
<div>
Please click me, and let take you into the login page!!!
</div>
<span>Before computed: {{ message }} </span>
<br>
<span>Afer computed: {{ computedMessage() }} </span>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello World!'
}
},
computed: {
reversedMessage: function() {
return this.message.split('').reverse().join('')
}
},
methods: {
computedMessage: function() {
return this.message.split('').reverse().join('')
}
}
}
</script>
Note pointing users to what should be the accepted answer
At the moment of posting my initial answer I wasn't aware of the possibility of actually building MPAs in VueJS. My answer doesn't address the question asked therefore I will recommend to take a look at the answer provided by PJ.Wanderson bellow which should be the accepted answer
Inital Answer
Vue.js projects are SPAs(single page applications). You only have one .html file in the entire project which is the index.html file you mentioned. The "pages" you want to create, in vue.js are referred to as components. They will be plugged into the index.html file and rendered in the browser. A vue.js component comprises 3 parts:
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
Template: it contains all the html your page should display (this is where you put the html of your pages)
Script: it contains all JavaScript code that will be executed on the page/component
Style: it contains the CSS that will style that specific component/page
You can check this tutorial out for a quick-start Vue.js 2 Quickstart Tutorial 2017
It explains vue.js project structure and how the various files relate to each other
Related
I have a fairly simple question.
I was writing simple express page for company (basic table delete row button and form page)
easyyyy
But I decided to spruce form page a little bit with ajax validation and dynamic fields. Since I did few full react sites before i thought making this component would be easy. And tbh making component is easy but babel and webpack is not.
is there any easy way to transpile jsx with imports to the web without configuring whole separate workspace?
If react for this overkill and jQuery fell out of grace is there any other suitable library?
Hope you can understand my broken English
yes react is powerful, you can add it and setup it like,
you can create the component separately and load the script on your target page just make sure you place the mount point right
and you can use create react app with this as well
just build the project place the bundle js path on the same page
and add the mount point and it should work smoothly
<div id="mountPoint" ></div>
another way like the react does suggest here
I did minimal configuration of webppack and babel. When it wasnt so bad still configuring two libraries for one small component with 2 dependencies seems ridiculous :P
My config maybe somebody finds it useful:
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
output: {
filename: 'public/javascripts/dist.js'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', "#babel/preset-react"]
}
}
}
]
}
};
babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = [];
const plugins = ['#babel/plugin-proposal-class-properties'];
return {
presets,
plugins
};
}
I'm developing a Chrome Extension and I use Webpack to bundle it. I've got my compiled bundle, which is the main part of the app, but I also need an options page to describe the functionality. This options page has nothing to do with the bundle, it's just a static HTML file.
I must put a lot of things in that options page so I want to render that page with Mustache and define all content with JavaScript. For the most part, I've done that.
Here's my Webpack config (I've removed the parts regarding my app):
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
output: {
path: path.join(__dirname, 'extension/build/')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/options/index.html',
inject: false
})
],
module: {
rules: [
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render: require('./src/options/index.js')
}
}
]
}
}
and in my src/index.js, I have:
require('./options/index.html')
This will open the template and render it with the data in src/options/index.js.
There's a problem with that, however. I run Webpack with webpack --watch and changes to index.js (it holds the template data) do not trigger a rebuild. Also, I would need to go through a lot of trouble to create another static HTML file in the same manner.
It would be ideal if HtmlWebpackPlugin automatically used the template I require() in my entry point so that I don't need to explicitly set it. Also, it would be great if it automatically used a js in that same location to get the data. For example:
require('./options/index.html`)
Renders the template with data from ./options/index.html.js and then emits it. It would be even better if it emitted it to a custom folder specified in the Webpack config.
Is that possible? I couldn't find a plugin/loader that does that.
Edit: I was able to partly fix the rebuild problem by specifying the render option as a function:
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render () {
var file = './src/options/index.js'
delete require.cache[require.resolve(file)]
return require(file)
}
}
}
But it still doesn't work properly. The rebuild would only trigger after I make changes to index.html. This means that if I change index.js, I need to go and save index.html as well to trigger the build.
Lately I've been dabbling with React and Webpack, and so far i've been loving it. Sure it took a lot of reading, looking at exemples, and trying some stuff out, but eventually, react itself as well as hot reloading my components grew on me and I'm pretty sure I want to continue that way.
For the past couple of days though I've been trying to make my pretty simple "application" (so far it's just a menu with a couple of sub components and react router to display a dummy page) render server side.
Here's my project's layout:
Here's what my webpack config looks like so far:
let path = require("path"),
webpack = require("webpack"),
autoprefixer = require("autoprefixer");
module.exports = {
entry: [
"webpack-hot-middleware/client",
"./client"
],
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js",
publicPath: "/static/"
},
resolve: {
root: path.resolve(__dirname, "common")
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
exclude: /node_modules/,
include: __dirname
},
{ test: /\.scss$/, loader: "style!css!sass!postcss" },
{ test: /\.svg$/, loader: "file" }
]
},
sassLoader: {
includePaths: [path.resolve(__dirname, "common", "scss")]
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
BROWSER: JSON.stringify(true),
NODE_ENV: JSON.stringify( process.env.NODE_ENV || "development" )
}
}),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
postcss: [autoprefixer({remove: false})]
}
And, omitting the module requirements, my server:
// Initialize express server
const server = express();
const compiler = webpack(webpackConfig);
server.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath }));
server.use(webpackHotMiddleware(compiler));
// Get the request
server.use((req, res) => {
// Use React Router to match our request with our components
const location = createLocation(req.url);
match({routes, location}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
res.send(renderFullPage(renderProps));
} else {
res.status(404).send("Not found");
}
})
});
function renderFullPage(renderProps) {
let html = renderToString(<RoutingContext {...renderProps}/>);
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">${html}</div>
<script src="/static/bundle.js"></script>
</body>
</html>
`;
}
server.listen(3000);
Little React Router server-side specificities here but I can get my head around it.
Then came an issue with requiring styles inside a component. After a while I figured out this bit (thanks to a github issue thread):
if (process.env.BROWSER) {
require("../scss/components/menu.scss");
logo = require("../images/flowychart-logo-w.svg") ;
}
And along with it the fact that I still need to declare a router on my client, re-using the routes my server already uses:
// Need to implement the router on the client too
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import createBrowserHistory from "history/lib/createBrowserHistory";
import routes from "../common/Routes";
const history = createBrowserHistory();
ReactDOM.render(
<Router children={routes} history={history} />,
document.getElementById("app")
);
And now there I am. My style is applied and everything works. I'm still no expert, but so far I kind of understand everything that's going on (which is the main reason why I'm not using an existing universal js / react boilerplate - actually understanding what's going on).
But now my next issue to solve is how to require my images inside my components.
With this setup, it actually does work, however I'm getting this warning:
Warning: React attempted to reuse markup in a container but the
checksum was invalid.
This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting.
React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) "><img class="logo" src="/static/31d8270
(server) "><img class="logo" alt=""
And it makes sense, as you've seen in a previous code snippet, I actually set a logo variable when I'm in the browser environment, and I use that variable to set the src tag of my components. Sure enough on the server, no src attributes is then given, thus the error.
Now to my question:
Am i doing that style / image requirement right ? What am I missing to get rid of that warning ?
The one thing I love about that webpack / react association is how I can keep those dependencies close to the actual part of my UI that uses them. However the one thing I've found out was omitted from most of the tutorials out there was how to keep this sweetness working when rendering server side.
I'd be very grateful if you guys could give me some insight.
Thanks a lot !
I would recommend webpack-isomorphic-tools as a solution here.
I have a section on how to import SVG's into React apps in an example project: https://github.com/peter-mouland/react-lego#importing-svgs
Here is the code that was required to turn my Universal React app into one that accepts SVG's : https://github.com/peter-mouland/react-lego/compare/svg
There's at least two approaches you can take for this:
Try to de-webpackify your code to run in Node.
Compile your client code with Webpack using target 'node'.
The first option requires you to remove Webpack specific code such as require.ensure / System.import and teaching node your module resolution rules and how to convert non js assets into js. This is achievable with a few babel transformations and maybe modifications to the NODE_PATH however this approach does mean you're constantly chasing your tail, i.e. if you start using a new Webpack feature / loader / plugin you need to make sure you're able to support the equivalent functionality in Node.
The second approach is probably more practical as you can be sure that whichever Webpack features you're using will also work on the server. This is one way you may do it:
// webpack.config.js
module.exports = [
{
target: 'web',
entry: './client',
output: {
path: dist,
filename: 'client.js'
}
}, {
target: 'node',
entry: './server',
output: {
path: dist,
filename: 'server.js',
libraryTarget: 'commonjs2'
}
}
];
// client.js
renderToDom(MyApp, document.body);
// server.js
module.exports = (req, res, next) => {
res.send(`
<!doctype html>
<html>
<body>
${renderToString(MyApp)}`
<script src="/dist/client.js"></script>
</body>
</html>
`);
}
// index.js
const serverRenderer = require('./dist/server');
server.use(serverRenderer);
server.listen(6060);
I am trying to set up webpack production configuration. All looks well. However, I realized that while using the commons chunk plugin, it covers all the files in common as expected. What I want to do is, separation of common library modules and common application modules. My config file is :
module.exports = {
entry: {
lib: ["react", "react-dom"],
app: "./ui-v2/app/app.js",
app2: "./ui-v2/app/app2.js"
},
output: {
path: path.join(__dirname, "target/ui/v2"),
filename: "/app/[name].[chunkhash].min.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.(png|jpg|svg)/,
loader: "file-loader?name=img/[name].[hash].[ext]"
// loaders: ["url", "image-webpack"]
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader!sass-loader", {
publicPath: __dirname
})
},
{
test: /\.(woff|woff2|ttf|eot)$/,
loader: "file-loader?name=fonts/[name].[hash].[ext]"
}
]
},
plugins: [
clean,
new webpack.optimize.CommonsChunkPlugin("common", "app/common.[chunkhash].js"),
new webpack.ProvidePlugin({
React: "react",
ReactDOM: "react-dom",
$: "jquery",
_: "lodash"
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
sourceMap: true
},
mangle: {
except: ["exports", "import", "$", "_", "require", "React", "ReactDOM"]
}
}),
new ExtractTextPlugin("styles/[name].[contenthash].css"),
new Manifest()
]
}
Basically I have 3 modules in the app; app.js, app2.js and a common component user.js.
What I want to achieve is to bundle all library related files like react, react-dom, lodash, etc in a lib bundle, and common application components like user.js in a common bundle. In order to do this, I thought there might be an option to exclude the files that I don't want them to go to "common" file. If I use this output, what is the point for long term caching files for library bundles because whenever I get a common component in my project, they will go into the common bundle and the content hash will be different, but nothing changes in this library files like react, jquery, lodash, etc.
Anyway, what I have at the end of build process is everything still goes into the common bundle and lib has nothing and the file sizes are :
app.<hash>.min.js -> 3.05KB
app2.<hash>.min.js -> 3.05KB
lib.<hash>.min.js -> 165 Bytes (has almost nothing!)
common.<hash>.js -> 678 KB
Is there any way to achieve what I want or what would be the best approach to a production build in similar cases? Thank you!
Its because the first parameter for CommonsChunkPlugin is "common" where it should be "lib". The plugin picks up the entry with a name matching with the value of its first parameter.
A simple example config picked from webpack's wiki -
var webpack = require("webpack");
module.exports = {
entry: {
app: "./app.js",
vendor: ["jquery", "underscore", ...],
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js")
]
};
Note that the "vendor" entry is again specified in CommonsChunkPlugin
You should check out Webpack's DLL Plugin.
https://github.com/webpack/webpack/blob/cb3d8e2097503c7245c5dda5b7a6e9d63421a72b/examples/dll/README.md
With this plugin you bundle up common 3rd party vendor dependencies such as React and friends in a DLL, which is essentially just a JSON Manifest that goes along with your requires wrapped in webpack context and cached to disk.
In your project code, you would have your shared components which depend on React and friends, and you would have your application code which depend on your shared components as well as react and friends.
Your project would incorporate the DLL Reference plugin as you can see here:
https://github.com/webpack/webpack/blob/cb3d8e2097503c7245c5dda5b7a6e9d63421a72b/examples/dll-user/README.md
This will see to it that your shared components and your application code pull React and other 3rd party modules from the same DLL bundle. This can help improve build times and the performance of the dev server and hot module reloading.
I'm using Fluxible to help create an isomorphic app on a new project and it's going swimmingly. I love it so far. I have run into a speed bump, though, and wonder how to get over it.
Here is my Header component thus far:
import React from 'react'
import Nav from '../Nav/Nav'
import classNames from 'classnames'
if (process.env.BROWSER) var styles = require('./Header.css')
class Header extends React.Component {
render() {
// Header classes
var theClasses = process.env.BROWSER ? classNames({
[styles.Header]: true
}) : ''
return (
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
)
}
}
export default Header
You'll see I'm using process.env.BROWSER to detect which ENV I'm on. If we're in the client, I require the CSS. If we're on the server, I skip it. That works wonderfully.
The problem comes later in the file, where I build theClasses object based on the contents of the Header.css file, then use those classes on the Header like so:
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
The problem is because I'm not loading the css on the server, theClasses ends up being empty, and the content rendered for the client ends up different than the content on the server. React displays this warning:
Warning: React attempted to reuse markup in a container but the
checksum was invalid. This generally means that you are using server
rendering and the markup generated on the server was not what the
client was expecting. React injected new markup to compensate which
works but you have lost many of the benefits of server rendering.
Instead, figure out why the markup being generated is different on the
client or server:
(client) n28"><header class="Header--Header_ip_OK
(server) n28"><header class="" data-reactid=".2ei
What would you recommend to get this resolved?
UPDATE September 24, 2015
The original issue was that I couldn't get CSS to compile on the server side, so I started checking for the BROWSER like this:
if (process.env.BROWSER) var styles = require('./Application.css')
If I remove the if (process.env.BROWSER) bit I get this error:
SyntaxError: src/components/Application/Application.css: Unexpected token (2:0)
1 |
> 2 | #import 'styles/index.css';
| ^
3 |
In the following simple CSS file:
#import 'styles/index.css';
.Application {
box-shadow: 0 0 0 1px var(--medium-gray);
box-sizing: border-box;
lost-center: 1080px 32px;
}
I started this project with the Fluxible Yo Generator which provides two Webpack config files here: https://github.com/yahoo/generator-fluxible/tree/master/app/templates
I updated mine with a few loaders:
var webpack = require('webpack');
var path = require('path');
module.exports = {
resolve: {
extensions: ['', '.js', '.jsx']
},
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./client.js'
],
output: {
path: path.resolve('./build/js'),
publicPath: '/public/js/',
filename: 'main.js'
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: [
require.resolve('react-hot-loader'),
require.resolve('babel-loader')
]
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]!postcss-loader'
}, {
test: /\.(png|jpg|svg)$/,
loader: 'url?limit=25000'
}, {
test: /\.json$/,
loader: 'json-loader'
}
]
},
postcss: function () {
return [
require('lost'),
require('postcss-import')({
path: ['./src/'],
onImport: function (files) {
files.forEach(this.addDependency);
}.bind(this)
}),
require('postcss-mixins'),
require('postcss-custom-properties'),
require('autoprefixer')({
browsers: ['last 3 versions']
})
];
},
node: {
setImmediate: false
},
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
BROWSER: JSON.stringify(true)
}
})
],
devtool: 'eval'
};
So that's where I am… not sure how to get CSS compiled server side. Appreciate any help I can get.
You can try css-modules-require-hook
webpack.config.js
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]'
}
server.js
require('css-modules-require-hook')({
// This path should match the localIdentName in your webpack css-loader config.
generateScopedName: '[name]__[local]___[hash:base64:5]'
})
You need to ensure the server is generating the same markup as the client, which means that you need to use CSS Modules on the server too.
We achieve this in my current project by using Webpack to compile our Node code too. James Long wrote a really good guide on how to set this up, spread across three blog posts:
Backend Apps with Webpack (Part I)
Backend Apps with Webpack (Part II)
Backend Apps with Webpack (Part III)
You should certainly be rendering your server side HTML with the classes otherwise you are missing out on the time to glass perks of an isomorphic application. It means you will still have to wait for the JS to load in order to apply the CSS styles which defeats some of the purpose of building the HTML server side. It also means you can't "cut the mustard" and serve old browsers an application with Javascript turned off.
The question is why aren't you rendering the CSS classes on the server? This also confused me for a while but my guess is you don't have two Webpack entry points? One for the client, and one for the server.
If my assumption is correct then take a look at a sandbox repo here where I'm doing multiple builds for the Node server entrypoint and the Browser entrypoint also using Fluxible.
However I would take it all with a pinch of salt for now as it was just a test project for personal use. I have also used this approach with local css where it builds it on both the server and client and it works like a charm.
EDIT: I see you are using ES6 so I assume you are indeed building server side? If so what is the reason you don't include the CSS?
So you have to have two Webpack configs, the first is for compiling browser bundle, the second is for compiling server bundle.
With a standard configuration you have css-loader?...:
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]'
}
And you get two different bundles.
On a server. All classes are in css.locals object:
let css = require('styles.scss');
css.locals == { fooClass: 'some_foo_class' }
On a browser there is no .locals:
let css = require('styles.scss');
css == { fooClass: 'some_foo_class' }
So you need to get rid of .locals in a server version, so that it should be like in a browser.
You can do it using css-loader/locals?... in a webpack server config:
{
test: /\.scss$/i,
loader: "css/locals?modules&importLoaders=1&-minimize&localIdentName=[local]_[hash:3]!sass"
}
More details here https://github.com/webpack/css-loader/issues/59#issuecomment-109793167