@[TOC](Table of Contents)
Npm depends on Node.js, so download and install it directly, and configure the environment variables. Since I prefer using 'shift+right-click' to launch PowerShell to execute commands, by default, PowerShell does not allow running script files, so the security policy needs to be removed.
set-ExecutionPolicy RemoteSigned
Npm is installed by default in the C drive. To change the default path and view the Npm configuration:
npm config set prefix "E:/Npm" # Configure the global installation directory
npm config set cache "E:/Npm/npm_cache" # Configure the cache directory
npm config ls # View configuration
Due to some known reasons such as slow foreign network speed, I chose to use the Taobao's cnpm for building:
npm install cnpm -g
cnpm install vue
cnpm install --global vue-cli
Directly build a webpack-based project, and some configurations are required.
> vue init webpack project-name
? Project name project-name
? Project description A Vue.js project
? Author Czy <[email protected]>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) no
vue-cli · Generated "project-name".
# Project initialization finished!
# ========================
I chose to install the dependencies manually. However, due to the large size of the generated node_modules
and the need for a rebuild in case of any issues, the deletion process is quite slow due to the large number of files. Additionally, I prefer to share dependencies like Maven does, so I used mklink
to create directory links. First, copy package.json
to a certain directory and execute cnpm i
there, which will generate node_modules
. After that, execute mklink
in the project folder, or create the directory link first then execute cnpm i
(i
is short for install).
Directory: D:\Project\Library\Modules
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 20/01/09 09:21 node_modules
-a---- 20/01/04 14:09 62 mklink.md
-a---- 20/01/04 14:07 2707 package.json
PS D:\Project\Library\Modules> npm i
mklink
cannot be executed in PowerShell, it needs to be done using CMD, then run npm run dev
to start the project normally.
C:\Users\Czy\Desktop\project-name>mklink /J node_modules D:\Project\Library\Modules\node_modules
Junction created for node_modules <<===>> D:\Project\Library\Modules\node_modules
C:\Users\Czy\Desktop\project-name>npm run dev
> [email protected] dev C:\Users\Czy\Desktop\project-name
> webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
10 13 13 13 13% building modules 33/37 modules 4 active ...ct-name\src\components\HelloWorld.vue{ parser: "babylon" } is deprecated; we now treat it as { 14 14 95% emitting
DONE Compiled successfully in 14788ms 1:09:13 PM
I Your application is running here: http://localhost:8080
vue-cli
├── build/ # Webpack configuration directory
├── dist/ # Production environment project generated by build
├── config/ # Vue basic configuration files, can set listening ports, package output, etc.
├── node_modules/ # Dependency packages, generated by 'cnpm i'
├── src/ # Source code directory (build application focuses on this directory)
│ ├── assets/ # Place static files that need to be processed by Webpack, usually stylesheets such as CSS, SASS, and some external JS files
│ ├── components/ # Component directory
│ ├── filters/ # Filters
│ ├── store/ # State management
│ ├── routes/ # Routes, configure project routes here
│ ├── utils/ # Utility classes
│ ├── views/ # Route page components
│ ├── App.vue # Root component
│ └── main.js # Entry file
├── index.html # Main page, will be injected with Vue after opening the page
├── static/ # Place static files that do not need to be processed by Webpack, usually for images and other resources
├── .babelrc # Babel transcode configuration
├── .editorconfig # Code format
├── .eslintignore # ESLint ignore
├── .eslintrc # ESLint configuration
├── .gitignore # Git ignore
├── package.json # Configuration information for this project, startup method
├── package-lock.json # Records the specific source and version numbers of each npm package actually installed in the current status
└── README.md # Project description
Filters can filter data, for example, displaying 1 as OK in a printed table.
//Used in templates
{{status | statusFilter}} //Use {{ data | filter definition}} Support chaining {{ data | filter definition1 | filter definition2}}
//Can be referenced in style
:style="status | colorFilter"
{
"plugins": [["component", [
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]]
}
import {
Input,
Button
} from 'element-ui';
Vue.use(Input);
Vue.use(Button);
在定义的组件加入组件属性
import myfile from './file'
export default{
components: {
'my-file': myfile
}
}
cnpm i babel-plugin-component -D
.babelrc
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2",
["es2015", { "modules": false }]
],
"plugins": ["transform-vue-jsx", "transform-runtime",[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]
}
main.js
import { Menu, MenuItem } from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(Menu)
Vue.use(MenuItem)
Similar to these component libraries, use as an element in the component, you need a .vue file to create the custom component, and index.js to expose the interface
index.js
import layout from './layout';
/* istanbul ignore next */
layout.install = function(Vue) {
Vue.component(layout.name, layout);
};
export default layout;
Then import and mount in main.js
import layout from '@/components/common/layout';
Vue.use(layout)
// Configuration as follows
import Vue from 'vue'
import Router from 'vue-router'
// Import page components
import Index from "@/components/login/Index.vue"
import ManagerIndex from "@/components/manager/Index.vue"
// Mount Router
Vue.use(Router)
export default new Router({
routes: [
{
path: '/', // Page path /#/
name: "Index", // Page naming, when passing parameters to navigate to a page, use name
component: Index, // Load component
},
{
path: '/ManagerIndex',
name: "ManagerIndex",
component: ManagerIndex,
meta:{ // Some configuration information
auth: true // Configure this page to require authentication, needs to be used with router.beforeEach()
},
children: [ // Child components of this component
{
path: 'OverView', // Page path /#/ManagerIndex/OverView
name: "OverView",
component: OverView
}
]
}
]
})
router-link has a to property, which is used for anchor jumping, and router-view is a container for loading the components defined in Router based on the anchor.
<router-link :to="/">Go directly to</router-link>
<router-link :to="{ path: '/path', query: { id: 123 }}">Query Parameters</router-link>
<router-link :to="{ name: 'routername', params: { id: 123 }}">Parameter, using name, indicate placeholder :id in path</router-link>
<router-view></router-view>
router-link provides declarative navigation, while Router provides programmatic navigation
this.$router.push({ name: 'OverView'})
When router-link navigates, the component will be dynamically created and destroyed. If you want to maintain the component state, you can use <keep-alive/>
. Note that this will not trigger the component lifecycle.
<keep-alive><router-view></router-view></keep-alive>
When registering routes, an auth is declared in meat to perform authentication. It needs to be used in conjunction with router.beforeEach()
and global variables. Declare this method in main.js to perform navigation guards. Be sure to call the next() method, otherwise the navigation will not be executed.
router.beforeEach((to, from, next) => {
if (to.meta.auth && !Vue.prototype.$globalData.user) {
next({
path: "/"
})
} else {
next();
}
})
Declared and exported in dispose.js
const $globalData = {
user: 0,
url: "http://dev.touchczy.top/",
header: {
'content-type': 'application/x-www-form-urlencoded'
}
}
export default {
$globalData: $globalData
}
Import and extend Vue prototype in main.js
import dispose from '@/vector/dispose'
Vue.prototype.$globalData = dispose.$globalData;
Import components in dispose.js, encapsulate loading and alert, and export them
import {
Message,
Loading
} from 'element-ui'
function startLoading(options) {
if (!options.load) return true;
var loadingInstance = Loading.service({
lock: true,
text: 'loading...'
})
return loadingInstance;
}
function endLoading(options, loadingInstance) {
if (!options.load) return true;
loadingInstance.close();
}
function toast(msg, type = 'error') {
Message({
message: msg,
type: type,
duration: 2000,
center: true
})
}
export default {
$toast: toast
}
main.js
import dispose from '@/vector/dispose'
Vue.prototype.$toast = dispose.$toast;
function extend() {
var aLength = arguments.length;
var options = arguments[0];
var target = {};
var copy;
var i = 1;
if (typeof options === "boolean" && options === true) {
// Deep copy (only recursively process objects)
for (; i < aLength; i++) {
if ((options = arguments[i]) != null) {
if (typeof options !== 'object') {
return options;
}
for (var name in options) {
copy = options[name];
if (target === copy) {
continue;
}
target[name] = this.extend(true, options[name]);
}
}
}
} else {
// Shallow copy
target = options;
if (aLength === i) {
target = this;
i--;
} // If there is only one parameter, extend the function. If there are more than two parameters, add the subsequent objects to the first object
for (; i < aLength; i++) {
options = arguments[i];
for (var name in options) {
target[name] = options[name];
}
}
}
return target;
}
Since I just started learning Vue, I still prefer to use success
, fail
, and complete
for network requests. I have encapsulated the request to load Loading
when sending a request, pop up an error message if the request fails, and return a promise
object so that then()
and other methods can still be used. When making a post
request, I use { 'content-type': 'application/x-www-form-urlencoded'}
as the request header, and in transformRequest
I convert json format requests to form requests.
function ajax(requestInfo) {
var options = {
load: true,
url: "",
method: "GET",
data: {},
param: {},
success: () => {},
fail: function() { this.completeLoad = () => {toast("Server error", 'error');} },
complete: () => {},
completeLoad: () => {}
};
extend(options, requestInfo);
let loadingInstance = startLoading(options);
return axios.request({
url: options.url,
data: options.data,
params: options.param,
method: options.method,
headers: $globalData.header,
transformRequest: [function(data) {
let ret = ''
for (let it in data) ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
return ret
}]
}).then(function(res) {
try {
options.success(res);
} catch (e) {
options.completeLoad = () => {
toast("PARSE ERROR");
}
console.warn(e);
}
}).catch(function(res) {
options.fail(res);
}).then(function(res) {
endLoading(options, loadingInstance);
try {
options.complete(res);
} catch (e) {
console.warn(e);
}
options.completeLoad(res);
})
}
export default {
$ajax: ajax
}
main.js
import dispose from '@/vector/dispose'
Vue.prototype.$ajax = dispose.$ajax;
When testing, if you need to handle cross-origin requests and use cookies, first declare axios.defaults.withCredentials = true. At this point, the backend cannot set Access-Control-Allow-Origin to *. axios
axios.defaults.withCredentials = true
PHP
header('Content-Type: text/html;charset=utf-8');
header('Access-Control-Allow-Origin:http://localhost:8080'); // Allow website requests
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE'); // Allow request types
header('Access-Control-Allow-Credentials: true'); // Set whether cookies are allowed to be sent
header('Access-Control-Allow-Headers: Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin'); // Set the fields for allowing custom request headers
// Build production version node build/build.js
require('./check-versions')() // call the file check-versions to check the versions and execute this function directly
process.env.NODE_ENV = 'production' // Register on the window global variable to distinguish between production and development environments; this is the production environment
const ora = require('ora') // Terminal display of spinning loading
const rm = require('rimraf') // Library for the rm -rf command in the Node environment
const path = require('path') // File path processing library
const chalk = require('chalk') // Terminal display of text with colors
const webpack = require('webpack') // webpack
const config = require('../config') // Import configuration
const webpackConfig = require('./webpack.prod.conf') // Import configuration for the production environment
const spinner = ora('building for production...') // Terminal display of the build process
spinner.start() // Terminal display of loading
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { // Delete the compiled files, i.e., the dist folder
if (err) throw err
webpack(webpackConfig, (err, stats) => { // Start compiling in the callback function after deletion
spinner.stop() // Terminal stop loading
if (err) throw err
process.stdout.write(stats.toString({ // After compilation, output the compiled files in the terminal
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
/* ... */ // The following is the output of the compilation result
})
})
const chalk = require('chalk') // Terminal display of text with colors
const semver = require('semver') // Version checking
const packageConfig = require('../package.json') // Read the project configuration file
const shell = require('shelljs') // shell
function exec (cmd) { //return a newly created child process through the child_process module, execute the Unix system command and convert it to a string without spaces
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version), //use semver to format the version
versionRequirement: packageConfig.engines.node //get the node version set in package.json
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'), //call the npm --version command and pass the parameter to the exec function to get the clean version number
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
/* ... */ //warning or error message
}
//process css
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) { //export the location of the file and determine whether it is in the development environment or production environment based on the environment, according to the build.assetsSubDirectory or dev.assetsSubDirectory defined in the index.js file in the config folder
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path) //the path module provides some tools for handling file paths
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = { //use css-loader and postcssLoader, use the options.usePostCSS property to determine whether to use methods such as compression in postcssLoader
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
function generateLoaders(loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, { // Object.assign is a shallow copy in ES6 syntax, and the last two are merged and copied to complete the assignment.
sourceMap: options.sourceMap
})
})
}
if (options.extract) {
return ExtractTextPlugin.extract({ // ExtractTextPlugin can extract text, representing the use of the above processed loaders first, and using vue-style-loader when not correctly introduced
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders) // return the final value of vue-style-loader connection loaders
}
}
return {
css: generateLoaders(), // need css-loader and vue-style-loader
postcss: generateLoaders(), // need css-loader and postcssLoader and vue-style-loader
less: generateLoaders('less'), // need less-loader and vue-style-loader
sass: generateLoaders('sass', { indentedSyntax: true }), // need sass-loader and vue-style-loader
scss: generateLoaders('sass'), // need sass-loader and vue-style-loader
stylus: generateLoaders('stylus'), // need stylus-loader and vue-style-loader
styl: generateLoaders('stylus') // need stylus-loader and vue-style-loader
}
} exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) { // combine various css, less, sass, etc. to get the result and output the output
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier') // send notifications to the cross-platform notification system
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({ // when an error occurs, output the title of the error message, error message details, subtitle, and icon
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png') // used to join paths and will correctly use the path separator of the current system, "/" for Unix and "\" for Windows
})
}
}
// Processing .vue files, parsing each language block (template, script, style) in this file and converting it into a JS module that can be used by JS.
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = { // Processing CSS files in the project, enabling source map by default in production and testing environments, while extracting styles into separate files only in the production environment.
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: { // During template compilation, the compiler can convert certain attributes, such as src paths, into require calls so that the target resource can be processed by webpack.
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
// The base configuration file extracted for common use in development and production, mainly implementing entry configuration, output environment configuration, module resolution configuration, and plugins.
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) { // Concatenates the absolute path.
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
entry: { // Entry file, there can be multiple entries or just one, by default, only one entry 'app' for single page applications
app: './src/main.js'
},
output: { // Output configuration, the default target folder path is /dist
path: config.build.assetsRoot, // Path
filename: '[name].js', // Output file name
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'], // Automatically extend file suffixes, e.g., if it's a js file, you can omit the .js when referencing
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'), // @ is equivalent to /src
}
},
module: { // Use plugins to configure the processing method for corresponding files
rules: [{
test: /\.vue$/,
loader: 'vue-loader', // Use vue-loader to transform vue files into js modules
options: vueLoaderConfig
},{
test: /\.js$/,
loader: 'babel-loader', // js files need to be compiled into es5 files and compressed using babel-loader
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader', // Images, audio-visual, and fonts are all handled using url-loader, files over 10000 will be compiled into base64
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: { // The following options are Node.js global variables or modules, primarily to prevent webpack from injecting Node.js modules into Vue
setImmediate: false,
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
// Development environment webpack related configuration
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge') // Implement inheritance of webpack.base.config.js by webpack.dev.conf.js through webpack-merge
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // Plugin to beautify webpack error messages and logs
const portfinder = require('portfinder') // By default, search for the location of the idle port, searching for port 8000
const HOST = process.env.HOST // process is a global object of node to access the current program's environment variable, i.e., HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) // Rules are style loaders processed in the utils tool, generating rules for css, less, postcss, etc.
},
devtool: config.dev.devtool, // Enhanced debugging
devServer: { // All the configurations here are set in the config's index.js
clientLogLevel: 'warning',
historyApiFallback: { // When using the HTML5 History API, any 404 responses can be replaced with index.html
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true, // Hot reload
contentBase: false,
compress: true, // Compression
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser, // Automatically open the browser when debugging
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable, // Interface proxy
quiet: true, // Whether to disable printing warnings and errors in the console, if using FriendlyErrorsPlugin, this will be true
watchOptions: {
poll: config.dev.poll, // File system change detection
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(), // Module hot replacement plugin, no need to refresh the page when modifying modules
new webpack.NamedModulesPlugin(), // Display the correct name of files
new webpack.NoEmitOnErrorsPlugin(), // When there is a webpack compilation error, it stops the packaging process to prevent erroneous code from being packaged into files
new HtmlWebpackPlugin({ // This plugin can automatically generate an HTML5 file or inject the compiled code into a template file
filename: 'index.html',
template: 'index.html',
inject: true
}),
new CopyWebpackPlugin([ // Copy plugin
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => { // Find the port number
if (err) {
reject(err)
} else { // When the port is occupied, reset the port for evn and devServer
process.env.PORT = port
devWebpackConfig.devServer.port = port
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)
}
})
})
// Configuration file related to production environment in webpack
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge') // Plugin for merging webpack configurations
const baseWebpackConfig = require('./webpack.base.conf') // Basic webpack configuration
const CopyWebpackPlugin = require('copy-webpack-plugin') // Plugin for copying files and folders in webpack
const HtmlWebpackPlugin = require('html-webpack-plugin') // Plugin for generating and injecting HTML into .html files
const ExtractTextPlugin = require('extract-text-webpack-plugin') // Plugin for extracting CSS
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') // Plugin for optimizing and compressing CSS in webpack
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap, // Enable debug mode. Default is true
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: { // Compress
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
new ExtractTextPlugin({ // Extract text, for example, styles inserted on the index page after packaging are extracted by this plugin to reduce requests
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
new OptimizeCSSPlugin({ // CSS optimization plugin
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
new HtmlWebpackPlugin({ // HTML packaging
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true, // Remove comments
collapseWhitespace: true, // Remove spaces
removeAttributeQuotes: true // Remove attribute quotes
},
chunksSortMode: 'dependency' // Module sort, in the order we need
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.CommonsChunkPlugin({ // Extract common modules
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
)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
new CopyWebpackPlugin([ // For example, after packaging, the packaged files need to be copied to the dist directory
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
// Files in the config folder serve the build
const merge = require('webpack-merge') // webpack-merge provides a merge function, which creates a new object by merging arrays and merged objects, meeting functions and executing them, and encapsulating the return values in functions. Here, dev and prod are merged
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
// Configuration files are used to define parameters needed in development and production environments
const path = require('path')
module.exports = {
dev: { // Configuration under the development environment
assetsSubDirectory: 'static', // Subdirectory, generally for storing css, js, image and other files
assetsPublicPath: '/', // Root directory
proxyTable: {}, // This property can be used to solve cross-domain problems
host: 'localhost', // Service startup address
port: 8080, // Service startup port
autoOpenBrowser: false, // Whether to open the browser automatically
errorOverlay: true, // Browser error prompt
notifyOnErrors: true, // Cross-platform error prompt
poll: false, // Notify devServer.watchOptions of file system changes
devtool: 'cheap-module-eval-source-map', // Adding debugging. This property is for original source code (line only) and cannot be used in production environments
cacheBusting: true, // Invalidating the cache
cssSourceMap: true // It will be very difficult to locate bugs after code compression, so sourcemap is introduced to record the location information before and after compression. When an error occurs, the position before compression can be directly located
},
build: { // Configuration for production environment
index: path.resolve(__dirname, '../dist/index.html'), // The location and name of the compiled index file, change the suffix as needed, such as index.php
assetsRoot: path.resolve(__dirname, '../dist'), // Location for storing compiled production environment code
assetsSubDirectory: 'public/vue-app/index', // Folder name for storing js, css, images, etc.
assetsPublicPath: '/', // Root directory for publication as the absolute path of the web container, change to ./ for relative path
productionSourceMap: true,
devtool: '#source-map',
productionGzip: false, // Gzip command in unit is used to compress files. In gzip mode, the file extensions to be compressed are js and css
productionGzipExtensions: ['js', 'css'],
bundleAnalyzerReport: process.env.npm_config_report
}
}
// Use prod.env.js for production environment configuration during deployment
module.exports = {
NODE_ENV: '"production"'
}
// Configure proxyTable in config/index.js
proxyTable: {
'/':{
target: "http://www.xxx.com", // the address to be accessed
changeOrigin: true // enable cross-origin
}
}
// The configuration can be understood as proxying access to / as http://www.xxx.com to avoid triggering cross-origin issues and being intercepted by the browser.
// In Vue requests to access the http://www.xxx.com/testData interface can be written directly as requests to /testData
// Use process.env.NODE_ENV to differentiate between development and production environment when requesting URLs