官方网站:https://v3.vuejs.org/
中文文档: https://staging-cn.vuejs.org/guide/introduction.html
目的:
上节我们采用NPM 的方法来学习vue。
这次使用构建工具来构建Vue项目。
现在官方推荐使用 create-vue 来创建基于 Vite 的新项目
官方中文文档:https://cn.vitejs.dev/
官方推荐使用Vite来构建vue项目,这里简单了解一下。
Vite主要功能是用于打包场景的增强功能。
Vite作为一个基于浏览器原生ESM的构建工具,它省略了开发环境的打包过程,利用浏览器去解析imports,在服务端按需编译返回。同时,在开发环境拥有速度快到惊人的模块热更新,且热更新的速度不会随着模块增多而变慢。因此,使用Vite进行开发,至少会比Webpack快10倍左右。
前提条件
1、通过命令来初始化 vue3 项目。
npm init vue@latest
创建项目时,可以根据自己需求去添加组件。
2、启动项目
# 进入刚刚创建项目的目录 cd vue3-demo # 更新依赖 npm install # 启动项目 npm run dev
访问 http://127.0.0.1:5173/
1、新建目录如下:
打开APP.vue
,你会发现Vue 的单文件组件是网页开发中 HTML、CSS 和 JavaScript 三种语言经典组合的自然延伸。<template>
、<script>
和 <style>
三个块在同一个文件中封装、组合了组件的视图、逻辑和样式。完整的语法定义可以查阅 SFC 语法说明。
1、把对应没有用的页面先删除。
2、编写登录页
App.vue
<script setup> import Login from './views/Login.vue' </script> <template> <main> <Login /> </main> </template> <style scoped> </style>
创建views
文件夹,我们一般会将页面放在views
里。
Login.vue
<script setup> </script> <template> <div class="container"> <div class="login-wrapper"> <div class="header">登录</div> <div class="form-wrapper"> <input type="text" name="username" placeholder="账号" class="input-item"> <input type="password" name="password" placeholder="密码" class="input-item"> <div class="btn">登录</div> </div> </div> </div> </template> <style scoped> .container { height: 100%; } .login-wrapper { background-image: linear-gradient(to right, #fbc2eb, #a6c1ee); width: 358px; height: 588px; border-radius: 15px; padding: 0 50px; margin: 0 auto; } .header { font-size: 38px; font-weight: bold; text-align: center; line-height: 200px; } .input-item { display: block; width: 100%; margin-bottom: 20px; border: 0; padding: 10px; border-bottom: 1px solid rgb(128, 125, 125); font-size: 15px; outline: none; } .input-item:placeholder { text-transform: uppercase; } .btn { text-align: center; padding: 10px; width: 100%; margin-top: 40px; background-image: linear-gradient(to right, #a6c1ee, #fbc2eb); color: #fff; } </style>
效果如果下:
Vue Router官方文档:https://router.vuejs.org/zh/
1、添加Vue Router依赖
由于页面跳转需要路由(相当于控制你要去哪个页面的控制器)。
# 在项目的根目录下 npm install vue-router@4
2、添加router文件
可以新增
routers
文件夹,存放router
文件,看个人习惯。
添加router文件
import { createRouter, createWebHistory } from 'vue-router' // 导入路由组件. import Login from './views/Login.vue' import UserList from './views/UserList.vue' // 创建路由实例 export const router = createRouter({ history: createWebHistory(), // 定义路由 routes: [ { path: '/', component: Login }, { path: '/userList', component: UserList } ] })
UserList
是用来登录成功后跳转的页面。
<script> export default { } </script> <template> <div> <h1>用户管理</h1> </div> </template> <style scoped> </style>
3、修改main.js
import { createApp } from 'vue' import App from './App.vue' import { router } from './router' // import './assets/main.css' // createApp(App).mount('#app') const app = createApp(App) // 注册路由 app.use(router) app.mount('#app')
4、修改Login.vue
主要是添加点击事件和路由跳转
export default { data() { return { login: { username: '', password: '' } } }, methods: { toLogin() { // 这里可以判断账号和密码是否为空。 this.$router.push('/userList') } } }
5、修改App.vue
router-view
显示与路由的页面。按创建顺序来,默认显示第一个。
类似html的iframe
标签。
6、验证路由是否生效
点击登录,发现url产生变化,也发现页面跳转了,证明路由生效了。
演示一个用户管理功能,用于快速学习vue为目的。
UserList.vue
<script> export default { data(){ return { userForm:{ name: '', age: '' }, userList: [{ name: '小明', age:22 }, { name: '大白', age:23 }, { name: '张三', age:24 }], delIds:[] } }, methods:{ addUser(){ this.userList.push(this.userForm) this.userForm = { name: '', age: '' } }, delUser(){ let i = 0; for (const del of this.delIds.sort()) { this.userList.splice(del-i,1); i++; } this.delIds=[] } } } </script> <template> <div> <h1>用户管理</h1> <form action=""> <input type="text" placeholder="用户名" v-model="userForm.name" > <input type="text" placeholder="年龄" v-model.number="userForm.age" @keyup.enter="addUser"> <input type="button" value="添加" @click="addUser"> <input type="button" value="删除" @click="delUser"> </form> <ul> <li v-for="(user,index) in userList"> <input type="checkbox" v-model="delIds" :value="index" /> 姓名:{{user.name}}, 年龄:{{user.age}} </li> </ul> </div> </template> <style scoped> </style>
发现页面并不美观,下面我们引用UI组件库。
目前常用于VUE3的几款UI组件库:
适用于VUE3的几款高颜值UI组件库
# 在项目的根目录下 npm install element-plus --save
main.js
import { createApp } from 'vue' import App from './App.vue' import { router } from './router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // import './assets/main.css' // createApp(App).mount('#app') const app = createApp(App) // 注册路由 app.use(router) app.use(ElementPlus) app.mount('#app')
改造UserList.vue
<script> export default { data(){ return { userForm:{ name: '', age: '' }, userList: [{ name: '小明', age:22 }, { name: '大白', age:23 }, { name: '张三', age:24 }], delIds:[] } }, methods:{ addUser(){ this.userList.push(this.userForm) this.userForm = { name: '', age: '' } }, delUser(){ let i = 0; for (const del of this.delIds.sort()) { this.userList.splice(del-i,1); i++; } this.delIds=[] } } } </script> <template> <div> <h1>用户管理</h1> <form action=""> <input type="text" placeholder="用户名" v-model="userForm.name" > <input type="text" placeholder="年龄" v-model.number="userForm.age" @keyup.enter="addUser"> <input type="button" value="添加" @click="addUser"> <input type="button" value="删除" @click="delUser"> </form> <ul> <li v-for="(user,index) in userList"> <input type="checkbox" v-model="delIds" :value="index" /> 姓名:{{user.name}}, 年龄:{{user.age}} </li> </ul> </div> </template> <style scoped> </style>
发现比之前好看多了。
官网文档:http://www.axios-js.com/zh-cn/docs/vue-axios.html
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
功能与Ajax是一样的。都是请求后端接口获取数据。
# 在项目的根目录下 npm install --save axios vue-axios
请求方法的别名:
为方便使用,官方为所有支持的请求方法提供了别名,可以直接使用别名来发起请求:
axios.request(config) axios.get(url[, config]) axios.delete(url[, config]) axios.head(url[, config]) axios.post(url[, data[, config]]) axios.put(url[, data[, config]]) axios.patch(url[, data[, config]])
注意:在使用别名方法时, url、method、data 这些属性都不必在配置中指定。
main.js
import { createApp } from 'vue' import App from './App.vue' import { router } from './router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import axios from 'axios' import VueAxios from 'vue-axios' // import './assets/main.css' // createApp(App).mount('#app') const app = createApp(App) // 注册路由 app.use(router) app.use(ElementPlus) app.use(VueAxios, axios) app.mount('#app')
在public
添加测试数据userList.json
[ { "name": "小明", "age": 22 }, { "name": "大白", "age": 23 }, { "name": "张三", "age": 24 } ]
改造UserList.vue
<script> export default { created(){ this.getUserList() }, data(){ return { userForm:{ name: '', age: '' }, userList: [], delIds:[] } }, methods:{ getUserList(){ // 获取public下的test.json文件数据 this.axios.get('/userList.json') .then(res => ( //console.log(res) this.userList = res.data )) .catch(error => ( // 请求失败处理 console.log(error) )) }, addUser(){ this.userList.push(this.userForm) this.userForm = { name: '', age: '' } }, delUser(val){ let i = 0; for (const del of this.delIds.sort()) { this.userList.splice(del-i,1); i++; } this.delIds=[] }, selectObjs(val){ this.delIds = []; for (const it of val) { this.delIds.push(this.userList.indexOf(it)); } } } } </script> <template> <div> <h1>用户管理</h1> <el-form :model="userForm" :inline="true"> <el-form-item> <el-input placeholder="用户名" v-model="userForm.name" /> </el-form-item> <el-form-item> <el-input placeholder="年龄" v-model.number="userForm.age" @keyup.enter="addUser" /> </el-form-item> <el-form-item> <el-button type="primary" @click="addUser">添加</el-button> <el-button type="danger" @click="delUser">删除</el-button> </el-form-item> </el-form> <el-table :data="userList" stripe @selection-change="selectObjs"> <el-table-column type="selection" width="55" /> <el-table-column prop="name" label="名称" width="180" /> <el-table-column prop="age" label="年龄" width="180" /> </el-table> </div> </template> <style scoped> </style>
为什么要使用setup组合?
<script setup> import { ref, onMounted, onUnmounted } from 'vue' const x = ref(0) const y = ref(0) function update(event) { x.value = event.pageX y.value = event.pageY } onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) </script> <template>Mouse position is at: {{ x }}, {{ y }}</template>
但是,如果我们想在多个组件中复用这个相同的逻辑呢?我们可以把这个逻辑以一个组合式函数的形式提取到外部文件中
// mouse.js import { ref, onMounted, onUnmounted } from 'vue' // 按照惯例,组合式函数名以“use”开头 export function useMouse() { // 被组合式函数封装和管理的状态 const x = ref(0) const y = ref(0) // 组合式函数可以随时更改其状态。 function update(event) { x.value = event.pageX y.value = event.pageY } // 一个组合式函数也可以挂靠在所属组件的生命周期上 // 来启动和卸载副作用 onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) // 通过返回值暴露所管理的状态 return { x, y } }
下面是它在组件中使用的方式:
<script setup> import { useMouse } from './mouse.js' const { x, y } = useMouse() </script> <template>Mouse position is at: {{ x }}, {{ y }}</template>
组合式与普通的写法在于:
组合式有setup
。
组合式不能直接使用生命周期钩子,需要通过import { onMounted } from 'vue'
才可以。
除了 Vue 内置的一系列指令 (比如 v-model
或 v-show
) 之外。Vue 还允许你注册自定义的指令。
下面是一个自定义指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦:
const focus = { mounted: (el) => el.focus() } export default { directives: { // 在模板中启用 v-focus focus } }
<input v-focus />
上面是局部使用,如果我们想全局使用怎么做?
const app = createApp({}) // 使 v-focus 在所有组件中都可用 app.directive('focus', { /* ... */ })
官方文档:https://router.vuejs.org/
在上面我们只是简单使用Router来进行跳转。
实际上Router的功能很强大。
因为它是控制页面的跳转的,你完全可以在跳转前判断要不要跳转当前页面。
const routes = [ // 动态字段以冒号开始 { path: '/users/:id', component: User }, ] //接收参数 this.$route.params
跟iframe
标签里套iframe
标签是一样的。
const routes = [ { path: '/user/:id', component: User, // 请注意,只有子路由具有名称 children: [{ path: '', name: 'user', component: UserHome }], }, ]
主要用来通过跳转前来处理请求。
使用 router.beforeEach
注册一个全局前置守卫:
const router = createRouter({ ... }) router.beforeEach((to, from) => { // to: 即将要进入的目标, // from: 当前导航正要离开的路由 // ... // 返回 false 取消跳转,这里也可以重定向到别的页面。 return false // 将用户重定向到登录页面 // return { name: 'Login' } })
可以自定义一定属性在路由上,例如,某一个路由需要登录后才能访问。
const routes = [ { path: '/posts', component: PostsLayout, children: [ { path: 'new', component: PostsNew, // 只有经过身份验证的用户才能创建帖子 meta: { requiresAuth: true } }, { path: ':id', component: PostsDetail // 任何人都可以阅读文章 meta: { requiresAuth: false } } ] } ]
例子:判断页面是否要登录才能访问
router.beforeEach((to, from) => { if (to.meta.requiresAuth && !auth.isLoggedIn()) { // 此路由需要授权,请检查是否已登录 // 如果没有,则重定向到登录页面 return { path: '/login', // 保存我们所在的位置,以便以后再来 query: { redirect: to.fullPath }, } } })
1、主要对axios
进行封装成工具来使用。
import axios from 'axios' import { MessageBox, Message } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' // create an axios instance const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url withCredentials: true, // 跨域请求时发送 cookies timeout: 5000 // request timeout }) // request interceptor service.interceptors.request.use( config => { // Do something before request is sent if (store.getters.token) { // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改 config.headers['X-Token'] = getToken() } return config }, error => { // Do something with request error console.log(error) // for debug Promise.reject(error) } ) // response interceptor service.interceptors.response.use( /** * If you want to get information such as headers or status * Please return response => response */ /** * 下面的注释为通过在response里,自定义code来标示请求状态 * 当code返回如下情况则说明权限有问题,登出并返回到登录页 * 如想通过 XMLHttpRequest 来状态码标识 逻辑可写在下面error中 * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除 */ response => { const res = response.data if (res.code !== 20000) { Message({ message: res.message, type: 'error', duration: 5 * 1000 }) // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; if (res.code === 50008 || res.code === 50012 || res.code === 50014) { // 请自行在引入 MessageBox // import { Message, MessageBox } from 'element-ui' MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { store.dispatch('user/resetToken').then(() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } return Promise.reject('error') } else { return res } }, error => { console.log('err' + error) // for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default service
配置来源:https://github.com/PanJiaChen/vue-element-admin/blob/v4.0.0/src/utils/request.js
使用:
import request from '@/utils/request' export function getList(query) { return request({ url: '/demo/list', method: 'get', params: query }) }
# 在项目的根目录下 npm run build
执行完成后,会在 Vue 项目下会生成一个 dist 目录
写到这里,日常开发也差不多满足了
如果想更快理解,可以参照一些优秀项目来进行学习。
多练即可。
文章参考:
https://cn.vuejs.org/guide/introduction.html
https://zhuanlan.zhihu.com/p/482851017
https://www.runoob.com/vue3/vue3-tutorial.html
推荐后台管理模板:
https://vvbin.cn/doc-next/
http://blog.lgf196.top/ant-simple-pro-document/
https://gitee.com/codeZyZ_admin/vue3-composition-admin
https://gitee.com/panjiachen/vue-element-admin/tree/master/