diff --git a/examples/custom-build/nuxt.config.js b/examples/custom-build/nuxt.config.js index 08a9858644..5c0ff6fbae 100644 --- a/examples/custom-build/nuxt.config.js +++ b/examples/custom-build/nuxt.config.js @@ -1,6 +1,7 @@ module.exports = { build: { filenames: { + css: 'styles.[chunkhash].css', // default: common.[chunkhash].css manifest: 'manifest.[hash].js', // default: manifest.[hash].js vendor: 'vendor.[hash].js', // default: vendor.bundle.[hash].js app: 'app.[chunkhash].js' // default: nuxt.bundle.[chunkhash].js diff --git a/lib/build.js b/lib/build.js index 0c7f6bb081..f0a2a9e58d 100644 --- a/lib/build.js +++ b/lib/build.js @@ -51,6 +51,7 @@ const defaults = { analyze: false, publicPath: '/_nuxt/', filenames: { + css: 'common.[chunkhash].css', manifest: 'manifest.[hash].js', vendor: 'vendor.bundle.[hash].js', app: 'nuxt.bundle.[chunkhash].js' diff --git a/lib/webpack/base.config.js b/lib/webpack/base.config.js index c27efb6e49..ae163e5914 100644 --- a/lib/webpack/base.config.js +++ b/lib/webpack/base.config.js @@ -4,6 +4,8 @@ import vueLoaderConfig from './vue-loader.config' import { defaults } from 'lodash' import { join } from 'path' import { isUrl, urlJoin } from '../utils' +import { styleLoader, extractStyles } from './helpers' +import ExtractTextPlugin from 'extract-text-webpack-plugin' /* |-------------------------------------------------------------------------- @@ -71,15 +73,21 @@ export default function ({ isClient, isServer }) { cacheDirectory: !!this.dev }) }, - { test: /\.css$/, loader: 'vue-style-loader!css-loader' }, - { test: /\.less$/, loader: 'vue-style-loader!css-loader!less-loader' }, - { test: /\.sass$/, loader: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' }, - { test: /\.scss$/, loader: 'vue-style-loader!css-loader!sass-loader' }, - { test: /\.styl(us)?$/, loader: 'vue-style-loader!css-loader!stylus-loader' } + { test: /\.css$/, use: styleLoader.call(this, 'css') }, + { test: /\.less$/, use: styleLoader.call(this, 'less', 'less-loader') }, + { test: /\.sass$/, use: styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax') }, + { test: /\.scss$/, use: styleLoader.call(this, 'sass', 'sass-loader') }, + { test: /\.styl(us)?$/, use: styleLoader.call(this, 'stylus', 'stylus-loader') } ] }, plugins: this.options.build.plugins } + // CSS extraction + if (extractStyles.call(this)) { + config.plugins.push( + new ExtractTextPlugin({filename: this.options.build.filenames.css}) + ) + } // Add nuxt build loaders (can be configured in nuxt.config.js) config.module.rules = config.module.rules.concat(this.options.build.loaders) // Return config diff --git a/lib/webpack/client.config.js b/lib/webpack/client.config.js index 2ebdf1551a..f4054c05e0 100644 --- a/lib/webpack/client.config.js +++ b/lib/webpack/client.config.js @@ -3,12 +3,14 @@ import { each, defaults } from 'lodash' import webpack from 'webpack' import VueSSRClientPlugin from 'vue-server-renderer/client-plugin' +import ExtractTextPlugin from 'extract-text-webpack-plugin' import HTMLPlugin from 'html-webpack-plugin' import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin' import ProgressBarPlugin from 'progress-bar-webpack-plugin' import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer' import OfflinePlugin from 'offline-plugin' import base from './base.config.js' +import { extractStyles } from './helpers' import { resolve } from 'path' /* @@ -55,9 +57,18 @@ export default function () { // Extract vendor chunks for better caching new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', - filename: this.options.build.filenames.vendor + filename: this.options.build.filenames.vendor, + minChunks (module) { + // A module is extracted into the vendor chunk when... + return ( + // If it's inside node_modules + /node_modules/.test(module.context) && + // Do not externalize if the request is a CSS file + !/\.css$/.test(module.request) + ) + } }), - // Extract manifest + // Extract webpack runtime & manifest new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', minChunks: Infinity, @@ -68,6 +79,7 @@ export default function () { template: this.options.appTemplatePath, inject: false // <- Resources will be injected using vue server renderer }), + // Generate client manifest json new VueSSRClientPlugin({ filename: 'client-manifest.json' }) diff --git a/lib/webpack/helpers.js b/lib/webpack/helpers.js new file mode 100755 index 0000000000..9681212994 --- /dev/null +++ b/lib/webpack/helpers.js @@ -0,0 +1,15 @@ +import ExtractTextPlugin from 'extract-text-webpack-plugin' + +export function extractStyles(ext) { + return !this.dev && !!this.options.build.extractCSS && this.options.build.extractCSS[ext] !== false +} + +export function styleLoader(ext, loader = []) { + if (!extractStyles.call(this, ext)) { + return ['vue-style-loader', 'css-loader'].concat(loader) + } + return ExtractTextPlugin.extract({ + use: ['css-loader?minimize'].concat(loader), + fallback: 'vue-style-loader' + }) +} diff --git a/lib/webpack/vue-loader.config.js b/lib/webpack/vue-loader.config.js index d419725ddf..4e2335e42f 100644 --- a/lib/webpack/vue-loader.config.js +++ b/lib/webpack/vue-loader.config.js @@ -1,6 +1,7 @@ 'use strict' import { defaults } from 'lodash' +import { extractStyles, styleLoader } from './helpers' export default function ({ isClient }) { let babelOptions = JSON.stringify(defaults(this.options.build.babel, { @@ -8,18 +9,21 @@ export default function ({ isClient }) { babelrc: false, cacheDirectory: !!this.dev })) + + // https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations let config = { postcss: this.options.build.postcss, loaders: { 'js': 'babel-loader?' + babelOptions, - 'css': 'vue-style-loader!css-loader', - 'less': 'vue-style-loader!css-loader!less-loader', - 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', - 'scss': 'vue-style-loader!css-loader!sass-loader', - 'stylus': 'vue-style-loader!css-loader!stylus-loader', - 'styl': 'vue-style-loader!css-loader!stylus-loader' + 'css': styleLoader.call(this, 'css'), + 'less': styleLoader.call(this, 'less', 'less-loader'), + 'sass': styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax'), + 'scss': styleLoader.call(this, 'sass', 'scss-loader'), + 'stylus': styleLoader.call(this, 'stylus', 'stylus-loader'), + 'styl': styleLoader.call(this, 'stylus', 'stylus-loader') }, - preserveWhitespace: false + preserveWhitespace: false, + extractCSS: extractStyles.call(this, 'vue') } // Return the config return config diff --git a/package.json b/package.json index 51ca8a4b19..089e5e938a 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "compression": "^1.6.2", "css-loader": "^0.28.0", "debug": "^2.6.6", + "extract-text-webpack-plugin": "^2.1.0", "file-loader": "^0.11.1", "friendly-errors-webpack-plugin": "^1.6.1", "fs-extra": "^3.0.0",