主頁 > 企業開發 > Node.js 蠶食計劃(七)—— Koa + GraphQL + MongoDB + Vue 初體驗

Node.js 蠶食計劃(七)—— Koa + GraphQL + MongoDB + Vue 初體驗

2020-09-13 22:56:04 企業開發

首先需要搭建一個簡單的應用

前端部分不多贅述,如果確實沒接觸過 Vue 專案,可以參考我的《Vue 爬坑之路》系列

后端服務可以參考之前的文章《Node.js 蠶食計劃(六)—— MongoDB + Koa 入門》

完整的專案地址:https://github.com/wisewrong/Test-GraphQL-App,結合專案食用本文更香哦~

 

 

一、Mongoose

在上一篇文章《Node.js 蠶食計劃(六)》里,直接使用了 mongodb 中間件來連接資料庫,并嘗試著操作資料庫

但我們一般不會直接用 MongoDB 的原生函式來操作資料庫,Mongoose 就是一套操作 MongoDB 資料庫的介面

 

1. Schema 與 Model

Schema 是 Mongoose 的基礎,用來定義集合的資料模型,也就是傳統意義上的表結構

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// 影片資訊
const MovieSchema = new Schema({
  name: String,         // 影片名稱
  years: Number,        // 上映年代
  director: String,     // 導演
  category: [String],   // 影片型別
  comments: [           // 影評
    {
      author: String,
      createdAt: {
        type: Date,
        default: Date.now(),
      },
      updatedAt: {
        type: Date,
        default: Date.now()
      }
    }
  ],
});

module.exports = mongoose.model('Movie', MovieSchema);

上面的最后一行代碼,是基于定義好的 Schema 生成 Model,我們可以通過 Model 來操作資料庫

mongoose.model('ModelName', SchemaObj)

這里的 model() 方法可以接收兩個引數,第二個引數是創建好的 Schema 實體

第一個引數 ModelName 是資料庫中集合 (collection) 名稱的單數形式,Mongoose 會查找名稱為 ModelName 復數形式的集合

對于上例,Movie 這個 model 就對應資料庫中 movies 這個 collection,如果資料庫沒有對應的集合會自動創建

 

2. Model 的增刪改查

在 mongoose 中是通過操作 Model 來實作資料庫的增刪改查

< 新增 >

Model.create(data, callback)

< 查詢 >

// 回傳所有符合查詢條件 conditions 的資料
Model.find(conditions, callback);
// 回傳找到的第一個檔案
Model.findOne(conditions, callback);
// 只針對主鍵 _id 查詢
Model.findById('_id', callback);

< 修改 >

// 批量修改符合條件 conditions 的資料
Model.updateMany(conditions, update, options, callback)
// 修改指定 id 的資料
Model.findByIdAndUpdate(id, update, options , callback)
// 修改第一個符合查詢條件的資料
Model.updateOne(conditions, update, options , callback)
// 替換第一個符合查詢條件的資料
Model.replaceOne(conditions, update, options , callback)

< 洗掉 >

// 洗掉符合條件的所有資料
Model.remove(conditions, callback);
// 洗掉指定 id 的資料
Model.findByIdAndRemove(id, options, callback);

比如封裝一個插入資料的方法:

const Movie = require('../mongodb/models/movie');

// 新建電影
const createMovie = (req) => {
  return Movie.create(req);
}

// 更新電影資訊
const updateMovie = (req) => {
  return Movie.findByIdAndUpdate(req._id, req, {
    new: true,
  });
}

// 保存電影
const saveMovie = async (ctx, next) => {
  const req = ctx.request.body;
  // 校驗必填
  if (!req.name) {
    return { message: '影片名稱不能為空' }
  }
  const data =https://www.cnblogs.com/wisewrong/p/ req._id
    ? await updateMovie(req)
    : await createMovie(req);
  return { data };
};

module.exports = {
  saveMovie,
};

mongoose 也有更規范的查詢條件,可以參考官網的 Query 配置

 

3. 連接資料庫

使用 mongoose.connect 連接資料庫,可以在 connect 方法中傳入第二個引數作為回呼

也可以通過 mongoose.connection.on 來監聽相應的事件 

/* /mongodb/index.js */

const mongoose = require("mongoose");
const { dbUrl } = require("../config");
// const dbUrl = 'mongodb://127.0.0.1:27017/Movie'; // 資料庫地址

const connect = () => {
  // mongoose.set('debug', true)
  mongoose.connect(dbUrl);

  mongoose.connection.on("disconnected", () => {
    mongoose.connect(dbUrl);
  });

  mongoose.connection.on("error", (err) => {
    console.error('Connect Failed: ', err);
  });

  mongoose.connection.on("open", async () => {
    console.log('?? Connecting MongoDB Successfully ??');
  });
};

 

4. 介面實作

基于這些 API,我們就可以搭建一個相對規范的傳統后端服務

首先創建 model,然后創建 controller,在 controller 中引入 model,并使用 model 來操作資料庫

然后還可以通過 koa-router 來實作傳統介面

/* /router/api/movie.js */

const router = require('koa-router')();
const { apiPrefix } = require('../../config');
// const apiPrefix = '/api';
const movieController = require('../../controllers/movie');

router.prefix(apiPrefix);

router.post('/movie/save', movieController.saveMovie);
router.get('/movie/list', movieController.getMovie);
router.delete('/movie/delete/:id', movieController.deleteMovie);

module.exports = router;

最后只要在 app.js 中引入相應模塊,一個簡單的傳統服務就搭建好了

// app.js
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const api = require('./router/api');

// 連接資料庫
require('./mongodb');

const app = new Koa();

app.use(bodyParser());

// 注冊 API
for (const key in api) {
  const router = api[key];
  app.use(router.routes()).use(router.allowedMethods());
}

app.listen({port: 3200});

但這樣的傳統服務,介面的出參都是由后端決定的

如果業務調整,介面出參需要新增一個欄位,就需要后端和前端同時迭代

而如果使用 GraphQL 的話,這種改動就不用后端的小伙伴參與了

 

 

二、GraphQL

GraphQL 是一種新的 API 定義和查詢語言,它使前端能夠宣告式地獲取資料,從一定程度上自定義介面出參

像上圖這樣,介面的回應會按照入參的結構回傳出參,概念性的優點就不多贅述,實際感受之后才能明白它的優勢

先在專案中引入 koa-graphql 和 GraphQL.js 備用

npm install graphql koa-graphql --save

 

在 GraphQL 中,Schema 是定義整個查詢語言的入口

schema {
  query: Query
  mutation: Mutation
}

Schema 有一個必須定義的 query 型別,用來執行查詢操作;還有一個可選的 mutation,處理增刪改操作

這兩種型別其實都是 graphql.GraphQLObjectType 型別

構建一個 Schema 可以使用 graphql.buildSchema 或者構建型別 graphql.GraphQLSchema,先介紹一下 buildSchema

const Schema = buildSchema(`
  type Query {
    getList: [Movie]
    getDetail: [Movie]
  }
  type Mutation {
    add(post: input): [Movie],
  }
`)

這里的 type Query 就是定義上面提到的 schema 中必須包含的 query 型別

需要注意的是,在型別下定義的欄位,并不是像 mongoose 中 schema 定義的檔案結構

這個欄位只是宣告一種型別,而型別的值取決于對應的 resolve 處理函式,所以將這個欄位當作查詢指令更便于理解

上面  Query.getList: [Movie] 表示通過 getList 指令能夠回傳一個陣列,陣列的每個元素是一個 Movie 型別,這個 Movie 是我們需要定義的另一個型別

/* /graphql/schema.js - 使用 buildSchema 創建的 GraphQL Schema */

const { buildSchema } = require('graphql');

const Schema = buildSchema(`
  type Query {
    getAllMovie: [Movie]
  }
  type Movie {
    _id: String,
    name: String,
    years: String,
    director: String,
  }
`)
// 暫時不用 Mutation

module.exports = Schema;

這樣就定義了一個包含 name、years 等四個欄位的 Movie 型別,一個簡單的 Schema 就定義好了

然后來改造 controllers,引入 koa-graphql、剛才定義的 Schema,以及之前用 mongoose 生成的 Model

/* /controllers/movie.js */

const graphqlHTTP = require('koa-graphql');
const MovieSchema = require('../graphql/schema');
const Movie = require('../mongodb/models/movie');

// GraphQL 型別處理函式
const root = {
  getAllMovie: async () => {
    return Movie.find({});
  }
}

// 查詢所有電影
const getMovie = graphqlHTTP({
  schema: MovieSchema,
  rootValue: root,
  graphiql: true
});

module.exports = {
  getMovie,
};

用 koa-graphql 提供的 graphqlHTTP 方法作為介面的 handler 函式,并傳入定義好的 schema

這里有一個 rootValue 物件,用來配置 schema 型別的具體操作函式,比如上面就定義了 getAllMovie 的操作函式

然后介面路徑還是按之前的方式配置:

/* /router/api/movie.js */

const router = require('koa-router')();
const movieController = require('../../controllers/movie');

router.all('/movie/list', movieController.getMovie);

module.exports = router;

一個簡單的 GraphQL 服務就完成了,接下來處理前端的請求

請求的時候需要攜帶 JSON 格式的引數,所以通常使用 post 請求

最主要的是,需要設定請求頭 'Content-Type': 'application/json'

然后按照 schema 的格式設定入參,比如查詢 schema 中 query 型別下的 getAllMovie:

request.post('/api/movie/list', {
  query: `{
    getAllMovie {
      _id,
      name,
    }
  }`
});

可以看到回應的結果為:

 我們在 GraphQL 中定義的 Movie 型別有 name 等四個欄位,但入參中只設定了 name 和 _id,所以出參也只有 name 和 _id

如果把入參也改為四個欄位:

 后端邏輯不用調整,請求結果就會變成:

 Cool~

 

 

 三、GraphQL 構建型別

上面的 Schema 是使用 buildSchema 定義的,但 buildSchema 接收的型別引數只能是一整個字串

如果我們復用某些自定義型別就不太方便,而且欄位的處理函式需要寫在 rootValue 里面,不方便模塊化管理

所以更推薦使用 GraphQLSchema 構建型別

const { GraphQLSchema, GraphQLObjectType } = require('graphql');

const schema = new GraphQLSchema({
  query: new GraphQLObjectType(),
  mutation: new GraphQLObjectType(),
});

GraphQLObjectType 是構建 Schema 型別的基本方法,包括 query 和 mutation 在內的所有型別都需要通過該建構式構建

我們先嘗試用構建型別的方式,來改寫將上面 buildSchema 定義的 Schema

/* /graphql/schema.js - 構建型別 */

const { GraphQLSchema, GraphQLObjectType } = require('graphql');
const getAllMovie = require('./query/movie.js');

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    getAllMovie,
  }
});

module.exports = new GraphQLSchema({
  query: RootQuery,
  // mutation: RootMutation,
});

這里定義了一個 RootQuery 型別,對應的是之前的:

這里的 getAllMovie 是由 Movie 型別組成的陣列,需要另外構建:

/* /graphql/types/movies.js - 定義 Movie 型別 */
const graphql = require('graphql');

const { 
  GraphQLObjectType, 
  GraphQLList, 
  GraphQLString, 
  GraphQLInt,
} = graphql;

const MovieType = new GraphQLObjectType({
  name: 'Movie',
  fields: () => ({
    _id: { type: GraphQLString }, // String
    name: { type: GraphQLString }, 
    years: { type: GraphQLInt },  // Int
    poster: { type: GraphQLString },
    director: { type: GraphQLString },
    category: { type: new GraphQLList(GraphQLString) }, // [String]
  })
});

module.exports = MovieType;

在定義 Movie 型別下的具體欄位 fields 的時候,需要通過物件的形式規定型別 type

這里的 type 不能像之前那樣直接寫 String、Boolean,而是使用 graphql 中提供的型別物件

 

現在定義好了 Movie 型別,但是 getAllMovie 回傳的是 Movie 型別組成的陣列,還有一個對應的處理函式,所以我們要單獨維護一個 getAllMovie 物件

/* /graphql/query/movies.js - 定義 getAllMovie 欄位 */

const { GraphQLList } =  require('graphql');
const movieGraphQLType = require('../types/movie.js');
const Movie = require('../../mongodb/models/movie.js');

module.exports = {
  type: new GraphQLList(movieGraphQLType),
  args: {},
  resolve() {
    return Movie.find({})
  }
}

注意我們匯出的物件包含 type、args、resolve 三個欄位,而我們剛才定義 Movie 型別的時候,fields 欄位物件也包含一個 type 欄位

沒錯,這里匯出的物件其實就一個 field,而每個 field 都可以包含 type、args、resolve

其中 type 不用再提,resolve 就是該欄位對應的處理函式,對應上面 buildSchema 小節中 rootValue 中的欄位

args 用來描述 resolve 方法接收的引數,在后面介紹 mutation 的時候會介紹


由于每個 filed 都可以是一個獨立的型別,而每個型別可以配置自己的 resolve 處理函式,所以在 GraphQL 可以很方便的執行復雜查詢

只要在回應的型別中配置好 resolve,前端只需要調一次介面就能獲取到多個檔案的資料

 

到此為止,我們已經完成了從 buildSchema 到構建型別的改造,由于在 field 欄位中定義了 resolve,所以就可以不用定義 rootValue 了

/* /controllers/movie.js */

const graphqlHTTP = require('koa-graphql');
const MovieSchema = require('../graphql/schema');// 查詢所有電影
const getMovie = graphqlHTTP({
  schema: MovieSchema,
  graphiql: true
});

module.exports = {
  getMovie,
};

 

 

四、使用 mutation 執行增刪改

上面提到了 args,它用來描述 resolve 方法的引數

首先來看一下前端怎么在 resolve 方法中傳參

看起來就和我們平時用的 function 一樣,但這里面大有玄機

首先如果引數是一個 String,就需要手動添加雙引號,而且只能是雙引號

如果用單引號會報錯(主要是為了避免文本中帶有單引號的情況 desc: "I'm Wise" )

Syntax Error: Unexpected single quote character ('), did you mean to use a double quote (")?

如果引數是 Int 型別,就不能添加引號  years: ${data.years}, 

如果引數是陣列型別,需要用 JSON.stringify 轉換

由于對引數型別的處理較為復雜,可以封裝一個處理引數的工具函式來統一處理

// 這只是我簡單嘗試之后的感想,如果小伙伴有更好的處理思路,一定要在評論區留言,感謝 ??

 

知道了怎么向 resolve 方法傳參(不只是 mutation,query 也可以傳參),再來說說 args:

它可以像定義 fields 一樣定義接收的引數,如果 args 里只寫了一個引數,而介面入參傳了入了多個,介面會回傳錯誤

如果入參傳的引數少了是可以的,只要必填項 GraphQLNonNull 沒落下

然后可以從 resolve 的第二個引數中獲取到前端傳過來的引數,再通過 Mongoose 生成的 Model 來操作資料 

需要注意的是,前端在發送 mutation 請求的時候,要在 query 中宣告 mutation

定義好了 mutation,按照之前構建 RootQuery 物件的方式構建 RootMutation,并賦值給 schema,一個具有基本功能的 GraphQL 服務就完成了

如果對專案結構還不太清晰,可以看一下專案倉庫:https://github.com/wisewrong/Test-GraphQL-App

再回頭捋一下,其實后端服務只定義了一個介面,而具體的操作都是在前端分工

這樣雖然增加了前端的作業量,但也增加了前端的靈活性,讓后端的小伙伴能專注于資料庫的設計和優化

其實 GraphQL 早在 2015 年就發布了,卻一直沒有推廣開,當時尤大大還做了一波分析

GraphQL 為何沒有火起來? - 尤雨溪的回答 

但時至今日,GraphQL 已經得到了廣泛認可,有許多大廠已經開始廣泛使用(比如 TX 的 CSIG)

特別是對于有全堆疊發展興趣的小伙伴,學一下 GraphQL 是很有必要的,這樣我就不至于只能看國外的文章來學 GraphQL 了

 

 

參考文章:

《你必須要懂得關于 mongoose 的一小小部分》

《Why GraphQL is the future》

《How to GraphQL》

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/28115.html

標籤:JavaScript

上一篇:實作一個演算法,確定一個字串 s 的所有字符是否全都不同

下一篇:vue引入新版vue-awesome-swiper報錯問題處理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more