Vue3.0 实操学习笔记
安装
node.js 安装
https://nodejs.org/en
安装后执行 node -v 查看是否有异常以及 npm -v 查看是否异常
调整为淘宝镜像, cnpm -v 查看是否异常
npm install -g cnpm --registry=https://registry.npm.taobao.org
Vue 安装以及安装脚手架 vue -V 查看是否异常
cnpm i - g vue @vue/cli
创建项目
创建项目命令
vue create [project name]
选择 Manually select feature 或者你自己已经创建过得模式, 然后做出对应的选择, 最后可以保存选项作为选项模板, 下次可以直接使用
启动项目命令
npm run serve
可以自行更改启动命令, 更改的命令在 , 将 serve 改成 自己喜欢的
组件嵌套
组件定义模板
<template></template> <script> export default {}; </script> <style scoped lang="scss"></style>
组件引用的模板
<template></template> <script> import { defineComponent } from "vue"; export default defineComponent({ name: "Home", components: {
},
}); </script> <style scoped lang="scss"></style>
演示代码
目录结构
App.vue
调整浏览器的默认样式
<template> <router-view /> </template> <style lang="scss"> * { margin: 0; padding: 0; } </style>
定义组件 - NavHeader.vue
<template> <div>Header</div> </template> <script> export default {}; </script> <style scoped lang="scss"></style>
定义组件 - NavMain.vue
<template> <div>Main</div> </template> <script> export default {}; </script> <style scoped lang="scss"></style>
定义组件 - NavFooter.vue
<template> <div>Footer</div> </template> <script> export default {}; </script> <style scoped lang="scss"></style>
引用组件 - Home.vue
<template> <div> <nav-header></nav-header> <nav-main></nav-main> <nav-footer></nav-footer> </div> </template> <script> import NavHeader from "@/components/NavHeader.vue"; import NavFooter from "@/components/NavFooter.vue"; import NavMain from "@/components/NavMain.vue"; import { defineComponent } from "vue"; export default defineComponent({ name: "Home", components: { NavHeader, NavMain, NavFooter, }, }); </script> <style scoped lang="scss"></style>
实现效果
三个组件在被引用后可以正确展示即可
组件数据传递
父传子
父组件内的数据 直接在 子组件的标签中绑定的方式去传递
<child :msg="msg" ></child>
子组件接受数据在 porps 中获取, 并进行校验和一些处理, 比如默认值啥的
然后就可以直接拿去使用了
props: { msg: { type: String, required: false, default: "aaaa", }, },
子传父
子组件中用 ctx.emit 进行传递, 定一个一个事件名, 然后在父组件中被使用
可以通过按钮触发传递, 或者起始方法去传递到父组件中
let send = () => { ctx.emit("send", newName.value); }; onMounted(() => { ctx.emit("send", [newName.value, newNum.value]); });
父组件通过子标签中的绑定子组件的事件名, 然后关联自己的一个方法去获取并处理
<child @send="hsend"></child>
代码实例
父组件
<template> <div>下一行展示子组件</div> <div> <!-- 动态绑定属性传递父组件的数据 --> <!-- @send 是子组件的事件, hsend 为父组件内的方法用于接受数据 --> <child :msg="msg" @send="hsend"></child> </div> </template> <script> import { defineComponent, ref } from "vue"; import child from "../components/Child.vue"; export default defineComponent({ name: "About", components: { child, }, setup() { let msg = ref("cibo"); // 接收子组件传递来的数据 let hsend = (val) => { console.log(val); // 这里会打印子组件传递来的 newName }; return { msg, hsend, }; }, }); </script>
子组件
<template> <div> <div>我是子组件, 我接受了一个 msg: {{ msg }}</div> <div> <button @click="send">传值给父组件</button> </div> </div> </template> <script> import { defineComponent, ref, onMounted} from "vue"; export default defineComponent({ name: "child", props: { msg: { type: String, required: false, default: "aaaa", }, }, mits: ["send"], setup(props, ctx) { let newName = ref("haha"); let newNum = ref(10); // 确保 send 函数被返回 let send = () => { // 通过 ctx.emit 分发事件 ctx.emit("send", newName.value); }; // 通用起始事件进行发送, 传递多个 onMounted(() => { ctx.emit("send", [newName.value, newNum.value]); }); // 返回 send 函数和 newName return { newName, send, // 确保 send 函数被返回 }; }, }); </script>
ref 数据渲染
import 引入, 后在 setup 中定义并且 return 在模板中直接使用即可
<template> <div>{{ a }}</div> <div>{{ b }}</div> <div>{{ c[2] }}</div> <div>{{ d.b }}</div> </template> <script> import { defineComponent, ref } from "vue"; export default defineComponent({ name: "Home", components: {}, props: {}, setup(props, ctx) { let a = ref(10); let b = ref("b"); let c = ref(["a", 1, NaN]); let d = ref({ a: 6, b: "b", }); return { a, b, c, d, }; }, }); </script> <style scoped lang="scss"></style>
方法定义
<template> <!-- 调用的方法可以传入参数 --> <div @click="clickNum(100)"> {{ num }} </div> </template> <script> import { defineComponent, ref } from "vue"; export default defineComponent({ name: "Home", setup() { let num = ref(1); // 定义方法 let clickNum = (val) => { // 访问ref 的属性需要加 .value console.log("点击了 num ", num.value); console.log("传入参数为", val); }; return { num, // 方法也要传出, 否则无法使用 clickNum, }; }, }); </script> <style scoped lang="scss"></style>
计算属性
<template> <div>{{ n1 }} + {{ n2 }} = {{ addNum }}</div> <button @click="add">点击两个数字都加1</button> </template> <script> import { defineComponent, ref, computed } from "vue"; export default defineComponent({ name: "Home", setup() { let n1 = ref(1); let n2 = ref(10); let add = () => { n1.value++; n2.value++; }; // 实际使用中计算属性最好只作为只读, 不要对其进行修改 let addNum = computed(() => { // 必须返回一个值 return n1.value + n2.value; }); return { n1, n2, addNum, add, }; }, }); </script> <style scoped lang="scss"></style>
实现效果
点一下两个数字都+1。计算属性的变量也跟着改变
状态管理
路径在 src/store 下的 index.js 中
内部包含了5个属性分别各司其职
属性简述
import { createStore } from 'vuex' export default createStore({ // 定义所需要的状态, 此处定义的数据全局都可以使用, 所有组件内都可以 state: { name: "cibo", users: [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' } ] }, // 定义方法用于状态对应的计算属性 getters: { // 获取用户总数 userCount(state) { return state.users.length; }, // 根据 ID 获取用户 getUserById: (state) => (id) => { return state.users.find(user => user.id === id); } }, // 同步修改 state 的方法, 只有此处的方法可以去修改 state 的数据 mutations: { // 第一个参数为 state 对象, 第二个参数为修改参数, 或者不提供也行 setName(state, newVal) { state.name = newVal } }, // 异步提交调用 mutations 的方法, 比如发请求, 定时器 actions: { // 第一个参数为 store 对象, 第二个为实际业务参数 asyncSetName(store, params) { setTimeout(() => { // 提交 mutations(调用) store.commit('setName', params) }, 2000) } }, // 模块化, 高阶用法, 有空在写吧 modules: { } })
使用方法
引入后使用 useStore 方法即可
import { useStore } from "vuex";
let store = useStore();
console.log(store);
store 的相关的方法属性
其中 state 中就是定义的数据信息, 但是不可以直接拿来使用 (置灰的)
若想使用则需要借助计算属性, 导入后然后定义方法去取对应的值后赋予变量进行返回
import { defineComponent, ref, computed } from "vue";
let list = computed(() => {
return store.state.list;
});
return {
store,
list,
};
完整的代码样例
<template> <div> <nav-header></nav-header> <nav-main></nav-main> <nav-footer></nav-footer> <div>{{ list }}</div> <div>{{ list[0] }}</div> </div> </template> <script> import NavHeader from "@/components/NavHeader.vue"; import NavFooter from "@/components/NavFooter.vue"; import NavMain from "@/components/NavMain.vue"; import { useStore } from "vuex"; import { defineComponent, ref, computed } from "vue"; export default defineComponent({ name: "Home", components: { NavHeader, NavMain, NavFooter, }, setup() { let store = useStore(); let list = computed(() => { return store.state.list; }); console.log(store); return { store, list, }; }, }); </script> <style scoped lang="scss"></style>
路由
将一个组件作为一个路由, 即路由组件, 路径在 src/router/index.js 中
定义路由
引入模块, 创建路由 / 创建history路由 / 创建哈希路由
哈希路由会在路径前加 /#/, 一般情况会选择 history 居多
配置路由数组, 包含三个属性, 以及除首页外的路由可用按需引入组件
// 配置路由数组 // path 路径, 必须以 / 开头, 必填 // component 组件. 必填 // name 命名 , 可选 const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/test', name: 'test', component: TestView }, { path: '/about', name: 'about', // 按需引入, 如果未被访问就不会加载, 节约性能 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ]
定义路由对象
// 创建路由对象 const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes })
完整代码
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import TestView from '../views/Test.vue' import HomeView from '../views/Home.vue' // 配置路由数组 // path 路径, 必须以 / 开头, 必填 // component 组件. 必填 // name 命名 , 可选 const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/test', name: 'test', component: TestView }, { path: '/about', name: 'about', // 按需引入, 如果未被访问就不会加载, 节约性能 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ] // 创建路由对象 const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router
使用路由
引入方法
import { useRoute } from "vue-router";
定义全局路由对象
let router = useRouter();
可以打印他的属性方法看到有个push 是用于跳转的方法
定义方法即可实现跳转功能
let goto = () => { // push 方法实现跳转, 直接传入跳转路径 router.push("/about"); }; return { goto, };
其他方法比如
back: 回退到上一页
forward: 去到下一页
go(int): 传入正数代表前进, 负数代表后退
传递参数
push 方法使用对象定义, query 参数可以传递参数, 复杂对象用json 转义一下字符串后传递
router.push({ path: "/home", query: { name: name.value, age: age.value, // 复杂对象最好用 json 转一下 obj: JSON.stringify(obj), }, }); };
跳转后的url 会成为这样
接收参数
useRoute 方法定义当前路由
import { useRouter, useRoute } from "vue-router"; // 当前路由对象 let route = useRoute(); console.log(route);
console.log(route.query);
打印在 query 中即可拿到参数, 注意query 中拿到的参数都是字符串类型
不论是布尔还是数字还是对象
对象用 JSON.parse 转一下
完整的代码示例
实现一个路由跳转的功能
src/router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import StartView from '../views/Start.vue' // 配置路由数组 // path 路径, 必须以 / 开头, 必填 // component 组件. 必填 // name 命名 , 可选 const routes = [ { path: '/', name: 'start', component: StartView }, { path: '/home', name: 'home', component: () => import('../views/Home.vue') }, { path: '/about', name: 'about', // 按需引入, 如果未被访问就不会加载, 节约性能 component: () => import('../views/About.vue') } ] // 创建路由对象 const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router
src/views/start.vue
<template> <div> <button @click="goto">跳转路由</button> </div> </template> <script> import { useRouter, useRoute } from "vue-router"; import { defineComponent, ref, computed } from "vue"; export default defineComponent({ name: "Home", components: {}, setup() { // 全局路由对象 let router = useRouter(); console.log(router); // 当前路由对象 let route = useRoute(); // 拿到传递参数 console.log(route.query); // 测试拿到的数据正常展示 let name = ref(""); let age = ref(null); let obj = ref({}); name.value = route.query.name; age.value = route.query.age * 1; obj.value = JSON.parse(route.query.obj); console.log(name.value); console.log(age.value); console.log(obj.value.msg); let goto = () => { // push 方法实现跳转, 直接传入跳转路径 router.push("/about"); }; return { goto, }; }, }); </script> <style scoped lang="scss"></style>
生命周期
-
beforeCreate
: 组件实例被创建,数据观测和事件配置尚未完成。 -
created
: 组件实例已创建,数据观测和事件配置已完成,但 DOM 尚未挂载。 -
beforeMount
: 在挂载开始之前被调用,相关的 render 函数首次被调用。 -
mounted
: 组件已挂载到 DOM 中,所有的子组件也已挂载。 -
beforeUpdate
: 组件数据更新之前被调用,适合在此时进行一些操作。 -
updated
: 组件数据更新后被调用,DOM 也已更新。 -
beforeUnmount
: 组件实例被卸载之前调用,适合在此时进行清理工作。 -
unmounted
: 组件实例已卸载,所有的事件监听器和子组件也已被销毁。
完整的案例 - 任务列表
实现一个任务列表,. 可以添加删除和批量清除, 具备路由功能和父子传值以及数据存储在store并可进行修改
src\router\index.js 路由
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import StartView from '../views/Start.vue' // 配置路由数组 // path 路径, 必须以 / 开头, 必填 // component 组件. 必填 // name 命名 , 可选 const routes = [ { path: '/', name: 'start', component: StartView }, { path: '/home', name: 'home', component: () => import('../views/Home.vue') } ] // 创建路由对象 const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router
src\store\index.js 状态文件
import { createStore } from 'vuex' export default createStore({ state: { list: [ { title: "吃大饭", complete: false, }, { title: "睡大觉", complete: false, }, { title: "打豆豆", complete: true, }, ] }, getters: { }, mutations: { // 添加到队列 addTodo(state, data) { state.list.push(data) }, // 删除队列中的元素 delTodo(state, index) { state.list.splice(index, 1) }, // 清除已完成, 将传入的数据直接覆盖保存 clear(state, data) { state.list = data } }, actions: { }, modules: { } })
src\views\start.vue 首页
<template> <!-- 开始页面 --> <div> <button @click="start">开始任务</button> </div> </template> <script> import { useStore } from "vuex"; import { useRouter } from "vue-router"; import { defineComponent, ref, computed } from "vue"; export default defineComponent({ name: "Start", setup() { // 全局路由对象 let router = useRouter(); let name = ref("cibo"); let age = ref(18); let obj = ref({ msg: "success", }); let start = () => { // push 方法实现跳转, 直接传入跳转路径 router.push({ path: "/home", query: { name: name.value, age: age.value, // 复杂对象最好用 json 转一下 obj: JSON.stringify(obj.value), }, }); }; return { start, }; }, }); </script> <style scoped lang="scss"></style>
src\views\home.vue 主页
<template> <div> <NavHeader @add="add"></NavHeader> <NavMain :list="list" @del="del"></NavMain> <NavFooter :list="list" @clear="clear"></NavFooter> </div> </template> <script> import { defineComponent, ref, computed } from "vue"; import NavFooter from "@/components/NavFooter.vue"; import NavMain from "@/components/NavMain.vue"; import NavHeader from "@/components/NavHeader.vue"; import { useStore } from "vuex"; import store from "@/store"; export default defineComponent({ name: "Home", components: { NavHeader, NavMain, NavFooter, }, emits: ["del"], setup() { store = useStore(); let value = ref(""); let list = computed(() => { return store.state.list; }); // 1. 添加任务 let add = (val) => { // 拿到传递过来的数据进行保存 value.value = val; // 判断是否已存在,已存在就不添加了 let flag = true; list.value.map((item) => { if (item.title === value.value) { // 存在重复 flag = false; alert("任务已存在"); } }); // 不重复的情况下进行添加 if (flag) { // 将数据添加到 store 中, 通过 store.commit 调用 mutations 中的修改方法 store.commit("addTodo", { title: value.value, complete: false, }); } }; // 2. 删除任务 let del = (index) => { store.commit("delTodo", index); }; // 3. 清除任务 let clear = (arr) => { store.commit("clear", arr); }; return { value, list, add, del, clear, }; }, }); </script> <style scoped lang="scss"></style>
src\components\NavHeader.vue 子组件头部
<template> <input placeholder="请输入任务" v-model="value" @keydown.enter="enter" /> </template> <script> import { defineComponent, ref } from "vue"; export default defineComponent({ name: "narHeader", setup(props, ctx) { let value = ref(""); // 按回车确认 let enter = () => { // 把输入框的内容传递给父组件 ctx.emit("add", value.value); // 清空输入框 value.value = ""; }; return { value, enter, }; }, }); </script> <style scoped lang="scss"> input { margin-bottom: 10px; } </style>
src\components\NavMain.vue 子组件中部
<template> <div v-if="list.length > 0"> <div v-for="(item, index) in list" :key="index"> <div class="item"> <input type="checkbox" v-model="item.complete" /> {{ item.title }} <button class="del" @click="del(item, index)">删除</button> </div> </div> </div> <div v-else>暂无任务</div> </template> <script> import { defineComponent, ref } from "vue"; export default defineComponent({ name: "narMain", props: { list: { type: Array, required: true, }, }, setup(props, ctx) { // 删除 let del = (item, index) => { // 将要删除的索引提交给父组件 ctx.emit("del", index); }; return { del, }; }, }); </script> <style scoped lang="scss"> .item { width: 200px; height: 35px; line-height: 35px; position: relative; // 变成手图标 cursor: pointer; button { top: 6px; right: 20px; position: absolute; display: none; z-index: 99; } &:hover { background: #ddd; button { display: block; } } } </style>
src\components\NavFooter.vue 子组件尾部
<template> <div class="container"> <div>已完成 {{ isComplete }} / 全部 {{ list.length }}</div> <button class="btn" v-if="isComplete > 0" @click="clear">清除已完成</button> </div> </template> <script> import { defineComponent, ref, computed } from "vue"; export default defineComponent({ name: "narFooter", props: { list: { type: Array, required: true, }, }, setup(props, ctx) { let isComplete = computed(() => { let arr = props.list.filter((item) => { return item.complete; }); return arr.length; }); // 清除已完成 let clear = () => { // 过滤未完成的 let arr = props.list.filter((item) => { return item.complete === false; }); ctx.emit('clear', arr) }; return { isComplete, clear, }; }, }); </script> <style scoped lang="scss"> .container { display: flex; // 居中 align-items: center; .btn { margin-left: 10px; } } </style>
本文来自博客园,作者:羊驼之歌,转载请注明原文链接:https://www.cnblogs.com/shijieli/p/18562489
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
2019-11-25 机器学习 - 算法 - PCA 主成分分析
2019-11-25 机器学习 - 算法 - 聚类算法 K-MEANS / DBSCAN算法