日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

Vue 不睡觉教程3 - 来点实在的:自动计算剩余时间的任务列表

發布時間:2023/12/20 vue 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Vue 不睡觉教程3 - 来点实在的:自动计算剩余时间的任务列表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

名字靈感來自我的書《HBase不睡覺書》 意為讓你看了也不會想睡覺的教程 :)

目標

前兩課教的是入門和文件結構。都沒有什么實在的東西。這次我們要來點實在的。我們要做出一個待辦列表。這個待辦列表有以下特點:

  • 可以自動從文本中抽取出這件事情的開始時間
  • 可以顯示當前距離這件事情的開始時間還有多久,比如:23:40 回家 (還有 6 小時 36 分 15 秒)
  • 如果當前時間已經超過了計劃時間,則以灰色字體顯示任務,并加上刪除線
  • 通過這個例子我們可以學到以下知識點

  • v-for屬性
  • v-bind:key屬性
  • v-on屬性
  • 在vue中使用bootstrap
  • 在vue中使用localStorage
  • watch屬性
  • computed屬性
  • 在vue中定義私有方法
  • webpack自動打包
  • v-if, v-else-if, v-else屬性
  • v-show屬性
  • 背景

    • vue版本:2.5.16
    • 文件結構基于上節課的文件結構: Vue不睡覺教程2?大家可以直接從?https://github.com/alexxiyang/learn-vue 下載源碼,下載后使用git checkout lesson2 命令切換到lesson2的源碼

    注意事項

    ?在說本節課的步驟之前,先提醒大家,該完代碼記得用以下命令編譯后才能用瀏覽器看到你的更改

    npx webpack

    編譯后記得要訪問的頁面文件不是根目錄下的index.html。那只是源文件。你需要訪問 dist/index.html。

    創建TodoList組件

    修改App.vue

    我們先來構建項目框架。這個項目只有一個組件:TodoList。

    將App.vue中之前的?import 引用 修改為 import TodoList from './components/TodoList' 就像這樣

    import TodoList from './components/TodoList.vue'

    然后在template模板代碼塊中引用它,并在components對象中引用它。修改完的App.vue是這樣的:

    <template><div id="app"><TodoList/></div> </template><script> import TodoList from './components/TodoList.vue'export default {name: 'app',components: {TodoList} } </script>

    新建TodoList.vue組件

    將HelloVue.vue刪掉。然后在src/components文件夾下新建TodoList.vue組件,組件內容為

    <template><div id="todolist">{{ message }}</div> </template><script> export default {name: 'TodoList',data: function() {return {message: '這是一個待辦列表'}} } </script>

    照例使用 npx webpack打包,然后訪問?http://learn-vue/dist/index.html 。如果成功,你就可以 看到 “這是一個待辦列表”?的字樣。

    顯示任務列表(v-for)

    既然是一個待辦列表,那么核心的數據對象就應該是一個array。讓我們來新建這個array

    data: function() {return {taskList: ["7:00 學英語", "10:00 學Vue"]}}

    我們來使用v-for來顯示它

    <template><div id="todolist"><table><thead><th>任務</th></thead><tbody><tr v-for="task in taskList"><td>{{ task }}</td></tr></tbody></table></div> </template>

    顯示的效果為:

    如果你的task是一個object,你可以使用以下方式來顯示它的屬性

    <tr v-for="task in taskList"><td>{{ task.id }} {{ task.name}}</td> </tr>

    如果你使用的是 visual studio code,那么有可能看到以下錯誤提示:

    Elements in iteration expect to have 'v-bind:key' directives.

    這是因為當vue要求當使用v-for來顯示列表時,需要使用v-bind:key來標定列表主鍵,就像這樣

    <tr v-for="task in taskList" v-bind:key="task.id"><td>{{ task.id }} {{ task.name }}</td></tr>

    因為我們的例子過于簡單了,每個紀錄只是一行字符串,所以可以忽略這個錯誤提示。在本例中我們不需要理會這個錯誤提示。但是在實際的項目中,請一定加上:key。

    為什么要加上v-bind:key?

    以下引用自vue官網:

    當 Vue.js 用?v-for?正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的?track-by="$index"?。

    這個默認的模式是高效的,但是只適用于不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出

    為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一?key?屬性。理想的?key?值是每項都有的唯一 id。這個特殊的屬性相當于 Vue 1.x 的?track-by?,但它的工作方式類似于一個屬性,所以你需要用?v-bind?來綁定動態值?

    簡而言之就是:vue為了性能考慮,默認復用頁面上的dom元素。為了防止你的列表元素不更新,就要用key告訴vue,這些dom元素是不一樣的。

    添加任務按鈕(v-on)

    在<table>元素上面添加一個<button>組件,用來增加任務

    <button>添加任務</button>

    接下來,我們需要用到v-on語法來為按鈕添加對click事件的綁定

    <button v-on:click="addTask">添加</button>

    由此可見,v-on的語法就是 v-on:<事件名>=“js語句或者js方法名”

    寫好了模板,接下來就是在default對象中增加methods屬性,并添加addTaks方法了

    methods: {addTask: function(event) {this.taskList.push("新的待辦任務");}}

    完整的default對象為

    export default {name: 'TodoList',data: function() {return {taskList: ["7:00 學英語", "10:00 學Vue"]}},methods: {addTask: function(event) {this.taskList.push("新的待辦任務");}} }

    執行效果就是,每次點擊添加按鈕,就會新增一個任務

    美化頁面(bootstrap, css-loader, style-loader)

    我覺得這樣的頁面也太丑了,所以我們來為頁面加入bootstrap。直接使用原生bootstrap比較麻煩,我們使用bootstrap-vue來為vue項目添加bootstrap:

    $ npm i --save bootstrap-vue

    然后我們在main.js中寫上對BootstrapVue的引用,以及相關css的引用

    import BootstrapVue from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css'Vue.use(BootstrapVue);

    如果你現在執行npx webpack一定會看到如下錯誤

    ERROR in ./node_modules/bootstrap-vue/es/components/alert/alert.css 1:0 Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type.

    這是因為你目前還沒有為webpack.config.js添加css的loader。所以webpack不認識.css文件。

    我們來安裝跟css相關的loader

    $ npm i --save style-loader css-loader

    然后在webpack.config.js的rules節點中編寫規則來使用它

    rules: [{test: /\.vue$/,loader: 'vue-loader',},{test: /\.css$/,use: ['style-loader','css-loader']}]

    現在我們就可以使用npx webpack命令來打包項目了。

    打包好后,你再看頁面,會有些許的變化,但是變化不大。這是因為我們還沒有真正的使用bootstrap。現在我們來為頁面做以下美化

    ?

    • 為todoList根div增加class: container
    • 為button按鈕增加class:?btn btn-primary m-4
    • 為table增加class:?table m-4

    然后再來看看我們的頁面:

    ?

    這下好看多了。

    添加任務功能

    接下來,我們增加“添加任務”的功能。首先我們要添加一個用來輸入任務內容的input輸入框。但是直接在button右邊添加輸入框看起來又很丑。所以我打算從bootstrap的網站上復制一段<button>和<input>都包含在內的布局代碼,就像這段

    <div class="input-group mb-3"><div class="input-group-prepend"><button class="btn btn-outline-secondary" type="button">Button</button></div><input type="text" class="form-control" placeholder="" aria-label="" aria-describedby="basic-addon1"> </div>

    將其改造成我們需要的樣子:

  • 將<button>元素的文字改為添加并加上v-on:click="addTask"屬性
  • 將<input>元素的placeholder屬性修改為“請輸入任務內容”,并加上id="task_content"方便定位。
  • 然后,將之前的

    <button type="button" class="btn btn-primary m-4" v-on:click="addTask">添加</button>

    替換為我們修改后的代碼塊

    <div class="input-group mb-3"><div class="input-group-prepend"><button class="btn btn-outline-secondary" type="button" v-on:click="addTask">添加</button></div><input type="text" id="task_content" class="form-control" placeholder="請輸入任務內容" aria-label="" aria-describedby="basic-addon1"></div>

    接下來,我們來修改addTask任務。由于vue將對象和html dom元素進行了雙向綁定,原來我們需要用jquery來操作dom元素的大量代碼就被修改成了一行代碼

    this.taskList.push(task_content.value);

    加上獲取任務內容輸入框和清空輸入框內容的代碼,總共只需要三行代碼:

    addTask: function(event) {// 獲取任務內容let task_content = document.querySelector("#task_content");// 添加任務內容到任務列表中this.taskList.push(task_content.value);// 清空任務內容輸入框task_content.value = '';}

    我們把之前任務列表中初始化的兩個任務刪掉

    data: function() {return {taskList: []}},

    現在你只需要操作taskList對象,頁面上的任務列表也會跟著變動。現在你可以試試在任務內容框中輸入任務的內容,然后點擊添加按鈕:

    任務存儲:localStorage和watch方法

    現在有一個問題,那就是你一刷新頁面,你新建的任務就消失了。所以我們新建一個store.js來處理任務的存儲。store.js利用localstorage來存儲任務:

    const STORAGE_KEY='todo_list' export default{fetch(){return JSON.parse(window.localStorage.getItem(STORAGE_KEY)||'[]')},save(items){window.localStorage.setItem(STORAGE_KEY,JSON.stringify(items))} }

    然后,在TodoList.vue中引用 store.js

    import Store from './store.js'

    現在 data.taskList 就不只是用[]來初始化了,我們要改成從store中獲取

    data: function() {return {taskList: Store.fetch()}},

    現在我要介紹一個全新的屬性 watch。該屬性的作用是當你改變某個屬性的時候可以同時做一些其他的事情。比如現在我們就需要在增加任務的同時將taskList保存到localStorage中。你可以這樣寫

    watch:{taskList:{handler:function(tasks){Store.save(tasks)}}},

    注意:watch跟data, methods屬性是同級的。

    動態解析任務時間(computed)

    現在我們要使用computed屬性來做這個神奇的功能。當你想在頁面上顯示經過處理的變量時,你可以使用各種函數,比如 如果我們要將名字中的逗號都換成下劃線,然后截取第一個空格之前的文字。我們可能會這么寫

    name.replace(',', '_').substring(0, name.indexOf(' '));

    偶爾寫一次還好,要是項目的每個地方都要這么寫一遍就太惡心了。所以vue提供了一種屬性叫 computed。使用這個屬性我們可以定義出“虛擬的”變量,這個變量并不在data中被實際的定義出來,而是通過對實際的變量進行了計算而得出的。在這個例子中我們的需求是:

  • 列表要能夠自動計算出任務的剩余快完時間,比如:23:40 回家 (還有 6 小時 36 分 15 秒)
  • 如果當前時間已經超過了計劃時間,則不顯示剩余完成時間
  • 此時就需要用到computed屬性。使用computed屬性可以定義虛擬的變量。這種變量依賴于data中的變量計算得出,并且可以在html中像使用data中的屬性一樣的使用他們。在我們這個例子中,我們在html模板中使用一個虛擬變量parsedTaskList。

    <tr v-for="task in parsedTaskList"><td>{{ task }}</td></tr>

    我們在跟watch屬性同級的節點下增加computed屬性,并在其中增加parsedTaskList屬性。我們會在partedTaskList屬性中對taskList進行轉換,生成新的任務列表

    computed: {parsedTaskList: function () {let parsedTaskList = [];const regex = /[0-9]+:[0-9]+/;// 遍歷taskListfor (let i=0; i<this.taskList.length; i++) {let task = this.taskList[i];// 解析任務中的計劃時間let result = task.match(regex);if (result != null && result.length > 0) {let taskTime = result[0];let thisMoment = moment();let currentDate = thisMoment.format('YYYY-MM-DD');let taskMoment = moment(currentDate + " " + taskTime, 'YYYY-MM-DD HH:mm');if (taskMoment.valueOf() < thisMoment.valueOf()) {parsedTaskList.push(task);continue;}let duration = moment.duration(taskMoment.diff(thisMoment));let durationText = duration.hours() + " 小時 " + duration.minutes() + " 分 " + duration.seconds() + " 秒";// 將剩余時間拼接到任務上parsedTaskList.push(task + "(還有 " + durationText + ")'></span>");}parsedTaskList.push(task);}// 返回新的任務列表return parsedTaskList;}},

    抽取剩余時間的具體的過程很簡單,大家也不需要現在理解它,因為它并不是這課的核心內容,只需要知道該函數可以實現自動拼接上任務的剩余完成時間就行了。

    做到這里我遇到了一個問題,那就是:為了項目結構的簡潔,我希望可以把這段代碼中由任務字符串轉換為帶著剩余時間的任務字符串代碼抽取到一個私有函數中去。但是在這沒有像java中的private關鍵字可以讓我們定義私有函數。

    要如何定義私有函數呢?

    寫在export中的東西意思是要暴露出去的東西,所以只要你的函數寫在export中,就相當于是public函數了。要想函數不被暴露出去,只需要將函數塊寫到export以外就好了。現在我們將轉換任務字符串的代碼抽取出來,放在?export default { 這行代碼之上:

    import Store from './store.js' import * as moment from 'moment';const regex = /[0-9]+:[0-9]+/; /*** 該函數作用是解析出字符串中的時間,并將其跟當前時間比較,* 計算出還剩多久才會到達計劃時間,將剩余時間拼接在字符串后。* 如果當前時間已經過了計劃時間,則不對字符串做任何改變* 例子:* 23:40 回家 -> 23:40 回家 (還有 6 小時 36 分 15 秒)*/ const addRemainTime = (task) => {let result = task.match(regex);if (result != null && result.length > 0) {let taskTime = result[0];let thisMoment = moment();let currentDate = thisMoment.format('YYYY-MM-DD');let taskMoment = moment(currentDate + " " + taskTime, 'YYYY-MM-DD HH:mm');if (taskMoment.valueOf() < thisMoment.valueOf()) {return task;}let duration = moment.duration(taskMoment.diff(thisMoment));let durationText = duration.hours() + " 小時 " + duration.minutes() + " 分 " + duration.seconds() + " 秒";return task + " (還有 " + durationText + ")";}return task; }export default {

    這樣做了之后,parsedTaskList屬性的內容就變成異常簡潔了:

    computed: {parsedTaskList: function () {let parsedTaskList = [];for (let i=0; i<this.taskList.length; i++) {parsedTaskList.push(addRemainTime(this.taskList[i]));}return parsedTaskList;}},

    好了。在刷新頁面之前不要忘記運行 npx webpack 來重新打包項目。完成后的效果如下

    ?

    每次改完代碼都要手動打包真的很煩!其實,有一個方法可以讓webpack自動跟蹤你的改動,并自動打包

    Webpack自動打包(watch)

    通過帶上 --watch參數,比如

    npx webpack --watch

    或者,在webpack.config.js中增加watch相關屬性可以讓webpack自動的檢測當前項目是否有變動,如果有變動webpack會自動打包。以下我采取在 webpack.config.js 中增加watch相關屬性的方式來打開watch模式:

    watch屬性默認是關閉的。所以我們需要在webpack.config.js中加上watch屬性:

    watch: true,

    加上watch的設置

    watchOptions: {aggregateTimeout: 3000, // 編譯的超時時間,單位:毫秒poll: 30 // 掃描項目的間隔時間,單位:秒},

    改動后的webpack.config.js文件內容是

    var path = require('path'); const { VueLoaderPlugin } = require('vue-loader') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'development',entry: './src/main.js',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'},watch: true,watchOptions: {aggregateTimeout: 3000, // 編譯的超時時間,單位:毫秒poll: 30 // 掃描項目的間隔時間,單位:秒},module: {rules: [{test: /\.vue$/,loader: 'vue-loader',},{test: /\.css$/,use: ['style-loader','css-loader']}]},plugins: [new VueLoaderPlugin(),// 以下是HtmlWebpackPlugin的配置new HtmlWebpackPlugin({template: 'index.html',filename: './index.html',hash: true})] };

    設置完watch屬性后,我們就可以使用 npx webpack?來啟動自動打包了

    npx webpack

    啟動后命令行工具處于監聽狀態,一有代碼改動就會自動打包

    vagrant@homestead:~/Code/learn-vue$ npx webpackwebpack is watching the files…Hash: a38478266809719e3c32 Version: webpack 4.12.1 Time: 3706ms Built at: 2018-10-03 17:43:01Asset Size Chunks Chunk Namesbundle.js 1.85 MiB main [emitted] main ./index.html 273 bytes [emitted] [./node_modules/moment/locale sync recursive ^\.\/.*$] ./node_modules/moment/locale sync ^\.\/.*$ 2.91 KiB {main} [optional] [built] [./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/App.vue?vue&type=script&lang=js] ./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js 136 bytes {main} [built] [./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/App.vue?vue&type=template&id=7ba5bd90] ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=7ba5bd90 259 bytes {main} [built] [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 489 bytes {main} [built] [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {main} [built] [./src/App.vue] 1.02 KiB {main} [built] [./src/App.vue?vue&type=script&lang=js] 246 bytes {main} [built] [./src/App.vue?vue&type=template&id=7ba5bd90] 194 bytes {main} [built] [./src/main.js] 269 bytes {main} [built]+ 316 hidden modules Child html-webpack-plugin for "index.html":1 asset[./node_modules/html-webpack-plugin/lib/loader.js!./index.html] 399 bytes {0} [built][./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 489 bytes {0} [built][./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]+ 1 hidden module

    watch的副作用

    watch的副作用就是cpu占用率會提高,我的macbook一運行 watch模式風扇的聲音就變大,導致我一直沒敢用這個模式。

    為已完成任務增加刪除線(v-if)

    剩下最后一個需求了,那就是如果當前時間超過了計劃時間,則任務需要變灰并增加刪除線。我們使用v-if來實現這個功能

    通過在dom元素中增加 v-if="表達式" 我們可以靈活的控制該dom元素的顯示與否。就像這樣:

    <div v-if="type === 'A'">A </div> <div v-else-if="type === 'B'">B </div> <div v-else-if="type === 'C'">C </div> <div v-else>Not A/B/C </div>

    如果v-if中的表達式結果為true,則該元素會被渲染出來,反之則該元素不會被渲染。在這個例子中還用到了 v-else-if 和 v-else,有著豐富編程經驗的你肯定一下就看懂了它們的含義,所以在此我就不解釋了。

    跟v-show的區別

    還有一個跟v-if用法很像的屬性叫 v-show。同樣也是定義一個表達式,根據表達式的返回結果來決定該元素是否出現。不同的是v-if的表達式返回結果為false,則該元素完全不出現在html中,而v-show不管表達式結果怎樣都會渲染該元素,只是當表達式為false時為元素增加 display:none的樣式而已。

    好,現在我們就來根據任務是否已經完成來顯示不同的任務樣式。檢驗的條件是任務字符串中是否出現“還有 xx?小時 xx?分 xx?秒” 字樣。

    先把html模板改成

    <tr v-for="task in parsedTaskList"><td><span v-if="isDone(task)" style="color:gray;text-decoration:line-through;">{{ task }}</span><span v-else >{{ task }}</span></td></tr>

    可以看到在v-if中我們使用了一個函數isDone來判斷該任務是否完成。所以我們需要在method屬性中增加isDone方法(以下方法的定義使用了ES2015語法)

    isDone (task) {let result = task.match(/還有\s[0-9]+\s小時\s[0-9]+\s分\s[0-9]+\s秒/);return result == null || result.length == 0;}

    不使用ES2015語法的版本是

    isDone: function (task) {let result = task.match(/還有\s[0-9]+\s小時\s[0-9]+\s分\s[0-9]+\s秒/);return result == null || result.length == 0;}

    如果你使用的是Chrome,那么就可以放心大膽的使用ES2015語法咯。

    完成后,打包,刷新頁面,效果如下

    這樣就完成了本節課的所有內容了。

    method和computed有什么區別呢?

    這是我學習vue時最大的疑問,我覺得method和computed用法完全就沒區別!其實他們的區別在于:computed是帶緩存的,如果被依賴的變量不發生變化,則下次調用computed時不會重新計算結果。但是method則是每次調用都會重新運行以得出實時的結果。

    后記

    其實vue的官網教程已經寫的非常棒了!沒見過寫的這么棒的官網文檔,強力贊一個!所以原本不打算在更新新的文章了,由于有網友希望我繼續更新,所以我才繼續又寫了一篇。但是寫文太費時間了。所以未來應該不會再更新了,感謝大家的支持!這是vue官網中文文檔的學習傳送門:https://cn.vuejs.org/v2/guide/

    ?

    ?

    總結

    以上是生活随笔為你收集整理的Vue 不睡觉教程3 - 来点实在的:自动计算剩余时间的任务列表的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。