話不多說,碼上開始。
建立項目
- 首先下載 安裝 Node.js 環境,它會自動安裝 npm 包管理工具。在使用之前我們修改一下 npm 包的下載路徑,不然放在 C 盤怎么得了?在命令行中輸入
npm config ls
可以看到 prefix 的值就是下載路徑,輸入npm config set prefix "你的路徑"
就可以修改。 - 安裝 vue-cli 腳手架工具,
npm install -g vue-cli
,參數 -g 表示全局安裝(路徑在之前設置的 prefix 中),否則就是本地安裝(路徑在當前目錄)。 - 建立項目,在某目錄下,輸入
vue init webpack-simple 項目名稱
,參數 webpack-simple 是一個項目模板;按提示輸入相關信息后,將自動新建項目文件夾,輸入cd 文件夾名稱
進入目錄,輸入npm install
,在當前項目路徑下載所需的包;輸入npm run dev
就可運行了。 dev 是 package.json里面定義的腳本命令,作用是啟動服務器。
打開 Vue.app 文件,我們就可以在里面愉快的寫組件了。
這里不再介紹 Vue 的基本知識,若要學習請移步 Vue2.x 教程。
需求分析
其實文章開頭說的 話不多說,碼上開始 是一種十分錯誤的做法。我就是在想當然之后——按照腦海中最初浮現的邏輯——馬上開始寫界面,吭哧吭哧地開始寫事件處理程序。隨后,在測試中痛苦地發現,功能太簡陋,簡單的單次計算並不能滿足我們印象中對於計算器的期待。
好吧,推倒重來,如果上天再給我一次機會,我會好好地做需求分析……
首先,我們打開一個計算器看看它有什么功能:
也就是說,計算器應該支持單次運算、連續計算、繼續計算,遵循運算符的優先級順序,並具有一定的容錯機制。
幾經修改,最后整理流程圖如下:
組件結構
- 首先,為了進行四則運算,我們需要一個操作符棧
oprStack
和一個數值棧numStack
- 為了顯示算式的功能,我們需要一個數組
cacheArr
順序記錄輸入和操作符和數值。 - 由於顯示部分應該綁定一個字符串變量
cacheStr
,此變量又於上述數組相關,那么為了保持數據結構的簡單,對於這個字符串變量,我們使用計算屬性。 - 一些緩存變量,
inputData
preType
- 一些標志變量,
calculated
inputShow
errorOccurred
那么自然而然地,計算器組件的結構設計如下:
<template>
<div id="calculator">
...
<div id="panel">
<hr>
<div>{{ cacheStr }}</div>
<hr>
<div :class="{'text-danger' : errorOccurred}">{{ inputData }}</div>
<hr>
</div>
<div id="board" @click="changePanel($event);inputHandler($event)">
<table>
...
</table>
</div>
</div>
</template>
<script>
export default{
data () {
return{
cacheArr:[], //顯示算式
inputData:'', //緩存輸入數值
preType:'num', //上一個輸入類型
oprStack:[], //操作符棧
numStack:[], //操作數棧
calculated:0, //連續計算和重新計算等邏輯需要的標志
inputShow: 0, //inputData 有時作為輸入有時作為輸出,需要一個標志
errorOccurred: 0 //錯誤標志
}
},
computed:{
cacheStr() {
...
return this.cacheArr.join("");
}
},
methods: {
/** 初始化 */
init(){
...
},
/** 顯示處理 */
changePanel(e){
...
},
/** 輸入處理 */
inputHandler (e) {
...
//重新計算判斷:1. 上次計算后輸入 num 或 opr0; 2. 上次計算結果是 NaN;
if(...){
this.init();
}
//繼續計算判斷:1. 上次計算后輸入 opr1 或 opr2
if(...){
//主要處理顯示的部分
...
}
// inputData 從顯示模式調整為輸入模式
...
//處理數字-----------------------------------------
if(type === 'num'){
...
}
//處理管理級操作符---------------------------------
if(type==='opr0'){
...
}
//處理其他操作符----------------------------------
if(type==='opr1' || type==='opr2'){
//查看寄存器是否有數據
...
//容錯機制
//1. 如果輸入二元操作符-等號,則刪掉二元操作符
//2. 如果連續輸入兩次二元操作符,則刪掉前一個
...
//處理
if(value==='equals')
{
this.oprStack.push({type:type, value:value, level:level});
this.preCalculate();
this.calculated = 1;
}
else{
this.oprStack.push({type:type, value:value, level:level});
this.calculated = 0;
}
}
//保存等號前的最后一個輸入類型--------------------
...
},
/** 計算預處理 */
preCalculate () {
//計算
this.calculate(this.oprStack, this.numStack);
//顯示結果數據
...
},
/** 計算過程 */
calculate(oprStack, numStack){
//對操作符棧和數值棧進行計算,考慮四則運算的優先級
...
},
/** 具體計算函數 */
...
}
}
</script>
<style>
...
</style>
大體思路就是這樣,一個計算器應該保持簡單、直觀的使用方式,以免造成用戶的困惑。
比如說,自帶的計算器里面,上一次計算結果既在顯示屏上,也在數值棧中,當用戶進行“繼續計算”時,輸入一個二元運算符,緊接着輸入一個等號,計算器會把當前顯示屏的數值當做第二個操作數,但這可能不是用戶想要的,因為他並沒有輸入第二個操作數。
在 po主的計算器中,把最后一個輸入是二元運算符這種情況當作誤操作,自動去掉,以免造成混淆。
##踩坑小記 這是一點題外話,主要是記錄一下自己犯的錯誤,大佬們請忽略~ 1. Unicode 字符值的使用。可以到 [Unicode 官網](http://www.unicode.org/charts/) 上查詢字符值(16進制),比如說 ÷ 號:00f7。
在 JavaScript 中使用:`\u00f7`,在 CSS 中使用:`\00f7`,在 html 中使用:`&井號247;`。注意 html 中是10進制噢!這里 po主為了避免 markdown 語法自動轉換成除號把 ‘#’ 改成了 ‘井號’,各位童鞋自行轉換哈… 2. Vue 文件的 debug 方法。我們知道,使用了 webpack 的項目中,腳本最后要經過編譯、壓縮、打包,在瀏覽器的調試工具中是無法調試的。
一開始我采用的方法是,在源代碼要斷點的地方輸入 `debugger`,在運行時就可以進入調試模式了。
后來經老大介紹,有一個叫 Vue DevTools 的工具,各位童鞋可以網上自取。 3. 跟常識相悖的是:空對象 `{}` 竟然是真值。所以判斷空對象時 `if(!nullObj){...}` 並不會得到你想要的結果。可以使用 jQuery 中的判斷空對象方法: ```javascript function isEmptyObject(e) { var t; for (t in e) return !1; return !0 } ``` 4. 由於存在 NaN 這個神奇的毒瘤,以及 Infinity 這樣的值,所以判斷一個變量是否數值的方法: ```javascript function isNumber(value){ return typeof value === 'number' && isFinite(value); } ``` 5. 在 Vue 中,可以在你的方法中訪問計算屬性,但不要試圖去更改它,因為沒用。在下次使用該計算屬性時,它依然會調用自身的方法來計算。