React + Node + Mongo +Webpack寫一個簡單的Blog網站

用到的技術:React Node Webpack material-ui mongo

github地址:https://github.com/shenjiajun53/HiBlog
喜歡請給個star!!!
推薦兩個工具:https://app.astralapp.com/dashboard 這個網站可以用來管理github上的star

另一個叫 Octotree 是Chrome應用,可以看到github上的項目結構,跟IDE一樣

我是一名Android開發,雖然在互聯網公司,但是感覺自己的工作根本就是軟件行業的工作。
想想對服務端,前端都不了解真是挺無知的。
之前看同事有自己的博客站,百度了下,發現要做那種個人網站真是很簡單,只不過那根本就是個靜態網站,太辣雞,所以我決定自己擼個博客網站。

主頁

home_page.png

注冊頁

register_page.png

下面就是技術選型啦:

前端:

要用就用潮的,三個著名框架Angular,React,Vue
Angular聽說1和2完全不兼容,最討厭這種對開發人員不負責的框架了,不考慮
Vue的語法更像常規的前端開發,React默認支持es6,風格跟Android更像,尤其跟Android的DataBinding很像。
另一方面es6很像Java,我肯定使用es6開發的,我更喜歡面向對象。Vue當然也可以用es6,但我懶得鼓搗了,更重要的ReactNative可比Weex火多了。哈哈,所以前端我就選擇React了。
真正的前端開發應該更喜歡Vue吧,文檔都有中文的。

后端:

雖然我是做Android的,但是遙想兩年前第一次接觸JavaEE的時候真是被惡心到了,不考慮
python沒有花括號,知乎上都嘲笑寫python要用游標卡尺,不考慮
PHP開發比Java更快,而且現在快跟Java平分秋色了吧,可以考慮。
Node技術還是挺新的,各種文章資料會比較少,坑也可能比較多。但我真是不高興再學一門語言了,所以哈哈,就選Node了。

運行

1.git clone
2.安裝mongo,然后在安裝目錄下新建文件夾,比如“D:\data\db”,然后打開“D:\MongoDB\Server\3.4\bin\mongod.exe”開啟服務,會有個命令行窗口保持運行
3.建議安裝一個mongo可視化工具,Robomongo,默認連接localhost:27017,能連接上就說明好了
4.進入項目目錄下,執行命令

npm install

安裝相關依賴庫
5.執行

node server.js

開啟服務。
或者,比如我用的Webstorm,可以點擊左下角npm按鈕,點擊start-dev-serpervisor或者點擊start-dev-nodemon,都可以

webstorm.png

6.打開瀏覽器,輸入 localhost:5006

下面開始:

我對前后端幾乎零基礎,之前有看過ReactNative,順便看了下React。
所以寫的代碼在內行眼里可能很丟人,哈哈

第一步:

安裝node,新建個文件夾,HiBlog
命令行:

cd HiBlog

初始化:

npm init

生成package.json文件
然后安裝各種需要的包,比如express--node框架,webpack--打包工具

npm i express  --save;npm i webpack --save

然后這兩個就下載好了,在node_module文件夾下,引用也自動添加到package.json文件里了
或者package.json文件直接復制的別人的,里面有內容了,

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監聽工具,改動代碼自動重啟服務
    "supervisor": "^0.12.0",                                   //跟上面一樣的,用一個就行,反正我都加了
  },
  "dependencies": {
    "antd": "^2.6.4",                                         //螞蟻金服的UI框架,項目里沒用,哈哈
    "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框架,項目里主要用的就是這個
    "moment": "^2.17.1",
    "mongoose": "^4.8.1",                                     //數據庫框架
    "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",                                          //啟動服務
    "start-supervisor": "supervisor --harmony server",        //啟動服務    代碼有變動就重啟
    "start-nodemon": "nodemon server.js",                    //啟動服務       代碼有變動就重啟
    "build": "webpack --watch",                                   //打包React代碼, --watch代表代碼有變動就重新打包
    "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\""
  }
}

看下我的目錄結構

2017-02-18.png

看下我的github頁面,右邊就是我在最上面介紹的github插件,工欲善其事必先利其器。

新建文件webpack.config.js,webpack工具會識別到這個文件里的腳本,然后把React代碼打包,很重要,不然不管是React的JSX或者ES6,瀏覽器都讀不懂。

新建文件夾views,里面是前端代碼
新建文件夾routes,里面放路由代碼,因為我們用的React,所以這個網站就是所謂的單頁網站,One Page Website。看views下面只有一個index.html。
這個HTML就是個空殼,真正的網頁都是有js代碼生成的,不管是哪個頁面,都依托這個HTML。所以這里和常規網頁不通,網頁不是由服務器渲染的一個個HTML。網站的工作方式更像APP,通過api實現網頁內容變動。前后端分離才是人性化的開發方式,不是嗎?

文件夾config,里面放一些設置

最重要的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");

// 設置模板目錄
app.set('views', './views');

// 設置模板引擎為 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代碼里
設置模版目錄在views下面,其實就是那個index.html
這是模版引擎ejs,用來渲染成html,(有兩個主流引擎ejs和jade,ejs和html一毛一樣,所以我推薦這個,jade雖然省代碼,但是跟python一樣,太隨便了)
另外我不需要用ejs的功能,所以我直接設置成HTML了。

app.use('/output', express.static(path.join(__dirname, '/output')));
app.use('/uploadFiles', express.static(path.join(__dirname, '/uploadFiles')));
用來指定打包后的js代碼,在output目錄下,這個是在wenpack.config里設置的
uploadFiles是js代碼里引用的圖片,空目錄,里面的圖片是用戶上傳的。

RouterManager是我寫的Router管理類,從風格也能看出我就是比較習慣Java,還是面向對象舒服,雖然代碼量大一點。
routerManater把app傳進去

最后是端口號,默認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,會跳轉到主頁
如果輸入localhost:5006/api/xxx,會調起userRouter或者blogRouter里的接口,看那個匹配了

下面是部分前端代碼

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'     //很重要,設置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')
)

頁面跳轉完全由前端控制,用Rooter類,APP是整個網頁的父布局。在APP渲染完成后獲取user信息,this.props.children是當前頁面,可以clone一個ReactComponent,并設置props。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容