一,修改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页面为例)
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>