0.簡介
現(xiàn)在的開發(fā)都是講究的是前后端的分離,但是有時候一些傳統(tǒng)的老項目用的技術(shù)十分的不順手,接下來講一講我是如何把Vue框架集成到老項目中去的
1.項目環(huán)境配置:
- 后端:SSH
- 前端:npm,webpack vue全家桶
首先來一個整個項目的截圖

想必大家對這個結(jié)構(gòu)已經(jīng)十分的了解了,這里簡單的介紹下這個目錄的結(jié)構(gòu):
- .settings,.classpath,.project都是Eclipse里面的配置問你件
- adf , res ,core,metedata 是關(guān)聯(lián)的其他項目的源碼
- metedata是代碼生成器
- src 是以SSH為框架的后臺代碼
- WebRoot是前端代碼
吐:下,這個就是老舊的JavaWeb項目,adf ,core,等都是引入的外部的的項目,這些東西是特喵的本可以打成jar包的!
2.理清楚思路:
來來來,先整理一下這個思路
在整個項目中,我們最關(guān)心的問題就是這個開發(fā)的流程怎么走,首先,明確一下開發(fā)的流程:我這里采取從前到后的方式:即由一串url說起:如 http://127.0.0.1:8080/satellite_image//index/index.jsp ,這個是指向的是index.jsp,即index.jsp是整個頁面的入口,index.jsp主要的功用是分為兩個部分,1是通過域?qū)ο笙騤sp頁面里面進行傳值,而作為整個頁面的容器,即承載html,那么此時的html和部署在ngix上的純前端頁面就沒有什么區(qū)別了,我們只需要把npm相關(guān)配置文件丟在WebRoot的根目錄下即可,說白了,就是整個把整個純前端應(yīng)用限制在了WebRoot這么個文件夾下
前端需要分離的部分只能放到WebRoot下進行開發(fā),開發(fā)的流程與傳統(tǒng)的開發(fā)流程也沒有實質(zhì)性的區(qū)別:即是這樣的,我們所有的jsp都要引入一個關(guān)鍵的*.js文件,作為整個文件的操作入口文件,然后再根據(jù)這個文件的模塊,利用js模塊化的思想和Vue模塊化的思想將整個頁面進行分隔,
先上個圖:

build文件夾是存放編譯之后的js文件,css是指樣式文件,images是放置圖片的,js是老舊項目原始的文件,懶得管了,js-es6才是我們的重頭戲,重點編寫的文件,其中api是用來搞數(shù)據(jù)操作交互的和其他操作,里面封裝的Data.js就是重點通過ajax與服務(wù)器端的數(shù)據(jù)進行交互的,然后還有一些地圖處理的api啊之類的,components是放置的Vue組件了,啊這些就是前端需要搞的知識點兒了,啊這里的就不詳細介紹了,,我們重點關(guān)注的是流程的走通對不對,
3.正文:
其實吧,最重點的還是特喵的npm那一堆配置文件
,在實際開發(fā)的時候,在npm下就跑到Webroot目錄下,跑npm run dev 即可以進行熱重載的開發(fā),用npm run build-product進行最終文件的壓縮,混淆之類的.
下面針對這些文件進行簡單的介紹一下,先從npm的命令說起:
看一下package.json這么文件,他是整個前端項目的核心組件和文件(復(fù)制過去記得把注釋去掉):
{
"name": "mzb", //項目的名稱
"version": "0.1.0",//版本號
"private": true, //私有項目?
"dependencies": { //項目中實打?qū)嵭枰玫降牡谌讲寮?框架之類的,這些東西說白了,是項目的文件,或者是入口js文件需要用的,需要用的,會編譯進去代碼的
"babel-runtime": "^6.26.0",//運行時,墊片庫,貌似沒有用到,它的作用是,配合通過babel-plugin-transform-runtime自動重寫你的promise等原型對象。但是它不會模擬內(nèi)置對象原型上的方法,比如Array-prototype.find,
"es5-shim": "^4.5.10",//解決ie8以下的問題,這里投機了,由于入口文件都是import 而這個庫只能用require,所以我編譯了兩次(*/ω\*),在編譯完第一次之后,自動注入require('shim/eshim')然后再編譯一次
"sweetalert2": "^7.8.4",//甜美的彈出框
"vue": "^2.5.13",//
"vuex": "^3.0.1" //
},
"scripts": { // 輸入 npm run 腳本時,所能執(zhí)行的命令結(jié)合,在執(zhí)行腳本的時候是可以指定一些參數(shù)的,相當于cmd中進行執(zhí)行命令,多個命令之間用 && 進行分隔
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",//執(zhí)行webpack的命令
"server": "webpack-dev-server --open",
"dev": "webpack-dev-server --devtool eval-source-map --progress --colors --hot --inline ", //執(zhí)行熱更新開發(fā)者模式
"build": "webpack --config ./webpack.config.js --progress",
"build-product": "Set NODE_ENV=production && webpack --config ./webpack.production.config.js --progress && npm run build-product-compatible-ie ",
"build-product-compatible-ie": "webpack --config ./[webpack.compatible-ie.config.js](http://webpack.compatible-ie.config.js/) --progress"
},
"devDependencies": {//這些都是插件,說白了,是編譯器需要用到的,是webpack.js需要用到的
"babel-cli": "^6.26.0",//沒用到
"babel-core": "^6.26.0",//貌似和babel-profill有關(guān),待研究
"babel-loader": "^7.1.2",//必須要有的轉(zhuǎn)碼器啦
"babel-plugin-transform-runtime": "^6.23.0",//配合上述的進行工作滴
"babel-polyfill": "^6.26.0", //用于解決低版本瀏覽器api不全的問題,加入之后可以為所欲為的使用高級的api,在文件的頭部統(tǒng)一使用 import 'babel-polyfill'
"babel-preset-es2015": "^6.24.1",//轉(zhuǎn)成es5的語法 配合bable-loader
"babel-preset-es2015-loose": "^8.0.0",//es5語法的loose模式 配合bable-loader
"babel-preset-latest": "^6.24.1",//當前階段最新的語法 配合bable-loader
"babel-preset-stage-3": "^6.24.1",//當前的語法 配合bable-loader
"babel-register": "^6.26.0",//待研究,貌似沒用到
"babelify": "^7.3.0",//待研究,貌似沒用到
"compression-webpack-plugin": "^1.1.3",//gzip壓縮插件
"css-loader": "^0.28.7",//編譯css文件的
"es3ify-loader": "^0.2.0",//解決ie8-,主要是將訪問方式統(tǒng)一抓變成object['prop']的形式
"eslint-loader": "^1.9.0",//代碼檢查工具
"style-loader": "^0.18.2",//編譯樣式文件和css-laoder配合,待研究
"vue-loader": "^13.3.0",//編譯*.vue組件的
"vue-style-loader": "^3.0.1",//配合vue-laoder
"vue-template-compiler": "^2.5.2",//配合vue-laoder
"webpack": "^3.6.0",//打包工具啦
"webpack-dev-server": "^1.14.1"http://熱更新用到的服務(wù)器啦
}
}
詳解開發(fā)模式下的webpack.config.js,即 執(zhí)行npm run dev ,或者npm run build都發(fā)生了什么事情
看文件.babelre
{
"presets": [["es2015",{"loose":false}],["stage-3"]], //指定轉(zhuǎn)碼時,代碼所需要識別的高級語法有哪些
"plugins": [ //轉(zhuǎn)碼插件
"transform-runtime",//待研究
"transform-es2015-classes"]//轉(zhuǎn)es6里面的class語法,(其實吧,我覺得只用第一個就可以了,待研究)
}
看文件webpack.js
//這里用到的就是nodejs里面的內(nèi)容和語法了,遵循CommonJS的規(guī)范
const path = require('path')
const webpack = require('webpack'); //使用webpack自帶的插件
module.exports = {
devtool: 'eval-source-map', //調(diào)試時使用的source map 方便快速查找文件
entry: [
"webpack-dev-server/client?[http://localhost:3000](http://localhost:3000/)",
"webpack/hot/only-dev-server",
__dirname + '/index/js-es6/index.js'
], //入口文件 【注意:__dirname 是node.js中的一個全局變量,它指向當前執(zhí)行腳本所在的目錄,故多用于構(gòu)建工具的腳本中】
output: {
path: __dirname + '/index/build', //輸出的路徑
filename: 'index.js' //輸出的文件名
// publicPath: '[http://localhost:3000/](http://localhost:3000/)'
},
module: { //多個依賴模塊,如babel,react,laod等模塊
rules: [ //定義loader的規(guī)則,表示如何處理指定格式的拓展文件
{
test: /(\.js|\.jsx)$/,//解析的后綴
use: {
loader: 'babel-loader'//解碼器
},
exclude: /node_modules/ //自定哪些文件不搞
},
{
test: /(\.css)$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: { modules: true }
}
]
},
{
test: /(\.vue)$/,
use: [
{ loader: 'vue-loader' }
]
}
]
},
resolve: { alias: { 'vue': 'vue/dist/vue.js' } },//這個是使用完整版,而不只是運行時的vue
plugins: [new webpack.HotModuleReplacementPlugin()], //熱加載插件
devServer: {//這個就6了,這個是保證能在tomcat下,即同一個域下,實現(xiàn)熱加載,在執(zhí)行代理時,為保證符合代理的預(yù)期效果,可以到tomcat下把需要代理的文件刪掉,目前也就只能代理的是這么個入口文件
port: 3000, //代理服務(wù)器的端口
proxy: {
'**': {
target: '[http://localhost:8080](http://localhost:8080/)', //代理的 服務(wù)器地址,
secure: false,
prependPath: false
}
},
publicPath: '[http://localhost:3000/satellite_image/index/build/](http://localhost:3000/satellite_image/index/build/)',//代理的入口文件的地址,詳細的路徑呀
historyApiFallback: true
}
}
看文件webpack-inject-js-plugin.js
/*
在編譯之后的腳本之后,往其頭部添加一些引入的庫
用途:多用于連續(xù)打包,在下一次打包之前,引入必要的js庫
Author zgy
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const ModuleFilenameHelpers = require("./node_modules/webpack/lib/ModuleFilenameHelpers");
const injectScript = (str) => {
if (!str.includes("\n")) return `${str} \n`;
return `${str.split("\n").join("\n ")}\n`;
};
class InjectScript {
constructor(options) {
if (arguments.length > 1)
throw new Error("腳本注入插件只能傳入一個參數(shù)");
if (typeof options.banner === "object") {
options.banner = options.banner.map(item => `require('${item}');`).join('\n');
}
console.log(options.banner);
if (typeof options === "string")
options = {
banner: `require('${options}')`
};
this.options = options || {};
this.banner = this.options.raw ? options.banner : injectScript(options.banner);
console.log(this.banner);
}
apply(compiler) {
const options = this.options;
const banner = this.banner;
console.log("apply-->"+banner);
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunk-assets", (chunks, callback) => {
chunks.forEach((chunk) => {
if (options.entryOnly && !chunk.isInitial()) return;
chunk.files
.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options))
.forEach((file) => {
let basename;
let query = "";
let filename = file;
const hash = compilation.hash;
const querySplit = filename.indexOf("?");
if (querySplit >= 0) {
query = filename.substr(querySplit);
filename = filename.substr(0, querySplit);
}
if (filename.indexOf("/") < 0) {
basename = filename;
} else {
basename = filename.substr(filename.lastIndexOf("/") + 1);
}
const comment = compilation.getPath(banner, {
hash,
chunk,
filename,
basename,
query,
});
console.log("apply-->comment-->"+comment);
console.log("apply-->file-->"+compilation.assets[file]);
return compilation.assets[file] = new ConcatSource(comment, "\n", compilation.assets[file]);
});
});
callback();
});
});
}
}
module.exports = InjectScript;
看文件npm run build-product 發(fā)生了什么,重頭戲,webpack.compatible-ie.config.js和webpack.production.config.js
看文件webpack.production.config.js
// webpack.production.config.js
const webpack = require('webpack');
const InjectScript = require('./webpack-inject-js-plugin.js');
module.exports = {
entry: [
__dirname + '/index/js-es6/index.js'
], //入口文件 【注意:__dirname 是node.js中的一個全局變量,它指向當前執(zhí)行腳本所在的目錄,故多用于構(gòu)建工具的腳本中】
output: {
path: __dirname + '/index/build', //輸出的路徑
filename: 'index.js' //輸出的文件名
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
// {
// test: /(\.js)$/,
// use: {
// loader: 'es3ify-loader',
// }
// },
{
test: /(\.vue)$/,
use: [
{ loader: 'vue-loader' }
]
}
]
},
plugins: [
// new webpack.optimize.OccurrenceOrderPlugin(),
/*
*目前需要解決的是ie中的Object.defineProperty,根據(jù)[http://www.aliued.com/?p=3240](http://www.aliued.com/?p=3240) ,暫用得解決方案是轉(zhuǎn)碼再轉(zhuǎn)碼
*即先webpack轉(zhuǎn)碼一次通用的.js文件,然后注入腳本require('es5-shim')等包,再進行另外一次webpack進行打包轉(zhuǎn)碼
*/
// new InjectScript({banner:['es5-shim','es5-shim/es5-sham']}),//處理es9以下的瀏覽器api問題,兼容ie9暫時不用
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({ //混淆插件,其實如果不去兼容ie的即使用 InjectScript注入插件,則可以直接和下面這個文件的內(nèi)容進行合并
compress: {
properties: false,
warnings: false
},
output: {
beautify: true,
quote_keys: true
},
mangle: {
screw_ie8: false
},
sourceMap: false
})
///配置在了Comatible-ie-config.js里面了
// new CompressionWebpackPlugin({ //gzip 壓縮
// asset: '[path].gz[query]',
// algorithm: 'gzip',
// test: new RegExp(
// '\\.(js|css)$' //壓縮 js 與 css
// ),
// threshold: 10240,
// minRatio: 0.8
// })
],
resolve: { alias: { 'vue': 'vue/dist/vue.min.js' } }
};
webpack.compatible-ie.config.js,其實如不要使用注入引入的插件,則不要這樣子搞,合并即可
//其實這個文件才是最后執(zhí)行的,哈!
// webpack.production.config.js
const webpack = require('webpack');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
module.exports = {
entry: [
__dirname + '/index/build/index.js'
], //入口文件 【注意:__dirname 是node.js中的一個全局變量,它指向當前執(zhí)行腳本所在的目錄,故多用于構(gòu)建工具的腳本中】
output: {
path: __dirname + '/index/build', //輸出的路徑
filename: 'index.js' //輸出的文件名
},
module: {
rules: [
{
test: /(\.js)$/,
use: {
loader: 'es3ify-loader',
}
}
]
},
plugins: [
new webpack.BannerPlugin('版權(quán)所有,翻版必究'),
// new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
properties: false,
warnings: false
},
output: {
beautify: false,
quote_keys: true
},
mangle: {
screw_ie8: false
},
sourceMap: false
}),
///配置在了Comatible-ie-config.js里面了
new CompressionWebpackPlugin({ //gzip 壓縮
asset: '[path].gz[query]',
algorithm: 'gzip',
filename(oldFileName){
// 符合公司框架的解析規(guī)則
return oldFileName.substring(0,oldFileName.indexOf("."))+".gzjs";
},
test: new RegExp(
'\\.(js|css)$' //壓縮 js 與 css
),
threshold: 10240,
minRatio: 0.8
})
]
};
綜上所述,以上的開發(fā)流程是這樣子的
- 首先新建傳統(tǒng)的javaeb項目,maven也可以啦
- 在WebRoot下部署node環(huán)境,即編寫上述的那些個包之類的,是實際的項目發(fā)布時,是不需要/node_moudle/這個文件夾的
- 建立入口html或者jsp
- 新建入口.js,記得抽出一個文件單獨處理與服務(wù)器的數(shù)據(jù)交互工作
- 在html中引入這個編譯之后的入口文件,入口引入的方式不同,熱加載的方式也不同,即webServer中,publicPath需要變一變
- 編寫webpack的配置文件,指定入口出口文件啊,配置webserver等
- 在實際部署時,一定要記得跑product.config,讓js文件該壓縮的壓縮,該混淆的混淆
其實上面的那些不看也可以!
第一步:把這些npm相關(guān)的文件丟到webroot目錄下
第二步:配置好jsp里面的入口*.js
第三步,修改webpack.config里面的入口出口參數(shù),
第四步:到WebRoot目錄下:執(zhí)行npm run dev,部署的時候執(zhí)行npm run build-product