Vue 不睡觉教程3 - 来点实在的:自动计算剩余时间的任务列表
名字灵感来自我的书《HBase不睡觉书》 意为让你看了也不会想睡觉的教程 :)
目标
前两课教的是入门和文件结构。都没有什么实在的东西。这次我们要来点实在的。我们要做出一个待办列表。这个待办列表有以下特点:
- 可以自动从文本中抽取出这件事情的开始时间
- 可以显示当前距离这件事情的开始时间还有多久,比如:23:40 回家 (还有 6 小时 36 分 15 秒)
- 如果当前时间已经超过了计划时间,则以灰色字体显示任务,并加上删除线
通过这个例子我们可以学到以下知识点
- v-for属性
- v-bind:key属性
- v-on属性
- 在vue中使用bootstrap
- 在vue中使用localStorage
- watch属性
- computed属性
- 在vue中定义私有方法
- webpack自动打包
- v-if, v-else-if, v-else属性
- v-show属性
背景
- vue版本:2.5.16
- 文件结构基于上节课的文件结构: Vue不睡觉教程2 大家可以直接从 https://github.com/alexxiyang/learn-vue 下载源码,下载后使用git checkout lesson2 命令切换到lesson2的源码
注意事项
在说本节课的步骤之前,先提醒大家,该完代码记得用以下命令编译后才能用浏览器看到你的更改
npx webpack
编译后记得要访问的页面文件不是根目录下的index.html。那只是源文件。你需要访问 dist/index.html。
创建TodoList组件
修改App.vue
我们先来构建项目框架。这个项目只有一个组件:TodoList。
将App.vue中之前的 import 引用 修改为 import TodoList from './components/TodoList' 就像这样
import TodoList from './components/TodoList.vue'
然后在template模板代码块中引用它,并在components对象中引用它。修改完的App.vue是这样的:
<template><div id="app"><TodoList/></div>
</template><script>
import TodoList from './components/TodoList.vue'export default {name: 'app',components: {TodoList}
}
</script>
新建TodoList.vue组件
将HelloVue.vue删掉。然后在src/components文件夹下新建TodoList.vue组件,组件内容为
<template><div id="todolist">{{ message }}</div>
</template><script>
export default {name: 'TodoList',data: function() {return {message: '这是一个待办列表'}}
}
</script>
照例使用 npx webpack打包,然后访问 http://learn-vue/dist/index.html 。如果成功,你就可以 看到 “这是一个待办列表” 的字样。
显示任务列表(v-for)
既然是一个待办列表,那么核心的数据对象就应该是一个array。让我们来新建这个array
data: function() {return {taskList: ["7:00 学英语", "10:00 学Vue"]}}
我们来使用v-for来显示它
<template><div id="todolist"><table><thead><th>任务</th></thead><tbody><tr v-for="task in taskList"><td>{{ task }}</td></tr></tbody></table></div>
</template>
显示的效果为:
如果你的task是一个object,你可以使用以下方式来显示它的属性
<tr v-for="task in taskList"><td>{{ task.id }} {{ task.name}}</td>
</tr>
如果你使用的是 visual studio code,那么有可能看到以下错误提示:
Elements in iteration expect to have 'v-bind:key' directives.
这是因为当vue要求当使用v-for来显示列表时,需要使用v-bind:key来标定列表主键,就像这样
<tr v-for="task in taskList" v-bind:key="task.id"><td>{{ task.id }} {{ task.name }}</td></tr>
因为我们的例子过于简单了,每个纪录只是一行字符串,所以可以忽略这个错误提示。在本例中我们不需要理会这个错误提示。但是在实际的项目中,请一定加上:key。
为什么要加上v-bind:key?
以下引用自vue官网:
当 Vue.js 用
v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的track-by="$index"
。这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一
key
属性。理想的key
值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的track-by
,但它的工作方式类似于一个属性,所以你需要用v-bind
来绑定动态值
简而言之就是:vue为了性能考虑,默认复用页面上的dom元素。为了防止你的列表元素不更新,就要用key告诉vue,这些dom元素是不一样的。
添加任务按钮(v-on)
在<table>元素上面添加一个<button>组件,用来增加任务
<button>添加任务</button>
接下来,我们需要用到v-on语法来为按钮添加对click事件的绑定
<button v-on:click="addTask">添加</button>
由此可见,v-on的语法就是 v-on:<事件名>=“js语句或者js方法名”
写好了模板,接下来就是在default对象中增加methods属性,并添加addTaks方法了
methods: {addTask: function(event) {this.taskList.push("新的待办任务");}}
完整的default对象为
export default {name: 'TodoList',data: function() {return {taskList: ["7:00 学英语", "10:00 学Vue"]}},methods: {addTask: function(event) {this.taskList.push("新的待办任务");}}
}
执行效果就是,每次点击添加按钮,就会新增一个任务
美化页面(bootstrap, css-loader, style-loader)
我觉得这样的页面也太丑了,所以我们来为页面加入bootstrap。直接使用原生bootstrap比较麻烦,我们使用bootstrap-vue来为vue项目添加bootstrap:
$ npm i --save bootstrap-vue
然后我们在main.js中写上对BootstrapVue的引用,以及相关css的引用
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'Vue.use(BootstrapVue);
如果你现在执行npx webpack一定会看到如下错误
ERROR in ./node_modules/bootstrap-vue/es/components/alert/alert.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
这是因为你目前还没有为webpack.config.js添加css的loader。所以webpack不认识.css文件。
我们来安装跟css相关的loader
$ npm i --save style-loader css-loader
然后在webpack.config.js的rules节点中编写规则来使用它
rules: [{test: /\.vue$/,loader: 'vue-loader',},{test: /\.css$/,use: ['style-loader','css-loader']}]
现在我们就可以使用npx webpack命令来打包项目了。
打包好后,你再看页面,会有些许的变化,但是变化不大。这是因为我们还没有真正的使用bootstrap。现在我们来为页面做以下美化
- 为todoList根div增加class: container
- 为button按钮增加class: btn btn-primary m-4
- 为table增加class: table m-4
然后再来看看我们的页面:
这下好看多了。
添加任务功能
接下来,我们增加“添加任务”的功能。首先我们要添加一个用来输入任务内容的input输入框。但是直接在button右边添加输入框看起来又很丑。所以我打算从bootstrap的网站上复制一段<button>和<input>都包含在内的布局代码,就像这段
<div class="input-group mb-3"><div class="input-group-prepend"><button class="btn btn-outline-secondary" type="button">Button</button></div><input type="text" class="form-control" placeholder="" aria-label="" aria-describedby="basic-addon1">
</div>
将其改造成我们需要的样子:
- 将<button>元素的文字改为添加并加上v-on:click="addTask"属性
- 将<input>元素的placeholder属性修改为“请输入任务内容”,并加上id="task_content"方便定位。
然后,将之前的
<button type="button" class="btn btn-primary m-4" v-on:click="addTask">添加</button>
替换为我们修改后的代码块
<div class="input-group mb-3"><div class="input-group-prepend"><button class="btn btn-outline-secondary" type="button" v-on:click="addTask">添加</button></div><input type="text" id="task_content" class="form-control" placeholder="请输入任务内容" aria-label="" aria-describedby="basic-addon1"></div>
接下来,我们来修改addTask任务。由于vue将对象和html dom元素进行了双向绑定,原来我们需要用jquery来操作dom元素的大量代码就被修改成了一行代码
this.taskList.push(task_content.value);
加上获取任务内容输入框和清空输入框内容的代码,总共只需要三行代码:
addTask: function(event) {// 获取任务内容let task_content = document.querySelector("#task_content");// 添加任务内容到任务列表中this.taskList.push(task_content.value);// 清空任务内容输入框task_content.value = '';}
我们把之前任务列表中初始化的两个任务删掉
data: function() {return {taskList: []}},
现在你只需要操作taskList对象,页面上的任务列表也会跟着变动。现在你可以试试在任务内容框中输入任务的内容,然后点击添加按钮:
任务存储:localStorage和watch方法
现在有一个问题,那就是你一刷新页面,你新建的任务就消失了。所以我们新建一个store.js来处理任务的存储。store.js利用localstorage来存储任务:
const STORAGE_KEY='todo_list'
export default{fetch(){return JSON.parse(window.localStorage.getItem(STORAGE_KEY)||'[]')},save(items){window.localStorage.setItem(STORAGE_KEY,JSON.stringify(items))}
}
然后,在TodoList.vue中引用 store.js
import Store from './store.js'
现在 data.taskList 就不只是用[]来初始化了,我们要改成从store中获取
data: function() {return {taskList: Store.fetch()}},
现在我要介绍一个全新的属性 watch。该属性的作用是当你改变某个属性的时候可以同时做一些其他的事情。比如现在我们就需要在增加任务的同时将taskList保存到localStorage中。你可以这样写
watch:{taskList:{handler:function(tasks){Store.save(tasks)}}},
注意:watch跟data, methods属性是同级的。
动态解析任务时间(computed)
现在我们要使用computed属性来做这个神奇的功能。当你想在页面上显示经过处理的变量时,你可以使用各种函数,比如 如果我们要将名字中的逗号都换成下划线,然后截取第一个空格之前的文字。我们可能会这么写
name.replace(',', '_').substring(0, name.indexOf(' '));
偶尔写一次还好,要是项目的每个地方都要这么写一遍就太恶心了。所以vue提供了一种属性叫 computed。使用这个属性我们可以定义出“虚拟的”变量,这个变量并不在data中被实际的定义出来,而是通过对实际的变量进行了计算而得出的。在这个例子中我们的需求是:
- 列表要能够自动计算出任务的剩余快完时间,比如:23:40 回家 (还有 6 小时 36 分 15 秒)
- 如果当前时间已经超过了计划时间,则不显示剩余完成时间
此时就需要用到computed属性。使用computed属性可以定义虚拟的变量。这种变量依赖于data中的变量计算得出,并且可以在html中像使用data中的属性一样的使用他们。在我们这个例子中,我们在html模板中使用一个虚拟变量parsedTaskList。
<tr v-for="task in parsedTaskList"><td>{{ task }}</td></tr>
我们在跟watch属性同级的节点下增加computed属性,并在其中增加parsedTaskList属性。我们会在partedTaskList属性中对taskList进行转换,生成新的任务列表
computed: {parsedTaskList: function () {let parsedTaskList = [];const regex = /[0-9]+:[0-9]+/;// 遍历taskListfor (let i=0; i<this.taskList.length; i++) {let task = this.taskList[i];// 解析任务中的计划时间let result = task.match(regex);if (result != null && result.length > 0) {let taskTime = result[0];let thisMoment = moment();let currentDate = thisMoment.format('YYYY-MM-DD');let taskMoment = moment(currentDate + " " + taskTime, 'YYYY-MM-DD HH:mm');if (taskMoment.valueOf() < thisMoment.valueOf()) {parsedTaskList.push(task);continue;}let duration = moment.duration(taskMoment.diff(thisMoment));let durationText = duration.hours() + " 小时 " + duration.minutes() + " 分 " + duration.seconds() + " 秒";// 将剩余时间拼接到任务上parsedTaskList.push(task + "(还有 " + durationText + ")'></span>");}parsedTaskList.push(task);}// 返回新的任务列表return parsedTaskList;}},
抽取剩余时间的具体的过程很简单,大家也不需要现在理解它,因为它并不是这课的核心内容,只需要知道该函数可以实现自动拼接上任务的剩余完成时间就行了。
做到这里我遇到了一个问题,那就是:为了项目结构的简洁,我希望可以把这段代码中由任务字符串转换为带着剩余时间的任务字符串代码抽取到一个私有函数中去。但是在这没有像java中的private关键字可以让我们定义私有函数。
要如何定义私有函数呢?
写在export中的东西意思是要暴露出去的东西,所以只要你的函数写在export中,就相当于是public函数了。要想函数不被暴露出去,只需要将函数块写到export以外就好了。现在我们将转换任务字符串的代码抽取出来,放在 export default { 这行代码之上:
import Store from './store.js'
import * as moment from 'moment';const regex = /[0-9]+:[0-9]+/;
/*** 该函数作用是解析出字符串中的时间,并将其跟当前时间比较,* 计算出还剩多久才会到达计划时间,将剩余时间拼接在字符串后。* 如果当前时间已经过了计划时间,则不对字符串做任何改变* 例子:* 23:40 回家 -> 23:40 回家 (还有 6 小时 36 分 15 秒)*/
const addRemainTime = (task) => {let result = task.match(regex);if (result != null && result.length > 0) {let taskTime = result[0];let thisMoment = moment();let currentDate = thisMoment.format('YYYY-MM-DD');let taskMoment = moment(currentDate + " " + taskTime, 'YYYY-MM-DD HH:mm');if (taskMoment.valueOf() < thisMoment.valueOf()) {return task;}let duration = moment.duration(taskMoment.diff(thisMoment));let durationText = duration.hours() + " 小时 " + duration.minutes() + " 分 " + duration.seconds() + " 秒";return task + " (还有 " + durationText + ")";}return task;
}export default {
这样做了之后,parsedTaskList属性的内容就变成异常简洁了:
computed: {parsedTaskList: function () {let parsedTaskList = [];for (let i=0; i<this.taskList.length; i++) {parsedTaskList.push(addRemainTime(this.taskList[i]));}return parsedTaskList;}},
好了。在刷新页面之前不要忘记运行 npx webpack 来重新打包项目。完成后的效果如下
每次改完代码都要手动打包真的很烦!其实,有一个方法可以让webpack自动跟踪你的改动,并自动打包
Webpack自动打包(watch)
通过带上 --watch参数,比如
npx webpack --watch
或者,在webpack.config.js中增加watch相关属性可以让webpack自动的检测当前项目是否有变动,如果有变动webpack会自动打包。以下我采取在 webpack.config.js 中增加watch相关属性的方式来打开watch模式:
watch属性默认是关闭的。所以我们需要在webpack.config.js中加上watch属性:
watch: true,
加上watch的设置
watchOptions: {aggregateTimeout: 3000, // 编译的超时时间,单位:毫秒poll: 30 // 扫描项目的间隔时间,单位:秒},
改动后的webpack.config.js文件内容是
var path = require('path');
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'development',entry: './src/main.js',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'},watch: true,watchOptions: {aggregateTimeout: 3000, // 编译的超时时间,单位:毫秒poll: 30 // 扫描项目的间隔时间,单位:秒},module: {rules: [{test: /\.vue$/,loader: 'vue-loader',},{test: /\.css$/,use: ['style-loader','css-loader']}]},plugins: [new VueLoaderPlugin(),// 以下是HtmlWebpackPlugin的配置new HtmlWebpackPlugin({template: 'index.html',filename: './index.html',hash: true})]
};
设置完watch属性后,我们就可以使用 npx webpack 来启动自动打包了
npx webpack
启动后命令行工具处于监听状态,一有代码改动就会自动打包
vagrant@homestead:~/Code/learn-vue$ npx webpackwebpack is watching the files…Hash: a38478266809719e3c32
Version: webpack 4.12.1
Time: 3706ms
Built at: 2018-10-03 17:43:01Asset Size Chunks Chunk Namesbundle.js 1.85 MiB main [emitted] main
./index.html 273 bytes [emitted]
[./node_modules/moment/locale sync recursive ^\.\/.*$] ./node_modules/moment/locale sync ^\.\/.*$ 2.91 KiB {main} [optional] [built]
[./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/App.vue?vue&type=script&lang=js] ./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js 136 bytes {main} [built]
[./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/App.vue?vue&type=template&id=7ba5bd90] ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=7ba5bd90 259 bytes {main} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 489 bytes {main} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {main} [built]
[./src/App.vue] 1.02 KiB {main} [built]
[./src/App.vue?vue&type=script&lang=js] 246 bytes {main} [built]
[./src/App.vue?vue&type=template&id=7ba5bd90] 194 bytes {main} [built]
[./src/main.js] 269 bytes {main} [built]+ 316 hidden modules
Child html-webpack-plugin for "index.html":1 asset[./node_modules/html-webpack-plugin/lib/loader.js!./index.html] 399 bytes {0} [built][./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 489 bytes {0} [built][./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]+ 1 hidden module
watch的副作用
watch的副作用就是cpu占用率会提高,我的macbook一运行 watch模式风扇的声音就变大,导致我一直没敢用这个模式。
为已完成任务增加删除线(v-if)
剩下最后一个需求了,那就是如果当前时间超过了计划时间,则任务需要变灰并增加删除线。我们使用v-if来实现这个功能
通过在dom元素中增加 v-if="表达式" 我们可以灵活的控制该dom元素的显示与否。就像这样:
<div v-if="type === 'A'">A
</div>
<div v-else-if="type === 'B'">B
</div>
<div v-else-if="type === 'C'">C
</div>
<div v-else>Not A/B/C
</div>
如果v-if中的表达式结果为true,则该元素会被渲染出来,反之则该元素不会被渲染。在这个例子中还用到了 v-else-if 和 v-else,有着丰富编程经验的你肯定一下就看懂了它们的含义,所以在此我就不解释了。
跟v-show的区别
还有一个跟v-if用法很像的属性叫 v-show。同样也是定义一个表达式,根据表达式的返回结果来决定该元素是否出现。不同的是v-if的表达式返回结果为false,则该元素完全不出现在html中,而v-show不管表达式结果怎样都会渲染该元素,只是当表达式为false时为元素增加 display:none的样式而已。
好,现在我们就来根据任务是否已经完成来显示不同的任务样式。检验的条件是任务字符串中是否出现“还有 xx 小时 xx 分 xx 秒” 字样。
先把html模板改成
<tr v-for="task in parsedTaskList"><td><span v-if="isDone(task)" style="color:gray;text-decoration:line-through;">{{ task }}</span><span v-else >{{ task }}</span></td></tr>
可以看到在v-if中我们使用了一个函数isDone来判断该任务是否完成。所以我们需要在method属性中增加isDone方法(以下方法的定义使用了ES2015语法)
isDone (task) {let result = task.match(/还有\s[0-9]+\s小时\s[0-9]+\s分\s[0-9]+\s秒/);return result == null || result.length == 0;}
不使用ES2015语法的版本是
isDone: function (task) {let result = task.match(/还有\s[0-9]+\s小时\s[0-9]+\s分\s[0-9]+\s秒/);return result == null || result.length == 0;}
如果你使用的是Chrome,那么就可以放心大胆的使用ES2015语法咯。
完成后,打包,刷新页面,效果如下
这样就完成了本节课的所有内容了。
method和computed有什么区别呢?
这是我学习vue时最大的疑问,我觉得method和computed用法完全就没区别!其实他们的区别在于:computed是带缓存的,如果被依赖的变量不发生变化,则下次调用computed时不会重新计算结果。但是method则是每次调用都会重新运行以得出实时的结果。
后记
其实vue的官网教程已经写的非常棒了!没见过写的这么棒的官网文档,强力赞一个!所以原本不打算在更新新的文章了,由于有网友希望我继续更新,所以我才继续又写了一篇。但是写文太费时间了。所以未来应该不会再更新了,感谢大家的支持!这是vue官网中文文档的学习传送门:https://cn.vuejs.org/v2/guide/
Vue 不睡觉教程3 - 来点实在的:自动计算剩余时间的任务列表相关推荐
- Vue 不睡觉教程2 - 洋气的文件结构
名字灵感来自我的书<HBase不睡觉书> 意为让你看了也不会想睡觉的教程 :) 目标 书接上回,上回那个例子实在太土了.实际开发中我们不可能把整个网站的js和html全写到一个页面上.所以 ...
- Vue 不睡觉教程1-从最土开始
名字灵感来自我的书<HBase不睡觉书> 意为让你看了也不会想睡觉的教程 :) 目标 最近在学习vue的过程中发现网上的vue教程总有些不同的问题,有的教程上来只说语法,有的教程上来就用v ...
- Vue.js入门教程-组件注册
一.组件创建 1.1 创建步骤 创建Vue的组件都有三个基本步骤是 [①创建组件构造器.②注册组件和③使用组件]. 1.2 基本示例 比如,我们创建一个Button组件. // 1. 创建一个组件构造 ...
- Vue.js入学教程
Vue.js是什么 Vue.js 是用于构建交互式的 Web 界面的库. Vue.js 提供了 MVVM 数据绑定和一个可组合的组件系统,具有简单.灵活的 API. Vue.js(类似于view)是一 ...
- vue整合视频流教程
vue整合视频流教程 业务需求:客户需要将监控接入到自己的app中,在市面上很多监控是不分享视频流地址的,推荐方案选择海康,大华等品牌的监控,获取到监控地址整合到自己的app中. 在开发前需要普及的一 ...
- 热烈庆祝《Vue.js 实战教程 V2.x(一)基础篇》上线了!
热烈庆祝<Vue.js 实战教程 V2.x(一)基础篇>上线了! 课程简介 课程地址:https://edu.csdn.net/course/detail/25641 机构名称:大华软件学 ...
- Vue.js入门教程(适合初学者)
Vue.js入门教程 Vue官网网址:Vue.js 中文网 Vue.js Vue.js是渐进式JavaScript 框架,是一套构建用户界面的渐进式框架.也可以说Vue.js 是一个用来构建网页界面的 ...
- 手把手Vue前端开发教程
手把手Vue前端开发教程 本文将手把手地教你如何使用Vue进行前端开发. 简介 Vue.js 是一款渐进式 JavaScript 框架,易于上手.Vue 可以被用来开发单页面应用 (SPA) 以及简单 ...
- HTML之Vue框架计算属性computed的简单使用实现自动计算总分和平均分
HTML之Vue框架计算属性computed的简单使用实现自动计算总分和平均分 预计效果 代码 结果展示 预计效果 页面输入数学.物理.英语分数,自动计算出总分和平均分,并展示到界面,如下图所示 代码 ...
- Vue.js 系列教程 3:Vue-cli,生命周期钩子
原文:intro-to-vue-3-vue-cli-lifecycle-hooks 译者:nzbin 这是 JavaScript 框架 Vue.js 五篇教程的第三部分.在这一部分,我们将学习 Vue ...
最新文章
- 生物信息学就是从统计和CS的community里借鉴合适的方法
- 清华团队研发,首款国产电力电子仿真软件来啦~已捐赠哈工大、海工大、清华使用!...
- 中相对路径与绝对路径的写法_相对路径和绝对路径?简洁易懂解释+实例
- 使用简单工厂模式demo
- Duplicate entry ‘XXX‘ for key
- C++关键字--volatile
- tempdb(转载)
- Uva 140 Bandwidth
- Qt5 QtQuick系列----QtQuick的Secne Graph剖析(1)
- Flink CDC 2.2 正式发布,新增四种数据源,支持动态加表,提供增量快照框架
- 柯桥在PPT中如何制作翻书动画?
- 搜狐新闻表情出现怪异现象
- .NET的.snk文件使用方法和DLL加密
- IEEE文献高级检索
- AutoMapper Project To OrderBy Skip Take 正确写法
- 安卓虚拟摄像头_iPhone 的第四颗摄像头位置,为什么给了激光雷达?
- SEO中的十大关键词竞争度分析方法
- BAP:PPP 带宽分配协议 BACP:PPP 带宽分配控制协议--网络大典
- 神经网络中epoch、batch、batchsize
- Coredump-X: C++:std::terminate
热门文章
- Resolver error Error Downloading VS Code Server failed - please install either curl or wget on the
- .NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记
- 对象存储s3cmd使用手册
- java输出数组中所有数字排列的集合
- sql语句中select……as的用法
- React基础之事件机制
- 编程语言【JAVA】编程(4)---摇色子
- linux命令中ll和ls的区别
- php 数字转人民币,php数字转人民币金额大写
- 打开我的计算机我的文档不见,我电脑桌面上的我的文档不见了,是什么原因造成我的文档不见了呢?是? 爱问知识人...