在上篇文章基础上https://blog.csdn.net/weixin_43554698/article/details/89914721
优化:
- 1.文件压缩(js、css、图片)
- 2.打包速度优化.使用happypack和thread-loader加速构建(由于HappyPack 对file-loader、url-loader 支持的不友好,所以不建议对该loader使用)
- 3.提取公共代码
- 4.使用第三方类库(如果想要缩小打包体积Tree Shaking需要使用ES6模块和UglifyJsPlugin插件,以及配置optimization选项,设置usedExports和sideEffects为true)
修改上次目录结构
- 需要下载的依赖package.js
{
"name": "webpack4-vue",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "cross-env BUILD_MODE=dev webpack-dev-server ",
"build": "cross-env BUILD_MODE=prod webpack "
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-proposal-decorators": "^7.2.3",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/polyfill": "^7.2.5",
"@babel/preset-env": "^7.0.0",
"@babel/runtime": "^7.0.0",
"babel-loader": "^8.0.5",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^5.0.3",
"cross-env": "^5.2.0",
"css-loader": "^0.28.11",
"glob": "^7.0.3",
"happypack": "^5.0.1",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.6.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-loader": "^2.1.5",
"sass-loader": "^7.0.3",
"sass-resources-loader": "^1.3.3",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.7",
"url-loader": "^1.1.2",
"vue-loader": "^15.7.0",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.3.1",
"webpack-merge": "^4.2.1"
},
"dependencies": {
"vue": "^2.6.10"
}
}
- webpack.config.js配置
// package.json中通过 --BUILD_MODE 指定当前执行的配置文件
const env = process.env.BUILD_MODE.trim();
module.exports = require(`./build/webpack.${env}.js`);
- build/config.js中多页的配置信息:
这个文件是为了配合多入口文件,这里只有一个所以就写一个就可以
- build/webpack.base.js基础配置文件
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = require('./config'); // 多页面的配置项
let HTMLPlugins = [];
let Entries = {};
config.HTMLDirs.forEach(item => {
let filename = `${item.page}.html`;
const htmlPlugin = new HTMLWebpackPlugin({
title: item.title, // 生成的html页面的标题
filename: filename, // 生成到dist目录下的html文件名称,支持多级目录(eg: `${item.page}/index.html`)
template: path.resolve(__dirname, `../src/template/${item.page}.html`), // 模板文件,不同入口可以根据需要设置不同模板
chunks: [item.page, 'vendor'], // html文件中需要要引入的js模块,这里的 vendor 是webpack默认配置下抽离的公共模块的名称
});
HTMLPlugins.push(htmlPlugin);
Entries[item.page] = path.resolve(__dirname, `../src/mainJs/${item.page}.js`); // 根据配置设置入口js文件
});
const env = process.env.BUILD_MODE.trim();
let ASSET_PATH = '/'; // dev 环境
if (env === 'prod') ASSET_PATH = './'; // build 时设置成实际使用的静态服务地址
module.exports = {
entry: Entries,
output: {
publicPath: ASSET_PATH,
filename: 'js/[name].[hash:8].js',
path: path.resolve(__dirname, '../dist'),
},
module: {
rules: [{
test: /\.vue$/, // 处理vue模块
use: 'vue-loader',
},
{
test: /\.js$/, //处理es6语法
exclude: /node_modules/,
use: ['babel-loader'],
}
]
},
resolve: { // 设置模块如何被解析
alias: {
'@': path.resolve(__dirname, '../src')
},
extensions: ['*', '.css', '.js', '.vue']
},
plugins: [
new VueLoaderPlugin(),
new CopyWebpackPlugin([{
from: path.resolve(__dirname, '../src/statick'),
to: path.resolve(__dirname, '../dist'),
ignore: ['*.html']
},
// {
// from: path.resolve(__dirname, '../src/scripts/lib'),
// to: path.resolve(__dirname, '../dist')
// }
]),
...HTMLPlugins, // 利用 HTMLWebpackPlugin 插件合成最终页面
new webpack.DefinePlugin({
'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH) // 利用 process.env.ASSET_PATH 保证模板文件中引用正确的静态资源地址
})
]
};
vue-loader要配合 VueLoaderPlugin 插件一起使用。 babel-loader 要配合 .babelrc 使用。这里配置“stage-2”以使用es7里的高级语法,实测如果不配置就无法处理 对象扩展符、async和await 等新语法特性。
- .babelrc配置:
{
// targets, useBuiltIns 等选项用于编译出兼容目标环境的代码
// 其中 useBuiltIns 如果设为 "usage"
// Babel 会根据实际代码中使用的 ES6/ES7 代码,以及与你指定的 targets,按需引入对应的 polyfill
// 而无需在代码中直接引入 import '@babel/polyfill',避免输出的包过大,同时又可以放心使用各种新语法特性。
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": [
"> 1%",
"last 2 versions",
"ie >= 11"
]
},
"useBuiltIns": "usage" // 按需引入 polyfill
}
]
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-syntax-dynamic-import",
["@babel/plugin-proposal-class-properties", { "loose": false }],
["@babel/plugin-proposal-decorators", { "legacy": true }],
]
}
关于 .babelrc 相关的配置可参考: 官方文档; babel配置-各阶段的stage的区别
- postcss.config.js配置
module.exports = {
plugins: {
"autoprefixer": {
browsers: ["last 5 version", "Android >= 4.0"],
//是否美化属性值 默认:true
cascade: true,
//是否去掉不必要的前缀 默认:true
remove: true
}
}
}
- build/webpack.dev.js开发配置文件
const path = require('path');
const webpackBase = require('./webpack.base');
const webpackMerge = require('webpack-merge');
const config = require('./config');
module.exports = webpackMerge(webpackBase, {
mode: 'development',
module: {
rules: [{
test: /\.css$/,
// 开发模式下使用 vue-style-loader,以便使用热重载
use: [
'vue-style-loader',
'css-loader',
'postcss-loader',
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader',
'postcss-loader',
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, '../src/styles/lib/main.scss'),
}
}
]
},
// {
// test: /\.(js|vue)$/,
// enforce: 'pre', // 强制先进行 ESLint 检查
// exclude: /node_modules|lib/,
// loader: 'eslint-loader',
// options: {
// // 启用自动修复
// fix: true,
// // 启用警告信息
// emitWarning: true,
// }
// },
{
test: /\.(png|svg|jpg|gif)$/, // 处理图片
use: {
loader: 'url-loader', // 解决打包css文件中图片路径无法解析的问题
options: {
// 打包生成图片的名字
name: '[name].[hash:8].[ext]',
// 图片的生成路径
outputPath: config.imgOutputPath,
}
}
},
// {
// test: /\.(woff|woff2|eot|ttf|otf)$/, // 处理字体
// use: {
// loader: 'url-loader',
// options: {
// outputPath: config.fontOutputPath,
// }
// }
// }
{ test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, // 处理 字体文件的 loader
]
},
devServer: {
contentBase: config.devServerOutputPath,
overlay: {
errors: true,
warnings: true,
},
open: true // 服务启动后 打开浏览器
}
});
- build/webpack.prod.js生产配置文件
// 引入基础配置
const path = require('path');
const webpackBase = require('./webpack.base');
// 引入 webpack-merge 插件
const webpackMerge = require('webpack-merge');
// 清理 dist 文件夹
const CleanWebpackPlugin = require('clean-webpack-plugin');
// js压缩、优化插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 抽取css extract-text-webpack-plugin不再支持webpack4,官方出了mini-css-extract-plugin来处理css的抽取
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
//由于HappyPack 对file-loader、url-loader 支持的不友好,所以不建议对该loader使用
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const config = require('./config'); // 多页面的配置项
const ASSET_PATH = '//abc.com/static/'; // 线上静态资地址
// 合并配置文件
module.exports = webpackMerge(webpackBase, {
mode: 'production',
module: {
rules: [{
test: /\.js$/,
//把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行
loader: 'happypack/loader?id=happyBabel',
//排除node_modules 目录下的文件
exclude: /node_modules/
},
{
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)$/, // 处理图片
use: {
loader: 'file-loader', // 解决打包css文件中图片路径无法解析的问题
options: {
// 打包生成图片的名字
name: 'list/[name].[hash:8].[ext]',
// 图片的生成路径
//outputPath: config.imgOutputPath,
publicPath: ASSET_PATH
}
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/, // 处理字体
use: {
loader: 'url-loader',
options: {
outputPath: config.fontOutputPath,
publicPath: ASSET_PATH
}
}
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
new UglifyJsPlugin({ // 压缩js
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: false,
drop_console: true
}
}
}),
new OptimizeCSSAssetsPlugin({ // 压缩css
cssProcessorOptions: {
safe: true
}
})
]
},
plugins: [
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyBabel',
//如何处理 用法和loader 的配置一样
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享进程池
threadPool: happyThreadPool,
//允许 HappyPack 输出日志
verbose: true,
}),
// 自动清理 dist 文件夹
new CleanWebpackPlugin(['dist'], {
root: path.resolve(__dirname, '..'), // 根目录
verbose: true, //开启在控制台输出信息
dry: false, // 启用删除文件
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[chunkhash:8].css'
})
]
});
- src/mainJs/index.js入口文件
import Vue from 'vue';
import App from '@/App.vue';
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app');
- src/App.vue
<template>
<div id="app">首页</div>
</template>
<script>
export default {
}
</script>
<style>
#app{
background: burlywood
}
</style>
src/template/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
- 如何使用和管理第三方JS库
- CDN:标签引入即可
- npm 包管理: 目前最常用和最推荐的方法
- 本地js文件:一些库由于历史原因,没有提供es6版本,需要手动下载,放入项目目录中,再手动引入。
针对第一种和第二种方法,各有优劣,有兴趣可以看这篇:《CDN 使用心得:加速双刃剑》
针对第三种方法,如果没有webpack,则需要手动引入import或者require来加载文件;但是,webpack提供了alias的配置,配合webpack.ProvidePlugin这款插件,可以跳过手动入,直接使用!
//页面使用
// app.js
$("div").addClass("new");
jQuery("div").addClass("old");
在配置文件中设置
webpack.ProvidePlugin参数是键值对形式,键就是我们项目中使用的变量名,值就是键所指向的库。
webpack.ProvidePlugin会先从npm安装的包中查找是否有符合的库。
如果webpack配置了resolve.alias选项(理解成“别名”),那么webpack.ProvidePlugin就会顺着这条链一直找下去。
resolve: {
alias: {
jQuery$: path.resolve(__dirname, "src/vendor/jquery.min.js")
}
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery", // npm
jQuery: "jQuery" // 本地Js文件
})
]