vue前端工程化

news2024/12/29 10:44:53

前言

本文介绍的是有关于vue方面的前端工程化实践,主要通过实践操作让开发人员更好的理解整个前端工程化的流程。

本文通过开发准备阶段、开发阶段和开发完成三个阶段开介绍vue前端工程化的整体过程。

准备阶段

准备阶段我将其分为:框架选择、规范制定、脚手架搭建。

框架选择

vue的框架有很多,有脚手架框架例如vue提供脚手架、nuxt等,还有vue版本对应的版本;UI组件库可选择性更多,element、Ant Design、Naive等,还有自封的ui组件库。

本文的实践选择的脚手架框架为:vue3+vite+typescript。

使用的UI组件库为:element组件库。

框架选择:

脚手架:vue3+vite+typescript

UI组件库:element

规范制定

规范制定主要有一下几个点

结构规范

即项目的目录规范,每个项目的文件夹命名代表的含义,公共组件和页面组件应该存在在哪,工具类,静态文件应该存放在哪个文件夹,应该如何命名等。

1.规范一:功能分离结构

作者自己取得名,即各个文件夹功能分离。

如上图所示,每个文件夹又有其含义,每个有特定含义的文件都应该放到对应的文件夹内。

api文件夹

用于存放接口请求的文件夹,内部文件命名有两种规则(这个是根据作者自身开发经验总结的)

1.api文件夹内不允许子文件夹的存在,文件名应该以路由名+api作为文件名;

2.api文件夹内可以有子文件夹,子文件夹名称应该与路由文件名同名,文件名按照其含义进行命名+api,切文件夹结构应该只允许存在二级结构;

对于第一种规则,如下图所示:

页面路由home-page下的全部请求,应该放在home-page-api文件中。这种规则适合于项目接口较少的项目,一般一个页面路由全部请求不超过20个推荐使用这种方式。

对于第二种规则:如下图所示

在api文件夹内建一个与页面路由同名的文件夹,该文件夹命名规则为页面路由子路由文件夹名+api的形式,即该子路由内的全部请求方法放到对应的api中的文件内。这种规则适用于路由页面请求方法比较多的情况下使用。

assets文件夹

文件夹要求:建于路由页面同名的文件,用于存放该路由下的图片资源,文件夹不可有三级结构,对于组件所需的图片资源,应该统一存放在一个统一的文件夹内。

components文件夹

按照组件名命名文件夹

pages-view文件夹

用于存放页面组件,pages页面路由入口,pages-view存放对应页面。具有共性的页面组件可以统一放到一个文件夹或者提到components文件夹。

例如下图所示:红色框表示的是首页路由,则绿色框怎就是对应的页面组件(关系为路由组件为页面组件提供出口,路由组件只做简单props接收和简单逻辑运算)

router

用于存放路由配置,如果路由过多建议拆分路由。

store

用于存放vuex等配置,但是项目中贡献数据不建议使用vuex而是建议存放在缓存中。

utils

用于存放工具类和工具方法

2.规范二:模块分离结构

所谓的模块聚合结构,则表示的是将api请求,页面组件,路由配置都存放到view文件夹中,router作为主配置引用子路由配置。对于根路径下的components、assets和store作用和规范一的作用一样。

此处下的pages-views、api和router文件夹下的文件名要求不太高,只要文件名有特定含义即可,但是文件夹下目录不可找过三级。

3.总结
  • 规范一:适用于小项目,协作人员少的项目,结构清晰明了,维护方便,弊端是协作性比较差。
  • 规范二:适用于中大型项目,协作人员多的项目,开发人员只要在自己的目录下开发即可,不相互影响,提交代码不容易发生冲突。

命名规范

命名规范主要有:文件和文件夹命名规范、变量&方法&计算属性&类&接口命名规范、css类名命名规范、html组件引用规范&导入组件名和组件名规范。

1.文件和文件夹命名规范

文件和文件夹命名:全为小写字母,多单词之前用横线隔开,主要是为了适配window和mac,windows不区分大小写,mac区分大小写。

2.变量&方法&类&接口命名规范

变量&方法&计算属性:使用小驼峰命名法,其中方法末尾需要加Fn,计算属性末尾添加Computed(这个是作者个人习惯)。

类&接口:使用大驼峰命名法,其中接口末尾加Types(作者个人习惯,用于区分类)。

3.css类名命名规范——BEM

使用BEM命名规范。

BEM(Block,Element,Modifier)是一种基于组件的web开发思想。这种开发思想主张将用户界面划分为独立的块。这使得网页开发变得简单快捷,即使是复杂的用户界面,也可以重用现有的代码,而无需复制和粘贴。

Block - 一个功能独立的页面组件,可以重用。

  • block名称描述了此模块的用途,它是什么?
  • 各个模块可以相互嵌套,嵌套层级数量不受限

例如:

<!-- `header` block -->
<header class="header">
    <!-- Nested `logo` block -->
    <div class="logo"></div>

    <!-- Nested `search-form` block -->
    <form class="search-form"></form>
</header>

header 模块嵌套了logo模块和搜索表单模块

Element - 块的组成模块,不能与块分开使用,也不能自己单独使用。

  • element名称描述了在这模块中的用途,它是什么?
  • element名称的语法结构为  **block-name__element-name**,使用双下划线将block名称和element名称连接起来。
  • element 元素彼此之间可以相互嵌套,嵌套层级数量不受限
  • 一个element元素里可以嵌套包含一个block块,这就意味着,element名称定义不能为多层级结构,如 block__elem1__elem2 ,这种命名是不被允许的。

例如:

<form class="search-form">
    <div class="search-form__content">
        <input class="search-form__input">

        <button class="search-form__button">Search</button>
    </div>
</form>

search-forminput,search-formbutton,search-form__content 即为element元素。

search-form__content 元素中嵌套了element元素。

但是search-form__content__input 这种多层级命名element元素是不被允许的,类名过长,层级结构过多,不清晰。

Modifier - 定义block块或element元素的外观、状态或行为。

  • modifier 的名称一般描述了block块或element元素的外观,它的大小?它的状态?它的颜色
  • modifier 的名称语法结构为:block-name_modifier-name;block-name__element-name_modifier-name
  • 一般使用单下划线将它跟block元素或者element元素连接起来;布尔形式,区分是或不是的状态,完整的语法结构为:block-name_modifier-name;block-name__element-name_modifier-name

例如:

<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <!-- The `button` element has the `size` modifier with the value `m` -->
    <button class="search-form__button search-form__button_disabled">Search</button>
</form>

search-form__button_disabled 这种命名结构是布尔形式。

key-value键值对的形式,区分不同的状态。完整的语法结构则为:

  • block-name_modifier-name_modifier-value
  • block-name__element-name_modifier-name_modifier-value

例如:

<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <!-- The `button` element has the `size` modifier with the value `m` -->
    <button class="search-form__button search-form__button_size_m">Search</button>
</form>

search-form__button_size_m 这种命名结构就是键值对的形式

  • modifier 不能被单独使用,必须与block元素或者element元素联合使用。因为一个modifier就是用来描述此元素的外观、大小、一个实体的状态。

 BEM的优点与缺点?

优点

  • 结构简单,一目了然
  • 组件化,代码复用
  • 不使用标签选择器,避免父级元素内的标签的受影响。举个例子,商品详情页是允许商家自定义标签的,那么商家展示区域标签的祖先元素,一旦用标签选择器定义了样式,子子孙孙都要背负.

例如,将这个网页拆分成BEM的写法

无BEM写法:

<section>
    <h1>Sterling Calculator</h1>
    <form action="process.php" method="post">
        <p>Please enter an amount: (e.g. 92p, &pound;2.12)</p>
        <p>
            <input name="amount"> 
            <input type="submit" value="Calculate">
        </p>
    </form>
</section>

BEM 写法:

<section class="widget">
    <h1 class="widget__header">Sterling Calculator</h1>
    <form class="widget__form" action="process.php" method="post">
        <p>Please enter an amount: (e.g. 92p, &pound;2.12)</p>
        <p>
            <input name="amount" class="widget__input widget__input_amount"> 
            <input type="submit" value="Calculate" class="widget__input widget__input_submit">
        </p>
    </form>
</section>

元素清单:

  • widget
  • widget__header
  • widget__form
  • widget__input

这样就形成了一个可复用的块

注意其中的 widget__input_amountwidget__input_submit为Modifier

缺点

  • 类名变的更长,一个元素可能拥有多个class
  • id选择器无用武之地
  • class命名不能重复
  • Block的抽象至关重要

谁适用于BEM

项目复杂,复用模块较多,多人协作团队使用。

4.html组件引入规范&导入组件名和组件名规范

html组件引入的组件名称使用大驼峰,带结束标签

导入组件名使用大驼峰,

import TipsDialog from '../tips-dialog/index.vue'

组件名规范使用大驼峰

defineOptions({

    name: 'CompModel'

})

代码规范

代码规范主要是代码校验和自动格式化以及git提交规范校验。

1.代码校验和自动格式化

代码校验和代码格式化两个是相辅相成的,通过插件对代码进行校验并自动格式化成符合要求的格式。要实现需要依赖两个插件eslint和prettier。

配置eslint

下载依赖

yarn add -D eslint eslint-plugin-vue

npx eslint --init

init 命令会自动生成 .eslintrc.js

修改配置为:

module.exports = {
    root: true,
    ignorePatterns: ['node_moduls/*'],
    env: {
        browser: true,
        es2021: true,
        node: true,
        commonjs: true
    },
    extends: ['eslint:recommended', 'plugin:vue/essential', 'prettier'],
    overrides: [],
    parserOptions: {
        ecmaVersion: 'latest',
        sourceType: 'module'
    },
    plugins: ['vue', 'prettier'],
    rules: {
        'linebreak-style': ['error', 'unix'],
        'no-multiple-empty-lines': [1, { max: 2 }], //空行最多不能超过2行
        'vue/multi-word-component-names': 'off' //vue组件名去掉多单词限制
    }
}

配置文档

规则 | Rules_Eslint_参考手册_非常教程 (verydoc.net)

配置代码风格工具prettier

安装

yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

创建 .prettierrc

{
    "useTabs": false,
    "tabWidth": 4,
    "printWidth": 80,
    "singleQuote": true,
    "trailingComma": "none",
    "semi": false,
    "endOfLine": "lf"
}

官方地址

Configuration File · Prettier 中文网

2.git提交规范

相关配置在这篇文章,内容有点多

git提交规范-CSDN博客

api和数据规范

这个部分需要后端配置,后端返回的code需要代表一定含义,这样才能做统一数据处理,例如token失效的code为多少,接口超时的code或者接口报错的返回code为多少。

1.api封装

这个部分比较灵活,需要按照公司的业务或者开发的个人习惯进行封装,以下是一个例子:

import axios, { AxiosRequestConfig } from 'axios'
import qs from 'qs'
import { getToken } from './userInfo/token'
import { subscribe, MetaDataHelper } from '@gogoal/fund-utils'
import { blob2File } from './tools'
import { isFileList, isObject } from './types'

const SUCCESS_CODE = 0
const service = axios.create({
    // timeout: 200000, // request timeout
})
// response interceptor
service.interceptors.response.use(
    (response) => {
        const res = response.data
        if (res.code === SUCCESS_CODE) {
            return res.data
        } else {
            subscribe.notify('xhr-fail', res)
            console.log('xhr-fail response', response)
            return Promise.reject(res)

            // if (res.code == '1100' || res.code == '1103') {
            //     dealLogout()
            // } else {
            //     return Promise.reject(res)
            // }
        }
    },
    (error) => {
        return Promise.reject({
            code: 1,
            message: '服务器错误',
            error
        })
    }
)

interface RequestDataParams extends AxiosRequestConfig {
    needToken?: boolean /* 是否需要token */
    needToString?: boolean /* 是否需要将参数转成token */
    data?: Record<string, any>
    headers?: Record<string, any>
    config?: Record<string, any>
}
// 请求接口数据
// interface ResponseData<T = any> {
//     code: number
//     data: T
//     message: string
// }

const getData = function <T>({
    url = '',
    params = {},
    data,
    method = 'GET',
    needToken = false,
    needToString = false,
    headers,
    config
}: RequestDataParams): Promise<T> {
    if (needToken) {
        const token = getToken()
        params.token = token
    }
    const _params = {
        url,
        method
    }

    // deleteEmptyProperty(params);
    if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {
        Object.assign(_params, {
            data: needToString ? qs.stringify(data) : data,
            params: params
        })
    } else {
        Object.assign(_params, {
            params: params
        })
    }
    if (headers) {
        Object.assign(_params, {
            headers
        })
    }
    if (config) {
        Object.assign(_params, {
            config
        })
    }
    return service(_params)
}

/**
 * @description 公用 GET 请求
 */
const get = function <T>({
    url,
    params,
    needToken = false,
    needToString = false
}: RequestDataParams) {
    return getData<T>({
        url,
        params,
        needToken,
        method: 'GET',
        needToString
    })
}

/**
 * @description 公用 POST 请求
 */
const post = function <T>({
    url,
    params,
    data,
    needToken = false,
    needToString = false
}: RequestDataParams) {
    return getData<T>({
        url,
        params,
        data,
        needToken,
        method: 'POST',
        needToString
    })
}
const upload = function ({ url, data, params = {}, needToken = false }) {
    // debugger
    const _url = url || '/file/comm_upload_file'
    const _data = new FormData()
    // 将得到的文件流添加到FormData对象
    Object.keys(data).forEach((_k) => {
        const _p = data[_k]

        if (isObject(_p)) {
            _data.append(_k, JSON.stringify(_p))
        } else if (isFileList(_p)) {
            Array.from(_p).forEach((res) => {
                _data.append(_k, res)
            })
        } else if (isArray(_p)) {
            _p.forEach((res) => {
                getType(res) == 'File' && _data.append(_k, res)
            })
        } else {
            _data.append(_k, _p)
        }
    })
    return getData({
        url: _url,
        method: 'POST',
        data: _data,
        params,
        needToken
    })
}

function formatFile(query) {
    const _params = new FormData()
    // 将得到的文件流添加到FormData对象
    Object.keys(query).forEach((_k) => {
        const _p = query[_k]
        if (isObject(_p)) {
            // _params.append(_k, JSON.stringify(_p))
            _params.append(_k, _p)
        } else if (isFileList(_p)) {
            _p.forEach((res) => {
                _params.append(_k, res)
            })
        } else {
            _params.append(_k, _p)
        }
    })
    return _params
}
const downloadBlob = function (url, fileName) {
    const xhr = new XMLHttpRequest()
    xhr.open('get', url, true)
    xhr.responseType = 'blob' // 返回类型blob
    // 定义请求完成的处理函数,请求前也可以增加加载框/禁用下载按钮逻辑
    xhr.onload = function () {
        // 请求完成
        if (this.status === 200) {
            // 返回200
            const blob = this.response
            blob2File(blob, fileName)
        }
    }
    // 发送ajax请求
    xhr.send()
}

type File = {
    url: string
    query?: string
    params?: string
    fileName: string
    config: any
}
const downloadFile = ({ url, query, params, fileName, config = {} }: File) => {
    const _config: AxiosRequestConfig = Object.assign(
        {
            method: 'post',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded' // 请求的数据类型为form data格式
            },
            responseType: 'blob'
        },
        config
    )

    let _url = url || '/file_export/excel/common'
    if (query) {
        _url += `?${qs.stringify(query)}`
    }

    _config.url = _url
    _config.headers['X-Pro'] = MetaDataHelper.getId()

    if (params) {
        _config.data = qs.stringify(params)
    }

    return new Promise<void>((resolve, reject) => {
        axios(_config as AxiosRequestConfig)
            .then((response) => {
                const { data, message } = response
                if (!data && message) {
                    reject(response)
                    return
                }
                blob2File(data, fileName)
                resolve()
            })
            .catch((error) => {
                reject(error)
            })
    })
}

export default getData

export {
    service /* 导出方便修改默认参数 */,
    get,
    post,
    upload,
    downloadFile,
    downloadBlob,
    formatFile
}

使用:

const response = await service({
            url: '/api/v1/zyfp_account_home/bind_mobile',
            method: 'post',
            data: {
                source: 'login',
                mobile: bindForm.value.mobile,
                sms_code: bindForm.value.sms_code
            }
        })
2.数据规范

这个需要后端定,主要是接口返回的数据格式,例如返回code规范,返回对象规范等,这个需要根据具体情况具体分析。

脚手架搭建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1552029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI智能分析网关智慧食安监管系统方案

3.15晚会刚过不久&#xff0c;淀粉肠的“屈辱”终于得以洗清&#xff0c;但某些品牌奶茶、梅菜扣肉、预制菜等等&#xff0c;生产过程仍是触目惊心。如何提升食品安全管理水平&#xff0c;保障食品从生产到消费环节的质量和安全&#xff1f;TSINGSEE青犀智利用智能分析网关V4Ea…

mysql--事务四大特性与隔离级别

事务四大特性与隔离级别 mysql事务的概念事务的属性事务控制语句转账示例 并发事务引发的问题脏读脏读场景 不可重复读幻读幻读场景 事务的隔离级别读未提交读已提交可重复读&#xff08;MySQL默认&#xff09; 总结 mysql事务的概念 事务就是一组操作的集合&#xff0c;他是一…

x86的内存分段机制

8086 是 Intel 公司第一款 16 位处理器&#xff0c;诞生于 1978 年&#xff0c;所以说它很古老。 一.8086 的通用寄存器 8086 处理器内部共有 8 个 16 位的通用处理器&#xff0c;分别被命名为 AX、 BX、 CX、 DX、 SI、 DI、 BP、 SP。如下图所示。 “通用”的意思是…

幻兽帕鲁服务器多少钱?可真便宜呀

2024年全网最全的幻兽帕鲁服务器租用价格表&#xff0c;阿里云幻兽帕鲁游戏服务器26元1个月、腾讯云32元一个月、京东云26元一个月、华为云24元1个月&#xff0c;阿腾云atengyun.com整理最新幻兽帕鲁专用4核16G、8核16G、8核32G游戏服务器租用价格表大全&#xff1a; 阿里云幻…

8、鸿蒙学习-HAR

HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP&#xff0c;不能独立安装运行在设备上。只能作为应用模块的依赖项被引用。 一、创建…

【案例·增】获取当前时间、日期(含,SQL中DATE数据类型)

问题描述&#xff1a; 需要使用当前时间、日期&#xff0c;可以使用 SQL 中的 CURDATE() 、NOW()、CURTIME()运算符 案例&#xff1a; INSERT INTO table_name(current_time, column_name2,...) VALUES (NOW(),, ...)规则(Date 相关函数)&#xff1a; 规则(Date数据类型)

初识C++之命名空间(namespace)

初识C之入门 命名空间(namespace) 文章目录 初识C之入门 命名空间(namespace)1.为什么要有命名空间2. 命名空间 namespace使用方法3. 作用域限定符(::&#xff09;和 命名空间(namespace)4. 命名空间的定义5. 命名空间的嵌套6. 命名空间的使用7. 总结 1.为什么要有命名空间 在C…

通过Caliper进行压力测试程序,且汇总压力测试问题解决

环境要求 第一步. 配置基本环境 部署Caliper的计算机需要有外网权限;操作系统版本需要满足以下要求:Ubuntu >= 16.04、CentOS >= 7或MacOS >= 10.14;部署Caliper的计算机需要安装有以下软件:python 2.7、make、g++(gcc-c++)、gcc及git。第二步. 安装NodeJS # …

Tensorflow2.0笔记 - 自定义Layer和Model

本笔记主要记录如何在tensorflow中实现自定的Layer和Model。详细内容请参考代码中的链接。 import time import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets, layers, optimizers, Sequential, metricstf.__version__ #关于自定义l…

软考 - 系统架构设计师 - 关系模型的完整性规则

前言 关系模型的完整性规则是一组用于确保关系数据库中数据的完整性和一致性的规则。这些规则定义了在关系数据库中如何存储、更新和查询数据&#xff0c;以保证数据的准确性和一致性。 详情 关系模型的完整性规则主要包括以下三类&#xff1a; 实体完整性规则 这是确保每个…

【Java程序设计】【C00373】基于(JavaWeb)Springboot的社区疫情返乡管控系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

计算机网络——26通用转发和SDN

通用转发和SDN 网络层功能&#xff1a; 转发&#xff1a; 对于从某个端口 到来的分组转发到合适的 输出端口路由&#xff1a; 决定分组从源端 到目标端的路径 网络层 传统路由器的功能 每个路由器(Per Route)的控制平面 &#xff08;传统&#xff09; 每个路由器上都有实…

线程创建方式、构造方法和线程属性

欢迎各位&#xff01;&#xff01;&#xff01;推荐PC端观看 文章重点&#xff1a;学会五种线程的创造方式 目录 1.开启线程的五种方式 2.线程的构造方法 3.线程的属性及获取方法 1.开启线程的五种方式 创造线程的基本两步&#xff1a;&#xff08;1&#xff09;使用run方法…

C++取经之路(其二)——含数重载,引用。

含数重载: 函数重载是指&#xff1a;在c中&#xff0c;在同一作用域&#xff0c;函数名相同&#xff0c;形参列表不相同(参数个数&#xff0c;或类型&#xff0c;或顺序)不同&#xff0c;C语言不支持。 举几个例子&#xff1a; 1.参数类型不同 int Add(int left, int right)…

白盒测试、接口测试、自动化测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&…

NEO 学习之 MLE(最大似然估计)

文章目录 简单题目MLE 在不同的分布的运用正态分布指数分布均匀分布泊松分布 简单题目 此题问的是求丢色子&#xff0c;求得到偶数点的概率 求两次都得到硬币的背面的概率 拿球问题 符合的点数是 1,5,6 MLE 在不同的分布的运用 正态分布 对于给定的数据集 {1, 3, 4, 6, 7}&am…

linux:线程同步

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言线程同步条件变量接口简单示例pthread_cond_wait为什么要有mutex伪唤醒问题的解决 (if->while) 总结 前言 本文作为我对于线程同步知识总结 线程同步 同步&…

Appium设备交互API

设备交互API指的是操作设备系统中的一些固有功能&#xff0c;而非被测程序的功能&#xff0c;例如模拟来电&#xff0c;模拟发送短信&#xff0c;设置网络&#xff0c;切换横竖屏&#xff0c;APP操作&#xff0c;打开通知栏&#xff0c;录屏等。 模拟来电 make_gsm_call(phon…

大数据之scala

为什么学习scala spark是新一代内存级大数据计算框架&#xff0c;是大数据的重要内容 spark就是使用scala编写的&#xff0c;因此为了更好的学习spark&#xff0c;需要掌握scala这门语言 spark的兴起&#xff0c;带动scala语言的发展 scala发展历史 联邦理工学院的马丁 奥德…

走迷宫(BFS两种写法)

题目描述&#xff1a; 给定一个 nm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0 表示可以走的路&#xff0c;1 表示不可通过的墙壁。 最初&#xff0c;有一个人位于左上角 (1,1) 处&#xff0c;已知该人每次可以向上、下、…