老板要求将之前的vue项目升级一下版本,也就是webpack4.x版本,对于从来没有升级过项目的本人来说是个不小的挑战。
在此之前已经研究了一天的webpack4.x文档,说实话文档真的不容易吃透,还要慢慢摸索。
0配置
相比于webpack3,webpack4可以零配置运行,即webpack4的0配置也只是支持了默认entry 和 output而已,即默认entry为./src,默认output为/dist。
模式mode
就是相比于webpack3,webpack4新增了一个mode配置选择,用来表示配置模式的选择情况,也就是生产环境production、开发环境devolopment和自定义none这三个选择可选
optimization
webpack4.x已经移除了commonchunk插件,改用了optimization属性,运用起来就很灵活了。具体列子可以去官方文档上面看~
开始升级
1.首先node版本要>=8.9.4,我的版本目前是8.10.0,主要还是es6语法增多,得到新的原声支持。
2.webpack4.x版本需要安装webpack-cli
3.升级主要部件 webpack、webpack-bundle-analyzer、webpack-dev-server、webpack-merge
cnpm install -D webpack webpack-cli webpack-bundle-analyzer webpack-dev-server webpack-merge 如果这样安装在package里面版本没及时更新,那就先卸载之前的在重新安装
3.升级插件copy-webpack-plugin、css-loader、eslint-loader、file-loader、html-webpack-plugin、url-loader、friendly-errors-webpack-plugin、optimize-css-assets-webpack-plugin、uglifyjs-webpack-plugin
4.升级vue-loader,这个非常重要。cnpm uninstall -D vue-loader 我现在是14.4.2版本
5.卸载插件extract-text-webpack-plugin,webpack4.x已经不需要这个了,所以配置中的这个都可以删除了,然后安装mini-css-extract-plugin插件代替,具体用法什么的可以去官方文档上面去看。
6.如果有的小伙伴用到happypack插件进行多线程打包,一定要升级happypac!!!!!!!(重点,不然会报 'length’的错误)
配置
webpack.base.config.js
增加node:process.env.NODE_ENV 即
module.exports = {
mode: process.env.NODE_ENV,
},
去掉const ExtractTextPlugin = require(‘extract-text-webpack-plugin’)
安装const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
对应的
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader?minimize', 'autoprefixer-loader'],
fallback: 'style-loader'
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: ['css-loader?minimize', 'autoprefixer-loader', 'less-loader'],
fallback: 'style-loader'
})
},
修改成
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{loader: 'css-loader'}
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{loader: 'css-loader'},
{loader: 'less-loader'}
]
},
然后这是我整个文件:
const path = require('path')
const os = require('os')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HappyPack = require('happypack')
require('babel-polyfill')
var happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length})
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
mode: process.env.NODE_ENV,
entry: {
main: ['babel-polyfill', '@/main'],
'vender-base': '@/vendors/vendors.base.js',
'vender-exten': '@/vendors/vendors.exten.js'
},
output: {
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
css: 'vue-style-loader!css-loader',
less: 'vue-style-loader!css-loader!less-loader'
},
postLoaders: {
html: 'babel-loader'
}
}
},
{
test: /iview\/.*?js$/,
loader: 'happypack/loader?id=happybabel',
exclude: /node_modules/
},
{
test: /\.js$/,
loader: 'happypack/loader?id=happybabel',
exclude: /node_modules/
},
{
test: /\.js[x]?$/,
include: [resolve('src')],
exclude: /node_modules/,
loader: 'happypack/loader?id=happybabel'
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{loader: 'css-loader'}
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{loader: 'css-loader'},
{loader: 'less-loader'}
]
},
{
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url-loader?limit=1024'
},
{
test: /\.(html|tpl)$/,
loader: 'html-loader'
}
]
},
plugins: [
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader'],
threadPool: happyThreadPool,
verbose: true
}),
new CopyWebpackPlugin([
{
from: 'node_modules/layui-src/dist/images/face',
to: 'images/face'
},
{
from: 'src/images/edi',
},
])
],
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue': 'vue/dist/vue.esm.js',
'@': resolve('../src'),
'nodemodules': resolve('../node_modules'),
'api': resolve('../src/api'),
'libs': resolve('../src/libs'),
'my-components': resolve('../src/views/my-components'),
'system': resolve('../src/views/system'),
'views': resolve('../src/views'),
'jquery': 'layui-src/dist/lay/modules/jquery.js',
'jQuery': 'layui-src/dist/lay/modules/jquery.js',
'layui': 'layui-src/dist',
'layui-lay': 'layui-src/dist/lay/modules'
}
}
}
webpack.dev.config.js
去掉const ExtractTextPlugin = require(‘extract-text-webpack-plugin’);
添加const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
然后对应的
new ExtractTextPlugin({
filename: '[name].css',
allChunks: true
}),
修改成
new MiniCssExtractPlugin({
filename: '[name].css',
allChunks: true
}),
去掉
new webpack.optimize.CommonsChunkPlugin({
name: ['vender-exten', 'vender-base'],
minChunks: Infinity
}),
这个详情说一下CommonsChunkPlugin已经废除,然后4.x中用optimize.SplitChunksPlugin代替,因为这里是测试环境,所以不需要对这个做太多处理,只针对生产环境,下面会说到,我当时在这里卡了好久了,dev出来,项目无任何报错,但是页面为空白,最后还是被我找到解决办法了哈哈哈。
整个文件代码:
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const merge = require('webpack-merge')
const webpackBaseConfig = require('./webpack.base.config.js')
const fs = require('fs')
const path = require('path')
const package = require('../package.json')
const config = require('./config.js')
fs.open('./build/env.js', 'w', function (err, fd) {
const buf = 'export default "development";'
fs.write(fd, buf, 0, buf.length, 0, function (err, written, buffer) {})
})
module.exports = merge(webpackBaseConfig, {
mode: 'development',
devtool: 'eval-source-map', // 指定加source-map的方式
output: {
publicPath: '/dist/',
filename: '[name].js',
chunkFilename: '[name].chunk.js'
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
allChunks: true
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
},
webpackVar: {
// 获取到cross-env定义的变量cusSystem
cusSystem: JSON.stringify(process.env.cusSystem) || '""',
cusRouter: JSON.stringify(process.env.cusRouter) || '""',
}
}),
new HtmlWebpackPlugin({
title: '汕头外代电商平台 v' + package.version,
filename: '../index.html',
inject: false,
chunksSortMode: 'none' // 防止依赖包循环
}),
new CopyWebpackPlugin([
{
from: 'src/views/main-components/theme-switch/theme'
},
{
from: 'src/views/my-components/text-editor/tinymce'
}
], {
ignore: [
'text-editor.vue'
]
}),
new webpack.HotModuleReplacementPlugin(), //HMR
new webpack.NamedModulesPlugin() // HMR
],
// 设置跨域代理
devServer: process.env.cusSystem ? config.devServer[process.env.cusSystem] : {
historyApiFallback: true,
hot: true,
inline: true,
stats: {colors: true},
host: 'penavicost.gnway.cc',
// host: '192.168.1.241',
// port: 8066,
disableHostCheck: true,
proxy: {
// 匹配代理的url
'/api': {
// 目标服务器地址
target: 'http://pen.gnway.cc:8092',
// target: 'http://hg.gnway.cc:8092',
// target: 'http://qg.gnway.cc:8082',
// target: 'http://lyb.gnway.cc:8092',
// target: 'http://penavicost.l.gnway.cc', // 192.168.1.13服务器,记得要配置这个host
// 路径重写
pathRewrite: {'^/api': '/penavicost'}, // 连接192.168.1.13服务器可以不用重写接口路径
changeOrigin: true
}
}
}
})
webpack.prod.config.js
同理 ExtractTextPlugin去除,添加MiniCssExtractPlugin
去除
new webpack.optimize.CommonsChunkPlugin({
// name: 'vendors',
// filename: 'vendors.[hash].js'
name: ['vender-exten', 'vender-base'],
minChunks: Infinity
}),
写成这样
new webpack.optimize.SplitChunksPlugin({
cacheGroups: {
default: {
minChunks: 2, // 最小 chunk ,默认1
priority: -20, // 缓存组优先级
reuseExistingChunk: true, // 可设置是否重用该chunk(查看源码没有发现默认值)
},
//打包重复出现的代码
vendor: {
chunks: 'initial', // 必须三选一: "initial" | "all" | "async"(默认就是异步)
minChunks: 2,
maxInitialRequests: 5, // The default limit is too small to showcase the effect
minSize: 0, // This is example is too small to create commons chunks
name: 'vendor'
},
//打包第三方类库
commons: {
name: 'commons',
chunks: 'initial',
minChunks: Infinity
}
}
}),
new webpack.optimize.RuntimeChunkPlugin({
name: 'manifest'
}),
具体的属性值,用法看官方文档,这里不说明了~
然后是整个文件:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const cleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJsParallelPlugin = require('webpack-uglify-parallel');
const merge = require('webpack-merge');
const webpackBaseConfig = require('./webpack.base.config.js');
const os = require('os');
const fs = require('fs');
const path = require('path');
const package = require('../package.json');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
fs.open('./build/env.js', 'w', function(err, fd) {
const buf = 'export default "production";';
fs.write(fd, buf, 0, buf.length, 0, function(err, written, buffer) {});
});
module.exports = merge(webpackBaseConfig, {
output: {
// publicPath: 'http://www.huoyunji.com/dist/',
// publicPath: 'http://web.t.gnway.cc:8080/dist/', // 测试服务器
publicPath: '',
filename: '[name].[hash].js',
chunkFilename: '[name].[hash].chunk.js'
},
plugins: [
new cleanWebpackPlugin(['dist/*'], {
root: path.resolve(__dirname, '../')
}),
new ExtractTextPlugin({
filename: '[name].[hash].css',
allChunks: true
}),
new webpack.optimize.CommonsChunkPlugin({
// name: 'vendors',
// filename: 'vendors.[hash].js'
name: ['vender-exten', 'vender-base'],
minChunks: Infinity
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"' // 项目中有使用该变量,不要轻易修改
},
webpackVar: {
// 获取到cross-env定义的变量cusSystem
// 生产环境的命令行暂时没定义变量cusSystem,因此都返回'""'
cusSystem: JSON.stringify(process.env.cusSystem) || '""',
cusRouter: JSON.stringify(process.env.cusRouter) || '""',
}
}),
// new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false
// }
// }),
// new UglifyJSPlugin({ sourceMap: true }),
new UglifyJSPlugin({
parallel: true, // 并行压缩
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true, // true-去掉debugger的代码,默认就是true
// drop_console: true, // true-去掉console.*之类的代码,console.log与console.error等都会被清除掉;
pure_funcs: ['console.log'], // 可以删除对应的函数,这里暂时只删除console.log,留着console.error
},
// mangle: {
// safari10: true
// }
}
}),
// new UglifyJsParallelPlugin({
// workers: os.cpus().length,
// mangle: true,
// compressor: {
// warnings: false,
// drop_console: true,
// drop_debugger: true
// }
// }),
new CopyWebpackPlugin([
{
from: 'favicon.ico'
},
{
from: 'src/styles/fonts',
to: 'fonts'
},
{
from: 'src/views/main-components/theme-switch/theme'
},
{
from: 'src/views/my-components/text-editor/tinymce'
}
], {
ignore: [
'text-editor.vue'
]
}),
new HtmlWebpackPlugin({
title: '汕头外代电商平台 v' + package.version,
favicon: './favicon.ico',
filename: 'index.html',
template: '!!ejs-loader!./src/template/index.ejs',
inject: false
})
]
});
最后附上package.json代码
{
"name": "penavicost-frontend",
"version": "1.0.0",
"description": "汕头外代电商平台",
"main": "index.js",
"scripts": {
"init": "webpack --progress --config build/webpack.dev.config.js",
"dev": "webpack-dev-server --content-base ./ --open --inline --hot --compress --config build/webpack.dev.config.js",
"dev2": "cross-env cusRouter=1 webpack-dev-server --content-base ./ --open --inline --hot --compress --config build/webpack.dev.config.js",
"build": "webpack --progress --hide-modules --config build/webpack.prod.config.js",
"lint": "eslint --fix --ext .js,.vue src",
"test": "npm run lint"
},
"repository": {
"type": "git",
"url": "http://192.168.1.11:83/scm/git/penavicost-frontend"
},
"author": "",
"license": "MIT",
"dependencies": {
"@handsontable/vue": "^3.1.0",
"area-data": "^1.0.0",
"async-validator": "^1.8.2",
"axios": "^0.17.1",
"echarts": "^4.1.0",
"fingerprintjs2": "^1.8.6",
"handsontable": "^0.38.1",
"iview": "^3.2.2",
"iview-area": "^1.5.17",
"js-cookie": "^2.2.0",
"js-md5": "^0.7.3",
"layui-src": "^2.3.0",
"store": "^2.0.12",
"tinymce": "^4.7.13",
"vue": "^2.6.8",
"vue-handsontable-official": "1.1.0",
"vue-layer": "^0.9.10",
"vue-router": "^3.0.1",
"vuedraggable": "^2.16.0",
"vuex": "^3.0.1",
"vx-easyui": "^1.1.5"
},
"devDependencies": {
"autoprefixer-loader": "^3.2.0",
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-runtime": "^6.12.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-3": "^6.24.1",
"babel-runtime": "^6.26.0",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^5.0.2",
"cross-env": "^5.2.0",
"css-hot-loader": "^1.3.9",
"css-loader": "^2.1.1",
"ejs-loader": "^0.3.1",
"eslint": "^4.19.1",
"eslint-config-google": "^0.9.1",
"eslint-config-standard": "^10.2.1",
"eslint-loader": "^2.1.2",
"eslint-plugin-html": "^4.0.3",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-standard": "^3.1.0",
"file-loader": "^3.0.1",
"friendly-errors-webpack-plugin": "^1.7.0",
"happypack": "^5.0.0-beta.4",
"html-loader": "^0.5.4",
"html-webpack-plugin": "^3.0.7",
"less": "^2.7.3",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.5.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"semver": "^5.4.1",
"style-loader": "^0.19.1",
"uglifyjs-webpack-plugin": "^2.1.2",
"unsupported": "^1.1.0",
"url-loader": "^1.1.2",
"vue-hot-reload-api": "^2.3.0",
"vue-html-loader": "^1.2.3",
"vue-loader": "^14.2.4",
"vue-style-loader": "^3.1.2",
"vue-template-compiler": "^2.6.8",
"webpack": "^4.29.6",
"webpack-bundle-analyzer": "^3.1.0",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1",
"webpack-merge": "^4.2.1",
"webpack-uglify-parallel": "^0.1.4"
}
}
升级完后打包速度明显提升,而且包的体积明显也变小了
该文档只是一个参考,因为只有你升级的时候你才了解到更多其它知识点
结束
之后提交git,其它同事遇到import报错,解决方法加入"babel-plugin-dynamic-import-webpack": “^1.1.0” 插件,这样就不用删除依赖包,直接下载覆盖就可以了