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