As a novice who has used vue. js for some time, I believe that I have a little knowledge about the configuration of VUe-CLI, just like many friends who have just entered Vue. Later, through the study of Webpack, I also have a certain understanding of the configuration of scaffolding, so I want to share my achievements in this period of time with you, and hope to make progress with you.

Two points to note:

  1. To read this article, you need to know a little about WebPack, at least what entry, output, Module, plugins do, and some common loaders and plugins.
  2. This article uses the latest version of VUE, the configuration may be different from everyone, but the gap will not be too big, does not affect the reading;

Starting a.

Start with a simple map of your brain:



Vue-cli has two files — build and config: The build file contains how the scaffold webpack should be configured in the development and production environments. The config file contains the webPack configuration values in the build file. In other words, the values of the Webpack configuration under build are not available until they are imported into the Config.

There are three files in the config folder:

  • Dev.env.js: Export development environment name;
  • Prod.env.js: name of the exported production environment.
  • Index.js: exports the configuration of different environments.


There are seven files in the Build folder:

  • Build. js: the entry file at compile time, which when executing NPM run build is actually executing node build/build.js (in package.json);
  • Check – version. js: File executed during code compilation to verify the versions of node and NPM. If the versions do not match, stop compilation.
  • Js: this file has two functions. It is used as a configuration for the Vue-loader. The other is used to configure the loader for the development and production environments.
  • Vue-loader.conf. js: The configuration of vue-loader, used in webpack.base.conf.js;
  • Webpack. Base. Conf. Js: Vue – Basic Webpack configuration for CLI scaffolding, The “Don’t repeat yourself DRY” principle is implemented by merging the webpack.dev.conf.js and webpack.prod.conf.js configuration files (in a way I’ll cover in the next chapter), The same code will not be configured in different environments “.
  • Webpack.dev.conf.js: Configuration of WebPack in the development environment;
  • Webpack.prod.conf. js: Configuration of WebPack in production environment;


2. The config file

1. Prod. Env. Js:

// export an object that has a property of the current node environment, and the value is "production". Module. exports = {NODE_ENV:'"production"'}Copy the code

2. Dev. Env. Js:

// Export another object with property of the current Node environment and value of "development" const merge = require('webpack-merge')const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {  NODE_ENV: '"development"'})
Copy the code

  • The webpack-merge package is used to merge two configuration file objects and generate a new configuration file, somewhat similar to the object.assign () method in ES6. If conflicting properties are encountered during the merge, the value of the second parameter overrides the value of the first parameter.
  • The merge of webpack.base.conf.js with webpack.dev.conf.js and webpack.prod.conf.js also uses webpack-merge. Vue-cli takes a few common configurations and puts them in a file (webpack.base.conf.js), configes them differently for different environments, and finally merges them using Webpack-merge to reduce code duplication.

For more information on Webpack-merge click herewww.npmjs.com/package/web…

3.index.js:

I don’t think it’s necessary to post the code, but you can use the above brain map or your own project files to combine the code I’ll talk about later.

3. The build file

1. Check. Versions. Js:

Chalk is a package for writing different colors on the command line. You can use chalk. Yellow (" want to add colors to the text....") )
// to change the color of text;
const chalk = require('chalk')

// Semver is a semantically versioning NPM package, which is used to control versioning;
const semver = require('semver')const packageConfig = require('.. /package.json')

// A package for executing Unix commands
const shell = require('shelljs')

//child_process is the node.js module that provides the child process function. The execSync() method executes a CMD command synchronously.
// Call toString and trim methods on the returned value
function exec (cmd) {  
    return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {    name: 'node'.// The semver.clean() method returns a standard version number with whitespace removed, such as semver.clean(" =v1.2.3 ")
    // Return "1.2.3", semver has vaild, ancillary,gt,lt, and so on,
    / / here for https://npm.taobao.org/package/semver you can see more about semver method
    currentVersion: semver.clean(process.version),    
    versionRequirement: packageConfig.engines.node  
  }
]
// The shell.which method is to search the environment variable for parameters
if (shell.which('npm')) {  
    versionRequirements.push({    
        name: 'npm'.// Run the "NPM --version" command
        currentVersion: exec('npm --version'),     
        versionRequirement: packageConfig.engines.npm  
    }
)}

// The next part of the code is easier to understand
module.exports = function () {  const warnings = []
  for (let i = 0; i < versionRequirements.length; i++) {    const mod = versionRequirements[i]
    if(! semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push(mod.name +':' +        chalk.red(mod.currentVersion) + ' should be ' +        chalk.green(mod.versionRequirement)      )    }  }
  if (warnings.length) {    console.log(' ')    console.log(chalk.yellow('To use this template, you must update following to modules:'))    console.log()
    for (let i = 0; i < warnings.length; i++) {      const warning = warnings[i]      console.log(' ' + warning)    }
    console.log()    process.exit(1)}}Copy the code

2.utils.js:

const path = require('path')const config = require('.. /config'// This plugin is used to link the generated CSS file into the HTML. If not, the CSS code will be put in the style of the head tag const ExtractTextPlugin = require('extract-text-webpack-plugin')

const packageConfig = require('.. /package.json') // process.env.node_env is an environment variable declared by webpack.dev/prod.conf.js; // This means to determine whether the current development environment, If so, assign the build.assetsSubDirectory or //dev. AssetsSubDirectory value to assetsSubDirectory exports.assetsPath = in config index.jsfunction (_path) {  
    const assetsSubDirectory = process.env.NODE_ENV === 'production'? Config. Build. AssetsSubDirectory: config. Dev. AssetsSubDirectory / / path. The posix. Join is the path of the join a compatibility method and the effect is a path joining together, here is returned"static/_path"
  returnPath.posix. join(assetsSubDirectory, _path)} //cssLoaders is used to export a configuration for use by the Options of the Ue-Loader; exports.cssLoaders =function (options) {  
    options = options || {}
    const cssLoader = {    
        loader: 'css-loader',    
        options: {      
            sourceMap: options.sourceMap    
        }  
    }
    const postcssLoader = {    
        loader: 'postcss-loader',    
        options: {      
            sourceMap: options.sourceMap    
        }  
    }
function generateLoaders (loader, loaderOptions) {    
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
    if (loader) {     
         loaders.push({        
            loader: loader + '-loader',       
            options: Object.assign({}, loaderOptions, {          
                sourceMap: options.sourceMap        
            })      
        })    
    }
    if (options.extract) {      
        return ExtractTextPlugin.extract({        
            use: loaders,        
            fallback: 'vue-style-loader'})}else {      
        return ['vue-style-loader'].concat(loaders)    
        }  
    }
    return {    
        css: generateLoaders(),    
        postcss: generateLoaders(),    
        less: generateLoaders('less'),    
        sass: generateLoaders('sass', { indentedSyntax: true }),    
        scss: generateLoaders('sass'),    
        stylus: generateLoaders('stylus'),    
        styl: generateLoaders('stylus'}} // styleLoaders is used to provide all the CSS related loader configurations for webPack. It also uses the cssLoaders() method; exports.styleLoaders =function (options) {  
    const output = []  const loaders = exports.cssLoaders(options)
    for (const extension in loaders) {    
        const loader = loaders[extension]    
        output.push({      
            test: new RegExp('\ \. + extension + '$'),     
            use: loader    
        })  
    }
  return output
}

//'node-notifier'Is a cross-platform system notification page, when error is encountered, it can use system native push give you push information exports. CreateNotifierCallback = () = > {const notifier = require ('node-notifier')
    return (severity, errors) => {    
        if(severity ! = ='error') return
        const error = errors[0]    
        const filename = error.file && error.file.split('! ').pop()
        notifier.notify({      
            title: packageConfig.name,      
            message: severity + ':' + error.name,      
            subtitle: filename || ' ',     
             icon: path.join(__dirname, 'logo.png')}}}Copy the code

For those of you who don’t know what the cssLoaders() and styleLoaders() methods return, let me just write it here:

  • The cssLoaders method returns different values depending on whether the parameters (options) passed in have the extract property. If you look at the code below you will see that the extract property is true in production mode and false in development mode. In other words, in production mode, an array like this is returned:

ExtractTextPlugin.extract({
    use: ["css-loader"."less-loader"."sass-loader"...],
    fallback: 'vue-style-loader'
})
Copy the code

This CSS code is packaged into HTML as a link. Of course, the value of use should exactly look like this:

[ { loader: ‘css-loader’, options: { sourceMap: true } }, { loader: ‘less-loader’, options: { sourceMap: true } } ]

I’ll just abbreviate it for convenience.

In development mode, cssLoaders returns:

["vue-style-loader","css-loader","less-loader","sass-loader"...] // I'm going to abbreviate it

  • The styleLoaders method returns a simple value. It returns the usual webPack module configuration format:

[{test: /\.css$/,
        use: [ 'style-loader'.'css-loader']},... ]Copy the code

3.vue-loader.conf.js:

const utils = require('./utils')
const config = require('.. /config') // Assign isProduction for different environments: production istrue, the development environment isfalse
const isProduction = process.env.NODE_ENV === 'production'// Different environments aresourceMapEnabled assignment: here is bothtrue
const sourceMapEnabled = isProduction ? Config. Build. ProductionSourceMap: config. Dev. CssSourceMap / / export vue - loader configuration, here we use the utils file cssLoaders (); module.exports = { loaders: utils.cssLoaders({sourceMap: sourceMapEnabled,    
        extract: isProduction  
    }),  
    cssSourceMap: sourceMapEnabled, cacheBusting: config.dev.cachebusting, //transformToRequire; MapEnabled, cacheBusting: config.dev.cachebusting, //transformToRequire; transformToRequire: { video: ['src'.'poster'].source: 'src',    
        img: 'src',    
        image: 'xlink:href'}}Copy the code

4.webpack.base.conf.js

const path = require('path')
const utils = require('./utils')
const config = require('.. /config')
const vueLoaderConfig = require('./vue-loader.conf'// The resolve function returns the current directory".. /dir"This folder, __dirname refers to the current file pathfunction resolve (dir) {  return path.join(__dirname, '.. '} module.exports = {context: path.resolve(__dirname,'.. / '), // entry file entry: {app:'./src/main.js'Output: {path: config.build.assetsRoot, filename:'[name].js',    
        publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: {// Automatically parse extensions, such as import files,js,vue,json, and omit extensions: ['.js'.'.vue'.'.json'].alias: {// Precise matching, use vue instead of vue/dist/vue.esm.js'vue$': 'vue/dist/vue.esm.js', // Use @ instead of SRC. When importing files under SRC, you can use import XXfrom"@/xx"
            The '@': resolve('src'),}}, // loadermodule: {rules: [{test: /\.vue$/,        
                loader: 'vue-loader',        
                options: vueLoaderConfig      
            },      
            {        
                test: /\.js$/,        
                loader: 'babel-loader',        
                include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]},... ] }, // The options in node are all node.js global variables and modules. This is to prevent WebPack from injecting node.js into vue: {setImmediate: false,    
        dgram: 'empty',    
        fs: 'empty',    
        net: 'empty',    
        tls: 'empty',    
        child_process: 'empty'}}Copy the code

5.webpack.dev.conf.js

const utils = require('./utils')
const webpack = require('webpack')
const config = require('.. /config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf'// a plugin for copying resources const CopyWebpackPlugin = require('copy-webpack-plugin')

const HtmlWebpackPlugin = require('html-webpack-plugin'// A friendlier plugin for displaying WebPack error messages const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // a package to automatically retrieve ports const portFinder = require('portfinder')

const HOST = process.env.HOSTconst PORT = process.env.PORT && Number(process.env.PORT)

const devWebpackConfig = merge(baseWebpackConfig, {  
    module: {    
        rules: utils.styleLoaders({ 
            sourceMap: config.dev.cssSourceMap,
            usePostCSS: true})}, devtool: config.dev.devtool, devServer: {clientLogLevel:'warning'.historyApiFallback: {      
            rewrites: [        
                { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },      
            ],    
        },    
        hot: true,    
        contentBase: false,   
        compress: true,    
        host: HOST || config.dev.host,    
        port: PORT || config.dev.port,    
        open: config.dev.autoOpenBrowser,   
        overlay: config.dev.errorOverlay      
        ? { warnings: false, errors: true }      
        : false,    
        publicPath: config.dev.assetsPublicPath,    
        proxy: config.dev.proxyTable,    
        quiet: true, watchOptions: { poll: config.dev.poll, } }, plugins: New webpack.defineplugin ({process.env: require() {process.env: require();'.. /config/dev.env')}), / / module hot replacement plugin, modify the module does not need to refresh the page. New webpack HotModuleReplacementPlugin (), / / when using HotModuleReplacementPlugin, This plugin will display the correct relative path to the new webpack. NamedModulesPlugin (), / / when compile error, using NoEmitOnErrorsPlugin to skip the output stage, This will ensure that the output error resource will not include new webpack. NoEmitOnErrorsPlugin (), new HtmlWebpackPlugin ({filename:'index.html',      
            template: 'index.html',      
            inject: true}), // copy the static folder and its contents to the development mode path. For example, there is a static img folder with an image in it. localhost:8080/static/img/logo.png new CopyWebpackPlugin([ { from: path.resolve(__dirname,'.. /static'),        
                to: config.dev.assetsSubDirectory,        
                ignore: ['*'[}])]}) // This is where the port is searched and the error is handled after NPM run dev'friendly-errors-webpack-plugin'Module.exports = new Promise((resolve, reject) => { portfinder.basePort = process.env.PORT || config.dev.port portfinder.getPort((err, port) => {if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})Copy the code

Two notes about devServer:

  • ContentBase is used to tell the server where to serve static content. The reason we use false here is because we use the “copy-webpack-plugin”, so we don’t need to use contentBase.
  • When quiet is enabled (true), nothing except the initial startup information is printed to the console. Even webpack errors or warnings are not visible in the console. But we can use the ‘friendly-errors-webpack-plugin’ to set it to true.


6.webpack.prod.conf.js 

The configuration of webpack.prod.conf.js is pretty simple, just like the configuration of webpack.dev.conf.js, but with a few plugins:

  • UglifyJsPlugin is used to compress JS code
  • The optimize- csS-assets – Webpack-Plugin is used to compress CSS code
  • HashedModuleIdsPlugin generates a four-digit hash based on the relative path of the module as the module ID
  • ModuleConcatenationPlugin can precompile all modules into a package, to speed up the operation of the browser
  • CommonsChunkPlugin split the common module, vue split vendor, manifest and app three modules
  • Compression will webpack – plugin gzip compression
  • Webpack-bundle-analyzer allows you to look at packaging details, such as how many packages are typed, how big each package is, and so on

With that said, the introduction to plugins is over. The final file, build.js, is the entry file for NPM Run build.

In the same way, the build.js file doesn’t really have anything to say about it, other than executing the webpack.prod.conf.js file and getting an error on the command line. It is important to note that build.js has introduced the package “Rimraf”, which is used to clear out the dist file at each build to avoid folder duplication and confusion at multiple builds.

End four.

In fact, the analysis of VUe-CLI configuration is basically finished here. I believe that friends who know Webpack must look very simple. The main trouble of configuration is that the low coupling often leads to the need to go back and forth to read the configuration.

The bad news is that webpack4.0 is already online at the time of this release, and the vue-cli new version is in Beta, so just read this article to get an idea, and the configuration will be updated soon……