Vue3——简易版个人空间(上半部分)

创建项目

使用vue 的图形化界面创建一个新的vue3项目如下图所示

装两个新的插件——router和vuex插件

该过程的可能有点久,需要耐心等待。

 再装一些需要的依赖

 需要用到的依赖: boostrap和poperjs/core(bootstrap是提供给不会做美工的程序员的一个新的好选择)第二个依赖是使用bootstrap时需要用到的一个依赖。

 到此为止,该项目需要用到的东西已经装完。

项目结构

如上图所示的项目

main.js是这一整个项目的入口文件,在里面可以看见如下的一些语句,  上面的一句话意思是根据App.vue这个组件创建一个虚拟dom元素挂载到了id=app的区域里面,对应public文件夹下面的index.html里面的如下图所示的div区域的id就是app。

至于新装的两个依赖router和vuex在上面也有体现出来,上面的store对应的就是vuex插件。 

基本概念 

后端渲染模型:

每一次打开一个新的页面,都会由客户端client向服务器发送一次请求,服务器将请求的页面返回给客户端。

前端渲染模型:

只有在最开始的时候会请求一次服务器,服务器一次性将所有页面都返回给客户端,后面每一次切换页面都是从本地加载。

需求分析

需要完成的一个项目最后效果图如下所示

 需求实现

要使用bootstrap要先在App.vue里面进行引入,这里要连带js,css文件一起引入,不然就无法使用

bootstrap里面的一些交互样式

1.导航栏部分实现

在components文件夹下面新建一个NavBar.vue组件,并加上固定模板语句

随后去bootstrap官网里面去找一些喜欢的导航栏样式

 为了能够让别的组件能够引入,这里NavBar组件需要将自己暴露出去并给定一个name属性。

此处的name属性必须有两个大写的字母,否则语法检查过不了。

下面的style scoped可以让这个组件的样式不会影响别的组件里面的样式。

 因为每一个页面都需要导航栏,因此可以直接将NavBar组件引入到根组件App.vue里面,为了能使用NavBar,还需要在components区域标注出来,然后就可以直接在template区域里面使用它了。

 达成效果

代码实现:

<template>
    <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="#">鼠鼠空间</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
                aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">首页</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">鼠友列表</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">鼠友动态</a>
                    </li>
                </ul>
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link" href="#">登录</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">注册</a>
                    </li>
                </ul>

            </div>
        </div>
    </nav>
</template>

<script >
export default {
    name: "NavBar",
}
</script>

<style scoped></style>

为了实现网站美化,可以用一个框将网页内容都包裹起来的。为此使用到一个bootstrap里的一个模块——Card卡片 

2.首页页面实现

代码:

实现效果 

这里再加上一个container将里面内容括起来使得页面布局更对称,container也是bootstrap里面定义好的一个class,可以响应式的调整中间内容区域的大小。

代码:

实现效果:

可以看见card区域已经和上面导航栏区域对齐。这是因为上面的导航栏区域也是有一个container

 导航栏部分代码:

3.公共ContentBase部分实现 

由上面的这个图可以看出,所有的页面都会有一个content区域,如果每一个页面都要修改外边距就要全部修改,但是利用一个单独的ContentBase.vue组件就可以实现一次修改在所有页面都生效。

在components文件夹下新建一个ContentBase.vue组件 

内容如下,将公共部分抽取出来

<template>
    <div class="home">
        <div class="container">
            <div class="card">
                <div class="card-body">
                    <slot></slot>
                </div>
            </div>
        </div>
    </div>
</template>


<script>
export default {
    name: "ContentBase"
}
</script>

<style scoped></style>

随后在首页的组件HomeView.vue当中改成如下,将ContentBase.vue引入并在ContentBase标签里面填充当前页面的信息。

在<ContenBase></ContenBase>里面可以渲染任意的内容,如上面的效果图就是

 

 这里container被定义在了ContentBase里面,所以要将样式也移动到ContentBase.vue下面

知识点 

这里要想在Content.vue里面展示不同页面插入的信息,需要用到一个<slot></slot>标签,

可以将子组件的元素都渲染到slot里面。这个也叫做插槽

Vue3——路由的query参数和命名路由以及默认插槽slot的使用_vue3 路由query_北岭山脚鼠鼠的博客-CSDN博客

4.剩下的页面一起实现(登录、注册、404、鼠友列表,鼠友动态五个页面)

先将剩下的所有需要的页面组件都建好,然后将home页面的内容都复制粘贴进去,每一个都要改name属性。

注意点:所有的name属性都一定要有两个大写字母,否则语法检查过不去。

模板页面代码: 

<template>
  <ContentBase>

  </ContentBase>
</template>

<script>
import ContentBase from '@/components/ContentBase.vue';
export default {
  name: 'XXXX',
  components: {
    ContentBase
  }
}
</script>
<style scope></style>

 配置不同页面的路由信息:

现在所有的页面都准备好了,要能够根据不同的url后缀跳转不同的页面组件需要到Router里面配置

配置信息如下图所示

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import UserListView from '../views/UserListView.vue'
import UserProfileView from '../views/UserProfileView.vue'
import LoginView from '../views/LoginView.vue'
import NotFoundView from '../views/NotFoundView.vue'
import RegisterView from '../views/RegisterView.vue'


const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/userlist',
    name: 'UserList',
    component: UserListView
  },
  {
    path: '/userprofile',
    name: 'userprofile',
    component: UserProfileView
  },
  {
    path: '/login',
    name: 'login',
    component: LoginView
  },
  {
    path: '/register',
    name: 'register',
    component: RegisterView
  },
  {
    path: '/404',
    name: '404',
    component: NotFoundView
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

配置完之后能够跳转相应页面就算成功

 配置导航栏的跳转标签

这里设置到的router-link标签的使用可以到一下文章去学习一下,这里的router-link有两种配置方式。

这里直接放结果代码:Navbar.vue代码如下所示

<template>
    <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container">
            <router-link class="navbar-brand" :to="{ name: 'home',params:{} }">
                鼠鼠空间
            </router-link>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
                aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <router-link class="nav-link active" to="/">首页</router-link>
                    </li>
                    <li class="nav-item">
                        <router-link class="nav-link" :to="{ name: 'userlist',params:{} }">鼠友列表</router-link>
                    </li>
                    <li class="nav-item">
                        <router-link class="nav-link" :to="{ name: 'userprofile',params:{} }">鼠友动态</router-link>
                    </li>
                </ul>
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <router-link class="nav-link" :to="{ name: 'login',params:{} }">登录</router-link>
                    </li>
                    <li class="nav-item">
                        <router-link class="nav-link" :to="{ name: 'register' ,params:{}}">注册</router-link>
                    </li>
                </ul>

            </div>
        </div>
    </nav>
</template>

<script >
export default {
    name: "NavBar",
}
</script>

<style scoped></style>

到此为止,总体的布局已经完成了。

下面都是单独的页面的实现。

鼠友动态页面:

需求说明:

此处的三个模块使用三个不同的组件进行实现:

在Compontents文件夹下面新建三个组件如图

页面布局,这里使用到bootstrap的grid系统 这玩意是将一个单独的块划分为12份,可以支持调整多个块和每一块的大小。

此处需要grid外部是有container的才可以,因为这个是对container进行划分,在contentbase里面已经有了,说错了,这里不是对container进行划分,而是对一个新的块进行划分,就是上图中<div class="row">这个块。

这个row和container还有row之间的关系如下图所示 

效果图:

下面这个是进行了3-9分的,在class处使用了col-3和col-9

代码部分:

 用户信息组件实现

区域也要分成两个部分,左边区域显示头像,右边区域显示名字 

这里同样使用了39分的grid布局,在头像显示那里使用到了一个bootstrap提供的类属性可以让头像自适应布局的大小。

 效果演示 

此处再将头像变成圆形,设置CSS样式如下可以将图片变成圆形 

 头像部分完成了,右边的个人信息部分的有三个东西,姓名,粉丝数和一个关注按钮

按钮使用bootstrap提供的一个样式

效果如下

用户信息部分总体代码:  这里的个人信息也使用了一个card将其包围起来

<template>
    <div class="card">
        <div class="card-body">
            <div class="row">
                <div class="col-3">
                    <img class="img-fluid" src="https://cdn.acwing.com/media/user/profile/photo/150603_lg_5b00f49635.jpg"
                        alt="我图片呢" />
                </div>
                <div class="col-9">
                    <div class="name">yanghuiyu</div>
                    <div class="fans">粉丝数:123</div>
                    <button type="button" class="btn btn-primary btn-sm">关注</button>
                    <!---此处的btn-primary是控制按钮颜色,btn-sm是控制按钮大小 -->
                </div>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: "UserInfo",

}
</script>
<style scoped>
img {
    border-radius: 50%;
}

.username {
    /* 变成粗体 */
    font-weight: bold;
}

.fans {
    font-size: 15px;
    color: gray;
}

button {
    padding: 2px, 4px;
    font-size: 15px
}
</style>

UserProfileView.vue和UserInfo.vue之间传递信息实现

这里UserProfile页面需要有很多信息,这些信息都需要存储在UserProfile这个组件里面。

知识点

这里信息存储不再是data形式的vue2写法,而是使用一个新的api里面set up函数这个东西.

在setup函数里面可以定义变量,方法。

使用方法,

如果在一个对象里面的一个属性是函数,也可以简写成另外一个格如下所示

 定义需要的变量

这里定义变量有两种方式:

一个使用ref,另一个是使用reative

两个的效果差不多,但是ref的运行效率会比reative慢一些。

  • ref定义变量,可以用.value属性重新赋值  
  • reactive定义对象,不可重新赋值

根据上面,可以知道需要一个user的对象,这里用到了reative这个东西需要先进行引入 

然后这个user是需要在子组件里面使用的,所以这里又有一个新的知识点,组件之间的信息传递。

知识点

  • props存储父组件传递过来的数据
  • context.emit():触发父组件绑定的函数

使用:

详细的内容可以看下面这个

Vue3----props和emit的使用_vue3接收props_北岭山脚鼠鼠的博客-CSDN博客

父组件里面

子组件标签里面使用   :属性=“值”  方式进行传递。 

子组件里面

将接收到的参数都放到props的属性里面进行接收

在父组件里面的name信息应该是由两个部分组合而成的,也就是需要动态计算的

有需要用到一个新的东西

  • computed:动态计算某个数据

具体实现:(需要用到setup进行传值)

在setup当中是不能使用this.value这种东西的,所以要将props作为参数传进去,动态计算出一个fullname

然后return出去后就可以直接使用了

关注按钮实现

在一个个人页面中,如果是已经关注了应该显示以关注,如果是未关注应该显示取消关注。

这里使用到一个v-if标签,根据父组件传过来的信息判断显示哪一个按钮

然后需要点击修改状态,绑定两个函数

这两个函数需要去修改父组件的is_followed这个东西的状态,这里用到另外一个东西

  • context.emit():触发父组件绑定的函数

 在父组件当中

@follow="follow"左边是子组件里面的函数,右边是父组件里面的函数,要在下面定义,涉及到修改粉丝数之类的。

这里两个事件的名字不一定是一样的,只要能和对应组件对应上就可以了

 在子组件当中

使用context.emit(),上下文触发父组件里面的函数,这里要使用context要通过setup的参数列表先传进来,然后才能使用 

帖子列表组件实现

帖子列表那一栏要渲染一堆从父组件传过来的数据,也要用到props

先在父组件定义好数据:

定义的这个数组级的要返回

然后在组件的标签里面绑定子组件里面的posts

在子组件当中

首先要到props中去接收一个posts对象,然后使用到一个v-for标签进行数组内容的遍历

在vue和react当中都不推荐使用下标来当做key

<template>
    <div class="card">
        <div class="card-body">
            <div v-for="(post, index) in posts.posts" :key="index">
                <div class="card single-post">
                    <div class="card-body">
                        {
   
   { post.content }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: "UserPosts",
    props: {
        posts: {
            type: Object,
            required: true,
        }
    }
}
</script>


<style scoped>
.single-post {
    margin-bottom: 10px;
}
</style>

编辑发帖组件实现

在上面新建好的UserWrite.vue组件里面准备好格式在父组件中引入并使用

在该组件内部就直接到bootstrap里面copy一个编辑框的样式下来直接就可以了 

<template>
    <div class="card edit-filed">
        <div class="card-body">
            <label for="edit-post" class="form-label">编辑你的帖子 </label>
            <textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
            <button type="button" class="btn btn-primary btn-sm">发表</button>
        </div>
    </div>
</template>
<script>
export default {
    name: "UserWrite"
}
</script>

<style scoped>
.edit-filed {
    margin-top: 15px;
}
button {
    margin-top: 10px
}
</style>

效果图:

在发表的时候需要获取到textarea里面的内容,并将其存储起来返回到父组件的数据post当中,这里首先要在子组件当中准备一个变量接收数据内容,使用到一个v-model标签绑定textarea标签里面的内容和一个变量.

然后又使用到emit这个东西去绑定父组件里面定义的函数,并将帖子内容传了过去新增到post数组当中。

此处使用到ref时必须使用.value获取内容值。

<template>
    <div class="card edit-filed">
        <div class="card-body">
            <label for="edit-post" class="form-label">编辑你的帖子 </label>
            <textarea v-model="content" class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
            <button @click="post_a_post" type="button" class="btn btn-primary btn-sm">发表</button>
        </div>
    </div>
</template>
<script>
import { ref } from 'vue'
export default {
    name: "UserWrite",

    setup(props, context) {
        let content = ref('');
        const post_a_post = () => {
            context.emit("post_a_post", content.value)
            content.value = "";
        };
        return {
            content,
            post_a_post
        }
    }
}
</script>


<style scoped>
.edit-filed {
    margin-top: 15px;
}

button {
    margin-top: 10px
}
</style>

父组件中

 这里的发帖以及帖子以后都要存储到数据库里面,然后今后都是从数据库获取数据来更新页面。

到此为止,已经完成了一大段内容了————————————

猜你喜欢

转载自blog.csdn.net/m0_62327332/article/details/130520722