四,vue3的使用(1)

132 阅读1分钟

一,修改app.vue文件

<template>
  <router-view></router-view>
</template>

<style>
    * {
           margin: 0;
           padding: 0;
      }

    html,
    body {
            width: 100%;
            height: 100%;
    }
    #app {
            width: 100%;
            max-width: 100%;
            height: 100%;
            padding: 0;
            background: rgb(249, 249, 249);
    }
</style>

二,vue3+ts 使用 (login页面为例)

Snipaste_2022-08-23_14-56-37.png

1.在src文件夹的view文件夹下新建login.vue

给出页面结构加样式:

<template>
  <div class="login">
    <div class="login_content">
      <div class="login_content_left">
      </div>
      <div class="login_content_right">
        <div class="login_content_header">
          <div class="login_content_header_img">
            <img src="@/assets/image/logo.png" alt="" />
          </div>
          <p class="login_content_header_tit">Login to your account</p>
        </div>
        <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleFromRef" label-width="100px"
          class="login-ruleForm">
          <el-form-item label="" prop="phone" label-width="0px">
            <div class="el-form-item-content">
              <el-input v-model="ruleForm.phone" placeholder="phone">
                <img slot="prefix" src="@/assets/image/user.png" alt="" />
              </el-input>
            </div>
          </el-form-item>
          <el-form-item label="" prop="password" label-width="0px">
            <div class="el-form-item-content">
              <el-input type="password" v-model="ruleForm.password" placeholder="password">
                <img slot="prefix" src="@/assets/image/lock.png" alt="" />
              </el-input>
            </div>
          </el-form-item>
          <div class="rember">
            <div class="remberPass">
              <el-checkbox v-model="isRemPasFlag" @change="remmberPassword">Remember me</el-checkbox>
            </div>
            <el-button @click="submitForm('ruleForm')"
              style="width: 368px; height:40px; margin-top: 28px;background: rgb(66, 34, 241);color:#fff;color: rgb(255, 255, 255);font-weight: bold;border-radius: 6px;">
              LOGIN</el-button>
          </div>
          <div class="read">
            <div class="remberPass">
              <el-checkbox v-model="isread">Read and agree </el-checkbox>
            </div>
            <span @click="watchAgreement">《 the Demo agreement 》</span>
          </div>
        </el-form>
      </div>
    </div>
  </div>
</template>

<style lang='scss'>
  .login_content {
    .el-form-item {
      margin-bottom: 24px;

      .el-form-item__content {
        margin-left: 0;
      }
    }

    .read {
      display: flex;
      align-items: center;
      font-weight: 400;

      >span {
        color: #1989fa;
        font-size: 14px;
        cursor: pointer;
      }
    }

    .el-checkbox__label {
      font-size: 14px;
      font-weight: 400;
      color: rgba(0, 0, 0, 0.65);
      line-height: 14px;
    }
  }
</style>
<style lang="scss">
  .content_right_con {
    background-color: transparent;
    box-shadow: none;
  }

  .login {
    width: 100%;
    height: 100%;
    // background: #F0F3F7;
    display: flex;
    justify-content: center;
    align-items: center;
    background: url("@/assets/image/bg.png");
    background-size: 100%;
    box-sizing: border-box;
    position: relative;

    .rember {
      margin-top: 28px;

      .remberPass {
        width: 80px;
      }
    }

    .read {
      margin-top: 28px;
    }

    .login_content {
      margin: 0 auto;
      width: 1000px;
      height: 600px;
      // background: url("@/assets/image/login_bg.png") no-repeat 0 0;
      // background-size: 100% 100%;
      display: flex;
      padding: 14px;
      box-sizing: border-box;

      .login_content_left {
        width: 380px;
        background: url("@/assets/image/login_left.png") no-repeat 0 0;
        background-size: 100% 100%;
        padding-left: 30px;
        box-sizing: border-box;
      }

      .login_content_right {
        background-color: #ffffff;
        width: 620px;
        padding: 75px 0 68px 0;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        align-items: center;
        border-top-right-radius: 40px;
        border-bottom-right-radius: 40px;
      }

      .login_content_header {
        margin-bottom: 27px;

        .login_content_header_img {
          display: flex;
          align-items: center;
          justify-content: center;
          margin-bottom: 25px;

          img {
            width: 60px;
            height: 60px;
          }
        }

        .login_content_header_tit {
          color: #000000;
          line-height: 25px;
          margin-top: 12px;

        }
      }

      .el-form-item-content {
        width: 400px;
        border-radius: 8px;
      }
    }

    .login_project {
      position: absolute;
      top: 40px;
      left: 40px;
      display: flex;
      align-items: center;

      img {
        width: 36px;
        height: 40px;
      }

      span {
        margin-left: 10px;
        font-size: 28px;
        font-weight: 500;
        color: rgba(0, 0, 0, 0.85);
        line-height: 40px;
      }
    }
  }
</style>

vue3中,支持多个根节点哦,vue2写多啦布局的时候就惯性思维一个根节点啦,大家可以去试试多个根节点

2.重点分析script (这里用的是vue3的setup组合式哦)

  • [ setup 使用]

注意:

setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行。

基本用法:(照搬vue3官方)

<template>
  <button @click="count++">{{ count }}</button>
</template>
<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    // 返回值会暴露给模板和其他的选项式 API 钩子
    return {
      count
    }
  },

  mounted() {
    console.log(this.count) // 0
  }
}
</script>

单文件组件 <script setup>

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 TypeScript 声明 props 和自定义事件。
  • 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
  • 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。

顶层的绑定会被暴露给模板

当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用:

ref()

ref 对象是可更改的,也就是说你可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪,并且写操作会触发与之相关的副作用。

reactive()

返回一个对象的响应式代理 当数据是对象时可以通过.属性进行赋值 注意当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的

<template>
  <button @click="count++">{{ count }}</button>
  <div>{{ name }}</div>
  <button @click="editName">修改姓名</button>
</template>

<script setup lang="ts">
  import { ref, reactive } from "vue";
  let count = ref(0)
  
  let name = ref('张三')
  const editName = ()=>{
      name.value = '李四'
  }
  const obj = reactive({ count: 0 })
  obj.count++
  
  let meunList = reactive([])
  meunList.push(...data)
  
</script>

vuex的组合式API

可以通过调用 useStore 函数,来在 setup 钩子函数中访问 store。这与在组件中使用选项式 API 访问 this.$store 是等效的。

访问 Action

只需要在 setup 钩子函数中调用 dispatch 函数。

import { useStore } from "vuex";
const store = useStore();
const submitLogin = await store.dispatch("login/login", ruleForm);

Vue Router 和 组合式 API

引入 setup 和 Vue 的组合式 API,开辟了新的可能性,但要想充分发挥 Vue Router 的潜力,我们需要使用一些新的函数来代替访问 this 和组件内导航守卫。

在 setup 中访问路由和当前路由

因为我们在 setup 里面没有访问 this,所以我们不能再直接访问 this.$router 或 this.$route。作为替代,我们使用 useRouter 函数:

import { useRouter, useRoute } from 'vue-router';
const router = useRouter(), route = useRoute()
router.push({
   name: 'search',
   query: {
      ...route.query,
   },
})

login页完整script:

<script setup lang="ts">
  import { ref, reactive } from "vue";
  import { useStore } from "vuex";
  import { ElMessage } from "element-plus";
  import { useRouter, useRoute } from 'vue-router';
  const router = useRouter(), route = useRoute()
  const store = useStore();
  const ruleFromRef = ref();
  let ruleForm = reactive({
    phone: "",
    password: "",
  });
  let validatePass = (rule: any, value: any, callback: any) => {
    if (value === "") {
      callback(new Error("请输入密码"));
    } else {
      callback();
    }
  };
  let validateUser = (rule: any, value: any, callback: any) => {
    if (value === "") {
      callback(new Error("请输入用户名"));
    } else if (value.length < 6 || value.length > 15) {
      callback(new Error("用户名为6-15位"));
    } else {
      if (ruleForm.phone !== "") {
        // this.$refs.ruleForm.validateField('phone');
      }
      callback();
    }
  };

  let rules = reactive({
    phone: [
      {
        validator: validateUser,
        trigger: "blur",
      },
    ],
    password: [
      {
        validator: validatePass,
        trigger: "blur",
      },
    ],
  });
  let isRemPasFlag = ref(false);
  let isread = ref(true);
  const remmberPassword = () => { };
  const watchAgreement = () => {
    window.open("协议连接");
  };
  const submitForm = () => {
    ruleFromRef.value.validate(async (valid: any) => {
      // this.$refs.ruleForm.validateField('phone')
      if (valid) {
        if (!isread) {
          ElMessage.error("请勾选服务协议");
          return;
        }
        const submitLogin = await store.dispatch("login/login", ruleForm);
        if (submitLogin) {
          localStorage.setItem("password", ruleForm.password);
          localStorage.setItem("phone", ruleForm.phone);
          localStorage.setItem("token", submitLogin.token);
          localStorage.setItem("name", submitLogin.name);
          localStorage.setItem("avatar_url", submitLogin.avatar_url);
          router.push('/index')
          ElMessage.success({
            message: "登录成功",
            duration: 3 * 1000,
          });
        }

      } else {
        console.log("error submit!!");
      }
    });
  };
</script>