jirengu-inc / jrg-project-5

一个在线简历编辑器教程。
102 stars 14 forks source link

No Backend! #4

Open FrankFang opened 7 years ago

FrankFang commented 7 years ago
cp -r step-3 step-4
cd step-4
webpack --watch #然后新开窗口写代码,这个不要关

我们需要一台服务器吗?

上个任务中,我们的数据存在 localStorage 中,这样有很多弊端:

  1. 如果用户清空缓存,那么 todoList 就没了……
  2. 如果用户换一台电脑,那么 todoList 也看不见了……

所以,我们是不是应该买一台服务器来存所有用户的数据?

可以,但是服务器是要钱的,我们现在还没必要花这个钱。

No Backend(无后台)

没有服务器能不能存数据呢? 答案是「不能,但是又能」。

说「不能」是因为无论如何,我们都需要一个地方存数据。 说「能」是因为我们不用自己买服务器。

今天我们使用 LeanCloud 的免费服务来存储我们的所有数据。

创建 LeanCloud 账户

你需要去 https://leancloud.cn 创建一个账户。

创建成功后,你需要验证你的邮箱,否则无法创建应用。

创建 resumer 应用

如下图操作:

创建成功后就放在那里,因为接下来我们要按照 LeanCloud 的「JavaScript SDK 文档」来开发登录、注册功能。

登录和注册

首先还是用 HTML 把界面做出来。

页面分区

目前我们的页面的结构是

div#app > div.newTask + ol.todos

我们要改成

div#app 
  section#signInAndSignUp
  section#todo
     div.newTask + ol.todos

用一个 section#todo 将原有内容包起来,然后新建一个 section#signInAndSignUp(注意大小写)

最终结果是:

  <div id="app">
      <section id="signInAndSignUp">
        <div>
          <label><input type="radio" name="type" value="signUp">注册</label>
          <label><input type="radio" name="type" value="login">登入</label>
        </div>
        <div class="signUp">
          <form>
            <div class="formRow">
              用户名<input type="text">
            </div>
            <div class="formRow">
              密码<input type="password">
            </div>
            <div class="formActions">
              <input type="submit" value="注册">
            </div>
          </form>
        </div>
        <div class="login">
          <form>
            <div class="formRow">
              用户名<input type="text">
            </div>
            <div class="formRow">
              密码<input type="password">
            </div>
            <div class="formActions">
              <input type="submit" value="登入">
            </div>
          </form>
        </div>
      </section>

      <section id="todo">
        <div class="newTask">
          <input type="text" v-model="newTodo" @keypress.enter="addTodo">
        </div>
        <ol class="todos">
          <li v-for="todo in todoList">
            <input type="checkbox" v-model="todo.done"> {{ todo.title }}

            <span v-if="todo.done">已完成</span>
            <span v-else>未完成</span>

            <button @click="removeTodo(todo)">X</button>
          </li>
        </ol>
      </section>
    </div>

预览图:

Tab 切换

我们希望

  1. 用户点击「〇注册」这个 radio button 的时候显示注册表单
  2. 用户点击「〇登入」这个 radio button 的时候显示登入表单
  3. 默认显示注册表单

所以我们需要加一个变量,叫做 actionType,它有两个取值:'signUp' 和 'login',都是字符串。

app.js

...
el: '#app',
  data: {
    actionType: 'signUp',
...

然后将 actionType 与 radio button 绑定(使用 v-model):

<section id="signInAndSignUp">
        <div>
          <label><input type="radio" name="type" v-model="actionType" value="signUp">注册</label>
          <label><input type="radio" name="type" v-model="actionType" value="login">登入</label>
        </div>
...

最后让两个表单根据 actionType 来显示和隐藏(注意单引号,为什么要加单引号呢?想想):

        <div class="signUp" v-if="actionType=='signUp'">
          <form>
            <div class="formRow">
              用户名<input type="text">
            </div>
            <div class="formRow">
              密码<input type="password">
            </div>
            <div class="formActions">
              <input type="submit" value="注册">
            </div>
          </form>
        </div>
        <div class="login" v-if="actionType=='login'">
          <form>
            <div class="formRow">
              用户名<input type="text">
            </div>
            <div class="formRow">
              密码<input type="password">
            </div>
            <div class="formActions">
              <input type="submit" value="登入">
            </div>
          </form>
        </div>

这样一来,用户点击 radio button 时就会改变 actionType 的值,actionType 的值一变,两个表单就会一个隐藏,一个显示。

注册

要实现注册功能,首先我们要用数据来表达表单里的每个字段。

  data: {
    actionType: 'signUp',
    formData: {
      username: '',
      password: ''
    },

然后将 input 与数据绑定起来,另外还要绑定 form 的 submit 事件:

      <div class="signUp" v-if="actionType === 'signUp'">
        <form @submit.prevent=signUp> <!--👈-->
          <div class="formRow">
            用户名<input type="text" v-model="formData.username"> <!--👈-->
          </div>
          <div class="formRow">
            密码<input type="password" v-model="formData.password"> <!--👈-->
          </div>
          <div class="formActions">
            <input type="submit" value="注册">
          </div>
        </form>
      </div>

接下来我们来完善 signUp 的逻辑。在写代码之前,我们需要阅读 leanCloud 的文档:

  1. 安装 LeanCloud SDK https://leancloud.cn/docs/sdk_setup-js.html
    npm install leancloud-storage --save
  2. 初始化 https://leancloud.cn/docs/sdk_setup-js.html#初始化 app.js

    import Vue from 'vue'
    import AV from 'leancloud-storage'
    
    var APP_ID = '8axnRtGoxCJhEzsvNPEAHnol-gzGzoHsz';
    var APP_KEY = '0YH4XkYflb4CUPfA743TGj8G';
    AV.init({
      appId: APP_ID,
      appKey: APP_KEY
    });
    
    var app = new Vue({  ...
  3. 验证 LeanCloud SDK 安装成功 https://leancloud.cn/docs/sdk_setup-js.html#验证

    ...
    AV.init({
      appId: APP_ID,
      appKey: APP_KEY
    });
    
    var TestObject = AV.Object.extend('TestObject');
    var testObject = new TestObject();
    testObject.save({
      words: 'Hello World!'
    }).then(function(object) {
      alert('LeanCloud Rocks!');
    })
    
    var app = new Vue({ ...

    刷新 page.html 后看到 如果可以用 AV 对象了,然后把上面的验证代码删掉。

接下来我们看 LeanCloud 关于注册的文档,如果你看不懂,可以使用我们的「copy-run-modify」套路。按照文档的例子,我们写出这样的代码:

  methods: {
    addTodo: function(){
      ...
    },
    removeTodo: function(todo){
      ...
    },
    signUp: function () {
      let user = new AV.User();
      user.setUsername(this.formData.username);
      user.setPassword(this.formData.password);
      user.signUp().then(function (loginedUser) {
        console.log(loginedUser);
      }, function (error) {
      });
    }
  }

刷新页面,我们选择注册,然后用户名填入「123123」,密码填入「123123」,先别急着提交,打开开发者工具,切到 Network,然后提交:

你会看到发了两个请求到 LeanCloud 的服务器,这两个请求就是向 LeanCloud 的服务器存入用户名和密码。 然后再切到 console,你会看到打印出的 loginedUser:

这里我们只关注它的三个属性:attributes, createdAt, id。

其中 attributes 就是我们传给数据库的 username(我们不是还传了一个 password 吗?服务器是不会把 password 传给前端的)

createdAt 是这个数据创建的时间,id 是用户的 id,也是我们区别用户的唯一凭据。

好了,到此为止,我们的注册功能已经做好了。是不是很简单。 目前的代码快照在这里:https://github.com/jirengu-inc/jrg-project-5/blob/a2690c8efe55262e7850ca3e807fa4852198ffd5/step-4/app.js

去数据库看看这个用户

你到 LeanCloud 的 控制面板 点击「存储」,然后点击「_User」就能看到这个用户的数据了:

登入

注册做完了接下来是登入,步骤也差不多。

首先绑定数据,我们复用注册的 formData 这个数据,因为

  1. 字段相同,都是 username 和 password
  2. 这样一来用户切换登录注册的时候,已输入的数据就不需要再输入一遍
  3. 当然你想用另一个数据 formData2 也行
      <div class="login" v-if="actionType === 'login'"> 
        <form @submit.prevent="login"> <!--👈-->
          <div class="formRow">
            用户名<input type="text" v-model="formData.username"> <!--👈-->
          </div>
          <div class="formRow">
            密码<input type="password" v-model="formData.password"> <!--👈-->
          </div>
          <div class="formActions">
            <input type="submit" value="登入">
          </div>
        </form>
      </div>

然后看一下 LeanCloud 文档,这次大家自己找文档,找不到就用 Google 搜,你会找到的。 看懂文档你就可以添加 login 这个方法了:

    signUp: function () {
      let user = new AV.User();
      user.setUsername(this.formData.username);
      user.setPassword(this.formData.password);
      user.signUp().then(function (loginedUser) {
        console.log(loginedUser);
      }, function (error) {
      });
    },
    login: function () {
      AV.User.logIn(this.formData.username, this.formData.password).then(function (loginedUser) {
        console.log(loginedUser);
      }, function (error) {
      });
    }

接下来刷新 page.html,选中「登入」,输入用户名「123123」,密码「123123」。 观察 network 和 console,会得到跟注册类似的结果。

好了,登录功能就完成了。

登录前后

我们希望

那么我们如何判断用户是否已登录。

LeanCloud 文档说 AV.User.current() 可以获取当前登录的用户。那么我们这么做:

app.js

data: {
    ...
    todoList: [],
    currentUser: null,  // 👈
  },

app.js

    signUp: function () {
      let user = new AV.User();
      user.setUsername(this.formData.username);
      user.setPassword(this.formData.password);
      user.signUp().then((loginedUser) => { // 👈,将 function 改成箭头函数,方便使用 this
        this.currentUser = this.getCurrentUser() // 👈
      }, (error) => {
        alert('注册失败') // 👈
      });
    },
    login: function () {
      AV.User.logIn(this.formData.username, this.formData.password).then((loginedUser) => { // 👈
        this.currentUser = this.getCurrentUser() // 👈
      }, function (error) {
        alert('登录失败') // 👈
      });
    },
    getCurrentUser: function () { // 👈
      let {id, createdAt, attributes: {username}} = AV.User.current()
      // 上面这句话看不懂就得看 MDN 文档了
      // 我的《ES 6 新特性列表》里面有链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
      return {id, username, createdAt} // 看文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#ECMAScript_6%E6%96%B0%E6%A0%87%E8%AE%B0
    }

page.html

    <section id="signInAndSignUp" v-if="!currentUser">
    ...
    <section id="todo" v-if="currentUser">

然后刷新 page.html ,登录之后,登录表单就不见啦。

其他功能

后面的功能我写教程写不动了,大家看我的 commit

添加登出功能 如果用户已经登入,就直接展示 todo

致饥人谷学员

搞定上面的教程,满足以下功能:

  1. 可注册
  2. 可登入
  3. 可登出

挑战

  1. 在界面上显示当前用户的 username
  2. 将 TodoList 存到 User 名下,而不是存到 localStorage。(这是下个任务里我们要做的事情)

预览地址:https://jirengu-inc.github.io/jrg-project-5/step-4/page.html 代码:看本仓库的 step-4 目录

starlikerain commented 7 years ago

dogeeeeeeeee


small little cute pure code

fixed dddddddemo

FrankFang commented 7 years ago

@starlikerain 你加个 bool 干什么,直接双向绑定,谁跟你说 radio button 一定要绑定 bool 呀

hungryYang commented 7 years ago

page code YCR

TerenYeung commented 7 years ago

yjl for v-o-r task-04 source demo 蟹蟹方方老师,这部分学到很多新知识

zhangjiuyi commented 7 years ago

效果 ZJY 不知道为什么 我的bundle文件变的特别大..我看了下有两万多行..一打开电脑风扇就转不停,而且webstorm提示这文件有错误,但是运行时 窗口没有报任何错误..

ReedSun commented 7 years ago

完成咯 演示 代码仓库 期待下一个任务~

whiteblank commented 7 years ago

代码 预览 GJC

lzm320856 commented 7 years ago

预览 代码 LZM,搞定了但这玩意用的不熟,存储数据用的很蠢的办法,等下一期方方揭晓标准答案

0xjeso commented 7 years ago

预览 代码 SJ 还没挑战把数据存到User名下,好饿,趁着还没黑天,我先把早饭吃了···

yukui630 commented 7 years ago

预览 代码 老师辛苦了。

lightbuild commented 7 years ago

预览 代码 RL,方方老师辛苦了。

mimi3824ku commented 7 years ago

预览 代码 LWE

ab690257072 commented 7 years ago

预览 代码 ZLQ存储到User还是没搞明白,方老师辛苦啦

LisaLi85 commented 7 years ago

预览 代码 LLL

Zegendary commented 7 years ago

page code ZXW

Rice-F commented 7 years ago

预览 代码 SJ,辛苦啦

JaeJiang commented 7 years ago

预览 代码 当个反面教材把😐

Hsyneve commented 7 years ago

源码 预览 hsy

baixiaoji commented 7 years ago

演示 代码 ZLJ @FrankFang

WangXiaoyugg commented 7 years ago

代码 预览 WXY 方方老师辛苦了

muxi7 commented 7 years ago

代码 预览 zw 挑战2还没摸索出来,今天回家了,回家在做

wlf1112 commented 7 years ago

代码 预览 WLF 方方老师新年快乐!

batman-1 commented 7 years ago

代码 预览

方方老师 新年快乐!

Panda-HJN commented 7 years ago

过个年,胖五斤 - - ||| 代码 浏览

HJN

have-not-BUG commented 7 years ago

预览地址 代码 by:LC 方方老师辛苦了~

candy252324 commented 7 years ago

预览 代码地址 CJH

code-zhangrui commented 7 years ago

预览 代码

任务10班 张睿

chaocool commented 7 years ago

预览 代码 CJC

FrankFang commented 7 years ago

@chaocool BUG1: 每次刷新页面之后,登录的用户就不见了

chaocool commented 7 years ago

@FrankFang 已经修复 辛苦方方老师 预览

JayChenFE commented 7 years ago

预览 代码 任务3 陈捷

JeromeYangtao commented 7 years ago

代码 预览

superDCF commented 7 years ago

预览

MasterGaoJin commented 7 years ago

preview code

n313893254 commented 7 years ago

预览 代码

success-cg commented 7 years ago

陈功-task4 demo 老师辛苦了

kumabearplus commented 7 years ago

预览 代码

boloog commented 7 years ago

预览 boloog

selectyang commented 7 years ago

代码 预览

jamesXiao-coder commented 7 years ago

代码

jettzhang95 commented 7 years ago

Preview Code

robbchan commented 7 years ago

预览 代码

zhaipanyu commented 7 years ago

预览 代码

imgwho commented 7 years ago

预览 仓库 1705 郭文华

forsuccess commented 7 years ago

预览

tcitds1 commented 7 years ago

预览

HuangHongRui commented 7 years ago

Code Show

andreaxiang commented 7 years ago

demo预览

huoguozhang commented 7 years ago

任务4

ZhyCong commented 7 years ago

任务4 源码