IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 实战个人博客 -> 正文阅读

[JavaScript知识库]实战个人博客

1.知识点

1.1.定义属性和方法(响应式)

<script setup>
import {ref} from "vue"
const name=ref("hahaha");
const age=ref(18);
const handleClick=()=>{
    //在js中取值要.value,而在template中直接拿
    name.value="响应式属性";
}
</script>
    
<template>
    <div>
        <p>{{name}}-{{age}}</p>
        <button @click="handleClick">click me</button>
    </div>
</template>

1.2.ref和reactive

区别:
1.ref可以对基本数据类型(数值,字符串,布尔等)、对象、数组定义,并是数据驱动的形式
2.reactive仅仅可以对对象、数组进行实时渲染,不能对基本数据类型进行定义(即,以后定义对象用reactive)
//对象和数组
//ref取值:
refData.value.name
//reactive取值:
reactiveData.name

1.3.v-for,v-model

<script setup>
import { ref } from "vue"
const names = ref([{ name: "小猫" }, { name: "小狗" }, { name: "小明" }, { name: "小红" }, { name: "小猪" }])
const search=ref("");
</script>
    
<template>
    <div>
        <input type="text" v-model="search" />
        <!-- input textarea select -->
        <p>{{search}}</p>
        <p v-for="(item,index) in names" :key="index">{{item.name}}</p>
    </div>
</template> 

1.4.搜索实战案例

<script setup>
import { ref ,computed} from "vue"
const names = ref([{ name: "小猫" }, { name: "小狗" }, { name: "小明" }, { name: "小红" }, { name: "小猪" }])
const search=ref("");
const matchNames=computed(() => {
    return names.value.filter((item)=>item.name.includes(search.value))
})
</script>
    
<template>
    <div>     
        <input type="text" v-model="search" />   
        <p v-for="(item,index) in matchNames" :key="index">{{item.name}}</p>
    </div>
</template>      

1.5.watch和watchEffect

<script setup>
import { ref ,computed, watch, watchEffect} from "vue"
const search=ref("");
//实时监听search属性的变化
watch(search,(newSearch,preSearch)=>{
    console.log("watch函数触发了",newSearch,preSearch)
})
//相当于load事件,仅仅执行一次
// watchEffect(()=>{
//     console.log("watchEffect函数触发了")
// })
//search发生变化时就会执行
watchEffect(()=>{
    console.log("watchEffect函数触发了",search.value)
})
</script>
    
<template>
    <div>     
        <input type="text" v-model="search" />         
    </div>
</template>      

1.6.组件通信

1.6.1.父传子

//父传子 ,在template中直接将值传给子组件,用于通信的属性不需要冒号
//在父组件中
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>  
  <HelloWorld msg="Vite + Vue" />
</template>

//在子组件中
<script setup>
import { ref } from 'vue'
//接收从父组件传过来的值
defineProps({
  msg: String
})
//自定义属性
const count = ref(0) 
</script>
<template>
<h1>{{ msg }}</h1>
<button type="button" @click="count++">count is {{ count }}</button>
</template>
==================================================================
//父传子 ,在js中定义的属性传给子组件,用于通信的属性需要冒号
//在父组件中
<script setup>
import { ref } from 'vue'
import PostList from "../components/PostList.vue"
const posts = ref([{ title: "学习前端", body: "学习vue", id: 1 }, { title: "学习后端", body: "学习.net core", id: 2 }])
</script>
    
<template>
    <PostList :posts="posts" />
</template>
//在子组件中
<script setup>
defineProps({
    posts: Array
})
</script>
    
<template>
    <div class="post-list">
        <div v-for="item in posts" :key="item.id">
            <h3>{{item.body}}</h3>
        </div>
    </div>
</template>

1.6.2.子传父

见另一篇博文

1.7.钩子函数(onMounted,onUnmounted,onUpdated)

//数据请求,以下两种都可以
onMounted
watchEffect

2.开始项目

2.1.创建vite项目

//在目标文件夹中,cmd
npm init vite@latest vue3-vite-blog
//项目结构:
1.入口文件index.html
2.src中main.js,mount挂载index.html中id为app的容器,所有的组件都会渲染到app这个容器中,App为根组件
3.进入App这个组件中

2.2.配置路由

npm install vue-router
//在src下创建router文件夹,index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
    { path: "/", name: 'Home', component: Home },    
]

const router = createRouter({
    history: createWebHistory(),
    routes
})
export default router;
//在main.js中全局配置路由
import router from "./router"
.use(router)

//在App.vue中
<script setup>
</script>
<template>  
  <router-view></router-view>
</template>

2.3.组件嵌套

//在src下创建一个views文件夹(呈现在页面上的组件,路由跳转的组件)Home.vue
<script setup lang="ts">

</script>
    
<template>
    <div>这是Home.vue页面</div>
</template>
//就可以在App.vue中,显示Home.vue中的内容,因为有路由占位符

2.4.模拟数据json-server

//1.安装json-server
npm install -g json-server
//遇到的问题:权限原因
//解决办法:cmd,管理员身份打开
//2.
在根目录下创建data文件夹,db.json,写json数据
//3.
json-server --watch data/db.json --port=3303
//设置启动的简写方法package.json 中的scripts中加:
"generate":"json-server ./data/db.json"
//启动后端接口:
npm run generate
//生成的接口(可在浏览器中打开) :http://localhost:3303/posts
//可能遇到的问题:json-server : 无法加载文件 D:\My_install\nodejs\node_global\json-server.ps1,因为在此系统上禁止运行脚本
解决方案:
以管理员身份运行powershell(快捷键:windows+X)
set-executionpolicy remotesigned
y

2.5.数据请求封装

//在src下创建文件夹composibles,创建getPosts.js文件
import { ref } from 'vue'
import axios from "axios"
const getPosts = () => {
    const posts = ref([])
    const load = async () => {
        try {
            let { data } = await axios(" http://localhost:3303/posts")
            posts.value = data
        } catch (error) {
            console.log(error)
        }
    }
    return {posts,load}
}
export default getPosts
//在src下创建文件夹composibles,创建getPost.js文件,根据传来的id请求数据
import { ref } from "vue";
import axios from "axios";
const getPost = (id) => {
  const post = ref({});
  const load = async () => {
    try {
      let { data } = await axios(" http://localhost:3303/posts/"+id);
      post.value = data;
    } catch (error) {
      console.log(error);
    }
  };
  return { post, load };
};
export default getPost;

2.6.Home.vue嵌套PostList.vue组件,并将请求数据传递过去

//在Home.vue中
<script setup>
import PostList from "../components/PostList.vue";
import getPosts from "../composibles/getPosts";
const {posts,load} = getPosts();
load();

</script>
    
<template>
    <div class="Home">
        <div v-if="posts.length" class="layout">
            <PostList :posts="posts" />
        </div>
        <div v-else>
            加载中...
        </div>
    </div>
</template>
//在PostList.vue中
<script setup>
defineProps({
    posts: Array
})
</script>    
<template>
    <div class="post-list">
        <div v-for="item in posts" :key="item.id">
            {{item.title}}            
        </div>
    </div>       
</template>

2.7.PostList.vue嵌套Singlepost.vue组件(第二层组件嵌套;在js中使用从父组件传过来的属性,得用一个变量接收才可以使用)

//父组件(PostList.vue)
<script setup>
import SinglePost from "../components/SinglePost.vue"
defineProps({
    posts: Array
})
</script>    
<template>
    <div class="post-list">
        <div v-for="item in posts" :key="item.id">
            <SinglePost :post="item" />
        </div>
    </div>
</template>

//子组件(Singlepost.vue)
<script setup>
import { computed } from "vue";
//接受来自PostList.vue传来的值,数组中的对象
const props = defineProps({
  post: Object,
});
//要想用到传过来的属性值,得用一个变量接收,即props
const snippet = computed(() => {
  return props.post.body.substring(0, 100) + "...";
});
</script>

<template>
  <div class="post">
    <h3>{{ post.title }}</h3>    
    <p>{{ snippet }}</p>
    <span v-for="tag in post.tags" :key="tag">#{{ tag }}</span>
  </div>
</template>

2.8.显示详情页

2.8.1.跳转并传递参数

//1.点击Singlepost.vue中的post.title,页面跳转且跳转到对应的id下的详情页里,即涉及传参,传参的路由跳转 :
//1.在SinglePost.vue里
<template>
  <div class="post">
     <!-- 传参,需要一个绑定: -->
     <router-Link :to="{ name: 'Details', params: { id: post.id } }">
      <!-- 以下两种写下是一样的 -->
      <!-- <router-Link :to="{ name: 'Details' }">    -->
      <!-- <router-Link to="/posts"> -->
      <h3>{{ post.title }}</h3>
    </router-Link>
    <!-- <p>{{ post.body }}</p> -->
    <p>{{ snippet }}</p>
    <span v-for="tag in post.tags" :key="tag">#{{ tag }}</span>
  </div>
</template>
//2.配置路由
{ path: "/posts/:id", name: 'Details', component: Details}, 
//3.在Details.vue中显示
<script setup>
defineProps({
  id: Number,
});
</script>
<template>
  <div class="detail">details--{{ id }}</div>
</template>

2.8.2.根据传过来的id请求对应的数据

//在composibles中创建getPost.vue
import { ref } from "vue";
import axios from "axios";
const getPost = (id) => {
  const post = ref({});
  const load = async () => {
    try {
      let { data } = await axios(" http://localhost:3303/posts/"+id);
      post.value = data;
    } catch (error) {
      console.log(error);
    }
  };
  return { post, load };
};
export default getPost;

//在Detail.vue组件中
<script setup>
import getPost from "../composibles/getPost";
import axios from "axios";
import { useRoute, useRouter } from "vue-router"; //用来接收路由传来的参数,等价于defineProps

//接收参数(这种是路由传参),方法一
const route = useRoute(); //接收SinglePost.vue传来的参数,这种在路由中不用加props:true
const router = useRouter();
const { post, load } = getPost(route.params.id);

// // 接收参数(路由传参和父传子都可以),方法二
// const props = defineProps({
//   id: String,
// });
// // 这里又遇到了那个问题,即js中要用到传过来的参数,得用一个变量进行接收
// const { post, load } = getPost(props.id);//这种需要在路由中加props:true

load();
</script>

<template>
  <div class="post" v-if="post">
    <h3>{{ post.title }}</h3>
    <p class="pre">{{ post.body }}</p>
  </div>
  <div v-else>
    加载中...
  </div>
</template>

2.9.导航栏

//在components中创建Navbar.vue组件
//在App.vue中导入并在模板中添加组件
<script setup>
import Navbar from './components/Navbar.vue';
</script>
<template>  
<Navbar />
  <router-view></router-view>
</template>

2.10.新建组件博客组件

//在router中的index.ts
import Create from "../views/Create.vue";
 { path: "/create", name: "Create", component: Create },
//提交数据的时候最好采用这种写法:
const title = ref("");
const body = ref("");
const tags = ref([]);
const tag = ref("");
const handleSubmit = async () => {
  // 准备数据
  const post = {
    // id: Math.floor(Math.random() * 100),
    title: title.value,
    body: body.value,
    tags: tags.value,
  };

  const data = await axios.post("http://localhost:3303/posts", post);
  // if (data.status === 201) {
  //   router.push("/");
  // }
  router.push("/");
};

2.11.右侧标签

2.11.1.布局组件

//这个组件是在Home.vue中显示的,所以在Home.vue中引入并添加
import TagCloud from "../components/TagCloud.vue";
<template>
  <div class="Home">
    <div v-if="posts.length" class="layout">
      <PostList :posts="posts" />
      <TagCloud :posts="posts" />
    </div>
    <div v-else>加载中...</div>
  </div>
</template>

//在components下创建TagCloud.vue
<template>
  <div class="tag-cloud">
    <h3>标签</h3>
    <div v-for="tag in tags" :key="tag">      
        #{{ tag }}     
    </div>    
  </div>
</template>

<script setup>
import { ref } from "vue";
//利用数组=====================================================
// const tags = ref([]);
// //若用const tagArray = [];会有重复的标签
// const tagArray = [];

// //只需要传过来的tag,遍历tag数组中的每一个tag,并添加到tagSet中
// props.posts.forEach((item) => {
//   item.tags.forEach((tag) => tagArray.push(tag));
// });
// tags.value = tagArray;
//==================================================

//利用集合====================================================
const tags = ref([]);
const tagSet = new Set();

//只需要传过来的tag,遍历tag数组中的每一个tag,并添加到tagSet中
props.posts.forEach((item) => {
  item.tags.forEach((tag) => tagSet.add(tag));
});
tags.value = [...tagSet]; //通过数组解构
//===================================================
//接收从Home.vue组件中传来的数据
const props = defineProps({
  posts: Array,
});
</script>

2.11.2.点击标签跳转(包含标签内容的所有博客)

//在components下创建TagCloud.vue
<template>
  <div class="tag-cloud">
    <h3>标签</h3>
    <div v-for="tag in tags" :key="tag">
      <router-link :to="{ name: 'Tag', params: { tag } }">
        #{{ tag }}
      </router-link>
    </div>     
  </div>
</template>
//在views中建立Tag.vue组件,用来显示跳转后的页面
<template>
  <div class="tag">
    <div v-if="posts.length">
      <!-- 包含标签中的博客内容 -->
      <PostList :posts="postsWithTag" />
      <!-- 重复利用Home.vue中右侧的标签 -->
      <TagCloud :posts="posts" />
    </div>
  </div>
</template>
<script setup>
import { useRoute } from "vue-router"; //接收路由传来的参数
import getPosts from "../composibles/getPosts";//获取所有数据
import { computed } from "vue";//对获取的所有数据进行过滤
import PostList from "../components/PostList.vue";
import TagCloud from "../components/TagCloud.vue";

const route = useRoute();
// console.log(route.params.tag);//这里打印是来自TagCloud传来的参数

const { posts, load } = getPosts();
load();

//根据传来的tag参数,从所有的数据中进行过滤,p是一个个对象,p.tags是数组
const postsWithTag = computed(() => {
  return posts.value.filter((p) => p.tags.includes(route.params.tag));
});
</script>

2.12.详情中的编辑和删除

//在Details中
<router-link :to="{ name: 'edit' }">编辑</router-link>
  &nbsp; &nbsp;
<button @click="deleteCustomer">删除</button>

const deleteCustomer = async () => {
  ElMessageBox.confirm("确定要删除此条记录?", "提示", {
    confirmButtonText: "是",
    cancelButtonText: "否",
    type: "warning",
  }).then(async () => {
    await axios.delete("http://localhost:3303/posts/" + route.params.id);
    ElMessage({
      type: "success",
      message: "用户删除成功!",
    });
    router.push("/");
  });
  // .catch(() => {
  //   ElMessage({
  //     type: "info",
  //     message: "删除失败!",
  //   });
  // });
};
//在路由index.js中
import Edit from "../views/Edit.vue";
{
    path: "/edit/:id",
    name: "edit",
    component: Edit,
  },
//在Edit.vue中,和新建差不多
<template>
    <div class="create">
        <form @submit.prevent="handleSubmit">
            <label for="title">标题</label>
            <input type="text" v-model="blog.title" required />
            <label for="body">内容</label>
            <textarea v-model="blog.body"></textarea>
            <label for="tag">标签(回车添加标签)</label>
            <input type="text" v-model="tag" @keydown.enter.prevent="handleKeydown" />

            <!-- 显示标签 -->
            <!-- <div v-for="tag in blog.tags" :key="tag" class="pill">
                #{{ tag }}
            </div> -->
            <el-tag v-for="tag in blog.tags" :key="tag" class="mx-1" closable :disable-transitions="false"
                @close="handleClose(tag)">
               #{{ tag }}
            </el-tag>
            <button>确定</button>
        </form>
    </div>
</template>
  
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import axios from "axios";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";

const router = useRouter();
const route = useRoute();

const tagsss = ref<any>([]);
const tag = ref("");
const blog = ref<any>({});

onMounted(async () => {
    // console.log(route.params.id);
    const res = await axios.get("http://localhost:3303/posts/" + route.params.id);
    // console.log(res.data);
    blog.value = res.data;//这也是一个道理,把传来的值赋给一个对象,双向绑定
    tagsss.value = res.data.tags;//把传过来的tags重新赋值给自己定义的tagsss,此处是互联互通的,也就是说tags中的值会随着tagsss的值变而变    
});


const handleKeydown = () => {
    // 当输入的内容,数组中没有时执行
    if (!tagsss.value.includes(tag.value)) {
        tag.value = tag.value.replace(/\s/g, ""); //去掉空格
        tagsss.value.push(tag.value); //此处的tags为什么等于 res.data.tags 
        console.log(blog.value);//为什么此处是即时更新的
    }
    tag.value = "";
};

const handleClose = (tag: string) => {
    tagsss.value.splice(tagsss.value.indexOf(tag), 1)
}

const handleSubmit = async () => {
    console.log(blog.value);
    const data = await axios.put("http://localhost:3303/posts/" + route.params.id, blog.value);
    router.push("/");
    ElMessage({
        message: "修改成功!",
        type: "success",
    });
};
</script>

2.13.加载动画

//拿来直接用,在components中Spinner.vue
<template>
  <div class="spin"></div>
</template>

<style scoped>
.spin {
  display: block;
  width: 40px;
  height: 40px;
  margin: 30px auto;
  border: 3px solid transparent;
  border-radius: 50%;
  border-top-color: #ff8800;
  animation: spin 1s ease infinite;
}

@keyframes spin {
  to {
    -webkit-transform: rotateZ(360deg);
  }
}
</style>
//===================================================
将加载中...均换为<Spinner />
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:43:36  更:2022-09-30 00:47:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 19:51:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码