赞
踩
目录
8.3 源代码
最近看到新一届的蓝桥杯也是开始报名了,而且还准备了模拟题供大家练习,我花了点时间写了一下,跟大家分享一下我的解题思路。
我的小tips,因为web蓝桥杯通常 补全代码的形式,所以题目较多,我们不一定要阅读所有代码,直接看题目的要求即可,题目中会明确告诉我们哪里需要加代码或者是修改代码。(特别是前面的简单题目)
请在 style.css
文件中补全代码。
当用户向下滚动的高度没有超过标题栏(即 .heading
元素)的高度时,保持 Tab 栏在其原有的位置。当滚动高度超过标题栏的高度时,固定显示 Tab 栏在网页顶部。
第一题比较简单,就是让我们在style.css中添加代码实现定位的效果,想到定位我们脑子里应该想到的是:position,没错就是用它,如果你忘记了它有哪些用法,请看下面的表格:
static | 元素处于正常文档流中,不受 top, right, bottom, left 影响。 |
relative | 元素相对于其正常位置进行定位。 元素仍保持正常文档流,但会对其进行相对调整。 |
absolute | 元素相对于最近的已定位祖先元素进行定位。 如果没有已定位的祖先元素,则相对于最初的包含块(通常是 |
fixed | 元素相对于视口进行定位,即使页面滚动,元素位置也不会改变。 |
sticky | 元素根据用户的滚动位置在父元素中定位。在元素在屏幕中可见时,它的位置是相对定位的,当页面滚动超出范围时,它的位置是固定的。 |
当我们知道各个属性的作用后,在根据题目需要达到的效果,很明显这里要使用的是:sticky
- /* TODO: 请在此补充代码实现tab栏动态固定 */
- position: sticky;
- top: 0;
找到 css/style.csss
文件中的 TODO 部分,完成以下目标:
给 .earth-con
元素添加动画,设置如下:
orbit
这一题只要你知道css动画的使用方法,简直就是送分,所有动画的要求都已经给你了,而且还标注出来了动画的名称,直接在对应的css部分添加动画代码即可。
- /* TODO:待补充代码,添加动画 */
- animation: orbit 36.5s linear infinite;
完善 js/index.js
中的 handle
函数中的 TODO 部分,实现以下功能:
为输入框(即 this.inputEl
)绑定 input
事件,当输入框的值发生变化时,调用已经提供的 handleInput
方法进行搜索处理,注意 handleInput
方法调用时的 this
指向应为 search
对象本身。
根据题目的名称可以看出来这一题应该是与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问题。
- handle() {
- // TODO:待补充代码
- //使用bind来纠正 this的指向
- this.inputEl.addEventListener('input', this.handleInput.bind(this))
- },
找到 index.html
中 TODO 部分,正确修复代码使 data
对象恢复响应式特性,即点击页面上的 - 与 + 按钮可以改变 value
的值。正确实现后效果如下:
这个题目主要是按钮事件失灵了,如果不仔细看可能一时半会看不出来问题,其实就是
- //TODO:待修复代码
- let { value }={ ...data }
这行代码将value从data中结构出来后,value就失去了响应式效果了,而且仔细看的话,可以看到题目中引入了toRefs,但是没有使用,toRefs的作用就是让结构出来的元素不失去响应式,在日常开发中主要是为了简化代码,如果不结构,在html中就要使用这种:data.value的形式渲染数据。
- //TODO:待修复代码
- let { value } = toRefs(data) //结构赋值将value失去的响应式
- //利用toRefs 恢复响应式
el-drawer
sortItem
函数,按照食物属性名称将早餐/午餐/晚餐的数组数据从大到小排序,然后找到排序后的数组中第一个不大于对应摄入量上限的食材对象,并将其返回。第一个要求很简单,就是在el-drawer上绑定它的开关属性,在触发按钮事件时将其改成 true即可
第二个要求涉及到的是排序以及遍历,排序我们直接使用 sort对属性名称进行排序即可,然后再遍历查找到对应的元素进行比较返回对应的值即可。
第一处:
第二处:
这里提供二种方法,如果对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
- }
- }
- }
groupByAge
函数,groupByAge
接收一个参数,参数为数组对象,age
代表司龄,name
代表人名,返回一个根据司龄进行分组的对象,key
是司龄,value
是一个数组,其中包含了所有当前司龄的人的对象。这个题目首先第一步就是要对数据进行重新改装,将数据按照age进行分组改装
要实现其实比较简单,直接遍历插入即可
第二步是要设置Echarts的X轴数据和Y轴数据,x轴的数据是司龄,也就是age,但是有个要求是要进行升序排序,这里又用到了sort排序,y轴的数据是人数,也就是对应的age数组的长度,因为前面已经进行了排序,我们直接遍历返回数组长度就行。
-
- // 按照年龄分组的函数
- 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 轴数据 seriesData
- xAxisData.value = Object.keys(groupedPeople.value).sort((a, b) => a - b)
- seriesData.value = Object.values(groupedPeople.value).map((item) => {
- return item.length
- })
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。第一步:
- 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.value
- depositAmount.value = null
- }
- }
- return {
- deposit,
- depositAmount,
- store
- }
- }
启动服务后,如果没有勾选则相应对应的内容,有勾选相应其他的相关内容。
Javascript
,除了查询 tag
为 Javascript
的内容,也需要查询 Javascript
的相关推荐标签 HTML5
,CSS3
的内容(即 relevance
字段对应的数据),一并返回。HTML5
的相关推荐标签的内容。多个标签之间的相关标签可能重复,需要对查询内容进行去重,比如选择了标签 Javascript
和 CSS3
,最终的去重结果应该是标签 Javascript
、CSS3
、HTML5
的内容。这里主要的要求就是提交一个表单过来,根据表单的数据展示对应的文本,不过还需要注意的是需要查询其中的relevance连带进来,比如:
表单数据有一个Python,那么Java也要一起携带返回相应。注意标红的字,HTML5是要忽略的!
初看题目,我们要写的地方都在js文件中,如果会nodejs的人,应该能读懂代码,我简单解释一下,(如果能看懂可以跳过)这里就是启动服务在8080端口,如果访问的是:‘/’,则会读取index.html文件相应给客户端,而在index.html中有一个表单,它的提交地址是:‘/customized’,所以当submit按钮单击后就会跳到此页面,并携带这些数据。
此时我们需要相应对应的页面给客户端,这里就需要进行判判表单元素有没有数据了,如果没有则相应一段提示文本,否则相应对应的提示,这里的提示还算简单,主要看怎么根据对应的数据解析出所有需要的推荐数据。
- // 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
- })
- //查出来后再去对比relevance
- if (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()
根据当前蛇的移动方向(this.direction
)以及蛇身块的大小(this.size
),计算新的蛇头位置,更新蛇身坐标数组 (this.snakeBody
),即可实现蛇的正确移动。蛇的移动和增加蛇的长度代码已提供。
这一题乍一看挺难的,代码很长,但其实通过观察发现,其实大多数代码都已经写好了,我们只需要拿到蛇头的方向对它的坐标进行替换就行,后面身子部分啥的坐标都不用管。所以我们只要拿到蛇头的坐标,判断其目前的方向,坐标加上自身的大小(size)即可。
- // 移动蛇的头部
- nextStep() {
- // TODO:待补充代码
- const snake = this.snakeBody
- const head = { ...snake[0] }
- switch (this.direction) {
- case 'right':
- head.left += this.size
- break
- case 'down':
- head.top += this.size
- break
- case 'left':
- head.left -= this.size
- break
- case 'up':
- head.top -= this.size
- break
- }
- this.snakeBody.unshift(head) // 在头部插入新坐标
- this.snakeBody.pop() // 删除尾部坐标
- this.render() // 重新渲染蛇身体
- }
完成 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
(表单数据的键值对) 进行获取。第一问:
- // 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.message
- break
- }
-
- if (rule.type && !validateByType(rule.type, props.formData[field])) {
- errors.value[field] = rule.message
- break
- }
-
- if (rule.min && props.formData[field].length < rule.min) {
- errors.value[field] = rule.message
- break
- }
-
- if (rule.max && props.formData[field].length > rule.max) {
- errors.value[field] = rule.message
- break
- }
- }
- }
-
- // 添加对 nickname 字段的非空判断
- if (props.rules.nickname && !props.formData.nickname) {
- errors.value.nickname = '昵称不能为空'
- }
- // TODO:END
总的来说这一次的题目不算太难吧,大家觉得呢,大多数题目还是考的是大家对语法的熟练度已经js代码的编写,所以蓝桥杯对基础的考察还是占比比较大的,大家要想在蓝桥杯中取得一个好的成绩,需把握好基础知识。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。