最近越看项目越不顺眼,流程和打包实在太老了,用node8以上的版本无法识别gulp,打包后的代码也没有压缩,页面也没有source指引,html手动更换链接等等。。。
so,下决心把这块儿升级一下
原本的node版本也太低,这次一起升,下载node.js。。。
有兴趣的可以在github下载试试,一起交流。
因为用惯了之前的gulp流程化,加上项目的多入口多出口,这次决定还是继续gulp+webpack。查了一下现在已经不是gulp-webpack了,而是webpack-stream,本次gulp只作为任务流,主要还是webpack,配置来了
先看最终效果图

文件结构
多个项目整合在一起,要分开单独打包,感觉还是用gulp来执行webpack更合适一点,个人理解而已。
package.json
以下就是所有的依赖了,这里没什么好说的,要什么就install什么
下面的 browserslist 是为css自动添加前缀用的
-2020.5.27 更新-
-2020.6.8 更新-
-2020.6.18 更新-
{
"name": "webpack-stream",
"version": "1.0.0",
"description": "",
"private": true,
"keywords": [],
"author": "JackSY",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/plugin-transform-object-assign": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.9.6",
"@babel/runtime": "^7.4.4",
"@babel/runtime-corejs2": "^7.4.4",
"babel-core": "^6.26.3",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-dynamic-import-webpack": "^1.1.0",
"babel-plugin-lodash": "^3.3.4",
"clean-css": "^4.2.3",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.5.3",
"cssnano": "^4.1.10",
"eslint": "^7.1.0",
"eslint-config-standard": "^14.1.1",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-flowtype": "^5.1.3",
"eslint-plugin-html": "^6.0.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.0.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"gulp": "^4.0.2",
"html-loader": "^1.1.0",
"html-webpack-plugin": "^4.3.0",
"image-webpack-loader": "^6.0.0",
"less-loader": "^6.1.0",
"lodash": "^4.17.15",
"lodash-webpack-plugin": "^0.11.5",
"mini-css-extract-plugin": "^0.9.0",
"node-notifier": "^7.0.1",
"node-sass": "^4.14.1",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"uglify-js": "^3.9.3",
"url-loader": "^4.1.0",
"vue": "^2.6.11",
"vue-loader": "^15.9.2",
"vue-router": "^3.3.2",
"vue-server-renderer": "^2.6.11",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"vuex": "^3.4.0",
"webpack": "^4.43.0",
"webpack-dev-server": "^3.11.0",
"webpack-stream": "^5.2.1"
},
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
}
webpack.dev.conf.js
开发模式已经添加了devServer,在gulp中动态添加,可以及时响应更改,而无需一遍又一遍地打包
-2020.5.27 更新-
-2020.6.8 更新-
-2020.6.18 更新-
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
const LodashWebpackPlugin = require("lodash-webpack-plugin");
const notifier = require("node-notifier");
const InsertHtmlPlugin = require("./public/insert-html-code-plugin");
const statistics = require("./public/statisticsTemplate");
module.exports = {
mode: "development",
output: {
filename: "build.js",
globalObject: "this"
},
stats: {
children: false // Tells stats whether to add information about the children.
},
plugins: [
new HtmlWebpackPlugin({
hash: true,
meta: false
}),
new VueLoaderPlugin(),
new MiniCssExtractPlugin(),
new LodashWebpackPlugin(),
new FriendlyErrorsPlugin({
// 运行错误
onErrors: function (severity, errors) {
// 可以收听插件转换和优先级的错误
// 严重性可以是"错误"或"警告"
if (severity !== "error") {
return;
}
const error = errors[0];
notifier.notify({
title: "Webpack error",
message: severity + ": " + error.name,
subtitle: error.file || ""
});
},
// 是否每次编译之间清除控制台
// 默认为true
clearConsole: true
}),
new InsertHtmlPlugin({
minimize: false,
scriptCode: statistics,
scriptPaths: [
'https://cdn.bootcdn.net/ajax/libs/jquery/1.8.2/jquery.min.js'
]
}),
new webpack.HotModuleReplacementPlugin() // webpack内置的热更新插件
],
externals: {
jquery: "jQuery"
},
resolve: {
extensions: [".vue", ".js", ".scss", ".sass", ".less", ".css", ".json"],
alias: {
vue$: "vue/dist/vue.esm.js"
}
},
devtool: "inline-source-map",
module: {
rules: [
{
test: /\.css$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
sourceMap: true,
importLoaders: 1
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
],
sourceMap: true
}
}
]
},
{
test: /\.(sass|scss)$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
sourceMap: true,
importLoaders: 2
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
],
sourceMap: true
}
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
},
{
test: /\.less$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
sourceMap: true,
importLoaders: 2
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
],
sourceMap: true
}
},
{
loader: "less-loader",
options: {
sourceMap: true
}
}
]
},
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.js$/,
use: [{
loader: "eslint-loader",
options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine
formatter: require("eslint-friendly-formatter") // 指定错误报告的格式规范
}
}],
enforce: "pre", // 编译前检查
exclude: [/node_modules/] // 不检测的文件
},
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: file => (
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
)
},
{
test: /\.(png|jp?g|gif|svg|ico)$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192, // 小于8192字节的图片打包成base 64图片
name: "images/[name].[hash:8].[ext]",
publicPath: "./",
esModule: false
}
}
]
},
{
// 文件依赖配置项——字体图标
test: /\.(woff|woff2|svg|eot|ttf)$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "fonts/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
// 文件依赖配置项——音频
test: /\.(wav|mp3|ogg)?$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "audios/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
// 文件依赖配置项——视频
test: /\.(ogg|mpeg4|webm|mp4)?$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "videos/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
test: /\.(html)?$/,
use: {
loader: "html-loader",
options: {
minimize: false
}
}
}
]
}
}
gulpfile.js
-2020.5.27 更新-
-2020.6.8 更新-
-2020.6.18 更新-
为了拆分gulp任务,废了我不少功夫
结构大概是这样
index.js这样
const activity2020 = require('./project');
const allGulpTasks = Object.assign({},
activity2020
);
Object.keys(allGulpTasks).forEach(key => {
exports[key] = allGulpTasks[key];
});
接下来是其他子任务project.js
const { src, dest } = require('gulp');
const webpackStream = require('webpack-stream');
const { webpackEntryOption, openWebpackDevServe } = require('../public/webpackOptionChange');
const gulpTasks = require('../gulpfile.json/project.json');
Object.keys(gulpTasks).forEach(key => {
const path = gulpTasks[key][0];
const outPath = gulpTasks[key][1] || 'dist';
const main = gulpTasks[key][2] || 'main';
const template = gulpTasks[key][3] || 'index.html';
exports['activity2020_' + key + '_dev'] = async function () {
openWebpackDevServe(path, outPath, main, template);
};
exports['activity2020_' + key + '_prod'] = function () {
return src('**/*.js', { allowEmpty: true })
.pipe(webpackStream(webpackEntryOption(path, outPath, true, main, template)))
.pipe(dest(path + outPath + '/'));
};
});
这里在dev任务时gulp是异步任务,为的是开启webpack的devServer
当然,一些方法我封装了一下,比如 openWebpackDevServe 和 webpackEntryOption ,具体可以去我的github上交流一下
注意
有的电脑可能需要 powershell 开启权限,管理员执行 set-executionpolicy RemoteSigned
这个配置里:
- 本项目以 webpack 打包为主,gulp 只提供任务流
- node.js ^v12.14.3
- gulp -g ^v4.0.0 (全局升级可能会失败,用 --force 覆盖即可)
- gulp-cli -g ^v2.2.1
- 若 npm 安装失败请使用 cnpm 或 yarn
webpack.prod.conf.js
-2020.5.27 更新-
-2020.6.8 更新-
-2020.6.18 更新-
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const LodashWebpackPlugin = require("lodash-webpack-plugin");
const UglifyjsWebapckPlugin = require('uglifyjs-webpack-plugin');
const InsertHtmlPlugin = require("./public/insert-html-code-plugin");
const statistics = require("./public/statisticsTemplate");
module.exports = {
mode: "production",
output: {
filename: "js/[name].build.js",
globalObject: "this"
},
stats: {
children: false // Tells stats whether to add information about the children.
},
performance: {
maxEntrypointSize: 500000, // 入口文件最大限制提示
maxAssetSize: 200000 // 输出文件最大限制提示
},
optimization: {
minimizer: [
new UglifyjsWebapckPlugin({
uglifyOptions: {
compress: {
// 设置打包时将console语句不打包进去
drop_console: true
}
}
})
],
splitChunks: {
cacheGroups: {
// 处理入口chunk,同步的
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
name: 'vendors'
},
// 处理异步chunk
'async-vendors': {
test: /[\\/]node_modules[\\/]/,
minChunks: 2,
chunks: 'async',
name: 'async-vendors'
},
// 处理css
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
},
// 为每个入口提取出webpack runtime模块
runtimeChunk: { name: 'manifest' }
},
plugins: [
new HtmlWebpackPlugin({
minify: { // 压缩HTML文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true, // 压缩内联css(使用clean-css进行的压缩)
minifyJS: true, // 压缩html里的js(使用uglify-js进行的压缩)
removeAttributeQuotes: true // 移除属性的引号
},
hash: true,
meta: false
}),
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
}),
new LodashWebpackPlugin(),
new InsertHtmlPlugin({
minimize: true,
scriptCode: statistics,
scriptPaths: [
'https://cdn.bootcdn.net/ajax/libs/jquery/1.8.2/jquery.min.js'
]
}),
new CleanWebpackPlugin()
],
externals: {
jquery: "jQuery"
},
resolve: {
extensions: [".vue", ".js", ".scss", ".sass", ".less", ".css", ".json"],
alias: {
vue$: "vue/dist/vue.min.js"
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
importLoaders: 1
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
]
}
}
]
},
{
test: /\.(sass|scss)$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
importLoaders: 2
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
]
}
},
"sass-loader"
]
},
{
test: /\.less$/,
use: [
"vue-style-loader",
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
importLoaders: 2
}
},
{
loader: "postcss-loader",
options: {
plugins: (loader) => [
require("postcss-import")({
root: loader.resourcePath
}),
require("postcss-preset-env")(),
require("cssnano")()
]
}
},
"less-loader"
]
},
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: file => (
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
)
},
{
test: /\.(png|jp?g|gif|svg|ico)$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192, // 小于8192字节的图片打包成base 64图片
name: "images/[name].[hash:8].[ext]",
publicPath: "../",
esModule: false
}
},
{
loader: "image-webpack-loader"
}
]
},
{
// 文件依赖配置项——字体图标
test: /\.(woff|woff2|svg|eot|ttf)$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "fonts/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
// 文件依赖配置项——音频
test: /\.(wav|mp3|ogg)?$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "audios/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
// 文件依赖配置项——视频
test: /\.(ogg|mpeg4|webm|mp4)?$/,
use: [{
loader: "file-loader",
options: {
limit: 8192,
name: "videos/[name].[hash:8].[ext]",
publicPath: "./"
}
}]
},
{
test: /\.(html)?$/,
use: {
loader: "html-loader",
options: {
minimize: false
}
}
}
]
}
}
暂时就这些吧,后续还要完善不少,但是已经可以出货啦~~