Author: Wang Hongyu, Didi Public Front End Team

preface

As Vue users we are familiar with vuE-CLI, but we may pay little attention to its Webpack configuration, today we bring you vuE-CLI# 2.0 Webpack configuration analysis

Vue-cli introduction, installation we do not repeat here, it is not familiar with the students can directly visit vue-CLI view

The directory structure

. ├ ─ ─ the README. Md ├ ─ ─ build │ ├ ─ ─ build. Js │ ├ ─ ─ check - versions. Js │ ├ ─ ─ dev - client. Js │ ├ ─ ─ dev - server. Js │ ├ ─ ─ utils. Js │ ├ ─ ─ webpack. Base. Conf. Js │ ├ ─ ─ webpack. Dev. Conf., js │ └ ─ ─ webpack. Prod. Conf., js ├ ─ ─ the config │ ├ ─ ─ dev. The env. Js │ ├ ─ ─ Index. Js │ └ ─ ─ the prod. Env. Js ├ ─ ─ index. The HTML ├ ─ ─ package. The json ├ ─ ─ the SRC │ ├ ─ ─ App. Vue │ ├ ─ ─ assets │ │ └ ─ ─ logo. The PNG │ ├ ─ ─ │ ├ ─ ├ ─ 08.07.02 (├ ─ 08.02Copy the code

The main focus of this article is

  • Build-the code for the compile task

  • Config – configuration file for Webpack

  • Package. json – Basic information about the project

The entrance

We can see this in package.json

"scripts": {
    "dev": "node build/dev-server.js"."build": "node build/build.js"."lint": "eslint --ext .js,.vue src"
}Copy the code

When we run NPM run dev/NPM run build, we run node build /dev/server. js or node build/build.js

dev-server.js

Let’s start with build/dev-server.js


// Check Node and NPM versions
require('./check-versions') ()// Get the default config/index.js configuration
var config = require('.. /config')

// If the Node environment cannot determine the current dev/product environment
// Use config.dev.env.node_env as the current environment

if(! process.env.NODE_ENV) process.env.NODE_ENV =JSON.parse(config.dev.env.NODE_ENV)

// Use the file path tool provided with NodeJS
var path = require('path')

/ / use the express
var express = require('express')

/ / use webpack
var webpack = require('webpack')

// a plug-in that forces the browser to open and jump to the specified URL
var opn = require('opn')

/ / use proxyTable
var proxyMiddleware = require('http-proxy-middleware')

// Use the dev environment's Webpack configuration
var webpackConfig = require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic

// If no run port is specified, use config.dev.port as the run port
var port = process.env.PORT || config.dev.port

// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware

// Use the config.dev.proxyTable configuration as the proxy configuration for proxyTable
var proxyTable = config.dev.proxyTable

// Start a service with Express
var app = express()

// Start webpack to compile
var compiler = webpack(webpackConfig)

// Start webpack-dev-middleware to temporarily store the compiled files in memory
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true.chunks: false}})// Enable Webpack-hot-middleware, also known as hot-reload
var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation'.function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit'.function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
// Attach the request configuration in the proxyTable to the started Express service
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(context, options))
})

// handle fallback for HTML5 history API
// Use connect-history-api-fallback to match the resource. If not, redirect to the specified address
app.use(require('connect-history-api-fallback') ())// serve webpack bundle output
// Mount the webpack-compiled files temporarily stored in memory to the Express service
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
// Attach hot-reload to the Express service
app.use(hotMiddleware)

// serve pure static assets
// Concatenate the static resource path of the static folder
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
// Provide a response service for static resources
app.use(staticPath, express.static('./static'))

// Let's use the Express service to listen for port requests and expose the service as an interface to dev-server.js
module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }
  var uri = 'http://localhost:' + port
  console.log('Listening at ' + uri + '\n')

  // when env is testing, don't need open it
  // If it is not a test environment, the browser automatically opens and jumps to our development address
  if(process.env.NODE_ENV ! = ='testing') {
    opn(uri)
  }
})Copy the code

webpack.dev.conf.js

We just used webpack.dev.conf.js and index.js in dev-server.js. Let’s look at webpack.dev.conf.js first

// config/index.js is also used
var config = require('.. /config') 

/ / use webpack
var webpack = require('webpack') 

// Use WebPack to configure merge plug-ins
var merge = require('webpack-merge') 

// Use some gadgets
var utils = require('./utils') 

/ / load webpack. Base. Conf
var baseWebpackConfig = require('./webpack.base.conf') 

// Use the html-webpack-plugin, which can automatically generate HTML and inject it into.html files
var HtmlWebpackPlugin = require('html-webpack-plugin') 

// add hot-reload related code to entry chunks
// Add hol-reload relative paths to the corresponding entry of webpack.base.conf
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})

// Merge our webpack.dev.conf.js configuration with webpack.base.conf.js configuration
module.exports = merge(baseWebpackConfig, {
  module: {
    / / use styleLoaders
    loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
  },
  // eval-source-map is faster for development
  // Use #eval-source-map as a development tool. This configuration can be detailed in a previous DDFE article
  devtool: '#eval-source-map'.plugins: [

    // definePlugin accepts strings to insert into the code, so you can write JS strings if you want
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.optimize.OccurenceOrderPlugin(),

    // The HotModule plugin will only revert to the corresponding page module when the page is changed. It will not redraw the entire HTML file
    new webpack.HotModuleReplacementPlugin(),

    // If NoErrorsPlugin is used, errors in the page will not block, but will be reported after compiling
    new webpack.NoErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin

    // Use index.html as an entry point to generate an index.html file after injecting HTML code
    new HtmlWebpackPlugin({
      filename: 'index.html'.template: 'index.html'.inject: true})]})Copy the code

webpack.base.conf.js

We see the introduction of webpack.base.conf.js in webpack.dev.conf.js, which looks very important, so we’ll have to look at config/index.js in the next chapter.

// Use NodeJS's own file path plug-in
var path = require('path') 

/ / into the config/index. Js
var config = require('.. /config') 

// Introduce some widgets
var utils = require('./utils') 

// Concatenate our workspace path to an absolute path
var projectRoot = path.resolve(__dirname, '.. / ') 

// Use the NodeJS environment as our compilation environment
var env = process.env.NODE_ENV

// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
// various preprocessor loaders added to vue-loader at the end of this file

// Whether to enable cssSourceMap in dev environment can be configured in config/index.js
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)

// Whether to enable cssSourceMap in production environment can be configured in config/index.js
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)

// Finally use cssSourceMap
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd

module.exports = {
  entry: {
      // Compile the file entry
    app: './src/main.js' 
  },
  output: {
      // The root path to compile the output
    path: config.build.assetsRoot, 
    // Release path of compiled output in official release environment
    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, 
    // Compile the output file name
    filename: '[name].js' 
  },
  resolve: {
    // Autocomplete extension
    extensions: [' '.'.js'.'.vue'].// Files or folders that are not auto-complete or processed
    fallback: [path.join(__dirname, '.. /node_modules')].alias: {
    // The default path agent, such as import Vue from 'Vue', is automatically looked for in 'vue/dist/vue.com.js'
      'vue': 'vue/dist/vue.common.js'.'src': path.resolve(__dirname, '.. /src'),
      'assets': path.resolve(__dirname, '.. /src/assets'),
      'components': path.resolve(__dirname, '.. /src/components')}},resolveLoader: {
    fallback: [path.join(__dirname, '.. /node_modules')]},module: {
    preLoaders: [
      // Preprocessed file and loader to use
      {
        test: /\.vue$/.loader: 'eslint'.include: projectRoot,
        exclude: /node_modules/
      },
      {
        test: /\.js$/.loader: 'eslint'.include: projectRoot,
        exclude: /node_modules/}].loaders: [
      // The file to process and the loader to use
      {
        test: /\.vue$/.loader: 'vue'
      },
      {
        test: /\.js$/.loader: 'babel'.include: projectRoot,
        exclude: /node_modules/
      },
      {
        test: /\.json$/.loader: 'json'
      },
      {
        test: /\.(png|jpe? g|gif|svg)(\? . *)? $/.loader: 'url'.query: {
          limit: 10000.name: utils.assetsPath('img/[name].[hash:7].[ext]')}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/.loader: 'url'.query: {
          limit: 10000.name: utils.assetsPath('fonts/[name].[hash:7].[ext]'}}]},eslint: {
    // esLint code checks configuration tools
    formatter: require('eslint-friendly-formatter')},vue: {
    //.vue file configures loader and tools (autoprefixer)
    loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }),
    postcss: [
      require('autoprefixer') ({browsers: ['last 2 versions'})]}}Copy the code

config/index.js

Now that we’ve analyzed webpack.base.conf.js, let’s take a look at config/index.js

Both dev and Production environments are configured in index.js

// see http://vuejs-templates.github.io/webpack for documentation.
// Not to repeat the introduction...
var path = require('path')

module.exports = {
  / / production environment
  build: { 
      // Use the compilation environment defined in config/prod.env.js
    env: require('./prod.env'), 
    index: path.resolve(__dirname, '.. /dist/index.html'), // Compile the input index.html file
    // Compile the output static resource root path
    assetsRoot: path.resolve(__dirname, '.. /dist'), 
    // Compile the output secondary directory
    assetsSubDirectory: 'static'.// Compile the root directory of the publishing online path. The value can be the resource server domain name or CDN domain name
    assetsPublicPath: '/'.// Whether to enable cssSourceMap
    productionSourceMap: true.// Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    // Whether to enable gzip
    productionGzip: false.// The file extension that needs to be compressed using gzip
    productionGzipExtensions: ['js'.'css']},/ / dev environment
  dev: { 
      // Use the compilation environment defined in config/dev.env.js
    env: require('./dev.env'), 
    // The port to run the test page
    port: 8080.// Compile the output secondary directory
    assetsSubDirectory: 'static'.// Compile the root directory of the publishing online path. The value can be the resource server domain name or CDN domain name
    assetsPublicPath: '/'.// Interface that requires proxyTable proxy (cross-domain)
    proxyTable: {}, 
    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    // Whether to enable cssSourceMap
    cssSourceMap: false}}Copy the code

NPM run dev: NPM run dev: NPM run dev: NPM run dev: NPM run dev

build.js

// https://github.com/shelljs/shelljs

// Check Node and NPM versions
require('./check-versions') ()// The shelljs plugin allows you to use the shell in the js of the Node environment
require('shelljs/global') 
env.NODE_ENV = 'production'

// No further details
var path = require('path') 

/ / loaded config. Js
var config = require('.. /config') 

// A nice loading plugin
var ora = require('ora') 

/ / load webpack
var webpack = require('webpack') 

/ / load webpack. Prod. Conf
var webpackConfig = require('./webpack.prod.conf') 

// Output information ~ Prompt the user to view this page in the HTTP service. Otherwise, the page is blank
console.log(
  ' Tip:\n' +
  ' Built files are meant to be served over an HTTP server.\n' +
  ' Opening index.html over file:// won\'t work.\n'
)

// Use ora to print loading + log
var spinner = ora('building for production... ') 
// Start loading animation
spinner.start() 

// Concatenate compile output file path
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
// Delete this folder (recursive delete)
rm('-rf', assetsPath)
// Create this folder
mkdir('-p', assetsPath)
// Copy the static folder to our compile output directory
cp('-R'.'static/*', assetsPath)

// Start compiling webpack
webpack(webpackConfig, function (err, stats) {
  // Successfully compiled callback function
  spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true.modules: false.children: false.chunks: false.chunkModules: false
  }) + '\n')})Copy the code

webpack.prod.conf.js

// No further details
var path = require('path')

/ / load the confi. Index. Js
var config = require('.. /config')

// Use some gadgets
var utils = require('./utils') 

/ / load webpack
var webpack = require('webpack') 

// Load the WebPack configuration merge tool
var merge = require('webpack-merge') 

/ / load webpack. Base. Conf. Js
var baseWebpackConfig = require('./webpack.base.conf') 

// a Webpack extension that extracts code and separates it from files
// If we want to package webPack as a file and separate CSS and JS, we need this plugin
var ExtractTextPlugin = require('extract-text-webpack-plugin')

// a plugin that can insert HTML and create new.html files
var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = config.build.env

/ / merge webpack. Base. Conf. Js
var webpackConfig = merge(baseWebpackConfig, {
  module: {
    // The loader to use
    loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true})},For more information on whether to use the #source-map development tool, see DDFE's previous article
  devtool: config.build.productionSourceMap ? '#source-map' : false.output: {
    // Compile the output directory
    path: config.build.assetsRoot,
    // Compile the output file name
    // We can add :6 after the hash to determine how many bits to use
    filename: utils.assetsPath('js/[name].[chunkhash].js'), 
    // Specifies the name of the output file of a file without an output name
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')},vue: {
    // Loader used to compile.vue files
    loaders: utils.cssLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true})},plugins: [
    // The plug-in to use
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    // definePlugin accepts strings to insert into the code, so you can write JS strings if you want
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // Compress js (also can compress CSS)
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false}}),new webpack.optimize.OccurrenceOrderPlugin(),
    // extract css into its own file
    // Separate the CSS files
    new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    // Input/output.html files
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html'.// Whether to inject HTML
      inject: true.// Compression mode
      minify: { 
        removeComments: true.collapseWhitespace: true.removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // split vendor js into its own file
    // Static file name for the output of a file with no specified output file name
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor'.minChunks: function (module, count) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '.. /node_modules'= = =))0)}}),// extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    // Static file name for the output of a file with no specified output file name
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'.chunks: ['vendor']]}}))// Use the following configuration with gzip enabled
if (config.build.productionGzip) {
  // Load compression-webpack-plugin
  var CompressionWebpackPlugin =  require('compression-webpack-plugin')
  // Add the following plugins to webpackconfig.plugins
  var reProductionGzipExtensions = '\ \. (' + config.build.productionGzipExtensions.join('|') + '$)'
  webpackConfig.plugins.push(
    // Use compression-webpack-plugin for compression
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]'.algorithm: 'gzip'.test: new RegExp(reProductionGzipExtensions), // Note: there is a bug in the code formatting here, and the source code is different
      threshold: 10240.minRatio: 0.8}}))module.exports = webpackConfigCopy the code

conclusion

At this point ~ our VUE-CLI# 2.0 Webpack configuration analysis file on the end of the explanation ~

We did not explain the detailed options of some plug-ins. Interested students can go to the NPM store to search for the corresponding plug-ins and check options ~


Welcome to DDFE GITHUB: github.com/DDFE wechat official account: wechat search the official account “DDFE” or scan the qr code below