第一期 · 使用 Vue 3.1 + TypeScript + Router + Tailwind.css 构建手机底部导航栏、仿B站的登录、注册页面。
alicepolice/Vue-05 (github.com)

bootstrap-icons-vue - npm (npmjs.com)
在 Vue 3 和 Vite 安装 Tailwind CSS - Tailwind CSS 中文文档

PS C:\Users\小能喵喵喵\Desktop\Vue\Homework\homework2\src> tree /f
C:.
│ App.vue
│ index.css
│ main.ts
│ shims-vue.d.ts
│
├───assets
│ 3.png
│ 4.png
│ logo.png
│
├───components
│ BottomBar.vue
│
├───router
│ index.ts
│
├───store
│ index.ts
│
└───views
AboutView.vue
HomeLoginView.vue
HomeView.vue
LoginView.vue
RegisterView.vue

const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: '',
redirect: () => {
return { name: "home" }
}
},
{
path: '/home',
name: 'home',
component: HomeView
},
{
path: '/login',
name: 'login',
component: LoginViewVue
},
{
path: '/register',
name: 'register',
component: RegisterViewVue
},
{
path: '/about',
name: 'about',
component: AboutViewVue
}
]
使用 typescript 语法明确规定了setBottomFlag接收的布尔类型,同时严格规定 vue 应用实例 data 函数返回的对象中变量的类型,即 as 语法。
v-show="bottomFlag" 用于隐藏导航栏,setBottomFlag 由各个 router-view 负责 emit 触发。
<template>
<router-view @set-bottom-flag="setBottomFlag" />
<BottomBar v-show="bottomFlag" :items="bottomItems" />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import BottomBar from "@/components/BottomBar.vue";
type BottomItem = {
text: string;
icon: string;
routerName: string;
};
export default defineComponent({
name: "App",
components: {
BottomBar,
},
data() {
return {
bottomItems: [
{ text: "首页", icon: "b-icon-house-heart", routerName: "home" },
{ text: "理财", icon: "b-icon-coin", routerName: "about" },
{ text: "消息", icon: "b-icon-chat-dots", routerName: "about" },
{ text: "我的", icon: "b-icon-person-circle", routerName: "about" },
] as BottomItem[],
bottomFlag: true as boolean,
};
},
methods: {
setBottomFlag(value: boolean): void {
this.bottomFlag = value;
},
},
});
</script>
这里使用了 windtail css 功能性类语法,具体信息可以通过官方文档查到。
在vue3.1中,router-link的tag已经被废除,需要使用插槽的方式。给 router-link 添加 custom v-slot="{ navigate }"。navigate 是触发导航的函数。 @click = "navigate" 通过对其他标签的点击间接触发router-link的导航函数。
custom -> <router-link> 是否不应将其内容包装在 <a> 标记中。
icon的生成使用了动态控件,依赖外部传进去的数组 ->:is
// 来自 App.vue 的数组传递给了当前的 props -> items
bottomItems: [
{ text: "首页", icon: "b-icon-house-heart", routerName: "home" },
{ text: "理财", icon: "b-icon-coin", routerName: "about" },
{ text: "消息", icon: "b-icon-chat-dots", routerName: "about" },
{ text: "我的", icon: "b-icon-person-circle", routerName: "about" },
] as BottomItem[],
<template>
<div
class="
box-border
h-16
absolute
container
bg-blue-200
bottom-0
left-0
flex flex-nowrap
items-center
"
>
<div v-for="(item, index) in items" :key="index" style="width: 100%">
<router-link :to="{ name: item.routerName }" custom v-slot="{ navigate }">
<div @click="navigate" class="text-center">
<div class="pt-2">
<component :is="item.icon" class="m-auto text-2xl" />
<div class="text-lg">
{{ item.text }}
</div>
</div>
</div>
</router-link>
</div>
</div>
</template>
<script lang="ts">
export default {
props: {
items: Array,
},
};
</script>
在Home页面下默认显示底部导航栏,在挂载的时候通知父组件事件。
this.$emit("set-bottom-flag", true);
<template>
<div class="text-6xl">主页面 HELLO WORLD</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "HomeView",
components: {},
mounted() {
this.$emit("set-bottom-flag", true);
},
});
</script>

对于按钮和表单元素之类的小型组件,与简单的 CSS 类相比,创建模板片断或 JavaScript 组件通常会感觉过重。
官方建议使用 @layer components { ... } 指令包装自定义组件样式,以告诉 Tailwind 这些样式属于哪一层。
在 src/index.css 中定义表单标签、按钮标签共用的 Tailwind CSS 样式集合
/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.login-register-input {
@apply inline-block bg-white focus:outline-none py-3 pl-3 appearance-none leading-normal;
}
.login-register-solid-button{
@apply
focus:outline-none
text-white
bg-pink-400
font-medium
rounded-sm
text-lg
px-5
py-2.5
mb-2
}
.login-register-hollow-button{
@apply
focus:outline-none
text-pink-400
border-pink-400 border
font-medium
rounded-sm
text-lg
px-5
py-2.5
mb-2
}
.login-register-checkbox{
@apply
ml-2
text-sm
font-medium
text-gray-500
dark:text-gray-300
text-left
}
}
注意 router-link 的用法,这里分别绑定了左箭头、短信登录。主要靠如下语法。
custom v-slot="{ navigate }
// 上: router-link标签中的属性, 下: 绑定实现像a标签那样具备跳转功能的标签
@click="navigate"
require 从依赖项返回导出。是同步过程,不会触发对服务器的请求。编译器会确保依赖项可用。
<div
class="bg-cover bg-center h-24 shadow-inner"
:style="{
'background-image': 'url(' + banner + ')',
}"
></div>
data() {
return {
banner: require("../assets/3.png"),
};
}
更多资料可参考
https://stackoverflow.com/questions/67193179/how-can-i-link-background-image-vue-cli
https://stackoverflow.com/questions/35242272/vue-js-data-bind-style-backgroundimage-not-working
依托两个事件,通过当前光标对表单标签的进出实现。
@focusin="changeIMG('4.png')"
@focusout="changeIMG('3.png')"
methods: {
changeIMG(src: string): void {
this.banner = require(`../assets/${src}`);
},
},
<template>
<div class="container bg-gray-100 absolute inset-0">
<div class="box-border bg-white border-b-1 border-b-black h-16 p-2">
<router-link :to="{ name: 'home' }" custom v-slot="{ navigate }">
<b-icon-arrow-left-short
class="inline-block text-4xl align-middle mr-3 mt-2"
@click="navigate"
/>
</router-link>
<span class="text-xl absolute top-5">密码登录</span>
<router-link :to="{ name: 'register' }" custom v-slot="{ navigate }">
<span
class="text-lg absolute right-4 top-5 text-gray-500"
@click="navigate"
>短信登录</span
>
</router-link>
</div>
<div
class="bg-cover bg-center h-24 shadow-inner"
:style="{
'background-image': 'url(' + banner + ')',
}"
></div>
<div class="border-y">
<div class="login-register-input w-1/6">账号</div>
<input
id="username"
class="login-register-input w-5/6"
type="text"
placeholder="请输入手机号或邮箱"
/>
</div>
<div class="border-b">
<div class="login-register-input w-1/6">密码</div>
<input
id="password"
class="login-register-input w-3/6"
type="text"
placeholder="请输入密码"
@focusin="changeIMG('4.png')"
@focusout="changeIMG('3.png')"
/>
<div class="login-register-input pl-8 w-2/6 text-pink-400 text-center">
忘记密码?
</div>
</div>
<div class="text-center pt-6 flex justify-around">
<button type="button" class="login-register-hollow-button w-5/12">
注册
</button>
<button type="button" class="login-register-solid-button w-5/12">
登录
</button>
</div>
<div class="text-center pt-4">
<div class="flex items-center align-top">
<input
id="link-checkbox"
type="checkbox"
value=""
class="ml-4 w-5 h-5 bg-gray-100 rounded"
/>
<label
for="link-checkbox"
class="ml-2 text-sm font-medium text-gray-500 text-left"
>我已阅读并同意<a href="#" class="text-blue-600">用户协议</a>和<a
href="#"
class="text-blue-600"
>隐私政策</a
></label
>
</div>
</div>
<div class="text-center pt-6">
<label class="login-register-checkbox">
遇到问题?<a href="#" class="text-blue-600">查看帮助</a>
</label>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "LoginView",
components: {},
data() {
return {
banner: require("../assets/3.png"),
};
},
methods: {
changeIMG(src: string): void {
this.banner = require(`../assets/${src}`);
},
},
mounted() {
this.$emit("set-bottom-flag", false);
},
});
</script>
部分功能与 Login.view 类似。
当输入手机号时,获取验证码会由灰变成粉色。将字体颜色从固定的class抽取出放入动态class绑定计算属性。每当phone发生变化即可改变颜色。
<div
class="login-register-input w-2/6 text-center"
:class="changeGetCodeColor"
>
获取验证码
</div>
computed: {
changeGetCodeColor(): string {
if (this.phone == "") {
return "text-gray-400";
} else {
return "text-pink-400";
}
},
},
<template>
<div class="container bg-gray-100 absolute inset-0">
<div class="box-border bg-white border-b-1 border-b-black h-16 p-2">
<router-link :to="{ name: 'home' }" custom v-slot="{ navigate }">
<b-icon-arrow-left-short
class="inline-block text-4xl align-middle mr-3 mt-2"
@click="navigate"
/>
</router-link>
<span class="text-xl absolute top-5">手机号登录注册</span>
<router-link :to="{ name: 'login' }" custom v-slot="{ navigate }">
<span
class="text-lg absolute right-4 top-5 text-gray-500"
@click="navigate"
>密码登录</span
>
</router-link>
</div>
<div
class="bg-cover bg-center h-24 shadow-inner"
:style="{
'background-image': 'url(' + banner + ')',
}"
></div>
<div>
<select id="countries" class="login-register-input w-full border-y">
<option selected value="CN">中国大陆</option>
<option value="US">美国</option>
<option value="CA">加拿大</option>
<option value="FR">法国</option>
<option value="DE">德国</option>
</select>
</div>
<div class="border-b">
<div class="login-register-input w-1/6">+86</div>
<input
id="phone"
class="login-register-input w-3/6"
type="text"
placeholder="请输入手机号码"
v-model="phone"
/>
<div
class="login-register-input w-2/6 text-center"
:class="changeGetCodeColor"
>
获取验证码
</div>
</div>
<div class="border-b">
<div class="login-register-input w-1/6">验证码</div>
<input
id="code"
class="login-register-input w-5/6"
type="text"
placeholder="请输入验证码"
@focusin="changeIMG('4.png')"
@focusout="changeIMG('3.png')"
/>
</div>
<div class="text-center pt-6">
<button type="button" class="login-register-solid-button w-11/12">
验证登录
</button>
</div>
<div class="text-center pt-4">
<div class="flex items-center align-top">
<input
id="link-checkbox"
type="checkbox"
value=""
class="login-register-checkbox"
/>
<label
for="link-checkbox"
class="
ml-2
text-sm
font-medium
text-gray-500
dark:text-gray-300
text-left
"
>我已阅读并同意<a
href="#"
class="text-blue-600 dark:text-blue-500 hover:underline"
>用户协议</a
>和<a
href="#"
class="text-blue-600 dark:text-blue-500 hover:underline"
>隐私政策</a
>,未注册绑定的手机号验证成功后将自动注册</label
>
</div>
</div>
<div class="text-center pt-6">
<label
class="
ml-2
text-sm
font-medium
text-gray-500
dark:text-gray-300
text-left
"
>
遇到问题?<a
href="#"
class="text-blue-600 dark:text-blue-500 hover:underline"
>查看帮助</a
>
</label>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "RegisterView",
components: {},
data() {
return {
banner: require("../assets/3.png"),
phone: "",
};
},
methods: {
changeIMG(src: string): void {
this.banner = require(`../assets/${src}`);
},
},
computed: {
changeGetCodeColor(): string {
if (this.phone == "") {
return "text-gray-400";
} else {
return "text-pink-400";
}
},
},
mounted() {
this.$emit("set-bottom-flag", false);
},
});
</script>
Module not found: Error: Can't resolve 'sass-loader'
解决方法: 运行如下命令后重新启动服务
npm install sass-loader -D
npm install node-sass -D
命令式UI:构建全功能UI实体,然后在UI更改时使用方法对其进行变更。
声明式UI:描述当前的UI状态,并且不需要关心它是如何过渡到框架的。

npm config set registry=http://registry.npm.taobao.org
var hello = "hello world"
console.log(hello)
npm install -g typescript
tsc helloworld ::编译ts
node helloworld ::运行js
当使用var声明一个变量的时候,该变量会被提升到作用域的顶端,但是赋值的部分并不会被提升。
console.log(hello)
var hello = "hello world"
而let、const不会,实际开发中建议尽量使用用 let 和 const 代替var。
Tailwind CSS Select / Listbox Form - Free Examples (tailwind-elements.com)
Bootstrap Icons · Official open source SVG icon library for Bootstrap (getbootstrap.com)
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐
我正在开发我的第一个Rubygem,并捆绑了cucumber、rspec和shoulda-matches进行测试。当我运行rspec时,出现以下错误:/app/my_gem/spec/spec_helper.rb:6:in`':undefinedmethod`configure'forShoulda::Matchers:Module(NoMethodError)这是我的gem规范:#my_gem.gemspec...Gem::Specification.newdo|spec|......spec.add_development_dependency"activemodel"spec.a
我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara
require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame
我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,: