目錄
1.動態的Tab欄
1.1 題目要求
1.2?題目分析
1.3 源代碼
2.地球環游
2.1 題目要求
2.2 題目分析
2.3 源代碼
3.迷惑的this
3.1 題目要求
3.2 題目分析
3.3 源代碼
4.魔法失靈了
4.1 題目要求
4.2 題目分析
4.3 源代碼
5.燃燒你的卡路里
5.1 題目要求
5.2 題目分析
5.3 源代碼
6.司齡統計
6.1 題目要求
6.2 題目分析
6.3 源代碼
7.不翼而飛的余額
7.1 題目要求
7.2 題目分析
7.3 源代碼
8.個性化推薦
8.1 題目要求
8.2 題目分析
8.3?源代碼
9.貪吃蛇
9.1 題目要求
9.2 題目分析
9.3 源代碼
10.自定義表單驗證
10.1 題目要求
10.2 題目分析
10.3 源代碼
小結
代碼地址:第15屆藍橋杯Web模擬題-大學組-題目與解析
最近看到新一屆的藍橋杯也是開始報名了,而且還準備了模擬題供大家練習,我花了點時間寫了一下,跟大家分享一下我的解題思路。
我的小tips,因為web藍橋杯通常?補全代碼的形式,所以題目較多,我們不一定要閱讀所有代碼,直接看題目的要求即可,題目中會明確告訴我們哪里需要加代碼或者是修改代碼。(特別是前面的簡單題目)
1.動態的Tab欄
1.1 題目要求
請在?style.css
?文件中補全代碼。
當用戶向下滾動的高度沒有超過標題欄(即?.heading
?元素)的高度時,保持 Tab 欄在其原有的位置。當滾動高度超過標題欄的高度時,固定顯示 Tab 欄在網頁頂部。
1.2?題目分析
第一題比較簡單,就是讓我們在style.css中添加代碼實現定位的效果,想到定位我們腦子里應該想到的是:position,沒錯就是用它,如果你忘記了它有哪些用法,請看下面的表格:
static | 元素處于正常文檔流中,不受 top, right, bottom, left 影響。 |
relative | 元素相對于其正常位置進行定位。 元素仍保持正常文檔流,但會對其進行相對調整。 |
absolute | 元素相對于最近的已定位祖先元素進行定位。 如果沒有已定位的祖先元素,則相對于最初的包含塊(通常是 |
fixed | 元素相對于視口進行定位,即使頁面滾動,元素位置也不會改變。 |
sticky | 元素根據用戶的滾動位置在父元素中定位。在元素在屏幕中可見時,它的位置是相對定位的,當頁面滾動超出范圍時,它的位置是固定的。 |
?當我們知道各個屬性的作用后,在根據題目需要達到的效果,很明顯這里要使用的是:sticky
1.3 源代碼
/* TODO: 請在此補充代碼實現tab欄動態固定 */position: sticky;top: 0;
2.地球環游
2.1 題目要求
找到?css/style.csss
?文件中的 TODO 部分,完成以下目標:
給?.earth-con
?元素添加動畫,設置如下:
- 動畫名稱:?
orbit
- 動畫時間: 36.5 秒
- 速度函數:線性
- 播放方式:無限循環
2.2 題目分析
這一題只要你知道css動畫的使用方法,簡直就是送分,所有動畫的要求都已經給你了,而且還標注出來了動畫的名稱,直接在對應的css部分添加動畫代碼即可。
2.3 源代碼
/* TODO:待補充代碼,添加動畫 */animation: orbit 36.5s linear infinite;
3.迷惑的this
3.1 題目要求
完善?js/index.js
?中的?handle
?函數中的 TODO 部分,實現以下功能:
為輸入框(即?this.inputEl
)綁定?input
?事件,當輸入框的值發生變化時,調用已經提供的?handleInput
?方法進行搜索處理,注意?handleInput
?方法調用時的?this
?指向應為?search
?對象本身。
3.2 題目分析
根據題目的名稱可以看出來這一題應該是與this相關的問題,js文件中需要用到的dom元素和函數都已經給我們寫好了,只需要我們給input綁定一個input事件,我們相當然的就會寫出這樣的代碼:
handle() {// TODO:待補充代碼this.inputEl.addEventListener('input', this.handleInput(this))},
但是運行發現不對,達不到效果,錯誤的原因就是忽略了this的指向問題,當你使用addEventListener
將一個函數作為事件處理程序附加到元素時,該函數會失去其原始上下文,函數內部的this
將指向觸發事件的元素。我們可以打印此時的this看看:
可以看到此時的this指向已經變成了input這個dom元素了,他的身上并沒有hanleInput()這個方法,此時我們就要使用bind來修改this的指向。在面向對象中bind是很重要的,關乎到整個class對象的this問題。
3.3 源代碼
handle() {// TODO:待補充代碼//使用bind來糾正 this的指向this.inputEl.addEventListener('input', this.handleInput.bind(this))},
4.魔法失靈了
4.1 題目要求
找到?index.html
?中 TODO 部分,正確修復代碼使?data
?對象恢復響應式特性,即點擊頁面上的?-?與?+?按鈕可以改變?value
?的值。正確實現后效果如下:
4.2 題目分析
這個題目主要是按鈕事件失靈了,如果不仔細看可能一時半會看不出來問題,其實就是
//TODO:待修復代碼let { value }={ ...data }
這行代碼將value從data中結構出來后,value就失去了響應式效果了,而且仔細看的話,可以看到題目中引入了toRefs,但是沒有使用,toRefs的作用就是讓結構出來的元素不失去響應式,在日常開發中主要是為了簡化代碼,如果不結構,在html中就要使用這種:data.value的形式渲染數據。
4.3 源代碼
//TODO:待修復代碼let { value } = toRefs(data) //結構賦值將value失去的響應式//利用toRefs 恢復響應式
5.燃燒你的卡路里
5.1 題目要求
- 點擊“定制方案”按鈕后,彈出側滑頁面,所使用的組件為?
el-drawer
- 封裝?
sortItem
?函數,按照食物屬性名稱將早餐/午餐/晚餐的數組數據從大到小排序,然后找到排序后的數組中第一個不大于對應攝入量上限的食材對象,并將其返回。
5.2 題目分析
第一個要求很簡單,就是在el-drawer上綁定它的開關屬性,在觸發按鈕事件時將其改成 true即可
第二個要求涉及到的是排序以及遍歷,排序我們直接使用 sort對屬性名稱進行排序即可,然后再遍歷查找到對應的元素進行比較返回對應的值即可。
5.3 源代碼
第一處:
第二處:
這里提供二種方法,如果對js的array方法熟練的可以直接使用方法,不熟練的可以遍歷
const sortItem = (arr, pro, compare) => {// TODO 根據 compare 匹配食材對象后返回這個對象// 按照食物屬性名稱將早餐/午餐/晚餐的數組數據從大到小排序,//然后找到排序后的數組中第一個不大于對應攝入量上限的食材對象,并將其返回。//第一種const sortedArr = arr.sort((a, b) => b[pro] - a[pro]);const optimalItem = sortedArr.find(item => item[pro] < compare);return optimalItem;//第二種arr.sort((a, b) => b[pro] - a[pro])for (const item of arr) {if (item[pro] < compare) {return item}}}
6.司齡統計
6.1 題目要求
- 找到?
groupByAge
?函數,groupByAge
?接收一個參數,參數為數組對象,age
?代表司齡,name
?代表人名,返回一個根據司齡進行分組的對象,key
?是司齡,value
?是一個數組,其中包含了所有當前司齡的人的對象。 - 正確設置 ECharts 圖表中的 x 軸和 y 軸數據,x 軸表示司齡,從小到大排序,y 軸表示司齡對應的人數個數。如 1 年司齡的有 4 人,則 y 軸對應的數據為 4。
6.2 題目分析
這個題目首先第一步就是要對數據進行重新改裝,將數據按照age進行分組改裝
要實現其實比較簡單,直接遍歷插入即可
第二步是要設置Echarts的X軸數據和Y軸數據,x軸的數據是司齡,也就是age,但是有個要求是要進行升序排序,這里又用到了sort排序,y軸的數據是人數,也就是對應的age數組的長度,因為前面已經進行了排序,我們直接遍歷返回數組長度就行。
6.3 源代碼
// 按照年齡分組的函數
const groupByAge = (peoples) => {// TODO:待補充代碼,按照年齡進行分組//按照age進行排序const data = {}peoples.forEach((item) => {//判斷data中是否存在if (!data[item.age]) {data[item.age] = []}data[item.age].push(item)})return data
}// TODO: 設置 Echars X 軸數據 xAxisData 和 Y 軸數據 seriesDataxAxisData.value = Object.keys(groupedPeople.value).sort((a, b) => a - b)seriesData.value = Object.values(groupedPeople.value).map((item) => {return item.length})
7.不翼而飛的余額
7.1 題目要求
- 找到?
index.html
?中的 TODO 部分,為項目配置?history
?模式路由,瀏覽器中訪問?/
?的時候顯示?WalletPage
?組件,訪問?/deposit
?的時候顯示?DepositPage
?組件。 - 找到?
DepositPage.js
?中的 TODO 部分,在?DepositPage
?頁面中的 (id = deposit-balance
)元素正確顯示錢包余額(store
?中的?balance
)。 - 完善?
js/store.js
?和?component/DepositPage.js
中的 TODO 部分,在?DepositPage
?頁面中,在輸入框(input
)輸入數字(只考慮正整數),點擊 “Deposit” 按鈕(button
)后,余額 = 現在的余額 + 輸入框中輸入的金額,在?DepositPage
?頁面正確顯示錢包余額(兩個頁面的余額相同)。初始余額為 23。
7.2 題目分析
- 第一步就是配置路由,如果不會的建議學習一下vue-router:https://router.vuejs.org/zh/guide/essentials/history-mode.html,這里二種模式都是可以使用的,按照要求配置即可,沒什么難的。
- 第二步是再DepositPage組件中修改balance的數據,需要保持組件間的數據統一性,這里使用了Pinia倉庫,不過這里不知道pinia也沒事,這里唯一要注意的是數據的正確性,題目要求是要正整數,所以做一個判斷即可,最后加上后清空input框。
7.3 源代碼
第一步:
const { createRouter, createWebHistory } = VueRouter; // TODO:待補充代碼,在此引入路由相關 API const app = createApp({});app.use(createPinia());const router = createRouter({// TODO:待補充代碼,為項目配置 history 模式的路由history: createWebHistory(),routes: [{path: '/',component: WalletPage},{path: '/deposit',component: DepositPage}]})
第二步:
第三步:
setup() {const depositAmount = Vue.ref() // 輸入框中的的值->存款金額const store = useMoneyStore() // 引入 store// TODO:待補充代碼,完善點擊存款按鈕事件function deposit() {if (!isNaN(depositAmount.value) && depositAmount.value >= 0) {store.balance += depositAmount.valuedepositAmount.value = null}}return {deposit,depositAmount,store}}
8.個性化推薦
8.1 題目要求
啟動服務后,如果沒有勾選則相應對應的內容,有勾選相應其他的相關內容。
- 如果用戶勾選了標簽,則返回對應的標簽的查詢信息以及相關推薦,比如用戶選擇了標簽?
Javascript
,除了查詢?tag
?為?Javascript
?的內容,也需要查詢?Javascript
?的相關推薦標簽?HTML5
,CSS3
?的內容(即?relevance
?字段對應的數據),一并返回。 - 注意:不再需要進一步查詢相關推薦的相關推薦,即不需要查詢?
HTML5
?的相關推薦標簽的內容。多個標簽之間的相關標簽可能重復,需要對查詢內容進行去重,比如選擇了標簽?Javascript
?和?CSS3
,最終的去重結果應該是標簽?Javascript
、CSS3
、HTML5
?的內容。
這里主要的要求就是提交一個表單過來,根據表單的數據展示對應的文本,不過還需要注意的是需要查詢其中的relevance連帶進來,比如:
表單數據有一個Python,那么Java也要一起攜帶返回相應。注意標紅的字,HTML5是要忽略的!
8.2 題目分析
初看題目,我們要寫的地方都在js文件中,如果會nodejs的人,應該能讀懂代碼,我簡單解釋一下,(如果能看懂可以跳過)這里就是啟動服務在8080端口,如果訪問的是:‘/’,則會讀取index.html文件相應給客戶端,而在index.html中有一個表單,它的提交地址是:‘/customized’,所以當submit按鈕單擊后就會跳到此頁面,并攜帶這些數據。
此時我們需要相應對應的頁面給客戶端,這里就需要進行判判表單元素有沒有數據了,如果沒有則相應一段提示文本,否則相應對應的提示,這里的提示還算簡單,主要看怎么根據對應的數據解析出所有需要的推薦數據。
8.3 源代碼
// TODO: 補充個性化頁面處理代碼//1.讀取文件const customizedPage = fs.readFileSync(path.join(__dirname, '../customized.html'), {encoding: 'utf8'})let indexHtml = ''if (interested.length === 0) {indexHtml = customizedPage.replace('<body></body>', `<body><div class="unselect">你還未選擇任何感興趣的標簽!</div></body>`)} else {//2.首先查出所有的tag以及附帶的tag (將interested轉為數組)let tags = Array.isArray(interested) ? interested : [interested]tags.forEach((item) => {const found = data.find((foundData) => {return foundData.tag == item})//查出來后再去對比relevanceif (found && found.tag != 'HTML5') {//小坑,found.relevance.forEach((relevance) => {if (!tags.includes(relevance)) {tags.push(relevance)}})}})let html = ''tags.forEach((item) => {const found = data.find((foundData) => {return foundData.tag == item})if (found) {html += `<div class="interest"><div class="tag">${found.tag}</div><div>${found.content}</div></div>`}})indexHtml = customizedPage.replace('<body></body>', `<body>${html}</body>`)}res.writeHead(200, { 'Content-Type': 'text/html' })res.write(indexHtml)res.end()
9.貪吃蛇
9.1 題目要求
根據當前蛇的移動方向(this.direction
)以及蛇身塊的大小(this.size
),計算新的蛇頭位置,更新蛇身坐標數組 (this.snakeBody
),即可實現蛇的正確移動。蛇的移動和增加蛇的長度代碼已提供。
9.2 題目分析
這一題乍一看挺難的,代碼很長,但其實通過觀察發現,其實大多數代碼都已經寫好了,我們只需要拿到蛇頭的方向對它的坐標進行替換就行,后面身子部分啥的坐標都不用管。所以我們只要拿到蛇頭的坐標,判斷其目前的方向,坐標加上自身的大小(size)即可。
9.3 源代碼
// 移動蛇的頭部nextStep() {// TODO:待補充代碼const snake = this.snakeBodyconst head = { ...snake[0] }switch (this.direction) {case 'right':head.left += this.sizebreakcase 'down':head.top += this.sizebreakcase 'left':head.left -= this.sizebreakcase 'up':head.top -= this.sizebreak}this.snakeBody.unshift(head) // 在頭部插入新坐標this.snakeBody.pop() // 刪除尾部坐標this.render() // 重新渲染蛇身體}
10.自定義表單驗證
10.1 題目要求
-
完成?
FormInput.js
?中的 TODO 部分,當輸入框(class= form-input
) 的值變化時,觸發事件更新index.html
?中組件(form-input
)的?v-model
?值。(調試 tips:考生可以在點擊按鈕時通過打印?formData
?的值進行查看)。 -
完成?
js/util.js
?中的?is_email
?函數,參數是郵箱地址,是合法郵箱返回?true
,否則返回?false
。(郵箱的正則校驗) - 完成?
components/FormValidator.js
?中通用表單驗證函數?validateForm
?中的 TODO 部分。如果表單驗證通過,則?Promise
?為?resolve(true)
,否則為?resolve(false)
(此部分代碼已提供)。?index.html
?中定義的?formRules
?對象對應字段的表單值驗證失敗時,使用?validateForm
?函數中提供的?errors
?對象,在對應字段中存儲錯誤信息。若某個字段對應的錯誤信息為多個時,將按照驗證規則數組的順序優先顯示,即只顯示第一個錯誤信息。函數使用的數據通過?props.rules
(字段名和對應的驗證規則) 和?props.formData
(表單數據的鍵值對) 進行獲取。
10.2 題目分析
- 第一問,改變父組件中的數據,我們首先應該想到的是自定義事件,子傳父嘛,但是通過觀察后發現父組件好像并沒有定義自定義事件,而且題目要求我們只修改子組件,然后通過觀察可以知道子組件引入了emit,而父組件使用了v-model對表單進行了綁定,此時我們就可以使用v-model來進行通信,如果對如果使用v-model來通信不了解的,可以查看這篇文章,里面列舉了vue3組件間的通信方式:https://blog.csdn.net/m0_64642443/article/details/131787640
- ?第二問是郵箱正則沒啥好說的,不會的可以先去學習一下正則怎么寫喔。
- 第三問:題目要求中也寫出來了數據從,props.rules和props.formData中拿,我們拿到數據對其進行驗證然后返回相應的錯誤信息就行(message)。
10.3 源代碼
第一問:
// TODO:目標 1 當輸入框的值變化時,觸發 input 事件更新父組件的 v-model 值watch(inputValue, (newValue) => {emit('update:value', newValue)})// TODO:end
第二問:
const is_email = (val) => {// TODO:目標 2 待補充代碼const emailRegex = /^[a-zA-Z0-9]+@[a-zA-Z0-9]+.[a-zA-Z]{2,4}$/return emailRegex.test(val)
}
第三問:
// TODO:目標 3 編寫通用的表單驗證規則,并將錯誤信息放置到 errors 對象中for (const field in props.rules) {const fieldRules = props.rules[field]for (const rule of fieldRules) {if (rule.required && !props.formData[field]) {errors.value[field] = rule.messagebreak}if (rule.type && !validateByType(rule.type, props.formData[field])) {errors.value[field] = rule.messagebreak}if (rule.min && props.formData[field].length < rule.min) {errors.value[field] = rule.messagebreak}if (rule.max && props.formData[field].length > rule.max) {errors.value[field] = rule.messagebreak}}}// 添加對 nickname 字段的非空判斷if (props.rules.nickname && !props.formData.nickname) {errors.value.nickname = '昵稱不能為空'}// TODO:END
小結
總的來說這一次的題目不算太難吧,大家覺得呢,大多數題目還是考的是大家對語法的熟練度已經js代碼的編寫,所以藍橋杯對基礎的考察還是占比比較大的,大家要想在藍橋杯中取得一個好的成績,需把握好基礎知識。