I have been using scaffolding before, but this time I set up the webpack front end project by myself. I spent a lot of thought, so I made a summary.
Use 1.
The project structure is as follows:
project |- bulid <! - this directory are automatically generated - > | - public | | - CSS - js | - page1. HTML <! - plug-in generated HTML file - > | - page2. HTML <! -- HTML file generated by the plugin -->... |- public/ <! - store fonts, pictures, web templates and other static resources - > | - SRC <! - source folder - > | - components / / | | - CSS - js / | - page1. Js <! -- unique VUE instance per page, bound to#app-->|- page2.js <! -- unique VUE instance per page, bound to#app-->. |- package.json |- package-lock.json |- README.mdCopy the code
The public folder holds some static files, and the SRC folder holds source code. Each page passes through an entry file (page1.js, page2.js,..). Generate a vUE instance and mount it to the #app element of the HTML file generated by the plug-in.
Install dependencies
$ npm install
Copy the code
Go into development mode
$ npm run start
Copy the code
The browser opens http://localhost:3000 and the page goes blank with the words cannot get. Don’t panic, add /page1. HTML to the URL and press Enter to see our page. This is because I set the home page of the development server to index.html, whereas in this case the pages are page1.html,page2.html, so it will be blank.
Development is complete, build the production version:
$ npm run build
Copy the code
This will result in a build/ folder with optimized files from which the server responds to resources.
2. Introduction
2.1 Basic WebPack Configuration
Our development is divided into production and development environments, so we need to have two webPack configuration files (you might want to use env environment variables, and then use the 3-mesh operator to return different values depending on the value of env). However, this method does not work in webPack export module properties, I have tried ~~~). Here we split it into three files, of which webpack.common.js is the general configuration and is used in both environments, while webpack.dev.js and webpack.prod.js are the specific configurations in both environments. The webpack-merge package is used here to combine common and specific configurations.
- webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
constdevMode = process.env.NODE_ENV ! = ='production';
// An array of entry files needs to be packaged
/ / array element type {string | object}
// string: The bundle will be generated using the default rules
/ / object {filename | in the | template} generated bundle. The HTML file name | | title tag content under the path/public template file (suffix specified file)
const entryList = [
'page1'.'page2',];/** * @param {array} entryList * @param {object} option: Optional manual configuration */
const createEntry = (list = [], option = {}) = > {
const obj = {};
list.forEach((item) = > {
const name = item.filename ? `./js/${item.filename}` : `./js/${item}`;
obj[name] = path.resolve(__dirname, './src'.`. /${item}.js`);
});
return Object.assign(obj, option);
};
module.exports = {
entry: createEntry(entryList),
output: {
path: path.resolve(__dirname, './build'),},module: {
rules: [{test: /\.js$/.exclude: /(node_modules|bower_components)/.use: {
loader: 'babel-loader'.options: {
presets: ['@babel/preset-env'],},},}, {test: /\.vue$/.use: 'vue-loader'}, {test: /\.(woff|woff2|eot|ttf|otf)$/.use: {
loader: 'file-loader'.options: {
name: 'public/fonts/[name].[ext]',},},}, {test: /\.(png|svg|jpg|gif)$/.use: {
loader: 'file-loader'.options: {
name: 'public/images/[name].[ext]',},},},],},plugins: createPluginInstance(entryList).concat([
Vue SFCs single file support
new VueLoaderPlugin(),
]),
};
Copy the code
The CSS file is not configured here, because it needs to be optimized and extracted in the production environment, so it is configured in the other two files.
- webpack.dev.js
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development'.devtool: 'inline-source-map'.output: {
filename: '[name].js'.chunkFilename: '[name].js',},module: {
rules: [{test: /\.(css|less)$/.use: [
'vue-style-loader'.'css-loader'.'postcss-loader'.'less-loader'],},],},resolve: { alias: { vue: 'vue/dist/vue.js'}}});Copy the code
Vue is divided into development and production versions, and the last line specifies which version to use based on the path.
- webpack.prod.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production'.output: {
filename: '[name].[contenthash].js'.chunkFilename: '[name].[contenthash].js',},resolve: { alias: { vue: 'vue/dist/vue.min.js'}},module: {
rules: [{test: /\.(css|less)$/.use: [
MiniCssExtractPlugin.loader,
'css-loader'.'postcss-loader'.'less-loader'],},],},Copy the code
In the production environment, we used hashes to facilitate caching, as will be the case when we add other resources to the production environment.
2.2 Solving the problem of file Output directory
We expect the build folder to have the following structure:
build
|- css/
|- js/
|- page1.html
|- page2.html
...
Copy the code
That is, files are grouped by type. HTML files are placed directly in this directory, but the output from our configuration above is mixed together. Since the name attribute can be either a file name or a file name with a path, such as /dir/a, we make some changes based on this feature.
-
Change the output path of output directly
Such as build/js, other resources use relative paths such as.. /page1.html is modified. I did this from the start, but it ended up making the development server unable to respond to file changes because it could only listen for files in the output directory, not files in that directory.
-
Modifying an entry Name
This is our final solution. Change the original file name page1 to /js/page1, and the final output JS files will be placed in the JS folder. In production we extract CSS from the JS file using the MiniCssExtractPlugin. This is the configuration of the plugin:
new MiniCssExtractPlugin({
filename:'[name].[contenthash].css'
})
Copy the code
The name is the name of the first entry here, under the influence of the entry name change, it will eventually become a js/page1.131 de8553ft82, CSS, and the placeholder [name] only effective at compile time, which means that can’t use the function to deal with this value. So instead of using the [name] placeholder for the desired purpose, you simply use [id].
new MiniCssExtractPlugin({
filename:'/css/[id].[contenthash].css'
})
Copy the code
3. Code segmentation
In webpack4, segmentation is done using optimization.splitchunks.
//webpack.common.js
const path = require('path');
module.exports = {
/ /... Leave out the rest
optimization:{
runtimeChunk: {name:'./js/runtime'
},
splitChunks: {// To avoid excessive segmentation, set the size to at least 30KB
//cacheGroups inherits this value
minSize:30000.cacheGroups: {// VUE related framework
main:{
test: /[\\/]node_modules[\\/]vue[\\/]/.name: './js/main'.chunks:'all'
},
// Other frameworks besides Vue
vendors:{
test:/[\\/]node_modules[\\/]? ! (vue)[\\/]/.name: './js/vendors'.chunks:'all'
},
// Business reusable JS
extractedJS:{
test:/[\\/]src[\\/].+\.js$/.name:'./js/extractedJS'.chunks:'all'}}}}};Copy the code
RuntimeChunk contains some webapck boilerplate files that allow you to package without changing the contents of the source file. The hash value still changes, so we’ll separate it out and learn more here. CacheGroups used to extract reusable module, the test will attempt to match (module absolute path | | module name), the return value is true and meet the conditions of module will be split. The conditions can be customized, such as the minimum size of the module and the minimum number of chunks to be imported (i.e. the number of reuse times). By default, modules are segmented only when they are at least 30KB before packaging.
4. The tree shaking
Add it to package.json
"sideEffects": ["*.css"."*.less"."*.sass"]
Copy the code
Files outside this array will be affected by tree jitter — unused code will be culled from the export export object. This will greatly reduce useless code. If tree jitter has a side effect on some files, put those file names in the array to skip this operation. CSS files (including.less,.sass) must be included, otherwise styles will be lost.
5. Use of plug-ins
5.1 the clean – webpack – the plugin
After each package, new files are generated, which may lead to the accumulation of useless old files. It is too troublesome to delete these useless files one by one, the plug-in will automatically clean up before each package. In practice, we didn’t want to clean up the files generated by the build command in the development environment, so we only used this plug-in in the production environment.
5.2 HTML – Webpack – the plugin
There are no HTML files in our source directory, so we use this plugin to generate multiple HTML files after packaging.
//webpack.common.js
/ /... Omit anything that has already appeared above
// One instance of the plugin is required per HTML
// Generate HTML files in batches
const createPluginInstance = (list = []) = > (
list.map((item) = > {
return new HtmlWebpackPlugin({
filename: item.filename ? `${item.filename}.html` : `${item}.html`.template: item.template ? `./public/${item.template}` : './public/template.html'.title: item.title ? item.title : item,
chunks: [
`./js/${item.filename ? item.filename : item}`.'./js/extractedJS'.'./js/vendors'.'./js/main'.'./js/runtime'.'./css/styles.css',
devMode ? './css/[id].css' : './css/[id].[contenthash].css',]}); }));Copy the code
The default is to pack all the entry files, code split files into an HTML file, specifying chunks to tell the plug-in which chunks to include, or exludeChunks specifying chunks not to include. There is a slight problem here, we can’t get the file to contain exactly the blocks it needs. If you want to exclude chunks that are not used, you have to manually configure them as required. A batch file generated with this function will always contain all the common packages.
5.3 the mini – CSS – extract – the plugin (prooduction)
This plug-in is used to extract CSS from a JS file into a separate CSS file.
//webpack.prod.js
/ /... Leave out the rest
plugins:[
new CleanWebpackPlugin('build'),
/ / CSS
new MiniCssExtractPlugin({
filename:'./css/[id].[contenthash].css'
}),
// Optimize the cache
new webpack.HashedModuleIdsPlugin()
]
Copy the code
Optimize – CSS – Assets -webpack-plugin (Production)
To simplify packaged CSS code, set it in the Minimizer property of the Optimization configuration, which overrides the WebPack default, so also set js minimizer (here we use uglifyPlugin) :
optimization: {
minimizer: [new UglifyJsPlugin({
cache: true.parallel: true
}),
new OptimizeCSSAssetsPlugin()
]
}
Copy the code
6. Development server and hot module replacement
Add the following to webpack.dev.js:
/ /... Leave out the rest
devServer:{
index:'index.html'.hot:true.contentBase:path.resolve(__dirname,'./build'),
port:3000.noInfo:true
},
plugins: [new webpack.HotModuleReplacementPlugin()
]
Copy the code
Using the development server, we can refresh the source files automatically after we make changes, because we are keeping the data in memory, so the build folder on the hard disk is not affected. Hot module replacement also needs to be modified in the source file. We also configured the dynamic import syntax accordingly.
7. Other
Public is used to store static resources. After packaging, a folder of the same name will be created under build/ to store the resources that public will use. If you reference a public resource such as an image in a.css file, use an absolute path when adding urls:
<! -- src/css/page1.css --> .bg-img { background-image:url(/public/images/1.jpg) }Copy the code
This works when opened over HTTP/HTTPS, but if opened as a file (such as double-clicking page1.html after packaging), the browser will not be able to find the resource. You can use both absolute and relative paths by importing images as variable references (import name from path).