In the last six months, I was maintaining a management background project of the company. At the beginning of construction, the technology stack was quite chaotic. The construction scheme adopted the method of calling Webpack in Gulp, in which Gulp was responsible for processing. In this set of construction scheme, there are mainly these problems:
- JS compression, CSS compatibility and other functions are not implemented.
- In development mode, the code is saved, the project is completely repackaged, and the continuous build is not only slow, but also cacheable (page refresh changes do not take effect after the build is finished).
- Because the current program is not used
http-proxy-middleware
Such request proxy module leads to the deployment of back-end services when the project is developed locally, which is unfriendly to new developers and often causes code synchronization problems between the test environment and the local environment due to delayed communication.
Therefore, after getting familiar with the project, I plan to upgrade its build scheme, mainly to solve the above problems.
1. Description of the original construction scheme
Original build speed
npm run build
: Pack about 50 PCSnpm run dev
: Enable the development mode for about 50s, save the automatic recompilation takes about 6s, after the compilation is complete, you need to refresh to see the effect, occasionally due to cache problems, you need to automatically recompile again to see the effect
Original construction results
./build/development
: Stores rendered JS files./build/html
: Stores the rendered HTML file./build/rev
: A JSON file that stores the hash value of each entry file
Package code parsing
/** * Use the gulp-clean plugin to delete files in the build directory */
gulp.task('clean'.function () {
if(! stopClean) {return gulp.src('build/' + directory, { read: false }).pipe(clean())
}
})
/** * Use webpack to package vue and JS files, */ after clean
gulp.task('webpack'['clean'].function (callback) {
deCompiler.run(function (err, stats) {
if (err) throw new gutil.PluginError('webpack', err)
gutil.log('[webpack]', stats.toString({}))
callback()
})
})
/** * Use gulp-uglify to uglify js files after webpack
gulp.task('minify'['webpack'].function () {
if (environment) {
return
} else {
return gulp.src('build/' + directory + '/*.js').pipe(uglify())
}
})
/** * What does gulp-rev do when run after minify? * Generate MD5 signature according to static resource content, packaged file name will add MD5 signature, and generate a JSON to save file name path correspondence. * Replace static resource paths in HTML with file paths with MD5 values so that HTML can find resource paths. * Some people might do this: Static servers set the expiration time of static resources to never expire. 304 * Version update only updates the content of the modified static resource * does not delete the static resource of the old version, only updates the HTML when version rollback, also does not increase the number of HTTP requests */
gulp.task('hashJS'['minify'].function () {
var dest = gulp.src(['A string of entry files... '])
.pipe(rev()) // Set the hash key for the file
.pipe(gulp.dest('build/' + directory)) // Write out the piped file to the directory
.pipe(rev.manifest({})) // Generate JSON to map hash keys
.pipe(gulp.dest('build/rev')) // Write out the piped file to the directory! environment && gulp.src(['A string of entry files... ']).pipe(clean())
return dest
})
/** * Use gulp-rev-replace to replace the js and CSS referenced in HTML with a new hash */ Use gulp-livereload to partially update the page after all files have been repackaged */
gulp.task('revReplace'['hashJS'].function () {
return gulp.src(['html/*.html'])
.pipe(revReplace({ ... })) // Provide a new hash for js references in HTML
.pipe(gulp.dest('build/html')) // Output file
.pipe(livereload()) // Update the page locally
})
/** * With gulp.watch, when any files in the application directory change, execute the package command * gulp.watch: monitor the files and do something when the files change. * /
gulp.task('watch'['revReplace'].function () {
stopClean = true
livereload.listen()
gulp.watch('app/**/*'['clean'.'webpack'.'minify'.'hashJS'.'revReplace'])})/** * Outputs dev and build workflows */
gulp.task('default'['clean'.'webpack'.'minify'.'hashJS'.'revReplace'.'watch']) // dev
gulp.task('build'['clean'.'webpack'.'minify'.'hashJS'.'revReplace']) // build
/** * Webpack configuration */
var devCompiler = webpack({
entry: {...// a bunch of entry files
vendor: ['vue'.'vue-router'.'lodash'.'echarts'] // Public module
},
output: {
path:... .// The destination path of all output filespublicPath: ... .// Output the directory of the parsed filefilename: ... .// Output file
chunkFilename: ... // File requested asynchronously
},
// Remove the following contents from the bundle and reduce the file size
external: {
jquery: 'jQuery'.dialog: 'dialog'
},
plugins: [
/** * By stripping out the public module, the resultant file can be loaded once at the beginning and stored in the cache for later use. * This leads to an increase in page speed because browsers quickly remove common code from the cache, rather than loading a larger file every time a new page is visited. * /
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor']}),/** * DefinePlugin allows you to create a global constant that can be configured at compile time. This can be very useful when the development mode and production mode are built to allow different behaviors. * /
new webpack.DefinePlugin({
__VERSION__: new Date().getTime()
})
],
resolve: {
root: __dirname,
extensions: [' '.'.js'.'.vue'.'.json'].// Parse the file suffix whitelist for the component
alias: { ... } // Configure the path alias
},
module: {
// Loaders for each file
loaders: [
{ test: /\.vue$/.loader: 'vue-loader' },
{ test: /\.css$/.loader: 'style-loader! css-loader' },
{ test: /\.jsx$/.loader: 'babel-loader'.include: [path.join(__dirname, 'app')].exclude: /core/ },
{ test: /\.json$/.loader: 'json'}},vue: {
loaders: {
js: 'babel-loader'}}})Copy the code
2.Gulp
Function to move toWebpack1
Performed on the
usehtml-webpack-plugin
The plug-in build project owner.html
file
module.exports = {
plugins: [
new HtmlWebpackPlugin({
filename: '... '.// Output path
template: '... '.// Extract the path to the source HTML
chunks: ['... '].// The module to import
inject: true // Whether to append to the bottom of the body}})]Copy the code
usewebpack.optimize.UglifyJsPlugin
plug-insJS
The compression
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false}}})]Copy the code
usewebpack-dev-server
Module, providingnode
Set up the development environment
module.exports = {
devServer: {
clientLogLevel: 'warning'.// Log output level. The log output level is set to a level higher than warning
inline: true.// Enable Live Reload
hot: true.// Enable hot overloading
compress: true.// Gzip all static resources
open: true.// By default, the browser is opened when the local service is started
quiet: true.// Disable verbose build logshost: ... .// Service startup domain nameport: ... .// The port on which the service starts
proxy: { ... }, // HTTP proxy configuration
/** * This configuration is often used to solve the problem of matching all 404 routes back to index.html in SPA h5 routing mode. * Since the production environment matches a simple alias for the home page, the development environment also copies the configuration of the backend service */
historyApiFallback: {
rewrites: [{ from: '/^\/admin/'.to: '... '}}}}]Copy the code
Hit the pit
[email protected] requires a peer of webpack^@4.0.0 but none is installed.
: The two module versions are incompatible, go back towebpack-dev-server@2
Succeeded.Cannot resolve module 'fsevents' ...
: will be globalwebpack
Call instead directly fromnode_modules/webpack
Call directly to solve the problem,node node_modules/webpack/bin/webpack.js --config webpack.config.js
.Cannot resolve module 'fs' ...
Configuration:config.node.fs = 'empty'
forWebpack
providenode
Native module that allows it to be loaded into this object.- Thermal overload only
.js
and.css
and.vue
In the<style>
Inside style works, right.vue
In the filehtml
The template andjs
The message “module code has been changed and recompiled, but hot overloading does not take effect, and the global refresh policy may be enabled” will be printed. It has not been solved for the time being, and the initial assessment is that the version is lowvue-hot-reload-api
If you have any questions about these parts, please explain them in the comments section.
From 3.Webpack1
Upgrade to theWebpack3
Since Webpack2 is almost completely compatible with Webpack3 and only involves some incremental functionality, choose to migrate directly from Webpack1 to Webapck3. Install Webpack3 in your project first, and then follow the section “Migrating from Webpack1” in the Webpack2 documentation. To make changes to the configuration item, refer to this document: www.html.cn/doc/webpack…
There were no problems with the upgrade and it worked with a few changes to the documentation configuration. Take a look at the features implemented so far:
- The new
Webpack
The build code already implements all of the original functionality, as listed below. - use
webpack-dev-server
As a development server, save time is implementedlive reload
The function. - use
http-proxy-middleware
Plugins, which proxy requests directly to the test server, separate the development environment from locally deployed back-end services, greatly reducing the time cost of development environment deployment. - new
friendly-errors-webpack-plugin
, output friendly build logs, print the development environment addresses of several important modules, complete configuration referencevue-cli@2
Is the default configuration. - new
postcss-loader
, to add CSS compatibility processing, complete configuration referencevue-cli@2
Is the default configuration. - use
webpack.optimize.UglifyJsPlugin
Compress js code.
Try to build and print the build time record:
npm run build
: about135s
npm run dev
: Initial build about58s
Continue to build about30s
The project took too long to build (the first time I packed it scared me…) , can only continue to seek optimization on the construction speed
In 4.Webpack3
To optimize the construction speed
usewebpack-jarvis
Monitor build performance
Webpack-jarvis is a graphical webpack performance monitoring tool that is easy to configure and has specific outputs for the time ratio of the build process, as well as a detailed record of the build results
// After a simple configuration, you can output the build result record on local port 3001
const Jarvis = require('webpack-jarvis')
module.exports = {
plugins: [
new Jarvis({
watchOnly: false.port: 3001}})]Copy the code
usehappypack
The happypack module uses the multi-process model to speed up code construction. However, after using it, there seems to be no obvious results. The build time is probably reduced by a few seconds. I’m not sure what the effect of this module is on the optimization of scenes. I read an article about happypack principle before, but I haven’t read it carefully. If you are interested in it, you can study it. Taobaofed.org/blog/2016/1…
const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
module.exports = {
plugins: [
new HappyPack({
// The id of the happypack, which needs to be declared when called. If you want to compile other types of files, you need to declare another happypack
id: 'js'.// cacheDirectory: When set, we will try to use the results of the cache loader when Babel is compiled to avoid the costly reworking of Babel
use: [{ loader: 'babel-loader'.cacheDirectory: true}].// Determine how many process pools need to be split based on the number of CPU cores
threadPool: happyThreadPool,
// Whether to output logs about the compilation process
verbose: true}})]Copy the code
After this step, print the build time log:
npm run build
: about130s
npm run dev
: Initial build about60s
Continue to build about30s
devtool
Configured tocheap-module-eval-source-map
The devtool option enables the cheap-module-eval-source-map mode: vue-cli@2 defaults to this mode, with cheap indicating that column information is omitted when output source-map; Module indicates that a precompiler such as babel-loader is enabled during compilation so that uncompiled source code can be seen directly during debugging; Eval means to enable eval mode compilation. This mode directly uses the eval function to execute the string of the compiled module, reducing the step of converting the string into an executable code file and speeding up reconstruction in project development. Source-map represents the mapping table of the output source code, so that errors can be directly located to the source code during development, improving development efficiency.
After this step, the effect is not obvious =.= (compared to the original source-map), about a few seconds less, output the build time record:
npm run build
: about130s
npm run dev
: Initial build about58s
Continue to build about30s
usehtml-webpack-plugin-for-multihtml
Speed up reconstruction of multi-entry projects
It takes 30 seconds to rebuild! Various searches found an issue of the HTML-webpack-plugin and found that html-webpack-plugin@2 does have a noticeable slowdown in building multi-entry applications because the build content is not successfully cached so that all code is recompiled with each rebuild. The solution presented by the author is to use a branch project of this module (a project forked by the author and fixed for this problem) html-webpack-plugin-for-multihtml, which uses exactly the same usage as html-webpack-plugin. It only takes about 1s to rebuild after use.
After this step, print the build time log:
npm run build
: about130s
npm run dev
: Initial build about58s
Continue to build about1s
usewebpack.DllPlugin
Extract common modules
I found a lot of big dependency packages in the output, such as Vue core library, Lodash, Echarts, etc., and some static resources that do not want to be packaged. I wanted to avoid compiling these content every time and improve the compilation speed, so I found this plug-in.
The webpack.DllPlugin plugin comes from a.dll file (dynamic link library) for Windows. First, a package containing public modules and a mapping table are constructed by the DllPlugin module, and then corresponding dependencies are associated with each module through the mapping table by the DllReferencePlugin module. In this way, these public modules can be pre-packaged, so that there is no need to deal with these modules in the future construction, reducing the packaging time.
// webpack.dll.conf.js
const webpack = require('webpack')
module.exports = {
entry: {
vendor: [...]. },output: {
path: resolve('build/development'),
filename: '[name].dll.js'.library: '[name]_library'
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new webpack.DllPlugin({
path: resolve('build/development/[name]-manifest.json'), // Generate the location and file name of the manifest file output
name: '[name]-library'.// This is the same as output.library, which corresponds to the name field in the manifest.json file to prevent global variable conflicts
context: __dirname
})
]
}
// webpack.base.conf.js
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('.. /build/development/vendor-manifest.json') // Let WebPack get the used dependencies from the mapping table}})]Copy the code
Once packaged, the common library vendor.dll.js file needs to be included in the HTML file
<html>
<head></head>
<body>
<div id="app"></div>
<script src="/build/development/vendor.dll.js"></script>
<! Other JS should be injected at the back of the DLL to ensure that the contents of the public library can be referenced.
</body>
</html>
Copy the code
After this step, output the build time log and see a significant increase in build efficiency:
npm run dll
: about25s
npm run build
: about70s
npm run dev
: Initial build about55s
Continue to build about1s
5. Afterword.
The optimization is almost over here. This optimization provides the old project with some functions of the new generation SPA project, and builds a more modern local development environment. Because this article is a bit too long, the complete configuration is left in another article.
6. Q&A
Q: why not just upgrade to Webpack4?
A: Webpack4 only supports vue-loader@15 or earlier versions, but this version cannot parse Vue1 files.
The author information
Other Articles by author
- (1) 【JavaScript】
- 【 Interview 】 Social recruitment intermediate front-end written interview questions summary – answers and development
- What is cross-domain, why do browsers prohibit cross-domain, and the divergent learning it causes