I want to optimize my webpack build file and tried to use code splitting (https://webpack.js.org/guides/code-splitting/). The largest part of my application is vuetify and I try to separate it from my index.js
Here is my index.js file:
import Vue from 'vue'
import App from './App.vue';
import router from './router/router'
import vuetify from './plugins/vuetify'
new Vue({
router,
vuetify,
el:'#vue-app',
render: a => a(App)
})
vuetify is configurated in ./src/plugins/vuetify.js
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
Vue.use(Vuetify)
const opts = {}
export default new Vuetify(opts)
and here is my webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')
module.exports = {
// mode:'development',
entry: {
main: './src/index.js',
vuetify: './src/plugins/vuetify.js',
},
output: {
filename: '[name].bungle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
template:'./index.html'
}),
new VueLoaderPlugin(),
new VuetifyLoaderPlugin()
],
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']
}
}
},
{
//Правила для vue.js
test: /\.vue$/,
loader: 'vue-loader'
},
{
// Правила для обработки css стилей
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.s(c|a)ss$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
sassOptions: {
fiber: require('fibers'),
indentedSyntax: true // optional
},
},
},
],
},
]
},
};
but when I run npm run build it makes two entry points:
Entrypoints:
main (452 KiB)
main.bungle.js
vuetify (315 KiB)
vuetify.bungle.js
With one entry point main.js is the same:
Entrypoints:
main (452 KiB)
main.bungle.js
Looks like in result application vuetify is build in main.bungle.js and not imported from vuetify.bungle.js. Another solution is to lazy-loading, but I still want to do this by code-splitting.
Related
Webpack collected files (.css, .js) into a library and use it in another React project. The styles specified in the component do not pass, although the .css file is present and the styles are there.
UiButton.jsx file
import styles from './UiButton.module.css';
const UiButton = () => {
return (
<>
<button className={styles.button}>Text</button>
</>
);
}
export default UiButton;
index.js file
import UiButton from './UiButton/UiButton';
import './index.css';
export { UiButton };
Webpack.config.js file
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: { main: './src/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: "umd",
library: "uilibrarytest"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [ 'style-loader', MiniCssExtractPlugin.loader, 'css-loader' ]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'index.css',
}),
// new HtmlWebpackPlugin({
// template: './public/index.html',
// }),
new webpack.ProvidePlugin({
"React": "react",
}),
],
resolve: {
extensions: ['.js', '.jsx'],
},
}
This is what webpack builds:
.button {
background: red;
}
html {
margin: 0;
padding: 0;
}
How to make it so that when using a component from a given library, the styles are also pulled to it ???
Loaders in Webpack are evaluated from right to left. In your configuration the evaluation order is 'css-loader', MiniCssExtractPlugin.loader, and finally 'style-loader'. But 'style-loader' only injects styles into the DOM. You need MiniCssExtractPlugin.loader to be the first element in the "use" array. See below...
{
test: /\.css$/,
use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
}
Furthermore you can tell webpack to use MiniCssExtractPlugin.loader during production and at other times use 'style-loader'.
const isProduction = process.env.NODE_ENV == 'production';
const stylesHandler = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
...other config
{
test: /\.css$/,
use: [ stylesHandler, 'css-loader' ]
}
And in your package.json scripts,
"build": "webpack --mode=production --node-env=production"
I have a variable in my vars.scss that I want to access from Javascript in root/app/app.vue.
root/app/scss/vars.scss
:export {
cursor: #fff;
}
root/app/app.vue
<template>
<div id="yes">
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import colors from '#/scss/vars.scss';
export default Vue.extend({
mounted() {
console.log(colors.cursor);
},
});
</script>
<style >
</style>
I have read approximately 30 different stackoverflow questions that appear to be dealing with the similar problem of importing variables into the style block of the .vue file, as well as the identical problem of importing the variables directly into the Javascript code. As a result, my webpack.config.js looks like the following:
root/webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const env = process.env.NODE_ENV
module.exports = {
entry: './app/index.ts',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'staticfiles')
},
resolve: {
extensions: [ '.ts', '.js', '.vue', '.scss', '.sass'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'#': path.resolve(__dirname, '/app/')
}
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),
new VueLoaderPlugin()
],
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|vue|ts)$/,
loader: 'eslint-loader',
exclude: /node_modules/
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// Since sass-loader (weirdly) has SCSS as its default parse mode, we map
// the "scss" and "sass" values for the lang attribute to the right configs here.
// other preprocessors should work out of the box, no loader config like this necessary.
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
}
// other vue-loader options go here
}
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/],
}
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
},
{
test: /\.s(a|c)ss$/,
use: [ {
loader: "style-loader",
options: {
sourceMap: env === 'development',
}
}, {
loader: "css-loader",
options: {
sourceMap: env === 'development',
}
}, {
loader: "sass-loader",
options: {
sourceMap: env === 'development',
}
},
'vue-style-loader'],
}]
}
};
I have also tried, in the test: /\.s(a|c)ss$/ section, to put vue-style-loader at the beginning of the array.
I have tried many combinations of filenames when attempting to import the .scss file, such as relative (../scss/vars.scss), removing the extension, using .css as an extension, etc.
The error I get is:
ERROR in /home/Documents/application/app/app.vue.ts
[tsl] ERROR in /home/Documents/application/app/app.vue.ts(10,28)
TS2307: Cannot find module '#/scss/vars.scss'.
My question:
In a project that uses vue-style-loader and vue-loader to build .vue files with webpack, how can I import .scss variables into the <script> portion of a .vue file? (please note - I am NOT attempting to import them into the <style> section of the .vue file)
An example based on my comment:
SCSS fragment:
$foo: #333;
body {
--variable-foo: $foo;
}
And then anywhere in the JavaScript
const value = document.body.style.getPropertyValue("--variable-foo");
console.log(value); // outputs "#333"
I've been googling for a couple hours now and can't seem to resolve my issue.
I have a webpack/React/Typescript/Mobx setup and am attempting to use firebase.
Here is my webpack config: (boilerplate from this repo)
var webpack = require('webpack');
var path = require('path');
// variables
var isProduction = process.argv.indexOf('-p') >= 0;
var sourcePath = path.join(__dirname, './src');
var outPath = path.join(__dirname, './dist');
// plugins
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
module.exports = {
context: sourcePath,
entry: {
main: './main.tsx'
},
output: {
path: outPath,
filename: 'bundle.js',
chunkFilename: '[chunkhash].js',
publicPath: '/'
},
target: 'web',
resolve: {
extensions: ['.js', '.ts', '.tsx'],
// Fix webpack's default behavior to not load packages with jsnext:main module
// (jsnext:main directs not usually distributable es6 format, but es6 sources)
mainFields: ['module', 'browser', 'main'],
alias: {
app: path.resolve(__dirname, 'src/app/'),
assets: path.resolve(__dirname, 'src/assets/')
}
},
module: {
rules: [
// .ts, .tsx
{
test: /\.tsx?$/,
use: [
isProduction
? 'ts-loader'
: {
loader: 'babel-loader',
options: {
babelrc: false,
plugins: ['react-hot-loader/babel']
}
},
'ts-loader'
],
// : ['babel-loader?plugins=react-hot-loader/babel&presets=', 'ts-loader'],
exclude: /node_modules/
},
// css
{
test: /\.css$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
query: {
modules: true,
sourceMap: !isProduction,
importLoaders: 1,
localIdentName: '[local]__[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('postcss-import')({ addDependencyTo: webpack }),
require('postcss-url')(),
require('postcss-cssnext')(),
require('postcss-reporter')(),
require('postcss-browser-reporter')({
disabled: isProduction
})
]
}
}
]
})
},
{
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader']
},
// static assets
{ test: /\.html$/, use: 'html-loader' },
{ test: /\.(png|jpg)$/, use: 'url-loader?limit=10000' },
{ test: /\.webm$/, use: 'file-loader' }
]
},
optimization: {
splitChunks: {
name: true,
cacheGroups: {
commons: {
chunks: 'initial',
minChunks: 2
},
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
priority: -10
}
}
},
runtimeChunk: true
},
plugins: [
new WebpackCleanupPlugin(),
new ExtractTextPlugin({
filename: 'styles.css',
disable: !isProduction
}),
new HtmlWebpackPlugin({
template: 'assets/index.html'
})
],
devServer: {
contentBase: sourcePath,
hot: true,
inline: true,
historyApiFallback: {
disableDotRule: true
},
stats: 'minimal'
},
devtool: 'cheap-module-eval-source-map',
node: {
// workaround for webpack-dev-server issue
// https://github.com/webpack/webpack-dev-server/issues/60#issuecomment-103411179
fs: 'empty',
net: 'empty'
}
};
Just by including firebase in my app i relentlessly end up with this error:
Uncaught TypeError: Cannot read property 'navigator' of undefined auth.esm.js?69b5:10
I have tested by including a simple component like so:
import * as React from 'react';
import * as Styles from './styles.css';
import 'app/utils/FirebaseUtil';
interface TestProps {}
export const Test: React.StatelessComponent<TestProps > = () => (
<div className={Styles.root}>
{'Hello World'}
</div>
);
FirebaseUtil:
import * as firebase from 'firebase';
const config = {
apiKey: '**my key here**',
authDomain: '** my domain here **'
};
firebase.initializeApp(config);
export const fbAuth = firebase.auth;
No matter what I seem to do I get the navigator error. Even if i dont export the auth object. As far as I can tell its related to babel-loader adding strict-mode according to this SO question, i think? All other related searches seem to have to do with firebase-ui, which i am not using in any way.
But I have no idea how he manages to turn off strict mode, not to mention the OP is not using typescript and I am using ts-loader in this case. I can't for the life of me figure out how to get it working. Aside from all of this if I do try use the firebase object for auth() for example I get a bunch of warnings from webpack about auth not existing on the firebase object. Totally stumped.
So in case anyone else runs into this problem. It appears it was a package version issue. Im assuming that the package versions specifically included in the boilerplate i used didn't play well with firebase.
I updated typescript, react-hot-loader, and most likely the issue webpack from version 3.0.4 to 4.12.1 and things seem to be working ok now. Also with the updates I now import firebase like so:
import firebase from '#firebase/app';
import '#firebase/auth';
Hope this helps someone.
In my case I fixed this importing functions
import firebase from 'firebase/app'
import 'firebase/functions'
import 'firebase/analytics'
I have created configureStore.dev.js file to configure store for my react-redux app. Below is my code for configureStore.dev.js file.
I am getting "require not defined" error for the "const nextRootReducer = require('../reducers').default;".
How can I rewrite this part to solve the issue?
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import StockApp from '../reducers';
import DevTools from '../containers/DevTools';
const createDevStoreWithMiddleware = compose(
applyMiddleware(thunk),
applyMiddleware(createLogger()),
DevTools.instrument()
)(createStore);
export default function configureStore() {
const store = createDevStoreWithMiddleware(StockApp);
// enable webpack hot module replacement for reducers
if (module.hot) {
module.hot.accept('../reducers', () => {
// eslint-disable-next-line
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
Here is my webpack.config.js:
// We are using node's native package 'path'
// https://nodejs.org/api/path.html
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(paths.SRC, 'index.html'),
}),
new ExtractTextPlugin('style.bundle.css'),
],
// We are telling webpack to use "babel-loader" for .js and .jsx files
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader',
}),
},
{
test: /\.(png|jpg|gif)$/,
use: [
'file-loader',
],
},
],
},
// Enable importing JS files without specifying their's extenstion -> ADDED IN THIS STEP
//
// So we can write:
// import MyComponent from './my-component';
//
// Instead of:
// import MyComponent from './my-component.jsx';
resolve: {
extensions: ['.js', '.jsx'],
},
target: 'node',
externals: [nodeExternals()],
node: {
console: true,
fs: 'empty',
net: 'empty',
tls: 'empty'
},
// Dev server configuration -> ADDED IN THIS STEP
// Now it uses our "src" folder as a starting point
devServer: {
contentBase: paths.SRC,
},
};
Could anyone please help me solve this issue?
Thank you.
Hoping that you are using Babel 6+, you can replace this code:
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
With this:
store.replaceReducer('.default')
No need to require. Hope it helps.
I am using this config:
'use strict';
const path = require('path');
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AssetsPlugin = require('assets-webpack-plugin');
const assetsPluginInstance = new AssetsPlugin();
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
cache: true,
watch: true,
devtool: 'cheap-module-source-map',
context: path.join(__dirname, "/src"),
entry: {
vendors: "./vendors",
main: "./main"
},
output: {
path: path.join(__dirname, '/public'),
publicPath: "/",
filename: "[name].js"
},
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: [/\.(spec|e2e)\.ts$/]
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css')
},
{
test: /\.(scss|sass)$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss-loader!resolve-url!sass?sourceMap')
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'url?name=imgs/[name].[ext]&limit=100000'
},
{
test: /\.(svg|ttf|eot|woff|woff2)$/,
loader: 'file?name=fonts/[name].[ext]',
exclude: /\/src\/imgs\//
},
{
test: /\.json$/,
loader: 'json?name=data/[name].[ext]'
},
{
test: /index\.html$/,
loader: 'html'
},
{
test: /\.html$/,
loader: 'raw',
exclude: path.join(__dirname, "src/index.html")
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendors',
filename: 'vendors.js',
minChunks: 2
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
"window.jQuery": 'jquery'
}),
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new ExtractTextPlugin("[name].css", {
disable: false,
allChunks: true
}),
new HtmlWebpackPlugin({
template: './index.html',
inject: 'body',
favicon: 'favicon.ico',
chunksSortMode: 'none'
}),
new AssetsPlugin({
filename: 'assets.json',
path: path.join(__dirname, 'public/')
}),
new CleanWebpackPlugin(['./public'])
],
postcss: function () {
return {
defaults: [autoprefixer({browsers: ['last 2 versions']})]
};
}
};
All work perfect. I get vendors.js with all libraries and main.js with only app code and logic. I want to save this state.
Look to vendors.ts:
// Polyfills
import 'angular2/bundles/angular2-polyfills.js';
// Angular 2
import 'angular2/platform/browser';
import 'angular2/core';
import 'angular2/http';
import 'angular2/router';
import 'rxjs/Observable';
import 'rxjs/Subject';
import 'jquery/dist/jquery.min.js';
import 'bootstrap/dist/js/bootstrap.min.js';
import 'lodash/lodash.min.js';
import 'moment';
import 'accounting/accounting.min.js';
//css
import './vendors.scss';
Here we have lodash library. If we open chrome console we can see it work.
Now I want to use lodash in my app.component.ts
export class AppComponent implements OnInit {
constructor() {
console.log(_.last([1, 2, 3]));
}
ngOnInit() {
}
}
I got this error: ERROR in [default] /home/g-montag/WebstormProjects/Chat/src/app/app.component.ts:17:16
Cannot find name '_'.
I need import library again, and if I do this, lodash library will imported in my main.js file. It works, but duplicate code.
So, finally the question :) How to import lodash or something else in vendors.ts and use it in app without importing again and again.
got this error: ERROR in [default] /home/g-montag/WebstormProjects/Chat/src/app/app.component.ts:17:16
Cannot find name '_'
Its a compile time error. Quick workaround: Create a vendor.d.ts file that contains:
declare var _:any;
More
See ambient declarations : https://basarat.gitbooks.io/typescript/content/docs/types/ambient/d.ts.html