拋棄自帶的vue.config.js的配置模式,手動使用webpack4進行構建:javascript
webpack4 相關loaders和pluginscss
>>>>>>>>>>>>>>>>>>>>>>>相關loader<<<<<<<<<<<<<<<<<<<<<< vue-loader 編譯.vue文件 babel-loader 編譯成ES5 file-loader 解決文件中 import/require() 的資源,轉化爲URL,再輸出到指定文件夾內 url-loader 把圖片轉化成 base64 URLs, 能夠根據limit大小自由控制 css-loader css-loader 解釋 @import and url() 好比 import/require() 而後解析他們 file-loader file-loader 解析文件中的 import/require() 成一個URL 而後輸出到輸出文件中 vue-style-loader style-loader dev環境,把css注入到DOM >>>>>>>>>>>>>>>>>>>>>>>相關plugin<<<<<<<<<<<<<<<<<<<<<< mini-css-extract-plugin 提取css到單獨的文件 clean-webpack-plugin 清理構建的資源 webpack-build-notifier 構建完成桌面提醒 html-webpack-plugin 生成html入口模板 optimize-css-assets-webpack-plugin css去重壓縮 purgecss-webpack-plugin 去除css中未使用的代碼 webpack-dev-server 本地server webpack-spritesmith 自動整合成雪碧圖 compression-webpack-plugin @gfx/zopfli 壓縮代碼,根據算法生成gzip webpack-bundle-analyzer 生成bundle後分析報告,方便優化 複製代碼
安裝依賴html
yarn add -D webpack webpack-cli webpack-dev-server vue-loader babel-loader file-loader css-loader style-loader url-loader mini-css-extract-plugin clean-webpack-plugin webpack-build-notifier html-webpack-plugin optimize-css-assets-webpack-plugin purgecss-webpack-plugin webpack-spritesmith compression-webpack-plugin webpack-bundle-analyzer
複製代碼
@babel/core
@babel/preset-env
@babel/cli
@babel/polyfill
// runtime
@babel/runtime
@babel/plugin-transform-runtime
// 動態插入
@babel/plugin-syntax-dynamic-import
// 支持 ...spread
@babel/plugin-syntax-object-rest-spread
// commonjs
@babel/plugin-transform-modules-commonjs
// 支持 vue jsx語法
@babel/plugin-syntax-jsx
babel-plugin-transform-vue-jsx
//支持 element-ui 按需加載
babel-plugin-component
//支持 lodash 按需加載
babel-plugin-lodash
// 移除 console.log
babel-plugin-transform-remove-console
複製代碼
babel.config.jsvue
const removeConsolePlugin = [];
if (process.env.NODE_ENV === 'production') {
removeConsolePlugin.push('transform-remove-console');
}
module.exports = {
// presets: ['@vue/app'],
presets: [
[
'@babel/preset-env',
{
// transform any
loose: true
}
]
],
// 藉助 babel-plugin-component,咱們能夠只引入須要的組件,以達到減少項目體積的目的
plugins: [
// import
'@babel/plugin-syntax-dynamic-import',
// transform
'@babel/plugin-transform-runtime',
'@babel/plugin-transform-modules-commonjs',
// vue jsx語法
'@babel/plugin-syntax-jsx',
'transform-vue-jsx',
'lodash',
// spread ...
// '@babel/plugin-syntax-object-rest-spread',
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
],
...removeConsolePlugin
]
};
複製代碼
webpack.config.jsjava
/** * webpack 4 config * @author master2011zhao@gmail.com * @Date 20190910 */
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4 使用 mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// extract 被廢棄
// const ExtractTextPlugin = require('extract-text-webpack-plugin');
// clean project
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 壓縮css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// notifier
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
// 壓縮代碼
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const zopfli = require('@gfx/zopfli');
// vue loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// 圖片整合成雪碧圖
const SpritesmithPlugin = require('webpack-spritesmith');
// bundle分析
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// path function
const resolve = src => {
return path.resolve(__dirname, src);
};
// nginx 配置二級目錄 base url
let serverBaseUrl = '';
// customerTemplate
const templateFunction = function(data) {
// console.log('---', data)
const shared = `.sprite_ico { background-image: url(I);display:inline-block;background-size: Wpx Hpx;}`
.replace('I', data.sprites[0].image)
.replace('W', data.spritesheet.width)
.replace('H', data.spritesheet.height);
const perSprite = data.sprites
.map(function(sprite) {
return `.sprite_ico_N { width: Wpx; height: Hpx; background-position: Xpx Ypx;}`
.replace('N', sprite.name)
.replace('W', sprite.width)
.replace('H', sprite.height)
.replace('X', sprite.offset_x)
.replace('Y', sprite.offset_y);
})
.join('\n');
return '//out:false' + '\n' + shared + '\n' + perSprite;
};
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
console.log('isProduction', isProduction);
// 傳遞給 babel.config.js
process.env.NODE_ENV = argv.mode;
// console.log(process.env.NODE_ENV);
let plugins = [new VueLoaderPlugin()];
// 生成模板
let HtmlTemplates = [];
// 生產環境
if (isProduction) {
// 清理項目, 清理不乾淨,須要使用 rm.sh
plugins.push(
new CleanWebpackPlugin({
dry: false,
verbose: true
})
);
// 雪碧圖
plugins.push(
new SpritesmithPlugin({
src: {
//下面的路徑,根據本身的實際路徑配置
cwd: path.resolve(__dirname, 'src/assets/icons'),
glob: '*.png'
},
// 輸出雪碧圖文件及樣式文件
target: {
//下面的路徑,根據本身的實際路徑配置
image: path.resolve(__dirname, 'src/assets/sprite.png'),
css: [
[
path.resolve(__dirname, 'src/less/sprite.less'),
{
format: 'function_based_template'
}
]
]
// css: path.resolve(__dirname, './src/less/sprite.less')
},
// 自定義模板
customTemplates: {
function_based_template: templateFunction
},
// 樣式文件中調用雪碧圖地址寫法
apiOptions: {
// 這個路徑根據本身頁面配置
cssImageRef: '../assets/sprite.png'
},
spritesmithOptions: {
// algorithm: 'top-down'
padding: 5
}
})
);
// 構建完成提醒
plugins.push(
new WebpackBuildNotifierPlugin({
title: 'project build',
suppressSuccess: true,
suppressWarning: true,
messageFormatter: function() {
return 'build completely';
}
})
);
// 分離css
// plugins.push(new ExtractTextPlugin('css/[name].[hash:8].css'));
plugins.push(
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// all options are optional
filename: 'css/[name].[hash:8].css',
chunkFilename: 'css/[name].[hash:8].css',
publicPath: './' + serverBaseUrl,
ignoreOrder: false // Enable to remove warnings about conflicting order
})
);
// 去除重複的 less, 好比 common
plugins.push(
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: [
'default',
{
discardComments: {
removeAll: true
}
}
]
},
canPrint: true
})
);
//再次壓縮代碼
plugins.push(
new CompressionWebpackPlugin({
deleteOriginalAssets: false,
test: /\.(js|css|html|woff|ttf|png|jpg|jpeg)$/,
compressionOptions: {
numiterations: 15
},
threshold: 10240,
minRatio: 0.8,
algorithm(input, compressionOptions, callback) {
return zopfli.gzip(input, compressionOptions, callback);
}
})
);
// 公共提取的chunk
const commonChunks = ['chunk-vendors', 'runtime', 'chunk-commons', 'css-commons'];
const minify = {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
};
// 生成模板
HtmlTemplates = [
new HtmlWebpackPlugin({
title: 'Index',
template: resolve('public/index.html'),
filename: 'index.html',
hash: true,
minify,
chunks: [...commonChunks, 'index'],
favicon: resolve('public/favicon.ico')
})
];
// 分析生成的包
plugins.push(
new BundleAnalyzerPlugin({
// 生成report.html
analyzerMode: 'static'
})
);
} else {
// 生成模板
HtmlTemplates = [
new HtmlWebpackPlugin({
title: 'Index',
template: resolve('./public/index.html'),
filename: 'index.html',
favicon: resolve('public/favicon.ico'),
chunks: ['index', 'runtime']
})
];
}
return {
entry: {
index: resolve('src/main.js')
},
output: {
path: resolve('cdn'),
filename: 'js/[name].[hash:8].js',
publicPath: isProduction ? './' + serverBaseUrl : ''
},
// 本地調試
devtool: !isProduction ? 'inline-source-map' : '',
devServer: {
port: 3000,
open: true,
hot: true,
// 配置 browserHistory 路由,防止刷新就 404
historyApiFallback: true,
compress: true,
contentBase: path.resolve(__dirname, ''),
noInfo: false,
overlay: {
warnings: true,
errors: true
},
proxy: {
'/api/v1': {
target: 'http://192.168.1.100:18080',
changeOrigin: true,
router: {
'/shareIndex': 'http://192.168.1.110:18080',
}
},
}
},
resolve: {
// 別名
alias: {
'@': resolve('src'),
'@c': resolve('src/components'),
'@less': resolve('src/less'),
'@util': resolve('src/utils'),
'@assets': resolve('src/assets'),
'@pages': resolve('src/pages')
},
// 自動添加後綴
extensions: ['.vue', '.js', '.less']
},
module: {
rules: [
{
test: /\.vue?$/,
use: 'vue-loader'
},
{
test: /\.js?$/,
use: 'babel-loader'
},
{
test: /\.css?$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
{
loader: 'css-loader',
options: {}
}
]
},
{
test: /\.less$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
},
{
test: /\.(png|jpg|svg|gif|ico|woff|ttf)?$/,
use: [
{
loader: 'url-loader',
options: {
// 這裏的options選項參數能夠定義多大的圖片轉換爲base64
fallback: 'file-loader',
limit: 10 * 1024, // 表示小於10kb的圖片轉爲base64,大於10kb的是路徑
outputPath: 'images', //定義輸出的圖片文件夾名字
publicPath: '../images', //css中的路徑
// name: '[name].[contenthash:8].[ext]'
name: '[sha512:contenthash:base64:8].[ext]'
}
}
]
}
]
},
plugins: [...plugins, ...HtmlTemplates],
optimization: {
splitChunks: {
// 靜態資源緩存
// test, priority and reuseExistingChunk can only be configured on cache group level.
cacheGroups: {
// 提取 node_modules 裏面依賴的代碼
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'chunk-vendors',
chunks: 'all',
minSize: 0,
minChunks: 2, //2個共享以及以上都提取
priority: -10 //優先級
},
// 提出每一個模塊公共的代碼
commons: {
name: 'chunk-commons',
test: /\.js$/,
chunks: 'initial',
minChunks: 2, //兩個共享以及以上都提取,
minSize: 0,
priority: -20, //優先級
reuseExistingChunk: true
},
css: {
name: 'css-commons',
test: /\.less$/,
minChunks: 2,
minSize: 0,
priority: -30,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
// I pull the Webpack runtime out into its own bundle file so that the
// contentHash of each subsequent bundle will remain the same as long as the
// source code of said bundles remain the same.
runtimeChunk: 'single'
}
};
};
複製代碼
package.jsonnode
scripts:{
"dev": "webpack-dev-server --mode development",
"build": "webpack --mode production",
}
複製代碼