vue基础(一)

箭头函数#

非常简洁的语法,使用箭头函数比普通函数少些动词,如:function或return。

Copy Highlighter-hljs
() => { ... } // 零个参数用 () 表示。
x => { ... } // 一个参数可以省略 ()。
(x, y) => { ... } // 多参数不能省略 ()。
如果只有一个return,{}可以省略。
//createElement就是h,一个形参,没有具体意义,可以随意改变书写
render:(function(createElement){
return createElement(App);
})
说明:render是一个方法,自带一个形参createElement,这个参数也是一个方法,是用来创建vue 节点的,也就是html模板的,然后渲染(render)到指定的节点上

普通函数中的this

this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this指向的是window;当函数被作为某个对象的方法调用时,this就等于那个对象

Copy Highlighter-hljs
var name = "the window";
var obj = {
name:"my object",
getNameFunc:function(){
return function(){
return this.name;
}
}
}
console.log(obj.getNameFun()()); //the window

匿名函数的执行环境是全局的,而且this只在函数内部起作用。此时的this.name在匿名函数中找不到,所以就从全局中找。

Copy Highlighter-hljs
var obj = {
func:function(){
console.log(this);
},
say:function(){
var that = this;
setTimeout(function(){
console.log(that);
});
}
}
obj.func(); //obj
obj.say(); //obj
var obj = {
func:function(){
console.log(this);
},
say:function(){
setTimeout(()=>{
console.log(this);
});
}
}
obj.func(); //obj
obj.say(); //obj

箭头函数的this定义:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。

Copy Highlighter-hljs
我们试着把函数的执行对象放在window下看看:
function test(){
console.log(this);
}
test() //window对象

vue-cli构建项目如何运行#

使用vue-cli构建项目后, 在项目目录中,运行命令 npm run dev运行项目,运行成功后会返回一个地址路径。在地址栏直接输入该路径可以看到效果。

解决vue npm ERR! missing script: dev#

今天在运行Vue项目时,在运行npm run dev时报错如下图:

打开package.js文件夹,发现文件夹里的scripts有dev如下图:

可就是好不到,什么原因呢?最后一看路径不对,vue init webpack my-project时,自己又建立一个文件夹,取不到对应的package.js中的dev.

cd 到对应文件夹,重新运行npm run dev 就OK了。

还有一种情况,打开的是当前文件夹,但是文件夹package.js里的scripts确实没有dev,

输入vue init webpack 将package.json中丢失的:

Copy Highlighter-hljs
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"

重新下载过来,然后在npm intall 安装依赖,最后 npm run dev即可

也适用于解决start, build丢失

创建一个vue项目#

Copy Highlighter-hljs
1、vue创建
vue create hello-world

vue中this.$el 和 this.$ref#

vue组件中this和$el指向#

Copy Highlighter-hljs
.
.
.
mounted(){
console.log(this,'this');
console.log(this.$el,'$el')
}
打印:
VueComponent{.......} "this"
<div class="el-select">....</div> "$el"

结论:

this指向组件的实例。

$el指向当前组件的DOM元素。

this.$el是在mounted中才会出现的

Vue 解决通过this.$refs来获取DOM或者组件报错问题#

1. 关于this.$refs的使用场景

如果ref属性加在普通元素上,那么this.$refs.name则指向该DOM元素

Copy Highlighter-hljs
<p ref="p">hello</p>
<!-- this.$refs.p 指向该DOM元素 -->

如果ref属性加在组件上,那么this.$refs.name指向该组件实例

Copy Highlighter-hljs
<child-component ref="child"></child-component>
<!-- this.$refs.child 指向该组件 -->

2. 为什么有时候通过this.$refs.name来获取会报错?

一个比较常见的场景:在一个弹窗打开的时候立刻通过this.$refs来获取内容就会出现xxx is undefined的错误

因为ref本身是作为渲染结果被创建的,在渲染的时候是不能访问的,因为他们还不存在!

如果此时代码是需要这样来写代码,那么你可以在DOM渲染完毕后再进行获取

Copy Highlighter-hljs
this.$nextTick(() => {
this.$refs.name... //DOM渲染完毕后就能正常获取了
})

this.$nextTick(function(){})#

Vue 异步执行 DOM 更新,监视所有数据改变,一次性更新DOM。如果需要拿到更新后dom中的数据 则需要通过 Vue.nextTick(callback):在DOM更新后,执行某个操作(属于DOM操作)。

this.$nextTick 一般在created钩子函数中使用,有人会问,在created 和 mounted 钩子函数中使用有什么区别呢?

这就要回到vue的生命周期了:

Copy Highlighter-hljs
beforeCreated : 初始化Vue,此时 data 和 el 并未被创建
created: data被初始化但是el并未被创建
beforeMounted: data 和 el 都被创建,但是还未被挂载
mounted: data 和 el 都被创建,并且被挂载

在created 钩子函数中,数据初始化了但是页面并没有更新,而 $nextTick 是异步行为,在数据变化的同时,页面也会同步;

在mounted钩子函数中,由于页面已经加载完了,所以不需要异步操作。但是写了也不会错。。

下面看一个例子,进一步说明 $nextTick 的作用:

Copy Highlighter-hljs
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
<div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
<div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
Copy Highlighter-hljs
new Vue({
el: '.app',
data: {
msg: 'Hello Vue.',
msg1: '',
msg2: '',
msg3: ''
},
methods: {
changeMsg() {
this.msg = "Hello world."
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
})

点击前:

Copy Highlighter-hljs
Hello Vue.
change the Message.

点击后:

Copy Highlighter-hljs
Hello world.
Message got outside $nextTick:Hello Vue.
Message got outside $nextTick:Hello world.
Message got outside $nextTick:Hello Vue.

最后nextTick的应用场景有哪些呢?

1.在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中

2.在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。

nextTick方法的回调会在dom更新后再执行,因此可以和一些dom操作搭配一起用,如 ref。
非常好用,可以解决很多疑难杂症。

Copy Highlighter-hljs
场景:
你用ref获得一个输入框,用v-model绑定。
在某个方法里改变绑定的值,在这个方法里用ref去获取dom并取值,你会发现dom的值并没有改变。
因为此时vue的方法,还没去触发dom的改变。
因此你可以把获取dom值的操作放在vm.$nextTick的回调里,就可以了。

Vue-Vue文本渲染三种方法 {{}}、v-html、v-text#

{{ }}
将元素当成纯文本输出

v-html
v-html会将元素当成HTML标签解析后输出

v-text
v-text会将元素当成纯文本输出

Copy Highlighter-hljs
<div id="app">
<!-- {}}/v-text不能解析html元素,只会照样输出 -->
<p>{{hello}}</p>
<p v-text = 'hello'></p>
<p v-html = 'hello'></p>
</div>

事件修饰符#

.stop 阻止冒泡,调用 event.stopPropagation()

.prevent 阻止默认行为,调用 event.preventDefault()

.capture 添加事件侦听器时使用事件捕获模式

.self 只当事件在该元素本身(比如不是子元素)触发时,才会触发事件

.once 事件只触发一次

.self只执行直接作用在该元素身上的事件,所以它相当于忽略了 其他元素的冒泡或者捕获事件。但是这种忽略只局限于自身。
所以,为了避免一些会被冒泡事件影响,加上修饰符.self是很有用的。

指令#

样式处理 -class和style#

  • 使用方式:v-bind:class="expression" or :class="expression"
  • 表达式的类型:字符串、数组、对象(重点) 语法:
Copy Highlighter-hljs
<!-- 1 -->
<div v-bind:class="{ active: true }"></div> ===> 解析后
<div class="active"></div>
<!-- 2 -->
<div :class="['active', 'text-danger']"></div> ===>解析后
<div class="active text-danger"></div>
<!-- 3 -->
<div v-bind:class="[{ active: true }, errorClass]"></div> ===>解析后
<div class="active text-danger"></div>
--- style ---
<!-- 1 -->
<div v-bind:style="{ color: activeColor, 'font-size': fontSize + 'px' }"></div>
<!-- 2 将多个 样式对象 应用到一个元素上-->
<!-- baseStyles 和 overridingStyles 都是data中定义的对象 -->
<div v-bind:style="[baseStyles, overridingStyles]"></div>

v-if 和 v-show#

  • 条件渲染
  • v-if:根据表达式的值的真假条件,销毁或重建元素
  • v-show:根据表达式之真假值,切换元素的 display CSS 属性
Copy Highlighter-hljs
<p v-show="isShow">这个元素展示出来了吗???</p>
<p v-if="isShow">这个元素,在HTML结构中吗???</p>

提升性能:v-pre#

  • 说明:vue会跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache标签。跳过大量没有指令的节点会加快编译。
Copy Highlighter-hljs
<span v-pre>{{ this will not be compiled }}</span>

提升性能:v-once#

  • 说明:vue只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
Copy Highlighter-hljs
<span v-once>This will never change: {{msg}}</span>

过滤器 filter#

  • 作用:文本数据格式化
  • 过滤器可以用在两个地方:{{}}和 v-bind 表达式
  • 两种过滤器:1 全局过滤器 2 局部过滤器

全局过滤器

  • 说明:通过全局方式创建的过滤器,在任何一个vue实例中都可以使用
  • 注意:使用全局过滤器的时候,需要先创建全局过滤器,再创建Vue实例
  • 显示的内容由过滤器的返回值决定
Copy Highlighter-hljs
Vue.filter('filterName', function (value) {
// value 表示要过滤的内容
})
Copy Highlighter-hljs
<div>{{ dateStr | date }}</div>
<div>{{ dateStr | date('YYYY-MM-DD hh:mm:ss') }}</div>
<script>
Vue.filter('date', function(value, format) {
// value 要过滤的字符串内容,比如:dateStr
// format 过滤器的参数,比如:'YYYY-MM-DD hh:mm:ss'
})
</script>

局部过滤器

  • 说明:局部过滤器是在某一个vue实例的内容创建的,只在当前实例中起作用
Copy Highlighter-hljs
{
data: {},
// 通过 filters 属性创建局部过滤器
// 注意:此处为 filters
filters: {
filterName: function(value, format) {}
}
}

监视数据变化 - watch#

  • 概述:watch是一个对象,键是需要观察的表达式,值是对应回调函数
  • 作用:当表达式的值发生变化后,会调用对应的回调函数完成响应的监视操作
  • VUE $watch
Copy Highlighter-hljs
<div id="app">
<input type="text" name="name2" v-model="b.age">
</div>
new Vue({
data: { a: 1, b: { age: 10 } },
watch: {
a: function(val, oldVal) {
// val 表示当前值
// oldVal 表示旧值
console.log('当前值为:' + val, '旧值为:' + oldVal)
},
// 监听对象属性的变化
b: {
handler: function (val, oldVal) { /* ... */ },
// deep : true表示是否监听对象内部属性值的变化
deep: true
},
// 只监视b对象中age属性的变化
'b.age': function (val, oldVal) {
},
}
})

计算属性 - computed#

  • 说明:计算属性是基于它们的依赖进行缓存的,只有在它的依赖发生改变时才会重新求值
  • 注意:Mustache语法({{}})中不要放入太多的逻辑,否则会让模板过重、难以理解和维护
  • 注意:computed中的属性不能与data中的属性同名,否则会报错
  • Vue computed属性原理
Copy Highlighter-hljs
<div id="app">
<input type="text" name="name" v-model="firstname"><br>
<input type="text" name="name1" v-model="lastname"><br>
<input type="text" name="name2" v-model="fullname">
</div>
var vm = new Vue({
el: '#app',
data: {
firstname: 'jack',
lastname: 'rose'
},
computed: {
fullname() {
return this.firstname + '.' + this.lastname
}
}
})

computed和watch的区别

computed:不支持异步,当computed内有异步操作时无效,无法监听数据的变化

watch:支持异步,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。这是和computed最大的区别

vue生命周期(一)#

  • 生命周期钩子函数的定义:从组件被创建,到组件挂载到页面上运行,再到页面关闭组件被卸载,这三个阶段总是伴随着组件各种各样的事件,这些事件,统称为组件的生命周期函数!
  • 注意:Vue在执行过程中会自动调用生命周期钩子函数,我们只需要提供这些钩子函数即可
  • 注意:钩子函数的名称都是Vue中规定好的!

钩子函数 - beforeCreate()

  • 说明:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
  • 注意:此时,无法获取 data中的数据、methods中的方法

钩子函数 - created()

  • 注意:这是一个常用的生命周期,可以调用methods中的方法、改变data中的数据

  • vue实例生命周期 - 参考1

  • vue实例生命周期 参考2

  • 实例已经创建完成之后被调用。

  • 使用场景:发送请求获取数据

    Copy Highlighter-hljs
    tip:
    通常我们可以在这里对实例进行预处理。
    也有一些童鞋喜欢在这里发ajax请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的。
    因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个页面发请求。
    建议在组件路由勾子beforeRouteEnter中来完成。

钩子函数 - beforeMounted()

  • 说明:在挂载开始之前被调用:相关的 render 函数首次被调用。

钩子函数 - mounted()

  • 说明:此时,vue实例已经挂载到页面中,可以获取到el中的DOM元素,进行DOM操作

    Copy Highlighter-hljs
    tip:
    1.在这个周期内,对data的改变可以生效。但是要进下一轮的dom更新,dom上的数据才会更新。
    2.这个周期可以获取 dom。
    3.beforeRouteEnter的next的勾子比mounted触发还要靠后
    4.指令的生效在mounted周期之前

钩子函数 - beforeUpdated()

  • 说明:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
  • 注意:此处获取的数据是更新后的数据,但是获取页面中的DOM元素是更新之前的

钩子函数 - updated()

  • 说明:组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。

钩子函数 - beforeDestroy()

  • 说明:实例销毁之前调用。在这一步,实例仍然完全可用。
  • 使用场景:实例销毁之前,执行清理任务,比如:清除定时器等

钩子函数 - destroyed()

  • 说明:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

说了这么多来点实在的,大佬一般如何在代码中合理的利用生命周期来构建代码呢?

Copy Highlighter-hljs
beforeCreate () {
// 进行初始化事件,进行数据的观测,可以看到在created的时候数据已经和data属性进行绑定(放在data中的属性当值发生改变的同时,视图也会改变)。注意:此时还是没有el选项
},
components: {},
data () {
return {
show: false,
msg: "hello word",
mg: "你好世界"
}
},
watch: {
},
methods: {
destory () {
// 调用销毁
this.$destroy();
}
},
created () {
// 在这一阶段发生的事情还是比较多的。首先,会判断对象是否有el选项:如果有的话就继续向下编译,如果没有el选项,则停止编译,也就意味着停止了生命周期,直到在该vue实例上调用vm.$mount(el)
},
beforeMount () {
// &emsp;可以看到此时是给vue实例对象添加$el成员,并且替换掉挂在的DOM元素。因为在之前console中打印的结果可以看到beforeMount之前el上还是undefined。
},
mounted () {
this.intervalID = setInterval(() => {
console.log("++++++-------++++");
this.show = !this.show;
}, 1000);
// 在mounted之前p中还是通过{{message}}进行占位的,因为此时还没有挂在到页面上,还是JavaScript中的虚拟DOM形式存在的。在mounted之后可以看到h1中的内容发生了变化。
},
beforeUpdate () {
// 当vue发现data中的数据发生了改变,会触发对应组件的重新渲染,先后调用beforeUpdate和updated钩子函数。
},
updated () {
// 在beforeUpdate可以监听到data的变化,但是view层没有被重新渲染,view层的数据没有变化。等到updated的时候,view层才被重新渲染,数据更新。
},
beforeDestory () {
clearInterval(this.intervalID);
// &emsp;beforeDestroy钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用,可以用于清除定时器
},
destory () {
//调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
},

全局路由钩子#

作用于所有路由切换,一般在main.js里面定义

router.beforeEach

Copy Highlighter-hljs
示例
router.beforeEach((to, from, next) => {
console.log('路由全局勾子:beforeEach -- 有next方法')
next()
})

一般在这个勾子的回调中,对路由进行拦截。
比如,未登录的用户,直接进入了需要登录才可见的页面,那么可以用next(false)来拦截,使其跳回原页面。
值得注意的是,如果没有调用next方法,那么页面将卡在那。

Copy Highlighter-hljs
next的四种用法
1.next() 跳入下一个页面
2.next('/path') 改变路由的跳转方向,使其跳到另一个路由
3.next(false) 返回原来的页面
4.next((vm)=>{}) 仅在beforeRouteEnter中可用,vm是组件实例。

router.afterEach

Copy Highlighter-hljs
示例
router.afterEach((to, from) => {
console.log('路由全局勾子:afterEach --- 没有next方法')
})

在所有路由跳转结束的时候调用,和beforeEach是类似的,但是它没有next方法

组件路由勾子#

和全局勾子不同的是,它仅仅作用于某个组件,一般在.vue文件中去定义。

beforeRouteEnter

Copy Highlighter-hljs
示例
beforeRouteEnter (to, from, next) {
console.log(this) //undefined,不能用this来获取vue实例
console.log('组件路由勾子:beforeRouteEnter')
next(vm => {
console.log(vm) //vm为vue的实例
console.log('组件路由勾子beforeRouteEnter的next')
})
}

这个是一个很不同的勾子。因为beforeRouterEnter在组件创建之前调用,所以它无法直接用this来访问组件实例。

为了弥补这一点,vue-router开发人员,给他的next方法加了特技,可以传一个回调,回调的第一个参数即是组件实例。
一般我们可以利用这点,对实例上的数据进行修改,调用实例上的方法。

我们可以在这个方法去请求数据,在数据获取到之后,再调用next就能保证你进页面的时候,数据已经获取到了。没错,这里next有阻塞的效果。你没调用的话,就会一直卡在那

Copy Highlighter-hljs
tip:
next(vm=>{console.log('next') })
这个里面的代码是很晚执行的,在组件mounted周期之后。没错,这是一个坑。你要注意。
beforeRouteEnter的代码时很早执行的,在组件beforeCreate之前;
但是next里面回调的执行,很晚,在mounted之后,可以说是目前我找到的,离dom渲染最近的一个周期。

beforeRouteLeave

Copy Highlighter-hljs
beforeRouteLeave (to, from, next) {
console.log(this) //可以访问vue实例
console.log('组件路由勾子:beforeRouteLeave')
next()
},

在离开路由时调用。可以用this来访问组件实例。但是next中不能传回调。

beforeRouteUpdate

这个方法是vue-router2.2版本加上的。因为原来的版本中,如果一个在两个子路由之间跳转,是不触发beforeRouteLeave的。这会导致某些重置操作,没地方触发。在之前,我们都是用watch $route来hack的。但是通过这个勾子,我们有了更好的方式。

vue生命周期(二)#

那么进入某个路由对应的组件的时候,我们会触发哪些类型的周期呢?

  1. 根实例的加载相关的生命周期(beforeCreate、created、beforeMount、mounted)
  2. 组件实例的加载相关的生命周期(beforeCreate、created、beforeMount、mounted)
  3. 全局路由勾子(router.beforeEach)
  4. 组件路由勾子(beforeRouteEnter)
  5. 组件路由勾子的next里的回调(beforeRouteEnter)
  6. 指令的周期(bind,inserted)
  7. nextTick方法的回调

接下来,让我们用vue-cli简单改造后的项目,做一个测试,看看各个声明周期的触发顺序是怎样的

main.js:

Copy Highlighter-hljs
router.beforeEach((to, from, next) => {
console.log('路由全局勾子:beforeEach')
next()
})
router.afterEach((to, from) => {
console.log('路由全局勾子:afterEach')
})
new Vue({
beforeCreate () {
console.log('根组件:beforeCreate')
},
created () {
console.log('根组件:created')
},
beforeMount () {
console.log('根组件:beforeMount')
},
mounted () {
console.log('根组件:mounted')
}
el: '#app',
router,
template: '<App/>',
components: { App }
})

test.vue

Copy Highlighter-hljs
<template>
<h1 v-ooo @click = "$router.push('/')">test</h1>
</template>
<script>
export default {
beforeRouteEnter (to, from, next) {
console.log('组件路由勾子:beforeRouteEnter')
next(vm => {
console.log('组件路由勾子beforeRouteEnter的next')
})
},
beforeCreate () {
console.log('组件:beforeCreate')
},
created () {
this.$nextTick(() => {
console.log('nextTick')
})
console.log('组件:created')
},
beforeMount () {
console.log('组件:beforeMount')
},
mounted () {
console.log('组件:mounted')
},
directives: {
ooo: {
bind (el, binding, vnode) {
console.log('指令binding')
},
inserted (el, binding, vnode) {
console.log('指令inserted')
}
}
}
}
</script>

接下来,直接进入test.vue对应的路由。在控制台,我们看到如下的输出

我们看到执行的顺序为

  1. 路由勾子 (beforeEach、beforeRouteEnter、afterEach)
  2. 根组件 (beforeCreate、created、beforeMount)
  3. 组件 (beforeCreate、created、beforeMount)
  4. 指令 (bind、inserted)
  5. 组件 mounted
  6. 根组件 mounted
  7. beforeRouteEnter的next的回调
  8. nextTick

结论#

路由勾子执行周期非常早,甚至在根实例的渲染之前

具体的顺序 router.beforeEach > beforeRouteEnter > router.afterEach

Copy Highlighter-hljs
tip:在进行路由拦截的时候要避免使用实例内部的方法或属性。
在开发项目时候,我们脑门一拍把,具体拦截的程序,写在了根实例的方法上了,到beforeEach去调用。
结果导致整个拦截的周期,推迟到实例渲染的之后。
因此对于一些路由组件的beforeRouteEnter里的请求并无法拦截,页面看上去好像已经拦截下来了。
实际上请求依然发了出去,beforeRouteEnter内的函数依然执行了。

指令的绑定在组件mounted之前,组件的beforeMount之后

不得不提的, beforeRouteEnter的next勾子

beforeRouteEnter的执行顺序是如此靠前,而其中next的回调勾子的函数,执行则非常靠后,在mounted之后!!

我们通常是在beforeRouteEnter中加载一些首屏用数据,待数据收到后,再调用next勾子,通过回调的参数vm将数据绑定到实例上。
因此,请注意next的勾子是非常靠后的。

posted @   caibaotimes  阅读(227)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
CONTENTS