vue-cliから見てnode.jsとwebpackの動作
前言
vue-cliとwebpackを組み合わせた足場は使いやすいが、その中の構成とnpmパッケージの参照を初めて使用するといつもぼんやりしていて、この文章はいくつかの関連モジュールの簡単な分析である.
主な目的は私自身のwebpackとnodeを深めることです.jsの認知.
本文
1.プロジェクト構造
vue-cliのプロファイルは主にbuildとconfigフォルダにあり、configフォルダには主に環境変数、webpackのパスなどのパラメータが配置されています.
2.npm run devから
ローカルデバッグでは、一般的にホット・リロードが必要であり、ローカル・サーバを作成するためによく使用されるコマンドは
dev-server.js
ではdev-serverを見てみましょうjsはいったい何をしたのか.
正直に言うと、expressを利用してサービスを作成し、特定のポートを傍受し、express()を利用する.use()はwepackのミドルウェアを使用します.エージェントは、ローカルlocalhostからデータを取得し、他のエージェントアドレスからデータを取得するように変更できます.
次に何をしたかを見続けます.
はい、今express()がポートを傍受して、自動的に開くこともできますが、中身が入っていませんね.そして、ホットリロードなどはどうやってできますか.
まだいくつかあります.
3.webpackを見てみましょう.conf構成
まずmergeモジュールについて説明します.
utils.js
ツールクラスの関数をもう一度見てみましょう.
webpack.base.conf
いくつかのツールが定義されています.具体的には、次のように構成されています.
webpack.dev.conf
開発された構成は主に基礎構成が特殊な開発構成オブジェクト(主にプラグイン)を再結合したものである.
webpack.prod.conf
webpack.test.conf
3.npm run build
このコマンドを実行するのは
build.js
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.
'
))
})
})