npm i -g @vue/cli
vue ui
常见问题1:Windows上运行vue,提示无法加载文件,表示用户权限不足。
解决方案:用管理员身份打开终端,输入set-ExecutionPolicy RemoteSigned,然后输入y
export default对象的属性:
:存放父组件传过来的children。
标签添加scope属性后,不同组件间的css不会相互影响。在App.vue中添加需要导入的包
<template>
<NavBar />
<router-view/>
</template>
<script>
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/js/bootstrap';
import NavBar from './components/NavBar';
export default {
name: "App",
components: {
NavBar,
}
}
</script>
<style>
</style>
在component中添加NavBar.vue,html部分直接利用bootstrap:
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">MySpace</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<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>
<li class="nav-item">
<a class="nav-link disabled">登陆</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">注册</a>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default {
name: "NavBar",
}
</script>
<style scoped>
</style>
由于多个页面存在相似的部分,所以外面在component中创建contentBase.vue,这样在其他页面直接调用countentBase组件即可:
<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>
在homeview.vue中使用contentBase:
<template>
<CountentBase>
首页
</CountentBase>
</template>
<script>
import CountentBase from '@/components/CountentBase.vue'
export default {
name: 'HomeView',
components: {
CountentBase
}
}
</script>
在router/index.js中添加路由:
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 RegisterView from '../views/RegisterView.vue';
import NotFoundView from '../views/NotFoundView.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
添加路由后已经可以按网址目录实现网页的跳转。
将NavBar中a标签全部换为router-link,在页面的NavBar上实现链接的跳转。
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<router-link class="navbar-brand" :to="{name: 'home'}">Myspace</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>
<style scoped>
</style>
<template>
<ContentBase>
<div class="row">
<div class="col-3">
<UserProfileInfo @follow123="follow" @unfollow123="unfollow" :user="user" />
<UserProfileWrite @post_a_post123="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: 'UserList',
components: {
ContentBase,
UserProfileInfo,
UserProfilePosts,
UserProfileWrite
},
// 初始化变量、函数
setup() {
const user = reactive({
id: 1,
username: "YanYuchen",
lastName: "Yan",
firstName: "Yuchen",
followerCount: 0,
is_followed: false,
});
const posts = reactive({
count: 3,
posts: [
{
id: 1,
userId: 1,
content: "今天上了web课真开心",
},
{
id: 2,
userId: 1,
content: "今天上了算法课,更开心了",
},
{
id: 3,
userId: 1,
content: "今天上了acwing ,开心极了",
},
]
});
// 关注函数
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>
UserProfileInfo.vue:
<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/29150_lg_1f2ac240fe.jpg" alt="">
</div>
<div class="col-9">
<div class="username">{{ fullName }}</div>
<div class="fans">粉丝:{{ user.followerCount }}</div>
<!-- v-if判断,没有关注的话就是关注按钮,已经关注的话就是取消关注按钮 -->
<!-- v-on:click或@click属性:绑定事件 -->
<button @click="follow1234" v-if="!user.is_followed" type="button" class="btn btn-secondary btn-sm">+关注</button>
<button @click="unfollow1234" 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,
},
},
// computed:动态计算某个数据
// props:存储父组件传递给子组件的数据
setup(props, context) {
// 从UserProfileView中读取user数据
let fullName = computed(() => props.user.lastName + ' ' + props.user.firstName);
// 关注函数
const follow1234 = () => {
context.emit('follow123'); //context.emit():触发父组件绑定的函数
};
// 取消关注函数
const unfollow1234 = () => {
context.emit("unfollow123"); // 触发父组件UserProfileView中unfollow函数
}
return {
fullName,
follow1234,
unfollow1234,
}
}
}
</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>
UserProfilePosts.vue:
<template>
<div class="card">
<div class="card-body">
<!-- v-for是循环,相当于有多少个post建多少个div -->
<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>
UserProfileWrite.vue;
<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>
<!-- @click绑定函数post_a_post,点击调用函数post_a_post -->
<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: "UserProfileWrite",
setup(props, context) {
let content = ref('');
const post_a_post = () => {
context.emit('post_a_post123', content.value); // 触发父组件中的post_a_post
content.value = "";
};
return {
content,
post_a_post,
}
}
}
</script>
<style scoped>
.edit-field {
margin-top: 20px;
}
button {
margin-top: 10px;
}
</style>