The article brief introduction
Build your own webpack4.0 project by studying vue-cli scaffolding. Understand webPack principle, deployment environment, configuration process, etc.
Learning goals
- Learn about the WebPack configuration of VUE-CLI# 2.0
- Understand webpack principles
- Familiar with webPack deployment environment
- Familiar with webPack configuration process
The learning process
A detailed explanation of Webpack
What is a webpack?
Webpack is a packaging tool whose purpose is that all static resources can be packaged. One might ask why webpack? Webpack is the cornerstone of modern front-end technology, and conventional development methods such as jquery, HTML, and CSS static web development have fallen behind. This is the age of MVVM, data-driven interfaces. Webpack packs all kinds of new and useful technologies in modern JS development.
Core WebPack concepts
Webpack has four core concepts that you need to understand well to get started with Webpack. These are Entry, Output, Loader and Plugins, and Module. These four core concepts are described in detail.
- Entry: Entry, the first step in the build that Webpack performs will start with Entry, which can be abstracted into input. Module: Module, in Webpack everything is a Module, a Module corresponds to a file. Webpack recursively finds all dependent modules starting from the configured Entry.
- Output: Outputs the result after Webpack has gone through a series of processes and produced the final desired code.
- Loader: module converter, used to convert the original content of a module into new content as required.
- Plugin: Extension Plugin that injects extension logic at specific points in the Webpack build process to change the build result or do what you want.
- Module: Module, in Webpack everything is a Module, a Module corresponds to a file. Webpack recursively finds all dependent modules starting from the configured Entry.
Webpack executes the process
After webpack starts, it will recursively parse all modules that entry depends on in the module configured in the entry. After finding a module, it will find the corresponding conversion rule according to the configured Loader. After module transformation, the modules that the current module depends on will be parsed. These modules will be grouped into entry groups. One entry and all modules that depend on each other are just one chunk. Webpack executes plugin logic at appropriate times throughout the process.
Webpack and gulp
Webpack is often compared to Gulp. To be honest, webPack developed to 4.0, these two functions are basically similar before, should have everyone, but the processing mechanism is different.
Gulp is a tool chain, build tool, can work with a variety of plug-ins to do JS compression, CSS compression, less compilation instead of manual implementation of automation. So its main functions are:
- 1. Build tools
- Automation 2.
- 3. Increase efficiency
Gulp focuses on the control and management of the whole process of front-end development (like pipeline). We can configure tasks that are not functioning in Gulp by using gulp.task() in Gulp. Such as starting server, sASS /less precompilation, file consolidation and compression, etc.) to allow gulP to implement different functions to build the whole front-end development process.
Webpack is a file packaging tool, can put the project of a variety of JS files, CSS files and other packaged synthesis of one or more files, mainly used for modular scheme, pre-compiled module scheme. So its main role is: 1. Package tools 2. Modular identification 3
Webpack all resources in development (pictures, JS files, CSS files, etc.) can be regarded as modules. Initially, Webpack itself is designed for front-end JS code packaging, and later extended to other resources packaging processing. Webpack handles resources through loaders and plugins.
Analyze the Webpack configuration of vuE-CLI scaffolding
Vue-cli directory structure
├─ readme.md ├─ Build build (webpack) code │ ├─ Build.js // Production Environment Build code │ ├─ check-versions Dev - client. Js / / thermal overload related │ ├ ─ ─ dev - server. Js / / build local server │ ├ ─ ─ utils. Js / / build tools related │ ├ ─ ─ webpack. Base. Conf., js / / webpack based configuration │ ├ ─ ─ webpack. Dev. Conf. Js / / webpack development environment configuration │ └ ─ ─ webpack. Prod. Conf., js / / webpack production environment configuration ├ ─ ─ the config / / project development environment configuration │ ├ ─ ─ Dev. Env. Js / / development environment variable │ ├ ─ ─ index. The js / / project some configuration variables │ └ ─ ─ the prod. The env. Js / / production environment variable ├ ─ ─ index. The HTML / / entry page ├ ─ ─ package. The json / / Project basic information ├ ─ ─ SRC / / source directory │ ├ ─ ─ App. Vue / / the entry file │ ├ ─ ─ assets │ │ └ ─ ─ logo. The PNG │ ├ ─ ─ the components / / vue common components │ │ └ ─ ─ Hello.vue │ ├ ─ ├ ─ 02.txt // load all kinds of public ├ ─ 02.txt // static files, like some photos, json data, etcCopy the code
Vue-cli basic functions
- Handle sASS/SCSS/LESS /stylus conversion to CSS
- Parse files with CSS suffix, compress extracted CSS, and compatible CSS plug-ins
- Compressed JS files
- Simplifies the creation of HTML files and introduces external resources
- Compressed file, can be converted to base64 images
- Compatible with VUE files
- Babel translation es5
- Hot update at development time
Vue-cli webpack specific file analysis
The entry point can be seen in package.json
"scripts": {// dev webpack-dev-server --inline mode --progress displays progress --config specifies a configuration file (default is webpack.config.js)."dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"."start": "npm run dev", // Jest test"unit": "jest --config test/unit/jest.conf.js --coverage", // E2E tests"e2e": "node test/e2e/runner.js", // Run jEST tests and E2E tests"test": "npm run unit && npm run e2e", // eslint --ext specifies the extension and the corresponding file"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs"// node executes the build/build.js file"build": "node build/build.js"
},
Copy the code
The underlying implementation principle of Npm Script is to run Script commands by calling Shell. NPM run start is equivalent to running NPM run dev.
Another important feature of Npm Script is the ability to run executable modules installed in node_modules in the project directory.
For example, after installing webpack-dev-server into the project by using the command NPM I -d webpack-dev-server, /node_modules/. Bin /webpack-dev-server can be used to build webpack-dev-server./node_modules/.
NPM run dev specifies the build/webpack dev. Conf. Js configuration to start the service
Basic configuration file is build/webpack. Base. Conf. Js
-
1. Introduce various plug-ins and configurations, including build/vue-loader.conf.js configuration.
-
Create esLint rule configuration, enable by default
-
3. Export webPack configuration objects, including context, entry, output, resolve, rules under module, node configuration, etc
'use strict'Const path = require(const path = require('path'// const utils = require('./utils'// config file const config = require('.. /config'Const vueLoaderConfig = require()'./vue-loader.conf'/** * get the absolute path * @method resolve * @param {String} dir relative to the path of this file * @return{String} Absolute path */function resolve (dir) {
return path.join(__dirname, '.. ', dir)
}
module.exports = {
context: path.resolve(__dirname, '.. / '),
entry: {
app: './src/main.js'}, output: {path: config.build.assetsRoot, filename:'[name].js', //[] placeholder // Check whether the environment is production environment. If the environment is packaged, publicPath is prefixed with publicPath: process.env.NODE_ENV ==='production'? Config. Build. AssetsPublicPath: config. Dev. AssetsPublicPath}, resolve: {/ / automatic completion of extension extensions: ['.js'.'.vue'.'.json'], // Path aliasalias: {
'vue$': 'vue/dist/vue.esm.js'.The '@': resolve('src'),
'common': resolve('src/common'),
'components': resolve('src/components')}}, module: {// module rules: [{// process vue filestest: /\.vue$/,
loader: 'vue-loader', options: vueLoaderConfig}, {// Compile jstest: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client'}, {// Process image filestest: /\.(png|jpe? g|gif|svg)(\? . *)? $/, loader:'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]'}}, {// Process audio and video filestest: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/, loader:'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]'}}, {// Process font filestest: /\.(woff2? |eot|ttf|otf)(\? . *)? $/, loader:'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native). setImmediate: false, // prevent webpack from injecting mocks to Node native modules // that does not make sense for the client dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty'}}Copy the code
Development environment of the entrance to the file is the build/webpack dev. Conf. Js.
-
1. Introduced various dependencies, as well as variables and configurations in the config folder, and a utility function called build/utils.js.
-
2, merge the build/webpack. Base. Conf. Js configuration file,
-
3. Configure the development environment with some devServer, Plugin and other configurations.
-
4. Finally, a Promise is exported to find available ports to start the service according to the configured ports.
'use strict'
const utils = require('./utils')
const webpack = require('webpack'// config file const config = require('.. /config'// webpack configuration merge plugin const merge = require('webpack-merge')
const path = require('path') // webpac const baseWebpackConfig = require('./webpack.base.conf'// copy plugin const CopyWebpackPlugin = require('copy-webpack-plugin'Const HtmlWebpackPlugin = require() const HtmlWebpackPlugin = require('html-webpack-plugin'Const FriendlyErrorsPlugin = require(const FriendlyErrorsPlugin = require() const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'Const portFinder = require()'portfinder'// Get the local address const HOST = process.env.host // Get the PORT const PORT = process.env.port && Number(process.env.port) // Merge basic Webpack configuration const devWebpackConfig = merge(baseWebpackConfig, {module: {// cssSourceMaptrue
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster forDevelopment // The cheap-module-eval-source-map option is faster in the development environment // the cheap-module-eval-source-map option is configured in the development environment. https://webpack.docschina.org/configuration/devtool/#devtool
devtool: config.dev.devtool,
// these devServer options should be customized in/config/index.js devServer: {// Configure the logging level on the client side. This will affect the logging content you see in the browser developer tools console. / / clientLogLevel is enumerated types, one of the following difference value none | error | warning | info. // The default level is INFO, that is, all types of logs are output. If the value is set to None, no logs are output. clientLogLevel:'warning', / /historyThe ApiFallback Boolean Object is used to facilitate the development of single-page applications using the HTML5 History API. // It can be simpletrueOr any 404 response can be provided as an index.html page.historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html'},],}, // Enable hot update:true// contentBase Configures the DevServer HTTP server file root directory. // The default is the current execution directory, usually the project root directory, so in general you do not need to set this unless you have additional files that need to be served by DevServer contentBase:false, // since we use CopyWebpackPlugin. // compress Specifies whether to enable gzip compression. Boolean is a type. The default value isfalse. compress:trueFor example, if you want other devices on the LAN to access your local service, you can start DevServer with --host 0.0.0.0 or set it to 0.0.0.0. The HOST | | config. Dev. HOST, / / the port number This configuration is 8080 port: the port | | config. Dev. Port, / / open the browser, this configuration is not openfalseOpen: config. Dev. AutoOpenBrowser, / / is displayed in the browser to mask an error message Configuration is heretrue
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false, publicPath: config. Dev. AssetsPublicPath, / / {} proxy configuration here is empty, may need to configure the proxy: Config.dev. proxyTable, // With quiet enabled, nothing is printed to the console except the initial startup information. This also means that errors or warnings from Webpack are not visible on the console. Your application is running here: http://localhost:8080 quiet:true, // necessary for FriendlyErrorsPlugin
// webpack-dev-middleware
// watch: false// Enable Watch mode. This means that after the initial build, WebPack will continue to listen for changes to any parsed files. The Watch mode is disabled by default. Watch mode is enabled by default in webpack-dev-server and webpack-dev-middleware. // Watch mode watchOptions: {// or specify milliseconds for polling. // Is configured asfalsePoll: config.dev.poll,}}, plugins: [// defined for development environment new webpack.defineplugin ({'process.env': require('.. /config/dev.env')}), / / hot update plug-in. New webpack HotModuleReplacementPlugin (), / / hot update according to the specific module path. New webpack NamedModulesPlugin (), // HMR shows correct file namesinConsole on update. // In case of compilation errors, use NoEmitOnErrorsPlugin to skip the output phase. New webpack. NoEmitOnErrorsPlugin (), new HtmlWebpackPlugin ({/ / inject the default valuestrueThe script tag is located at the bottom of the body of the HTML file // body passtrueThe, header, script tags are inside the head tag //falseInstead of inserting the generated JS file, simply generate an HTML file named filename:'index.html',
template: 'index.html',
inject: true,
thymeleaf: require('.. /src/common/js/thymeleaf.js') | |""}), // copy static assets // copy static assets to the directory new CopyWebpackPlugin([{from: path.resolve(__dirname,'.. /static'),
to: config.dev.assetsSubDirectory,
ignore: ['*'Exports = new promise ((resolve, reject) => {// process.env.PORT can specify a PORT number on the command line. For example, PORT=2000 NPM run dev The access is http://localhost:2000 / / config. Dev. Port configuration here is 8080 portfinder basePort = process. The env. Port | | config. Dev. Port / / Portfinder.getport ((err, port) => {portFinder.getPort ((err, 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
Webpack build/production environment configuration webpack. Prod. Conf. Js
-
1, the introduction of some plug-in and configuration, which introduces the build/webpack base. Conf., js webpack basic configuration file,
-
Define the environment with DefinePlugin
-
3. Merge the basic configuration, define their own configuration webpackConfig, configure some rules under modules, devtools configuration, output configuration, some processing JS, extract CSS, compress CSS, output HTML plug-in, extract common code, etc. Plugins.
-
4. If gZIP is enabled and the corresponding plug-in is used,
-
5. If analysis of packaged plug-ins is enabled, use webpack-bundle-Analyzer,
-
6. Finally export the configuration.
'use strict'// introduce node path const path = require('path'Const utils = require(const utils = require('./utils'Const webpack = require()'webpack'// import config/index.js const config = require('.. /config'// merge webpack configuration plugin const merge = require('webpack-merge') // Basic webpack configuration const baseWebpackConfig = require('./webpack.base.conf'// Copy file and folder plugin const CopyWebpackPlugin = require('copy-webpack-plugin'// const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin'// const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin'// const UglifyJsPlugin = require('uglifyjs-webpack-plugin'// Define environment const env = process.env.node_env === with DefinePlugin'testing'// here is {NODE_ENV:'"testing"'}? require('.. /config/test.env'// here is {NODE_ENV:'"production"' }
: require('.. /config/prod.env') // Merge the basic webpack configuration const webpackConfig = merge(baseWebpackConfig, {module: {// Some rules for generating styles through the styleLoaders function utils.styleLoaders({ //sourceThe Map is heretrue
sourceThe Map: config. Build. ProductionSourceMap, / / whether the extract extract CSS into a single CSS file:trueUsePostCSS:true})}, // config usesourceMap trueHere is the#source-map
devtool: config.build.productionSourceMap ? config.build.devtool : false(dist path: config.build.assetsRoot, dist path: config.build.assetsRoot, dist path: config.build.assetsRoot, dist path: config.'js/[name].[chunkhash].js'// chunkhash chunkFilename: utils.assetspath ('js/[id].[chunkhash].js')}, plugins: / / http://vuejs.github.io/vue-loader/en/workflow/production.html / / define the exact environment new webpack DefinePlugin ({'process.env'// new UglifyJsPlugin({uglifyOptions: {uglifyOptions: {// warning warnings:false// Remove console.logfalse. The incomingtrueCalls to the console function are discarded. // drop_console:true// Drop debugger:true, // Defaults to null. You can pass in an array of names, and UglifyJs will assume that those functions have no side effects. // pure_funcs: ['console.log'.'console.log.apply'],}}, // Whether to enablesourceThe Map is heretrue
sourceThe Map: config. Build. ProductionSourceMap, / / parallel processing (processing) at the same time speed up the parallel:true}), // extract CSS into its own file // Extract CSS into its own file new ExtractTextPlugin({// Extract the corresponding filename to use the contenthash contenthash
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, / / increasing the file size: / / https://github.com/vuejs-templates/webpack/issues/1110 allChunks is by defaultfalse.trueRefers to extracting all chunks including dynamically introduced components. allChunks:true,}). // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be Deduped. new OptimizeCSSPlugin({// here the configuration istrue
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash forcaching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin New HtmlWebpackPlugin({// Output the HTML name filename: process.env.node_env ==='testing'
? 'index.html'Dist /index.html: config.build.index, // which template to use:'index.html', // Inject default valuetrueThe script tag is located at the bottom of the body of the HTML file // body passtrueThe, header, script tags are inside the head tag //falseInstead of inserting the generated JS file, simply generate an HTML file inject:true, // compress minify: {// removeComments removeComments:true// delete space and line collapseWhitespace:true// Remove double quotes from attributes in HTML tags.true/ / more configuration view HTML - minifier plug-in / / more options: / / https://github.com/kangax/html-minifier#options-quick-reference}, // Necessary to preferably work with multiple chunks via CommonsChunkPlugin // You can control the sequence of chunks before they are inserted into the HTML. Allowed values' none '|' auto '|' dependency '| {function// dependency dependency chunksSortMode:'dependency'}), // keep module.id stable when vendor modules does not change // Generate a common moduleID based on the code content to ensure that the source code is unchanged and the moduleID is unchanged. new webpack.HashedModuleIdsPlugin(), //enableScope reactor1 // enable the new webpack3 reactor1 feature, Role is to make the code files smaller, run faster new webpack. Optimize the ModuleConcatenationPlugin (), / / the split vendor js into its own file / / to extract the common code new webpack.optimize.Com monsChunkPlugin ({name:'vendor',
minChunks (module) {
// 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 hashThe from being updated whenever the app bundle is / / to extract the common code new webpack.optimize.Com monsChunkPlugin ({/ / put part of the public The manifest of the name:'manifest'// Passing 'Infinity' immediately generates a public chunk, but no modules. minChunks: Infinity }), // This instance extracts shared chunks from code splitted chunks and bundles them //in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk/ / to extract dynamic components new webpack.optimize.Com monsChunkPlugin ({name:'app'// If set to 'true', an asynchronous public chunk will be created as a submodule of 'options.name' and a sibling of 'options.chunks'. // It will be loaded in parallel with 'options.chunks'. You can do this by supplying the desired string instead of 'true'to change the name of the output file. async:'vendor-async'// If set to 'true', all the submodules of the public chunk will be selected children:true// Copy static assets // copy static assets to the corresponding directory. new CopyWebpackPlugin([ { from: path.resolve(__dirname,'.. /static'Configuration), / / here is static, the config. Build. AssetsSubDirectory, / / ignore. File at the beginning. Git ignore: ['*'[}])]}) // If gzip compression is started, use the compression-webpack-plugin. The configuration here isfalseNPM I compression-webpack-plugin -Dif (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig. Plugins. Push (new CompressionWebpackPlugin ({/ / asset: the target resource name. [file] will be replaced with the original resource. // [path] is replaced with the path of the original resource, and [query] is replaced with a query string. The default value is"[path].gz[query]". asset:'[path].gz[query]'// algorithm: can befunction(buf, callback) or a string. For strings follow zlib's algorithm (or Zopfli's algorithm). The default value is"gzip". algorithm:'gzip', / /test: All resources matching this re are processed. The default value is all resources. / / config. Build. ProductionGzipExtensions here ['js'.'css']
test: new RegExp(
'\ \. (' +
config.build.productionGzipExtensions.join('|') +
'$'), // threshold: Only resources larger than this value will be processed. The unit is bytes. The default value is 0. Threshold: 10240, // minRatio: Only resources whose compression ratio is lower than this value will be processed. The default value is 0.8. minRatio: 0.8})} / / output analysis of plug-in run NPM run the build -- the report / / config. Build. BundleAnalyzerReport here is process. The env. Npm_config_report / / The http://127.0.0.1:8888 link will automatically open after buildif(config. Build. BundleAnalyzerReport) {/ / more view link address: https://www.npmjs.com/package/webpack-bundle-analyzer const BundleAnalyzerPlugin = require('webpack-bundle-analyzer'). BundleAnalyzerPlugin webpackConfig. Plugins. Push (new BundleAnalyzerPlugin ())} / /, of course, also can use the official web site http://webpack.github.io/analyse/#home// Export webpackConfig module.exports = // export webpackConfig module.exports = // export webpackConfig module.exports = webpackConfigCopy the code
Build /utils.js utility functions
Above the build/webpack. Dev. Conf. Js mentioned introduced the build/utils js tool function.
-
AssetsPath returns the output path.
-
2. CssLoaders returns the corresponding CSS-Loader configuration.
-
3. StyleLoaders return the configuration of the corresponding processing style,
-
4. CreateNotifierCallback prompts a callback message if an error occurs while creating the startup service.
'use strict'
const path = require('path'Const config = require(const config = require()'.. /config'// Const ExtractTextPlugin = require('extract-text-webpack-plugin'// import package.json const packageConfig = require('.. /package.json'// return path exports.assetspath =function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'// Static? The config. Build. AssetsSubDirectory / / level 2 directory Here is the static: Config. Dev. AssetsSubDirectory / / generation cross-platform compatible path / / more view Node API link: https://nodejs.org/api/path.html#path_path_posix
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function(options) {// The options object passed in as an argument // {// //sourceThe Map is heretrue
// sourceMap: true, // // whether to extract CSS to a separate CSS file // extract:true// // whether to usePostCSS // usePostCSS:true
// }
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
source// Generate loader string to be used with extract text plugin // Create the corresponding loader configurationfunctionGenerateLoaders (loader, loaderOptions) {// Whether to use usePostCSS to determine whether to use postcssLoader const loaders = options.usePostCSS? [cssLoader, postcssLoader] : [cssLoader]if (loader) {
loaders.push({
loader: loader + '-loader'LoaderOptions: object. assign({}, loaderOptions, {sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if(options.extract) {// If the ExtractTextPlugin is used, the ExtractTextPlugin is used. https://webpack.docschina.org/plugins/extract-text-webpack-plugin/returnExtractTextPlugin. Extract ({/ / refers to need what kind of loader to compile files / / loader is used to transform resources into a CSS export modules (required) use: loaders, / / loader (for example'style-loader') applies when CSS is not extracted (i.e., an extra chunk, when allChunks:false)
fallback: 'vue-style-loader'})}else {
return ['vue-style-loader'].concat(loaders)
}
}
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'), // sass indentedSyntax is indented, similar to the following format //#main// color: blue // font-size: 0.3em sass: generateLoaders'sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders forStandalone Style Files (Outside of.vue) // Eventually returns the webpack CSS related configuration exports.styleloaders =function (options) {
// {
// // sourceThe Map is heretrue
// sourceMap: true, // // whether to extract CSS to a separate CSS file // extract:true// // whether to usePostCSS // usePostCSS:true
// }
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\ \. + extension + '$'),
use: loader
})
}
returnThe output} / / NPM run dev when something goes wrong, FriendlyErrorsPlugin plug-in configuration onErrors output error exports. CreateNotifierCallback = () = > {/ /'node-notifier'Const notifier = require(const notifier = require() - a cross-platform system notification page that provides notification to you when an error occurs'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
. Babelrc Babel configuration
Some transcoding rules are configured
{// presets specify transcoding rules"presets"[// preset -- babel-env is used for es6, ES7, ES8, and set AMD, COMMONJS modularized files not to be preset [// Preset -- babel-ENV is used for ES6, ES7, ES8, and Babel is used for preset."env", {
"modules": false."targets": {
"browsers": ["1%" >."last 2 versions"."not ie <= 8"]}}],"stage-2"], // The plugins attribute tells Babel which plug-ins to use, and plug-ins control how code is transformed. Transform-vue-jsx indicates that JSX syntax can be used in the project, and this plug-in transformation will be used"plugins": ["transform-vue-jsx"."transform-runtime"], // Transcoding rules to be executed in a particular environment, when the environment variable is belowtestIt overrides the Settings above"env": {
// testIf BABEL_ENV is not set, NODE_ENV is used. If BABEL_ENV is not set, development is used by default"test": {
"presets": ["env"."stage-2"]."plugins": ["transform-vue-jsx"."transform-es2015-modules-commonjs"."dynamic-import-node"]}}}Copy the code
Build Vue Project 1 from scratch — WebPack basics and VUE-CLI analysis
3. Build your own webpack4.0 project
1. Front-end environment construction
1. Perform initial configuration
mkdir webpack-vuedeom
cd webpack-vuedeom
npm init -y
Copy the code
2. Configuration webpack
NPM install webpack webpack-cli --save-dev NPM install [email protected] webpack-cli --save-dev // Install the specified version of NPM info webpacke All Webpack version information webpack -v // View the Web Pack version but view the current project webpack version according to webpack NPM webpack -vCopy the code
3. Create a configuration file that is similar to vuE-CLI to set up a directory for the project and distinguish the development and production environments
│ ├─ ├─ WebPack.dev.conf.js │ ├─ Webpack.dev.de.js │ ├─ Webpack.dev.de.js │ ├─ Webpack.dev.de.js // Webpack development environment configuration │ └ ─ ─ webpack. Prod. Conf., js / / webpack production environment configuration ├ ─ ─ index. The HTML / / entry page ├ ─ ─ package. The json / / project basic information ├ ─ ─ SRC / / Source directory │ ├ ─ ─ App. Vue / / the entry file │ ├ ─ ─ common │ │ ├ ─ ─ SCSS │ │ │ └ ─ ─ style.css. SCSS / / style file │ │ ├ ─ ─ js │ │ │ └ ─ ─ test. │ js / / js file ├ ─ ─ the router │ │ └ ─ ─ the router. The js / / routing configuration │ ├ ─ ─ the components / / vue common components │ │ └ ─ ─ hello.html vue │ └ ─ ─ the main, js / / program entry documents, ├ ─ └─ load all kinds of public components-static // static files, like some images, JSON data, etcCopy the code
2. Webpackp configuration process
Webpack.base.conf. js basic configuration file, which is initialized in the configuration file
var path = require('path');
var config = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname + '/dist'),// Package generated file address filename:'[name].build.js'// Generate file Ming publicPath:'/dist/'Module.exports = config; // public path of file exports}} module.exports = config;Copy the code
Configure the dev directive in package.json
"scripts": {
"build": "webpack build/webpack.base.conf.js "."start": "npm run dev"
},
Copy the code
Webpack by default knows js module, does not support other formats, need to introduce loader install loader command here do not write, NPM install on the line
- url-loader
If there are many images, many HTTP requests will be sent, which degrades page performance. This problem can be solved by urL-loader. Url-loader will encode the imported image and generate the dataURl. It is equivalent to translating the image data into a string of characters. Package the string into a file, and eventually you just need to import the file to access the image. Of course, encoding costs performance if the image is large. Therefore, url-loader provides a limit parameter. Files smaller than limit bytes are converted to DataURl, and files larger than limit are copied using file-loader.
const path = require('path')
module.exports ={
entry:'./src/main.js',
output:{
path:path.resolve(__dirname,'dist'),
filename:"demo.js"
},
module:{
rules:[
{
test:/\.(png|gif|jpe? g)/, // ? E Optional use: {loader:"url-loader",
options:{
name:"[name]_[hash].[ext]", / / /hash] resolves caching, [ext] preserves the file suffix of old fileslimitOutputPath () {outputPath () {outputPath () {outputPath ();"image/"}}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/, loader:'url-loader', options: {// The file size is smaller thanlimitParameter, url-loader will convert the file to DataURlimit: 10000,
name: '[name]-[hash:5].[ext]',
output: 'fonts/'}}]}}Copy the code
- Style – loader and CSS – loader
module: {
[
...
{
test: /\.css$/,
loader: 'style-loader! css-loader'}},Copy the code
- sass-loader node-sass
{
test: /\.(sa|sc|c)ss$/,
include: path.resolve(__dirname, '.. /src'//exclude: /node_modules/, //exclude the node_modules folder use: ['style-loader'.'css-loader'.'sass-loader']}Copy the code
– postCSs-loader PostCSS is a system program used to process CSS. It is not a CSS language processor, a POST-processing PROCESSOR, a new syntax, or an optimization tool. Postcss itself does two things: convert CSS to A CSS abstract syntax tree, which can be easily understood as converting CSS to JS; The second thing postCSS does is to call a plug-in to handle the abstract syntax tree, through which CSS is handled. Autoprefixer is a post-processor that you can use with preprocessors such as Sass, Stylus, or LESS. It works with plain CSS, and you don’t have to worry about which browsers to prefix, just focus on the implementation and use the latest W3C specifications. It is better when used with PostCSS.
{
test: /\.(sa|sc|c)ss$/,
include: path.resolve(__dirname, '.. /src'//exclude: /node_modules/, //exclude the node_modules folder use: ['style-loader'.'css-loader',
{
loader: "postcss-loader",
options:{
indent:"postcss",
plugins:[require('autoprefixer')],
browser:['last 10 versions']
}
},
{
loader:'sass-loader',
options:{
sourceMap: true}}}]Copy the code
In the browser, you can see that CSS is dynamically created by js < style > tags to write, because the project is large, there will be a lot of styles, all put in JS too large, can not do cache; So the extract-text-webpack-plugin is usually used to extract the scattered CSS and generate a main.css file. The extract-text-webpack-plugin does not support webpack4.0 or higher, so replace it with miniCssExtractPlugin
const miniCssExtractPlugin = require('mini-css-extract-plugin') // Create a CSS instance const cssExtractPlugin = new miniCssExtractPlugin({filename: devMode?'[name].[chunkhash:8].css':'[name].css',
chunkFilename: '[id].css'... {})test: /\.(sa|sc|c)ss$/,
include: path.resolve(__dirname, '.. /src'Limit), / / packaging, improve the packaging speed / / exclude: / node_modules /, / / exclude node_modules folders use: [miniCssExtractPlugin loader,'css-loader',
{
loader: "postcss-loader",
options:{
indent:"postcss",
plugins:[require('autoprefixer')],
browser:['last 10 versions']
}
},
{
loader:'sass-loader',
options:{
sourceMap: true}}] plugins: [cssExtractPlugin,]Copy the code
- Soucemap source code packaged code mapping, in dev mode, enabled by default,
> devtool: "cheap-module-evel-source-map", / / / / dev environment devtool:"cheap-module-source-map"Environment, / / proCopy the code
- While ES6 syntax is widely available, it’s not particularly compatible across browsers, so we need to convert ES6 to ES5 to avoid errors. Install babel-loader, babel-core, babel-env, and babel-polyfill
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
Copy the code
Configure the Babel rule.babelrc file
{
"presets": [["@babel/preset-env",
{
"targets": {
"node": "8.10"
},
"corejs": "3"// Declare the corejs version"useBuiltIns": "usage"}}]]Copy the code
- html-webpack-plugin
Dynamically add hash after each compile for external resources such as script and link introduced in HTML files to prevent the problem of referencing external files in the cache. Create HTML entry files can be generated, such as a single page can generate an HTML file entry, configure N HTML-webpack-plugin can generate N page entry
const htmlWebpackPlugin = require('html-webpack-plugin')
const htmlweb = new htmlWebpackPlugin({
title: "I am the home page", / / used to generate the title of the page elements must be used in the template page template engines the syntax of the < % = htmlWebpackPlugin. Options. The title % > filename:"index.html"[name][.ext] template: path.resolve(__dirname,'.. /index.html'), // template file path, support loaderhash:true, / /trueA unique WebPack build will be addedhashPlugins: [cssExtractPlugin, htmlWeb,]Copy the code
- clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin'·· plugins: [cssExtractPlugin, htmlWeb, new CleanWebpackPlugin()]Copy the code
- vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin')... the plugins: [cssExtractPlugin, htmlWeb, // Copy the other rules defined and apply them to the corresponding language blocks in the.vue file. New VueLoaderPlugin(), new CleanWebpackPlugin()]Copy the code
- Hot update HMR&webpack – dev – server
Module hot replacement (HMR) is the ability to replace, add, and remove necessary modules while the application is running without having to refresh the page. HMR is useful for applications that consist of a single state tree. Because the components of these applications are “dumb” (as opposed to “smart”), the state of the component will still correctly reflect the latest state of the application even after the component’s code changes.
Hot update HMR is a feature that webPack originally came with
const webpack = require('webpack') ··· devServer:{contentBase: path.resolve(__dirname,".. /dist"), // Resource file directory open:truePort: 8081,// server port number hot:true// enable hot update host:'192.168.1.4',
hotOnly:true,
// proxy:{
// '/api': {
// target: "http://localhost:9092/"
// }
// }
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
]
Copy the code
- Modify webpack.base.conf.js to extract the common parts
const merge = require('webpack-merge')
const devConfig = require('./webpack.dev.conf')
const proConfig = require('./webpack.pro.conf')
letdevMode = process.env.http_env ! = ='development'
function resolve (dir) {
return path.join(__dirname, '.. 'Module.exports = (env) => {// Module.exports = (env) => {if(env && env.production){
return merge(baseConfig, proConfig)
}else{
return merge(baseConfig, devConfig)
}
}
Copy the code
Modify the webpack. Dev. Conf. Js
// add plugin const webpack = require('webpack')
const path = require('path')
const devConfig= {
mode: 'development', / / model development | production / / inlet configuration string | Array | Object devtool:"cheap-module-evel-source-map"//dev devServer:{contentBase: path.resolve(__dirname,".. /dist"), // Resource file directory open:truePort: 8081,// server port number hot:true// enable hot update host:'192.168.1.4',
hotOnly:true,
// proxy:{
// '/api': {
// target: "http://localhost:9092/"
// }
// }
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
]
}
module.exports = devConfig
Copy the code
Modify the webpack. Pro. Conf. Js
const webpack = require('webpack')
const proConfig= {
mode: 'production', / / model development | production devtool:"cheap-module-source-map"Plugins: [new webpack.defineplugin ({'process.env': {
'http_env': JSON.stringify(process.env.http_env)
}
})
]
}
module.exports = proConfig
Copy the code
Modify the package. The json
Cross-env can set and use environment variables across platforms. In most cases, on Windows, it can be used as follows: NODE_ENV=production command line directives get stuck, there are many differences between Windows and POSIX when using command lines (e.g., $ENV_VAR on POSIX, %ENV_VAR%… on Windows) Cross-env makes it easy to use unique directives for different platforms without worrying about cross-platform issues
"scripts": {
"build": "cross-env http_env=production webpack -env.production --mode=production --config build/webpack.base.conf.js "."dev": "webpack-dev-server --config build/webpack.base.conf.js"."start": "npm run dev"
},
Copy the code
- Optimize the CSS – assets – webpack – the plugin to modify webpack. Pro. Conf. Js
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'Plugins: [new OptimizeCSSAssetsPlugin(), // compression CSS]Copy the code
- Uglifyjs webpack – the plugin to modify webpack. Pro. Conf. Js
const uglifyjs = require('uglifyjs-webpack-plugin'); Plugins: [new uglifyjs(), // compressed js]Copy the code
The complete code
- webpack.base.conf.js
const path = require('path')
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const htmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const merge = require('webpack-merge')
const devConfig = require('./webpack.dev.conf')
const proConfig = require('./webpack.pro.conf')
letdevMode = process.env.http_env ! = ='development'
console.log(devMode)
function resolve (dir) {
return path.join(__dirname, '.. '} // create multiple instances const cssExtractPlugin = new miniCssExtractPlugin({filename: devMode?'[name].[chunkhash:8].css':'[name].css',
chunkFilename: '[id].css'
})
const htmlweb = new htmlWebpackPlugin({
title: "I am the home page", / / used to generate the title of the page elements must be used in the template page template engines the syntax of the < % = htmlWebpackPlugin. Options. The title % > filename:"index.html"[name][.ext] template: path.resolve(__dirname,'.. /index.html'), // template file path, support loaderhash:true, / /trueA unique WebPack build will be addedhash}) const baseConfig = {entry:{index:"./src/main.js"Output :{path: path.resolve(__dirname,".. /dist"),
filename: "[name].js"//[] placeholder // publicPath:"http://www/cdn.com"}, resolve: {extensions: ['.js'.'.vue'.'.json'].alias: {
'vue$': 'vue/dist/vue.esm.js'.The '@': resolve('src'),
'common': resolve('src/common'),
'components': resolve('src/components'}}, optimization: {splitChunks:{"all"// initial async all // minSize: // minChunks:1 is not recommended. // At least how many chunks of a packaged chunk file refer to this chunk. If the value is 2, // maxAsyncRequests: 3,// maxAsyncRequests: 3,// automaticNameDelimiter:The '-',// pack the split symbol // name:true,// The packaged name can accept a function in addition to a Boolean valuefunction
// cacheGroup:{
// vendors:{
// test:/[\\/]node_modules[\\/]/,
// name: 'vendor',// Name of the chunk to be cached // priority: -10 // Cache group priority a larger number has a higher priority //}, // other:{// Only synchronous references are supported // chunks:'initial', / / must choose three: initial | | all / / async default valuestest: / vue | loadsh /, / / verify the regular rules, if conform to extract the chunk / / name:"other",
// minSize: 30000,
// minChunks: 1,
// },
// default:{
// minChunks: 2,
// priority: -20,
// reuseExistingChunk: true// You can set whether to reuse chunk //} //}}, usedExports:true}, module:{// Handle module rules:[{// recognize.vue filestest: /\.vue$/,
loader: 'vue-loader'}, {test: /\.(sa|sc|c)ss$/,
include: path.resolve(__dirname, '.. /src'Limit), / / packaging, improve the packaging speed / / exclude: / node_modules /, / / exclude node_modules folders use: [devMode? MiniCssExtractPlugin. Loader:'style-loader'.'css-loader',
{
loader: "postcss-loader",
options:{
indent:"postcss",
plugins:[require('autoprefixer')],
browser:['last 10 versions']
}
},
{
loader:'sass-loader',
options:{
sourceMap: true}}]}, {test: /\.js$/,
exclude: /node_modules/,
include: path.resolve(__dirname, '.. /src'),
loader: "babel-loader"}, {test:/\.(png|gif|jpe? g)/, // ? E Optional use: {loader:"url-loader",
options:{
name:"[name]_[hash].[ext]", / / /hash] resolves caching, [ext] preserves the file suffix of old fileslimitOutputPath () {outputPath () {outputPath () {outputPath ();"image/"}}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/, loader:'url-loader', options: {// The file size is smaller thanlimitParameter, url-loader will convert the file to DataURlimit: 10000,
name: '[name]-[hash:5].[ext]',
output: 'fonts/'} } ] }, plugins: [cssExtractPlugin, htmlweb, // Copy the other rules defined and apply them to the.vue file in the corresponding language block new VueLoaderPlugin(), Module.exports = (env) => {// Module.exports = (env) => {if(env && env.production){
return merge(baseConfig, proConfig)
}else{
return merge(baseConfig, devConfig)
}
}
Copy the code
- webpack.dev.conf.js
// add plugin const webpack = require('webpack')
const path = require('path')
const devConfig= {
mode: 'development', / / model development | production / / inlet configuration string | Array | Object devtool:"cheap-module-evel-source-map"//dev devServer:{contentBase: path.resolve(__dirname,".. /dist"), // Resource file directory open:truePort: 8081,// server port number hot:true// enable hot update host:'192.168.1.4',
hotOnly:true,
// proxy:{
// '/api': {
// target: "http://localhost:9092/"
// }
// }
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
]
}
module.exports = devConfig
Copy the code
- webpack.pro.conf.js
const webpack = require('webpack')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const uglifyjs = require('uglifyjs-webpack-plugin');
const proConfig= {
mode: 'production', / / model development | production devtool:"cheap-module-source-map"Plugins: [new OptimizeCSSAssetsPlugin(), // compress CSS new uglifyjs(), // compress JS new webpack.defineplugin ({'process.env': {
'http_env': JSON.stringify(process.env.http_env)
}
})
]
}
module.exports = proConfig
Copy the code
package.json
"scripts": {
"build": "cross-env http_env=production webpack -env.production --mode=production --config build/webpack.base.conf.js "."dev": "webpack-dev-server --config build/webpack.base.conf.js"."start": "npm run dev"
}
Copy the code
Learn to summarize
Through the in-depth study of webpack, understand the VUE – CLI webpack configuration, how to build vUE project through webpack. This is the simplest basic configuration for vuE-CLI projects and can be optimized later