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

前言

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

正文

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

這裏是關鍵, 一定要引起大家的興趣, 不然很難進行。

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

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

前端想要的代碼

{
 name:1
}

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

②: 設想一個場景, 我想要通過文章的id獲取作者信息, 再通過作者信息裏面的作者id請求他其他的作品列表, 那麼我就需要兩步才能完成, 但是如果這兩個請求在服務端完成那麼我們只用寫一個請求, 而且還不用找後端同學寫新接口。

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

二. 原生GraphQL嚐鮮。

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

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"}};

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

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類型, 意思是單個類型

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 很貼心的起一個服務配置好後效果如下:左邊是輸入, 右邊是返回的結果

[圖片上傳失敗…(image-4141e1-1593261952584)]

四. 多層對象定義

我們返回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的值時:
[圖片上傳失敗…(image-746434-1593261952583)]

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

[圖片上傳失敗…(image-ca0147-1593261952583)]

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

五. 數組

定義起來會有些特殊

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裏面定義的返回, 不然會報錯.

結果如下:
[圖片上傳失敗…(image-6f97c5-1593261952583)]

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

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. 既然說了要傳參, 那就必須傳參不然會報錯

[圖片上傳失敗…(image-f8ed0b-1593261952583)]

因爲左側的參數是要放在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.

效果如下:
image.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與提示信息
image.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如圖:
image.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 使我們接下來要寫的一個操作模塊.

image.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類來做的

實際效果如圖所示:

image.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

說了這麼多也是因爲學習期間遇到了好多坑, 不管怎麼樣最後還是可以使用起來了, 在之後的使用過程中還是會不斷的總結並記錄, 遇到問題我們可以一起討論.
希望和你一起進步.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章