从零开始做Vue前端架构(5)
前言
弄完了前后端分離,我們自然想打包發(fā)布項(xiàng)目了。
不多說,就讓我們來看看吧。
開發(fā)
直接上代碼:
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,創(chuàng)建兩份ExtractTextPlugin // base作為基礎(chǔ)的css,基本不變,所以,可以抽離出來充分利用瀏覽器緩存 // app作為迭代的css,會(huì)經(jīng)常改變 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) } // 網(wǎng)站圖標(biāo)配置 const favicon = resolve('favicon.ico') // 網(wǎng)站版本號(hào)設(shè)置 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)的庫(kù)打包,從而充分利用瀏覽器緩存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 hostingnew webpack.optimize.ModuleConcatenationPlugin(),// 刪除build文件夾new CleanWebpackPlugin(resolve('dist')),// 抽離出cssextractBaseCSS,extractAppCSS,// 提供公共代碼vendornew 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代碼幾乎全都有注釋,有不懂的可以在評(píng)論去留言。
思考
雖然代碼寫好了,但是我們不禁發(fā)出一聲“臥槽”,好多和webpack.config.js一樣的代碼啊,要是改了一樣的代碼部分,我還得同時(shí)改兩份,而且,這么多的冗余代碼對(duì)于一個(gè)優(yōu)秀的程序員來講,是不可容忍的。
那我們改怎么呢?
重構(gòu)webpack的配置代碼
一、創(chuàng)建一個(gè)基礎(chǔ)的webpack配置文件
我們就叫webapck.config.base.js吧:
const path = require('path')// 為了抽離出兩份CSS,創(chuàng)建兩份ExtractTextPlugin // base作為基礎(chǔ)的css,基本不變,所以,可以抽離出來充分利用瀏覽器緩存 // app作為迭代的css,會(huì)經(jīng)常改變 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 // 開發(fā)環(huán)境下不抽離css}) const extractAppCSS= new ExtractTextPlugin({filename:'static/css/app.[chunkhash:8].css',allChunks: true,disable: !isProduction // 開發(fā)環(huán)境下不抽離css})// 減少路徑書寫 function resolve(dir) {return path.join(__dirname, dir) }// 網(wǎng)站圖標(biāo)配置 const favicon = resolve('favicon.ico')// __dirname: 總是返回被執(zhí)行的 js 所在文件夾的絕對(duì)路徑 // __filename: 總是返回被執(zhí)行的 js 的絕對(duì)路徑 // process.cwd(): 總是返回運(yùn)行 node 命令時(shí)所在的文件夾的絕對(duì)路徑 const config = {resolve: {// 擴(kuò)展名,比如import 'app.vue',擴(kuò)展后只需要寫成import 'app'就可以了extensions: ['.js', '.vue', '.scss', '.css'],// 取路徑別名,方便在業(yè)務(wù)代碼中importalias: {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 }二、重構(gòu)webpack開發(fā)環(huán)境配置
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: ['babel-polyfill', // 這里是配合babel-present-env導(dǎo)入的動(dòng)態(tài)babel-polyfill,因此npm需dev依賴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,開發(fā)環(huán)境其實(shí)不抽離,但是為了配合extract-text-webpack-plugin插件,需要做個(gè)樣子webpackConfigBase.extractAppCSS,webpackConfigBase.extractBaseCSS,// 熱替換插件new webpack.HotModuleReplacementPlugin(),// 更友好地輸出錯(cuò)誤信息new FriendlyErrorsPlugin()],devServer: {proxy: {// 凡是 `/api` 開頭的 http 請(qǐng)求,都會(huì)被代理到 localhost:7777 上,由 koa 提供 mock 數(shù)據(jù)。// koa 代碼在 ./mock 目錄中,啟動(dòng)命令為 npm run mock。'/api': {target: 'http://localhost:7777', // 如果說聯(lián)調(diào)了,將地址換成后端環(huán)境的地址就哦了secure: false}},host: '0.0.0.0',port: '9999',disableHostCheck: true, // 為了手機(jī)可以訪問contentBase: webpackConfigBase.resolve('dev'), // 本地服務(wù)器所加載的頁(yè)面所在的目錄// historyApiFallback: true, // 為了SPA應(yīng)用服務(wù)inline: true, //實(shí)時(shí)刷新hot: true // 使用熱加載插件 HotModuleReplacementPlugin} })module.exports = config三、重構(gòu)webpack開發(fā)環(huán)境配置
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')// 網(wǎng)站版本號(hào)設(shè)置 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)的庫(kù)打包,從而充分利用瀏覽器緩存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 hostingnew webpack.optimize.ModuleConcatenationPlugin(),// 刪除build文件夾new CleanWebpackPlugin(webpackConfigBase.resolve('dist')),// 抽離出csswebpackConfigBase.extractAppCSS,webpackConfigBase.extractBaseCSS,// 提取公共代碼vendornew 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代碼瞬間變得清晰、精簡(jiǎn)、高大上有沒有?!(^-^)V
看一下打包處理后代碼情況(兼容IE10及以上):
總結(jié)
這一篇我們編寫了開發(fā)環(huán)境用的webpack配置文件,然后發(fā)現(xiàn)代碼的冗余從而重構(gòu)了開發(fā)和發(fā)布環(huán)境的webpack配置。
之后,我們還需要能夠自動(dòng)測(cè)試我們寫的業(yè)務(wù)代碼,避免人工手動(dòng)各種戳頁(yè)面(雖然大部分公司都是這么干的,即使是大公司會(huì)騰出時(shí)間和人手寫測(cè)試用例的部門也不多),不過架構(gòu)還是要做的。
下篇我們會(huì)來完成測(cè)試的流程 - 從零開始做Vue前端架構(gòu)(6)
項(xiàng)目完整代碼
Vue前端架構(gòu)-by 子咻
總結(jié)
以上是生活随笔為你收集整理的从零开始做Vue前端架构(5)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小班教案《小蝌蚪变青蛙》反思
- 下一篇: html5倒计时秒杀怎么做,vue 设