vue-cliから見てnode.jsとwebpackの動作

28401 ワード

前言
vue-cliとwebpackを組み合わせた足場は使いやすいが、その中の構成とnpmパッケージの参照を初めて使用するといつもぼんやりしていて、この文章はいくつかの関連モジュールの簡単な分析である.
主な目的は私自身のwebpackとnodeを深めることです.jsの認知.
本文
1.プロジェクト構造
vue-cliのプロファイルは主にbuildとconfigフォルダにあり、configフォルダには主に環境変数、webpackのパスなどのパラメータが配置されています.
| -- build
    | -- build.js
    | -- check-version.js
    | -- dev-client.js
    | -- dev-server.js
    | -- utils.js
    | -- vue-loader.conf.js
    | -- webpack.base.conf.js
    | -- webpack.dev.conf.js
    | -- webpack.prod.conf.js
    | -- webpack.test.conf.js
| -- config
    | -- dev.env.js
    | -- index.js
    | -- prod.env.js
    | -- test.env.js
...
...

2.npm run devから
ローカルデバッグでは、一般的にホット・リロードが必要であり、ローカル・サーバを作成するためによく使用されるコマンドはnpm run devです.package.jsonによると、実際に実行されているコマンドはnode build/dev-server.jsであることがわかります.
dev-server.js
ではdev-serverを見てみましょうjsはいったい何をしたのか.
正直に言うと、expressを利用してサービスを作成し、特定のポートを傍受し、express()を利用する.use()はwepackのミドルウェアを使用します.エージェントは、ローカルlocalhostからデータを取得し、他のエージェントアドレスからデータを取得するように変更できます.
//           ,        npm node      
require('./check-versions')()

// ./check-versions.js
//      ,chalk(            )、semver(        ),child_process(      )
var chalk = require('chalk')
var semver = require('semver')

//   package.json  node npm    
var packageConfig = require('../package.json')

// child_process.execSync(cmd)            cmd  
function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

//         
var versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version),
    versionRequirement: packageConfig.engines.node
  },
  {
    name: 'npm',
    currentVersion: exec('npm --version'),
    versionRequirement: packageConfig.engines.npm
  }
]

//       
module.exports = function () {
  var warnings = []
  for (var i = 0; i < versionRequirements.length; i++) {
    var mod = versionRequirements[i]

    // semver          
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {

    //    ,    warning  
      warnings.push(mod.name + ': ' +
        chalk.red(mod.currentVersion) + ' should be ' +
        chalk.green(mod.versionRequirement)
      )
    }
  }

    // warning   ,      warning
  if (warnings.length) {
    console.log('')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()
    for (var i = 0; i < warnings.length; i++) {
      var warning = warnings[i]
      console.log('  ' + warning)
    }
    console.log()

    //           (            ,      )
    process.exit(1)
  }
}

次に何をしたかを見続けます.
//   opn (         uri  ),express(web  )
var opn = require('opn')
var express = require('express')

//         
var app = express()

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

//     NODE_ENV  ,  ("production";"develpoment" "testing")
if (!process.env.NODE_ENV) {
  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

//    ,           (8080)
var port = process.env.PORT || config.dev.port

// config        true
var autoOpenBrowser = !!config.dev.autoOpenBrowser

// app    ,      (      )
module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }

  // when env is testing, don't need open it
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
})

はい、今express()がポートを傍受して、自動的に開くこともできますが、中身が入っていませんね.そして、ホットリロードなどはどうやってできますか.
//       url  
var uri = 'http://localhost:' + port

//   process.env.NODE_ENV       webpack  
var webpackConfig = process.env.NODE_ENV === 'testing'
  ? require('./webpack.prod.conf')
  : require('./webpack.dev.conf')

var webpack = require('webpack')  
var compiler = webpack(webpackConfig)

// webpack-dev-middler  webpack      ,  webpack            ,    
//       ,         
//         ,              ,            。                    。
// Usage: app.use(webpackMiddleWare(webpack({}))
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

app.use(devMiddleware)

//        
devMiddleware.waitUntilValid(function () {
  console.log('> Listening at ' + uri + '
'
) }) // webpack 。 。 var hotMiddleware = require('webpack-hot-middleware')(compiler, { log: () => {} }) app.use(hotMiddleware) // html-webpack-plugin , compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) }) // , "/static" var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) // express.static , , : app.use(staticPath, express.static('./static'))

まだいくつかあります.
//          
var proxyMiddleware = require('http-proxy-middleware')

//      ,      
var proxyTable = config.dev.proxyTable

/**
*          
* var express = require('express');
* var proxy = require('http-proxy-middleware');
* 
* var app = express()
*
* app.user('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
* app.listen(3000);
* http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
*        ,/api             host,      
*/
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  //            host
  app.use(proxyMiddleware(options.filter || context, options))
})

//         H5  API  
//             index    html  ,               404。
app.use(require('connect-history-api-fallback')())

3.webpackを見てみましょう.conf構成
まずmergeモジュールについて説明します.
var merge = require('webpack-merge')
module.exports = merge(baseWebpackConfig, {})

utils.js
ツールクラスの関数をもう一度見てみましょう.
//             bundle               
//   
// const ExtractTextPlugin = require("extract-text-webpack-plugin");

// module.exports = {
//  module: {
//    rules: [
//      {
//        test: /\.css$/,
//        use: ExtractTextPlugin.extract({
//          fallback: "style-loader",
//          use: "css-loader"
//        })
//      }
//    ]
//  },
//    plugins: [
//      new ExtractTextPlugin("styles.css"),
//    ]
// }
//             *.css                (styles.css),css   js        
var ExtractTextPlugin = require('extract-text-webpack-plugin')

//            ,       
exports.assetsPath = function (_path) {
  var assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory
  return path.posix.join(assetsSubDirectory, _path)
}

/**
*** @
***
***
***
***
***
***
***
*** @return: {
***     css: [
                'vue-style-loader', 
                [
                    {
                        loader: 'css-loader',
                        options: {
                            minimize: process.env.NODE_ENV === 'production',
                            sourceMap: options.sourceMap
                        }
                    }
                ]
             ]
*** }
*** 
*/
exports.cssLoaders = function (options) {
  options = options || {}

  var cssLoader = {
    loader: 'css-loader',
    options: {
      minimize: process.env.NODE_ENV === 'production',
      sourceMap: options.sourceMap
    }
  }

  // generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
    var loaders = [cssLoader]
    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

  // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less'),
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
  var output = []
  var loaders = exports.cssLoaders(options)
  for (var extension in loaders) {
    var loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }
  return output
}

webpack.base.conf
var path = require('path')

//    
var utils = require('./utils')
var config = require('../config')

// vue-loader options  
var vueLoaderConfig = require('./vue-loader.conf')

//   dir       
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

いくつかのツールが定義されています.具体的には、次のように構成されています.
const webpackConfig = {

  //     ,webpack               
  entry: {
    app: './src/apps/index.js'
  },

  //     
  //   path: '../dist'
  // publicPath: '/'
  // '../dist/'
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },

  // extensions,         ,         
  // alias,  
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'apps': path.resolve(__dirname, '../src/apps'),
      'common': path.resolve(__dirname, '../src/common'),
    }
  },

  //     ,webpack      (       )   js,     loader
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

webpack.dev.conf
開発された構成は主に基礎構成が特殊な開発構成オブジェクト(主にプラグイン)を再結合したものである.
//   webpack          HTML     bundles
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

module.exports = merge(baseWebpackConfig, {

    //exports.styleLoaders = function (options) {
    //  var output = []
    //  var loaders = exports.cssLoaders(options)
    //  for (var extension in loaders) {
    //    var loader = loaders[extension]
    //    output.push({
    //      test: new RegExp('\\.' + extension + '$'),
    //      use: loader
    //    })
    //  }
    //  return output
    //}

  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
  },

  devtool: '#cheap-module-eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),

    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),

    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true,
      chunks: ['app']
    }),
    new FriendlyErrorsPlugin()
  ]
})

webpack.prod.conf
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')

//    webpack        css         
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

webpack.test.conf
3.npm run build
このコマンドを実行するのはnode build/build.jsです.
build.js
require('./check-versions')()

process.env.NODE_ENV = 'production'

// ora                  
var ora = require('ora')
var spinner = ora('building for production...')
spinner.start()
spinner.stop()
var rm = require('rimraf')

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, function (err, stats) {
    spinner.stop()
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }) + '

'
) console.log(chalk.cyan(' Build complete.
'
)) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.
'
+ ' Opening index.html over file:// won\'t work.
'
)) }) })