imwty

imwty 查看完整档案

成都编辑浙江师范大学  |  网络工程 编辑萌想科技  |  平台研发主管 编辑 imwty.com 编辑
编辑

Enjoy creating rather than coding!

个人动态

imwty 赞了文章 · 2020-07-22

MySQL的limit用法和分页查询的性能分析及优化

一、limit用法

在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能。

SELECT * FROM table LIMIT [offset,] rows | `rows OFFSET offset ` 
(LIMIT offset, `length`)
SELECT
*
FROM table
where condition1 = 0
and condition2 = 0
and condition3 = -1
and condition4 = -1
order by id asc
LIMIT 2000 OFFSET 50000

LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量第二个参数指定返回记录行的最大数目初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。

mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 

//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:

mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. 

//如果只给定一个参数,它表示返回最大的记录行数目:
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n

二、Mysql的分页查询语句的性能分析

MySql分页sql语句,如果和MSSQL的TOP语法相比,那么MySQL的LIMIT语法要显得优雅了许多。使用它来分页是再自然不过的事情了。

最基本的分页方式:

SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ... 

在中小数据量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引:
举例来说,如果实际SQL类似下面语句,那么在category_id, id两列上建立复合索引比较好:

SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 50, 10

子查询的分页方式:

随着数据量的增加,页数会越来越多,查看后几页的SQL就可能类似:
SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 10000, 10

一言以蔽之,就是越往后分页,LIMIT语句的偏移量就会越大,速度也会明显变慢
此时,我们可以通过子查询的方式来提高分页效率,大致如下:

SELECT * FROM articles WHERE  id >=  
(SELECT id FROM articles  WHERE category_id = 123 ORDER BY id LIMIT 10000, 1) LIMIT 10 

JOIN分页方式

SELECT * FROM `content` AS t1   
JOIN (SELECT id FROM `content` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) AS t2   
WHERE t1.id <= t2.id ORDER BY t1.id desc LIMIT $pagesize; 

经过我的测试,join分页和子查询分页的效率基本在一个等级上,消耗的时间也基本一致。
explain SQL语句:

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1  
1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 6264 Using where
2 DERIVED content index NULL PRIMARY 4 NULL 27085 Using index

为什么会这样呢?因为子查询是在索引上完成的,而普通的查询时在数据文件上完成的,通常来说,索引文件要比数据文件小得多,所以操作起来也会更有效率。

实际可以利用类似策略模式的方式去处理分页,比如判断如果是一百页以内,就使用最基本的分页方式,大于一百页,则使用子查询的分页方式。

三、对于有大数据量的mysql表来说,使用LIMIT分页存在很严重的性能问题。

查询从第1000000之后的30条记录:

SQL代码1:平均用时6.6秒 SELECT * FROM `cdb_posts` ORDER BY pid LIMIT 1000000 , 30

SQL代码2:平均用时0.6秒 SELECT * FROM `cdb_posts` WHERE pid >= (SELECT pid FROM  
`cdb_posts` ORDER BY pid LIMIT 1000000 , 1) LIMIT 30

因为要取出所有字段内容,第一种需要跨越大量数据块并取出,而第二种基本通过直接根据索引字段定位后,才取出相应内容,效率自然大大提升。对limit的优化,不是直接使用limit,而是首先获取到offset的id,然后直接使用limit size来获取数据。

可以看出,越往后分页,LIMIT语句的偏移量就会越大,两者速度差距也会越明显。

实际应用中,可以利用类似策略模式的方式去处理分页,比如判断如果是一百页以内,就使用最基本的分页方式,大于一百页,则使用子查询的分页方式。

优化思想:避免数据量大时扫描过多的记录

clipboard.png

clipboard.png

为了保证index索引列连续,可以为每个表加一个自增字段,并且加上索引

参考:mysql分页offset过大,Sql优化经验

查看原文

赞 51 收藏 58 评论 5

imwty 赞了文章 · 2020-03-20

python定投交易与低于平均值交易比较

import tushare as ts
import xlrd
import pandas as pd

import matplotlib.pyplot as plt
ts.set_token('**********************')
pro = ts.pro_api()

def strategy_invest(fund,money,c_rate,be_data,en_date):
    df = pro.fund_nav(ts_code=fund)
    df.sort_values(by=['end_date'],inplace=True)#按时间排序从小到大
    df = df.reset_index(drop=True)#index重新标记
    df['交易时间'] = pd.to_datetime(df['end_date'])
    df=df[['交易时间','unit_nav']]#只选日期,和净值
    #设置投资时间
    df=df[df['交易时间']>=be_data]
    df=df[df['交易时间']<=en_date]
    df["每次投入资金"]=money
    df["累计投入资金"]=df["每次投入资金"].cumsum()
    c_rate=0.002
    df["每次数量"]=df["每次投入资金"]/df['unit_nav']*(1-c_rate)
    df["累计数量"]=df["每次数量"].cumsum()
    df["盈亏"]=df["累计数量"]*df['unit_nav']-df["累计投入资金"]
    df["收益率"]=df["盈亏"]/df["累计投入资金"]*100
    return df

def get_draw(df):
    dfplot=df.copy()
    dfplot.index=dfplot["交易时间"]
    dfplot[["收益率"]].plot()
    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
    return plt.show()
def average_invest(df):
    dfstrategy=df.copy()
    dfstrategy["移动平均值"] = dfstrategy['unit_nav'].rolling(60).mean()  # 计算移动平均值
    dfstrategy=dfstrategy[["交易时间","unit_nav","移动平均值"]]
    dfstrategy.loc[dfstrategy['unit_nav']<=(dfstrategy["移动平均值"]*0.97),"每次投入资金"]=200
    dfstrategy.fillna(0,inplace=True)
    dfstrategy["累计投入资金"]=dfstrategy["每次投入资金"].cumsum()
    c_rate=0.002
    dfstrategy["每次数量"]=dfstrategy["每次投入资金"]/dfstrategy['unit_nav']*(1-c_rate)
    dfstrategy["累计数量"]=dfstrategy["每次数量"].cumsum()
    dfstrategy["盈亏"]=dfstrategy["累计数量"]*dfstrategy['unit_nav']-dfstrategy["累计投入资金"]
    dfstrategy["收益率"]=dfstrategy["盈亏"]/dfstrategy["累计投入资金"]*100
    return dfstrategy


df=strategy_invest(fund="001632.OF",money=50,be_data="2019-04-03",en_date="2020-04-04",c_rate=0.002)
dfstrategy=average_invest(df)
df=df[["交易时间","unit_nav","累计投入资金","盈亏","收益率"]]
dfstrategy=dfstrategy[["交易时间","unit_nav","累计投入资金","盈亏","收益率"]]
print(df.tail(3))
get_draw(df)
print(dfstrategy.tail(3))
get_draw(dfstrategy)
查看原文

赞 1 收藏 1 评论 0

imwty 关注了专栏 · 2019-01-02

python3 :Mark的快乐家园?

为公司整体团队向python转型而制定的培训专栏,从简单、实用角度入手,逐渐完善,根据公司内部培训人员反馈完善细节,内部微信交流群请加微信号:zlx1782980833(个人),备注python就还带你进群,我们会对群内所有的问题进行总结,然后整理出简单易懂的文章,发布在该专题上。

关注 244

imwty 收藏了文章 · 2018-09-07

React Router 4 简易入门

原文

React Router4是一个流行的纯React重写的包。现在的版本中已不需要路由配置,现在一切皆组件。

本文涵盖了开始使用React Router构建网站所需要的一切知识。我们将会为本地运动队制作一个网站。

代码

想看网站最终效果,查看demo

安装

React Router被拆分成三个包:react-router,react-router-domreact-router-nativereact-router提供核心的路由组件与函数。其余两个则提供运行环境(即浏览器与react-native)所需的特定组件。

进行网站(将会运行在浏览器环境中)构建,我们应当安装react-router-domreact-router-dom暴露出react-router中暴露的对象与方法,因此你只需要安装并引用react-router-dom即可。

npm install --save react-router-dom

路由器(Router)

在你开始项目前,你需要决定你使用的路由器的类型。对于网页项目,存在<BrowserRouter><HashRouter>两种组件。当存在服务区来管理动态请求时,需要使用<BrowserRouter>组件,而<HashRouter>被用于静态网站。

通常,我们更倾向选择<BrowserRouter>,但如果你的网站仅用来呈现静态文件,那么<HashRouter>将会是一个好选择。

对于我们的项目,将设将会有服务器的动态支持,因此我们选择<BrowserRouter>作为路由器组件。

历史(History)

每个路由器都会创建一个history对象并用其保持追踪当前location[注1]并且在有变化时对网站进行重新渲染。这个history对象保证了React Router提供的其他组件的可用性,所以其他组件必须在router内部渲染。一个React Router组件如果向父级上追溯却找不到router组件,那么这个组件将无法正常工作。

渲染<Router>

路由器组件无法接受两个及以上的子元素。基于这种限制的存在,创建一个<App>组件来渲染应用其余部分是一个有效的方法(对于服务端渲染,将应用从router组件中分离也是重要的)。

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))

<App>

应用通过<App>组件定义。简化一下,我们将应用拆分成两个部分。<Header>组件包含网站的导航链接。<Main>组件则呈现其余内容。

// this component will be rendered by our <___Router>
const App = () => (
  <div>
    <Header />
    <Main />
  </div>
)

注意:你可以按你喜欢的方式对应用布局,但是将路由与导航拆分出来对于这个入门教程会成家简单。

路由(Route)

<Route>组件是React Router中主要的结构单元。在任意位置只要匹配了URL的路径名(pathname)你就可以创建<Route>元素进行渲染。

路径(Path)

<Route>接受一个数为string类型的path,该值路由匹配的路径名的类型。例如:<Route path='/roster'/>会匹配以/roster[注2]开头的路径名。在当前path参数与当前location的路径相匹配时,路由就会开始渲染React元素。若不匹配,路由不会进行任何操作[注3]。

<Route path='/roster'/>
// 当路径名为'/'时, path不匹配
// 当路径名为'/roster'或'/roster/2'时, path匹配
// 当你只想匹配'/roster'时,你需要使用"exact"参数
// 则路由仅匹配'/roster'而不会匹配'/roster/2'
<Route exact path='/roster'/>

注意:在匹配路由时,React Router只关注location的路径名。当URL如下时:

http://www.example.com/my-projects/one?extra=false

React Router去匹配的只是'/my-projects/one'这一部分。

匹配路径

path-to-regexp包用来决定route元素的path参数与当前location是否匹配。它将路径字符串编译成正则表达式,并与当前location的路径名进行匹配比较。除了上面的例子外,路径字符串有更多高级的选项,详见[path-to-regexp文档]。
当路由地址匹配成功后,会创建一个含有以下属性的match对象

  • url :与当前location路径名所匹配部分

  • path :路由的地址

  • isExact :path 是否等于 pathname

  • params :从path-to-regexp获取的路径中取出的值都被包含在这个对象中

使用route tester这款工具来对路由与URL进行检验。

注意:本例中路由路径仅支持绝对路径[注4]。

创建你的路由

可以在路由器(router)组件中的任意位置创建多个<Route>,但通常我们会把它们放在同一个位置。使用<Switch>组件来包裹一组<Route>。<Switch>会遍历自身的子元素(即路由)并对第一个匹配当前路径的元素进行渲染。

对于本网站,我们希望匹配一下路径:

  • / : 主页

  • /roster : 团体列表

  • /roster/:number :运动员页面,使用运动员的编号作为标识

  • /schedule :团队的赛程表

为了在应用中能匹配路径,在创建<Route>元素时必须带有需要匹配的path作为参数。

<Switch>
  <Route exact path='/' component={Home}/>
  {/* both /roster and /roster/:number begin with /roster */}
  <Route path='/roster' component={Roster}/>
  <Route path='/schedule' component={Schedule}/>
</Switch>

<Route>是如何渲染的?

当一个路由的path匹配成功后,路由用来确定渲染结果的参数有三种。只需要提供其中一个即可。

  • component : 一个React组件。当带有component参数的route匹配成功后,route会返回一个新的元素,其为component参数所对应的React组件(使用React.createElement创建)。

  • render : 一个返回React element的函数[注5]。当匹配成功后调用该函数。该过程与传入component参数类似,并且对于行级渲染与需要向元素传入额外参数的操作会更有用。

  • children : 一个返回React element的函数。与上述两个参数不同,无论route是否匹配当前location,其都会被渲染。

<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
  <Page {...props} data={extraProps}/>
)}/>
<Route path='/page' children={(props) => (
  props.match
    ? <Page {...props}/>
    : <EmptyPage {...props}/>
)}/>

通常component参数与render参数被更经常地使用。children参数偶尔会被使用,它更常用在path无法匹配时呈现的'空'状态。在本例中并不会有额外的状态,所以我们将使用<Route>的component参数。

通过<Route>渲染的元素会被传入一些参数。分别是match对象,当前location对象[注6]以及history对象(由router创建)[注7]。

<Main>

现在我们清楚了根路由的结构,我们需要实际渲染我们的路由。对于这个应用,我们将会在<Main>组件中渲染<Switch>与<Route>,这一过程会将route匹配生成的HTML放在<main>节点中。

import { Switch, Route } from 'react-router-dom'
const Main = () => (
  <main>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/roster' component={Roster}/>
      <Route path='/schedule' component={Schedule}/>
    </Switch>
  </main>
)

注意:主页路由包含额外参数。该参数用来保证路由能准确匹配path。

嵌套路由

运动员路由/roster/:number并未包含在上述<Switch>中。它由<Roster>组件负责在路径包含'/roster'的情形下进行渲染。

在<Roster>组件中,我们将为两种路径进行渲染:

  • /roster :对应路径名仅仅是/roster时,因此需要在exact元素上添加exact参数。

  • /roster/:number : 该路由使用一个路由参数来获取/roster后的路径名。

    const Roster = () => (
    <Switch>

    <Route exact path='/roster' component={FullRoster}/>
    <Route path='/roster/:number' component={Player}/>

    </Switch>
    )

    组合在相同组件中分享共同前缀的路由是一种有用的方法。这就需要简化父路由并且提供一个区域来渲染具有相同前缀的通用路由。

例如,<Roster>用来渲染所有以/roster开始的全部路由。

const Roster = () => (
  <div>
    <h2>This is a roster page!</h2>
    <Switch>
      <Route exact path='/roster' component={FullRoster}/>
      <Route path='/roster/:number' component={Player}/>
    </Switch>
  </div>
)

路径参数

有时路径名中存在我们需要获取的参数。例如,在运动员界面,我们需要获取运动员的编号。我们可以向route的路径字符串中添加path参数

如'/roster/:number'中:number这种写法意味着/roster/后的路径名将会被获取并存在match.params.number中。例如,路径名'/roster/6'会获取到一个对象:

{ number: '6' } // 注获取的值是字符串类型的

<Player>组件可以使用props.match.params对象来确定需要被渲染的运动员的数据。

// 返回运动员对象的API
import PlayerAPI from './PlayerAPI'
const Player = (props) => {
  const player = PlayerAPI.get(
    parseInt(props.match.params.number, 10)
  )
  if (!player) {
    return <div>Sorry, but the player was not found</div>
  }
  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>{player.position}</h2>
    </div>
)

你可以通过阅读path-to-regexp文档来了解更多。

除了<Player>组件,我们的页面还包含<FullRoster>, <Schedule>以及 <Home>组件。

const FullRoster = () => (
  <div>
    <ul>
      {
        PlayerAPI.all().map(p => (
          <li key={p.number}>
            <Link to={`/roster/${p.number}`}>{p.name}</Link>
          </li>
        ))
      }
    </ul>
  </div>
)
const Schedule = () => (
  <div>
    <ul>
      <li>6/5 @ Evergreens</li>
      <li>6/8 vs Kickers</li>
      <li>6/14 @ United</li>
    </ul>
  </div>
)
const Home = () => (
  <div>
    <h1>Welcome to the Tornadoes Website!</h1>
  </div>
)

Link

现在,我们应用需要在各个页面间切换。如果使用锚点元素(就是)实现,在每次点击时页面将被重新加载。React Router提供了<Link>组件用来避免这种状况的发生。当你点击<Link>时,URL会更新,组件会被重新渲染,但是页面不会重新加载。

import { Link } from 'react-router-dom'
const Header = () => (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)

<Link>使用'to'参数来描述需要定位的页面。它的值即可是字符串也可是location对象(包含pathname,search,hash与state属性)。如果其值为字符床将会被转换为location对象。

<Link to={{ pathname: '/roster/7' }}>Player #7</Link>

注意:本例的link的pathname属性只能是绝对路径[注4]。

例子

一个完整的网站例子

获取路由

希望当下你已准备好深入构建你自己的网站了。

我们已经了解了构建网站所需要的所有必须组件(<BrowserRouter>, <Route>, 以及 <Link>)。当然,还有一些我们没有涉及的组件。所幸React Router拥有优质的文档,你可以查看并从中了解更多的信息。文档也提供一系列的例子与源代码。

注释:

[1] locations 是一个含有描述URL不同部分属性的对象:

// 一个基本的location对象
{ pathname: '/', search: '', hash: '', key: 'abc123' state: {} }

[2] 你可以渲染无路径的<Route>,其将会匹配所有location。此法用于访问存在上下文中的变量与方法。

[3] 如果你使用children参数,即便在当前location不匹配时route也将进行渲染。

[4] 当需要支持相对路径的<Route>与<Link>时,你需要多做一些工作。相对<Link>将会比你之前看到的更为复杂。因其使用了父级的match对象而非当前URL来匹配相对路径。

[5] 这是一个本质上无状态的函数组件。内部实现,component参数与render参数的组件是用很大的区别的。使用component参数的组件会使用React.createElement来创建元素,使用render参数的组件则会调用render函数。如果我们定义一个内联函数并将其传给component参数,这将会比使用render参数慢很多。

<Route path='/one' component={One}/>
// React.createElement(props.component)
<Route path='/two' render={() => <Two />}/>
// props.render()

[6] <Route>与<Switch>组件都会带有location参数。这能让你使用与实际location不同的location去匹配地址。

[7] 可以传入staticContext参数,不过这仅在服务端渲染时有用。

查看原文

imwty 评论了文章 · 2018-07-26

微信小程序开发之从“跳伞”到“吃鸡”

写在前边

微信小程序随着官方开放越多越多的接口,也是变的越来越火了,越来越多的企业已经开始布局小程序生态。所以,对于我们开发者来说,掌握小程序开发显得分外重要。如果点亮了该技能,那么离升职加薪赢取白富美的日子就又近了一步啦!

关于我

笔者算是一个野生的程序猿吧,没有什么大厂经验,搞开发就是一个字,干!从不来虚的。因此,随性的性格造就了我全栈(一窍不通)的本事,做项目那就是一把抓,前后端全包,什么前后端撕逼的问题统统没有了,哈哈哈(想想心里还有点小激动)。不过本文笔者只重点分享小程序开发相关的东西,想了解后端的话,可以坐等我下一次的分享哈哈哈(可能会遥遥无期)。

准备工作

话不多说,进入正题。现在开始分享我是怎么开发完一款小程序的。小程序的官方文档其实写的是很详细很清晰的,相比微信公众号的开发文档来说真的是非常非常的良心,所以开发前浏览一遍开发文档非常关键。下边我罗列一下其他准备工作。

1. 开发工具

工欲善其事,必先利其器,首选的就是把开发环境给弄好咯,我推荐如下:

开发工具

  • 官方微信开发者工具
  • visio studio code

调试工具

  • 官方微信开发者工具

当然开发工具什么的只要自己习惯就好,我的推荐只是参考。笔者最后习惯是直接在官方的开发者工具上编辑加调试。

2. 小程序配置

这块的内容官方文档都有详细的教程,我就不多补充啦。总体来说就是需要去申请一个开发者账号,然后配置小程序的基本信息,比较值得重点说的就是小程序api的配置,官方要求必须是https。说到这里,如果读者你的api接口已经上了https可以跳过了,如果不是,然后你也像笔者一样是全栈开发,那么笔者将告诉你如何快速让接口踏上https的航班。

是的,没错, 就是它!Certbot

在官网上选好http服务器和linux系统后,按照命令一个一个敲,就ok了,简直不要太简单。哈哈哈,至于想深入了解这个东东的话,可以去了解下Let's Encrypt

开发

总算可以正式的开始撸功能了!因为考虑到现在程序猿找对象是真的太难了,所以笔者撸的小程序是一个脱单交友的小程序(单身汪的福音哦),小程序页面也不多,核心就是让用户填写个人信息然后展示出来。是的,就是这么简单!

授权登录

小程序的授权登录其实可以看作是两件事情,授权和登录,这两者是可以单独分开处理的(个人观点,允许反驳)。

授权

对于授权,其实官方已经有讲到,就是换成微信内的授权询问,就是如下这个东西

这个微信授权的询问弹窗之前的版本中只要调用获取用户信息的api,是会自动弹出的,现在小程序做了调整需要自行通过按钮触发,所以这个很蛋疼。那么需要怎么去设计呢,这里也有两个方案,一个是做个单独的页面,另一个方案是做弹窗。个人建议是选方案一,因为这样可以把授权逻辑从页面逻辑里独立出来,方便所有页面渲染前调用。具体coding如下:

在app.js中,判断是否已经授权,如果未授权则跳转到授权页面

App({
  onLaunch: function () {
    ...
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },

在'pages/auth/index.wxml'页面中,使用button做授权按钮

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">微信账号授权登录</button>

'pages/auth/index.js'中定义绑定的回调方法,重新跳转回上一页

Page({
  ...
  // 点击授权后跳回首页
  bindGetUserInfo (e) {
    wx.reLaunch({ url: '../index/index' })
    // 用户已经同意小程序使用获取用户信息功能,后续调用 wx.startRecord 接口不会弹窗询问
  }
})

这样,一个授权逻辑就完成了。

登录

登录的目录其实是和后端交互,需要在服务器端存储当前用户的标识,以便用户下一次登录时服务器知道是谁登录了。做过微信公众号开发的朋友应该都知道,能承担这个作用的角色就是open_id了,所以要实现登录的话,其实就是需要获取当前用户的open_id,官方文档中是这么介绍的:

1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2.开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

所以完成登录的前提就是需要后端提供一个接口,咱们把code传给后端就行了,剩下的工作就是后端去完成啦。

App({
  onLaunch: function () {
    // 授权判断
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },
  // 登录
  wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (res.code) {
          api.post('/user/mplogin', { 'code': res.code }).then(res => {
              wx.setStorageSync('access_token', res.access_token)
          }).catch(error=>{
            console.log(error)
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    })

这里需要多提的一点是关于会话维持的方案,就是如何让服务器端知道访问接口的是谁。笔者提供两个办法:

  1. 通过wx.request()在header中强行组装cookie字符串,来实现传统浏览器上用cookie维持会话的效果
  2. 使用access_token的方式,比如jwt

笔者选择的是第二种,通过后端的登录接口返回token,然后将token存入Storage,然后在发起请求的时候将token封装到http请求体中。 两种方案都可行,读者们可根据自己情况自行实现。 由于wx.request()方法发起请求比较麻烦,还需要处理会话逻辑,所以建议读者们还是进行一次封装,下边贴上笔者的代码:

utils文件夹中创建request.js文件

const domain = "https://domain.com" // 接口域名
function GET(url, params) {
  return request('GET', url, params)
}
function POST(url, params) {
  return request('POST', url, params)
}
function request(method, url, params) {
  return new Promise((resolve, reject) => {
    wx.request({
      url: domain + url,
      data: params,
      method: method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'bearer' + ' ' + wx.getStorageSync('access_token'),
      },
      success(res) {
        if (res.data.code === 100) {
          let resData = res.data.data
          if (!resData) {
            resData = ''
          }
          resolve(resData)
        } else {
          let err = {
            code: res.data.code,
            msg: res.data.msg
          }
          reject(err)
        }
      }
    })
  })
}

module.exports = {
  get: GET,
  post: POST
}

使用

const api = require('../../utils/request')
...
api.post('/user/mplogin', { 'code': res.code }).then(res => {
     wx.setStorageSync('access_token', res.access_token)
 }).catch(error=>{
   console.log(error)
 })

至此,登录逻辑便已完成,是不是觉得小程序开发也不过如此呢?由于篇幅有限,今天就分享到这啦,后续我将继续和看官们一起探究以下小程序开发的话题

  • 图片上传
  • 表单提交
  • 级联选择器的实现
  • 如何使用iconfont图标
  • 微信支付
  • 模板消息
  • ....

欢迎看官们持续关注,也欢迎看官们私信告知我其他疑问,我尽量都一一分享,知无不言,言无不尽。

不是结束的结语

打波小小的广告,个人开发的找对象小程序“佛系处对象”已经顺利上线,欢迎读者看官们扫码体验,如果脱单了记得通知我哟!

最后祝大家在事业和爱情的“战场”中都能脱颖而出,顺利吃鸡!

查看原文

imwty 评论了文章 · 2018-07-26

微信小程序开发之从“跳伞”到“吃鸡”

写在前边

微信小程序随着官方开放越多越多的接口,也是变的越来越火了,越来越多的企业已经开始布局小程序生态。所以,对于我们开发者来说,掌握小程序开发显得分外重要。如果点亮了该技能,那么离升职加薪赢取白富美的日子就又近了一步啦!

关于我

笔者算是一个野生的程序猿吧,没有什么大厂经验,搞开发就是一个字,干!从不来虚的。因此,随性的性格造就了我全栈(一窍不通)的本事,做项目那就是一把抓,前后端全包,什么前后端撕逼的问题统统没有了,哈哈哈(想想心里还有点小激动)。不过本文笔者只重点分享小程序开发相关的东西,想了解后端的话,可以坐等我下一次的分享哈哈哈(可能会遥遥无期)。

准备工作

话不多说,进入正题。现在开始分享我是怎么开发完一款小程序的。小程序的官方文档其实写的是很详细很清晰的,相比微信公众号的开发文档来说真的是非常非常的良心,所以开发前浏览一遍开发文档非常关键。下边我罗列一下其他准备工作。

1. 开发工具

工欲善其事,必先利其器,首选的就是把开发环境给弄好咯,我推荐如下:

开发工具

  • 官方微信开发者工具
  • visio studio code

调试工具

  • 官方微信开发者工具

当然开发工具什么的只要自己习惯就好,我的推荐只是参考。笔者最后习惯是直接在官方的开发者工具上编辑加调试。

2. 小程序配置

这块的内容官方文档都有详细的教程,我就不多补充啦。总体来说就是需要去申请一个开发者账号,然后配置小程序的基本信息,比较值得重点说的就是小程序api的配置,官方要求必须是https。说到这里,如果读者你的api接口已经上了https可以跳过了,如果不是,然后你也像笔者一样是全栈开发,那么笔者将告诉你如何快速让接口踏上https的航班。

是的,没错, 就是它!Certbot

在官网上选好http服务器和linux系统后,按照命令一个一个敲,就ok了,简直不要太简单。哈哈哈,至于想深入了解这个东东的话,可以去了解下Let's Encrypt

开发

总算可以正式的开始撸功能了!因为考虑到现在程序猿找对象是真的太难了,所以笔者撸的小程序是一个脱单交友的小程序(单身汪的福音哦),小程序页面也不多,核心就是让用户填写个人信息然后展示出来。是的,就是这么简单!

授权登录

小程序的授权登录其实可以看作是两件事情,授权和登录,这两者是可以单独分开处理的(个人观点,允许反驳)。

授权

对于授权,其实官方已经有讲到,就是换成微信内的授权询问,就是如下这个东西

这个微信授权的询问弹窗之前的版本中只要调用获取用户信息的api,是会自动弹出的,现在小程序做了调整需要自行通过按钮触发,所以这个很蛋疼。那么需要怎么去设计呢,这里也有两个方案,一个是做个单独的页面,另一个方案是做弹窗。个人建议是选方案一,因为这样可以把授权逻辑从页面逻辑里独立出来,方便所有页面渲染前调用。具体coding如下:

在app.js中,判断是否已经授权,如果未授权则跳转到授权页面

App({
  onLaunch: function () {
    ...
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },

在'pages/auth/index.wxml'页面中,使用button做授权按钮

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">微信账号授权登录</button>

'pages/auth/index.js'中定义绑定的回调方法,重新跳转回上一页

Page({
  ...
  // 点击授权后跳回首页
  bindGetUserInfo (e) {
    wx.reLaunch({ url: '../index/index' })
    // 用户已经同意小程序使用获取用户信息功能,后续调用 wx.startRecord 接口不会弹窗询问
  }
})

这样,一个授权逻辑就完成了。

登录

登录的目录其实是和后端交互,需要在服务器端存储当前用户的标识,以便用户下一次登录时服务器知道是谁登录了。做过微信公众号开发的朋友应该都知道,能承担这个作用的角色就是open_id了,所以要实现登录的话,其实就是需要获取当前用户的open_id,官方文档中是这么介绍的:

1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2.开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

所以完成登录的前提就是需要后端提供一个接口,咱们把code传给后端就行了,剩下的工作就是后端去完成啦。

App({
  onLaunch: function () {
    // 授权判断
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },
  // 登录
  wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (res.code) {
          api.post('/user/mplogin', { 'code': res.code }).then(res => {
              wx.setStorageSync('access_token', res.access_token)
          }).catch(error=>{
            console.log(error)
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    })

这里需要多提的一点是关于会话维持的方案,就是如何让服务器端知道访问接口的是谁。笔者提供两个办法:

  1. 通过wx.request()在header中强行组装cookie字符串,来实现传统浏览器上用cookie维持会话的效果
  2. 使用access_token的方式,比如jwt

笔者选择的是第二种,通过后端的登录接口返回token,然后将token存入Storage,然后在发起请求的时候将token封装到http请求体中。 两种方案都可行,读者们可根据自己情况自行实现。 由于wx.request()方法发起请求比较麻烦,还需要处理会话逻辑,所以建议读者们还是进行一次封装,下边贴上笔者的代码:

utils文件夹中创建request.js文件

const domain = "https://domain.com" // 接口域名
function GET(url, params) {
  return request('GET', url, params)
}
function POST(url, params) {
  return request('POST', url, params)
}
function request(method, url, params) {
  return new Promise((resolve, reject) => {
    wx.request({
      url: domain + url,
      data: params,
      method: method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'bearer' + ' ' + wx.getStorageSync('access_token'),
      },
      success(res) {
        if (res.data.code === 100) {
          let resData = res.data.data
          if (!resData) {
            resData = ''
          }
          resolve(resData)
        } else {
          let err = {
            code: res.data.code,
            msg: res.data.msg
          }
          reject(err)
        }
      }
    })
  })
}

module.exports = {
  get: GET,
  post: POST
}

使用

const api = require('../../utils/request')
...
api.post('/user/mplogin', { 'code': res.code }).then(res => {
     wx.setStorageSync('access_token', res.access_token)
 }).catch(error=>{
   console.log(error)
 })

至此,登录逻辑便已完成,是不是觉得小程序开发也不过如此呢?由于篇幅有限,今天就分享到这啦,后续我将继续和看官们一起探究以下小程序开发的话题

  • 图片上传
  • 表单提交
  • 级联选择器的实现
  • 如何使用iconfont图标
  • 微信支付
  • 模板消息
  • ....

欢迎看官们持续关注,也欢迎看官们私信告知我其他疑问,我尽量都一一分享,知无不言,言无不尽。

不是结束的结语

打波小小的广告,个人开发的找对象小程序“佛系处对象”已经顺利上线,欢迎读者看官们扫码体验,如果脱单了记得通知我哟!

最后祝大家在事业和爱情的“战场”中都能脱颖而出,顺利吃鸡!

查看原文

imwty 评论了文章 · 2018-07-26

微信小程序开发之从“跳伞”到“吃鸡”

写在前边

微信小程序随着官方开放越多越多的接口,也是变的越来越火了,越来越多的企业已经开始布局小程序生态。所以,对于我们开发者来说,掌握小程序开发显得分外重要。如果点亮了该技能,那么离升职加薪赢取白富美的日子就又近了一步啦!

关于我

笔者算是一个野生的程序猿吧,没有什么大厂经验,搞开发就是一个字,干!从不来虚的。因此,随性的性格造就了我全栈(一窍不通)的本事,做项目那就是一把抓,前后端全包,什么前后端撕逼的问题统统没有了,哈哈哈(想想心里还有点小激动)。不过本文笔者只重点分享小程序开发相关的东西,想了解后端的话,可以坐等我下一次的分享哈哈哈(可能会遥遥无期)。

准备工作

话不多说,进入正题。现在开始分享我是怎么开发完一款小程序的。小程序的官方文档其实写的是很详细很清晰的,相比微信公众号的开发文档来说真的是非常非常的良心,所以开发前浏览一遍开发文档非常关键。下边我罗列一下其他准备工作。

1. 开发工具

工欲善其事,必先利其器,首选的就是把开发环境给弄好咯,我推荐如下:

开发工具

  • 官方微信开发者工具
  • visio studio code

调试工具

  • 官方微信开发者工具

当然开发工具什么的只要自己习惯就好,我的推荐只是参考。笔者最后习惯是直接在官方的开发者工具上编辑加调试。

2. 小程序配置

这块的内容官方文档都有详细的教程,我就不多补充啦。总体来说就是需要去申请一个开发者账号,然后配置小程序的基本信息,比较值得重点说的就是小程序api的配置,官方要求必须是https。说到这里,如果读者你的api接口已经上了https可以跳过了,如果不是,然后你也像笔者一样是全栈开发,那么笔者将告诉你如何快速让接口踏上https的航班。

是的,没错, 就是它!Certbot

在官网上选好http服务器和linux系统后,按照命令一个一个敲,就ok了,简直不要太简单。哈哈哈,至于想深入了解这个东东的话,可以去了解下Let's Encrypt

开发

总算可以正式的开始撸功能了!因为考虑到现在程序猿找对象是真的太难了,所以笔者撸的小程序是一个脱单交友的小程序(单身汪的福音哦),小程序页面也不多,核心就是让用户填写个人信息然后展示出来。是的,就是这么简单!

授权登录

小程序的授权登录其实可以看作是两件事情,授权和登录,这两者是可以单独分开处理的(个人观点,允许反驳)。

授权

对于授权,其实官方已经有讲到,就是换成微信内的授权询问,就是如下这个东西

这个微信授权的询问弹窗之前的版本中只要调用获取用户信息的api,是会自动弹出的,现在小程序做了调整需要自行通过按钮触发,所以这个很蛋疼。那么需要怎么去设计呢,这里也有两个方案,一个是做个单独的页面,另一个方案是做弹窗。个人建议是选方案一,因为这样可以把授权逻辑从页面逻辑里独立出来,方便所有页面渲染前调用。具体coding如下:

在app.js中,判断是否已经授权,如果未授权则跳转到授权页面

App({
  onLaunch: function () {
    ...
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },

在'pages/auth/index.wxml'页面中,使用button做授权按钮

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">微信账号授权登录</button>

'pages/auth/index.js'中定义绑定的回调方法,重新跳转回上一页

Page({
  ...
  // 点击授权后跳回首页
  bindGetUserInfo (e) {
    wx.reLaunch({ url: '../index/index' })
    // 用户已经同意小程序使用获取用户信息功能,后续调用 wx.startRecord 接口不会弹窗询问
  }
})

这样,一个授权逻辑就完成了。

登录

登录的目录其实是和后端交互,需要在服务器端存储当前用户的标识,以便用户下一次登录时服务器知道是谁登录了。做过微信公众号开发的朋友应该都知道,能承担这个作用的角色就是open_id了,所以要实现登录的话,其实就是需要获取当前用户的open_id,官方文档中是这么介绍的:

1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2.开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

所以完成登录的前提就是需要后端提供一个接口,咱们把code传给后端就行了,剩下的工作就是后端去完成啦。

App({
  onLaunch: function () {
    // 授权判断
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },
  // 登录
  wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (res.code) {
          api.post('/user/mplogin', { 'code': res.code }).then(res => {
              wx.setStorageSync('access_token', res.access_token)
          }).catch(error=>{
            console.log(error)
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    })

这里需要多提的一点是关于会话维持的方案,就是如何让服务器端知道访问接口的是谁。笔者提供两个办法:

  1. 通过wx.request()在header中强行组装cookie字符串,来实现传统浏览器上用cookie维持会话的效果
  2. 使用access_token的方式,比如jwt

笔者选择的是第二种,通过后端的登录接口返回token,然后将token存入Storage,然后在发起请求的时候将token封装到http请求体中。 两种方案都可行,读者们可根据自己情况自行实现。 由于wx.request()方法发起请求比较麻烦,还需要处理会话逻辑,所以建议读者们还是进行一次封装,下边贴上笔者的代码:

utils文件夹中创建request.js文件

const domain = "https://domain.com" // 接口域名
function GET(url, params) {
  return request('GET', url, params)
}
function POST(url, params) {
  return request('POST', url, params)
}
function request(method, url, params) {
  return new Promise((resolve, reject) => {
    wx.request({
      url: domain + url,
      data: params,
      method: method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'bearer' + ' ' + wx.getStorageSync('access_token'),
      },
      success(res) {
        if (res.data.code === 100) {
          let resData = res.data.data
          if (!resData) {
            resData = ''
          }
          resolve(resData)
        } else {
          let err = {
            code: res.data.code,
            msg: res.data.msg
          }
          reject(err)
        }
      }
    })
  })
}

module.exports = {
  get: GET,
  post: POST
}

使用

const api = require('../../utils/request')
...
api.post('/user/mplogin', { 'code': res.code }).then(res => {
     wx.setStorageSync('access_token', res.access_token)
 }).catch(error=>{
   console.log(error)
 })

至此,登录逻辑便已完成,是不是觉得小程序开发也不过如此呢?由于篇幅有限,今天就分享到这啦,后续我将继续和看官们一起探究以下小程序开发的话题

  • 图片上传
  • 表单提交
  • 级联选择器的实现
  • 如何使用iconfont图标
  • 微信支付
  • 模板消息
  • ....

欢迎看官们持续关注,也欢迎看官们私信告知我其他疑问,我尽量都一一分享,知无不言,言无不尽。

不是结束的结语

打波小小的广告,个人开发的找对象小程序“佛系处对象”已经顺利上线,欢迎读者看官们扫码体验,如果脱单了记得通知我哟!

最后祝大家在事业和爱情的“战场”中都能脱颖而出,顺利吃鸡!

查看原文

imwty 评论了文章 · 2018-07-26

微信小程序开发之从“跳伞”到“吃鸡”

写在前边

微信小程序随着官方开放越多越多的接口,也是变的越来越火了,越来越多的企业已经开始布局小程序生态。所以,对于我们开发者来说,掌握小程序开发显得分外重要。如果点亮了该技能,那么离升职加薪赢取白富美的日子就又近了一步啦!

关于我

笔者算是一个野生的程序猿吧,没有什么大厂经验,搞开发就是一个字,干!从不来虚的。因此,随性的性格造就了我全栈(一窍不通)的本事,做项目那就是一把抓,前后端全包,什么前后端撕逼的问题统统没有了,哈哈哈(想想心里还有点小激动)。不过本文笔者只重点分享小程序开发相关的东西,想了解后端的话,可以坐等我下一次的分享哈哈哈(可能会遥遥无期)。

准备工作

话不多说,进入正题。现在开始分享我是怎么开发完一款小程序的。小程序的官方文档其实写的是很详细很清晰的,相比微信公众号的开发文档来说真的是非常非常的良心,所以开发前浏览一遍开发文档非常关键。下边我罗列一下其他准备工作。

1. 开发工具

工欲善其事,必先利其器,首选的就是把开发环境给弄好咯,我推荐如下:

开发工具

  • 官方微信开发者工具
  • visio studio code

调试工具

  • 官方微信开发者工具

当然开发工具什么的只要自己习惯就好,我的推荐只是参考。笔者最后习惯是直接在官方的开发者工具上编辑加调试。

2. 小程序配置

这块的内容官方文档都有详细的教程,我就不多补充啦。总体来说就是需要去申请一个开发者账号,然后配置小程序的基本信息,比较值得重点说的就是小程序api的配置,官方要求必须是https。说到这里,如果读者你的api接口已经上了https可以跳过了,如果不是,然后你也像笔者一样是全栈开发,那么笔者将告诉你如何快速让接口踏上https的航班。

是的,没错, 就是它!Certbot

在官网上选好http服务器和linux系统后,按照命令一个一个敲,就ok了,简直不要太简单。哈哈哈,至于想深入了解这个东东的话,可以去了解下Let's Encrypt

开发

总算可以正式的开始撸功能了!因为考虑到现在程序猿找对象是真的太难了,所以笔者撸的小程序是一个脱单交友的小程序(单身汪的福音哦),小程序页面也不多,核心就是让用户填写个人信息然后展示出来。是的,就是这么简单!

授权登录

小程序的授权登录其实可以看作是两件事情,授权和登录,这两者是可以单独分开处理的(个人观点,允许反驳)。

授权

对于授权,其实官方已经有讲到,就是换成微信内的授权询问,就是如下这个东西

这个微信授权的询问弹窗之前的版本中只要调用获取用户信息的api,是会自动弹出的,现在小程序做了调整需要自行通过按钮触发,所以这个很蛋疼。那么需要怎么去设计呢,这里也有两个方案,一个是做个单独的页面,另一个方案是做弹窗。个人建议是选方案一,因为这样可以把授权逻辑从页面逻辑里独立出来,方便所有页面渲染前调用。具体coding如下:

在app.js中,判断是否已经授权,如果未授权则跳转到授权页面

App({
  onLaunch: function () {
    ...
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },

在'pages/auth/index.wxml'页面中,使用button做授权按钮

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">微信账号授权登录</button>

'pages/auth/index.js'中定义绑定的回调方法,重新跳转回上一页

Page({
  ...
  // 点击授权后跳回首页
  bindGetUserInfo (e) {
    wx.reLaunch({ url: '../index/index' })
    // 用户已经同意小程序使用获取用户信息功能,后续调用 wx.startRecord 接口不会弹窗询问
  }
})

这样,一个授权逻辑就完成了。

登录

登录的目录其实是和后端交互,需要在服务器端存储当前用户的标识,以便用户下一次登录时服务器知道是谁登录了。做过微信公众号开发的朋友应该都知道,能承担这个作用的角色就是open_id了,所以要实现登录的话,其实就是需要获取当前用户的open_id,官方文档中是这么介绍的:

1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2.开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

所以完成登录的前提就是需要后端提供一个接口,咱们把code传给后端就行了,剩下的工作就是后端去完成啦。

App({
  onLaunch: function () {
    // 授权判断
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },
  // 登录
  wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (res.code) {
          api.post('/user/mplogin', { 'code': res.code }).then(res => {
              wx.setStorageSync('access_token', res.access_token)
          }).catch(error=>{
            console.log(error)
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    })

这里需要多提的一点是关于会话维持的方案,就是如何让服务器端知道访问接口的是谁。笔者提供两个办法:

  1. 通过wx.request()在header中强行组装cookie字符串,来实现传统浏览器上用cookie维持会话的效果
  2. 使用access_token的方式,比如jwt

笔者选择的是第二种,通过后端的登录接口返回token,然后将token存入Storage,然后在发起请求的时候将token封装到http请求体中。 两种方案都可行,读者们可根据自己情况自行实现。 由于wx.request()方法发起请求比较麻烦,还需要处理会话逻辑,所以建议读者们还是进行一次封装,下边贴上笔者的代码:

utils文件夹中创建request.js文件

const domain = "https://domain.com" // 接口域名
function GET(url, params) {
  return request('GET', url, params)
}
function POST(url, params) {
  return request('POST', url, params)
}
function request(method, url, params) {
  return new Promise((resolve, reject) => {
    wx.request({
      url: domain + url,
      data: params,
      method: method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'bearer' + ' ' + wx.getStorageSync('access_token'),
      },
      success(res) {
        if (res.data.code === 100) {
          let resData = res.data.data
          if (!resData) {
            resData = ''
          }
          resolve(resData)
        } else {
          let err = {
            code: res.data.code,
            msg: res.data.msg
          }
          reject(err)
        }
      }
    })
  })
}

module.exports = {
  get: GET,
  post: POST
}

使用

const api = require('../../utils/request')
...
api.post('/user/mplogin', { 'code': res.code }).then(res => {
     wx.setStorageSync('access_token', res.access_token)
 }).catch(error=>{
   console.log(error)
 })

至此,登录逻辑便已完成,是不是觉得小程序开发也不过如此呢?由于篇幅有限,今天就分享到这啦,后续我将继续和看官们一起探究以下小程序开发的话题

  • 图片上传
  • 表单提交
  • 级联选择器的实现
  • 如何使用iconfont图标
  • 微信支付
  • 模板消息
  • ....

欢迎看官们持续关注,也欢迎看官们私信告知我其他疑问,我尽量都一一分享,知无不言,言无不尽。

不是结束的结语

打波小小的广告,个人开发的找对象小程序“佛系处对象”已经顺利上线,欢迎读者看官们扫码体验,如果脱单了记得通知我哟!

最后祝大家在事业和爱情的“战场”中都能脱颖而出,顺利吃鸡!

查看原文

imwty 评论了文章 · 2018-07-26

微信小程序开发之从“跳伞”到“吃鸡”

写在前边

微信小程序随着官方开放越多越多的接口,也是变的越来越火了,越来越多的企业已经开始布局小程序生态。所以,对于我们开发者来说,掌握小程序开发显得分外重要。如果点亮了该技能,那么离升职加薪赢取白富美的日子就又近了一步啦!

关于我

笔者算是一个野生的程序猿吧,没有什么大厂经验,搞开发就是一个字,干!从不来虚的。因此,随性的性格造就了我全栈(一窍不通)的本事,做项目那就是一把抓,前后端全包,什么前后端撕逼的问题统统没有了,哈哈哈(想想心里还有点小激动)。不过本文笔者只重点分享小程序开发相关的东西,想了解后端的话,可以坐等我下一次的分享哈哈哈(可能会遥遥无期)。

准备工作

话不多说,进入正题。现在开始分享我是怎么开发完一款小程序的。小程序的官方文档其实写的是很详细很清晰的,相比微信公众号的开发文档来说真的是非常非常的良心,所以开发前浏览一遍开发文档非常关键。下边我罗列一下其他准备工作。

1. 开发工具

工欲善其事,必先利其器,首选的就是把开发环境给弄好咯,我推荐如下:

开发工具

  • 官方微信开发者工具
  • visio studio code

调试工具

  • 官方微信开发者工具

当然开发工具什么的只要自己习惯就好,我的推荐只是参考。笔者最后习惯是直接在官方的开发者工具上编辑加调试。

2. 小程序配置

这块的内容官方文档都有详细的教程,我就不多补充啦。总体来说就是需要去申请一个开发者账号,然后配置小程序的基本信息,比较值得重点说的就是小程序api的配置,官方要求必须是https。说到这里,如果读者你的api接口已经上了https可以跳过了,如果不是,然后你也像笔者一样是全栈开发,那么笔者将告诉你如何快速让接口踏上https的航班。

是的,没错, 就是它!Certbot

在官网上选好http服务器和linux系统后,按照命令一个一个敲,就ok了,简直不要太简单。哈哈哈,至于想深入了解这个东东的话,可以去了解下Let's Encrypt

开发

总算可以正式的开始撸功能了!因为考虑到现在程序猿找对象是真的太难了,所以笔者撸的小程序是一个脱单交友的小程序(单身汪的福音哦),小程序页面也不多,核心就是让用户填写个人信息然后展示出来。是的,就是这么简单!

授权登录

小程序的授权登录其实可以看作是两件事情,授权和登录,这两者是可以单独分开处理的(个人观点,允许反驳)。

授权

对于授权,其实官方已经有讲到,就是换成微信内的授权询问,就是如下这个东西

这个微信授权的询问弹窗之前的版本中只要调用获取用户信息的api,是会自动弹出的,现在小程序做了调整需要自行通过按钮触发,所以这个很蛋疼。那么需要怎么去设计呢,这里也有两个方案,一个是做个单独的页面,另一个方案是做弹窗。个人建议是选方案一,因为这样可以把授权逻辑从页面逻辑里独立出来,方便所有页面渲染前调用。具体coding如下:

在app.js中,判断是否已经授权,如果未授权则跳转到授权页面

App({
  onLaunch: function () {
    ...
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },

在'pages/auth/index.wxml'页面中,使用button做授权按钮

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">微信账号授权登录</button>

'pages/auth/index.js'中定义绑定的回调方法,重新跳转回上一页

Page({
  ...
  // 点击授权后跳回首页
  bindGetUserInfo (e) {
    wx.reLaunch({ url: '../index/index' })
    // 用户已经同意小程序使用获取用户信息功能,后续调用 wx.startRecord 接口不会弹窗询问
  }
})

这样,一个授权逻辑就完成了。

登录

登录的目录其实是和后端交互,需要在服务器端存储当前用户的标识,以便用户下一次登录时服务器知道是谁登录了。做过微信公众号开发的朋友应该都知道,能承担这个作用的角色就是open_id了,所以要实现登录的话,其实就是需要获取当前用户的open_id,官方文档中是这么介绍的:

1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2.开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

所以完成登录的前提就是需要后端提供一个接口,咱们把code传给后端就行了,剩下的工作就是后端去完成啦。

App({
  onLaunch: function () {
    // 授权判断
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          console.log('已经授权')
        } else {
              // 未授权,跳转到授权页面,必须要用reLauch进行跳转
          wx.reLaunch({
            url: '/pages/auth/index',
          })
          console.log('需要授权')
        }
      }
    })
  },
  // 登录
  wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (res.code) {
          api.post('/user/mplogin', { 'code': res.code }).then(res => {
              wx.setStorageSync('access_token', res.access_token)
          }).catch(error=>{
            console.log(error)
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    })

这里需要多提的一点是关于会话维持的方案,就是如何让服务器端知道访问接口的是谁。笔者提供两个办法:

  1. 通过wx.request()在header中强行组装cookie字符串,来实现传统浏览器上用cookie维持会话的效果
  2. 使用access_token的方式,比如jwt

笔者选择的是第二种,通过后端的登录接口返回token,然后将token存入Storage,然后在发起请求的时候将token封装到http请求体中。 两种方案都可行,读者们可根据自己情况自行实现。 由于wx.request()方法发起请求比较麻烦,还需要处理会话逻辑,所以建议读者们还是进行一次封装,下边贴上笔者的代码:

utils文件夹中创建request.js文件

const domain = "https://domain.com" // 接口域名
function GET(url, params) {
  return request('GET', url, params)
}
function POST(url, params) {
  return request('POST', url, params)
}
function request(method, url, params) {
  return new Promise((resolve, reject) => {
    wx.request({
      url: domain + url,
      data: params,
      method: method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'bearer' + ' ' + wx.getStorageSync('access_token'),
      },
      success(res) {
        if (res.data.code === 100) {
          let resData = res.data.data
          if (!resData) {
            resData = ''
          }
          resolve(resData)
        } else {
          let err = {
            code: res.data.code,
            msg: res.data.msg
          }
          reject(err)
        }
      }
    })
  })
}

module.exports = {
  get: GET,
  post: POST
}

使用

const api = require('../../utils/request')
...
api.post('/user/mplogin', { 'code': res.code }).then(res => {
     wx.setStorageSync('access_token', res.access_token)
 }).catch(error=>{
   console.log(error)
 })

至此,登录逻辑便已完成,是不是觉得小程序开发也不过如此呢?由于篇幅有限,今天就分享到这啦,后续我将继续和看官们一起探究以下小程序开发的话题

  • 图片上传
  • 表单提交
  • 级联选择器的实现
  • 如何使用iconfont图标
  • 微信支付
  • 模板消息
  • ....

欢迎看官们持续关注,也欢迎看官们私信告知我其他疑问,我尽量都一一分享,知无不言,言无不尽。

不是结束的结语

打波小小的广告,个人开发的找对象小程序“佛系处对象”已经顺利上线,欢迎读者看官们扫码体验,如果脱单了记得通知我哟!

最后祝大家在事业和爱情的“战场”中都能脱颖而出,顺利吃鸡!

查看原文

认证与成就

  • 获得 228 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-02-16
个人主页被 1.7k 人浏览