学习Vue及项目工程化
- Vue
- 快速上手
- 插值表达式
- Vue基本命令
- v-html
- v-show
- v-if 和 v-else 和 v-else-if
- v-on和@click函数调用
- v-bind
- v-for
- 案例--书架
- v-model
- 功能总结
- 综合案例-小黑记事本
- 列表渲染
- 删除功能
- 添加功能
- 底部统计
- 清空
- 项目工程化
- 更换npm镜像
- 方式一:在网页是去创建
- 方式二:在命令行里创建
- 案例:文章搜索
- main.js
- Api.vue
- Article.vue
- 优化
- 接口调用的js代码一般封装在.js文件中,并以函数的形式暴露给外部
- article.js
- Article1.vue
- 定义一个变量,记录公共的前缀,baseURL
- 拦截器 request.js
- request.js
- article1.js
- element
- 安装
- Vue3
- Vue2
- 快速上手
- 完整引入
- main.js
- Button.vue
- 案例
- 环境准备
- 跨域
- 配置代理来解决跨域
- 优化axios响应拦截器
- 路由
- 子路由
- Bug:前端没有接收token
- Pinia状态管理库
- 优化:Axios请求拦截器
- Bug:刷新时,Pinia数据丢失
- persist-Pinia持久化插件
- 未登录处理
Vue
构建用户界面的渐进式框架
快速上手
创建一个Vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box2">
{{msg}}
</div>
<div class="box">
{{count}}
</div>
<div id="app">
<!-- 这里将来会编写一些用于渲染的代码逻辑 -->
<h1>{{msg}}</h1>
<a href="#">{{count}}</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
//一旦引入VueJS核心包,在全局环境,就依赖Vue构造函数
const app = new Vue({
// 通过el配置选择器,指定Vue管理的是哪个盒子
el: '#app',
// 通过data提供数据
data: {
msg: 'Hello World',
count: 666
}
})
</script>
</body>
</html>
插值表达式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{nickname}}</p>
<p>{{nickname.toUpperCase()}}</p>
<p>{{nickname + '你好'}}</p>
<p>{{age >= 18 ? '成年' : '未成年'}}</p>
<p>{{friend.name}}</p>
<p>{{friend.desc}}</p>
//不能使用
<!-- <p title="{{nickname}}">我是p标签</p> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
nickname: 'tony',
age: 18,
friend: {
name: 'jepson',
desc:'热爱学习Vue'
}
}
})
</script>
</body>
</html>
Vue基本命令
v-html
v-show
v-show和v-if都可以做隐藏:
v-show主要用于频繁切换的地方,如购物车显示
v-if主要用于不频繁切换,如弹窗提示
v-if 和 v-else 和 v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
v-show底层原理:切换css的display:one 来控制显示隐藏
v-if底层原理:根据判断条件控制元素创建和移除(条件渲染)
-->
<div id="app">
<div v-html="msg"></div> <!-- v-html解析特殊标签 -->
<div v-show="flag" class="box">v-show控制</div>
<div v-if="flag" class="box">v-if控制</div>
<p v-if="gender === 1">性别:男</p>
<p v-else>性别:女</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg:
'<a href="https://www.baidu.com" target="_blank">百度一下</a>',
flag: false,
gender: 2
}
})
</script>
</body>
</html>
v-on和@click函数调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button v-on:click="count--">-</button>
<span>{{ count }}</span>
<button v-on:click="count++">+</button>
<br>
<br>
<button @click="fn">切换显示隐形</button>
<h1 v-show="isShow">程序员</h1>
<div class="box">
<h3>小王自动贩卖机</h3>
<button @click="buy(5)">可乐5元</button>
<button @click="buy(10)">咖啡10元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 100,
isShow: true,
money: 1000
},
methods: {
fn() {
console.log('切换显示')
app.isShow = !app.isShow
},
buy (price) {
console.log('扣费')
this.money -= price
}
}
})
</script>
</body>
</html>
v-bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<img v-bind:src="imgUrl" v-bind:title="msg" alt="">
<img :src="imgUrl" :title="msg" alt=""> <!-- 简写 -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
imgUrl: './imgs/maven.jpg',
msg: 'Maven Logo'
}
})
</script>
</body>
</html>
v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>小王水果店</h3>
<ul>
<li v-for="(item, index) in list">
{{ item }} - {{ index }}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list: ['西瓜','苹果','鸭梨','榴莲']
}
})
</script>
</body>
</html>
案例–书架
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>小王的书架</h3>
<ul>
<!-- key给列表项添加唯一标识。便于Vue进行正确排序复用 -->
<li v-for="(item, index) in booksList" ::key="item.id">
<span>{{ item.name }}</span>
<span>{{ item.author }}</span>
<button @click="del(item.id)">删除</button>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
booksList: [
{id: 1, name: '《红楼梦》', author: '曹雪芹'},
{id: 2, name: '《西游记》', author: '吴承恩'},
{id: 3, name: '《三国演义》', author: '罗贯中'},
{id: 4, name: '《水浒传》', author: '施耐庵'}
]
},
methods: {
del (id) {
//fillter不会删除原数组会把结果保存在新数组里,所以我们要把原数组重新赋值
this.booksList = this.booksList.filter(item => item.id !== id)
}
},
})
</script>
</body>
</html>
v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- v-model 数据和视图双向数据绑定 -->
账号:<input type="text" v-model="username"> <br><br>
密码:<input type="password" v-model="password"> <br><br>
<button @click="login">登录</button>
<button @click="reset">重置</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
password: ''
},
methods: {
login () {
console.log('登录');
console.log(this.username);
console.log(this.password);
},
reset () {
console.log('重置');
this.username = '';
this.password = '';
}
}
})
</script>
</body>
</html>
功能总结
综合案例-小黑记事本
列表渲染
删除功能
添加功能
底部统计
清空
项目工程化
更换npm镜像
设置镜像源:npm config set registry https://registry.npmmirror.com
查看当前使用的镜像地址:npm config get registry
方式一:在网页是去创建
在命令行中,执行如下指令:
vue ui
方式二:在命令行里创建
在命令行中,执行如下指令:
npm init vue@latest
案例:文章搜索
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
Api.vue
<!-- <script>
export default {
data() {
return {
message: 'Hello, Vue 3!'
}
}
}
</script> -->
<script setup>
import { ref } from 'vue'
//导入 Api.vue文件
import ApiVue from './Api.vue'
//导入 Article.vue文件
import ArticleVue from './Article.vue'
import ArticleVue1 from './Article1.vue'
//调用ref函数,定义响应式数据
const message = ref('Hello, Vue 3!')
</script>
<template>
<h1>{{ message }}</h1>
<!-- 导入并使用 Api.vue组件 -->
<ApiVue />
<!-- 导入并使用 Article.vue组件 -->
<!-- <ArticleVue /> -->
<!-- 导入并使用 Article1.vue组件 -->
<ArticleVue1 />
</template>
<style>
h1 {
color: blue;
}
</style>
Article.vue
<script setup>
import { ref } from 'vue'
import axios from 'axios'
//定义响应式数据 ref
const articleList = ref([]);
//发送异步请求,获取所有文章数据
axios.get('http://localhost:8080/product/findByCate?cate_id=1')
.then(result => {
// console.log(result.data);
//把服务器响应的数据保存起来
articleList.value = result.data.data;
}).catch(err => {
console.log(err);
})
//定义响应式数据 searchConditions
const searchConditions = ref({
name: ''
})
//定义search函数
const search = () => {
//发送异步请求,根据条件查询文章数据
axios.get('http://localhost:8080/product/findName', {
params: {...searchConditions.value}}).then(result => {
console.log(result.data);
//把服务器响应的数据保存起来
articleList.value = result.data.data;
}).catch(err => {
console.log(err);
})
}
</script>
<template>
<!-- <div>{{ articleList }}</div> -->
<div>
文章标题: <input type="text" v-model="searchConditions.name">
文章分类: <input type="text" v-model="searchConditions.category">
发布状态: <input type="text" v-model="searchConditions.state">
<button v-on:click="search">搜索</button>
<br />
<br />
<table border="1 solid" colspa="0" cellspacing="0">
<tr>
<th>文章标题</th>
<th>分类</th>
<th>发表时间</th>
<th>状态</th>
<th>操作</th>
</tr>
<tr v-for="(article, index) in articleList" :key="index">
<td>{{ article.name }}</td>
<td>{{ article.cate_id }}</td>
<td>{{ article.create_time }}</td>
<td>{{ article.sales_volume }}</td>
<td>
<button>编辑</button>
<button>删除</button>
</td>
</tr>
</table>
</div>
</template>
优化
接口调用的js代码一般封装在.js文件中,并以函数的形式暴露给外部
article.js
import axios from 'axios'
//获取所有文章数据函数
export async function articleGetAllService() {
//发送异步请求,获取所有文章数据
//同步等待服务器响应的结果,并返回,async await
return await axios.get('http://localhost:8080/product/findByCate?cate_id=1')
.then(result => {
return result.data.data;
}).catch(err => {
console.log(err);
})
}
//获取单个文章数据函数
export async function articleSearchService(conditions) {
//发送异步请求,根据条件查询文章数据
return await axios.get('http://localhost:8080/product/findName', {
params: conditions
}).then(result => {
return result.data.data;
}).catch(err => {
console.log(err);
})
}
Article1.vue
<script setup>
import { ref } from 'vue'
import { articleGetAllService, articleSearchService } from '@/api/article.js'; // 导入函数
//定义响应式数据 ref
const articleList = ref([]);
//获取所有文章数据
// let data = articleGetAllService();
// articleList.value = data;
//同步获取articleGetAllService的返回结果 async await
const getAllArticle = async function () {
let data = await articleGetAllService();
articleList.value = data;
}
getAllArticle();
//定义响应式数据 searchConditions
const searchConditions = ref({
name: ''
})
//定义search函数
const search = async function() {
//文章搜索
let data = await articleSearchService({...searchConditions.value})
articleList.value = data;
}
</script>
<template>
<!-- <div>{{ articleList }}</div> -->
<div>
文章标题: <input type="text" v-model="searchConditions.name">
文章分类: <input type="text" v-model="searchConditions.category">
发布状态: <input type="text" v-model="searchConditions.state">
<button v-on:click="search">搜索</button>
<br />
<br />
<table border="1 solid" colspa="0" cellspacing="0">
<tr>
<th>文章标题</th>
<th>分类</th>
<th>发表时间</th>
<th>状态</th>
<th>操作</th>
</tr>
<tr v-for="(article, index) in articleList" :key="index">
<td>{{ article.name }}</td>
<td>{{ article.cate_id }}</td>
<td>{{ article.create_time }}</td>
<td>{{ article.sales_volume }}</td>
<td>
<button>编辑</button>
<button>删除</button>
</td>
</tr>
</table>
</div>
</template>
定义一个变量,记录公共的前缀,baseURL
const baseURL = ‘http://localhost:8080’;
const instance = axios.create({baseURL})
instance.get(‘/product/findByCate?cate_id=1’)
拦截器 request.js
request.js
//定制请求的实例
//导入axios npm install axios
import axios from 'axios';
//定义一个变量,记录公共的前缀 , baseURL
const baseURL = 'http://localhost:8080';
const instance = axios.create({baseURL})
//添加响应拦截器
instance.interceptors.response.use(
result=>{
return result.data.data;
},
err=>{
alert('服务异常');
return Promise.reject(err);//异步的状态转化成失败的状态
}
)
export default instance;
article1.js
/* //导入axios npm install axios
import axios from 'axios';
//定义一个变量,记录公共的前缀 , baseURL
const baseURL = 'http://localhost:8080';
const instance = axios.create({baseURL}) */
import request from '@/util/request.js'
export async function articleGetAllService() {
return request.get('/product/findByCate?cate_id=1');
}
export async function articleSearchService(conditions) {
return request.get('/product/findName', { params: conditions });
}
element
安装
首先查看自己安装vue的版本,输入vue -V
如果自己的vue版本是3.0以上说明需要引入的是element-ui plus而不是element-ui
Vue3
npm install element-plus --save
如果下载的时候还会报错,执行这条指令:npm install element-plus --save --legacy-peer-deps
Vue2
npm install --save element-ui
如果下载的时候还会报错,执行这条指令:npm install element-ui --save --legacy-peer-deps
快速上手
完整引入
main.js
import Vue from ‘vue’;
import ElementUI from ‘element-ui’;
import ‘element-ui/lib/theme-chalk/index.css’;
import App from ‘./App.vue’;
Vue.use(ElementUI);
new Vue({
el: ‘#app’,
render: h => h(App)
});
Button.vue
<template>
<el-row>
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
</el-row>
<el-row>
<el-button plain>朴素按钮</el-button>
<el-button type="primary" plain>主要按钮</el-button>
<el-button type="success" plain>成功按钮</el-button>
<el-button type="info" plain>信息按钮</el-button>
<el-button type="warning" plain>警告按钮</el-button>
<el-button type="danger" plain>危险按钮</el-button>
</el-row>
<el-row>
<el-button round>圆角按钮</el-button>
<el-button type="primary" round>主要按钮</el-button>
<el-button type="success" round>成功按钮</el-button>
<el-button type="info" round>信息按钮</el-button>
<el-button type="warning" round>警告按钮</el-button>
<el-button type="danger" round>危险按钮</el-button>
</el-row>
<el-row>
<el-button icon="el-icon-search" circle></el-button>
<el-button type="primary" icon="el-icon-edit" circle></el-button>
<el-button type="success" icon="el-icon-check" circle></el-button>
<el-button type="info" icon="el-icon-message" circle></el-button>
<el-button type="warning" icon="el-icon-star-off" circle></el-button>
<el-button type="danger" icon="el-icon-delete" circle></el-button>
</el-row>
</template>
案例
环境准备
跨域
配置代理来解决跨域
优化axios响应拦截器
路由
子路由
Bug:前端没有接收token
Pinia状态管理库
优化:Axios请求拦截器
Bug:刷新时,Pinia数据丢失
persist-Pinia持久化插件