国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

node express mongoDB個(gè)人博客總結(jié)

missonce / 2557人閱讀

摘要:前端渲染可以減輕服務(wù)器端的開銷,但是首屏的渲染會(huì)加長時(shí)間后端渲染增加服務(wù)器的開銷,但是減少客戶端展示的時(shí)間

1 注冊、登錄和退出 1.1 用戶注冊、登錄

配置模板引擎、mongoDB數(shù)據(jù)庫驅(qū)動(dòng)、靜態(tài)文件路徑和post請求解析中間件

統(tǒng)一api.js路由的數(shù)據(jù)返回格式

// 統(tǒng)一返回?cái)?shù)據(jù)格式
var responseData;
// 每次請求進(jìn)來都進(jìn)行初始化
router.use(function (res, req, next) {
  responseData = {
    code: 0,     // 狀態(tài)碼,默認(rèn)為0
    message: ""   //  狀態(tài)碼的提示信息,默認(rèn)為0
  };

  next();   // 調(diào)用next()交由下一個(gè)中間件繼續(xù)處理
});

設(shè)計(jì)用戶的數(shù)據(jù)模型設(shè)計(jì)與創(chuàng)建

var mongoose = require("mongoose");
var Schema = mongoose.Schema;

// userSchema代表名為用戶的collection集合
var usersSchema = new Schema({
  // 每個(gè)屬性代表collection中的每個(gè)document

  // 用戶名: 字符串
  username: String,
  // 密碼: 字符串
  password: String,
  // 是否為管理員,默認(rèn)為false
  isAdmin: {
    type: Boolean,
    default: false
  }
});

// 對外導(dǎo)出定義的用戶的collection結(jié)構(gòu)
module.exports = usersSchema;
var mongoose = require("mongoose");
// 加載創(chuàng)建的usersSchema模型
var usersSchema = require("../schemas/users");

// 利用usersSchema創(chuàng)建Model,使用mongoose.model()方法
// 第一個(gè)參數(shù)是模型的名字;第二個(gè)參數(shù)是創(chuàng)建模型的數(shù)據(jù)結(jié)構(gòu)
// User是一個(gè)構(gòu)造函數(shù),可以利用Model直接操作collection,也可以實(shí)例化對象來操作每個(gè)document
var User = mongoose.model("User", usersSchema);

// 對外暴露模型,提供給業(yè)務(wù)邏輯操作
module.exports = User;

完成注冊邏輯

前端將數(shù)據(jù)提交到指定路由(Ajax或整頁刷新)

服務(wù)器獲取提交的數(shù)據(jù),進(jìn)行基本驗(yàn)證與數(shù)據(jù)庫查重驗(yàn)證

如果數(shù)據(jù)庫中用戶名已經(jīng)存在,返回錯(cuò)誤信息;如果不存在,則保存當(dāng)前注冊信息

完成登錄邏輯

前端利將數(shù)據(jù)提交到指定路由(Ajax或整頁刷新)

服務(wù)器通過body-parser中間件獲取post請求中的數(shù)據(jù)req.body;通過req.query獲取get請求中的數(shù)據(jù),進(jìn)行基本驗(yàn)證

進(jìn)行數(shù)據(jù)庫查詢驗(yàn)證:使用usernamepassword兩個(gè)字段進(jìn)行查詢,如果存在,則返回登錄成功;否則登錄失敗

同時(shí),為登錄成功的用戶發(fā)送一個(gè)cookie,保存必要的信息,但不能是密碼等敏感信息,用于保存用戶的登錄狀態(tài),cookie只有在瀏覽器沒有上傳cookie是才發(fā)送,并且只發(fā)送一次,注意設(shè)置過期時(shí)間

// 設(shè)置Cookie,每個(gè)請求進(jìn)入路由處理前,先處理req對象中的cookie信息
// 用戶無論何時(shí)訪問站點(diǎn),都通通過這個(gè)中間件,并且通過next()方法將返回值傳遞下去
// 在登錄成功后,通過cookies.set()方法將cookie一起返回給瀏覽器
// 第一次登錄時(shí),客戶端沒有cookie,需要發(fā)送一個(gè)cookie回去;
app.use(function (req, res, next) {
  req.cookies = new Cookies(req, res);

  // 在訪問admin中評論、留言等功能時(shí)都需要用到登錄信息,定義一個(gè)全局的req對象的屬性,來保存用戶登錄的cookie信息
  req.userInfo = {};

  if(req.cookies.get("userInfo")) {
try {
  req.userInfo = JSON.parse(req.cookies.get("userInfo"));  // 將cookie解析為一個(gè)對象
  // 需要實(shí)時(shí)獲取當(dāng)前的用戶是否為管理員,查詢數(shù)據(jù)庫,利用當(dāng)前用戶的_id查詢
  User.findById({_id: req.userInfo._id}).then(function (userInfo) {
 req.userInfo.isAdmin = Boolean(userInfo.isAdmin);  // 新增req對象的一個(gè)全局屬性,判斷是否非管理員
 next();
  })
} catch (e) {
  next();
}
  } else {
next();
  }
});

// 發(fā)送一個(gè)cookie,瀏覽器會(huì)緩存cookie,以后每次請求,瀏覽器都會(huì)帶上這個(gè)cookie
// cookie應(yīng)該能唯一標(biāo)識(shí)一個(gè)用戶,所以使用用戶信息作為cookie,cookie是一個(gè)字符串
req.cookies.set("userInfo", JSON.stringify({
  _id: userInfo._id,
  username: userInfo.username
}));

刷新頁面時(shí),瀏覽器將cookie中的數(shù)據(jù)發(fā)送到服務(wù)器,服務(wù)器利用cookie中的信息完成登錄頁面的展示

利用cookie判斷是否 為管理員(是否是管理員的信息一般不放在cookie中),登錄后臺(tái)管理界面

1.2 退出

利用cookies模塊,將cookie字段設(shè)置為null,然后在客戶端刷新頁面,即未登錄狀態(tài)下展示的頁面

router.get("/", function (req, res, next) {
  res.render("admin/index.html", {
    userInfo: req.userInfo
  });
});
2 后臺(tái)管理 2.1 判斷是否為管理員

利用cookie中添加的后續(xù)信息,判斷是否為管理員,只有管理員才能繼續(xù)后續(xù)操作

// 利用中間件判斷是否為管理員賬戶
router.use(function (req, res, next) {
  if(!req.userInfo.isAdmin) {
    res.send("Sorry, it"s only for Administor!");
    return;
  }
  // 如果是管理員,繼續(xù)下面的操作
  next();
});

2.2 后臺(tái)信息展示界面

利用get請求,獲取后臺(tái)頁面,傳入cookie中的信息req.userInfo

router.get("/", function (req, res, next) {
  res.render("admin/index.html", {
    userInfo: req.userInfo
  });
});

2.3 用戶信息的展示

同樣利用get請求,從數(shù)據(jù)庫中查詢所有的用戶信息,并將數(shù)據(jù)返回前端。

分頁展示數(shù)據(jù)的功能通過:limit()skip()約束實(shí)現(xiàn)

倒序通過sort({_id: -1})約束實(shí)現(xiàn)

同時(shí)查詢關(guān)聯(lián)字段的數(shù)據(jù)通過.populate(["category", "article"])實(shí)現(xiàn)

router.get("/user", function (req, res, next) {
/* 從數(shù)據(jù)庫中讀取數(shù)據(jù),每頁展現(xiàn)的數(shù)據(jù)數(shù)量相同,內(nèi)容不相同
 *   1、limit()約束限制取出數(shù)據(jù)的條數(shù)
 *   2、skip()約束限制開始取數(shù)據(jù)的位置,skip(2)表示忽略前兩天數(shù)據(jù),從第三條開始取
 *   3、每頁顯示4條
 *     第1頁: skip(0)  --> 當(dāng)前頁 - 1 * limit
 *     第2頁: skip(4)
 *     第3頁: skip(8)
 */
var page = req.query.page || 1;   // 如果用戶不傳,默認(rèn)為第1頁
var limit = 10;
var pages = 0;   // 保存總的頁數(shù)

User.count().then(function (count) {
  pages = Math.ceil(count / limit);
  // 當(dāng)前頁數(shù)不能大于總頁數(shù)pages
  page = Math.min(page, pages);
  // 當(dāng)前頁數(shù)不能小于1
  page = Math.max(1, page);

  var skip = (page - 1) * limit;   // 計(jì)算page之后,確定需要skip的個(gè)數(shù)

  User.find().limit(limit).skip(skip).then(function (users) {
res.render("admin/user_index", {
  userInfo: req.userInfo,
  users: users,

  count: count,   // 總的數(shù)據(jù)條數(shù)
  pages: pages,   // 總頁數(shù)
  limit: limit,   // 每頁顯示幾條數(shù)據(jù)
  page: page     // 當(dāng)前頁數(shù)
});
  })
})
});

2.4 分類信息的添加

構(gòu)建分類信息的數(shù)據(jù)結(jié)構(gòu)和模型

var mongoose = require("mongoose");
var Schema = mongoose.Schema;

// categoriesSchema代表名為用戶的collection集合
var categoriesSchema = new Schema({
  // 每個(gè)屬性代表collection中的每個(gè)document
  // 分類名稱
  name: String
});

// 對外導(dǎo)出定義的用戶的collection結(jié)構(gòu)
module.exports = categoriesSchema;
var mongoose = require("mongoose");
var categoriesSchema = require("../schemas/categories");

var Category = mongoose.model("Category", categoriesSchema);

module.exports = Category;

完成get路由獲取展示分類的頁面(包括修改和刪除分類的入口):通過數(shù)據(jù)庫查詢獲取所有的分類信息,同樣利用limit()skip()實(shí)現(xiàn)分頁

Category.find().sort({_id: -1}).limit(limit).skip(skip).then(function (categories){...}

完成get路由獲取分類添加的頁面,利用post請求提交數(shù)據(jù)

后端通過post路由獲取添加分類的數(shù)據(jù),進(jìn)行基本驗(yàn)證與數(shù)據(jù)庫查重:如果通過,則保存數(shù)據(jù);否則返回錯(cuò)誤信息。利用findOne()方法查詢一條記錄;

new Model().save()方法保存數(shù)據(jù)

// 分類添加的數(shù)據(jù)提交后保存
router.post("/category/add", function (req, res, next) {
  // 如果用戶沒有輸入數(shù)據(jù),或提交的數(shù)據(jù)不符合需求的格式
  // 沒有使用Ajax,所以不符合時(shí)直接跳轉(zhuǎn)到另外一個(gè)錯(cuò)誤頁面
  var category = req.body.category || "";
  if(!req.body.category) {    // 如果名稱為空,跳轉(zhuǎn)到錯(cuò)誤頁面
    res.render("admin/error", {
      userInfo: req.userInfo,
      message: "分類名稱不能為空"
    });
    return;
  }
  // 數(shù)據(jù)庫中是否已經(jīng)存在相同的分類名稱
  Category.findOne({name: category}).then(function (rs) {
    // 數(shù)據(jù)庫中已經(jīng)存在該分類
    if(rs) {
      res.render("admin/error", {
        userInfo: req.userInfo,
        message: "分類已經(jīng)存在"
      });
      return Promise.reject();   // 退出異步執(zhí)行
    } else {
      // 數(shù)據(jù)庫中不存在該分類,創(chuàng)建Category的實(shí)例對象保存到數(shù)據(jù)庫
      return new Category({
        name: category
      }).save();
    }
  }).then(function (newCategory) {
    res.render("admin/success", {   // 渲染分類成功的頁面
      userInfo: req.userInfo,
      message: "分類保存成功",
      url: "/admin/category"
    });
  });
});

2.5 分類信息的修改與刪除

通過分類信息展示頁的入口,通過get請求完成分類修改頁面還原;再利用post請求將修改的數(shù)據(jù)進(jìn)行更新

前端通過url傳入修改和刪除分類的_id

通過數(shù)據(jù)庫查詢到該條記錄,返回原有數(shù)據(jù),渲染到編輯分類頁面,展示原來的分類名稱

分類修改后,利用post請求上傳數(shù)據(jù):_id和修改內(nèi)容

后臺(tái)拿到數(shù)據(jù)后,先進(jìn)行基本驗(yàn)證;數(shù)據(jù)庫驗(yàn)證(查詢分類名是否已經(jīng)存在)

  Category.findOne({
      id: {$ne: id},    // 不同的記錄中是否存在相同的分類名稱
      name: nameCategory
    })

如果分類名不存在,再利用update()更新本條數(shù)據(jù)

// 分類信息修改保存
router.post("/category/edit", function (req, res, next) {
// 獲取要修改的分類信息
var id = req.query.id;
// 獲取post請求提交的分類名稱數(shù)據(jù)
var nameCategory = req.body.category;
// 查看提交的分類名稱數(shù)據(jù)是否存在
Category.findOne({
  _id: id
}).then(function (category) {
  if(!category) {
res.render("admin/error", {
  userInfo: req.userInfo,
  message: "分類信息不存在"
});
return Promise.reject();
  } else {
  // 判斷用戶是否做了修改
  if(nameCategory === category.name) {  // 如果沒有修改,直接提示修改成功,跳轉(zhuǎn)到首頁
    res.render("admin/success", {
      userInfo: req.userInfo,
      message: "修改成功",
      url: "/admin/category"
    });
    return Promise.reject();
  } else {
    // 判斷添加的分類名稱是否已經(jīng)存在
    Category.findOne({
      id: {$ne: id},    // 不同的記錄中是否存在相同的分類名稱
      name: nameCategory
    }).then(function (sameCategory) {
      if(sameCategory) {
        res.render("admin/error", {
          userInfo: req.userInfo,
          message: "分類名稱已經(jīng)存在"
        });
        return Promise.reject();
      } else {    // 如果不重復(fù),則保存改的數(shù)據(jù)
        Category.update({_id: id}, {name: nameCategory}).then(function () {
          res.render("admin/success", {
            userInfo: req.userInfo,
            message: "修改成功",
            url: "/admin/category"
          });
        })
      }
    })
  }
  }
})
});

數(shù)據(jù)庫的刪除只需使用remove({_id: id})即可

router.get("/category/delete", function (req, res, next) {
// 獲取要?jiǎng)h除分類的id
var id = req.query.id;
Category.remove({_id: id}).then(function () {
  res.render("admin/success", {
userInfo: req.userInfo,
message: "刪除成功",
url: "/admin/category"
  });
})
});

2.6 文章管理

文章管理的實(shí)現(xiàn)邏輯與分類管理基本一致:

完成文章管理的列表展示頁,有添加文章的入口

// 文章首頁
router.get("/article", function (req, res, next) {

// 從數(shù)據(jù)庫獲取文章的內(nèi)容
var page = req.query.page || 1;
var limit = 10;
var pages = 0;

// 分類管理時(shí),常識(shí)應(yīng)該講新添加的分類放在最前面,所以展示時(shí)應(yīng)該降序從數(shù)據(jù)庫中讀取數(shù)據(jù)
Article.count().then(function (count) {
  pages = Math.ceil(count / limit);
  page = Math.min(page, pages);
  page = Math.max(1, page);

  var skip = (page - 1) * limit;
  // sort()約束有兩個(gè)值:1表示升序;-1表示降序
  Article.find().sort({_id: -1}).limit(limit).skip(skip).populate(["category", "user"]).then(function (articles) {
console.log(articles);
res.render("admin/article_index", {
  userInfo: req.userInfo,
  articles: articles,
  pages: pages,
  count: count,
  limit: limit,
  page: page
});
  });
});
});

完成文章添加頁的表單,通過post提交填寫的數(shù)據(jù)

router.get("/article/add", function (req, res, next) {
// 從服務(wù)器中讀取所有分類信息
Category.find().sort({_id:-1}).then(function (categories) {
  res.render("admin/article_add", {
userInfo: req.userInfo,
categories: categories
  });
});
});

后端解析獲取文章的數(shù)據(jù),進(jìn)行基本驗(yàn)證,通過后進(jìn)行數(shù)據(jù)保存

// 文章內(nèi)容保存路由
router.post("/article/add", function (req, res, next) {
var categoryId = req.body.category;
var description = req.body.description;
var title = req.body.title;
var article = req.body.article;

// console.log(categoryId, description, title, article);
// 基本驗(yàn)證,分類、標(biāo)題、簡介、內(nèi)容不能為空
if(!categoryId) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章分類不能為空"
  });
  return;
}
if(!title) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章標(biāo)題不能為空"
  });
  return;
}
if(!description) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章簡介不能為空"
  });
  return;
}
if(!article) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章內(nèi)容不能為空"
  });
  return;
}

// 保存數(shù)據(jù)到數(shù)據(jù)庫
return new Article({    // 利用Model創(chuàng)建一個(gè)實(shí)例對象,利用save()方法保存數(shù)據(jù)
  category: categoryId,
  user: req.userInfo._id.toString(),
  title: title,
  description: description,
  article: article
}).save().then(function () {
  res.render("admin/success", {
userInfo: req.userInfo,
message: "文章添加成功",
url: "/admin/article"
  });
})
});

文章的修改有兩個(gè)步驟:首先是獲取文章的編輯頁,將原來的內(nèi)容渲染到頁面上,編輯修改后,將數(shù)據(jù)提交到后臺(tái);后臺(tái)完成基本驗(yàn)證后,更新數(shù)據(jù)庫中的內(nèi)容

// 文章內(nèi)容修改的數(shù)據(jù)提交
router.post("/article/edit", function (req, res, next) {
// 獲取文章的id
var id = req.query.id;
var category = req.body.category;
var title = req.body.title;
var description = req.body.description;
var article = req.body.article;

if(!category) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章分類不能為空"
  });
  return;
}
if(!title) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章標(biāo)題不能為空"
  });
  return;
}
if(!description) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章簡介不能為空"
  });
  return;
}
if(!article) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "文章內(nèi)容不能為空"
  });
  return;
}

Article.update({_id: id}, {
  category: category,
  description: description,
  title: title,
  article: article
}).then(function () {
  res.render("admin/success", {
userInfo: req.userInfo,
message: "內(nèi)容修改成功",
url: "/admin/article"
  })
});
});

文章的刪除與分類刪除一致:使用remove()方法

router.get("/article/delete", function (req, res, next) {
var id = req.query.id || "";
if(!id) {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "指定 文章不存在"
  })
  return;
}
Article.remove({_id: id}).then(function () {
  res.render("admin/error", {
userInfo: req.userInfo,
message: "刪除成功",
url: "/admin/article"
  });
})
});

2.7 評論的管理

評論可以多帶帶存放在一個(gè)coolection中,便于各種操作管理,通用性強(qiáng),可以編輯和刪除;增加復(fù)雜度

將評論存儲(chǔ)為文章的一個(gè)字段,與一片文章綁定在一起,操作簡便,但是編輯與刪除功能很難實(shí)現(xiàn)

3 前臺(tái)展示

通過后端將數(shù)據(jù)返回之后,服務(wù)器端一般將數(shù)據(jù)構(gòu)造為JSON格式,便于操作,可以利用后端模板或者前端操作DOM的方式將數(shù)據(jù)添加到頁面。

前端渲染:可以減輕服務(wù)器端的開銷,但是首屏的渲染會(huì)加長時(shí)間

后端渲染:增加服務(wù)器的開銷,但是減少客戶端展示的時(shí)間

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86955.html

相關(guān)文章

  • NodeJS+Express搭建個(gè)人博客-環(huán)境搭建(一)

    摘要:本項(xiàng)目持續(xù)更新中,開源免費(fèi)與各位愛好技術(shù)達(dá)人共勉,注現(xiàn)階段仍在開發(fā)中。。。。。 NodeJS+Express+MongoDb開發(fā)的個(gè)人博客 NodeJS+Express搭建個(gè)人博客-環(huán)境搭建(一)NodeJS+Express搭建個(gè)人博客-gulp自動(dòng)化構(gòu)建工具使用(二)NodeJS+Express搭建個(gè)人博客-Express+Mongodb組合架構(gòu)介紹(三)NodeJS+Express...

    Clect 評論0 收藏0
  • VueCli+Node+mongodb打造個(gè)人博客(含前臺(tái)展示及后臺(tái)管理系統(tǒng))(上)

    摘要:前言學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講項(xiàng)目地址效果后臺(tái)管理系統(tǒng)前端頁面架構(gòu)可以看到,在整個(gè)項(xiàng)目中,沒有頁面的跳轉(zhuǎn)只有前后端的數(shù)據(jù)交換,所有的頁面更新都是組件更新和數(shù)據(jù)更新后端只對數(shù) 前言 學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講 項(xiàng)目github地址:https://git...

    不知名網(wǎng)友 評論0 收藏0
  • VueCli+Node+mongodb打造個(gè)人博客(含前臺(tái)展示及后臺(tái)管理系統(tǒng))(上)

    摘要:前言學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講項(xiàng)目地址效果后臺(tái)管理系統(tǒng)前端頁面架構(gòu)可以看到,在整個(gè)項(xiàng)目中,沒有頁面的跳轉(zhuǎn)只有前后端的數(shù)據(jù)交換,所有的頁面更新都是組件更新和數(shù)據(jù)更新后端只對數(shù) 前言 學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講 項(xiàng)目github地址:https://git...

    tinyq 評論0 收藏0
  • VueCli+Node+mongodb打造個(gè)人博客(含前臺(tái)展示及后臺(tái)管理系統(tǒng))(上)

    摘要:前言學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講項(xiàng)目地址效果后臺(tái)管理系統(tǒng)前端頁面架構(gòu)可以看到,在整個(gè)項(xiàng)目中,沒有頁面的跳轉(zhuǎn)只有前后端的數(shù)據(jù)交換,所有的頁面更新都是組件更新和數(shù)據(jù)更新后端只對數(shù) 前言 學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講 項(xiàng)目github地址:https://git...

    kidsamong 評論0 收藏0
  • VueCli+Node+mongodb打造個(gè)人博客(含前臺(tái)展示及后臺(tái)管理系統(tǒng))(上)

    摘要:前言學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講項(xiàng)目地址效果后臺(tái)管理系統(tǒng)前端頁面架構(gòu)可以看到,在整個(gè)項(xiàng)目中,沒有頁面的跳轉(zhuǎn)只有前后端的數(shù)據(jù)交換,所有的頁面更新都是組件更新和數(shù)據(jù)更新后端只對數(shù) 前言 學(xué)習(xí)前端也有一段時(shí)間了做個(gè)個(gè)人博客網(wǎng)站吧正好總結(jié)練習(xí)一下這段時(shí)間的所學(xué)文章很長,會(huì)拆成三篇來講 項(xiàng)目github地址:https://git...

    xcold 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<