豆包Marscode体验官:用云编译器半小时完成轮播组件紧急开发!被公司奖励500!

7,131 阅读17分钟

我正在参加「豆包MarsCode初体验」征文活动

豆包使用背景

前几天,业务拉了一个大客户,客户需要先看我们做的样本项目(类似于官网首页),然后才会决定是否和我们进一步合作。

作为技术人员,我有幸和老板一同来到客户公司参观,早上和客户简单的沟通后,我们就静静等待下午的演示了。

然而,不出意外的话,意外发生了,我们的业务同学突然发现客户前几天单独强调的【合作客户】模块居然没有!

沟通后,才发现是产品忘了给我说!这下,我们在场的所有人都傻眼了。

老板突然问我,现在做来的及吗?

我愣了一下,说道:我现在没电脑啊(客户演示用他们的大屏)?

业务立马说:我可以给你从客户呢里拿一个用!

我内心想:这群不懂技术的人在搞笑吗?借一个电脑开发,我需要先给客户电脑装nodegitvscode还要拉代码到本地才能开发,光环境准备早上时间就过去了,还开发个屁!就算开发完毕,部署也费劲啊,呢帮运维沟通起来太费力了!

我刚想反驳回去,突然想到,如果我解决了这个问题,拿下客户,老板是不是会对我刮目相看?我岂不离升职加薪不远了!

于是,我立即自信的告诉老板:我可以完成!放心交给我吧,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(代码大家可以在文章中复制也可以在彩蛋中复制):

juejin.cn/post/734843…

如果我们看完按照文章想尝试,我们必须本地起一个vue服务,下载依赖、创建模板,这是非常耗费时间的!因此,我相信没有人愿意真正的去实践,毕竟成本太大!

但借助豆包,我们可以一键创建好模板,立即就可以把demo粘贴进行效果查看,极大的方便了我们学习!

233.gif

因此,笔者认为,豆包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>