webpack+koa框架搭建官網(wǎng)

前言

搭建官網(wǎng)随常,考慮后期的SEO優(yōu)化掩蛤,一般會(huì)使用前端寫靜態(tài)頁面哮伟,后端來渲染的模式潭辈。但其實(shí)前端也可以進(jìn)行服務(wù)端渲染,今天講的主要內(nèi)容就是前端方面的服務(wù)端渲染澈吨。本案例使用node做中間層向后端請(qǐng)求數(shù)據(jù)把敢,再用koa-swig進(jìn)行服務(wù)端模板渲染。

首先配置webpack文件谅辣,這個(gè)主要是習(xí)慣了寫es6修赞,還有用到它的公共代碼提取和代碼壓縮。


image.png

官網(wǎng)如圖所示桑阶,主要內(nèi)容有首頁柏副、案例、行業(yè)和案例詳情蚣录、資訊詳情等割择。選主要的2個(gè)點(diǎn),案例和案例詳情頁萎河,這樣就可以了荔泳。

1蕉饼、配置webpack
1、電腦安裝node
2玛歌、在目標(biāo)文件夾輸入
npm init

開始新建項(xiàng)目


image.png

輸入項(xiàng)目名稱后也可以一直回車完成昧港。

3、全局安裝webpack
4支子、在文件目錄里新建webpack.config.js创肥,這個(gè)就是webpack的配置文件
var webpack = require("webpack");
var path = require("path");
var glob = require('glob');
// var HtmlWebpackPlugin = require('html-webpack-plugin');
var ROOT_PATH = path.resolve(__dirname,'./dist/js');
var BUILD_PATH = path.resolve(ROOT_PATH, '../es6js');

/**
 * 根據(jù)目錄獲取入口
 * @param  {[type]} globPath [description]
 * @return {[type]}          [description]
 */
function getEntry(globPath) {
    let entries = {};
    glob.sync(globPath).forEach(function(entry) {
        let basename = path.basename(entry, path.extname(entry)),
            pathname = path.dirname(entry);
        if (!entry.match(/js\/lib\//)) {
            entries[basename] = pathname + '/' + basename;
        }
    });
    return entries;
}

let entryJs = getEntry('./dist/js/*.js');


module.exports = {
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')]
    },
    entry: entryJs,
    output: {
        path: BUILD_PATH,
        publicPath: BUILD_PATH,
        filename: "[name].js"
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                loader: ["style-loader", "css-loader", "sass-loader", ]
            },
            {
                test: /\.js$/,
                loader: ["babel-loader?cacheDirectory"],
            },
            {
                test: /\.css$/,
                loader: ["style-loader", "css-loader"],
                include: path.resolve(__dirname, './src/es6/route')
            }
        ]
    },
    plugins: [
        /**
         * 抽出公共JS
         */
        new webpack.optimize.CommonsChunkPlugin({
            name: "common",
            filename: "common.js",
            minChunks: 2,
        }),
        new webpack.optimize.UglifyJsPlugin()
        // new HtmlWebpackPlugin()
    ]
}

引入依賴項(xiàng),引入的都是需要使用npm安裝的值朋,當(dāng)然叹侄,你也可以使用淘寶鏡像。
其中ROOT_PATH是轉(zhuǎn)換前的源文件昨登,BUILD_PATH是轉(zhuǎn)換為es5打包后的文件地址圈膏。
插件:

  • CommonsChunkPlugin是用來抽取公共引入的部分打包成common.js,這里配置的是引入2次及以上的模塊會(huì)被打包進(jìn)common.js篙骡。
  • UglifyJsPlugin這個(gè)是用來進(jìn)行代碼壓縮稽坤。
5、配置編譯es6還需要加個(gè)文件糯俗,.babelrc
image.png

.babelrc代碼

{
    "presets": [
        [
            "es2015",
            {
                "modules": false
            }
        ]
    ],
    "plugins": []
}

這樣就可以把es6直接轉(zhuǎn)換為es5運(yùn)行了尿褪。

6、目錄解釋:我把模板文件放在了views文件夾得湘,其他附帶的js\css\img等統(tǒng)一放在靜態(tài)目錄dist文件夾.設(shè)置后可以當(dāng)做根目錄直接使用端口調(diào)用杖玲。
image.png
7、node服務(wù)端app.js
const Koa = require("koa");
const serv = require("koa-static");
const render = require("koa-swig");
const co = require("co");
const path = require("path");
const app = new Koa();
const initRouter = require("./router");

app.use(serv(__dirname+'/dist'));
app.context.render = co.wrap(render(app, {
    root: path.join(__dirname, 'views'),
    autoescape: true,
    cache: 'memory', 
    writeBody: false, 
    ext: 'html'
  }));

  initRouter(app);  
app.listen( (process && process.env && process.env.PORT) || 3000, ()=>{
    console.log('服務(wù)已啟動(dòng)淘正,port:' + ((process && process.env && process.env.PORT) || 3000));
});

注意其中app.use(serv(__dirname+'/dist'))就是設(shè)置靜態(tài)目錄摆马。

8、node端請(qǐng)求封裝base.js
const request = require('request');
const querystring = require('querystring');

let baseHttp = "http://172.1.1.1/TB/";        //這里是你的后端的接口默認(rèn)地址鸿吆。

class myRequest{
    constructor(){
        
    }
    get(url,data,callback){
        let myUrl = `${url}?`;
        let dataArray = [];
        for(let k in data){
            let paramStr = `${k}=${data[k]}`;
            dataArray.push(paramStr);
        }
        let urlStr = dataArray.join("=");
        myUrl = `${myUrl}${urlStr}`;
        request(myUrl,(error, response, body)=> {
            if (response && response.statusCode && response.statusCode === 200) {
                try{
                    body = JSON.parse(body);
                }catch(e){
                    body = null;
                } 
                callback&&callback(body);
            }
        });
    }
    post(url,data){
        return new Promise(function (resolve, reject) {
            request.post({
                url: `${baseHttp}${url}`,
                json: true,
                form:(data)
            }, function(error, response, data) {
                if (response) {
                    if (!error && response.statusCode == 200) {
                        resolve(data);
                            // callback && callback(data.Data);
                    }else{
                        reject('error===');
                    }
                } else {
                    console.log(error);
                    //后臺(tái)程序錯(cuò)誤
                    var data = {
                        ResultCode:0,
                        Message:'與后臺(tái)通信異常'
                    }
                    resolve(data);
                }
            });
        })
    }
}
module.exports=myRequest;
9囤采、路由router.js
const Router = require('koa-router')
const koaBody = require('koa-body');
const myrequest = require('./modules/base');
let myRequest = new myrequest();

module.exports = function (app) {
    const router = new Router();
    app.use(router.routes());
    app.use(router.allowedMethods());
    
  //案例頁路由
  router.get('/cases.html',async (ctx,next)=>{
    // ctx.router available
    let cbdata = {};
    let data = {};
    let pagecurrent = ctx.query.page || 1;
    data = await myRequest.post('/api/Home/GetProjectCaseList',{PageNo:pagecurrent,PageSize:20});
    if(data && data.ResultCode=="6666"){
      cbdata = data.Data;
    }
    cbdata.base = {
      title:'案例頁',
      self:'cases',
    };
    if(cbdata.PageInfo){
      cbdata.PageInfo.PageList = [];
      let count = cbdata.PageInfo.PageCount || 0;
      let className = "";
      for(let i=0;i<count;i++){
        if(i == (pagecurrent-1)){
          className = "active";
        }else{
          className = "";
        }
        cbdata.PageInfo.PageList.push(className);
      }
    }
    ctx.body = await ctx.render('cases',cbdata);
  });
  //案例詳情頁路由
  router.get('/casesdetail.html',async (ctx,next)=>{
    // ctx.router available
    let id = ctx.query.id;
    let cbdata = {};
    let data = {};
    data = await myRequest.post('/api/Home/GetProjectCaseById',{'id':id});
    if(data && data.ResultCode=="6666"){
      cbdata = data.Data;
    }
    cbdata.base = {
      title:'案例詳情頁',
      self:'casesdetail',
    };
    ctx.body = await ctx.render('casesdetail',cbdata);
  });

}

這個(gè)js寫的比較粗糙,主要作用:
router.get('/cases.html'惩淳,async(ctx,next)=>{
let id = ctx.query.id;
ctx.body = await ctx.render('cases',cbdata);
}
根據(jù)koa-router路由捕獲到觸發(fā)路由cases.html后蕉毯,ctx.query.id獲取參數(shù),例如瀏覽器顯示路由/cases.html?id=12345,那么就可以直接提取到12345作為id.可以用這個(gè)來進(jìn)行分頁和判斷選擇了那個(gè)類目
ctx.body = await ctx.render('cases',cbdata);就是從views文件夾里尋找文件名為cases的文件思犁,將值cbdata放進(jìn)模板渲染然后形成一個(gè)html文件代碼返回給ctx.body顯示代虾。
cbdata.base = {
title:'案例頁',
self:'cases',
};
里面的self用來在公共模板按每個(gè)不同的頁面引入文件名對(duì)應(yīng)的js

10、模板文件:
image.png
  • 模板文件也是html后綴激蹲,頁面頂部導(dǎo)航和底部導(dǎo)航公用棉磨,所以提取出來作為header和footer
  • header.html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {% if ObjDetail %}
    <meta name="keyword" content="{{ObjDetail.SeoKeywords}}">
    {% endif %}
    {% if ObjDetail %}
    <meta name="description" content="{{ObjDetail.SeoDescription}}">
    {% endif %}
    {% if base %}
    <title>{{base.title}}</title>
    <link href="./plugin/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <!-- <link href="./css/common.css" rel="stylesheet"> -->
    <link href="./css/{{base.self}}.css" rel="stylesheet">
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
      <div class="header">
          <div class="navbar navbar-default nocolor" role="navigation">
              <div class="navbar-header">
                  <!-- .navbar-toggle樣式用于toggle收縮的內(nèi)容,即nav-collapse collapse樣式所在元素 -->
                   <button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".navbar-responsive-collapse">
                     <span class="sr-only">導(dǎo)航菜單</span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                   </button>
              </div>
              <!-- 屏幕寬度小于768px時(shí)学辱,div.navbar-responsive-collapse容器里的內(nèi)容都會(huì)隱藏乘瓤,顯示icon-bar圖標(biāo)环形,當(dāng)點(diǎn)擊icon-bar圖標(biāo)時(shí),再展開馅扣。屏幕大于768px時(shí),默認(rèn)顯示着降。 -->
              <div class="collapse navbar-collapse navbar-responsive-collapse">
                <div class="nav-center">
                  <ul class="nav navbar-nav">
                      <li class="hnav"><a href="/">首頁</a></li>
                      <li class="snav"><a href="/service.html">服務(wù)</a></li>
                      <li class="cnav"><a href="/cases.html">案例</a></li>
                      <li><a href="javascript:void(0);"><img src="./img/logo.png"/></a></li>
                      <li class="nnav"><a href="/news.html">行業(yè)資訊</a></li>
                      <li class="anav"><a href="/about.html">關(guān)于我們</a></li>
                 </ul>
                </div>
              </div>
          </div>
      </div>
      {% endif %}

{{}}插值運(yùn)算
判斷是否存在:{% if ... %},以{% endif %}結(jié)尾

  • cases.html
{% include 'header.html' %}
<div class="banner"></div>
<div class="main">
  <div class="con">
    <div class="head">
      <p class="mtitle">用真實(shí)案例說話</p>
      <p class="stitle">“為客戶打造傳奇品牌差油,成就電影人生”</p>
    </div>
    <div class="mcon">
        {% if List %}
      <ul class="itemlist">
        {% for item in List %}
        <li class="item" data-id="{{item.ProjectCaseId}}">
          <a href="javascript:void(0);">
            <div class="himgcon">
              <img class="simg" src="{{item.PhotoURL}}"/>
              <div class="mask">
                  <div class="post_info">
                      <div class="play_in">
                          <span class="line1"></span>
                          <span class="line2"></span>
                          <span class="line3"></span>
                      </div>
                      <div class="post_tt" title="{{item.Title}}">{{item.Title}}</div>
                  </div>
              </div>
            </div>
          </a>
          <!-- <div class="introduce" title="{{item.Title}}">
              {{item.Title}}
          </div> -->
        </li>
        {% endfor %}
      </ul>
      {% endif %}
      <div class="clb"></div>
      <div class="pagelist">
        {% if (PageInfo && PageInfo.PageList) %}
        <div class="page">
          {% for item in PageInfo.PageList %}
          <span class="page-num {{item}}">{{loop.index}}</span>
          {% endfor %}
        </div>
        {% endif %}
      </div>
    </div>
  </div>
</div>
{% include 'footer.html' %}

{% include template%}引入模板
{% for i in k %}、{% endfor %}循環(huán)渲染

  • casedetail.html
{% include 'headerdetail.html' %}
<div class="banner"></div>
<div class="main">
  <div class="con">
    {% if ObjDetail %}
    <div class="head">
      <p class="title">{{ObjDetail.Title}}</p>
      <p class="subintro">{{ObjDetail.PublishDate}} {{ObjDetail.Author}}</p>
    </div>
    <div class="content">
        {% autoescape false %}
        {{ ObjDetail.Content }}
        {% endautoescape %}
    </div>
  </div>
</div>
{% include 'footer.html' %}

{% autoescape false %}任洞、{% endautoescape %}html渲染,相當(dāng)于jq的.html()和vue的v-html
這樣模板就渲染好了蓄喇,footer.js中我也引入了common.js和各自對(duì)應(yīng)的文件名js文件

好了,這樣就做完了交掏,css我是用sass寫的妆偏,不知道怎么用的也可以看我之前的文章。使用命令:webpack -w可以打包文件,node app.js可以運(yùn)行項(xiàng)目盅弛,然后在瀏覽器中打開钱骂,當(dāng)然也可以在package.json中配置start,這樣直接運(yùn)行npm start也可以直接運(yùn)行項(xiàng)目.
image.png

在這里還要特別感謝沃土社區(qū)老胡挪鹏,你的指導(dǎo)讓我們?cè)絹碓綇?qiáng)大
寫完了见秽,覺得辛苦就支持下吧,另外有小項(xiàng)目兼職的也可以給我一點(diǎn)小單一起做啊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讨盒,一起剝皮案震驚了整個(gè)濱河市解取,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌返顺,老刑警劉巖禀苦,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異遂鹊,居然都是意外死亡振乏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門秉扑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昆码,“玉大人,你說我怎么就攤上這事邻储「逞剩” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵吨娜,是天一觀的道長(zhǎng)脓匿。 經(jīng)常有香客問我,道長(zhǎng)宦赠,這世上最難降的妖魔是什么陪毡? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任米母,我火速辦了婚禮,結(jié)果婚禮上毡琉,老公的妹妹穿的比我還像新娘铁瞒。我一直安慰自己,他們只是感情好桅滋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布慧耍。 她就那樣靜靜地躺著,像睡著了一般丐谋。 火紅的嫁衣襯著肌膚如雪芍碧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天号俐,我揣著相機(jī)與錄音泌豆,去河邊找鬼。 笑死吏饿,一個(gè)胖子當(dāng)著我的面吹牛踪危,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猪落,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼陨倡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了许布?” 一聲冷哼從身側(cè)響起兴革,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜜唾,沒想到半個(gè)月后杂曲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袁余,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年擎勘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颖榜。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棚饵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掩完,到底是詐尸還是另有隱情噪漾,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布且蓬,位于F島的核電站欣硼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恶阴。R本人自食惡果不足惜诈胜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一豹障、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧焦匈,春花似錦血公、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荚虚,卻和暖如春薛夜,著一層夾襖步出監(jiān)牢的瞬間籍茧,已是汗流浹背版述。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寞冯,地道東北人渴析。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吮龄,于是被迫代替她去往敵國(guó)和親俭茧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345