# 前置知识
export
导出, import
导入, as
取别名export
可以添加在方法前,也可以统一导出,通过 default
默认导出
export {complexMessage as cm}
export default {complexMessage}
import {complexMessage as cm} from './test.js'
import methods from './test.js'
vscode 插件:Live Server
test.js
function simpleMessage(msg) { | |
console.log(msg); | |
} | |
// 方法一:函数前添加 export | |
export function complexMessage(msg) { | |
console.log(new Date() + " - " + msg); | |
} | |
// 方法二:统一 export | |
export {simpleMessage, complexMessage as cm}; | |
// 方法三:默认 export | |
export default {simpleMessage, complexMessage}; |
test.html
<!DOCTYPE html> | |
<html lang="zh"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>测试</title> | |
</html> | |
</head> | |
<body> | |
<div> | |
<button id="message">测试</button> | |
</div> | |
<script type="module"> | |
// 方法一 | |
import {simpleMessage as sm, cm} from './test.js'; | |
document.getElementById('message').onclick = function() { | |
cm('测试,测试,测试'); | |
} | |
// 方法二 | |
import methods from './test.js'; | |
document.getElementById('message').onclick = function() { | |
methods.complexMessage('测试,测试,测试'); | |
} | |
</script> | |
</body> | |
</html> |
# 框架介绍
Vue:用于构建用户界面的渐进式 js 框架
<!DOCTYPE html> | |
<html lang="zh"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Vue测试</title> | |
</html> | |
</head> | |
<body> | |
<div id="app"></div> | |
<!-- ES 格式,引入 Vue 模块 --> | |
<script type="module"> | |
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' | |
/* 创建实例 */ | |
createApp({ | |
data() { | |
return { | |
// Vue 实例数据 | |
message: 'Hello Vue!' | |
} | |
} | |
}).mount('#app') // 控制 id 为 app 的元素 | |
</script> | |
</body> | |
</html> |
# 常用指令
- v-for:列表渲染,遍历元素;
v-for = "(item,index) in items"
- v-bind:为 html 标签绑定属性值;
v-bind:href
或:href
- v-if/v-else-if/v-else:条件判断,控制元素的创建和移除;
v-if="表达式"
- v-show:切换 display 属性值,控制元素的显示与隐藏;
v-show="表达式"
- v-on:为 html 标签绑定事件;
v-on:click="方法名"
或@:click="方法名"
- v-model:创建双向数据绑定,获取和设置数据;
v-model="变量名"
<!DOCTYPE html> | |
<html lang="zh"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Vue测试</title> | |
</head> | |
<body> | |
<div id="app"> | |
<table border="1" cellspacing="0" cellpadding="5"> | |
<tr> | |
<th>标题</th> | |
<th>内容</th> | |
<th>作者</th> | |
</tr> | |
<tr v-for="article in articleList"> | |
<!-- v-for 指令用于循环遍历 articles 数组 --> | |
<td></td> | |
<td></td> | |
<td></td> | |
</tr> | |
</table> | |
<!-- v-bind 指令用于绑定属性 --> | |
<a v-bind:href="url" target="_blank">点击跳转1 </a> | |
<a :href="url" target="_blank">点击跳转2</a> | |
<!-- v-if 指令用于条件渲染 --> | |
<div v-if="articleList.length > 0"> | |
<p>文章列表不为空</p> | |
</div> | |
<div v-else> | |
<p>文章列表为空</p> | |
</div> | |
<!-- v-show 指令用于显示或隐藏元素 --> | |
<div v-show="articleList.length != 0"> | |
<p>文章列表不为空</p> | |
</div> | |
<!-- v-on 指令用于绑定事件 --> | |
<div> | |
<button v-on:click="complexMessage('按钮被点击了1')">点击我1</button> | |
<button @click="complexMessage('按钮被点击了2')">点击我2</button> | |
</div> | |
<!-- v-model 指令用于双向数据绑定 --> | |
<div> | |
<input type="text" v-model="name" placeholder="请输入姓名" /> | |
<p>姓名:</p> | |
<button @click="clear">清空</button> | |
</div> | |
</div> | |
<!-- ES 格式,引入 Vue 模块 --> | |
<script type="module"> | |
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' | |
/* 创建实例 */ | |
createApp({ | |
data() { | |
return { | |
// Vue 实例数据 | |
articleList: [ | |
{ title: 'Vue.js', content: 'Vue.js是一个渐进式JavaScript框架', author: '尤雨溪' }, | |
{ title: 'React', content: 'React是一个用于构建用户界面的JavaScript库', author: 'Facebook' }, | |
{ title: 'Angular', content: 'Angular是一个用于构建单页应用的框架', author: 'Google' } | |
], | |
url: 'https://www.example.com', | |
name: '' | |
} | |
}, | |
methods: { | |
// Vue 实例方法 | |
complexMessage: function(msg) { | |
console.log(new Date() + ' ' + msg) | |
alert(msg) | |
}, | |
clear: function() { | |
this.name = '' | |
} | |
} | |
}).mount('#app') // 挂载到 id 为 app 的元素上,控制元素 | |
</script> | |
</body> | |
</html> |
# 生命周期
生命周期:一个对象从创建到销毁的整个过程
八个阶段,会自动执行一个生命周期方法(钩子)
状态 | 阶段周期 |
---|---|
beforeCreate | 创建前 |
created | 创建后 |
beforeMount | 载入前 |
mounted | 挂载完成 |
beforeUpdate | 数据更新前 |
updated | 数据更新后 |
beforeUnmount | 组件销毁前 |
unmounted | 组件销毁后 |
createApp({ | |
mounted: function() { | |
// Vue 实例挂载 | |
console.log('Vue实例已挂载') | |
} | |
}).mount('#app') |
# Axios
Ajax(Asynchronous Javascript And XML),即是异步的 JavaScript 和 XML
Axios 对原生的 Ajax 进行封装
# 使用
<!DOCTYPE html> | |
<html lang="zh"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Axios测试</title> | |
</head> | |
<body> | |
<!-- 引入 axios --> | |
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script> | |
<script> | |
// 发送 GET 请求 | |
axios({ | |
method: 'GET', | |
url: 'https://api.github.com/users/octocat', | |
}).then((resp) => { | |
// 成功回调 | |
console.log(resp); | |
}).catch((err) => { | |
// 失败回调 | |
console.error(err); | |
}); | |
// 发送 POST 请求 | |
axios({ | |
method: 'POST', | |
url: 'https://jsonplaceholder.typicode.com/posts', | |
data: { | |
title: 'foo', | |
body: 'bar', | |
userId: 1, | |
}, | |
}).then((resp) => { | |
// 成功回调 | |
console.log(resp); | |
}).catch((err) => { | |
// 失败回调 | |
console.error(err); | |
}); | |
// 别名方式发送请求 | |
axios.get('https://api.github.com/users/octocat') | |
.then((resp) => { | |
// 成功回调 | |
console.log(resp); | |
}).catch((err) => { | |
// 失败回调 | |
console.error(err); | |
}); | |
axios.post('https://jsonplaceholder.typicode.com/posts', { | |
title: 'foo', | |
body: 'bar', | |
userId: 1, | |
}).then((resp) => { | |
// 成功回调 | |
console.log(resp); | |
}).catch((err) => { | |
// 失败回调 | |
console.error(err); | |
}); | |
</script> | |
</body> | |
</html> |
# 拦截器
import axios from "axios"; | |
const baseurl = "http://www.example.com" | |
const instance = axios.create({baseurl}); // 创建 axios 实例 | |
// 添加请求拦截器 | |
instance.interceptors.request.use(function (config) { | |
// 在发送请求之前做些什么 | |
// 可以添加请求头 | |
return config; | |
}, function (error) { | |
// 对请求错误做些什么 | |
return Promise.reject(error); | |
}); | |
// 添加响应拦截器 | |
instance.interceptors.response.use(function (response) { | |
// 2xx 范围内的状态码都会触发该函数。 | |
if (response.data.code === 0) { | |
// 请求成功 | |
return response.data; | |
}else { | |
// 请求失败 | |
console.log(response.data.msg); | |
return Promise.reject(response.data.msg); | |
} | |
}, function (error) { | |
// 超出 2xx 范围的状态码都会触发该函数。 | |
// 对响应错误做点什么 | |
return Promise.reject(error); | |
}); | |
export default instance; |
# 项目创建
# 方法一 | |
npm init vue@latest | |
npm install | |
npm run dev | |
# 方法二 | |
pnpm create vue@latest | |
pnpm install | |
pnpm dev # 运行 | |
pnpm build # 打包 |
项目结构:
- package.json:项目配置文件
- vite.config.js:Vue 项目的配置信息
- src
- assets
- components:组件目录
- App.vue:根组件
- main.js:入口文件
*.vue:单文件组件,封装逻辑(script)、模板(template)和样式(style)
<!-- 方法一 --> | |
<script> | |
export default { | |
data() { | |
return { | |
title: 'Hello Vue 3!' | |
} | |
} | |
} | |
</script> | |
<!-- 方法二 --> | |
<script setup> | |
import { ref } from 'vue' | |
const title = ref('Welcome!') | |
</script> | |
<template> | |
<h1>Vue</h1> | |
</template> | |
<style scoped> | |
h1 { | |
color: #42b983; | |
font-size: 2em; | |
text-align: center; | |
} | |
</style> |
# API 风格
选项式 API:用 data
、 methods
、 mounted
等
<script> | |
export default { | |
data() { | |
return { | |
title: 'Hello Vue 3!' | |
} | |
} | |
} | |
</script> |
组合式 API: ref
响应式数据
一般会将方法封装到 js 文件中,export 出来,函数复用
导入自定义方法: import {test} from '@/api/myapi.js'
, @
表示 src 目录
<!-- 方法二 --> | |
<script setup> | |
// pnpm add axios | |
import axios from 'axios' | |
import { onMounted, ref } from 'vue' | |
const title = ref('Welcome!') | |
const data = ref({ | |
name: 'Vue 3', | |
role: "admin" | |
}) | |
function changeTitle() { | |
//... 解构运算符,将对象的属性展开 | |
axios.get('https://www.example.com', {params: {...data.value}}) | |
.then(response => { | |
title.value = 'success!' | |
}) | |
.catch(error => { | |
title.value = 'fail!' | |
}) | |
} | |
onMounted(() => { | |
setTimeout(() => { | |
changeTitle() | |
}, 2000) | |
}) | |
</script> |
# Element Plus
Element Plus:基于 Vue 3,面向设计师和开发者的组件库
Overview 组件总览
安装: pnpm install element-plus
完整导入
main.js
import { createApp } from 'vue' | |
import ElementPlus from 'element-plus' | |
import 'element-plus/dist/index.css' | |
import zhCn from 'element-plus/es/locale/lang/zh-cn' | |
import * as ElementPlusIconsVue from '@element-plus/icons-vue' | |
import App from './App.vue' | |
const app = createApp(App) | |
app.use(ElementPlus, { | |
locale: zhCn, | |
}) | |
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { | |
app.component(key, component) | |
} | |
app.mount('#app') |
调用其他.vue
<template> | |
<demo></demo> | |
</template> | |
<script setup> | |
import demo from "@/views/demo.vue"; | |
</script> |
# 跨域
由于浏览器的同源策略策略,向不同源(不同协议、不同域名、不同端口)发送 ajax 请求会失败
在 vite.config.js
配置 proxy 代理,前端向 /api 请求,会向 8080 端口请求,将 api 替换为空
import { fileURLToPath, URL } from 'node:url' | |
import { defineConfig } from 'vite' | |
import vue from '@vitejs/plugin-vue' | |
import vueDevTools from 'vite-plugin-vue-devtools' | |
// https://vite.dev/config/ | |
export default defineConfig({ | |
plugins: [ | |
vue(), | |
vueDevTools(), | |
], | |
resolve: { | |
alias: { | |
'@': fileURLToPath(new URL('./src', import.meta.url)) | |
}, | |
}, | |
server: { | |
proxy: { | |
'/api': { | |
target: 'http://localhost:3000', | |
changeOrigin: true, | |
rewrite: (path) => path.replace(/^\/api/, '') | |
} | |
} | |
} | |
}) |
# 路由
路由:决定从起点到终点的路径的进程,根据不同访问路径,显示不同组件内容
官方路由:Vue Router
安装: pnpm add vue-router
router 实例:路由实例,createRouter 创建,维护路由信息<router-link>
:路由链接组件,解析为 <a>
<router-view>
:动态视图组件,渲染与路由路径对应的组件
路由跳转:router.push ('/')
src/router/index.js
创建路由器并导出
import { createRouter, createWebHistory } from "vue-router"; | |
// 导入组件 | |
import LoginVue from "@/views/Login.vue"; | |
import LayoutVue from "@/views/Layout.vue"; | |
import UserInfoVue from "@/views/UserInfo.vue"; | |
import UserSettingVue from "@/views/UserSetting.vue"; | |
// 定义路由 | |
const routes = [ | |
{ path: '/login', component: LoginVue }, | |
{ | |
path: '/', | |
component: LayoutVue, | |
// 子路由 | |
children: [ | |
{path: '/user/info', component: UserInfoVue}, | |
{path: '/user/setting', component: UserSettingVue}, | |
] | |
} | |
] | |
// 创建路由器 | |
const router = createRouter({ | |
history: createWebHistory(), | |
routes: routes | |
}); | |
export default router; |
main.js
import router from '@/router' | |
app.use(router) |
# Pinia
Vue 的专属状态管理库,允许跨组件和页面共享状态,传递 token 等信息pnpm add pinia
main.js
import { createPinia } from 'pinia' | |
const pinia = createPinia() | |
app.use(pinia) |
src/stores/token.js
定义 store 方法
import { defineStore } from "pinia"; | |
import { ref } from "vue"; | |
export const useTokenStore = defineStore("token", () => { | |
const token = ref(""); | |
function setToken(newToken) { | |
token.value = newToken; | |
} | |
function clearToken() { | |
token.value = ""; | |
} | |
return { token, setToken, clearToken }; | |
} | |
); |
之后在组件中引入 @/stores/token.js
,调用 useTokenStore
pinia 默认存储在内存中,刷新浏览器时会丢失数据
Persist 插件:将 pinia 内的数据持久化存储pnpm add pinia-persistedstate-plugin
main.js
import { createPersistedState } from 'pinia-persistedstate-plugin' | |
const persistedState = createPersistedState() | |
pinia.use(persistedState) |
创建 store 时添加 persist 选项export const useTokenStore = defineStore("token", ()=>{}, {persist: true});
# Demo
<template> | |
<el-container class="layout-container-demo"> | |
<el-header style="text-align: center; font-size: 20px"> | |
tlias管理系统 | |
</el-header> | |
<el-container> | |
<!-- 侧边栏 --> | |
<el-aside width="140px"> | |
<el-menu> | |
<el-sub-menu index="1"> | |
<template #title> | |
<el-icon><Menu /></el-icon>部门管理 | |
</template> | |
<el-menu-item index="1-1">Option 1-1</el-menu-item> | |
<el-menu-item index="1-2">Option 1-2</el-menu-item> | |
<el-menu-item index="1-3">Option 1-3</el-menu-item> | |
</el-sub-menu> | |
<el-sub-menu index="2"> | |
<template #title> | |
<el-icon><Avatar /></el-icon>员工管理 | |
</template> | |
<el-menu-item index="2-1">Option 1-1</el-menu-item> | |
<el-menu-item index="2-2">Option 1-2</el-menu-item> | |
<el-menu-item index="2-3">Option 1-3</el-menu-item> | |
</el-sub-menu> | |
<el-sub-menu index="3"> | |
<template #title> | |
<el-icon><Search /></el-icon>数据统计 | |
</template> | |
<el-menu-item index="3-1">Option 1-1</el-menu-item> | |
<el-menu-item index="3-2">Option 1-2</el-menu-item> | |
<el-menu-item index="3-3">Option 1-3</el-menu-item> | |
</el-sub-menu> | |
</el-menu> | |
</el-aside> | |
<el-container> | |
<el-main> | |
<!-- 对话框 --> | |
<el-button | |
plain | |
@click="dialogTableVisible = true" | |
style="margin-bottom: 20px" | |
> | |
点击查看数据 | |
</el-button> | |
<el-dialog v-model="dialogTableVisible" title="数据" width="600"> | |
<el-table :data="emps"> | |
<el-table-column property="id" label="ID" width="150" /> | |
<el-table-column property="name" label="姓名" width="200" /> | |
<el-table-column property="deptName" label="部门" /> | |
</el-table> | |
</el-dialog> | |
<!-- 表单 --> | |
<el-form :inline="true" :model="formInline"> | |
<el-form-item label="用户名"> | |
<el-input | |
style="width: 150px" | |
v-model="formInline.name" | |
clearable | |
/> | |
</el-form-item> | |
<el-form-item label="性别"> | |
<el-select | |
style="width: 100px" | |
v-model="formInline.gender" | |
clearable | |
> | |
<el-option label="所有" value="0" /> | |
<el-option label="男" value="1" /> | |
<el-option label="女" value="2" /> | |
</el-select> | |
</el-form-item> | |
<el-form-item label="开始时间"> | |
<el-date-picker | |
style="width: 150px" | |
v-model="formInline.begin" | |
type="date" | |
value-format="YYYY-MM-DD" | |
clearable | |
/> | |
</el-form-item> | |
<el-form-item label="结束时间"> | |
<el-date-picker | |
style="width: 150px" | |
v-model="formInline.end" | |
type="date" | |
value-format="YYYY-MM-DD" | |
clearable | |
/> | |
</el-form-item> | |
<el-form-item> | |
<el-button type="primary" @click="search">查询</el-button> | |
<el-button type="info" @click="clear">清空</el-button> | |
</el-form-item> | |
</el-form> | |
<!-- 表格数据 --> | |
<el-table :data="emps"> | |
<el-table-column prop="id" label="ID" align="center"/> | |
<el-table-column prop="username" label="用户名" align="center" /> | |
<el-table-column prop="name" label="姓名" align="center"/> | |
<el-table-column prop="gender" label="性别" width="60px" align="center"> | |
<template #default="scope"> | |
<!--swig6--> | |
</template> | |
</el-table-column> | |
<el-table-column prop="phone" label="电话号码" align="center" /> | |
<el-table-column prop="job" label="职位" align="center" > | |
<template #default="scope"> | |
<span v-if="scope.row.job == 1">班主任</span> | |
<span v-else-if="scope.row.job == 2">讲师</span> | |
<span v-else-if="scope.row.job == 3">学工主管</span> | |
<span v-else-if="scope.row.job == 4">教研主管</span> | |
<span v-else-if="scope.row.job == 5">咨询师</span> | |
<span v-else>其他</span> | |
</template> | |
</el-table-column> | |
<el-table-column prop="salary" label="薪水" align="center" /> | |
<el-table-column prop="entryDate" label="入职日期" align="center" /> | |
<el-table-column prop="deptName" label="部门" align="center" /> | |
<el-table-column prop="updateTime" label="修改时间" width="200px" align="center" /> | |
</el-table> | |
<!-- 分页栏 --> | |
<div class="demo-pagination-block"> | |
<el-pagination | |
v-model:current-page="currentPage" | |
v-model:page-size="pageSize" | |
:page-sizes="[5, 10, 50, 100]" | |
:background="true" | |
layout="sizes, prev, pager, next" | |
v-model:total="total" | |
@size-change="search" | |
@current-change="search" | |
/> | |
</div> | |
</el-main> | |
</el-container> | |
</el-container> | |
</el-container> | |
</template> | |
<script lang="ts" setup> | |
import { ref, reactive, onMounted } from "vue"; | |
import axios from "axios"; | |
const currentPage = ref(1); | |
const pageSize = ref(5); | |
const total = ref(50); | |
const dialogTableVisible = ref(false); | |
const formInline = ref({ | |
name: "", | |
gender: "0", | |
begin: "", | |
end: "", | |
}); | |
const emps = ref([]); | |
const search = async () => { | |
const data = await axios.get("http://localhost:8080/emps", { | |
params: { | |
page: currentPage.value, | |
pageSize: pageSize.value, | |
...formInline.value, | |
}, | |
}); | |
emps.value = data.data.data.rows; | |
total.value = data.data.data.total; | |
}; | |
const clear = () => { | |
emps.value = []; | |
} | |
onMounted(() => { | |
search(); | |
}); | |
</script> | |
<style scoped> | |
</style> |