IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue常用属性 -> 正文阅读

[JavaScript知识库]Vue常用属性

目录

1、计算属性 - computed

2、监听器 - watch

3、综合案例:完善购物车

4、自定义指令 - directive

5、过滤器 - filter

6、混入 - mixin

7、生命周期

8、虚拟DOM与diff算法

9、双向数据绑定原理


1、计算属性 - computed

模板中放入太多的逻辑(方法)会让模板过重且难以维护,使用计算属性可以让模板变得简洁易于维护。计算属性是基于它们的响应式依赖进行缓存的,计算属性比较适合对多个变量或者对象进行处理后返回一个结果值,也就是数多个变量中的某一个值发生了变化则我们监控的这个值也就会发生变化。

计算属性定义在Vue对象中,通过关键词computed属性对象中定义一个个函数,并返回一个值,使用计算属性时和data中的数据使用方式一致。

核心点:

  • 计算属性其在代码的表现也是方法,但是与methods不同

    • 计算属性必须有return

  • 在某些场景下,计算属性的效率要比methods效率高

    • 计算属性支持数据的缓存操作(在依赖数据不变的情况下),而methods不行

示例

<div id="app">
 ? ?<!-- 当多次调用 cfn计算属性时只要里面的 num值不改变,它会把第一次计算的结果直接返回直到data中的num值改变 计算属性才会重新发生计算 -->
 ? ?<div>{{ cfn }}</div>
 ? ?<div>{{ cfn }}</div>
 ? ?<!-- 调用methods中的方法的时候  他每次会重新调用 -->
 ? ?<div>{{ fn() }}</div>
 ? ?<div>{{ fn() }}</div>
</div>
<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?const vm = new Vue({
 ? ? ? ?el: "#app",
 ? ? ? ?data: {
 ? ? ? ? ? ?num: 10,
 ? ? ?  },
 ? ? ? ?// 方法
 ? ? ? ?methods: {
 ? ? ? ? ? ?fn() {
 ? ? ? ? ? ? ? ?console.log("methods");
 ? ? ? ? ? ? ? ?return this.num;
 ? ? ? ? ?  },
 ? ? ?  },
 ? ? ? ?// 计算属性
 ? ? ? ?computed: {
 ? ? ? ? ? ?cfn() {
 ? ? ? ? ? ? ? ?console.log("computed");
 ? ? ? ? ? ? ? ?return this.num;
 ? ? ? ? ?  },
 ? ? ?  },
 ?  });
</script>

注意:只要依赖的数据源不发生改变,计算属性里的对应方法就只被调用1次,其它时候被调用时则使用缓存。提高效率。

2、监听器 - watch

使用watch来侦听data中数据的变化,watch中的属性(watch是对象格式)一定是data 中已经存在的数据。(特殊情况除外)

使用场景:数据变化时执行异步或开销比较大的操作

典型应用:http://www.pinyinzi.cn/

监听器

案例:给定三个输入框,第一个为姓输入框,第二个为名输入框,第三个为姓名组合结果框;要求当用户更新姓或名后,第三个输入框自动生成完整的姓名结果。

语法

new Vue({
    .....
    watch: {
        data中数据的名称: fn方法,
        ....
    }
})

参考代码:

<div id="app">
 ? ?<p><input type="text" v-model='firstName' placeholder="姓" /></p>
 ? ?<p><input type="text" v-model='lastName' placeholder="名" /></p>
 ? ?<p><input type="text" v-model='fullName' placeholder="全名" /></p>
</div>
?
<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?const vm = new Vue({
 ? ? ? ?el: '#app',
 ? ? ? ?data: {
 ? ? ? ? ? ?firstName: '',
 ? ? ? ? ? ?lastName: '',
 ? ? ? ? ? ?fullName: ''
 ? ? ?  },
 ? ? ? ?watch: {
 ? ? ? ? ? ?firstName: function(val) {
 ? ? ? ? ? ? ? ?this.fullName = val + ' ' + this.lastName
 ? ? ? ? ?  },
 ? ? ? ? ? ?lastName: function(val) {
 ? ? ? ? ? ? ? ?this.fullName = this.firstName + ' ' + val
 ? ? ? ? ?  }
 ? ? ?  }
 ?  })
</script>

注意点:

  • 声明监听器,使用的关键词是watch

  • 每个监听器的方法,可以接受2个参数,第一个参数是新的值,第二个参数是之前的值

注意:当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,此时就需要deep属性对对象进行深度监听

使用对象的数据形式改写上述案例参考代码:

<div id="app">
 ? ?<p><input type="text" v-model='userinfo.firstName' placeholder="姓" /></p>
 ? ?<p><input type="text" v-model='userinfo.lastName' placeholder="名" /></p>
 ? ?<p><input type="text" v-model='userinfo.fullName' placeholder="全名" /></p>
</div>
?
<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?const vm = new Vue({
 ? ? ? ?el: '#app',
 ? ? ? ?data: {
 ? ? ? ? ? ?userinfo: {
 ? ? ? ? ? ? ? ?firstName: '',
 ? ? ? ? ? ? ? ?lastName: '',
 ? ? ? ? ? ? ? ?fullName: ''
 ? ? ? ? ?  }
 ? ? ?  },
 ? ? ? ?watch: {
 ? ? ? ? ? ?userinfo: {
 ? ? ? ? ? ? ? ?// handler是固定的写法
 ? ? ? ? ? ? ? ?handler(val) {
 ? ? ? ? ? ? ? ? ? ?this.userinfo.fullName = val.firstName + ' ' + val.lastName
 ? ? ? ? ? ? ? ? ? ?// 对象支持引用传值
 ? ? ? ? ? ? ? ? ? ?val.fullName = val.firstName + ' ' + val.lastName
 ? ? ? ? ? ? ?  },
 ? ? ? ? ? ? ? ?deep: true
 ? ? ? ? ?  }
 ? ? ?  }
 ?  })
</script>

面试题:vue中计算属性与监听器有什么区别??

  • 设计方式上的区别

    • 计算属性:依赖数据的,只要数据不变,它会套用固定的流程去执行。我们写好之后一劳永逸的。(我们只要告诉其数据的处理规则)

    • 监听器:需要我们自己去写比较复杂的数据处理过程,比如说一些异步的操作、开销大的操作(我们自己写数据处理逻辑)

  • 响应方式上的区别

    • 计算属性:支持深度深度数据是否变化的监听的(默认的)

    • 监听器:默认不支持深度响应,仅支持字面量处理,但是其支持通过代码的改动来支持深度监听

3、综合案例:完善购物车

进一步需求:

  • 增加自动计算总价功能,且只计算被选中的商品【计算属性】

  • 增加反选功能【事件绑定】

  • 当手动选中全部商品,全选复选框自动选中,但凡有一个商品的复选框没有被选中,则全选复选框不选中【监听器】

4、自定义指令 - directive

[directive] ?https://cn.vuejs.org/v2/guide/custom-directive.html?

除了核心功能默认内置的指令,Vue也允许开发者注册自定义指令。有的情况下,对普通DOM元素进行底层操作,这时候就会用到自定义指令绑定到元素上执行相关操作。

自定义指令分为:全局指令和局部指令,当全局指令和局部指令同名时以局部指令为准(局部指令的优先级高于全局的)。

问题:全局与局部有什么区别?

  • 在当前(非工程化,每一个文件都是一个html文件)的时候是没区别的

  • vue工程化的时候是有区别的

    • 全局的适用于整个项目的(常用)

    • 局部的适用于当前组件的

自定义指令常用钩子函数(名字固定的函数)有:

  • bind:在指令第一次绑定到元素时调用(在该环节中是获取不到父节点的,父节点是null),序号:1

  • inserted:被绑定元素插入父节点时调用(在该环节中是可以获取到父节点的),序号:2

  • update:数据更新时调用,序号:3(该环节会重复触发)

  • componentUpdated:指定元素及子节点更新完成后会触发

  • unbind:取消绑定后触发

请注意:不管在定义全局还是局部自定义指令时,所提及的指令名均是不带v-前缀的名称

全局指令语法

// 无参
Vue.directive('指令名',{
    钩子函数名: function(el){
 ? ? ? ?// 业务逻辑
 ?      // el参数是挂载到的元素的DOM对象
 ?      // <div v-abc>123</div>
 ?  }
}
?
// 传参
Vue.directive('指令名',{
    钩子函数名: function(el,binding){
 ?      let param = binding.value
 ? ? ? ?// 业务逻辑
 ?  },
 ? ?....
}

请务必注意,作为全局配置,不能将其写在指定的Vue实例里,后续其它全局配置亦是如此

局部自定义指令定义

可以在new Vue的时候添加directives以注册局部自定义指令,局部自定义指令只能在当前组件实例中使用:

directives: {
 ?指令名: {
 ? ?// 指令的定义
 ? ?钩子函数名: function (el,binding) {
 ? ? ?// 业务逻辑
 ?  }
  }
}

函数简写(了解,使用机会很少)

部分时候,我们可能想在 bind update 时触发相同行为(如果只是其一,则还是单独分开声明),而不关心其它的钩子。那么这样写:

// 全局
Vue.directive('指令名', function (el,binding) {
 ?// 业务逻辑
})
?
// 局部
directives: {
 ?指令名: function (el,binding) {
 ? ? ?// 业务逻辑
  }
}

在自定义指令的方法中,不能像以前的methods中的方法一样使用关键词this,此时this关键词指向的是Window对象。

案例:使用自定义指令实现以下效果

  • 使用全局指令定义自定义的v-red(不传参)v-color(传参),在元素被插入时设置内容颜色

  • 使用局部自定义指令实现v-mobile(不传参)验证用户输入的是否是合法的手机号,不合法手机号为红色,合法为黑色

<div id="app">
 ? ?<div>
 ? ? ? ?<!-- 指令v-red,实现将文字的颜色设置成红色 -->
 ? ? ? ?<div v-red>武汉上演建党百年长江灯光秀</div>
 ? ? ? ?<!-- 指令v-color:实现将文字的颜色设置成指定的颜色 -->
 ? ? ? ?<div v-color="'blue'">将延长边境防疫管控1年?中方回应</div>
 ? ?</div>
 ? ?<div>
 ? ? ? ?<!-- 指令v-mobile:需要验证用户输入的手机号是否合法 -->
 ? ? ? ?<input type="text" v-model="mobile" v-mobile />
 ? ?</div>
</div>
<script src="./js/vue.js"></script>
<script>
 ? ?// 全局自定义指令
 ? ?Vue.directive("red", {
 ? ? ? ?// bind: function(el){
 ? ? ? ?// ? ? // el是指令绑定的dom对象
 ? ? ? ?// ? ? console.log(el);
 ? ? ? ?// ? ? // 获取父节点,当然当前bind的时候是获取不到的,因此为null
 ? ? ? ?// ? ? console.log(el.parentNode);
 ? ? ? ?// },
 ? ? ? ?inserted: function (el) {
 ? ? ? ? ? ?// el表示dom对象
 ? ? ? ? ? ?console.log(el);
 ? ? ? ? ? ?// el.parentNode表示其父节点
 ? ? ? ? ? ?console.log(el.parentNode);
 ? ? ? ? ? ?// 通过dom对象,设置颜色
 ? ? ? ? ? ?el.style.color = 'red'
 ? ? ?  }
 ?  });
 ? ?Vue.directive("color", {
 ? ? ? ?inserted: function (el, binding) {
 ? ? ? ? ? ?console.log(el, binding);
 ? ? ? ? ? ?// binding.value表示属性的值(该值不是看到的表达式,而是解析完后的值)
 ? ? ? ? ? ?el.style.color = binding.value
 ? ? ?  }
 ?  })
 ? ?new Vue({
 ? ? ? ?el: "#app",
 ? ? ? ?data: {
 ? ? ? ? ? ?mobile: ""
 ? ? ?  },
 ? ? ? ?// 自定义指令:
 ? ? ? ?directives: {
 ? ? ? ? ? ?mobile: {
 ? ? ? ? ? ? ? ?// 定义需要使用的函数
 ? ? ? ? ? ? ? ?update: function (el) {
 ? ? ? ? ? ? ? ? ? ?// console.log(el);
 ? ? ? ? ? ? ? ? ? ?// 获取手机号
 ? ? ? ? ? ? ? ? ? ?let mobile = el.value
 ? ? ? ? ? ? ? ? ? ?// 正则表达式验证手机号是否合法
 ? ? ? ? ? ? ? ? ? ?if (/^1[3-9]\d{9}$/.test(mobile)) {
 ? ? ? ? ? ? ? ? ? ? ? ?el.style.color = "black"
 ? ? ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ? ? ?el.style.color = "red"
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  }
 ?  })
</script>

5、过滤器 - filter

作用:格式化数据,比如将字符串格式化为首字母大写、将日期格式化为指定的格式等。

  • 过滤器可以定义成全局过滤器和局部过滤器。

  • 过滤器的本质就是一个方法,使用过滤器实际上就相当于方法调用,仅是书写形式上的差异(使用的时候需要用“|”(shift + \),其也可以被称之为管道变量/数据修饰符

    • 语法:

      • {{待修饰的数据|过滤器方法名}}

      • {{待修饰的数据|过滤器方法名(参数1,参数2....)}}

  • 这玩意在vue3中已经废弃了

    • vue3中解决办法是通过methods来替代

过滤器

声明语法:

// 全局过滤器
Vue.filter('过滤器名称',function(value[,arg1,arg2...]){
    //过滤器业务逻辑
    return ....
})
?
// 局部过滤器
el: '#app',
data: {},
filters: {
 ? ?过滤器名称: function(value[,arg1,arg2...]){
 ? ? ? ?return something
 ?  },
 ? ?// ....
}

过滤器的处理函数中的第一个参数固定绑定的待处理数据,后续可以根据需要添加自定义参数

使用语法:

<!-- 过滤器使用 -->
<div>{{msg | upper}}</div>
?
<!-- 过滤器允许连续使用,“前 → 后”按顺序执行 -->
<div>{{msg | upper | lower}}</div>
?
<!-- 过滤器支持在v-bind中使用 -->
<div v-bind:id='id | formatId'></div>
?
<!-- 过滤器支持传参 -->
<div>{{msg | mysub(1,2)}}</div>

案例:声明转字母为大写的全局过滤器和转字母为小写的局部过滤器并使用

<body>
 ? ?<div id="app">
 ? ? ? ?<h4>{{msg | toUpper}}</h4>
 ? ? ? ?<h4>{{msg | toLower}}</h4>
 ? ?</div>
</body>
?
<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?// 全局过滤器:转字母为大写
 ? ?Vue.filter('toUpper',(val) => {
 ? ? ? ?return val.toUpperCase()
 ?  })
?
 ? ?const vm = new Vue({
 ? ? ? ?el: '#app',
 ? ? ? ?data: {
 ? ? ? ? ? ?msg: 'HeLLo WoRld'
 ? ? ?  },
 ? ? ? ?// 局部过滤器:转字母为小写
 ? ? ? ?filters: {
 ? ? ? ? ? ?toLower: (val) => {
 ? ? ? ? ? ? ? ?return val.toLowerCase()
 ? ? ? ? ?  }
 ? ? ?  }
 ?  })
</script>

6、混入 - mixin

混入(mixins)是一种分发Vue组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,==所有混入对象(加的水)的选项将被混入该组件本身的选项(锅底)。==

通俗来讲,就是把一部分可复用的代码片段,加入到另一个代码中。

混入分为全局混入和局部混入。

混入

示例:

  • 局部混入(按需混入)【推荐】

<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?// 定义一个混入对象(局部混入)
 ? ?var myMixin = {
 ? ? ? ?created: function () {
 ? ? ? ? ? ?this.hello();
 ? ? ?  },
 ? ? ? ?methods: {
 ? ? ? ? ? ?hello: function () {
 ? ? ? ? ? ? ? ?console.log("hello from mixin!");
 ? ? ? ? ?  },
 ? ? ?  },
 ? ? ? ?computed:{
 ? ? ? ? ? ?....
 ? ? ?  }
 ?  };
 ? ?// Vue实例
 ? ?const vm = new Vue({
 ? ? ? ?mixins: [myMixin],
 ?  });
</script>
  • 全局混入(强制混入)

<script src="./js/vue.js"></script>
<script type="text/javascript">
 ? ?// 全局混入
 ? ?Vue.mixin({
 ? ? ? ?created: function () {
 ? ? ? ? ? ?var myOption = this.myOption;
 ? ? ? ? ? ?if (myOption) {
 ? ? ? ? ? ? ? ?console.log(myOption);
 ? ? ? ? ?  }
 ? ? ?  },
 ?  });
?
 ? ?new Vue({
 ? ? ? ?data: {
 ? ? ? ? ? ?myOption: "hello!",
 ? ? ?  }
 ?  });
</script>

注意事项

  • 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,合并策略:

    • data数据对象发生冲突时以组件(被混入对象)数据优先

    • 同名钩子函数(生命周期函数)将合并为一个数组,都将被调用,并且混入对象的钩子将在组件自身钩子之前调用

    • 值为对象的选项,例如 methodscomponentsdirectives,将被合并为同一个对象。两个对象键名冲突时,取组件对象(自身)的键值对

  • 全局注册使用时需要格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例

7、生命周期

生命周期:从vue实例产生开始到vue实例被销毁这段时间所经历的过程。

vue更像工具人,在整个过程中只会按照作者预设的程序去做事,不能由开发者去控制或者diy。如果这样开发时限制是比较多的,因此作者开放了生命周期,允许我们定义vue在特定的时候去做我们让其做的事情(钩子函数)。

每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM,在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,目的是给予用户在一些特定的场景下添加他们自己代码的机会。

vue2中一共有11个生命周期。

Vue生命周期的主要阶段

  • 挂载(初始化相关属性)

    • beforeCreate

      • 注意点:在此时不能获取data中的数据,也就是说this.msg得到的是undefined

    • created

    • beforeMount

    • mounted【页面加载完毕的时候就是此时】

      • 注意点:默认情况下,在组件的生命周期中只会触发一次

  • 更新(元素或组件的变更操作)

    • beforeUpdate

    • updated

      • 注意点:可以重复触发的

  • 销毁(销毁相关属性)

    • beforeDestroy

      • 注意点

    • destroyed

销毁(手动)使用this.$destroy()

生命周期

关于8个生命周期涉及到的方法,可以参考Vue官网API:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90

8、虚拟DOM与diff算法

什么是虚拟DOM?

虚拟DOM

定义:指将真实的dom按照特定的语法转化(抽象)成一个js对象,这个js对象称之为虚拟dom

什么是diff(different)算法?

差异比较算法的一种,把树形结构按照层级分解,只比较同级元素。不同层级的节点只有创建和删除操作

diff算法

虚拟DOM+diff算法的方式与传统DOM操作相比,有什么好处?

传统DOM操作:在一次操作中,往往会伴随多次个DOM节点更新,浏览器收到第一个DOM请求后并不知道还有若干次更新操作,因此会马上执行流程,最终执行若干次次。在后续找DOM坐标的时候,可能因为前期更新DOM导致了后续需要寻找的DOM坐标发生了变化。而操作DOM频繁还会出现页面卡顿,影响用户体验。

虚拟DOM+diff算法:若一次操作中有若干次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这若干次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性放到DOM树上,再进行后续操作,避免大量无谓的计算量。

建议:面试之前一定要去找下比较正规的理论性的东西。

9、双向数据绑定原理

面试可能会被大概率问到,需要领悟其中的关键词“代理”、“Object.defineProperty(vue2)”、“Proxy(vue3)”

核心:数据订阅、数据劫持(代理)

当把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,使用Object.defineProperty把这些属性全部转为getter(获取)/setter(设置)。

Object.defineProperty(obj, prop, descriptor)
// 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
// 应当直接在Object构造器对象上调用此方法,而不是在任意一个Object类型的实例上调用。

obj

要定义属性的对象。

prop

要定义或修改的属性的名称 。

descriptor

要定义选项,“{ }”。在这里面设置getter和setter。

示例代码:

<body>
 ? ?<div id="app">
 ? ? ? ?<div>
 ? ? ? ? ? ?<!-- 输入框 -->
 ? ? ? ? ? ?<input type="text" id="inpt" οninput="changeVal(this.value)" />
 ? ? ? ?</div>
 ? ? ? ?<div id="content"></div>
 ? ?</div>
?
 ? ?<script>
 ? ? ? ?// 1. 定义数据源
 ? ? ? ?var data = {
 ? ? ? ? ? ?msg: "hello world.",
 ? ? ?  };
 ? ? ? ?// 等同于之前的data属性
?
 ? ? ? ?// 2. 通过dom操作将数据写在页面上(一锤子买卖)
 ? ? ? ?document.getElementById("inpt").value = data.msg;
 ? ? ? ?document.getElementById("content").innerText = data.msg;
?
 ? ? ? ?// 3. 通过Object.defineProperty()去实现数据的劫持(代理)
 ? ? ? ?var obj = {};
 ? ? ? ?Object.defineProperty(obj, "proxy", {
 ? ? ? ? ? ?// 设置getter和setter
 ? ? ? ? ? ?// 代理获取数据
 ? ? ? ? ? ?get() {
 ? ? ? ? ? ? ? ?return data.msg;
 ? ? ? ? ?  },
 ? ? ? ? ? ?// 代理设置数据
 ? ? ? ? ? ?set(val) {
 ? ? ? ? ? ? ? ?data.msg = val;
 ? ? ? ? ? ? ? ?document.getElementById("content").innerText = val;
 ? ? ? ? ?  },
 ? ? ?  });
?
 ? ? ? ?// 4. input事件的处理程序
 ? ? ? ?function changeVal(val){
 ? ? ? ? ? ?// console.log(val);
 ? ? ? ? ? ?// 更新数据源
 ? ? ? ? ? ?obj.proxy = val;
 ? ? ?  }
 ? ?</script>
</body>

vue2与vue3双向数据绑定的实现比较参考:

https://www.jianshu.com/p/255d4dec710a

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:05:05  更:2021-08-03 11:05:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 4:11:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码