一個(gè)標(biāo)準(zhǔn)的前端項(xiàng)目空民,必定始于yarn start,它將會(huì)經(jīng)歷babel編譯酪呻,webpack構(gòu)建殴瘦,server啟動(dòng)等流程,然后由瀏覽器加載頁(yè)面号杠。這是很Dev的開發(fā)方式,可生產(chǎn)環(huán)境我們卻往往不這么做丰歌。
1.何為前端姨蟋?
如果按照以前的看法违柏,前后端最本質(zhì)的區(qū)別當(dāng)然是運(yùn)行環(huán)境了佛吓,一個(gè)是瀏覽器中所寫即所見
的UI界面趁矾,另一個(gè)則是藏在背后
的服務(wù)叠聋。
在這種簡(jiǎn)單的區(qū)分下酝陈,前端往往會(huì)被定義為HTML/CSS/Javascript
戳粒。沒錯(cuò)悔叽,前端就是這些東西爽篷,這就是瀏覽器所需要呈現(xiàn)的绑咱;可也不能只有這些東西:前端有時(shí)也需要自己的后端server
來充當(dāng)API的中間層绰筛,也需要數(shù)據(jù)存儲(chǔ)(如localStorage, sessionStorage, indexedDB...)
,甚至JS也快支持多線程了描融。所以現(xiàn)今铝噩,絕對(duì)不能用語(yǔ)言運(yùn)行時(shí)(別給我說JS只在瀏覽器內(nèi)運(yùn)行)或者某項(xiàng)技術(shù)(ESX已經(jīng)在草案了)來去定義前端。
個(gè)人認(rèn)為窿克,最簡(jiǎn)單的區(qū)別方法就是用API來劃分:API的處理方如果在Node.js
端骏庸,那么這個(gè)JS項(xiàng)目絕對(duì)就是后端(Node端如果只作為中間層轉(zhuǎn)發(fā)則不算數(shù))毛甲;否則,如果只是作為API數(shù)據(jù)的請(qǐng)求方具被,并且有UI展現(xiàn)玻募,就算是前端了。
2.有無server?
為什么要浪費(fèi)篇幅去講前端的定義一姿,就是因?yàn)橹挥忻鞔_定義好前端之后七咧,才能解決一個(gè)問題:前端到底要不要server?
,因?yàn)樗艽蟪潭壬蠜Q定了如何部署啸蜜。
一般的前端項(xiàng)目都會(huì)有dist
產(chǎn)出坑雅,通常是由一個(gè)index.html
, 多個(gè)vendor.js
和其他類似圖片字體等資源構(gòu)成。
那么這個(gè)產(chǎn)出物是如何被render出來的呢衬横?
- Case1: 利用webpack server
以下為webpack配置啟用webpack server的片段:
var path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: 3000,
watchOptions: {
poll: true
}
}
};
這種case顯然只是render靜態(tài)的html和資源裹粤,因此webpack server只在開發(fā)使用,生產(chǎn)則根本不需要蜂林。
- Case2: 利用靜態(tài)服務(wù)器
其實(shí)和case1類似遥诉,如果只是render靜態(tài)html,那么利用類似Python
:
$ python -m SimpleHTTPServer 3000
或者Nginx
做代理都是很方便實(shí)現(xiàn)的噪叙。
- Case3: 不必要的Node服務(wù)
var express = require("express");
var path = require('path');
var project = require('../project.config');
const app = express();
app.use('*', function (req, res) {
const file = path.resolve(project.basePath, project.outDir, 'index.html');
res.sendFile(file);
});
app.listen(process.env.PORT || 3000, function () {
console.log("Listening on port %d!", this.address().port);
});
如上矮锈,只是借用Node服務(wù)去render產(chǎn)出物,并未做其他任何請(qǐng)求處理睁蕾,其實(shí)和case2,3沒有本質(zhì)區(qū)別苞笨。
- Case4: 將Node服務(wù)作為API中間層
router.route('/articles/send')
.post(async (req, res) => {
const {params} = req.body;
const requestUrl = '/x/x/x/x';
const response = await requestArticle(requestUrl, params);
res.status(200).send(response).end();
});
這種情況下,Node服務(wù)就必須存在子眶,因?yàn)楹苡锌赡苷嬲腁PI處理方不支持跨域瀑凝,或者有身份驗(yàn)證,那么就得在這里去處理臭杰,生產(chǎn)環(huán)境自然也得有粤咪。
- Case5: Node端有完善的RESTful API
這種情況下,已經(jīng)可以定義為一個(gè)前后端項(xiàng)目了渴杆,只是恰好前后端的語(yǔ)言一樣寥枝,并且可以共用大部分模塊。
參考如上磁奖,你的項(xiàng)目囊拜,屬于哪種情況呢?
3.如何部署比搭?
終于到了正題艾疟,其實(shí)部署無非就是運(yùn)行環(huán)境(server)+資源(包),因此才需要搞清楚你的項(xiàng)目到底需不需要server?
蔽莱,更確切的說是你的項(xiàng)目的生產(chǎn)環(huán)境到底需不需要server弟疆?
,從而決定如何部署盗冷。
- 對(duì)于case1,2,3:
可以選擇任意靜態(tài)服務(wù)器怠苔,運(yùn)行在生產(chǎn)環(huán)境,每次部署只需拉取最新的代碼或生成最新的包仪糖。如果需要多機(jī)部署柑司,則推薦docker的node或nginx鏡像,server只作一層簡(jiǎn)單的router和render锅劝,并將最新的源碼打包在內(nèi)即可攒驰。
體總來說這種情況是最簡(jiǎn)單的case,一個(gè)靜態(tài)服務(wù)器就可以故爵,想練手的可以利用Github Page去玩玩玻粪。
- 對(duì)于case4,5:
單機(jī)環(huán)境建議有部署腳本(如ansible,去初始化各種環(huán)境依賴)诬垂;多機(jī)部署則考慮node鏡像劲室,每次部署時(shí)都把代碼打進(jìn)鏡像,并且設(shè)置啟動(dòng)命令结窘,最后的部署方式就是一鍵部署
很洋。
BTW,對(duì)于這種case隧枫,如果部署時(shí)就是多機(jī)的情況喉磁,倒不如一勞永逸,開發(fā)環(huán)境直接用docker官脓。但開發(fā)和生產(chǎn)還有點(diǎn)區(qū)別:建議開發(fā)時(shí)不要把源碼打進(jìn)鏡像线定,畢竟代碼總是在變,可以將代碼作為VOLUMN
每次加載上去确买,然后手動(dòng)啟動(dòng),如下片段:
Dockerfile:
FROM node:7.2
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb http://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install yarn
WORKDIR /app
VOLUME /app
只依賴Node服務(wù)和yarn纱皆。
Run docker:
docker run --rm -v [your path]:/app -ti -p 3000:3000 image:version /bin/bash
只需將代碼掛載上去即可湾趾,完全的環(huán)境代碼分離模式。
4.寫在最后
說了這么多派草,給想實(shí)踐的朋友推薦以下資源: