jirengu-inc / jrg-project-5

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

Get the shit done! #3

Open FrankFang opened 7 years ago

FrankFang commented 7 years ago

首先新建项目目录

cp -r step-2 step-3

这样我们就可以基于之前的代码做新任务,同时不改动之前的代码。

学会一个框架的最好办法

学会一个框架的最好办法那就是——做毁一个项目。

接下来我们就要用 Vue.js 做一个待办事项小应用。简单起见,我们就不写 CSS 了,只用 HTML 和 JS 搞定。

我们的目标只有一个,就是搞清楚怎样用 Vue.js 进行开发。

需求

这个项目的英文名就暂定为 Todo,它有以下功能:

  1. 用户可以新建一个待办事项
  2. 用户可以删除一个待办事项
  3. 用户可以将一个待办事项标记为已完成
  4. 用户刷新页面之后,待办事项还在

Getting Started

由于我们现在对 Vue.js 还一无所知,所以就走一步算一步先。

首先我们用 HTML 描绘一下我们的界面

page.html

<html>
  <head>
    <meta charset=utf-8>
  </head>
  <body>
    <div id="app">
      <div class="newTask">
        <input type="text">
      </div>
      <ol class="todos">
      </ol>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>
  1. charset 要加上,不然出现中文就乱码了
  2. 加一个 div#app,用于给 Vue 初始化
  3. div.newTask > input 用于让用户输入待办的内容
  4. ol.todos 用于容纳所有待办,每个待办就是一个 <li>

添加待办

接下来我们做第一个需求,添加待办。

做之前你要想好流程:

  1. 用户输入待办内容
  2. 用户按下回车
  3. 新的待办出现在 ol.todos

好的,开始做了。

import Vue from 'vue'

var app = new Vue({
  el: '#app',
  data: {
    newTodo: '',
    todoList: []
  }
})                                                               

我们用 todoList 数组作为所有待办事项的容器,newTask 作为 input 的值。

为什么要有 data?

这里出现了第一个令我们费解的地方——「为什么我们需要将 DOM 与 JS 变量(data)对应起来」。

如果我们用 jQuery 来写,直接在 input 的键盘事件中取出 input.value,构造一个 <li>,插入到 ol.todos 就完了嘛。对不对?

这就是框架和库的区别了。jQuery 作为一个库,你想怎么用就怎么用,但是你在使用一个框架的时候,有很多「指导思想」是你要遵循的。Vue 的指导思想之一就是「尽量不要操作 DOM」,因为这个框架会帮你操作 DOM。

绑定数据

      <div class="newTask">
        <input type="text" v-model="newTodo">
      </div>

这一句将 input.value 与 data.newTodo 绑定起来了,而且是双向的:

  1. 只要 input.value 被用户改了,data.newTodo 就会变成一样的值;
  2. 只要 data.newTodo 被 JS 改了,input.value 就会变成一样的值。

怎么验证呢?

首先我们来验证在 JS 里改变 newTodo,input.value 就会变:

import Vue from 'vue'

var app = new Vue({
  el: '#app',
  data: {
    newTodo: '',
    todoList: []
  },
  created: function(){
    let i = 0
    setInterval(()=>{
      this.newTodo = i // this.newTodo 就是 data.newTodo,实际上 this.newTodo 是 data.newTodo 的代理
      i+= 1
    },1000)
  }
})                                                               

运行 webpack,打开 page.html,可以看到 input 的值自己变化着。

Tips:如果你不想每次都运行 webpack,那么你可以新开一个命令行窗口,运行 webpack --watch,那么 webpack 就会在每次 JS 文件变化时自动重新运行。

接下来验证 input.value 改变会导致 data.newTodo 变化:

import Vue from 'vue'

var app = new Vue({
  el: '#app',
  data: {
    newTodo: '',
    todoList: []
  },
  created: function(){
    setInterval(()=>{
      console.log(this.newTodo)
    },1000)
  }
})

F12 打开 console,然后在 input 里输入一些字符试试。

以上,就是双向绑定。

细节请自行查看 https://cn.vuejs.org/v2/guide/forms.html

绑定事件

我们需要在用户敲击 回车 的时候,在 data.todoList 里新建一个对象。

如何监听用户的键盘事件呢?请查看 https://cn.vuejs.org/v2/guide/events.html

看完这一节,你就能写出以下代码了:

app.js

import Vue from 'vue'

var app = new Vue({
  el: '#app',
  data: {
    newTodo: '',
    todoList: []
  },
  methods: {
    addTodo: function(){
      this.todoList.push({
        title: this.newTodo,
        createdAt: new Date()
      })
      console.log(this.todoList)
    }
  }
})   

page.html

      <div class="newTask">
        <input type="text" v-model="newTodo" @keypress.enter="addTodo">
      </div>

这时你刷新 page.html,在 input 里面输入 回车,就会在控制台看到 todoList 不是空字符串了:

展示新待办

虽然 data.todoList 已经含有一个新的项目了,但是页面里却没有展示。

根据 https://cn.vuejs.org/v2/guide/list.html 写出下面代码:

page.html

      <ol class="todos">
        <li v-for="todo in todoList">
          {{ todo.title }}
        </li>
      </ol>

然后重新刷新页面,在 input 输入一些字符,回车。你就会看到新增成功了:

优化

按照正常人的逻辑,添加成功后,input 的值应该清空,于是我们改写 app.js:

app.js

  methods: {
    addTodo: function(){
      this.todoList.push({
        title: this.newTodo,
        createdAt: new Date()
      })
      this.newTodo = ''  // 变成空
    }
  }

刷新试试效果如何吧。

标记为完成

思路:

  1. 给每一个 todo 添加一个 done 属性
  2. 给每一个 <li> 里面添加一个 checkbox
  3. 参考 https://cn.vuejs.org/v2/guide/forms.html#复选框 ,将 done 和 checkbox 双向绑定。

代码如下:

app.js

  methods: {
    addTodo: function(){
      this.todoList.push({
        title: this.newTodo,
        createdAt: new Date(),
        done: false // 添加一个 done 属性
      })
      this.newTodo = ''
    }
  }

page.html

      <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>
        </li>
      </ol>

效果如下:

删除待办

思路:

  1. 在每一项后面添加一个删除按钮
  2. 点击按钮则从 data.todoList 中删除该项

代码如下:

app.js

  methods: {
    addTodo: function(){
      this.todoList.push({
        title: this.newTodo,
        createdAt: new Date(),
        done: false // 添加一个 done 属性
      })
      this.newTodo = ''
    },
    // 加了👇这个函数
    removeTodo: function(todo){
      let index = this.todoList.indexOf(todo) // Array.prototype.indexOf 是 ES 5 新加的 API
      this.todoList.splice(index,1) // 不懂 splice?赶紧看 MDN 文档!
    }
  }

page.html

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

效果如下(GIF有点大,请稍等或开代理)

保存待办事项

我们发现每次刷新页面,待办就没了。

这是因为这些代码都保存在内存里,而内存是无法持久的。所以我们选择保存在 localStorage 中。

思路:

  1. 在用户关闭页面前,将数据保存在 localStorage 里
  2. 在用户进入页面后,立刻读取 localStorage

代码如下:

app.js

var app = new Vue({
  el: '#app',
  data: {
    newTodo: '',
    todoList: []
  },
  created: function(){
    // onbeforeunload文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onbeforeunload
    window.onbeforeunload = ()=>{
      let dataString = JSON.stringify(this.todoList) // JSON 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON
      window.localStorage.setItem('myTodos', dataString) // 看文档https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage
    }

    let oldDataString = window.localStorage.getItem('myTodos')
    let oldData = JSON.parse(oldDataString)
    this.todoList = oldData || []

  },
  methods: { ...

由于我们只涉及数据的变化,所以 page.html 不变。

让我看到你的应用

你可以将这个应用部署到 GitHub Pages 上。

  1. 新建一个 GitHub repo,或者使用你现有的 GitHub repo。
  2. 在 repo 的 Settings 页面里将 GitHub Pages 功能打开,并选中 master 分支,点 Save,你就会得到一个「部署地址」,比如我的地址是 https://jirengu-inc.github.io/jrg-project-5/
  3. 在这个部署地址后面接上你 repo 里的文件路径,也可以预览 HTML 了。比如我的预览页面是:https://jirengu-inc.github.io/jrg-project-5/step-3/page.html
  4. 由于国内访问 GitHub 较慢,所以你预览的时候可能看到 {{ }} 标记,不要紧,等一会就好。如果你不想让用户看见这些,可以看 https://cn.vuejs.org/v2/api/#v-cloak

致饥人谷学员

Just get this shit done.

把你的预览页面发到下面,你就成功了。

如果还有时间,建议看看 TodoMVC 项目。比我们这个应用复杂一丢丢,你已经可以独自完成 TodoMVC 了,如果你完成了,把你的链接放在下面单独 at 我。

挑战

  1. 你能否把 newTodo 的内容也保存下来,下次用户进入页面时,会显示之前输入但是还未提交的 newTodo 内容?
  2. 你能否把这个页面美化一下?
  3. 你能否添加「友好的」时间展示,让用户知道这个 todo 是什么时候创建的。
FrankFang commented 7 years ago

代码在这: https://github.com/jirengu-inc/jrg-project-5/tree/master/step-3

Zegendary commented 7 years ago

page code @FrankFang

wlf1112 commented 7 years ago

代码 展示 WLF

FrankFang commented 7 years ago

@wlf1112 bug 1: 刷新页面后待办就木有了~

wlf1112 commented 7 years ago

老师,改完了@FrankFang

TerenYeung commented 7 years ago

yjl for v-o-r task-03 source demo

lzm320856 commented 7 years ago

代码 展示 LZM

hungryYang commented 7 years ago

page code YCR

starlikerain commented 7 years ago

dogeeeeeeeee


样式加好了 demo Source Code

FrankFang commented 7 years ago

@lzm320856 无法将多个标记为已完成,报错:

Uncaught SyntaxError: Identifier 'CANDIDATE_MIME_TYPES' has already been declared
    at (index):1
ReedSun commented 7 years ago

这里是效果 这里是代码

whiteblank commented 7 years ago

代码 预览 GJC

whiteblank commented 7 years ago

@FrankFang 已修改

zhangjiuyi commented 7 years ago

效果 代码 ZJY

FrankFang commented 7 years ago

@zhangjiuyi 界面很小清新哦

candy252324 commented 7 years ago

效果 代码地址CJH

WangXiaoyugg commented 7 years ago

效果 代码地址 WXY

FrankFang commented 7 years ago

@WangXiaoyugg 永远不要给中文字加斜体样式

LisaLi85 commented 7 years ago

预览 代码 LLL

lightbuild commented 7 years ago

预览 代码 任磊6-任磊

FrankFang commented 7 years ago

@lightbuild 使用 Date.prototype.Format 改写 Date 不太好,你可以用一个 formatDate 函数来做

function formatDate(date, format){...}
0xjeso commented 7 years ago

预览 代码 SJ

chaocool commented 7 years ago

预览 代码 CJC

年底挤奶时间哇

ab690257072 commented 7 years ago

预览 源码 ZLQ

FrankFang commented 7 years ago

@ab690257072 BUG 1:你在页面上操作一段时间之后,就会发现新增的待办默认是「已完成」的。

ab690257072 commented 7 years ago

@FrankFang 已修改,麻烦方方老师啦

FrankFang commented 7 years ago

@wlf1112 都是自己写的么,很棒啊

wuhanjun commented 7 years ago

预览地址 代码地址 WHJ

wlf1112 commented 7 years ago

我参考官网的了@FrankFang

lightbuild commented 7 years ago

@FrankFang 方方老师辛苦了,已修改。

Rice-F commented 7 years ago

预览 代码 方方老师辛苦了~ SJ

mimi3824ku commented 7 years ago

预览 代码 方老师辛苦~ LWE

lzm320856 commented 7 years ago

@FrankFang 已修改

FrankFang commented 7 years ago

@WuHanJun 英文有待改进……

have-not-BUG commented 7 years ago

运行预览地址 代码地址 老师辛苦啦~ by:LC

muxi7 commented 7 years ago

代码地址 预览地址

老师辛苦了~ ZW

yukui630 commented 7 years ago

效果预览 代码

Panda-HJN commented 7 years ago

效果 代码 惭愧,做晚了

HJN

xiayujlu commented 7 years ago

预览 代码 老师辛苦!尝试优化ING

FrankFang commented 7 years ago

@WangXiaoyugg bug 1: 输入框里默认有个数字1,不是我输入的。其他都 OK

myql commented 7 years ago

预览 代码 WQ

JaeJiang commented 7 years ago

预览 /🤒来迟了 代码

Josh 姜杭轩

JayChenFE commented 7 years ago

预览 代码

迟到了抱歉 任务3-陈捷

Hsyneve commented 7 years ago

我是源码 我是预览 最近太忙了 实在抱歉 但我还是个好学森

baixiaoji commented 7 years ago

预览 源码 ZLJ 来迟了 @FrankFang

batman-1 commented 7 years ago

代码 预览 任务8 - 肖承华 后知后觉的以为要做完所有任务才开始毕设 - -.

code-zhangrui commented 7 years ago

预览 代码

任务10班 张睿

sfyr111 commented 7 years ago

待办事项预览 杨然

LuoQaxa commented 7 years ago

预览 代码

Anticlimax commented 7 years ago

预览 代码