background
Recently, I have been reconstructing the COMPANY’s H5 project, which involves construction optimization. Due to some historical reasons, the packaging tool originally used in the project was cooking developed by ele. me team (the packaging based on Webpack has been stopped maintenance at present). If we continue to use it, first, the project has been relatively complex, and the current construction method takes a long time to package each time; Second, using a tool that is no longer maintained carries its own risks. In addition, this reconstruction also requires Vue1.0 to Vue2.0 framework upgrade, which involves a series of dependent packages (vue-style-loader, etc.) version compatibility problems. After a day of messing around with vue2 and VUex3, I upgraded the build tool directly to WebPackage 4.
Due to the business needs of the company (SEO, the page is mainly put), our project adopts the multi-page architecture. The online single-page application template based on VUE is officially provided with VUE-CLI. There are also many third-party templates, but there are not many multi-page templates for reference. It took me about two weeks to sort out the multi-page application template based on WebPack4 + vue2 + vuex3 by referring to some blog materials and documents, and I recorded it for my convenience to check in the future and share it with other students who need reference.
The core concepts of WebPack
Inspired by zero-configuration build tools such as Parcel, WebPack4 has made a lot of optimizations to move towards no-configuration. Although zero-configuration is supported, you still need to manually set some configuration items if you want fine-grained control over your modules. But compared to previous versions of WebPack, it has been significantly simplified and much easier to get started. Here are the core configuration items of WebPack4, which will be expanded one by one:
- mode
- entry
- output
- loader
- plugins
- devServer
Next, I will follow the above order and try to list in detail the whole process of building vue2 and VUex multi-page applications based on WebPack4
mode
Webpack4 new, specify the packaging mode, the optional values are:
- Development
- Process.env.node_env is set to development
- Enable NamedChunksPlugin and NamedModulesPlugin
- Production D
- Process.env.node_env is set to production
- Maximized optimization (compression of modules, concatenation, etc.) is enabled
- None. This mode is not optimized
Mode Setting mode:
- Package. json is set in shell command parameters
webpack --mode=production
Copy the code
- Configure the mode configuration item
module.exports = {
mode: 'production'
};
Copy the code
For more information, see the official document Mode
entry
The biggest difference between a multi-page app and a single page app (SPA) is the entry point
- Multi-page: The final package generates multiple entries (HTML pages), generally each entry file in addition to the introduction of a common static file (JS/CSS) but also the introduction of page-specific static resources
- Single page: There is only one entry (index.html), all the static files need to be packed into the page, and all the page content is controlled by JavaScript
It should be noted that the entry mentioned above refers to the HTML file that is finally packaged into the dist directory. The entry we configured here is actually the JS module that needs to be introduced by HTML. These js modules, along with the removed common JS modules, eventually need to be combined into an HTML file using the htMl-webpack-plugin:
const config = require('./config'); // Multi-page configuration items
let HTMLPlugins = [];
let Entries = {};
config.HTMLDirs.forEach(item= > {
let filename = `${item.page}.html`;
if (item.dir) filename = `${item.dir}/${item.page}.html`;
const htmlPlugin = new HTMLWebpackPlugin({
title: item.title, // The title of the generated HTML page
filename: filename, Dist (${item.page}/index.html); dist (${item.page})
template: path.resolve(__dirname, `.. /src/template/index.html`), // Template file, different entry can be set according to the needs of different templates
chunks: [item.page, 'vendor'].// The js module to be imported in the HTML file, where vendor is the name of the public module removed under the default configuration of Webpack
});
HTMLPlugins.push(htmlPlugin);
Entries[item.page] = path.resolve(__dirname, `.. /src/pages/${item.page}/index.js`); // Set the entry js file according to the configuration
});
// ...
Copy the code
Multi-page configuration information in config.js:
module.exports = {
HTMLDirs: [{page: 'index'.title: 'home'
},
{
page: 'list'.title: 'List page'.dir: 'content' // Multi-level directories are supported
},
{
page: 'detail'.title: 'Details Page'}].// ...
};
Copy the code
Finally, introduce the relevant configuration:
module.exports = {
entry: Entries,
// ...
plugins: [
...HTMLPlugins // Use HTMLWebpackPlugin to synthesize the final page
]
// ...
}
Copy the code
The decoupling of public modules will be covered separately
Html-webpack-plugin plugin is the official website of html-webpack-plugin
output
Configuration exit file name and path:
const env = process.env.BUILD_MODE.trim();
let ASSET_PATH = '/'; / / dev environment
if (env === 'prod') ASSET_PATH = '//abc.com/static/'; // Build to the actual static service address
module.exports = {
entry: Entries,
output: {
publicPath: ASSET_PATH,
filename: 'js/[name].[hash:8].js'.path: path.resolve(__dirname, '.. /dist'),}}Copy the code
The generated JS file is attached with an 8-bit MD5 stamp to take full advantage of the CDN cache.
For the hash calculation methods and differences, see the differences of Hash, Chunkhash, and Contenthash in Webpack
loader
Loader is used to convert the source code of the module, and is responsible for converting the content of a certain file format into packaged modules that WebPack can support, for example, converting sASS preprocessing into CSS modules; Convert TypeScript to JavaScript; Or convert an inline image to a data URL
Specific configuration:
- Webpack.base.js (base configuration file)
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// ...
module: {
rules: [{test: /\.vue$/.// Process the vUE module
use: 'vue-loader'}, {test: /\.js$/.// Handle es6 syntax
exclude: /node_modules/.use: ['babel-loader'],}, {test: /\.(png|svg|jpg|gif)$/.// Process the image
use: {
loader: 'file-loader'.// Resolve the problem that image paths cannot be resolved in packaged CSS files
options: {
// The name of the generated image
name: '[name].[ext]'.// Image generation path
outputPath: config.imgOutputPath,
}
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/.// Handle fonts
use: {
loader: 'file-loader'.options: {
outputPath: config.fontOutputPath,
}
}
}
]
},
plugins: [
// ...
new VueLoaderPlugin()
]
// ...
Copy the code
Vue-loader is used together with the VueLoaderPlugin plug-in. Babel-loader is used with. Babelrc. Here “stage-2” is configured to use the advanced syntax in ES7. Without this configuration, new syntax features such as object extenders, async and await cannot be handled.
Babelrc configuration:
{
"presets": [["env", {
"modules": false."targets": {
"browsers": ["1%" >."last 2 versions"."not ie <= 8"]}}],"stage-2"]."plugins": ["transform-runtime"]}Copy the code
For configuration related to.babelrc, see: official documentation; Babel configuration – The difference between stages
- Webpack.dev.js (development configuration file)
// ...
module: {
rules: [{test: /\.css$/.exclude: /node_modules/.use: [
'vue-style-loader'.// Handle CSS styles in vue files
'css-loader'.'postcss-loader',]}, {test: /\.scss$/.exclude: /node_modules/.use: [ // These loaders process styles from right to left
'vue-style-loader'.'css-loader'.'sass-loader'.'postcss-loader',
{
loader: 'sass-resources-loader'.// Pack defined sass variables, mix, and other uniform styles into each CSS file, avoiding manual introduction on each page
options: {
resources: path.resolve(__dirname, '.. /src/styles/lib/main.scss'),}}]}, {test: /\.(js|vue)$/.enforce: 'pre'.// Force ESLint checking first
exclude: /node_modules|lib/.loader: 'eslint-loader'.options: {
// Enable automatic repair
fix: true.// Enable the warning message
emitWarning: true,}}]},// ...
Copy the code
- Webpack.prod.js (production configuration file)
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ASSET_PATH = '//abc.com/static/'; // Static resource address online
// ...
module: {
rules: [{test: /\.css$/.exclude: /node_modules/.use: [
MiniCssExtractPlugin.loader,
'css-loader'.'postcss-loader'] {},test: /\.scss$/.exclude: /node_modules/.use: [
MiniCssExtractPlugin.loader,
'css-loader'.'sass-loader'.'postcss-loader',
{
loader: 'sass-resources-loader'.options: {
resources: path.resolve(__dirname, '.. /src/styles/lib/main.scss'),},}]}, {test: /\.(png|svg|jpg|gif)$/.// Process the image
use: {
loader: 'file-loader'.// Resolve the problem that image paths cannot be resolved in packaged CSS files
options: {
// The name of the generated image
name: '[name].[hash:8].[ext]'.// Image generation path
outputPath: config.imgOutputPath,
publicPath: ASSET_PATH
}
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/.// Handle fonts
use: {
loader: 'file-loader'.options: {
outputPath: config.fontOutputPath,
publicPath: ASSET_PATH
}
}
}
]
},
// ...
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[chunkhash:8].css' // CSS is finally detached as a single file to the dist/ CSS directory})]Copy the code
The extract-text-webpack-plugin used to extract CSS into a single file no longer supports WebPack4, the mini-CSs-extract-plugin is now available to handle CSS extraction
plugins
In the Webpack packaging process, the module code conversion work is handled by loader, other work can be completed by Plugin. Commonly used are:
- Uglifyjs-webpack-plugin, handle JS code compression
- Mini-css-extract-plugin, extract CSS into a single file
- Clean-webpack-plugin, used to clean up the DIST folder at each build
- Copy -webpack-plugin, copy file
- Webpack HotModuleReplacementPlugin, heat load
- Webpack.defineplugin, which defines environment variables
Specific configuration:
- Webpack.base.js (base configuration file)
const HTMLWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
// ...
plugins: [
new VueLoaderPlugin(),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '.. /public'),
to: path.resolve(__dirname, '.. /dist'),
ignore: ['*.html'] {},from: path.resolve(__dirname, '.. /src/scripts/lib'), // Move local library resources
to: path.resolve(__dirname, '.. /dist')}]),... HTMLPlugins,// Use HTMLWebpackPlugin to synthesize the final page
new webpack.DefinePlugin({
'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH) // Use process.env.asset_path to ensure that the template file references the correct static resource address})]Copy the code
- Webpack.prod.js (production configuration file)
// Extract CSS extract-text-webpack-plugin no longer supports Webpack4, the mini-css-extract-plugin is now available to handle CSS extraction
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
// Automatically clean up the dist folder
new CleanWebpackPlugin(['dist'] and {root: path.resolve(__dirname, '.. '),
verbose: true.// Enable output information on the console
dry: false,}).new MiniCssExtractPlugin({
filename: 'css/[name].[chunkhash:8].css'})]Copy the code
devServer
In daily development, we need to start a static server locally to facilitate development and debugging. We use webpack-dev-server, an official provided tool, to quickly start a static service based on the current Webpack build configuration. When the mode of development, will have the hot reload function, so no longer need to manually introduced webpack. HotModuleReplacementPlugin plug-in.
Install webpack-dev-server as a development dependency and start it with NPM scripts:
npm install webpack-dev-server -S
Copy the code
Scripts configuration in package:
"scripts": {
"dev": "cross-env BUILD_MODE=dev webpack-dev-server ",
},
Copy the code
For details about devServer configuration, see the official document dev-server
SplitChunks configuration
Webpack 4 CommonsChunkPlugin removed, replaced by two new configuration items (optimization) splitChunks and optimization) runtimeChunk) is used to extract the public js module. Through optimization. RuntimeChunk: true options, webpack will add a runtime (runtime) contains only additional blocks of code to each entry. (Note: This is scenario-dependent and causes each entry to load an extra copy of the runtime code).
SplitChunks default configuration description:
module.exports = {
// ...
optimization: {
splitChunks: {
chunks: 'async'.// Controls which code blocks WebPack chooses to split (other types of code blocks are packed the default way). There are three optional values: Initial, async, and all.
minSize: 30000.// Form the smallest size of a new code block
maxSize: 0.minChunks: 1.// The minimum number of times the code block should be referenced before splitting (the default policy is to split without multiple references)
maxAsyncRequests: 5.// The maximum number of code blocks loaded on demand should be less than or equal to 5
maxInitialRequests: 3.// The maximum number of code blocks initially loaded should be less than or equal to 3
automaticNameDelimiter: '~'.name: true.cacheGroups: {
vendors: { // Assign all modules from node_modules to a cache group called vendors
test: /[\\/]node_modules[\\/]/.priority: - 10 // Cache group priotity is negative, so all custom cache groups can have a higher priority than this
},
default: {
minChunks: 2.// All code that is referenced twice or more is assigned to the default cache group.
priority: - 20.// A module can be assigned to multiple cache groups. The optimization policy assigns modules to the cache group with the highest priority
reuseExistingChunk: true // Allows reuse of existing code blocks, rather than creating new ones, only when the module matches exactly.}}}}};Copy the code
For details about SplitChunksPlugin configuration, see the official document SplitChunksPlugin
Vue && Vuex
Vue:
We know that the vUE single-page application has only one entry. The default entry file is main.js, where the VUE template is processed, the Vuex finally constructs the Vue object. A multi-page application has multiple entries, which means that each entry handles the same thing main.js does on a single page. The general configuration looks something like this:
import Vue from 'vue';
import Tpl from './index.vue'; / / the Vue template
import store from '.. /.. /store'; // Vuex
new Vue({
store,
render: h= > h(Tpl),
}).$mount('#app');
Copy the code
Vuex:
The store is divided into multiple modules in order to avoid that all states are concentrated in the Store object, resulting in a bloated file that is difficult to maintain. Each module has its own state, mutation, and action. At the same time, extract the getter into a separate file. The file structure is as follows:
|- store
| |-modules
| | |-app.js / / a single module
| | |-user.js // // Single Module
| |-getters.js
| |-index.js // Organize the modules here
Copy the code
The Settings for a single module are as follows:
const app = {
state: { // state
count: 0
},
mutations: { // mutations
ADD_COUNT: (state, payload) = >{ state.count += payload.amount; }},actions: { // actions
addCount: ({ commit }, payload) = > {
commit('ADD_COUNT', {
amount: payload.num }); }}};export default app;
Copy the code
Finally assemble each module in index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import app from './modules/app';
import user from './modules/user';
import getters from './getters';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
app,
user
},
getters
});
export default store;
Copy the code
conclusion
Finally finished, filled in a lot of holes in the middle, but all the way down there is still a lot of harvest, later there is time to continue to improve. The github address of the project source code is here: webpack4-vue2-multipage, there is a need to directly take, if you have some help, also please give a star ha ~~
The resources
- Official wepack documentation
- Webpack4 incomplete migration indicates north
- Multi-page application solution based on Webpack4 + Vue
- Without the CommonsChunkPlugin, how can we subcontract?
- Hash, Chunkhash, contenthash differences in webpack
- Webpack 4 finally knows that convention is better than configuration
- What are the 7 SourceMap modes in devtool?