用到的技術(shù):React Node Webpack material-ui mongo
github地址:https://github.com/shenjiajun53/HiBlog
喜歡請(qǐng)給個(gè)star?闷4媪А!
推薦兩個(gè)工具:https://app.astralapp.com/dashboard 這個(gè)網(wǎng)站可以用來管理github上的star
另一個(gè)叫 Octotree 是Chrome應(yīng)用恋脚,可以看到github上的項(xiàng)目結(jié)構(gòu)恋腕,跟IDE一樣
我是一名Android開發(fā)抹锄,雖然在互聯(lián)網(wǎng)公司,但是感覺自己的工作根本就是軟件行業(yè)的工作荠藤。
想想對(duì)服務(wù)端伙单,前端都不了解真是挺無(wú)知的。
之前看同事有自己的博客站哈肖,百度了下吻育,發(fā)現(xiàn)要做那種個(gè)人網(wǎng)站真是很簡(jiǎn)單,只不過那根本就是個(gè)靜態(tài)網(wǎng)站淤井,太辣雞布疼,所以我決定自己擼個(gè)博客網(wǎng)站。
主頁(yè)
注冊(cè)頁(yè)
下面就是技術(shù)選型啦:
前端:
要用就用潮的币狠,三個(gè)著名框架Angular游两,React,Vue
Angular聽說1和2完全不兼容漩绵,最討厭這種對(duì)開發(fā)人員不負(fù)責(zé)的框架了贱案,不考慮
Vue的語(yǔ)法更像常規(guī)的前端開發(fā),React默認(rèn)支持es6止吐,風(fēng)格跟Android更像宝踪,尤其跟Android的DataBinding很像。
另一方面es6很像Java碍扔,我肯定使用es6開發(fā)的瘩燥,我更喜歡面向?qū)ο蟆ue當(dāng)然也可以用es6蕴忆,但我懶得鼓搗了颤芬,更重要的ReactNative可比Weex火多了。哈哈套鹅,所以前端我就選擇React了。
真正的前端開發(fā)應(yīng)該更喜歡Vue吧汰具,文檔都有中文的卓鹿。
后端:
雖然我是做Android的,但是遙想兩年前第一次接觸JavaEE的時(shí)候真是被惡心到了留荔,不考慮
python沒有花括號(hào)吟孙,知乎上都嘲笑寫python要用游標(biāo)卡尺澜倦,不考慮
PHP開發(fā)比Java更快,而且現(xiàn)在快跟Java平分秋色了吧杰妓,可以考慮藻治。
Node技術(shù)還是挺新的,各種文章資料會(huì)比較少巷挥,坑也可能比較多桩卵。但我真是不高興再學(xué)一門語(yǔ)言了,所以哈哈倍宾,就選Node了雏节。
運(yùn)行
1.git clone
2.安裝mongo,然后在安裝目錄下新建文件夾高职,比如“D:\data\db”钩乍,然后打開“D:\MongoDB\Server\3.4\bin\mongod.exe”開啟服務(wù),會(huì)有個(gè)命令行窗口保持運(yùn)行
3.建議安裝一個(gè)mongo可視化工具怔锌,Robomongo寥粹,默認(rèn)連接localhost:27017,能連接上就說明好了
4.進(jìn)入項(xiàng)目目錄下埃元,執(zhí)行命令
npm install
安裝相關(guān)依賴庫(kù)
5.執(zhí)行
node server.js
開啟服務(wù)涝涤。
或者,比如我用的Webstorm亚情,可以點(diǎn)擊左下角npm按鈕妄痪,點(diǎn)擊start-dev-serpervisor或者點(diǎn)擊start-dev-nodemon,都可以
6.打開瀏覽器,輸入 localhost:5006
下面開始:
我對(duì)前后端幾乎零基礎(chǔ)楞件,之前有看過ReactNative衫生,順便看了下React。
所以寫的代碼在內(nèi)行眼里可能很丟人土浸,哈哈
第一步:
安裝node罪针,新建個(gè)文件夾,HiBlog
命令行:
cd HiBlog
初始化:
npm init
生成package.json文件
然后安裝各種需要的包黄伊,比如express--node框架泪酱,webpack--打包工具
npm i express --save;npm i webpack --save
然后這兩個(gè)就下載好了,在node_module文件夾下还最,引用也自動(dòng)添加到package.json文件里了
或者package.json文件直接復(fù)制的別人的墓阀,里面有內(nèi)容了,
npm i
把package里所有引用都下載到node_module下拓轻。
跟Android的依賴很像啊
我的module引用
{
"name": "HiBlog",
"version": "0.1.0",
"private": true,
"engines": {
"node": "^7.4.0",
"npm": ">= 3.x.x"
},
"devDependencies": {
"concurrently": "^3.1.0",
"nodemon": "^1.11.0", //node監(jiān)聽工具斯撮,改動(dòng)代碼自動(dòng)重啟服務(wù)
"supervisor": "^0.12.0", //跟上面一樣的,用一個(gè)就行扶叉,反正我都加了
},
"dependencies": {
"antd": "^2.6.4", //螞蟻金服的UI框架勿锅,項(xiàng)目里沒用帕膜,哈哈
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.22.0",
"body-parser": "^1.16.0",
"config-lite": "^1.5.0",
"connect-flash": "^0.1.1",
"connect-mongo": "^1.3.2",
"cookie-parser": "^1.4.3",
"css-loader": "^0.26.1",
"ejs": "^2.5.5",
"express": "^4.14.1",
"express-formidable": "^1.0.0",
"express-session": "^1.15.0",
"express-winston": "^2.1.3",
"jsx-loader": "^0.13.2",
"marked": "^0.3.6",
"material-ui": "^0.16.7", //UI框架,項(xiàng)目里主要用的就是這個(gè)
"moment": "^2.17.1",
"mongoose": "^4.8.1", //數(shù)據(jù)庫(kù)框架
"multer": "^1.3.0",
"objectid-to-timestamp": "^1.3.0",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-router": "^3.0.2",
"react-tap-event-plugin": "^2.0.1",
"roboto": "^0.8.2",
"sha1": "^1.1.1",
"style-loader": "^0.13.1",
"webpack": "^1.14.0",
"winston": "^2.3.1"
},
"scripts": {
"start": "node server.js", //啟動(dòng)服務(wù)
"start-supervisor": "supervisor --harmony server", //啟動(dòng)服務(wù) 代碼有變動(dòng)就重啟
"start-nodemon": "nodemon server.js", //啟動(dòng)服務(wù) 代碼有變動(dòng)就重啟
"build": "webpack --watch", //打包React代碼溢十, --watch代表代碼有變動(dòng)就重新打包
"start-dev": "concurrently \"npm run start\" \"npm run build\"",
"start-dev-supervisor": "concurrently \"npm run start-supervisor\" \"npm run build\"",
"start-dev-nodemon": "concurrently \"npm run start-nodemon\" \"npm run build\""
}
}
看下我的目錄結(jié)構(gòu)
看下我的github頁(yè)面垮刹,右邊就是我在最上面介紹的github插件,工欲善其事必先利其器张弛。
新建文件webpack.config.js荒典,webpack工具會(huì)識(shí)別到這個(gè)文件里的腳本,然后把React代碼打包乌庶,很重要种蝶,不然不管是React的JSX或者ES6,瀏覽器都讀不懂瞒大。
新建文件夾views螃征,里面是前端代碼
新建文件夾routes,里面放路由代碼透敌,因?yàn)槲覀冇玫腞eact盯滚,所以這個(gè)網(wǎng)站就是所謂的單頁(yè)網(wǎng)站,One Page Website酗电∑桥海看views下面只有一個(gè)index.html。
這個(gè)HTML就是個(gè)空殼撵术,真正的網(wǎng)頁(yè)都是有js代碼生成的背率,不管是哪個(gè)頁(yè)面,都依托這個(gè)HTML嫩与。所以這里和常規(guī)網(wǎng)頁(yè)不通寝姿,網(wǎng)頁(yè)不是由服務(wù)器渲染的一個(gè)個(gè)HTML。網(wǎng)站的工作方式更像APP划滋,通過api實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容變動(dòng)饵筑。前后端分離才是人性化的開發(fā)方式,不是嗎处坪?
文件夾config根资,里面放一些設(shè)置
最重要的server.js
/**
* Created by shenjj on 2017/1/23.
*/
let express = require('express');
let app = express();
let path = require('path');
let ejs = require('ejs');
let bodyParser = require("body-parser");
let MongoUtil = require("./server/lib/MongoUtil");
let session = require('express-session');
let MongoStore = require('connect-mongo')(session);
let cookieParser=require("cookie-parser");
let config = require('config-lite');
let flash = require('connect-flash');
let homeRouter = require('./routes/HomePageRouter');
let userRouter = require('./routes/userInfo');
// let signRouter = require("./server/signUp").signUp;
// let DealSignUp = require("./server/DealSignUp");
let RouterManager = require("./routes/RouterManager");
// 設(shè)置模板目錄
app.set('views', './views');
// 設(shè)置模板引擎為 ejs
app.set('view engine', 'html');
app.engine('html', ejs.renderFile);
// app.use配置
app.use('/output', express.static(path.join(__dirname, '/output')));
//app.use('/views', express.static(path.join(__dirname, '/views')));
app.use('/uploadFiles', express.static(path.join(__dirname, '/uploadFiles')));
...
...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// app.use('/', homeRouter);
app.use('/users', userRouter);
let routerManager = new RouterManager(app);
routerManager.startRouters();
app.get('*', function (request, response) {
response.sendFile(path.resolve(__dirname, 'views', 'index.html'))
});
app.listen(process.env.PORT || 5006);
console.log("url=" + process.env.PORT);
這里開始就是真的node代碼里
設(shè)置模版目錄在views下面,其實(shí)就是那個(gè)index.html
這是模版引擎ejs同窘,用來渲染成html玄帕,(有兩個(gè)主流引擎ejs和jade,ejs和html一毛一樣想邦,所以我推薦這個(gè)桨仿,jade雖然省代碼,但是跟python一樣案狠,太隨便了)
另外我不需要用ejs的功能服傍,所以我直接設(shè)置成HTML了。
app.use('/output', express.static(path.join(__dirname, '/output')));
app.use('/uploadFiles', express.static(path.join(__dirname, '/uploadFiles')));
用來指定打包后的js代碼骂铁,在output目錄下吹零,這個(gè)是在wenpack.config里設(shè)置的
uploadFiles是js代碼里引用的圖片,空目錄拉庵,里面的圖片是用戶上傳的灿椅。
RouterManager是我寫的Router管理類,從風(fēng)格也能看出我就是比較習(xí)慣Java钞支,還是面向?qū)ο笫娣S迹m然代碼量大一點(diǎn)。
routerManater把a(bǔ)pp傳進(jìn)去
最后是端口號(hào)烁挟,默認(rèn)5006
RouterManager里:
...
this.app.use("/", new HomePageRouter().getRouter());
this.app.use("/api", new UserRouter().getRouter());
this.app.use("/api", new BlogRouter().getRouter());
...
HomePageRouter里:
router.get('/', function (req, res) {
console.log("homepage on render");
res.render('index');
// res.render('');
// res.redirect("/SignUp");
});
所以就是婴洼,直接在瀏覽器輸入localhost:5006,會(huì)跳轉(zhuǎn)到主頁(yè)
如果輸入localhost:5006/api/xxx撼嗓,會(huì)調(diào)起userRouter或者blogRouter里的接口柬采,看那個(gè)匹配了
下面是部分前端代碼
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
hasLogin: false,
user: null
}
}
componentWillMount() {
let url = "/api/getUserInfo";
fetch(url, {
method: "post",
credentials: 'include' //很重要,設(shè)置session,cookie可用
}).then(
(response) => {
return response.json();
}
).then(
(json) => {
console.log(JSON.stringify(json));
if (json.result) {
this.setState({
hasLogin: json.result.hasLogin,
user: json.result.user
});
}
// console.log("state=" + this.state.hasLogin);
}
).catch(
(ex) => {
console.error('parsing failed', ex);
});
}
render() {
console.log('app render');
// console.log('chileren=' + this.props.children.name);
return (
<MuiThemeProvider>
<div>
<TopBar hasLogin={this.state.hasLogin} user={this.state.user}/>
{React.cloneElement(this.props.children, {user: this.state.user})}
</div>
</MuiThemeProvider>
);
}
}
render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="SignUp" component={SignUp}/>
<Route path="SignIn" component={SignIn}/>
<Route path="UserCenter" component={UserCenter}/>
<Route path="MyFollow" component={MyFollow}/>
<Route path="WriteBlog" component={WriteBlog}/>
<Route path="BlogDetail/:blogId" component={BlogDetail}/>
<Route path="Settings" component={Settings}/>
<Route path="Favorites" component={Favorites}/>
<Route path="MyBlogs" component={MyBlogs}/>
</Route>
</Router>
,
document.getElementById('root')
)
頁(yè)面跳轉(zhuǎn)完全由前端控制且警,用Rooter類粉捻,APP是整個(gè)網(wǎng)頁(yè)的父布局鸿竖。在APP渲染完成后獲取user信息瓷炮,this.props.children是當(dāng)前頁(yè)面,可以clone一個(gè)ReactComponent速缨,并設(shè)置props杏头。