前言
? ? ? ? ? 学习 Vue 框架 内容!
简介
vue 会将所有的组件打包成一个 js 文件,采用前端渲染。 整个项目的设计要从上往下来进行构思。
组成
views:用于写各个页面,每个页面对应一个 view ,里面也可以写组件。 components:组件。 router:路由。 main.js:整个的入口,根组件,将根组件挂载到 index.html 里。 .vue:每个页面是一个 .vue 文件,是 vue 自定义的文件类型,每个 .vue 文件都有三部分组成,分别是 html ,css 和 js 。每个 .vue 文件都会 export 一个对象。
<template>
<ContentBase>
首页
</ContentBase>
</template>
<script>
import ContentBase from '../components/ContentBase';
export default {
name: 'HomeView',
components: {
ContentBase,
}
}
</script>
<style scoped>
</style>
特点
- 每个页面(
.vue 文件)定义好 html 后,通过将 css 文件和 js 文件动态引入。 - 组件化的框架
一个页面需要拆分成若干不同的部分,每个部分用组件实现,每个组件又可以单独拆分,每个组件由 html 、css 、js 组成,定义完组件后,通过 <组件/> 的方式在其它组件中使用。
个人空间项目简介
-
项目图示如下: -
导航栏(NavBar):不同内容中不同页面对应的导航栏不变。 -
页面不同(Content):分成多个页面如下,每个页面用组件实现。
- 首页:每个页面的首页都相同
- 好友列表:可以索引到所有好友
- 好友动态:展示发过所有的帖子
- 登录
- 注册
- 404
项目实现
导航栏实现
所用知识点:路由,不同页面(组件)对应不同的 url ,router-link 实现前端渲染。
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue';
import LoginView from '../views/LoginView';
import NotFoundView from '../views/NotFoundView';
import RegisterView from '../views/RegisterView';
import UserListView from '../views/UserListView';
import UserProfileView from '../views/UserProfileView';
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/userlist',
name: 'userlist',
component: UserListView
},
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/register',
name: 'register',
component: RegisterView
},
{
path: '/userprofile',
name: 'userprofile',
component: UserProfileView
},
{
path: '/404',
name: '404',
component: NotFoundView
},
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<router-link class="navbar-brand" :to="{name: 'home'}">我的空间</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" :to="{name: 'home'}">首页</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name: 'userlist'}">好友列表</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name: 'userprofile'}">用户动态</router-link>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<router-link class="nav-link" :to="{name: 'login'}">登录</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name: 'register'}">注册</router-link>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default {
name: "NavBar",
}
</script>
用户动态页面
1. 知识点
- 当某个组件(父组件)调用另一个组件(子组件)时,父组件中有子元素,子元素传递给组件,子组件通过 slot 接收所有的子元素,
<slot></slot> 存放父组件传过来的children。 - 组件间数据交互
- 多个组件进行数据交互时,将数据放到最顶层的组件中,这里最顶层的组件为用户动态组件。
- export default 中定义
- setup(props, context) :定义并初始化变量、定义函数,在 setup 中定义的只有 return 之后才能在当前 templante 中使用。
- ref :定义变量,当变量需要重新赋值时用 ref 定义,() 中内容用于初始化初值。
- reactive:当变量不需要重新赋值时用 reactive 定义,只能定义对象,() 中内容用于初始化初值。
- props存储父组件传递过来的数据。
- context.emit():触发父组件绑定的函数
- 不同组件间传递信息:父组件传子组件数据通过 props 接收,子组件调用父组件通过事件的方式调用父组件中的函数
- 传数据,给父组件绑定属性通过
:名字 = "在setup中定义的变量名" ,{{}} 取值 - template
- v-if:当条件成立此标签显示出来,不成立则隐藏掉。
- v-on:click或 @click 属性:绑定事件。子组件想要修改父组件中的数据,通过绑定事件的方式触发父组件中的函数进而修改父组件中的数据。
@事件名 = ”绑定的函数名“ 。 - v-for 属性:循环,:key循环的每个元素需要有唯一的 key
2. 代码
<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>
.container {
margin-top: 20px;
}
</style>
<template>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-3">
<img class="img-fluid" src="https://lh1.hetaousercontent.com/static/e643aba1d4f5d678" alt="">
</div>
<div class="col-9">
<div class="username">{{fullName}}</div>
<div class="fans">粉丝:{{user.followerCount}}</div>
<button @click="follow" v-if="!user.is_followed" type="button" class="btn btn-secondary btn-sm">+关注</button>
<button @click="unfollow" v-if="user.is_followed" type="button" class="btn btn-secondary btn-sm">取消关注</button>
</div>
</div>
</div>
</div>
</template>
<script>
import {computed} from 'vue';
export default {
name: "UserProfileInfo",
props: {
user: {
type: Object,
required: true,
},
},
setup(props,context) {
let fullName = computed(() => props.user.lastName + ' ' + props.user.firstName);
const follow = () => {
context.emit("follow");
}
const unfollow = () => {
context.emit("unfollow");
}
return {
fullName,
follow,
unfollow,
}
}
}
</script>
<style scoped>
img {
border-radius: 50%;
}
.username {
font-weight: bold;
}
.fans {
font-size: 12px;
color: gray;
}
button {
padding: 2px 4px;
font-size: 12px;
}
</style>
<template>
<div class="card">
<div class="card-body">
<div v-for="post in posts.posts" :key="post.id">
<div class="card single-post">
<div class="card-body">
{{post.content}}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "UserProfilePosts",
props: {
posts: {
type: Object,
required: true,
}
}
}
</script>
<style scoped>
.single-post {
margin-bottom: 10px;
}
</style>
<template>
<div class="card edit-field">
<div class="card-body">
<label for="edit-post" class="form-label">评论</label>
<textarea v-model='content' class="form-control" id="edit-post" rows="3"></textarea>
<button @click="post_a_post" type="button" class="btn btn-success btn-sm">发帖</button>
</div>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: "UserProfileWrite",
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-field {
margin-top: 20px;
}
button {
margin-top: 10px;
}
</style>
<template>
<ContentBase>
<div class="row">
<div class="col-3">
<UserProfileInfo @follow="follow" @unfollow="unfollow" :user="user"/>
<UserProfileWrite @post_a_post="post_a_post" />
</div>
<div class="col-9">
<UserProfilePosts :posts="posts"/>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from '../components/ContentBase';
import UserProfileInfo from '../components/UserProfileInfo';
import UserProfilePosts from '../components/UserProfilePosts';
import UserProfileWrite from '../components/UserProfileWrite';
import {reactive} from 'vue';
export default {
name: 'UserProfile',
components: {
ContentBase,
UserProfileInfo,
UserProfilePosts,
UserProfileWrite
},
setup() {
const user = reactive({
id: 1,
username: "Yjx",
lastName: "Y",
firstName: "jx",
followerCount: 0,
is_followed: false,
});
const posts = reactive({
count: 3,
posts: [
{
id: 1,
userId: 1,
content: "今天冲了!",
},
{
id: 2,
userId: 2,
content: "今天冲猛了!",
},
{
id: 3,
userId: 3,
content: "今天冲不动了!",
},
]
})
const follow = () => {
if (user.is_followed) return;
user.is_followed = true;
user.followerCount ++;
}
const unfollow = () => {
if (!user.is_followed) return;
user.is_followed = false;
user.followerCount --;
}
const post_a_post = (content) => {
posts.count ++;
posts.posts.unshift({
id: posts.count,
userId: 1,
content: content,
})
}
return {
user,
follow,
unfollow,
posts,
post_a_post
}
}
}
</script>
<style scoped>
</style>
vuex
维护全局变量的机制,在全局维护一个对象(状态树) state:存储所有数据。 getters:对 state 里的内容需通过计算获得的内容放到 getters 中,只能读取,不能进行修改。 actions:更新 state 的操作。 mutations:对 state 的直接修改(= 操作)放到 mutations。 modules:对state 里面的内容进行拆分,避免 state 内容太多,代码复杂冗余。modules 维护的是 state 里的对象,还属于 state 里,维护时同样具有 getters,actions, mutations 这几个属性
|