TOP

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: {
  
 },
    props: {
  
  },
    setup(props, ctx) {
  
  },
});
</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 居多

import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'

配置路由数组, 包含三个属性, 以及除首页外的路由可用按需引入组件

复制代码
// 配置路由数组
// 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 是用于跳转的方法

定义方法即可实现跳转功能

复制代码
 <template>
   <div>
     <button @click="goto">跳转路由</button>
   </div>
 </template>

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>
复制代码

 

posted @   羊驼之歌  阅读(20)  评论(0编辑  收藏  举报
编辑推荐:
· 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算法
点击右上角即可分享
微信分享提示