0
点赞
收藏
分享

微信扫一扫

Web学习(十一) Vue

一只1994 2022-08-02 阅读 66

Web学习(十一) Vue

1.配置环境

1.1 安装Nodejs

1.2 安装@vue/cli

npm i -g @vue/cli

1.3 启动vue自带的图形化项目管理界面

vue ui

常见问题1:Windows上运行vue,提示无法加载文件,表示用户权限不足。
解决方案:用管理员身份打开终端,输入set-ExecutionPolicy RemoteSigned,然后输入y

2.基本概念

script部分

export default对象的属性:

  • name:组件的名称
  • components:存储中用到的所有组件
  • props:存储父组件传递给子组件的数据
  • watch():当某个数据发生变化时触发
  • computed:动态计算某个数据
  • setup(props, context):初始化变量、函数
    • ref定义变量,可以用.value属性重新赋值
    • reactive定义对象,不可重新赋值
    • props存储父组件传递过来的数据
    • context.emit():触发父组件绑定的函数

template部分

  • <slot></slot>:存放父组件传过来的children。
  • v-on:click或@click属性:绑定事件
  • v-if、v-else、v-else-if属性:判断
  • v-for属性:循环,:key循环的每个元素需要有唯一的key
  • v-bind:或::绑定属性

style部分

  • <style>标签添加scope属性后,不同组件间的css不会相互影响。

第三方组件

  • view-router包:实现路由功能。
  • vuex:存储全局状态,全局唯一。
    • state: 存储所有数据,可以用modules属性划分成若干模块
    • getters:根据state中的值计算新的值
    • mutations:所有对state的修改操作都需要定义在这里,不支持异步,可以通过$store.commit()触发
    • actions:定义对state的复杂修改操作,支持异步,可以通过$store.dispatch()触发。注意不能直接修改state,只能通过mutations修改state。
    • modules:定义state的子模块

项目组成

  • views:用于写各个页面,每个页面对应一个 view,里面也可以写组件。 components:组件。
  • router:路由。
  • main.js:整个的入口,根组件,将根组件挂载到 index.html 里。
  • .vue:每个页面是一个 .vue 文件,是 vue 自定义的文件类型,每个 .vue 文件都有三部分组成,分别是 html,css 和
    js。每个 .vue 文件都会 export 一个对象。
    在这里插入图片描述

3.Vue3实战

安装插件后,启动项目
在这里插入图片描述
在输入栏找到运行地址后打开网页。
在这里插入图片描述

3.1 实现导航栏

在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>

3.2 实现contentBase组件

由于多个页面存在相似的部分,所以外面在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>

3.3 编写LoginView.vue,notFoundView.vue,RegisterView.vue等,并添加路由

在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>

在这里插入图片描述

3.4 实现UserProfileInfo,UserProfilePosts与UserProfileWrite:

在这里插入图片描述
用户动态主页面UserProfileView.vue:

<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>

举报

相关推荐

0 条评论