[02] Vue.js
1. 简述#
Vue 是一套用于构建用户界面的渐进式框架。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
- M (Model):模型,包括数据和一些基本操作
- V (View):视图,页面渲染结果
- VM (View-Model):模型与视图间的双向操作(无需开发人员干涉)
在 MVVM 之前,开发人员从后端获取需要的数据模型,然后要通过 DOM 操作 Model 渲染到 View 中。而后当用户操作视图,我们还需要通过 DOM 获取 View 中的数据,然后同步到 Model 中。而 MVVM 中的 VM 要做的事情就是把 DOM 操作完全封装起来,开发人员不用再关心 Model 和 View 之间是如何互相影响的:
- 只要我们 Model 发生了改变,View 上自然就会表现出来;
- 当用户修改了 View,Model 中的数据也会跟着改变。
把开发人员从繁琐的 DOM 操作中解放出来,把关注点放在如何操作 Model 上。
简单示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!-- id 标识 vue 作用的范围 -->
{{message}} <!-- {{}}:插值表达式,绑定 vue 中的 data 数据 -->
</div>
<script>
let app = new Vue({ // 创建一个 vue 对象
el: '#app', // 绑定 vue 作用的范围
data: { // 定义页面中显示的 model 数据
message: 'Hello! Vue!'
}
})
</script>
</body>
</html>
这就是声明式渲染:Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。这里的核心思想就是没有繁琐的 DOM 操作,例如 jQuery 中,我们需要先找到 div 节点,获取到 DOM 对象,然后进行一系列的节点操作。
在 VS Code 中创建代码片段:文件 → 首选项 → 用户代码片段 → 新建全局代码片段/文件夹代码片段
{
"vuehtml": {
"scope": "html",
"prefix": "vuehtml",
"body": [
"<!DOCTYPE html>",
"<html lang=\"en\">",
"<head>",
" <meta charset=\"UTF-8\">",
" <title>Document</title>",
"</head>",
"<script src=\"vue.min.js\"></script>",
"<body>",
" <!-- id标识vue作用的范围 -->",
" <div id=\"app\">",
" <!-- {{}} 插值表达式,绑定vue中的data数据 -->",
" {{message}}",
" </div>",
" <script>",
" // 创建一个vue对象",
" let app = new Vue({",
" el: '#app', // 绑定vue作用的范围",
" data: { // 定义页面中显示的模型数据",
" $1",
" }",
" })",
" </script>",
"</body>",
"</html>",
],
"description": "vue基础代码片段"
}
}
补充:光标放在首行开头,按住 alt
+ shift
, 鼠标选中尾行开头,即可批量插入到选中数行的打头位置;插入完成后,直接按键盘 end
键,光标就会移动到行尾,此时即可进行行尾的批量插入。
2. 基本语法#
2.1 基本数据渲染和指令#
你看到的 v-bind
特性被称为“指令”,指令带有前缀 v-
。除了使用插值表达式 {{}}
进行数据渲染,也可以使用 v-bind
指令,它的简写的形式就是一个冒号 :
。
<script src="vue.min.js"></script>
<body>
<div id="app">
<!--
如果要将模型数据绑定在html属性中,则使用v-bind指令。此时
title中显示的是模型数据。它把普通属性变成可被渲染的属性。
<h1 :title="title">{{message}}</h1> // v-bind 可省
-->
<h1 v-bind:title="title">{{message}}</h1>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello! Vue!',
title: '标题'
}
})
</script>
</body>
2.2 双向数据绑定#
使用 v-model
进行双向数据绑定。
<body>
<div id="app">
<!-- v-bind 只能进行单向的数据渲染 -->
<label >搜索:<input type="text" v-bind:value="search.keyword"></label>
<!-- v-model 可以进行双向的数据绑定 -->
<label >搜索:<input type="text" v-model="search.keyword"></label>
<p>{{search.keyword}}</p>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
search: {
keyword: '刘佳琦'
}
}
})
</script>
</body>
2.3 绑定事件#
使用 v-on
进行事件处理,v-on:click
表示处理鼠标点击事件,事件调用的方法定义在 vue 对象声明的 methods
节点中。
<body>
<div id="app">
<label >搜索:<input type="text" v-model="search.keyword"></label>
<!-- v-on: 用于绑定事件,简写:@ -->
<!-- <p><button v-on:click="searchInfo()">搜索</button></p> -->
<p><button @click="searchInfo()">搜索</button></p>
<p>{{search.keyword}}</p>
<p><a v-bind:href="result.site">{{result.title}}</a></p>
</div>
<script>
let app = new Vue({
el: '#app',
data: { // 定义页面中显示的模型数据
search: {
keyword: '白世珠'
},
result: {}
},
methods: {
searchInfo() {
let p1 = new Promise((resolve, reject)=>{
$.ajax({
url: 'mock/data1.json',
type: 'get',
success(data) {
console.log(data);
resolve(data);
},
error(error) {
reject(error);
}
})
})
p1.then(data=>{
data.items.forEach(element => {
if (element.title === this.search.keyword) {
this.result = element;
}
});
}).catch(error=>{
console.log(error)
})
}
}
});
</script>
</body>
在 vue 中我们只需要专注于业务逻辑的处理,不需要考虑 DOM 的操作。
2.4 修饰符#
修饰符(Modifiers) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault()
,即阻止事件原本的默认行为。
<body>
<div id="app">
<form action="#" v-on:submit.prevent="doSubmit()">
<label for="name">姓名:</label>
<input id="name" type="text" v-model="user.name">
<input type="submit" value="提交" />
</form>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
user: {}
},
methods: {
doSubmit() {
console.log('提交表单');
}
}
})
</script>
</body>
2.5 条件渲染#
条件指令有俩个:v-if
和 v-show
。
<body>
<!-- id标识vue作用的范围 -->
<div id="app">
<input type="checkbox" name="" id="" v-model="agree"/>同意{{agree}}
<p v-if="agree">同意→下一步</p>
<!-- <p v-if="!agree">查看协议 。。。</p> -->
<p v-else>查看协议 。。。</p>
</div>
<!-- id标识vue作用的范围 -->
<div id="app2">
<input type="checkbox" name="" id="" v-model="agree"/>同意{{agree}}
<p v-show="agree">同意→下一步</p>
<p v-show="!agree">查看协议 。。。</p>
</div>
<script>
let app = new Vue({ // 创建一个vue对象
el: '#app', // 绑定vue作用的范围
data: { // 定义页面中显示的模型数据
agree: false
}
})
let app2 = new Vue({ // 创建一个vue对象
el: '#app2', // 绑定vue作用的范围
data: { // 定义页面中显示的模型数据
agree: false
}
})
</script>
</body>
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的,如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。- 相比之下,
v-show
就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 - 一般来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。
2.6 列表渲染#
<body>
<div id="app">
<!-- a. 列表渲染 -->
<ul>
<li v-for="n in 5">{{n}}</li>
</ul>
<!-- b. 表格渲染 -->
<table>
<!-- 如果想获取索引,则使用一个自定义变量,注意,该变量必须放在后面 -->
<tr v-for="(user, xyz) in userList">
<td>{{xyz+1}}</td>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</table>
<!-- c. 表单渲染 -->
<form action="">
<p v-for="(value, key) in user">
<label>{{key}}: <input type="text" v-model="value"></label>
</p>
</form>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
nums: [1, 2, 3, 4, 5],
userList: [
{id: 1, name: 'fangziyan', age: 30},
{id: 2, name: 'shujie', age: 28},
{id: 3, name: 'baishizhu', age: 33}
],
user: {
id: 4,
name: 'liuxinyou',
age: 40
}
}
})
</script>
</body>
2.7 计算属性#
1. 差值模板中使用 JS 表达式
<p>反转消息: {{ message.split('').reverse().join('') }}</p>
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
2. 使用计算属性&使用方法
<body>
<!-- id标识vue作用的范围 -->
<div id="app">
<!-- 列表渲染 -->
<ul>
<li v-for="n in computedNums">{{n}}</li>
</ul>
<hr/>
<p>原文:{{message}}</p>
<p>computed 翻转1:{{reversedMessage}}</p>
<p>computed 翻转2:{{reversedMessage}}</p>
<p>methods 翻转1:{{methodReverse()}}</p>
<p>methods 翻转2:{{methodReverse()}}</p>
</div>
<script>
let app = new Vue({ // 创建一个vue对象
el: '#app', // 绑定vue作用的范围
data: { // 定义页面中显示的模型数据
nums: [1, 2, 3, 4, 5],
message: "Hello World !"
},
computed: {
computedNums() {
return this.nums.filter(num=>{
return num % 2 === 0;
})
},
reversedMessage() {
console.log("计算属性反转"); // 走缓存
return this.message.split('').reverse().join('');
}
},
methods: {
methodReverse() {
console.log("方法反转"); // 调一次算一次
return this.message.split('').reverse().join('');
}
}
})
</script>
</body>
看起来计算属性和方法能完成相同的功能,那么他们有什么区别呢?
- 计算属性基于缓存:在相关依赖发生改变时它们才会重新求值。
- 方法将总会再次执行
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的计算!如果你不希望有缓存,请用方法来替代。
2.8 侦听属性#
当你有一些数据需要随着其它数据变动而变动时,可以使用侦听属性。
<body>
<div id="app">
<p><input type="text" v-model="firstName"></p>
<p><input type="text" v-model="lastName"></p>
<!-- 计算属性方式 -->
<!-- {{getFullName}} -->
<!-- 监听方式 -->
{{fullName}}
</div>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '尼古拉斯',
lastName: '赵四',
fullName: '尼古拉斯 赵四'
},
computed: {
getFullName() {
return this.firstName + " " + this.lastName;
}
},
watch: {
firstName(val) {
this.fullName = val + " " + this.lastName
},
lastName(val) {
this.fullName = this.firstName + " " + val
}
}
})
</script>
</body>
实际上,上一个例子是对 watch 的滥用;我们完全可以用计算属性更高效的完成同样的操作。
<body>
<div id="app">
<p><label>
Quiz:
<input type="text" v-model="question">
<button v-on:click="getAnswer()">回答</button>
</label></p>
<p>{{answer}}</p>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
question: '',
answer: 'say~'
},
methods: {
getAnswer() {
console.log(this.question)
this.answer = 'let me think think'
let vm = this // 这个this是vue对象
$.ajax({
url: 'https://yesno.wtf/api',
type: 'get',
success(data) {
console.log(data);
// this.answer = data.answer; // 这个this是$
vm.answer = data.answer;
},
error(error) {
console.log(error);
}
})
}
},
watch: {
// 如果 question 发生改变,这个函数就会运行
question(newQuiz, oldQuiz) {
this.getAnswer()
}
}
})
</script>
</body>
当需要在数据变化时执行异步或开销较大的操作时,watch 方式是最有用的。
2.9 过滤器#
2.9.1 局部过滤器#
<body>
<!-- id标识vue作用的范围 -->
<div id="app">
<table>
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<!--<td>{{user.gender === 1 ? 'male' : 'female'}}</td>-->
<!-- | 管道符号:表示使用后面的过滤器处理前面的数据 -->
<td>{{user.gender|genderFilter}}</td>
</tr>
</table>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
nums: [1, 2, 3, 4, 5],
userList: [
{id: 1, name: 'fangziyan', age: 30, gender: 0},
{id: 2, name: 'shujie', age: 28, gender: 0},
{id: 3, name: 'baishizhu', age: 33, gender: 0},
{id: 4, name: 'liuyuan', age: 16, gender: 1}
]
},
// filters 定义局部过滤器,只可以在当前vue实例中使用
filters: {
genderFilter(gender) {
return gender === 1 ? 'male' : 'female'
}
}
})
</script>
</body>
2.9.2 全局过滤器#
<body>
<!-- id标识vue作用的范围 -->
<div id="app">
<table>
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name|caseFilter}}</td>
<td>{{user.age}}</td>
<td>{{user.gender|genderFilter}}</td>
</tr>
</table>
</div>
<!--把全局过滤器放在外部文件当中,如果要使用,就直接引入-->
<script src="filters/caseFilter.js"></script>
<script>
/*
// 在创建 Vue 实例之前全局定义过滤器
Vue.filter('caseFilter', value=>{
return value.charAt(0).toUpperCase() + value.slice(1)
})
*/
let app = new Vue({
el: '#app',
data: {
nums: [1, 2, 3, 4, 5],
userList: [
{id: 1, name: 'fangziyan', age: 30, gender: 0},
{id: 2, name: 'shujie', age: 28, gender: 0},
{id: 3, name: 'baishizhu', age: 33, gender: 0},
{id: 4, name: 'liuyuan', age: 16, gender: 1}
]
},
// filters 定义局部过滤器,只可以在当前vue实例中使用
filters: {
genderFilter(gender) {
return gender === 1 ? 'male' : 'female'
}
}
})
</script>
</body>
3. 组件#
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
组件其实也是一个 Vue 实例,因此它在定义时也会接收:data、methods、生命周期函数等。
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有 el 属性;
- 但是组件渲染需要 html 模板,所以增加了 template 属性,值就是 HTML 模板;
- 全局组件定义完毕,任何 vue 实例都可以直接在 HTML 中通过组件名称来使用组件了;
- 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝;
3.1 全局组件#
1. 定义全局组件:components/Navbar.js
// 定义全局组件
Vue.component('Navbar', {
template: '<ul><li>首页</li><li>学员管理</li><li>讲师管理</li></ul>'
})
2. 使用组件
<script src="vue.min.js"></script>
<script src="components/Navbar.js"></script>
<div id="app">
<Navbar></Navbar>
</div>
<script>
var app = new Vue({
el: '#app'
})
</script>
3.2 局部组件#
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着 Vue 的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。
1. 定义局部组件
var app = new Vue({
el: '#app',
// 定义局部组件,这里可以定义多个局部组件
components: {
// 组件的名字
'Navbar': {
// 组件的内容
template: '<ul><li>首页</li><li>学员管理</li></ul>'
}
}
})
2. 使用组件
<div id="app">
<Navbar></Navbar>
</div>
components 就是当前 Vue 对象子组件集合:
- 其 key 就是子组件名称
- 其值就是组件对象名
- 效果与刚才的全局注册是类似的,不同的是,这个 counter 组件只能在当前的 Vue 实例中使用
4. 自定义指令#
4.1 局部指令#
除了默认设置的核心指令(v-model
和 v-show
),Vue 也允许注册自定义指令。
<script>
var app = new Vue({
el: '#app',
// 局部指令的定义
directives: {
// 定义一个局部自定义指令 `v-focus`
focus: {
// 当被绑定的元素插入到 DOM 中时
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
}
</script>
<input type="text" v-focus>
4.2 全局指令#
定义全局指令:directives/focus.js
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时
inserted (el) {
// 聚焦元素
el.focus()
}
})
5. 实例的生命周期#
每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等等。Vue 为生命周期中的每个状态都设置了钩子函数(监听函数)。每当 Vue 实例处于不同的生命周期时,对应的函数就会被触发调用。
<div id="app">
<h1 id="h1">{{message}}</h1>
<p><button @click="update()">更新</button></p>
</div>
分析生命周期相关方法的执行时机
let app = new Vue({
el: '#app', // 绑定vue作用的范围
data: { // 定义页面中显示的模型数据
message: '亭有枇杷树'
},
methods: {
show() {
console.log('show...');
},
update() {
console.log('update...');
this.message = '吾妻死之年所手植也'
}
},
// --------- 初始化的4个钩子方法 ---------
beforeCreate() { // vue实例的属性方法均未初始化
console.log('-----> beforeCreate...');
// console.log(this.message); // undefined
// this.show(); // TypeError: this.show is not a function
},
created() { // vue实例的属性方法均已初始化
// debugger
console.log('-----> created...');
console.log(this.message); // 亭有枇杷树
this.show(); // show...
},
beforeMount () { // 模板已经在内存中编辑完成了,尚未被渲染到页面中
console.log('-----> beforeMount...');
let text = document.getElementById("h1").innerText;
console.log("模板:" + this.message); // 模板:亭有枇杷树
console.log("页面:" + text); // 页面:{{message}}
},
mounted () { // 内存中的模板已经渲染到页面,用户已经可以看见内容
// debugger
console.log('-----> mount...');
let text = document.getElementById("h1").innerText;
console.log("模板:" + this.message); // 模板:亭有枇杷树
console.log("页面:" + text); // 页面:亭有枇杷树
},
// --------- 更新的2个钩子方法 ---------
beforeUpdate () { // 内存中的数据已更新,但是页面尚未被渲染
console.log('-----> beforeUpdate...');
let text = document.getElementById("h1").innerText;
console.log("模板:" + this.message); // 模板:吾妻死之年所手植也
console.log("页面:" + text); // 页面:亭有枇杷树
},
updated () { // 内存中的数据已更新,并且页面已经被渲染
console.log('-----> updated...');
let text = document.getElementById("h1").innerText;
console.log("模板:" + this.message); // 模板:吾妻死之年所手植也
console.log("页面:" + text); // 页面:吾妻死之年所手植也
}
})
6. Vue 的路由#
Vue.js 路由允许我们通过不同的 URL 访问不同的内容。通过 Vue.js 可以实现多视图的单页 Web 应用(Single Page web Application,SPA)。
1. Vue.js 路由需要载入 vue-router 库(必须按如下顺序引入)
<script src="vue.min.js"></script>
<script src="vue-router.min.js"></script>
2. 编写 html
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航 -->
<!-- 通过传入 `to` 属性指定链接 -->
<!-- <router-link> 默认会被渲染成一个<a>标签 -->
<router-link to="/">首页</router-link>
<router-link to="/student">会员管理</router-link>
<router-link to="/teacher">讲师管理</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
3. 编写 js
// 1. 定义(路由)组件,可以从其他文件 import 进来
const Welcome = { template: '<div>欢迎</div>' }
const Student = { template: '<div>student list</div>' }
const Teacher = { template: '<div>teacher list</div>' }
// 2. 定义路由,每个路由应该映射一个组件
const routes = [
{ path: '/', redirect: '/welcome' }, // 设置默认指向的路径
{ path: '/welcome', component: Welcome },
{ path: '/student', component: Student },
{ path: '/teacher', component: Teacher }
]
// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes // ← 缩写,相当于 routes: routes
})
// 4. 创建和挂载根实例,从而让整个应用都有路由功能
const app = new Vue({
el: '#app',
router
})
7. Axios#
axios 是独立于 vue 的一个项目,基于 Promise 用于浏览器和 node.js 的 HTTP 客户端。在浏览器中可以帮助我们完成 ajax 请求的发送,在 node.js 中可以向远程接口发送请求。
1. 页面引入 vue 和 axios
<script src="vue.min.js"></script>
<script src="axios.min.js"></script>
2. 创建数据文件:data.json
{
"success": true,
"code": 20000,
"msg": "成功",
"data": {
"items": [
{"name": "刘心悠", "age": 31},
{"name": "金敏喜", "age": 33},
{"name": "绫濑遥", "age": 30}
]
}
}
3. 使用 axios 发送 Ajax 请求
let app = new Vue({ // 固定的结构
el: '#app',
data: {
userList: [] // 声明一个空数组
},
created () {
this.getUserList() // 调用methods中定义的方法
},
methods: {
getUserList() { // 查询所有用户数据(使用 axios 发送 Ajax 请求)
// axios.提交方式("请求接口路径").then(箭头函数).catch(箭头函数)
axios.get("data.json")
.then(resp => { // 请求成功执行then方法
this.userList = resp.data.data.items
console.log(this.userList);
})
.catch(error => { // 请求失败执行catch方法
console.log(error);
})
}
}
})
4. 将得到的响应数据显示在页面上
<div id="app">
<div v-for="user in userList">
{{user.name}} - {{user.age}}
</div>
</div>
8. Vscode 创建模板#
Alt+Shift+F 代码格式化
{
"生成 vue 模板": {
"prefix": "vue",
"body": [
"<template>",
"<div></div>",
"</template>",
"",
"<script>",
"// 这里可以导入其他文件(e.g. 组件/工具js/第三方插件js/json文件/图片...)",
"// 例如:import <组件名称> from '<组件路径>';",
"",
"export default {",
"// import 引入的组件需要注入到对象中才能使用",
"components: {},",
"props: {},",
"data() {",
"// 这里存放数据",
"return {",
"",
"};",
"},",
"// 计算属性, 类似于 data 概念",
"computed: {},",
"// 监控 data 中的数据变化",
"watch: {},",
"// 方法集合",
"methods: {",
"",
"},",
"// 生命周期 - 创建完成(可以访问当前 this 实例)",
"created() {",
"",
"},",
"// 生命周期 - 挂载完成(可以访问 DOM 元素)",
"mounted() {",
"",
"},",
"beforeCreate() {}, // 生命周期 - 创建之前",
"beforeMount() {}, // 生命周期 - 挂载之前",
"beforeUpdate() {}, // 生命周期 - 更新之前",
"updated() {}, // 生命周期 - 更新之后",
"beforeDestroy() {}, // 生命周期 - 销毁之前",
"destroyed() {}, // 生命周期 - 销毁完成",
"activated() {}, // 如果页面有 keep-alive 缓存功能,这个函数会触发",
"}",
"</script>",
"",
"<style scoped>",
"</style>"
],
"description": "生成 vue 模板"
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-03-01 39-KMP 执行过程