GraphQL入門有

本文將從GraphQL是什么,為什么要使用GraphQL,使用GraphQL創建簡單例子,以及GraphQL實戰,四個方面對GraphQL進行闡述。說得不對的地方,希望大家指出斧正。

github項目地址:https://github.com/Charming2015/graphql-todolist

一、GraphQL是什么?


關于GraphQL是什么,網上一搜一大堆。根據官網的解釋就是一種用于 API 的查詢語言

一看到用于API的查詢語言,我也是一臉懵逼的。博主你在開玩笑吧?你的翻譯水平不過關?API還能查嗎?API不是后端寫好,前端調用的嗎?

的確可以,這就是GraphQL強大的地方。
引用官方文檔的一句話:

ask exactly what you want.

二、為什么要使用GraphQL?


在實際工作中往往會有這種情景出現:比如說我需要展示一個游戲名的列表,可接口卻會把游戲的詳細玩法,更新時間,創建者等各種各樣的 (無用的) 信息都一同返回。

問了后端,原因大概如下:

  • 原來是為了兼容PC端和移動端用同一套接口
  • 或者在整個頁面,這里需要顯示游戲的標題,可是別的地方需要顯示游戲玩法啊,避免多次請求我就全部返回咯
  • 或者是因為有時候項目經理想要顯示“標題+更新時間”,有時候想要點擊標題展開游戲玩法等等需求,所以把游戲相關的信息都一同返回

簡單說就是:

  • 兼容多平臺導致字段冗余
  • 一個頁面需要多次調用 API 聚合數據
  • 需求經常改動導致接口很難為單一接口精簡邏輯

有同學可能會說那也不一定要用GraphQL啊,比方說第一個問題,不同平臺不同接口不就好了嘛

http://api.xxx.com/web/getGameInfo/:gameID
http://api.xxx.com/app/getGameInfo/:gameID
http://api.xxx.com/mobile/getGameInfo/:gameID

或者加個參數也行

http://api.xxx.com/getGameInfo/:gameID?platfrom=web

這樣處理的確可以解決問題,但是無疑加大了后端的處理邏輯。你真的不怕后端程序員打你?

這個時候我們會想,接口能不能不寫死,把靜態變成動態?

回答是可以的,這就是GraphQL所做的!

三、GraphQL嘗嘗鮮——(GraphQL簡單例子)


下面是用GraphQL.jsexpress-graphql搭建一個的普通GraphQL查詢(query)的例子,包括講解GraphQL的部分類型和參數,已經掌握了的同學可以跳過。

1. 先跑個hello world

  1. 新建一個graphql文件夾,然后在該目錄下打開終端,執行npm init --y初始化一個packjson文件。
  2. 安裝依賴包:npm install --save -D express express-graphql graphql
  3. 新建schema.js文件,填上下面的代碼

//schema.js
const {GraphQLSchema,GraphQLObjectType,GraphQLString,} = require('graphql');
const queryObj = new GraphQLObjectType({name: 'myFirstQuery',description: 'a hello world demo',fields: {hello: {name: 'a hello world query',description: 'a hello world demo',type: GraphQLString,resolve(parentValue, args, request) {return 'hello world !';}}}
});
module.exports = new GraphQLSchema({query: queryObj
});

這里的意思是新建一個簡單的查詢,查詢名字叫hello,會返回字段hello world !,其他的是定義名字和查詢結果類型的意思。

  1. 同級目錄下新建server.js文件,填上下面的代碼

// server.js
const express = require('express');
const expressGraphql = require('express-graphql');
const app = express();const schema = require('./schema');
app.use('/graphql', expressGraphql({schema,graphiql: true
}));app.get('/', (req, res) => res.end('index'));app.listen(8000, (err) => {if(err) {throw new Error(err);}console.log('*** server started ***');
});

這部分代碼是用express跑起來一個服務器,并通過express-graphqlgraphql掛載到服務器上。

  1. 運行一下node server,并打開http://localhost:8000/

這里寫圖片描述

如圖,說明服務器已經跑起來了
打開http://localhost:8000/graphql,是類似下面這種界面說明已經graphql服務已經跑起來了!
這里寫圖片描述
在左側輸入 (graphql的查詢語法這里不做說明)

{hello
}

點擊頭部的三角形的運行按鈕,右側就會顯示你查詢的結果了
這里寫圖片描述

2. 不僅僅是hello world

先簡單講解一下代碼:

const queryObj = new GraphQLObjectType({name: 'myFirstQuery',description: 'a hello world demo',fields: {}
});

GraphQLObjectType是GraphQL.js定義的對象類型,包括namedescriptionfields三個屬性,其中namedescription 是非必填的。fields是解析函數,在這里可以理解為查詢方法

hello: {name: 'a hello world query',description: 'a hello world demo',type: GraphQLString,resolve(parentValue, args, request) {return 'hello world !';}}

對于每個fields,又有name,description,type,resolve參數,這里的type可以理解為hello方法返回的數據類型,resolve就是具體的處理方法。

說到這里有些同學可能還不滿足,如果我想每次查詢都想帶上一個參數該怎么辦,如果我想查詢結果有多條數據又怎么處理?

下面修改schema.js文件,來一個加強版的查詢(當然,你可以整理一下代碼,我這樣寫是為了方便閱讀)

const {GraphQLSchema,GraphQLObjectType,GraphQLString,GraphQLInt,GraphQLBoolean} = require('graphql');const queryObj = new GraphQLObjectType({name: 'myFirstQuery',description: 'a hello world demo',fields: {hello: {name: 'a hello world query',description: 'a hello world demo',type: GraphQLString,args: {name: {  // 這里定義參數,包括參數類型和默認值type: GraphQLString,defaultValue: 'Brian'}},resolve(parentValue, args, request) { // 這里演示如何獲取參數,以及處理return 'hello world ' + args.name + '!';}},person: {name: 'personQuery',description: 'query a person',type: new GraphQLObjectType({ // 這里定義查詢結果包含name,age,sex三個字段,并且都是不同的類型。name: 'person',fields: {name: {type: GraphQLString},age: {type: GraphQLInt},sex: {type: GraphQLBoolean}}}),args: {name: {type: GraphQLString,defaultValue: 'Charming'}},resolve(parentValue, args, request) {return {name: args.name,age: args.name.length,sex: Math.random() > 0.5};}}}
});module.exports = new GraphQLSchema({query: queryObj 
});

重啟服務后,繼續打開http://localhost:8000/graphql,在左側輸入

{hello(name:"charming"),person(name:"charming"){name,sex,age}
}

右側就會顯示出:
這里寫圖片描述

你可以在左側僅輸入person方法的sexage兩個字段,這樣就會只返回sexage的信息。動手試一試吧!

{person(name:"charming"){sex,age}
}

當然,結果的順序也是按照你輸入的順序排序的。

定制化的數據,完全根據你查什么返回什么結果。這就是GraphQL被稱作API查詢語言的原因。

四、GraphQL實戰


下面我將搭配koa實現一個GraphQL查詢的例子,逐步從簡單koa服務到mongodb的數據插入查詢,再到GraphQL的使用,最終實現用GraphQL對數據庫進行增刪查改。

項目效果大概如下:
這里寫圖片描述

有點意思吧?那就開始吧~
先把文件目錄建構建好

1. 初始化項目

  • 初始化項目,在根目錄下運行npm init --y
  • 然后安裝一些包:npm install koa koa-static koa-router koa-bodyparser --save -D
  • 新建configcontrollersgraphqlmongodbpublicrouter這幾個文件夾。裝逼的操作是在終端輸入mkdir config controllers graphql mongodb public router回車,ok~

2. 跑一個koa服務器
新建一個server.js文件,寫入以下代碼

// server.js
import Koa from 'koa'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'const app = new Koa()
const router = new Router();
const port = 4000app.use(bodyParser());router.get('/hello', (ctx, next) => {ctx.body="hello world"
});app.use(router.routes()).use(router.allowedMethods());app.listen(port);console.log('server listen port: ' + port)

執行node server跑起來服務器,發現報錯了:
這里寫圖片描述

這是正常的,這是因為現在的node版本并沒有支持es6的模塊引入方式。

百度一下就會有解決方案了,比較通用的做法是用babel-polyfill進行轉譯。

詳細的可以看這一個參考操作:How To Enable ES6 Imports in Node.JS

具體操作是:新建一個start.js文件,寫入:

// start.js
require('babel-register')({presets: [ 'env' ]
})
require('babel-polyfill')
require('./server.js')

安裝相關包:npm install --save -D babel-preset-env babel-polyfill babel-register

修改package.json文件,把"start": "start http://localhost:4000 && node start.js"這句代碼加到下面這個位置:
這里寫圖片描述

運行一下npm run start,打開http://localhost:4000/hello,結果如圖:
這里寫圖片描述
說明koa服務器已經跑起來了。

那么前端頁面呢?

(由于本文內容不是講解前端,所以前端代碼自行去github復制)

  • 在public下新建index.html文件和js文件夾,代碼直接查看我的項目public目錄下的 index.htmlindex-s1.js 文件
  • 修改server.js,引入koa-static模塊。koa-static會把路由的根目錄指向自己定義的路徑(也就是本項目的public路徑)

//server.js
import Koa from 'koa'
import Router from 'koa-router'
import KoaStatic from 'koa-static'
import bodyParser from 'koa-bodyparser'const app = new Koa()
const router = new Router();
const port = 4000app.use(bodyParser());router.get('/hello', (ctx, next) => {ctx.body="hello world"
});app.use(KoaStatic(__dirname + '/public'));
app.use(router.routes()).use(router.allowedMethods());app.listen(port);console.log('server listen port: ' + port)

打開http://localhost:4000/,發現是類似下面的頁面:
這里寫圖片描述

這時候頁面已經可以進行簡單的交互,但是還沒有和后端進行數據交互,所以是個靜態頁面。

3. 搭一個mongodb數據庫,實現數據增刪改查

注意: 請先自行下載好mongodb并啟動mongodb。

a. 寫好鏈接數據庫的基本配置和表設定

config文件夾下面建立一個index.js,這個文件主要是放一下鏈接數據庫的配置代碼。

//  config/index.js
export default {dbPath: 'mongodb://localhost/todolist'
}

mongodb文件夾新建一個index.jsschema文件夾, 在 schema文件夾文件夾下面新建list.js

mongodb/index.js下寫上鏈接數據庫的代碼,這里的代碼作用是鏈接上數據庫

// mongodb/index.js
import mongoose from 'mongoose'
import config from '../config'require('./schema/list')export const database = () => {mongoose.set('debug', true)mongoose.connect(config.dbPath)mongoose.connection.on('disconnected', () => {mongoose.connect(config.dbPath)})mongoose.connection.on('error', err => {console.error(err)})mongoose.connection.on('open', async () => {console.log('Connected to MongoDB ', config.dbPath)})
}

mongodb/schema/list.js定義表和字段:

//mongodb/schema/list.js
import mongoose from 'mongoose'const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectIdconst ListSchema = new Schema({title: String,desc: String,date: String,id: String,checked: Boolean,meta: {createdAt: {type: Date,default: Date.now()},updatedAt: {type: Date,default: Date.now()}}
})ListSchema.pre('save', function (next) {// 每次保存之前都插入更新時間,創建時插入創建時間if (this.isNew) {this.meta.createdAt = this.meta.updatedAt = Date.now()} else {this.meta.updatedAt = Date.now()}next()
})
mongoose.model('List', ListSchema)

b. 實現數據庫增刪查改的控制器

建好表,也鏈接好數據庫之后,我們就要寫一些方法來操作數據庫,這些方法都寫在控制器(controllers)里面。

controllers里面新建list.js,這個文件對應操作list數據的控制器,單獨拿出來寫是為了方便后續項目復雜化的模塊化管理。

// controllers/list.js
import mongoose from 'mongoose'
const List = mongoose.model('List')
// 獲取所有數據
export const getAllList = async (ctx, next) => {const Lists = await List.find({}).sort({date:-1}) // 數據查詢if (Lists.length) {ctx.body = {success: true,list: Lists}} else {ctx.body = {success: false}}
}
// 新增
export const addOne = async (ctx, next) => {// 獲取請求的數據const opts = ctx.request.bodyconst list = new List(opts)const saveList = await list.save() // 保存數據console.log(saveList)if (saveList) {ctx.body = {success: true,id: opts.id}} else {ctx.body = {success: false,id: opts.id}}
}
// 編輯
export const editOne = async (ctx, next) => {const obj = ctx.request.bodylet hasError = falselet error = nullList.findOne({id: obj.id}, (err, doc) => {if(err) {hasError = trueerror = err} else {doc.title = obj.title;doc.desc = obj.desc;doc.date = obj.date;doc.save();}})if (hasError) {ctx.body = {success: false,id: obj.id}} else {ctx.body = {success: true,id: obj.id}}
}// 更新完成狀態
export const tickOne = async (ctx, next) => {const obj = ctx.request.bodylet hasError = falselet error = nullList.findOne({id: obj.id}, (err, doc) => {if(err) {hasError = trueerror = err} else {doc.checked = obj.checked;doc.save();}})if (hasError) {ctx.body = {success: false,id: obj.id}} else {ctx.body = {success: true,id: obj.id}}
}// 刪除
export const delOne = async (ctx, next) => {const obj = ctx.request.bodylet hasError = falselet msg = nullList.remove({id: obj.id}, (err, doc) => {if(err) {hasError = truemsg = err} else {msg = doc}})if (hasError) {ctx.body = {success: false,id: obj.id}} else {ctx.body = {success: true,id: obj.id}}
}

c. 實現路由,給前端提供API接口

數據模型和控制器都已經設計好了,下面就利用koa-router路由中間件,來實現請求的接口。

我們回到server.js,在上面添加一些代碼。如下:

// server.js
import Koa from 'koa'
import Router from 'koa-router'
import KoaStatic from 'koa-static'
import bodyParser from 'koa-bodyparser'
import {database} from './mongodb' 
import {addOne, getAllList, editOne, tickOne, delOne} from './controllers/list' database() // 鏈接數據庫并且初始化數據模型const app = new Koa()
const router = new Router();
const port = 4000app.use(bodyParser());router.get('/hello', (ctx, next) => {ctx.body = "hello world"
});// 把對請求的處理交給處理器。
router.post('/addOne', addOne).post('/editOne', editOne).post('/tickOne', tickOne).post('/delOne', delOne).get('/getAllList', getAllList)app.use(KoaStatic(__dirname + '/public'));
app.use(router.routes()).use(router.allowedMethods());app.listen(port);console.log('server listen port: ' + port)

上面的代碼,就是做了:

1. 引入mongodb設置、list控制器,
2. 鏈接數據庫
3. 設置每一個設置每一個路由對應的我們定義的的控制器。

安裝一下mongoose:npm install --save -D mongoose

運行一下npm run start,待我們的服務器啟動之后,就可以對數據庫進行操作了。我們可以通過postman來模擬請求,先插幾條數據:
這里寫圖片描述
查詢全部數據:
這里寫圖片描述

d. 前端對接接口

前端直接用ajax發起請求就好了,平時工作中都是用axios的,但是我懶得弄,所以直接用最簡單的方法就好了。

引入了JQuery之后,改寫public/js/index.js文件:略(項目里的public/index-s2.js的代碼)

項目跑起來,發現已經基本上實現了前端發起請求對數據庫進行操作了。
至此你已經成功打通了前端后臺數據庫,可以不要臉地稱自己是一個小全棧了!

不過我們的目的還沒有達到——用grapql實現對數據的操作!

4. 用grapql實現對數據的操作

GraphQL 的大部分討論集中在數據獲取(query),但是任何完整的數據平臺也都需要一個改變服務端數據的方法。
REST 中,任何請求都可能最后導致一些服務端副作用,但是約定上建議不要使用 GET 請求來修改數據。GraphQL 也是類似 —— 技術上而言,任何查詢都可以被實現為導致數據寫入。然而,建一個約定來規范任何導致寫入的操作都應該顯式通過變更(mutation)來發送。

簡單說就是,GraphQL用mutation來實現數據的修改,雖然mutation能做的query也能做,但還是要區分開這連個方法,就如同REST中約定用GET來請求數據,用其他方法來更新數據一樣。

a. 實現查詢
查詢的話比較簡單,只需要在接口響應時,獲取數據庫的數據,然后返回;

const objType = new GraphQLObjectType({name: 'meta',fields: {createdAt: {type: GraphQLString},updatedAt: {type: GraphQLString}}
})
let ListType = new GraphQLObjectType({name: 'List',fields: {_id: {type: GraphQLID},id: {type: GraphQLString},title: {type: GraphQLString},desc: {type: GraphQLString},date: {type: GraphQLString},checked: {type: GraphQLBoolean},meta: {type: objType}}
})
const listFields = {type: new GraphQLList(ListType),args: {},resolve (root, params, options) {return List.find({}).exec() // 數據庫查詢}
}
let queryType = new GraphQLObjectType({name: 'getAllList',fields: {lists: listFields,}
})export default new GraphQLSchema({query: queryType
})

把增刪查改都講完再更改代碼~
b. 實現增刪查改

一開始說了,其實mutationquery用法上沒什么區別,這只是一種約定。
具體的mutation實現方式如下:

const outputType = new GraphQLObjectType({name: 'output',fields: () => ({id:     { type: GraphQLString},success:   { type: GraphQLBoolean },})
});const inputType = new GraphQLInputObjectType({name: 'input',fields: () => ({id:          { type: GraphQLString },desc:        { type: GraphQLString },title:       { type: GraphQLString },date:        { type: GraphQLString },checked:        { type: GraphQLBoolean }})
});
let MutationType = new GraphQLObjectType({name: 'Mutations',fields: () => ({delOne: {type: outputType,description: 'del',args: {id: { type: GraphQLString }},resolve: (value, args) => {console.log(args)let result = delOne(args)return result}},editOne: {type: outputType,description: 'edit',args: {listObj: { type: inputType }},resolve: (value, args) => {console.log(args)let result = editOne(args.listObj)return result}},addOne: {type: outputType,description: 'add',args: {listObj: { type: inputType }},resolve: (value, args) => {console.log(args.listObj)let result = addOne(args.listObj)return result}},tickOne: {type: outputType,description: 'tick',args: {id: { type: GraphQLString },checked: { type: GraphQLBoolean },},resolve: (value, args) => {console.log(args)let result = tickOne(args)return result}},}),
});export default new GraphQLSchema({query: queryType,mutation: MutationType
})

c. 完善其余代碼

在實現前端請求Graphql服務器時,最困擾我的就是參數以什么樣的格式進行傳遞。后來在Graphql界面玩Graphql的query請求時發現了其中的訣竅…

關于前端請求格式進行一下說明:
這里寫圖片描述
如上圖,在玩Graphql的請求時,我們就可以直接在控制臺network查看請求的格式了。這里我們只需要模仿這種格式,當做參數發送給Graphql服務器即可。

記得用反引號: `` ,來拼接參數格式。然后用data: {query: params}的格式傳遞參數,代碼如下:

let data = {query: `mutation{addOne(listObj:{id: "${that.getUid()}",desc: "${that.params.desc}",title: "${that.params.title}",date: "${that.getTime(that.params.date)}",checked: false}){id,success}}`}$.post('/graphql', data).done((res) => {console.log(res)// do something})

最后更改server.jsrouter/index.jscontrollers/list.jspublic/index.js改成github項目對應目錄的文件代碼即可。

完整項目的目錄如下:
在這里插入圖片描述

五、后記

對于Vue開發者,可以使用vue-apollo使得前端傳參更加優雅~

六、參考文獻

  • graphql官網教程
  • GraphQL.js
  • 30分鐘理解GraphQL核心概念
  • 我的前端故事----我為什么用GraphQL
  • GraphQL 搭配 Koa 最佳入門實踐


---------------------
作者:__Charming__
來源:CSDN
原文:https://blog.csdn.net/qq_41882147/article/details/82966783
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284233.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284233.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284233.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

對話莊表偉:開源第一課

莊表偉目前就職于華為的開源管理中心。自2014年開源社成立之初,他便友情參與了開源社的籌辦工作。2017年,開源社轉型為完全由個人成員組成的組織,莊表偉就以個人身份加入了開源社。作為開源社理事,當被問到“為什么要參選”時&…

【FME實戰教程】002:FME完美實現CAD數據轉shp案例教程(以三調土地利用現狀數據為例)

FME完美實現CAD數據轉shp案例教程(以三調土地利用數據為例) 文章目錄1. cad數據預覽2. 轉換過程3. shp數據預覽1. cad數據預覽 2. 轉換過程 (1)打開FME Desktop2020中文軟件,點擊【新建】。 (2&#xff09…

Linux學習之01_基礎命令介紹

初學Linux,還在摸索中,在這個過程中希望能記錄下學習到的東西,參考的的書籍為《鳥哥的Linux私房菜》 在這里學到的主要命令有這幾個: data cal bc man shutdown sync 1、基礎命令操作 data----顯示日期與實踐的命令 cal----顯示日…

窮舉算法實例

public static void main(String[] args) {Scanner scnew Scanner(System.in);System.out.println("輸入頭的個數:");int headsc.nextInt();System.out.println("輸入腿的個數:");int footsc.nextInt();for(int i0;i<head;i){//假設兔子的數量為iint jh…

VMware Workstation All Key

官方下載&#xff1a;https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html 懶人打包&#xff1a;鏈接:https://pan.baidu.com/s/1kWJRfjL 密碼:wzce 注&#xff1a;如果是WinXP或32位系統請用 10.0 版本 VMware 所有版本永久許可證激活密鑰&…

【GlobalMapper精品教程】017:KML generator快速將坐標轉為KML文件

本文介紹KML generator軟件,并快速將坐標轉為KML文件的使用方法,并用globalmapper中打開kml文件加以驗證。本專欄配套完整的案例數據包,請打開data017.rar獲取軟件及數據。 文章目錄 1. KML文件介紹2. kml generator軟件介紹2.1 單點KML制作2.2 Excel數據KML制作2.3 文本文件…

從cpp向qml文件傳中文字符串的方法

Qt 使用Unicode編碼來存儲操作字符串&#xff0c;但很多情況下&#xff0c;我們不得不處理采用其他編碼格式的數據&#xff0c;舉例來說&#xff0c;中文多采用GBK和Big5編碼&#xff0c;而日本則多采用Shift-JIS or ISO2022編碼。將其他編碼格式的字符串轉化成采用Unicode編碼…

Codeforces 746 G. New Roads

題目鏈接&#xff1a;http://codeforces.com/contest/746/problem/G mamaya&#xff0c;不知道YY了一個什么做法就這樣過去了啊 2333 首先我顯然可以隨便構造出一棵樹滿足他所給出的深度要求。 但是他還對于葉子節點的數目有要求。 考慮首先構造一棵樹最大化在滿足給出的深度條…

模型驗證組件 FluentValidation

FluentValidation 是 .NET 下的模型驗證組件&#xff0c;和 ASP.NET MVC 基于Attribute 聲明式驗證的不同處&#xff0c;其利用表達式語法鏈式編程&#xff0c;使得驗證組件與實體分開。正如 FluentValidation 的 介紹&#xff1a; A small validation library for .NET that u…

第二屆中國PWA開發者日

點擊藍字關注我們活動介紹為加速推動漸進式 Web 應用 (PWA) 在中國的發展&#xff0c;微軟與英特爾攜手舉辦“第二屆中國 PWA 開發者日”。本次活動邀請一眾業界大咖圍繞 PWA 展開分享&#xff0c;探討最新技術進展&#xff0c;及 PWA 生態的實踐與落地。期待與您線上相聚。活動…

【GlobalMapper精品教程】018:提取影像數據的范圍生成矢量圖層

文章目錄 1. 加載影像數據2. 生成邊界3. 導出矢量范圍4. 背景影響邊界解決辦法1. 加載影像數據 以DSM為例,加載如下所示: 2. 生成邊界 在影像圖層上右鍵→圖層→【邊界框/覆蓋-創建圖層覆蓋框/多邊形區要素】,如下圖所示: 選擇【否】。 邊界創建完成。 3. 導出矢量范圍 …

MPMoviePlayerController屬性方法簡介

屬性說明property (nonatomic, copy) NSURL *contentURL播放媒體URL&#xff0c;這個URL可以是本地路徑&#xff0c;也可以是網絡路徑property (nonatomic, readonly) UIView *view播放器視圖&#xff0c;如果要顯示視頻必須將此視圖添加到控制器視圖中property (nonatomic, re…

在Leangoo里怎么設置看板周期?

設置看板周期有兩種方式&#xff1a; 1&#xff09;點擊看板上的看板周期時間直接修改 2&#xff09;通過菜單 設置看板周期 瀏覽器訪問官網鏈接&#xff1a;www.leangoo.com 轉載于:https://www.cnblogs.com/shineshine/p/5663104.html

consul部署多節點和consul-template部署

一.consul的介紹 1.1consul是什么&#xff1f; Consul是HashiCorp公司推出的開源工具,用于實現分布式系統的服務發現與配置。 Consul是分布式的、高可用的、可橫向擴展的。它具備以下特性 : service discovery:consul通過DNS或者HTTP接口使服務注冊和服務發現變的很容易,一些外…

基于ABP實現DDD

什么是DDD呢&#xff1f;領域驅動設計[DDD]是一種針對復雜需求的軟件開發方法。將軟件實現與不斷發展的模型聯系起來&#xff0c;專注于核心領域邏輯&#xff0c;而不是基礎設施細節。DDD適用于復雜領域和大規模應用&#xff0c;而不是簡單的CRUD應用。它有助于建立一個靈活、模…

二、通過工廠方法來配置bean

調用靜態工廠方法創建 Bean是將對象創建的過程封裝到靜態方法中. 當客戶端需要對象時, 只需要簡單地調用靜態方法, 而不同關心創建對象的細節. 要聲明通過靜態方法創建的 Bean, 需要在 Bean 的 class 屬性里指定擁有該工廠的方法的類, 同時在 factory-method 屬性里指定工廠方法…

【GlobalMapper精品教程】019:基于DSM提取離散隨機點的高程信息

本文講解在globalmapper中,基于DSM提取離散隨機點的高程信息,配套數據為data019.rar。 文章目錄 1. 離散點創建2. 提取離散點高程信息3. 高程標注1. 離散點創建 本文在ArcGIS中,根據給定的范圍,隨機生成離散點,如下圖: 拓展閱讀: ArcGIS根據范圍創建隨機點教程:【ArcG…

shell腳本注意點

2019獨角獸企業重金招聘Python工程師標準>>> 直接命令行寫腳本的時候&#xff0c;可以用 ; 分割&#xff0c;或 也可以直接回車&#xff0c;然后在繼續寫腳本在使用 方括號[ ] 的時候&#xff0c;里面空格兩邊都必須要有空格&#xff0c;比如 [ $a -gt 3 ] 在方括號…

C語言編程規范--------2 注釋

2.1 注釋的原則 注釋的目的是解釋代碼的目的、功能和采用的方法&#xff0c;提供代碼以外的信息&#xff0c;幫助讀者理解代碼&#xff0c;防止沒必要的重復注釋信息。 示例&#xff1a;如下注釋意義不大。 /* if receive_flag is TRUE */ if (receive_flag) 而如下的注釋則給出…