随笔 - 65  文章 - 0 评论 - 0 阅读 - 28752
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

Ajax属于客户端网络技术,属于js范畴。

一 数据的传输格式json

  说明 语法 总结
json

json指的是类似于javascript对象的一种数据格式

与json类似的还有xml,yaml,ini,text,protobuf。

作用是在不同的系统平台,或不同编程语言之间传递数据

1
2
3
4
5
6
7
8
9
10
11
{
  "name":"小明",
  "age": 40,
  "fav":["code","eat","swim","read"],
  "son":{
    "name":"小小明",
    "age":10
  }
}
// json数据的数组格式(这个格式只能用于网络传输,不会保存到json文件的):
["name":"小明","age":40,"fav":["code","eat","swim","read"],"son":{"name":"小小明","age":10}]}

  

1. json文件的后缀是json
2. json文件一般保存一个单一的json数据对象
3. json数据的属性不能是方法或者undefined,属性值只能:数值(整数、浮点数、布尔值)、字符串、对象和数组
4. json数据只使用双引号、每一个属性成员之间使用逗号隔开,并且最后一个成员没有逗号。
{
"name":"小明",
"age":40,
"fav":["code","eat","swim","read"],
"son":{
"name":"小小明",
"age":10
}
}

二 json数据的数据转换

javascript提供了一个JSON对象来操作json数据的数据转换。

方法参数返回值描述示例
stringify json对象 字符串 json对象转成字符串
复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
    // js原生对象
    var js_obj = {
      name:"小明",
      age:40,
      fav:["code","eat","swim",'read',],
      son:{
        name:'小小明',
        age:10,
      }
    };
    // json对象转换成字符串格式的json数据
    var json_str = JSON.stringify(js_obj);
    console.log( "json_str=", json_str );

    // 字符串格式的json数据转换成json对象
    data_str = `{"name":"小明","age":40,"fav":["code","eat","swim","read"],"son":{"name":"小小明","age":10}}`
    var json_obj = JSON.parse(data_str);
    console.log( json_obj );
    console.log( json_obj.name );
    </script>
</head>
<body>

</body>
</html>
js中提供的json数据转换方法
复制代码

代码执行结果:

parse 字符串 json对象 字符串格式的json数据转成json对象

三 ajax

1 相关说明

ajax:Async Javascript And Xml 译作:异步js和xml传输数据技术。现为异步js和json传输数据技术。

作用: ajax可以让js代替浏览器向服务端程序发送http请求,与服务端进行数据通信,在用户不知道的情况下操作数据和信息,从而实现页面局部刷新数据/无刷新更新数据

开发中ajax是很常用的技术,主要用于操作服务端提供的数据接口,从而实现网站的前后端分离。技术原理是实例化js的全局对象XMLHttpRequest,使用此对象提供的内置方法就可以与服务端进行http数据通信。

数据接口也叫api接口,表示服务端提供操作数据/功能的url地址给客户端使用。

前后端分离

 

2 基本使用

ajax的使用必须与服务端程序配合使用,但是开发中我们对于ajax请求的数据,不仅仅可以是自己写的服务端代码,也可以是别人写好的数据接口进行调用。以下为别人写好的数据接口:

接口地址
天气接口 http://wthrcdn.etouch.cn/weather_mini?city=城市名称
音乐接口搜索 http://msearchcdn.kugou.com/api/v3/search/song?keyword=歌曲标题

 

原生js提供了不同的方式,允许前端开发者调用ajax。编写代码获取接口提供的数据

方式一:基于XMLHttpRequest实现ajax1.0版本

有以下5个步骤

1. 创建ajax对象 xhr =new XMLHttpRequest()

2. 打开网络请求 xhr.open(method,url)

3. 发送网络请求 xhr.send()

4. 监听http通信状态 xhr.onreadystatechange

5. 判断xhr执行状态已经执行完成 xhr.readyState

 

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
    .data-table{
        border-collapse: collapse;
        width: 800px;
        display: none;
    }
    .data-table td, .data-table th{
        font-weight: normal;
        border:1px solid red;
        text-align: center;
        width: 200px;
    }
    </style>
</head>
<body>
<input type="text" name="city" value="北京" placeholder="请输入一个国内的城市名称"><button>获取天气</button>
<table class="data-table">
    <tr>
        <th>日期</th>
        <th>类型</th>
        <th>风力</th>
        <th>温度</th>
    </tr>
</table>
</body>
<script>
    var btn = document.querySelector("button")
    var city = document.querySelector('input[name="city"]')
    let data = document.querySelector(".data-table")
    let old_conent = data.innerHTML // 原来的内容

    btn.addEventListener("click", get_weather)

    function get_weather(){
        // 原生js的ajax: 基于XMLHttpRequest实现ajax1.0版本,有5个步骤
        // 1. 创建ajax对象
        var xhr = new XMLHttpRequest()
        // console.log(xhr.readyState)  // 0
        // 2. 打开网络请求
        // 参数1:method,http请求,默认XMLHttpRequest只实现了get与post请求
        // 参数2:url,请求的资源路径,可以携带查询字符串
        xhr.open("get", `http://wthrcdn.etouch.cn/weather_mini?city=${city.value}`)
        // console.log(xhr.readyState)  // 1
        // 3. 发送网络请求
        // 参数1:可选参数,如果是post请求,则填写请求体
        //       请求体必须按查询字符串格式编写,属性=值&属性=值&属性=值.....
        xhr.send()
        // 4. 监听http通信状态
        xhr.onreadystatechange = ()=>{
            // 4.1 判断ajax的状态
            /**
             * readyState属性表示ajax的使用状态
             * 0 表示刚创建ajax对象,还没有创建http网络请求
             * 1 表示ajax对象已经创建http网络请求,但是还没有发送请求
             * 2 表示ajax已经发送了http请求,但是服务端还没有接收到请求
             * 3 表示服务端已经接收到客户端的http请求,但是没有处理完成
             * 4 表示服务端已经处理完成客户端的http请求,并把处理结果返回给客户端了
             */
            // 5. 判断xhr执行状态已经执行完成,并且http响应状态码是200才获取服务端的数据
            if(xhr.readyState === 4){
                if(xhr.status === 200){
                    // console.log(xhr.response)     // 获取原生的响应数据[例如图片,音频等]
                    // console.log(xhr.responseText) // 获取纯文本格式数据
                    // console.log(xhr.responseXML)  // 获取xml格式的数据
                    let response = JSON.parse(xhr.response)
                    console.log(response)
                    data.style.display = "block"
                    data.innerHTML = old_conent
                    console.log(response.data.forecast)
                    for (let info of response.data.forecast) {
                         data.innerHTML+=`<tr>
                            <td>${info.date}</td>
                            <td>${info.type}</td>
                            <td>${info.fengxiang} ${info.fengli.replace(/<!\[CDATA\[(.*?)\]\]>/,"$1")}</td>
                            <td>${info.low} ~ ${info.high}</td>
                            </tr>`
                    }
                }
            }

        }
    }



</script>
</html>
基于XMLHttpRequest实现ajax1.0
复制代码

 

方式二:使用fetch全局方法实现ajax2.0,内置本质上就是XMLHttpRequest与Promise(ES6,es2015)来封装的一个函数

1

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
    .data-table{
        border-collapse: collapse;
        width: 800px;
        display: none;
    }
    .data-table td, .data-table th{
        font-weight: normal;
        border:1px solid red;
        text-align: center;
        width: 200px;
    }
    </style>
</head>
<body>
<input type="text" name="city" value="北京" placeholder="请输入一个国内的城市名称"><button>获取天气</button>
<table class="data-table">
    <tr>
        <th>日期</th>
        <th>类型</th>
        <th>风力</th>
        <th>温度</th>
    </tr>
</table>
</body>
<script>

    var btn = document.querySelector("button")
    var city = document.querySelector('input[name="city"]')
    let data = document.querySelector(".data-table")
    let old_conent = data.innerHTML // 原来的内容

    btn.addEventListener("click", get_weather)

    function get_weather(){
        // ajax2.0
        // 基于fetch全局方法来实现
        // fetch使用方式1:
        // 参数1:Request对象
        // fetch使用方式2:
        // 参数1:字符串格式的url地址,
        // 参数2:Request对象
        // 返回值:Promise回调对象
        fetch(`http://wthrcdn.etouch.cn/weather_mini?city=${city.value}`, {
            method: "GET",
            mode: 'cors',
        }).then(response => { // 此处可以简写成 then(res=>res.json())
            // 如果要接收来自服务端的json数据,则先要使用return response.json()来处理  //then对前边的返回值进行处理
            // 如果要接收来自服务端的纯文本数据,则先要使用return response.text()来处理
            // 如果要接收来自服务端的二进制数据,则先要使用return response.blob()来处理
            return response.json()
        })
            .then(response => {
                // 显示表格
                data.style.display = "block"
                data.innerHTML = old_conent
                for (let info of response.data.forecast) {
                    data.innerHTML += `
                    <tr>
                        <td>${info.date}</td>
                        <td>${info.type}</td>
                        <td>${info.fengxiang} ${info.fengli.replace(/<!\[CDATA\[(.*?)\]\]>/, "$1")}</td>
                        <td>${info.low} ~ ${info.high}</td>
                    </tr>
                `
                }

            }).catch((err) => {
            // 如果ajax请求失败,或者then方法中的匿名函数中有代码异常,则自动执行catch方法
            console.log("错误:", err);
            // 如果要转换处理来自服务端的异常错误
            console.log("来自服务端的错误:", err.response);
        });
        console.log("hello!!!!")
    }


</script>
</html>
使用fetch全局方法实现ajax2.0
复制代码

 

2服务端代码提供接口:

复制代码
from aiohttp import web
import aiohttp_cors

# 路由对象
# 路由[router],是一种用于绑定url地址与服务端应用处理程序[python里面的函数/类方法]之间的映射关系。
# /a1  ---> a函数
# /a2  ---> b函数
# 路由的格式类似hash映射(字典结构)
# {
#     "/a1": a函数,
#     "/a2": b函数,
# }
# 在实际开发中,我们所说的路由,往往也表示在服务端中实现路由关系的类,也叫路由类。

routes = web.RouteTableDef()

@routes.get("/")
async def get_index(request):
    print("hello!, 用户访问进来了!")
    data = [
        {"name": "xiaoming", "age": 17},
        {"name": "xiaoming", "age": 18},
        {"name": "xiaoming", "age": 19},
    ]
    # 返回json数据
    return web.json_response(data)


@routes.post("/")
async def post_index(request):

    # 接受客户端上传过来的请求体数据,并使用json.loads()转换成字典
    data = await request.json()
    print("data=", data)
    # 返回json数据
    return web.json_response(data)


@routes.put("/{name}")  # {name} 变量,当用户通过地址栏使用get请求时,会自动/斜杠后面的内容作为值赋值给name变量
async def num(request):
    # 获取url地址的一部分,这个叫路由参数
    name = request.match_info.get('name', "Anonymous")
    # 返回json数据
    return web.json_response({"name": name, "age": 19})


class Application(web.Application):
    def __init__(self, routes):
        """
        :param routes: url和服务端处理程序之间的绑定关系, 这种绑定关系,我们一般称之为路由,当然,在服务端开发中,我们也会经常把具有路由功能功能的类/对象,称之为"路由类"或"路由对象"
        """
        print(routes._items) # 路由列表
        # 先让官方的aiohttp的web框架先进行初始化
        super(Application, self).__init__()
        # 注册路由信息到web框架中,routes是一个list列表
        self.add_routes(routes)

        # 这里内部完成了一个for循环,把routes里面每一个路由信息都了遍历,添加一个返回值用于实现跨域资源共享[CORS]。
        cors = aiohttp_cors.setup(self, defaults={
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True,
                expose_headers="*",
                allow_headers="*",
            )
        })

        # Configure CORS on all routes.
        for route in list(self.router.routes()):
            cors.add(route)


if __name__ == '__main__':
    web.run_app(Application(routes),host="0.0.0.0",port=8000)
服务端http://0.0.0.0:8000
复制代码

 

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button class="btn1">点击获取服务端的数据1</button>
    <button class="btn2">点击获取服务端的数据2</button>
    <button class="btn3">点击获取服务端的数据3</button>
    <script>
    var btn1 = document.querySelector(".btn1")
    var btn2 = document.querySelector(".btn2")
    var btn3 = document.querySelector(".btn3")
    btn1.onclick = function(){
        fetch("http://127.0.0.1:8000/",{
            method: "get",
            mode: "cors",
        }).then(response=>{
            console.log(response.text());
        })
    }

    btn2.onclick = function(){
        fetch("http://127.0.0.1:8000/",{
            method: "post",
            mode: "cors",
            headers:{
                "Content-Type": "application/json",
            },
            body: JSON.stringify({'name': 'abc'}) // 发送json数据到服务端
        }).then(response=>{
            console.log(response.text());
        })
    }

    btn3.onclick = function(){
        fetch("http://127.0.0.1:8000/xiaohong",{
            method: "put",
            mode: "cors",
        }).then(response=>{
            console.log(response.json());
        })
    }

    </script>
</body>
</html>
ajax请求服务端提供的数据
复制代码

 

3 同源策略

 

同源策略是浏览器为了保护用户信息在服务端的安全设置的一种安全机制

所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器客户端页面地址)之间比较,是否协议域名(IP)端口相同。不同源的客户端脚本[javascript]在没有服务器明确授权的情况下,没有权限读写服务端的信息。ajax本质上是javascript,是运行在浏览器中的脚本语言,所以会被受到浏览器的同源策略所限制。

如下图示例:

前端地址:http://www.fuguang.cn/index.html是否同源原因
http://www.fuguang.cn/user/login.html 协议、域名、端口相同
http://www.fuguang.cn/about.html 协议、域名、端口相同
https://www.fuguang.cn/user/login.html 协议不同 ( https和http ),端口不同
http:/www.fuguang.cn:5000/user/login.html 端口不同( 5000和80)
http://bbs.fuguang.cn/user/login.html 域名不同 ( bbs和www )

代码示例:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="text" name="music" value="月亮之上">
    <button class="btn">搜索歌曲</button>
    <script>
    let btn = document.querySelector(".btn")
    let music = document.querySelector("input[name=music]")
    btn.onclick = function(){
        fetch(`http://msearchcdn.kugou.com/api/v3/search/song?keyword=${music.value}`,{
            method: "get",
        }).then(response=>response.json()).then(response=>{
            console.log(response)
        }).catch(error=>{
            console.log(error)
        })
    }
    </script>
</body>
</html>
同源策略针对ajax的拦截
复制代码

效果

上面的错误不一定是同源策略的问题,也有可能是因为以下原因:
1. 服务端代码报错,或者服务端没有正确的返回授权
2. 客户端请求的服务端的请求报文出错[错误的url地址,错误的请求方法,错误的请求头]
3. 客户端携带了不明来历的cookie信息

4 ajax跨域(跨源)方案

跨域的解决方案一般以下三种:

1 cors方案,W3C维护的一个跨域资源共享的协议。这个协议要求服务端通过响应头的方式告知浏览器,当前跨域访问的客户端是否属于可信任客户端。cors方案的使用前提是服务端已经支持了cors。 

 

 

服务端代理(Proxy Server),自己搭建一个可控的代理服务端(自己的服务端项目),因为同源策略只存在于浏览器,所以我们让客户端直接请求代理服务端,然后代理服务端接收客户端请求以后,执行代码请求目标服务端那边获取到客户端需要的数据,最终由代理服务端把数据返回给客户端。

复制代码
import json

from aiohttp import web, ClientSession

import aiohttp_cors

routes = web.RouteTableDef()

@routes.get("/")
async def get_index(request):
    # 返回json数据
    music = request.query.get("music", None)
    if music is None:
        return web.json_response({"errno": -1, "errmsg": "参数有误!"})
    # 发起请求,搜索音乐
    # http://msearchcdn.kugou.com/api/v3/search/song?keyword=
    url = f"http://msearchcdn.kugou.com/api/v3/search/song?keyword={music}"
    async with ClientSession() as session:
        async with session.get(url) as response:
            print("status:{}".format(response.status))
            if response.status == 200:
                data = await response.text()
                data = json.loads(data)
                print(data)
                return web.json_response(data)
            else:
                return web.json_response({"errno": -1, "errmsg": "搜索失败!"})

class Application(web.Application):
    def __init__(self, routes):
        """
        :param routes: url和服务端处理程序之间的绑定关系, 这种绑定关系,我们一般称之为路由,当然,在服务端开发中,我们也会经常把具有路由功能功能的类/对象,称之为"路由类"或"路由对象"
        """
        print(routes._items) # 路由列表
        # 先让官方的aiohttp的web框架先进行初始化
        super(Application, self).__init__()
        # 注册路由信息到web框架中,routes是一个list列表
        self.add_routes(routes)

        # 这里内部完成了一个for循环,把routes里面每一个路由信息都了遍历,添加一个返回值用于实现跨域资源共享[CORS]。
        cors = aiohttp_cors.setup(self, defaults={
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True,
                expose_headers="*",
                allow_headers="*",
            )
        })

        # Configure CORS on all routes.
        for route in list(self.router.routes()):
            cors.add(route)


if __name__ == '__main__':
    web.run_app(Application(routes),host="0.0.0.0",port=8000)
server.py
复制代码

 

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="text" name="music" value="月亮之上">
    <button class="btn">搜索歌曲</button>
    <script>
    let btn = document.querySelector(".btn")
    let music = document.querySelector("input[name=music]")
    btn.onclick = function(){
        fetch(`http://127.0.0.1:8000/?music=${music.value}`,{
            method: "get",
        }).then(response=>response.json()).then(response=>{
            console.log(response)
        }).catch(error=>{
            console.log(error)
        })
    }
    </script>
</body>
</html>
client.html
复制代码

jsonp方案,原则上来说jsonp不是ajax技术,而是利用了HTML中的某些默认能跨域的标签来实现的。script,link,src属性,iframe,…客户端先预定义一个js函数。然后服务端生成js代码,代码里面把数据作为函数的参数,在客户端请求的时候返回。

 

 

 

复制代码
import json
from aiohttp import web

routes = web.RouteTableDef()


@routes.get("/search")
async def get_js(request):
    # 接受客户端上传过来的请求体数据,并使用json.loads()转换成字典
    username = request.query.get("username")
    func = request.query.get("func", "callback")
    # 假设根据username到数据库中查询数据
    data = {
        "id": 1,
        "username": username,
        "age": 20
    }

    return web.Response(text=f"{func}({json.dumps(data)})", content_type="text/javascript")


class Application(web.Application):
    def __init__(self, routes):
        """
        :param routes: url和服务端处理程序之间的绑定关系, 这种绑定关系,我们一般称之为路由,当然,在服务端开发中,我们也会经常把具有路由功能功能的类/对象,称之为"路由类"或"路由对象"
        """
        print(routes._items) # 路由列表
        # 先让官方的aiohttp的web框架先进行初始化
        super(Application, self).__init__()
        # 注册路由信息到web框架中,routes是一个list列表
        self.add_routes(routes)

if __name__ == '__main__':
    web.run_app(Application(routes),host="0.0.0.0",port=8000)
server.py
复制代码

 

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="text" name="username" value="小明">
    <button class="btn">发送数据</button>
    <script>
    var btn = document.querySelector('.btn')
    var user = document.querySelector('input[name="username"]')
    btn.onclick = function(){
        // <script src="http://127.0.0.1:8000/1.js?username=xiaoming&func=callback"><//script>
        let script = document.createElement("script")  // 定义引用外部脚本的URI,这可以用来代替直接在文档中嵌入脚本
        script.src= `http://127.0.0.1:8000/search?username=${user.value}&func=callback`
        document.head.append(script)  //返回当前文档中的 <head> 元素
    }
    function callback(data){
        console.log(data);
    }

    </script>
</body>
</html>
client.html
复制代码

四 异步编程

js默认是单线程运行的。为了实现javascript的异步编程,常用的方式有定时器、ajax、Promise、Generator、async/await、服务器推技术(WebSocket,Worker,EventSource)。

1 promise

Promise 是一个代理对象(代理一个值),被代理的值在 Promise 对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知值的代理。它让你能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。

 

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。
如果一个 promise 已经被兑现或被拒绝,那么我们也可以说它处于 已敲定(settled) 状态。
一个经常跟 promise 一起使用的术语:已决议(resolved),它表示 promise 已经处于已敲定状态,或者为了匹配另一个 promise 的状态被“锁定”了。

示例

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
    var status = false;
    var response = new Promise((resolve, reject)=>{
      // 下载/读取文件等耗时操作,容易阻塞的代码
      // ....
      if(status === "false"){
        // 写耗时的代码,结果放在resolve作为参数提供给调用者
        resolve("成功的结果");
      }else{
        reject("错误了!!!你玩大了!");
      }
    });
    console.log("1")
    response.then(response=>{
        console.log(response);
    }).catch(error=>{
        console.log(error)
    })
    console.log("2")
    </script>
</body>
</html>
promise.html 
复制代码

2 生成器Generator

复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<script>
    // 生成器函数
    function* gen(){
        // yield 暂停
        yield 100
        yield 200
        yield 300
        yield 400
        return 500
    }
    // 生成器函数的返回值就是生成器对象
    let g = gen()
    console.log(g)
    console.log(g.next())
    console.log(g.next())
    console.log(g.next())
    console.log(g.next())
    console.log(g.next()) // 可以获取retrun返回值

    // for循环无法获取生成器的return返回值,仅能回去yield的返回值
    for(let item of gen()){
        console.log(item)
    }

</script>
</body>
</html>
生成器.html
复制代码

3 协程异步async/await

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // async 标记当前函数为异步
    async function request() {
        try {
            // await 等待
            const data1 = await fetch("http://127.0.0.1:8000",{method: "get", mode:"cors"}).then(response=>response.json());
            const data2 = await fetch("http://127.0.0.1:8000",{method: "get", mode:"cors"}).then(response=>response.json());
            console.log(data1);
            console.log(data2);
        } catch (e) {
            console.log('出错啦');
        }
    }
    request()
    console.log("hello!!")
</script>
</body>
</html>
协程异步async/await
复制代码

 

posted on   大明花花  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示