記一次GraphQL真正的詳細入門(原生,koa2,vue中的實戰, 建議收藏)分享會

記錄了組內技術分享會, 有一樣需求的同窗能夠參考一下
分享全程下來時間大約 55分鐘

前言

痛點:網上找的資料,文章, GraphQL的官網,一看就是很‘自我’的大神寫的(太爛了)徹底管讀者能不能看懂,舉例子只講用法!不告訴代碼怎麼實現的(可是當你學完這一篇你就能夠看懂了), 而且歷來不曬出總體代碼,致使根本不知道他們怎麼玩的,有被冒犯到!!能夠說那些資料都不適合入門。
定位:GraphQL並非必須用的技術, 但它是必須會的技術,之因此說它必會是由於能夠靠它爲‘前端’這一行業佔領更大的‘領地’, 同時它的思想是值得琢磨與體會的。
是啥:他不是json更不是js, 他是GraphQL自身的語法, 兼容性很是好.
選擇:GraphQL爲了使開發更高效、簡潔、規範而生,若是對工程對團隊形成了負擔能夠果斷捨棄(別猶豫,這小子不是必需品),畢竟服務端多一層處理是會消耗性能的,那麼就要理智計算他的「損失收益比」了。
前提:學習這門技術須要有必定的「先後交互」的知識儲備, 好比會node, 這裏會介紹如何在node端使用, 如何集成入koa2項目裏面使用, 而且會捨棄一些你們都用的技術, 不作跟風黨。

正文

一. GraphQL到底幹啥的?用它有啥好處哦?

這裏是關鍵, 必定要引發你們的興趣, 否則很難進行。

①: 嚴格要求返回值
好比下面是後端返回的代碼前端

{
  name:1,
  name:2,
  name:3,
 }

前端想要的代碼vue

{
 name:1
}

從上面能夠看出, name2 與 name3 其實我不想要, 那你傳給我那麼多數據幹什麼,單純爲了浪費帶寬嗎?可是吧。。也可理解爲某些場景下確實很雞肋,可是請求多了效果就厲害了。node

②: 設想一個場景, 我想要經過文章的id獲取做者信息, 再經過做者信息裏面的做者id請求他其餘的做品列表, 那麼我就須要兩步才能完成, 可是若是這兩個請求在服務端完成那麼咱們只用寫一個請求, 並且還不用找後端同窗寫新接口。ios

③: 控制默認值: 好比一個做品列表的數組, 沒有做品的時候後端很肯能給你返的是null, 可是咱們更想保證一致性但願返回[],這個時候能夠用GraphQL進行規避.git

二. 原生GraphQL嚐鮮。

隨便建個空白項目npm install graphql.
入口路徑以下src->index.jsgithub

var { graphql, buildSchema } = require('graphql');

// 1: 定義模板/映射,  有用mongoose操做數據庫經驗的同窗應該很好理解這裏
var schema = buildSchema(`
  type Query {
    # 我是備註, 這裏的備註都是單個井號;
    hello: String
    name: String
  }
`);

// 2: 數據源,能夠是熱熱乎乎從mongodb裏面取出來的數據
var root = { 
  hello: () => 'Hello!',
  name:'金毛cc',
  age:5
};

// 3: 描述語句, 我要取什麼樣的數據, 我想要hello與name 兩個字段的數據, 其餘的不要給我
const query =  '{ hello, name }'

// 4: 把準備好的食材放入鍋內, 模型->描述->整體的返回值
graphql(schema, query, root).then((response) => {
  console.log(JSON.stringify(response));
});

上面的代碼直接 node就能夠,結果以下: {"data":{"hello":"Hello!","name":"金毛cc"}};web

逐一攻克
1: buildSchema 創建數據模型mongodb

var schema = buildSchema(
// 1. type: 指定類型的關鍵字
// 2. Query: 你能夠理解爲返回值的固定類型
// 3. 他並非json,他是graphql的語法, 必定要注意它沒有用','
// 4. 返回兩個值, 而且值爲字符串類型, 注意: string小寫會報錯
` type Query {
    hello: String
    name: String
  }
`);

GraphQL 中內置有一些標量類型 String、 Int、 Float、 Boolean、 ID,這幾種都叫scalar類型, 意思是單個類型vuex

2: const query = '{ hello, name }'
作外層{}基本不變, hello的意思就是我要這一層的hello字段, 注意這裏用','分割, 以後會把這個','優化掉.數據庫

到這裏一個最基本的例子寫出來了, 感受也沒啥是吧, 我當時學到這裏也感受會很順利, 可是... 接下來文章斷層, 官網表達不清, 社區不完善等等問題克服起來好心酸.

三. 利用庫更好的原生開發

畢竟這樣每次node命令執行不方便, 而且結果出如今控制檯裏也很差看, 因此咱們要用一個專業工具'yoga'.

yarn add graphql-yoga

const { GraphQLServer } =  require('graphql-yoga');

// 類型定義 增刪改查
const typeDefs = `
  type Query{
    hello: String! #必定返回字符串
    name: String
    id:ID!
  }
`
const resolvers = {
  Query:{
    hello(){
      return '我是cc的主人'
    },
    name(){
      return '魯魯'
    },
    id(){
      return 9
    },
  }
}
const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(()=>{
  console.log('啓動成功, 默認是4000')
})

固然最好用nodemon來啓動文件 npm install nodemon -g

  1. hello: String! 像這種加了個'!'就是必定有值的意思, 沒值會報錯.
  2. Query 這裏定義的返回值, 對應函數的返回值會被執行.
  3. new GraphQLServer 的傳參 定義的數據模型, 返回值, 由於具體的請求語句須要咱們在web上面輸入.
  4. id的類型使用ID這個會把id轉換爲字符串,這樣設計也許是爲了兼容全部形式的id.
  5. server.start 很貼心的起一個服務配置好後效果以下:左邊是輸入, 右邊是返回的結果

jm.png

四. 多層對象定義

咱們返回data給前端,基本都會有多層, 那麼定義多層就要有講究了

const {GraphQLServer} =  require('graphql-yoga');
const typeDefs = `
  type Query{
     me: User!  # 這裏把me這個key對應的value定義爲User類型, 而且必須有值
  }
  type User { # 首字母必須大寫
    name:String
  }
`
const resolvers = {
  Query:{
    me(){
      return {
        id:9,
        name:'lulu'
      }
    }
    
  }
}
const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(()=>{
  console.log('啓動成功, 默認是4000')
})
  1. User類型不是原生自帶 , 因此咱們要本身用type關鍵字定義一個User數據類型.(首字母必須大寫)
  2. Query裏面return的值, 必須知足User類型的定義規則

當咱們取name的值時:
id.png

我剛纔故意在返回值裏面寫了id, 那麼能夠取到值麼?

did.png

結論: 就算數據裏面有, 可是類型上沒有定義, 那麼這個值就是取不到的.

五. 數組

定義起來會有些特殊

const { GraphQLServer } = require('graphql-yoga');

const typeDefs = `
  type Query {
    # 返回是數組
    arr:[Int!]! 
  }
`
const resolvers = {
  Query: {
    arr() {
      return [1, 2, 3, 4]
    }
  }
}

const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(() => {
  console.log('啓動成功, 默認是4000')
})
  1. arr:[Int!] 若是寫成 arr:[] 會報錯, 也就是說必須把數組裏面的類型定義徹底.
  2. Query裏面的返回值必須嚴格按照type裏面定義的返回, 否則會報錯.

結果以下:
arr.png

六. 傳參(前端能夠傳參數,供給服務端函數的執行)這個思路很神奇吧.

const { GraphQLServer } = require('graphql-yoga');

const typeDefs = `
  type Query{
     greeting(name: String):String # 須要傳參的地方, 必須在這裏定義好
     me: User!
  }
  type User { # 必須大寫
    name:String
  }
`
const resolvers = {
  Query: {
    // 四個參數大有文章
    greeting(parent, args, ctx, info) {
      return '默認值' + args.name
    },
    me() {
      return {
        id: 9,
        name: 'lulu'
      }
    }

  }
}

const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(() => {
  console.log('啓動成功, 默認是4000')
})
  1. greeting(name: String):String greeting是key沒的說, 他接收一個name參數爲字符串類型, 這裏必須指明參數名字, 返回值也必須是字符串類型, 也就是greeting是一個字符串.
  2. greeting(parent, args, ctx, info) { 這裏咱們用到 args也就是參數的集合是個對象, 咱們args.name就能夠取到name的值, 剩下的值後面用到會講.
  3. 既然說了要傳參, 那就必須傳參否則會報錯

changcan.png

由於左側的參數是要放在url請求上的, 因此要用雙引號;

七. 關聯關係

就像數據庫建表同樣, 咱們不可能把全部數據放在一張表裏, 咱們可能會用一個id來指定另外一張表裏面的某些值的集合.
const { GraphQLServer } = require('graphql-yoga');

const typeDefs = `
  type Query{
    lulu: User!
  }
  type User{
    name:String
    age: Int
    chongwu: Chongwu!
  }
  type Chongwu{
     name:String!
     age:Int
  }
`
// 自定義的數據
const chongwuArr = {
  1: {
    name: 'cc',
    age:8
  },
  2: {
    name: '芒果',
    age:6
  },
  9: {
    name: '芒果主人',
    age:24
  }
}

const resolvers = {
  Query: {
    lulu() {
      return {
        name: '魯路修',
        age: 24,
        chongwu: 9
      }
    },
  },
  // 注意, 它是與Query並列的
  User:{
    // 1: parent指的就是 user, 經過他來獲得具體的參數
    chongwu(parent,args,ctx,info){
        console.log('=======', parent.chongwu ) // 9
        return chongwuArr[parent.chongwu]
    }
  }
}

const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(() => {
  console.log('啓動成功, 默認是4000')
})

這裏數據量有點多, 我慢慢解析

  1. lulu屬於User類, User類裏面的chongwu(寵物)屬於Chongwu類, 咱們須要根據chongwu輸入的id 查詢出 展現哪一個寵物.
  2. 因爲這個寵物的列表可能來自外部, 因此他的定義方式須要與Query同級.
  3. parent 指的就是父級數據, 也就是經過他能夠獲取到輸入的id.

效果以下:
ddai.png

這裏就能夠解釋剛開始的一個問題, 就是那個經過文章id找到做者, 經過做者找到其餘文章的問題, 這裏的知識點就可讓咱們把兩個接口合二爲一, 或者合n爲一.

八. 不是獲取, 是操做.

有沒有發現上面我演示的都是獲取數據, 接下來咱們來講說操做數據, 也就是'增刪改'沒有'查'
graphql規定此類操做須要放在Mutation這個類裏面, 相似vuex會要求咱們按照他的規範進行書寫
const { GraphQLServer } = require('graphql-yoga');

const typeDefs = `
  type Query{
    hello: String!
  }
  # 是操做而不是獲取, 增刪改:系列
  type Mutation{
    createUser(name:String!, age:Int!):CreateUser

    # 這裏面能夠繼續書寫create函數...
  }

  type CreateUser{
    id:Int
    msg:String
  }
`

const resolvers = {
  Query: {
    hello() {
      return '我是cc的主人'
    },
  },
  // query並列
  Mutation: {
    createUser(parent, args, ctx, info) {
      const {name,age} = args;
      // 這裏咱們拿到了參數, 那麼就能夠去awit 建立用戶
       return {
         msg:'建立成功',
         id:999
       }
    }
  }
}



const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(() => {
  console.log('啓動成功, 默認是4000')
})
  1. Mutation是特殊類, 也是與Query並列.
  2. 一個Mutation裏面能夠寫多個函數, 由於他是個集合.
  3. 爲函數的返回值也能夠定義類型

效果以下: 接收id與提示信息
creat.png

九. input特殊類型

const { GraphQLServer } = require('graphql-yoga');

const typeDefs = `
  type Query{
    hello: String!
  }
  # 是操做而不是獲取, 增刪改:系列
  type Mutation{
    # 這個data隨便叫的, 叫啥都行, 就是單獨搞了個obj包裹起來而已, 不咋地 
    createUser(data: CreateUserInput):CreateUser
  }

  type CreateUser{
    id:Int
    msg:String
  }
   
  # input 定義參數
  input CreateUserInput{
    # 裏面的類型只能是基本類型
    name: String!
    age:Int!
  }
`

const resolvers = {
  Query: {
    hello() {
      return '我是cc的主人'
    },
  },
  // query並列
  Mutation: {
    createUser(parent, args, ctx, info) {
      // **這裏注意了, 這裏就是data了, 而不是分撒開的了**
      const { data } = args;
      return {
        msg: '建立成功',
        id: 999
      }
    }
  }
}

const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(() => {
  console.log('啓動成功, 默認是4000')
})
  1. 把參數放在data裏面, 而後定義data的類
  2. 注意三個關鍵點的代碼都要改

效果與上面的沒區別, 只是多包了層data如圖:
data.png

這個只能說有利有弊吧, 多包了一層, 還多搞出一個類, 看似能夠封裝了實則'雞肋啊雞肋'

十. '更雞肋的'MutationType特殊類型

const {GraphQLServer} =  require('graphql-yoga');

// 很是雞肋, 這種事作不作, 該不應你作, 內心沒點數
const typeDefs = `
  type Query{
    hello: MutationType
  }

  enum MutationType{
    aaaa
    bbbb
    cccc
  }
`
const resolvers = {
  Query:{
    hello(){
      // 只能返回菜單裏面的內容, 這樣能夠保證不出格... p用
      return 'bbbb'
    },
  }
}

const server = new GraphQLServer({
  typeDefs,
  resolvers
})

server.start(()=>{
  console.log('啓動成功, 默認是4000')
})
  1. 我定義了一個MutationType的類, 限制只能用'aaa','bbb','ccc'中的一個字符串.
  2. 這不貓捉耗子麼? graphql自己定位不是幹這個事的, 這種事情交給統一的數據校驗模塊完成, 他作了校驗的話那麼其餘狀況他管無論? 關了又如何你又不改數據, 就會個報錯公雞想下蛋.
  3. 徹底不建議用這個, 當作瞭解, 具體的校驗模塊本身在中間件或者utils裏面寫.

十一. 集成進koa2項目

1. 終於到實戰了, 講了那麼多的原生就是爲了從最基本的技術點來理解這裏
2. 並不必定徹底使用graphql的規範, 徹底能夠只有3個接口用它
3. 咱們剛纔寫的那些type都是在模板字符串裏面, 因此確定有人要他模板拆解開, 以對象的形式去書寫才符合人類的習慣.

先創建一個koa的工程
// 若果你沒有koa的話, 建議你先去學koa, koa知識點比較少因此我暫時沒寫相應的文章.
koa2 graphqlx // main:工程名 不要與庫重名
npm install graphql koa-graphql koa-mount --save 大朗快把庫安裝好.

app.js文件裏面
const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')

////// 看這裏
const mount = require('koa-mount');
const graphqlHTTP = require('koa-graphql');
const GraphQLSchema=require('./schema/default.js');
//////

const index = require('./routes/index')
const users = require('./routes/users')

// error handler
onerror(app)

// middlewares
app.use(bodyparser({
  enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(logger())
app.use(require('koa-static')(__dirname + '/public'))

app.use(views(__dirname + '/views', {
  extension: 'pug'
}))

// 每個路徑, 對應一個操做
app.use(mount('/graphql', graphqlHTTP({
  schema: GraphQLSchema,
  graphiql: true // 這裏能夠關閉調試模式, 默認是false
})));

// logger
app.use(async (ctx, next) => {
  const start = new Date()
  await next()
  const ms = new Date() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

// routes
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())

// error-handling
app.on('error', (err, ctx) => {
  console.error('server error', err, ctx)
});

module.exports = app
  1. 我直接吧默認配置也粘進來了, 這樣能夠保證你拿走就用
  2. graphiql: true 這個時候開啓了調試模式 會出現下圖的調試界面, 默認是false
  3. mount 來包裹總體的路由
  4. graphqlHTTP 定義請求相關數據
  5. GraphQLSchema 使咱們接下來要寫的一個操做模塊.

scxs.png

這個畫面是否是似曾相識!

schema->default.js
const {
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLString,
  GraphQLSchema,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLInputObjectType,
} = require('graphql');

// id對應的詳情
let idArr = {
  1:{
    name:'我是id1',
    age:'19'
  },
  2:{
    name:'我是id2',
    age:'24'
  }
} 

// 定義id的類
let GID= new GraphQLObjectType({
  name: 'gid',
  fields: {
    name: { type: GraphQLString },
    age: { type: GraphQLString },
  }
})

// 參數類型 不太對
let cs = new GraphQLInputObjectType({
  name:'iddecanshu',
  fields: {
    id: { type: GraphQLString },
  }
})

//定義導航Schema類型
var GraphQLNav = new GraphQLObjectType({
  name: 'nav',
  fields: {
    cc:{ // 傳參
      type:GraphQLString,
      // args:new GraphQLNonNull(cs), // 1; 這種是錯的
      args:{
        data: {
          type:new GraphQLNonNull(cs), // 這種能夠用data爲載體了
        } 
      },
      // args:{ // 3:這種最好用了。。。
      //   id:{
      //     type:GraphQLString
      //   }
      // },
      resolve(parent,args){
        return '我傳的是' + args.data.id
      }
    },
    // greeting(name: String):String 
    title: { type: GraphQLString },
    url: { type: GraphQLString },
    id: {
      // type:GraphQLList(GID), // 這裏很容易忽略
      type:GraphQLNonNull(GID), // 反覆查找也沒有專門obj的 這裏用非空代替
      async resolve(parent,args){
        // console.log('wwwwwwwww', idArr[parent.id])
        // 這個bug我tm。。。。。
        // 須要是數組形式。。。。否則報錯
        // "Expected Iterable, but did not find one for field \"nav.id\".",
        // return [idArr[parent.id]];

        // 2: 更改類型後就對了
        return idArr[parent.id] || {}
      }
    },
  }
})

//定義根
var QueryRoot = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    navList: {
      type: GraphQLList(GraphQLNav),
      async resolve(parent, args) {
        var navList = [
          { title: 'title1', url: 'url1', id:'1' },
          { title: 'title2', url: 'url2', id:'2' }
        ]
        return navList;
      }
    }
  }
})

//增長數據
const MutationRoot = new GraphQLObjectType({
  name: "Mutation",
  fields: {
    addNav: {
      type: GraphQLNav,
      args: {
        title: { type: new GraphQLNonNull(GraphQLString) },
      },
      async resolve(parent, args) {
        return {
          msg: '插入成功'
        }
      }
    }
  }
})

module.exports = new GraphQLSchema({
  query: QueryRoot,
  mutation: MutationRoot
});

十二. koa2中的使用原理"逐句"解析

①引入
  1. 這個是原生自帶的, 好比咱們會把GraphQLID 這種象徵着單一類型的類單獨拿到.
  2. 定義type的方法也變成了 GraphQLObjectType這樣的實例化類來定義.
const {
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLString,
  GraphQLSchema,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLInputObjectType,
} = require('graphql');
②單一的類
  1. 咱們實例化GraphQLObjectType導出一個'type'
  2. 使用 type:GraphQLString的形式規定一個變量的類型
  3. name: 這裏的name能夠理解爲一個說明, 有時候能夠經過獲取這個值作一些事.
let GID= new GraphQLObjectType({
  name: 'gid',
  fields: {
    name: { type: GraphQLString },
    age: { type: GraphQLString },
  }
})
③ 定義根類
  1. fields必需要寫, 在它裏面才能夠定義參數
  2. GraphQLList意思就是必須爲數組
  3. type不能少, 裏面要規定好這組返回數據的具體類型
  4. resolve也是必須有的沒有會報錯, 而且必須返回值與type一致
var QueryRoot = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    navList: {
      type: GraphQLList(GraphQLNav),
      async resolve(parent, args) {
        var navList = [
          { title: 'title1', url: 'url1', id:'1' },
          { title: 'title2', url: 'url2', id:'2' }
        ]
        return navList;
      }
    }
  }
})

十三. koa2裏面的關聯關係與傳參

這裏的關聯關係是指, 以前咱們說過的 id 指向另外一個表
let GID= new GraphQLObjectType({
  name: 'gid',
  fields: {
    name: { type: GraphQLString },
    age: { type: GraphQLString },
  }
})
var GraphQLNav = new GraphQLObjectType({
  name: 'nav',
  fields: {
    cc:{
      type:GraphQLString,
      args:{
        data: 
          type:new GraphQLNonNull(cs), // 這種能夠用data爲載體了
        } 
      },
      resolve(parent,args){
        return '我傳的是' + args.data.id
      }
    },
    id: {
      type:GraphQLNonNull(GID),
      async resolve(parent,args){
        return idArr[parent.id] || {}
      }
    },
  }
})
  1. 上面cc這個變量比較特殊, 他須要args這個key來規範參數, 這裏能夠直接寫參也能夠像這裏同樣抽象一個類.
  2. id他規範了id對應的是一個對象, 裏面有name有age
  3. cc想要拿到傳參就須要args.data 由於這裏咱們用的input類來作的

實際效果如圖所示:

ccnz.png

十四. 對集成在koa2內的工程化思考(數據模型分塊)

1. 從上面那些例子裏面可看出, 咱們能夠用/api/blog這種路由path爲單位, 去封裝一個個的數據模型
2. 每一個模型裏面其實都須要操做數據庫
3. 說實話增長的代碼有點多, 這裏只演示了2個接口就已經這麼大了
4. 學習成本是不可忽略的, 並且這裏面各類古怪的語法報錯

十五. 前端的調用

這裏咱們以vue爲例
import axios from "axios"; 這個是前提
query= 這個是關鍵點, 咱們之後的參數都要走這裏

方式1(暴力調取)
created(){
    // 1: 查詢列表
    // ①: 必定要轉碼, 由於url上不要有{} 空格
    axios
      .get(
        "/graphql?query=%7B%0A%20%20navList%20%7B%0A%20%20%20%20title%0A%20%20%20%20url%0A%20%20%7D%0A%7D%0A"
      )
      .then(res => {
        console.log("返回值: 1", res.data);
      });
     }
方式2(封裝函數)
methods: {
    getQuery() {
      const res = `
        {
          navList {
            title
            url
            id {
              name
              age
            }
          }
        }`;
      return encodeURI(res);
    },
  },
  created() {
    axios.get(`/graphql?query=${this.getQuery()}`).then(res => {
      console.log("返回值: 2", res.data);
    });
  }
方式3(函數傳參)
methods: {
    getQuery2(id) {
      const res = `
        {
          navList {
            cc(data:{id:"${id}"})
            title
            url
            id {
              name
              age
            }
          }
        }`;
      return encodeURI(res);
    }
  },
  created() {
    axios.get(`/graphql?query=${this.getQuery2(1)}`).then(res => {
      console.log("返回值: 3", res.data);
    });
  }

十六. 前端插件的調研

  1. 一看前面的傳參方式就會發覺, 這確定不合理啊, 必定要把字符串解構出來.
  2. vue-apollo技術棧是當前比較主流的, 可是這個庫寫的太碎了, 而且配置起來還要更改我原本的代碼習慣.
  3. 又在github上面找了一些模板的庫, 可是並無讓我從書寫字符串的尷尬中解脫出來

因此暫時沒有找到我承認的庫, 固然了本身暫時也並不想把時間放在開發這個插件上.

十七. 我想要的插件什麼樣的

  1. 可使我,採用對象或者json的方式來定義query
  2. 不須要定義那麼多概念, 開箱即用, 我只須要那個核心的模板解決方案
  3. 不要改變我原本的代碼書寫方式, 好比說vue-apollo提供了新的請求方式, 但是個人項目都是axios, 我就是不想換 憑什麼要換

十八. 學習graphql有多少阻礙

  1. 網上的資料真的對初學者很不友好, 根本不舉例子, 致使每一個知識點都會我本身試了幾十次試出來的.
  2. 光學這一個技術不夠, 還要思考服務端的重構與新規範, 前端也要新規範.
  3. 這個技術也出來好幾年了, 可是社區真的不敢恭維, 因此啊仍是須要本身更多的精力投入進來.

十九. 此類技術趨勢的思考

  1. 前端工程師愈來愈不知足本身的代碼只在瀏覽器上運行了, 咱們也要參與到服務端交互的環節中.
  2. 身爲當今2020年的前端若是眼光還侷限在web端有可能會被後浪排在沙灘上了.
  3. 我的不太喜歡這種拋出不少新的知識點, 和架構體系的庫, 應該是越少的學習成本與代碼成本作到最多的事.
  4. 前端技術還在摸索階段, 也能夠說是擴展階段, 預計這幾年仍是會出現各類新的概念, 可是吧正所謂"合久必分,分久必合", 在不就的未來前端的範圍極可能從新劃定.

二十. end

說了這麼多也是由於學習期間遇到了好多坑, 無論怎麼樣最後仍是可使用起來了, 在以後的使用過程當中仍是會不斷的總結並記錄, 遇到問題咱們能夠一塊兒討論.但願和你一塊兒進步.

相關文章
相關標籤/搜索