webpack 发布版配置 & 配置重构
优质
小牛编辑
133浏览
2023-12-01
前言
弄完了前后端分离,我们自然想打包发布项目了。
不多说,就让我们来看看吧。
开发
直接上代码:
const webpack = require('webpack') const path = require('path') const ExtractTextPlugin = require('extract-text-webpack-plugin') const webpackConfigBase = require('./webpack.config.js') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const exec = require('child_process').execSync const pkg = require('./package.json') // 为了抽离出两份CSS,创建两份ExtractTextPlugin // base作为基础的css,基本不变,所以,可以抽离出来充分利用浏览器缓存 // app作为迭代的css,会经常改变 const extractBaseCSS = new ExtractTextPlugin({filename:'static/css/base.[chunkhash:8].css', allChunks: true}) const extractAppCSS = new ExtractTextPlugin({filename:'static/css/app.[chunkhash:8].css', allChunks: true}) // 减少路径书写 function resolve(dir) { return path.join(__dirname, dir) } // 网站图标配置 const favicon = resolve('favicon.ico') // 网站版本号设置 let appVersion = '' try { appVersion = exec('git rev-parse --short HEAD').toString().replace(/\n/, '') } catch (e) { console.warn('Getting revision FAILED. Maybe this is not a git project.') } const config = Object.assign(webpackConfigBase, { // You should configure your server to disallow access to the Source Map file for normal users! devtool: 'source-map', entry: { app: resolve('app/index.js'), // 将第三方依赖(node_modules)的库打包,从而充分利用浏览器缓存 vendor: Object.keys(pkg.dependencies) }, output: { path: resolve('dist'), // publicPath: 'https://cdn.self.com' publicPath: resolve('dist/'), filename: 'static/js/[name].[chunkhash:8].js' }, module: { rules: [ { test: /\.js$/, include: [resolve('app')], loader: 'babel-loader' }, { test: /\.vue$/, exclude: /node_modules/, loader: 'vue-loader', options: { extractCSS: true, loaders: { scss: extractAppCSS.extract({ fallback: 'vue-style-loader', use: [ { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }) } } }, { test: /\.(css|scss)$/, use: extractBaseCSS.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }) }, { test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/, loader: 'url-loader', options: { limit: 8192, name: 'static/img/[name].[hash:8].[ext]' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 8192, name: 'static/font/[name].[hash:8].[ext]' } } ] }, plugins: [ // Scope hosting new webpack.optimize.ModuleConcatenationPlugin(), // 删除build文件夹 new CleanWebpackPlugin( resolve('dist') ), // 抽离出css extractBaseCSS, extractAppCSS, // 提供公共代码vendor new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'static/js/[name].[chunkhash:8].js' }), // html 模板插件 new HtmlWebpackPlugin({ appVersion, favicon, filename: 'index.html', template: resolve('app/index.html'), minify: { removeComments: true, collapseWhitespace: false } }), // 定义全局常量 new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), // 可视化分析 new BundleAnalyzerPlugin(), // 加署名 new webpack.BannerPlugin('Copyright by 子咻 https://github.com/CodeLittlePrince/blog'), ] }) module.exports = config
代码几乎全都有注释,有不懂的可以在评论去留言。
思考
虽然代码写好了,但是我们不禁发出一声“卧槽”,好多和webpack.config.js一样的代码啊,要是改了一样的代码部分,我还得同时改两份,而且,这么多的冗余代码对于一个优秀的程序员来讲,是不可容忍的。
那我们改怎么呢?
重构webpack的配置代码
一、创建一个基础的webpack配置文件
我们就叫webapck.config.base.js
吧:
const path = require('path') // 为了抽离出两份CSS,创建两份ExtractTextPlugin // base作为基础的css,基本不变,所以,可以抽离出来充分利用浏览器缓存 // app作为迭代的css,会经常改变 const isProduction = process.env.NODE_ENV === 'production' const ExtractTextPlugin = require('extract-text-webpack-plugin') const extractBaseCSS = new ExtractTextPlugin( { filename:'static/css/base.[chunkhash:8].css', allChunks: true, disable: !isProduction // 开发环境下不抽离css } ) const extractAppCSS = new ExtractTextPlugin( { filename:'static/css/app.[chunkhash:8].css', allChunks: true, disable: !isProduction // 开发环境下不抽离css } ) // 减少路径书写 function resolve(dir) { return path.join(__dirname, dir) } // 网站图标配置 const favicon = resolve('favicon.ico') // __dirname: 总是返回被执行的 js 所在文件夹的绝对路径 // __filename: 总是返回被执行的 js 的绝对路径 // process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径 const config = { resolve: { // 扩展名,比如import 'app.vue',扩展后只需要写成import 'app'就可以了 extensions: ['.js', '.vue', '.scss', '.css'], // 取路径别名,方便在业务代码中import alias: { api: resolve('app/api/'), common: resolve('app/common/'), views: resolve('app/views/'), components: resolve('app/components/'), componentsBase: resolve('app/componentsBase/'), directives: resolve('app/directives/'), filters: resolve('app/filters/'), mixins: resolve('app/mixins/') } }, // loaders处理 module: { rules: [ { test: /\.js$/, include: [resolve('app')], loader: [ 'babel-loader', 'eslint-loader' ] }, { test: /\.vue$/, exclude: /node_modules/, loader: 'vue-loader', options: { extractCSS: true, loaders: { scss: extractAppCSS.extract({ fallback: 'vue-style-loader', use: [ { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }) } } }, { test: /\.(css|scss)$/, use: extractBaseCSS.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }) }, { test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/, loader: 'url-loader', options: { limit: 8192, name: isProduction ? 'static/img/[name].[hash:8].[ext]' : 'static/img/[name].[ext]' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 8192, name: isProduction ? 'static/font/[name].[hash:8].[ext]' : 'static/font/[name].[ext]' } } ] } } module.exports = { config, favicon, resolve, extractBaseCSS, extractAppCSS }
二、重构webpack开发环境配置
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const webpackConfigBase = require('./webpack.config.base.js') const config = Object.assign(webpackConfigBase.config, { // sourcemap 模式 devtool: 'cheap-module-eval-source-map', // 入口 entry: { app: webpackConfigBase.resolve('app/index.js') }, // 输出 output: { path: webpackConfigBase.resolve('dev'), filename: 'index.bundle.js' }, plugins: [ // html 模板插件 new HtmlWebpackPlugin({ favicon: webpackConfigBase.favicon, filename: 'index.html', template: webpackConfigBase.resolve('app/index.html') }), // 抽离出css,开发环境其实不抽离,但是为了配合extract-text-webpack-plugin插件,需要做个样子 webpackConfigBase.extractAppCSS, webpackConfigBase.extractBaseCSS, // 热替换插件 new webpack.HotModuleReplacementPlugin(), // 更友好地输出错误信息 new FriendlyErrorsPlugin() ], devServer: { proxy: { // 凡是 `/api` 开头的 http 请求,都会被代理到 localhost:7777 上,由 koa 提供 mock 数据。 // koa 代码在 ./mock 目录中,启动命令为 npm run mock。 '/api': { target: 'http://localhost:7777', // 如果说联调了,将地址换成后端环境的地址就哦了 secure: false } }, host: '0.0.0.0', port: '9999', disableHostCheck: true, // 为了手机可以访问 contentBase: webpackConfigBase.resolve('dev'), // 本地服务器所加载的页面所在的目录 // historyApiFallback: true, // 为了SPA应用服务 inline: true, //实时刷新 hot: true // 使用热加载插件 HotModuleReplacementPlugin } }) module.exports = config
三、重构webpack开发环境配置
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const exec = require('child_process').execSync const webpackConfigBase = require('./webpack.config.base.js') const pkg = require('./package.json') // 网站版本号设置 let appVersion = '' try { appVersion = exec('git rev-parse --short HEAD').toString().replace(/\n/, '') } catch (e) { console.warn('Getting revision FAILED. Maybe this is not a git project.') } const config = Object.assign(webpackConfigBase.config, { // You should configure your server to disallow access to the Source Map file for normal users! devtool: 'source-map', entry: { app: webpackConfigBase.resolve('app/index.js'), // 将第三方依赖(node_modules)的库打包,从而充分利用浏览器缓存 vendor: Object.keys(pkg.dependencies) }, output: { path: webpackConfigBase.resolve('dist'), // publicPath: 'https://cdn.self.com' publicPath: webpackConfigBase.resolve('dist/'), filename: 'static/js/[name].[chunkhash:8].js' }, plugins: [ // Scope hosting new webpack.optimize.ModuleConcatenationPlugin(), // 删除build文件夹 new CleanWebpackPlugin( webpackConfigBase.resolve('dist') ), // 抽离出css webpackConfigBase.extractAppCSS, webpackConfigBase.extractBaseCSS, // 提取公共代码vendor new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'static/js/[name].[chunkhash:8].js' }), // html 模板插件 new HtmlWebpackPlugin({ appVersion, favicon: webpackConfigBase.favicon, filename: 'index.html', template: webpackConfigBase.resolve('app/index.html'), minify: { removeComments: true, collapseWhitespace: false } }), // 定义全局常量 new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), // 可视化分析 new BundleAnalyzerPlugin(), // 加署名 new webpack.BannerPlugin('Copyright by 子咻 https://github.com/CodeLittlePrince/blog'), ] }) module.exports = config
代码瞬间变得清晰、精简、高大上有没有?!(^-^)V
看一下打包处理后代码情况(兼容IE10及以上):
总结
这一篇我们编写了开发环境用的webpack配置文件,然后发现代码的冗余从而重构了开发和发布环境的webpack配置。
之后,我们还需要能够自动测试我们写的业务代码,避免人工手动各种戳页面(虽然大部分公司都是这么干的,即使是大公司会腾出时间和人手写测试用例的部门也不多),不过架构还是要做的。