本文共 13906 字,大约阅读时间需要 46 分钟。
Vue 3.0 的使用需要 VueCli 4.5
更新脚手架
yarn global add @vue/cli
npm install -g @vue/cli
创建项目
vue create 项目名
根据个人情况设置配置
node_modules
: 这个目录存放的是项目的所有依赖,即 npm install 命令下载下来的文件public
: 静态资源index.html
: 主页src
: 这个目录下存放项目的源码,即开发者写的代码放在这里assets
: 静态文件components
: 目录用来存放组件(一些可复用,非独立的页面)App.vue
: 是一个Vue根组件,也是项目的第一个Vue组件main.js
: 整个项目的入口文件package.json
: 定义了项目的所有依赖,包括开发时依赖和发布时依赖.babel.config.js
: 该文件是babel
的配置文件组件组成
{ {counter}}
CounterView.vue
1000
App.vue
CounterView.vue
{ {counter}}
App.vue
CounterController.vue
App.vue
引入 Bootstrap
npm install bootstrap@4.5.2 --save
App.vue
模拟列表内容数据,并将数据传入列表组件
ResourceHome.vue
...setup(){ const data = reactive({ resources: [ { _id: "1", title: "2021 前端面试 | “HTML + CSS + JS”专题", description: "BAT面试1000题——数据结构(841~850题)", type: "video", link: "" }, { _id: "2", title: "一篇搞定前端高频手撕算法题(36道)", description: "《JavaScript 20 年》中文在线版发布", type: "book", link: "" }, { _id: "3", title: "32个手撕JS,彻底摆脱初级前端(面试高频)", description: "56 道高频 JavaScript 与 ES6+ 的面试题及答案", type: "video", link: "" }, { _id: "4", title: "字节跳动2020届秋招提前批前端面经", description: "初入WEB前端的新手,掌握这些核心知识点,年薪冲破20W", type: "book", link: "" }, { _id: "5", title: "前方预警!史上最全前端面试题来袭!(附答案)", description: "做一个数据可视化项目的难点在什么地方?", type: "video", link: "" }, { _id: "6", title: "JavaScript数据类型详解", description: "nodejs的websocket的服务器端是如何实现的?", type: "book", link: "" }, ] }) return { // 解包 ...toRefs(data), }}
ResourceList.vue
{ { resource.type }}{ { resource.title }}
{ { resource.description }}
利用方法实现列表数据的统计
ResourceHome.vue
{ { getResourcesLength()}}...// 列表数量统计方法const getResourcesLength = () => { return data.resources.length}// 导出数据return { getResourcesLength,}
利用计算属性实现列表数据的统计
ResourceHome.vue
{ { getResourcesLength()}}...import { computed } from 'vue';...// 列表数量统计(计算属性实现)const getResourcesLength = computed(() => { return data.resources.length})
v-if/v-else
实现视图切换
...import { ref } from 'vue';...// 3. 定义视图切换属性const isDetailView = ref(true)// 导出数据return { isDetailView}数据
ResourceHome.vue
...// 4. 添加数据事件const addResource = () => { // debugger // 随机获取id const _id = "_" + Math.random().toString(36).slice(2) // 随机获取列表内容类型 const type = ["book","blog","video"][Math.floor(Math.random() * 3)] // 新的列表内容 const newResource = { _id, title:`${ _id} title`, description:`${ _id} description`, link:'', type, } // 添加到数据列表前列 data.resources.unshift(newResource) }
修改切换按钮样式
ResourceHome.vue
...// 5. 切换按钮样式const togglesBtnClass = computed(() => { return !isDetailView.value ? "btn-primary" : "btn-warning"})数据
修改数据列表固定高度
ResourceList.vue
选中ResourceList组件中的列表数据,将点击事件传入ResourceHome父组件
从ResourceHome父组件传入ResourceDetail数据详情组件
ResourceList.vue
{ { resource.type }}{ { resource.title }}
{ { resource.description }}
ResourceHome.vue
ResourceDetail.vue
No Resource is selected :({ { resource.title }}编辑{
{ resource.description }}
选中列表数据改变其样式
ResourceList.vue
{ { resource.type }}{ { resource.title }}
{ { resource.description }}
安装 axios
npm install axis --save
ResourceHome.vue
// 生命周期钩子函数onMounted(async () => { const resources = await fetchResources()});
index.js
import axios from 'axios'// 请求数据方法export function fetchResource(){ return axios.get("https://vue3-fjord-81553.herokuapp.com/api/resources")}
blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
只要不同源,即为跨域
同源策略: 以下任意一个不同,即为跨域
协议头
http https file域名
baidu.com www.taobao.com端口号
8080 441 21 22域名不同 = 跨域
协议不同 = 跨域
端口不同 = 跨域
同源
jsonp
服务器代理
后端允许跨域
vue.config.js
module.exports = { devServer: { // 代理 proxy: { "^/api": { target: "https://vue3-fjord-81553.herokuapp.com", changeOrigin: true, }, }, },};
请求接口数据
index.js
import axios from 'axios'// 请求数据方法export function fetchResources(){ return axios.get("/api/resources").then((res) => res.data)}
数据赋值
ResourceHome.vue
// 生命周期钩子函数onMounted(async () => { const resources = await fetchResources() data.resources = resources});
ResourceUpdate.vue
ResourceHome.vue
watch
监听数据是否变化ResourceUpdate.vue
import { ref, watch } from 'vue'export default { props: { resource: Object }, setup(props, context) { const uResource = ref(props.resource) watch( () => props.resource, (resource, prevResource) => { uResource.value = resource } ) return { uResource } }}
ResourceUpdate.vue
...// 类型选项const types = ["blog","video","book"]
index.js
// 更新数据方法// 传入对应id,及新内容export function updateResource(id, resource){ return axios .patch(`/api/resources/${ id}`, resource) .then((res) => res.data)}
ResourceUpdate.vue
...import { updateResource } from '@/actions'...// 提交事件(异步)const handleUpdate = async () => { // 传入对应id和新内容 const updatedResource = await updateResource(uResource.value._id, uResource.value)}// 将更新的数据传给父组件context.emit("onUpdateResource", updateResource)
ResourceHome.vue
...// 8. 获取更新数据const handleUpdateResource = (newResource) => { // 拿到原先存储数据的数组下标 const index = data.resources.findIndex( (resource) => resource._id === newResource._id ) data.resources[index] = newResource selectResource(newResource)}
ResourceUpdate.vue
{ { alert.success }}{ { alert.error }}...// 封装方法// 初始化弹窗状态const initAlert = () => { return { success: null, error: null }}// 弹窗状态const setAlert = (type, message) => { data.alert = initAlert() data.alert[type] = message}// 提交事件(异步)const handleUpdate = async () => { // 抛出异常 try { // 传入对应id和新内容 const updatedResource = await updateResource( uResource.value._id, uResource.value ) // 将更新的数据传给父组件 context.emit("onUpdateResource", updatedResource) // 提交成功后弹窗信息 setAlert("success","Resource was updated") } catch (error) { setAlert("error", error?.message) } }
ResourceUpdate.vue
setup(props, context) { // 接收数据 const uResource = ref(props.resource) // 类型选项 const types = ["blog","video","book"] // 弹窗信息 const data = reactive({ alert: { success: null, error: null }, // 定义定时器 timeoutId: null }) // 监听数据切换 watch( () => props.resource, (resource, prevResource) => { // 判断改变的值存不存在 if(resource && (resource._id !== prevResource._id)){ // 关闭定时器 clearAlertTimeout() data.alert = initAlert() } uResource.value = resource } ) // 封装方法 // 初始化弹窗状态 const initAlert = () => { return { success: null, error: null } } // 钩子函数 // 离开组件之前调用 onBeforeUnmount(() => { // 清除定时器方法 clearAlertTimeout() }) // 清除定时器方法 const clearAlertTimeout = () => { data.timeoutId && clearTimeout(data.timeoutId) } // 弹窗状态 const setAlert = (type, message) => { data.alert = initAlert() data.alert[type] = message // 设置定时器 data.timeoutId = setTimeout(() => { data.alert = initAlert() },3000) }}
安装路由
yarn add vue-router@next
router.js
import { createRouter, createWebHistory } from "vue-router";import ResourceHome from "@/views/ResourceHome.vue";import ResourceNew from "@/views/ResourceNew.vue";const routes = [ { path: "/", component: ResourceHome }, { path: "/new", component: ResourceNew}];const router = createRouter({ history: createWebHistory(), // routes: routes, routes,});export default router;
main.js
import { createApp } from 'vue'import App from './App.vue'import router from './router'const app = createApp(App);// 挂载app.use(router);app.mount("#app");
router.js
import { createRouter, createWebHistory } from "vue-router";import ResourceHome from "@/views/ResourceHome.vue";import ResourceNew from "@/views/ResourceNew.vue";import ResourceRoutes from "@/views/ResourceRoutes.vue"const routes = [ // ================================================================= // { path: "/", component: ResourceHome }, // { path: "/new", component: ResourceNew} // ================================================================= // 重定向、定义名字 // http://localhost:8080/resources // { // path: "/", // name: "base", // redirect: { name: "resourceHomePage"} // }, // { // path: "/resources", // name: "resourceHomePage", // component: ResourceHome // }, // { // path: "/resources/new", // name: "resourceNewPage", // component: ResourceNew // } // ================================================================= // 子路由 { path: "/", name: "base", redirect: { name: "resourceHomePage"} }, { path: "/resources", name: "resourceHomePage", component: ResourceRoutes, children: [ { path: "", name: "resourceHome", component: ResourceHome, }, { path: "new", name: "resourceNewPage", component: ResourceNew, } ] }];const router = createRouter({ history: createWebHistory(), // routes: routes, routes,});export default router;
Header.vue
首页 添加
router.js
import { createRouter, createWebHistory } from "vue-router";import ResourceHome from "@/views/ResourceHome.vue";import ResourceNew from "@/views/ResourceNew.vue";import ResourceRoutes from "@/views/ResourceRoutes.vue"const routes = [ // 重定向、定义名字 // http://localhost:8080/resources { path: "/", name: "base", redirect: { name: "resourceHomePage"} }, { path: "/resources", name: "resourceHomePage", component: ResourceHome }, { path: "/resources/new", name: "resourceNewPage", component: ResourceNew }];const router = createRouter({ history: createWebHistory(), // routes: routes, routes, linkExactActiveClass: "active",});export default router;
ResourceForm.vue
ResourceUpdate.vue
ResourceNew.vue
ResourceDetailPage.vue
ResourceList.vue
{ { resource.type }}{ { resource.title }}
{ { resource.description }}
ResourceDetail.vue
ResourceDetailPage.vue
ResourceSearch.vue
index.js
// 搜索功能export function searchResources(title) { return axios.get(`/api/resources/s/${ title}`).then((res) => res.data);}
转载地址:http://jcqwi.baihongyu.com/