豆包使用背景
前几天,业务拉了一个大客户
,客户需要先看我们做的样本项目(类似于官网首页),然后才会决定是否和我们进一步合作。
作为技术人员,我有幸和老板一同来到客户公司参观,早上和客户简单的沟通后,我们就静静等待下午的演示了。
然而,不出意外的话,意外发生了,我们的业务同学突然发现客户前几天单独强调的【合作客户】模块居然没有!
沟通后,才发现是产品忘了给我说!这下,我们在场的所有人都傻眼了。
老板突然问我,现在做来的及吗?
我愣了一下,说道:我现在没电脑啊(客户演示用他们的大屏)?
业务立马说:我可以给你从客户呢里拿一个用!
我内心想:这群不懂技术的人在搞笑吗?借一个电脑开发,我需要先给客户电脑装node
、git
、vscode
还要拉代码到本地才能开发,光环境准备早上时间就过去了,还开发个屁!就算开发完毕,部署也费劲啊,呢帮运维沟通起来太费力了!
我刚想反驳回去,突然想到,如果我解决了这个问题,拿下客户,老板是不是会对我刮目相看?我岂不离升职加薪不远了!
于是,我立即自信的告诉老板:我可以完成!放心交给我吧,2个小时内搞定!
能够这么自信,是因为我前几天发现一个新东西:豆包 MarsCode
!它提供了编译器的AI编程插件;还提供了一个在线的编译器,免配任何环境,只要有网,开箱即用,非常强大!而且,用它启动的前端服务有一个公网域名,访问非常方便,还非常快!完全满足我的需求!
于是,在拿到电脑后,我第一时间就登录上了我的豆包 MarsCode
豆包 MarsCode是什么东西
开发之前,我先简单的介绍下豆包 MarsCode
。官网是这么说的:
豆包 MarsCode 为你提供了编程助手和 IDE 来协助你完成编程任务。
编程助手
简单来说,就是vscode或者IDEA中的一个插件,功能和ChatGpt一样,比文心一言强不少!
在线编译器
豆包 MarsCode IDE 是一个在线编译器,内部集成了各种环境!提供了Python、
Node.js、
Go、
C++、
Java、
React、
Vue等等模板,可以轻松的写各种代码!
我们的项目使用的是vue3,它完全符合我的要求!
使用豆包 MarsCode开发项目
导入git项目
我们的项目是存在git仓库
里的,因此我不需要使用豆包创建一个vue的模板,直接导入我们的仓库代码
到豆包里即可!
公开仓库导入
使用git授权后,我们就可以看到我们的仓库代码了
但是,非常难受,上面的代码没有公司的私有仓库代码!只有我git上的一些公开仓库。
豆包官方是这么解释的:
豆包 MarsCode IDE 不支持使用私人仓库。若填入私人仓库的 URL,输入框下方会提示 ”此代码仓库为私有仓库或无效地址,请检查“。
所以,为了方便代码的导入,如果允许,我们可以将仓库设置为公开。
私有仓库导入
作为一个聪明的开发,私有仓库的导入都不是问题!豆包的在线编译器是有命令行控制台的,我们可以使用命令行拉取私有仓库代码!
我们先随便选择一个模板吧,比如vue
模板创建完毕后,这些模板文件我们都可以删除,我们使用git拉取的代码即可。
首先,我们需要在gitee
上找到私有仓库的HTTPS
链接
然后在豆包的IDE中点击右下角的按钮打开控制台
按照上图的提示,我们先使用git clone
命令下载代码。最后,依此输入用户名和密码(git生成的私人令牌)即可完成代码拉取。
项目下载完成后,我们可以把我们的文件层级调整,使packge.json
位于根目录。
运行项目
豆包的node环境默认比较高,是v20.14.8
版本,我们的项目node版本只有16
,如果使用高版本node下载依赖,可能会导致项目运行异常。因此,保险起见,安装依赖前,需要切换下node环境。
node版本切换
传统的切换node
非常麻烦,需要配置环境或者安装插件。非常人性化的是,豆包内置了nvm工
具,切换node十分丝滑!
我们先下载指定版本的node
nvm install 16.14.2
然后切换node版本
nvm use 16.14.2
相比于本地网络,豆包的云下载速度非常快!
node环境切换好后,我们就可以安装依赖运行项目了!
依赖安装
和本地开发不同,我们并不用切换npm
镜像源,豆包IDE已经帮我们处理了这些繁琐的操作,我们直接使用npm i
安装依赖即可!
npm i
可以看到,即使我本地网速不好,使用豆包IDE下载依赖却用时不到5S,这一点简直吊锤本地依赖下载啊(不配置镜像源的情况下
)!网速不好同学的福音啊!
项目运行
同vscode一样,我们可以直接在命令行输入指令运行项目
npm run dev
或者使用点击豆包IDE的一键运行按钮
这个按钮实际上执行的是npm run start
命令。因此,我们的packge.josn中的命令最好配置成npm run start
。
项目运行成功后,我们就会在右侧看到项目的效果。
点击小眼睛按钮(webview)也可以手动查看代码效果。
项目的公网访问
如上图,运行项目后,和vscode一样,豆包控制台出现了http://localhost:5173/的提示,但注意,这并不意味着通过http://localhost:5173/可以访问到我们的项目。实际上,豆包是把这个服务通过公网的形式给我们暴露出来了!
复制项目的链接,直接在浏览器打开!
这是何等的优秀!用豆包IDE开发,以后给远程同事分享实时代码起步非常爽!
甚至,我只是不是可以把这个当一个临时服务器域名进行网页访问?还要什么tomcat和nginx,我就喜欢简单粗暴的东西!
开发前准备工作
插件安装
工欲善其事,必先利其器!开发代码前,怎么能不能安装一些提高开发效率的插件呢!
同vscode一样,豆包IDEA可以使用下载vscode的插件(个别的不行)! 这一点着实让我惊讶,非常牛逼!
豆包IDE是默认提供git管理工具的,但我觉得不好用。我的git管理一般用Git graph
,可以查看代码提交历史,进行一些列操作,最重要的时免费好用!
Git graph
我们点击豆包的拓展插件按钮,搜索Git graph
然后下载即可
安装好后,点击对应图标我们就可以看到对应的代码提交历史了。
Code Spell Checker
这个插件主要是检查单词拼写的,如果拼写错误,会给出提示。没办法,咱英语不好,需要一个这样的辅助插件。
git操作
云端授权
在正式开发前,我们还需要测试一下git是否能用。不然我辛辛苦苦开发完的代码最后不能推送到仓库,我岂不赔了夫人有折兵!
按照要添加的功能(轮播图组件),我先创建一个Banner文件夹
提交到仓库试试吧。
创建完文件后,豆包的git就会实时显示编辑更改后的文件。
但很可惜,此时并不能提交代码。豆包官网解释如下:
豆包 MarsCode IDE 默认已集成 Git,你可以在 IDE 中进行 Git 操作,提高开发效率。
- 已通过 GitHub 授权后,可以直接在豆包 MarsCode IDE 中使用 Git 命令,执行各种 Git 操作。
- 未通过 GitHub授权的,需要手动配置秘钥。
很可惜,我们的仓库是私有仓库,没有通过git授权,是手动通过命令行下载的。因此,我们需要配置一下私钥才行。
公钥生成
配置 Git 的用户信息
git config --global user.name "your username"
git config --global user.email "your email"
终端中运行以下命令生成 SSH 密钥
ssh-keygen -t rsa -b 4096 -C "your email"
按照操作,点击三次回车键出现如下界面即可生成秘钥。
生成秘钥后,我们查看生成的 SSH 公钥和私钥:
ls ~/.ssh/
如图,有.pub的就是公钥
我们使用下面的命令读取公钥并复制
cat ~/.ssh/id_rsa.pub
最后,我们在git上进行配置即可
配置成功后,我们就可以愉快的推送和拉取代码了!
轮播组件开发
思路分析
如图,按照客户的意思,无非就是开发一个轮播图组件,轮播图的每页有21张图片(按照三行七列排布),点击切换按钮可以切换轮播图。
这很容易,我们先维护一个数组,用于储存图片的资源,类似这样:
const list = ref([
{name:"",src:"/assets/img/pic1.png",id:1},
{name:"",src:"/assets/img/pic2.png",id:2},
// ....
])
然后,我们需要一个值用于储存选中页
const selectPage = ref(1)
最后,根据不同的选中页,渲染不同的列表即可
<template>
<div class="banner-box">
<div class="img-wrap" v-for="item in showList" :key="item.id">
<img :src="item.src">
</div>
</div>
</template>
<script setup>
const list = ref([
{name:"",src:"/assets/img/pic1.png",id:1},
{name:"",src:"/assets/img/pic2.png",id:2},
// ....
])
const selectPage = ref(1)
const showList = computed(()=>{
const bIndex = (selectPage.value -1) * 21;
const eIndex = (selectPage.value) * 21
return list.value.slice(bIndex,eIndex)
})
</script>
使用豆包AI生成.vue模板
要开发一个轮播图组件,我们肯定要先创建组件文件夹(Banner
),然后文件夹内创建index.vue
文件。默认生成的.vue
文件内部是没有任何东西的,我们先试用豆包AI帮我们生成一个模板文件吧:
生成完成后,我们点击【Aceept】按钮即可。
结合豆包AI完善基础功能
按照刚才的开发思路,我们先把代码的基础样式,功能完善一下。
首先是图片资源,这个好弄,我让UX切好图直接发我了,名称也是让她按照数字命名的,方便调用。
然后,我需要完善这个list列表,手写是不存在的,我们直接借助豆包AI补全吧!
这个生成结果还凑活,我们稍微改造一下数组吧。
<template>
<div class="banner-box">
<div class="banner-wrap">
<!-- 轮播图 -->
<div class="img-wrap" v-for="item in showList" :key="item.id">
<img :src="item.src" alt="">
</div>
</div>
<!-- 轮播图切换按钮 -->
<div class="option-btn">
// 待开发
</div>
</div>
</template>
<script setup>
const list = ref([
...[...Array(42).keys()].map((i) => ({ name: "", src: `/assets/img/pic${i}.png`, id: i})),
]);
const selectPage = ref(1)
const showList = computed(()=>{
const bIndex = (selectPage.value -1) * 21;
const eIndex = (selectPage.value) * 21
return list.value.slice(bIndex,eIndex)
})
</script>
<style lang="less" scoped>
.banner-wrap{
//样式省略
}
.option-btn{
//样式省略
}
</style>
看看效果
很尴尬,图片都没有加载成功。这也正常,在vite项目里,动态图片的加载是有专门的方法的。忘了怎么写,我用豆包AI生成一下吧。
ok,再改造一下我们的代码
<template>
<div class="banner-box">
<div class="banner-wrap">
<!-- 轮播图 -->
<div class="img-wrap" v-for="item in showList" :key="item.id">
<img :src="getListUrl(item.src)" alt="">
</div>
</div>
<!-- 轮播图切换按钮 -->
<div class="option-btn">
</div>
</div>
</template>
<script setup>
import { computed ,ref} from '@vue/reactivity';
const list = ref([
...[...Array(42).keys()].map((i) => ({ name: "", src: `/assets/img/pic${i}.png`, id: i})),
]);
const selectPage = ref(1)
const showList = computed(()=>{
const bIndex = (selectPage.value -1) * 21;
const eIndex = (selectPage.value) * 21
return list.value.slice(bIndex,eIndex)
})
const getListUrl = (path) => {
// 里面可以根据需求写逻辑
return new URL(`/src/${path}`, import.meta.url).href;
};
</script>
可以看到,图片已经正确的加载出来了。
现在,只要完善一下轮播的切换按钮就可以了。
\
切换按钮无非就是几个span元素,选中span的添加单独的类名控制样式即可,span的数量用总的图片数量除以21向上取整即可,那它的实现也非常容易。
<template>
<div class="banner-box">
<!-- 其他代码 -->
<!-- 轮播图切换按钮 -->
<div class="option-btn">
<span @click="selectPage = item" :class="{sel:selectPage===item}" v-for="item in dotList"></span>
</div>
</div>
</template>
<script setup>
import { computed ,ref} from '@vue/reactivity';
const list = ref([
...[...Array(42).keys()].map((i) => ({ name: "", src: `/assets/img/pic${i}.png`, id: i})),
]);
const dotList = computed(()=> {
const num = Math.floor(list.value.length / 21)
return Number(num)
})
</script>
<style lang="less" scoped>
.option-btn{
display: flex;
justify-content: center;
align-items: center;
span{
width: 8px;
display: block;
height: 8px;
border-radius: 50%;
background: #B0B0B0;
margin-right: 8px;
&:last-child{
margin-right:0;
}
}
.sel{
width: 16px;
border-radius: 4px;
background: #FA783A;
}
}
</style>
那么,现在我们的逻辑就基本完成了,看看效果,非常丝滑!
使用豆包AI生成注释
秉持着一个优秀的程序员必须写注释的原则,我决定给我的代码写上注释,但要用AI生成(正经程序员谁自己写注释啊!)
选中代码,点击豆包的DOC按钮
很快啊,这个注释就生成完毕了,我们点击图中绿色的yes按钮就可以应用注释了!干净卫生啊兄弟们!
完整代码
不得不说,AI写代码,就是快啊!
<template>
<div class="banner-box">
<div class="banner-wrap">
<!-- 轮播图 -->
<div class="img-wrap" v-for="item in showList" :key="item.id">
<img :src="getListUrl(item.src)" alt="">
</div>
</div>
<!-- 轮播图切换按钮 -->
<div class="option-btn">
<span @click="selectPage = item" :class="{sel:selectPage===item}" v-for="item in dotList"></span>
</div>
</div>
</template>
<script setup>
import { computed ,ref} from '@vue/reactivity';
// 创建一个响应式数据 list,初始值为一个数组,其元素根据键值对生成
const list = ref([
// 使用 Array(42) 创建一个包含 42 个元素的数组
// 使用 Array.prototype.keys() 获取数组每个元素的索引
// 使用展开运算符... 展开索引数组
// 使用 map 方法重新构造每个元素,为其添加 name、src 和 id 属性
// src 值为 `/assets/img/pic${i}.png`,其中 i 为元素的索引
...[...Array(42).keys()].map((i) => ({ name: "", src: `/assets/img/pic${i}.png`, id: i})),
]);
// 创建一个响应式数据 selectPage,设置其初始值为 1
const selectPage = ref(1)
// 计算属性,根据 selectPage 的值动态计算并返回 list 中的一部分
const showList = computed(() => {
// 计算当前页开始的索引
const bIndex = (selectPage.value - 1) * 21;
// 计算当前页结束的索引
const eIndex = (selectPage.value) * 21;
// 使用 slice 方法返回 list 数组中从 bIndex 到 eIndex 的元素
return list.value.slice(bIndex, eIndex);
});
// 计算属性,计算并返回 list 数组中全部元素可分的总页数
const dotList = computed(() => {
// 使用 Math.floor 将 list.length / 21 结果取整
const num = Math.floor(list.value.length / 21);
// 将结果转换为数字类型
return Number(num);
});
// 定义一个方法,根据给定的路径参数,返回一个 URL 字符串
const getListUrl = (path) => {
// 使用 new URL 实例化一个 URL 对象,指定 href 为 /src/{path},其中 path 为传入的参数
return new URL(`/src/${path}`, import.meta.url).href;
};
</script>
<style lang="less" scoped>
.banner-wrap{
// background: red;
display: flex;
margin-top: 86px;
height: 326px;
flex-wrap: wrap;
.img-wrap{
width: 154px;
height: 90px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 19px;
&:nth-child(7n){
margin-right: 0;
}
img{
width: 100%;
height: 100%;
}
}
}
.option-btn{
display: flex;
justify-content: center;
align-items: center;
span{
width: 8px;
display: block;
height: 8px;
border-radius: 50%;
background: #B0B0B0;
margin-right: 8px;
&:last-child{
margin-right:0;
}
}
.sel{
width: 16px;
border-radius: 4px;
background: #FA783A;
}
}
</style>
项目演示
在豆包IDE和AI的帮助下,很快啊,我就完成了需求的开发!自测无误后,我终于舒了一口气,看看时间,总用时半小时左右,时间还行。
此时,离中午吃饭还剩半个小,还好,差点耽误我吃饭了!
开完后,我复制出了项目的在线链接,然后直接甩给老板钉钉,然后特意钉他!
很快,老板立马过来就激动的问我:你开发完了?这么快!
是的,我斩钉截铁的说!
老板突然用手拍了我三下,说:小伙子,干的不错!
下午2点,我们成功的进行了项目演示,客户也比较满意!
回到公司,老板把我的事情讲给了同事们听,同事们都很惊讶,为啥我用客户的电脑还能这么快完成需求开发?
我只是淡淡一笑:豆包!
大家很疑惑,豆沙包?这啥东西!
不管怎么样,这次,也是借助豆包MarsCode顺利的完成了紧急任务,也让我对AI工具有了新的认识!最后,领导在会上也承诺给我发500的现金奖励,用来表彰我本次的表现!
总结
本篇文章,笔者结合实际经历,深度的体验了豆包MarsCode
,在豆包MarsCode
的帮助下,笔者也是顺利的完成了项目开发。
豆包对于普通用户的意义
实际开发中,我们可能也很少会使用豆包IDE进行项目开发,有些人可能会问豆包云IDE对于我们普通用户的意义。笔者认为,对我们普通用户来说,它最大的用途就是可以十分方便的运行个的demo,查看效果。
比如,笔者这篇文章中,有一段可以直接运行的demo(代码大家可以在文章中复制也可以在彩蛋中复制):
如果我们看完按照文章想尝试,我们必须本地起一个vue服务,下载依赖、创建模板,这是非常耗费时间的!因此,我相信没有人愿意真正的去实践,毕竟成本太大!
但借助豆包,我们可以一键创建好模板,立即就可以把demo粘贴进行效果查看,极大的方便了我们学习!
因此,笔者认为,豆包IDE对我们而言是一个非常好的学习工具,合理运用,能极大的提升我们的学习效率与质量!
最后,笔者提出自己对豆包MarsCode的一些看法。
豆包IDE
豆包的在线IDE着实是让人眼前一亮的东西,虽然笔者只使用了VSCODE
,但总体体验非常好,和本地开发几乎没有什么区别!也没有什么学习成本。
优点
- 云编译器,不吃电脑配置,开发体验非常流畅。
- 无需配置复杂的开发环境,任意电脑,登录账号就可以快速开发。
- npm依赖下载速度非常快,而且支持git管理项目。
- 界面简洁,用起来很清爽,操作简单。
- 界面功能和VSCODE一致,无学习成本。
- 支持插件下载,可以个性化完善自己的在线编译器。
- 服务启动后,页面支持公网访问,其他人访问非常方便。
缺点
- 私有仓库的代码导入比较麻烦。
- 控制台不能记录切换的node版本,刷新页面后,需要重新切换(可能是bug)。
- 手动创建的.vue文件不支持快速生成模板内容。
- 部分vscode插件不能安装,可能和浏览器运行环境有关。
豆包AI
和绝大部分类似产品如通义千问和文心一言而言,他们的使用方法基本一致的,属于有手就行。
优点
- 免费,不用充值会员。
- 和豆包IDE结合的很好,使用起来比较人性化。
- 豆包AI的响应速度比较稳定,速度也还行。
- 豆包的文档注释功能设计的很好,选中代码直接注释。
- 豆包AI也支持连续提问,问题回答能结合代码及上下文。
缺点
- 不支持图片
- 回答的结果有时候不尽人意,需要多次引导
总的来说,豆包MarsCode是一款非常优秀的产品,足够让人眼前一亮,相信大家使用后,也会深深的被这一款优秀的工具吸引。虽然现在它还有很多不足,但我相信,随着用户的增多,官方也会持续优化,一定会把产品打磨的更加好用!期待!
希望国产工具能做的越来越好!也希望AI产品能给我打工的我们带来更多真正有用的益处!
彩蛋
打开豆包云IDE,创建vue模板,将上述代码粘贴在app.vue中即可!
<template>
<div class="content" @mouseenter="stop" @mouseleave="start" :style="{height: 5 * 47 + 'px'}">
<div class="item-wrap" v-for="(item, index) in animationData"
:key="item.id"
:class="[{ moveToBottom: animationActive }, { show: animationActive && index === 0 }]"
>
{{ item.name }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref ,onBeforeUnmount} from 'vue'
// #假设这是接口请求的10条最新数据
const allCarouseData = ref<any>([])
// #需要轮播的数据
const animationData = ref<any>([])
// #是否开启动画
const animationActive = ref(false)
// *定时器
const animationTimerMeta: any = {
timer: null,
timeFuc() {
let setTimeoutId: any = null
if (this.timer) return
this.timer = setInterval(() => {
if (setTimeoutId) {
setTimeoutId = null
clearTimeout(setTimeoutId);
}
// 取轮播数据的第一条id
let firstId = animationData.value[0].id
// 查询
let index = allCarouseData.value.findIndex((res: any) => res.id === firstId)
let addIndex = index - 1 < 0 ? allCarouseData.value.length - 1 : index - 1
animationData.value.unshift(allCarouseData.value[addIndex])
// #开启动画
animationActive.value = true
setTimeoutId = setTimeout(() => {
animationActive.value = false
animationData.value.pop()
}, 1000)
// 删除数组的最后一项
}, 1500)
}
}
const mockData = () => {
const axiosData = [
{name:"收入翻倍!",id:1},
{name:"祝你今年",id:2},
{name:"找到宝藏!",id:3},
{name:"恭喜你",id:4},
{name:"少侠你来了",id:5},
{name:".........",id:6},
{name:".........",id:7},
{name:"成为亿万富翁",id:8},
{name:"走上人生巅峰",id:9},
{name:"迎娶白富美!",id:10},
]
allCarouseData.value = axiosData
animationData.value = axiosData.slice(-5)
animationTimerMeta.timeFuc()
}
mockData()
const stop = () => {
clearInterval(animationTimerMeta.timer)
animationTimerMeta.timer = null
}
const start = () => animationTimerMeta.timeFuc()
// !页面卸载时,关闭轮播
onBeforeUnmount(() => {
clearInterval(animationTimerMeta.timer)
animationTimerMeta.timer = null
})
</script>
<style>
.content{
overflow: hidden;
box-sizing: border-box;
}
.item-wrap{
box-sizing: border-box;
border: 1px solid #e4e4e4;
margin-right: 13px;
padding: 9px 12px;
display: flex;
height: 47px;
justify-content: space-between;
align-items: center;
}
@keyframes moveToBottom {
0% {
transform: translateY(-47px);
}
100% {
transform: translateY(0);
}
}
.moveToBottom {
animation: moveToBottom 500ms ease-in-out forwards;
}
@keyframes fadeInFromTop {
0% {
opacity: 0;
transform: translateY(-47px);
}
100% {
opacity: 1;
transform: translateY(0);
color: #683BD6;
}
}
.show {
animation: fadeInFromTop 500ms ease-in-out forwards;
}
</style>