js和vue巩固指引

一、环境:

1、安装vscode

2、需要安装一个叫node的东西,如果安装了下图就可以执行。

Node.js 提供了一个 ++可以在非浏览器环境下 执行 JavaScript 代码的++ 环境

(还包含,npm工具,就可以装东西了,所以需要装好node.js)
可以用来验证有没有node.js

官网安装指导

配置npm

# 查看
npm config get prefix   # 查看   全局安装目录   npm install xxx -g  时,会吧东西装进去
npm config get cache    # 查看   全局缓存目录

# 设置重新修改(C盘不好)---先新建文件(就建在安装的nodejs里面吧一般)
npm config set prefix "D:\soft\nodejs\node_global"
npm config set cache "D:\soft\nodejs\node_cache"

配置环境变量

在安装目录中,cmd 里, 可以 npm config list ,

其他地方如果不行,要配置环境变量, 安装时好像自动配上了,就是在系统变量path中加一个 安装目录路径就行、和上面安装指导有出入。

设置镜像源为淘宝源

npm config set registry https://registry.npm.taobao.org      # 过期
npm config set registry https://registry.npmmirror.com    # 最新
npm config list # 查看验证

npm --version

npm安装

全局安装

尝试安装nprogress

其他局部不用安装直接使用?(gpt说不能用)

局部安装

直接如:npm install screenfull@5.2.0, 安装后有package.json文件,里面记录有哪些包, node_modules文件夹里放实际的包

生产依赖和开发依赖关系分享

局部安装分装到生产依赖,还是开发依赖
默认为生产依赖

加 参数 --save-dev 或 -d 就保存为开发依赖:

本地运行时,其实不管是哪种,但是上线后,通常选择 生产依赖,在开发过程中,就把一些辅助工具的依赖,装到开发依赖

总结: 初学时安装,就都不加 -d , 当你知道它是打包工具,测试工具,等辅助工具时,就最好加上-d。

二、快速使用vue包学习

本来需要使用npm xxx 创建的vue项目+ 初始化调试,我们先不管了,直接解压里面的压缩包来用

提取码:iqvy

解压后,目录中package.json 中,name 就是项目名,可以改成自己喜欢的,然后把外层的文件名换一样的, 这样 就和 npm安装 是一样的效果了。

然后终端执行:npm install (要把packagejson中需要的插件,源码给下载好,生成node_modules文件)

仍然需要做一些准备工作:

1. 装插件 vetur (.vue文件高亮, 以及保存时代码风格化代码)

右键插件的【扩展配置】, 点stting.json。进行配置

javascript 复制代码
{
    "vetur.completion.scaffoldSnippetSources": {
        
        "workspace": "💼",
        "user": "🗒️",
        "vetur": "✌"
    },
    "[vue]":{
        "editor.defaultFormatter": "octref.vetur",
        "editor.detectIndentation": false,//缩进
        "editor.tabSize": 4,//缩进大小
        "editor.formatOnSave": true //保存自动格式化

    },
    "[python]": {
        "editor.formatOnType": true
    },
    "python.autoComplete.extraPaths": [],
    "python.analysis.extraPaths": []
}

2. 其他插件-下面有代码补全的功效

vue 3 snippets

vue vscode snippets

其他(如果不懂的话可以装一下这些,具体啥用我也不知道)

eslint先别装,它好像是限制代码格式的,不装为好

ESLint 插件 的主要功能是帮助你实时检查代码中的错误和规范问题,它会根据配置的规则自动检测并标记出代码中的潜在问题。)

如果已经装了 eslint , 那么项目下的.eslintrc.js, 可以下面这样设置

javascript 复制代码
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    'plugin:vue/vue3-essential',
    '@vue/standard'
  ],
  parserOptions: {
    parser: '@babel/eslint-parser',
    requireConfigFile: false
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'space-before-function-paren': 0, // 关闭在函数定义的左括号前强制使用一致的间距
    'eol-last': 0,                    // 关闭在文件末尾至少强制执行一个换行符
    'prefer-const': 0,                // 关闭对声明后从未重新赋值的变量,要常量声明
    'spaced-comment': 0,              // 关闭在注释中的//或/*后面强制使用一致的间距
    'no-multi-spaces': 0,              // 关闭不允许多个空格的检查
    'import/no-duplicates': 0,       // 解决vue组件重复导入问题
    indent: 0, // 解决js缩进问题
    'vue/multi-word-component-names': 0, // 解决组件名称单单词的问题
    'vue/no-unused-components': 0 //组件导入未使用问题
  }
}

运行启动

终端执行: npm run dev (其实就是使用vite 来启动,再package.json中还可以把dev 改成其他值,比如改成serve) ,那就还是npm run serve

然后就能浏览器访问页面了、

看src中的main.js文件, 引入的主文件, http://localhost:5173/ 访问的就是主文件。

接下来就是修改红框里的代码。来进行巩固学习。

1、入门知识

1. 这个看js的一些用法

http://localhost:5173/ 访问后,打开F12观察控制台

知识点都写在了 Study1.vue里面,就不介绍了

javascript 复制代码
// import App from './App.vue'
import App from './components/study/Study1.vue'

2. 这个是vue中的插值表达式{{}}

因为已经装了一些插件,如果是重新开始写代码,直接vbase, 选中其中一个模板,就可以快速生成代码,笔记已经写好了,直接用,知道就行。

不同变量的一些取值简单使用

javascript 复制代码
// import App from './App.vue'
import App from './components/study/Study2.vue'

3. v-text 和 v-html

效果和插值表达式差不多, v-text="" 中间可以放一些js语句,如 num +100

javascript 复制代码
import App from './components/study/Study3.vue'

4.v-bind, 简写 :

javascript 复制代码
import App from './components/study/Study4.vue'

属性前写了 v-bind: 或 : 后面就接变量

:class属性,后面的变量可以是字符串,可以是数组(推荐),也可以是对象

:style属性,后面可以字符串,数组, 字典对象(推荐)

a标签的,href属性能用, button标签的 disabled属性都可以使用

除此之外,引号中,是可以写js语句的,比如

javascript 复制代码
<div :style="{ color: 颜色变量名, 'font-size': 数值变量 + 'px' }">

5.v-if 和 v-show

注:v-if 和 v-else-if 和 v-else 是一组

javascript 复制代码
import App from './components/study/Study5.vue'

6.v-for 可以变出多个标签

javascript 复制代码
import App from './components/study/Study6a.vue' 

v-for = "" 引号中的写法 是 (item, index) in list, 当前标签就可以变成多个

  • 可以遍历数组(item,index) in list
  • 可以变量对象 (value, key, index) in person , 代码没举例

注:对于li标签,需要 :key="index" (是为了帮助 Vue 识别每个循环的元素,以便在数据更新时进行高效的重新渲染)

Study6a 和 Study6b的区别:

6a是 :key="index" , 6b是:key="item.id", 62更优秀,因为加值后,id 也不会变来变去

7. 操作动作属性 v-on 简写 @

javascript 复制代码
import App from './components/study/Study7.vue' 

v-on 写在有动作的标签里面,如<button></button>中

  • 可以放js代码,比如点了按钮就会执行
  • 可以触发函数,<button @click="addnum()">按钮</button>
  • 动作属性后面加.once, 如 <button v-on:click.once="count += 1">按钮</button> ,表示只会生效一次

接受参数 (能接受 事件对象)

1.定义事件与函数<input type="text" @input="input($event)" /> 也可以不打括号,函数就能默认接收到 事件对象

2.定义函数 const input = (e) => {console.log(e.target.value)} # e.target.value 表示该事件的对象的值 a.如是点击一个按钮, e.target 表示 该按钮标签 。

8. v-on 补充

  • @动作.once 一次
  • @动作.prevent
    • @click.prevent -----比如a标签的点击,会调整,可以prevent然后就不跳转,(也可以不加.prevent , 后面自定义函数,函数中要有,event.preventDefault()------Study8.vue
    • @submit.prevent 表单标签中加了这个,就不会刷新了
      • 我们使用 @submit.prevent 来监听表单的提交事件,并且使用 prevent 修饰符来阻止表单的默认提交行为。这样,当用户点击提交按钮时,不会触发表单的默认提交行为(即刷新),而是会执行我们自定义的 submitForm 方法。
      • 其实是 事件 event调用了e.preventDefault()
  • @动作.stop ,应该可以连起来用,没试过 .once.stop.prevent
  • 补充:input的键盘事件 - @keyup.enter=showMessge (键盘的回车事件)

9. v-on补充2.stop用在动作传递中起阻止传作用

  • @click.stop 停止(传递) (默认情况:如有多层,我点击内圈,相当于大的外圈也被点击了)
    • 其实是 事件 event调用了e.stopPropagation() 例子--Study9.vue----,如果在方法里使用了stopPropagation(),那么每层都会阻止了,可以注释,打开查

10. v-model双向绑定

---Study10.vue-

方式1:

假如不知道这个知识,可以用 @ v-on 和 : v-bind 结合,有双向绑定的效果,如下

: value,表示value取下面, @input 后面的js 意思又是把下面 改成 标签的value

<input type="text" :value="address" @input="address = $event.target.value" />
    {{ address }}

方式2

使用 v-model="xxxx" ,便可以直接双向绑定, 直接绑定就是input输入框的value值

v-model:value="name" 可以省略简写 v-model="name"

11. v-model 的修饰符

---Study11vue---

  • v-model.lazy 标移才把输入值,传到变量
  • v-model.number 只会把数字传到变量
  • v-model.trim 去两侧的空格

12. v-model用在单选框、各input项的value值要先定义

---Study12.vue---

单选type="radio" 和普通 input文本框不同的是, input的值是输入的,而单选框:

  • 每个value值,要先自己定义好(就像下来选项先有值)。 (再用 v-model = "变量")
  • 一组内每个input要同样的 v-model="变量"

13. v-model用在多选框、各input项的value值要先定义

---Study13.vue---

与单选框不同的是, v-model="xxx" , xxx需要是一个数组

14.v-model用在select下拉选框

---Study14.vue---

对于select标签。

  • 1.先定义,select中的option的 value的 值, (如果不定义,绑的就是文本值)
    1. select 标签中 写 v-model="xxx"

选中一项后, 绑定的值就是所选option的value

对于单选下拉框, xxx定义成 字符串

对于复选下拉框,xxx定义成 列表(数组)

2、 入门知识2

1. 计算属性变量

vue3中完整写法 :---Study15vue---

computed中间时一个对象

去获取customValue 值时, 就是调用的 get,获取到了xxxx

当某个函数中给 customValue 设置值时, 会调用其中的set方法

javascript 复制代码
    const customValue = computed({
      get() { return xxxx},
      set:()=> {},  //可以这么写 set(){}
    })

computed中间是一个函数时,那就默认为只读,及这个函数就是get的函数。---Study16.vue---

vue2中的写法,稍显复杂,但是也是要定义get 和set的方法。

vue2_计算属性.html , 直接右键open with live server 打开

如下的例子, 如果 全名:<input type="text" v-model="fullName">, 绑定fullName,

就可以实现写姓名和改全名, 写全名可以改姓名,

如果是绑定fullName2 , 那么只能实现get, 及只能写姓名和改全名

2. watch 侦听属性

---Study17.vue---

侦听对象,和处罚函数的位置。

最后一个对象,可以不写

  • immediate: true,表示初始化会调用watch中的 那个函数
  • deep: true, 暂未知效果

侦听对象可以是reactive对象,或者ref对象。 reactive里面的具体对象也可以侦听,但写发不同

---Study18.vue---

按钮都是改变值,然后他们会按箭头所示,来触发函数

其中: 侦听函数中的方法中,可以支持异步的东西。 如代码中,侦听到num变化,过了5秒后,又变成了900

对于侦听函数,可以有多个侦听对象。---Study19vue---

  • 侦听一个变量写法(深度) : watch(num, (newValue, oldValue) => {},{immediate:true, deep:true})
  • 侦听一组变量写法 : watch( [num1, num2] , (newValue, oldValue) => {}) 此时newValue 表示两个值

注:

  1. 对于监听对象是ref时,watch的第一个参数,监听对象写, 不要写num.value ,如笔记中,直接写 num就行

  2. 如果是监听reactive对象,监听对象不能写成 state.num , 可以试试写成 () => state.num (如代码中的监听state.hellonum时)

vue2的侦听属性写法 src\components\study\vue2_侦听属性.html

  • 复杂写法 wath:{ 监控对象名 :{handler(){xxx}, immediate:ture, deep:true}}
  • 简单写法 watch:{ 监控对象名(){ xxx } }

3. vue3新增的watchEffect函数

---Study20vue--- 由于写法简单,用法就简单了,大概不能获取到变化后的值和老值

4. 侦听和计算的区别:

计算: src\components\study\vue2_计算属性.html

侦听: src\components\study\vue_侦听与计算对比.html

侦听的对象,改变之后, 触发的函数里面可以加异步的东西。

如代码中: 修改了firstName, 2秒后才异步的设置了 全名

案例1:实现搜索框

  • 通过watch来实现---Study21.vue--- 逻辑:输入文本框,一变化,就侦听到, 把新填的值,进行判断,去过滤,赋值到定义好的 临时空数组 页面展示(新的临时数组)
  • 通过computed来实现---Study22.vue--- - 逻辑:输入文本框,一变化,就计算, 得到计算属性临时列表, 页面也展示这个临时列表。

案例2:实现搜索+升序降序

设置一个变量,不同的按钮,点击后来重设变量为不同的值,根据值来if判断, 实现升序,还是降序

  • 需要使用到 数组.sort() ,--原理看代码 src\components\study\Study23.vue
  • ------src\components\study\Study24.vue------- 代码里面用到了三目运算, 来实现是升序,还是降序

5. 生命周期函数

vue3的生命周期函数写法: src\components\study\Study25.vue

onUnmounted 的例子在后面看 ,搜索 路由导航 router-link 和 router.push()方式

注:代码里面的生命周期函数是 vue3里面的,写在setup里面的,如果要用vue2的写法,写在和setup同级。

补充:

onMounted 执行完,刷新一下,才会渲染出页面 , 看 src\components\study\Study26.vue

补充案例,它里面一直在执行 onMounted的回调函数, 看src\components\study\Study27.vue

3.入门知识3:父子组件

假如xxx2.vue里有xxx1.vue, 我们就说xxx2.vue为父组件。

1. 数据---父给子

-----src\components\study2\Father.vue----------注意main.js中注意路径对不对--------

效果如下:

父vue中

  • 需要指定components, 表示可以用哪些 子vue, (即能用到哪些子vue标签)
  • 子组件标签中,可自定义属性,把变量 传给子组件
    • 如果不用 v-bind , 如下图 taoke="111" 就是传的字符串, 下图用了v-bind , :taoke="111" , 传过去的是number

子组件中:

用props来接收

方法1:

javascript 复制代码
  props: ['abc', 'age', 'hij'],

方式2-定义了数据格式:

javascript 复制代码
props: { abc: String, age: Number, hij: Array },

方式3:放松2的基础上还可以这样写,就还能定义默认值

javascript 复制代码
props: {
    abc: String,
    age: Number,
    hij: Array,
    taoke: {
      type: String,
    //   required: true,
      default: '如果父没有传,就用我'
    }
  }

注: 子组件中,父给子的 props的值,不能直接更改,,可以的做法就是, 先把 props的值,取到放到data中

2. 数据- 子给父

方式1 子组件中emit的方式

src\components\study2\Father2.vue 效果如下

父vue中

  • 用 components ,包含哪些组件。
  • 在子组件标签中,用v-on绑定一个自定义属性 = 函数 (可以接受子组件 emit传过来的参数)。

子vue中(Child2.vue)

  • 使用emit('自定义属性', state) ,这样就能把state传给父中的函数,并能接收一个参数

方式2: 子组件中,定义props.onMounted中给props给值

子:

要定义props, onMounted中给props给值 (setup要写成 setup(props){} )

父中:

子组件标签中使用abc属性, 后面接函数 () (函数可以接受一个参数,该参数就是abc传来的值)

方式3(和1 差不多。还要多经过一层)

子中: 是一样的, 用emit 来发射

父中:子组件标签中, 写一个 ref= "child" , 然后用如图的写法

3. 父组件中,使用component标签来放子组件

src\components\study2\Father4.vue

4 . 插槽

插槽允许父组件在使用子组件时向子组件传递额外的内容,以便在子组件的特定位置插入这些内容。

根据现象可以观察,无名插槽会挤在一起, 展示顺序要看 子组件中 各个 slot的位置

无名插槽

src\components\study3\Father.vue

  • 子组件中,使用 <slot> 标签来标记一个插槽位置:
  • 父组件中,在子组件标签内插入内容来填充插槽。这些内容将会被传递到子组件中对应的 <slot>标签位置

有名插槽

src\components\study3\Father2.vue

使用具名插槽(有名插槽)来更灵活地在父组件中向子组件传递内容。

具名插槽允许你在子组件中定义多个插槽位置,并在父组件中选择要填充的插槽。这对于需要在不同位置插入不同内容的情况非常有用。

  • 子中,slot 标签要指定一个 name
  • 父中,使用template标签,并用v-slot指定 插槽名字, 或者用简写 #插槽名字

子组件里可以用在插槽位置 slot标签 把子组件的数据传给 父组件 。

src\components\study3\Father3.vue

那个tmplist命名是自己随便命的

4. 入门4、跨组件通信

实际业务情况常常为,一个公共组件,比 列表组件,它们里面多为inject, 当具体的一个业务列表时, 就会provide对应业务的数据

如下图分别是

src\components\study3\One1.vue 使用了公共组件作为子组件

src\components\study3\One.vue 使用了公共组件作为子组件

补充:

可以provide reactive的一项(上面的例子)

可以provide 整个reactive对象

也可以provide 一个 ref 对象

三、vue框架

1. 路由配置基础

原本:一个路由关联一个vue组件, 当访问的路由时,组件内容会去替换<router-view> 或者另写法 <RouterView />

以上知识点:

路由模式 即history属性有两种

  • createWebHashHistory, 项目路径要加 路由前面要加 /#
  • createWebHistory

两种方式:

// which is lazy-loaded when the route is visited.(访问路由,才加载这个组件 ,懒加载)

特点: 项目打包时,一个组件打包成一个js文件

    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')

npm i vue-router, 可以看下packagege.json中有没有安装 npm i vue-router@next --save

  1. 总组件中(要改main.js中的那个组件) 改成 import App from './App2.vue', 简介一点
  2. 配置新的url, 路由---组件 (router文件 中 index.js), /test路由中选一个 vue文件

路由配置是如何生效的, 是由于有main.js文件,里面使用到了配置。

总组件中(路由为/ 的),可以写访问 子路由的东西, 访问子路由后,对应的组件展示在位置。

2.路由重定向配置

准备工作:

main.js中,import App from './components/luyou/test1.vue'

然后点击他们都可以实现路由的跳转

方式1:使用 router-link ,跳转 (申明式) 方式2:是方法中使用router.push('/about1')

  • to表示路由, to后面的值 和 router index.js中的 path 一样, 如下有两种方式
javascript 复制代码
// 写法1
<router-link to="/about1" active-class="active">about</router-link>
// 写法2 (这样写,就可以用path 或者用name了)
<router-link :to="{ path: '/home1' }" active-class="active">首页ya</router-link>
  • active-class 表示 选中后的样式,如下active,就写到style里面, 比如代码里是写的红色
补充:不展示的路由的组件,它就失效了

时效了,就会触发生命周期函数的

setup中的onBeforeUnmount 和onUnmounted 方法 (vue3)

或者和setup同级的 unmounted 方法 (vue2)

让不展示的路由组件保持挂载在页面,不被摧毁 (拥有缓存)

作用:防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。

做法:在饮用子组件的的外层添加了 标签

src\components\luyou\test1.vue 中 没有生效,不知道为什么,如果成功,

参数:

  • include ="子组件的名称" 子组件的名称定义写在,和setup同级,写name="xxx",如下图,就可以针对性,对某个组件进行缓存
  • 如果需要限制缓存的组件数量,你可以使用 max 属性进行设置,如 <keep-alive max="3">

4. 路由的前进后退

vue3

javascript 复制代码
script中
import { useRouter } from 'vue-router'

或者   import router from '@/router'   // 项目文件中自己定义的(不要中括号) ,下面就不用const  router了
---------------setup中
const router = useRouter()



router.forward()    # 前进
router.back()       # 后退 
router.go()        # 传-1,就是后退一下,-2是两下, 1是前进,  不传没效果

vue2

javascript 复制代码
不是setup的
this.$router.forward()
this.$router.back()
this.$router.go(1)

5.嵌套路由(二级路由)

访问/kengcheng路由时, 访问kecheng.vue时,的css样式,需要装一个插件 npm install -D less

这里我们看出,那个Back.vue的内容,后端内容列表就放出来。过程是下图这个样子

main.js中,还是用import App from './components/luyou/test1.vue' 作为主组件,

点击传参到kekeng1,路由,对应组件是 src\components\luyou\Kecheng2.vue

使用this.$route.query 获取时,及格式为 路由?name=xxx&age=yyy的格式

传参方式1:

点击前端,进行查看

/about1/front?text2=小滴课堂yaya

多参数/about1/front?text2=小滴课堂yaya & address=xxxx


传参方式2-实现效果和上面一样也是url传参:

点击后端,进行查看

router-link 里面的to参数 里面放一个字典, 里面有个query, 再放参数对象

使用this.$route.params获取时,效果是把参数直接拼到路由

方式1: 点击params传参, 进行查看

三个地方:

  1. 路由配置,把参数配置成路由中。

2 router-link处

  1. 获取要这样写

方式2,

和方式1,不同的是 to后面接对象,且带一个params对象

router-link的replace的意思
  1. 看 这, 有了他效果就是,点击其他也没,再到这里, 后退就后退不过去了, 点击后退,或者点击浏览的返回都退不去了

7.编程式导航

看src\components\luyou\test1.vue

this.$router.push('/home1')

javascript 复制代码
//vue3
import { useRouter } from 'vue-router'
const router = useRouter()            // 这个也写在setup里面

或者  import router from '@/router'   // 项目文件中自己定义的(不要中括号)
方法中写这个:router.push('/about1')

//vue2的写法
this.$router.push('/home1')
   

传参:

路由配置一下, 也可以像6中 的prarams传参一样

javascript 复制代码
//多参数的传递
this.$router.push('/home1/taoke/18')        // 需要配置好路由为  path:/home1/:name/:age


// 也可以用对象的形式 (和下方的 params传参的对象,一样的传递方式)
this.$router.push(  { name: 'ceshi', params: { text: name } })     

// 待跳转的组件获取值,也和下面的  parms传参一样
mounted() {
    console.log(this.$route.params) // {myName: "直接拼在后面"}
  }

this.$router.replace('/home1')

如果是这种,相比方式2, 它会跳转,并且会替换上一个路由记录,返回就不会一层一层返回

8.路由守卫

  1. 组件的路由进行配置
  1. 看下面这部分代码,是要放到路由的index.js中的,

如果加上这部分代码后, 每个页面都不能访问,要自己回到 / login

javascript 复制代码
router.beforeEach((to, from, next) => {

  const userId = localStorage.getItem('userId')
  // eslint-disable-next-line no-cond-assign
  if (to.path === '/login') {
    next()
  } else if (userId) {
    next()
  } else {
  next('/login')
  }


}

)
  1. 换个判断逻辑,路由配置里的额外信息判断,某些也没再校验是否登录
javascript 复制代码
router.beforeEach((to, from, next) => {

  const userId = localStorage.getItem('userId')
  // eslint-disable-next-line no-cond-assign
  if (to.meta.needAuth ) {
    if (userId) {
      next()
    } else { 
      next('/login')
      alert('请登录')
    }
  } 
  else {
    next()
 
  }
}
)

模拟登录

相关推荐
向前看-9 分钟前
验证码机制
前端·后端
燃先生._.1 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖2 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235242 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240253 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar3 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人4 小时前
前端知识补充—CSS
前端·css
GISer_Jing4 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245524 小时前
吉利前端、AI面试
前端·面试·职场和发展