1. 使用脚手架创建项目
1.1 准备工作
- win+R,在弹出的数据框中输入cmd,数据命令查看node以及npm版本
- 下载vue cli
1.2 创建项目
1.2.1 创建一个英文目录文件夹,cmd打开命令命令提示符
1.2.2 vue ui命令打开控制台
1.2.3 创建项目
创建成功
1.3 项目结构
1.4 将项目在VSCode打开,终端运行页面
使用命令npm run serve命令打开
1.5 修改服务端口号
在vue.config.js中修改服务端口号,防止与后端tomcat端口冲突
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
port: 7070
}
})
保存文件,重启项目端口号修改完毕
2. vue基础回顾
2.1 文本插值
<template>
<div class="hello">
{{ name }}
</div>
</template>
<script>
export default {
data() {
return {
name: 'I LOVE YOU'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
2.2 属性绑定
为标签的属性绑定data方法中返回的属性
<template>
<div class="hello">
{{ name }}
<input type="name" :value="age">
</div>
</template>
<script>
export default {
data() {
return {
name: 'I LOVE YOU',
age: 18
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
2.3 事件绑定
<template>
<div class="hello">
{{ name }}
<input type="name" :value="age">
<!-- 点击事件 -->
<button @click="dianji()">点击</button>
</div>
</template>
<script>
export default {
data() {
return {
name: 'I LOVE YOU',
age: 18
}
},
methods: {
dianji() {
alert('点击了')
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
2.4 双向绑定
通过v-model将数据区的数据与输入框数据双向绑定
<template>
<div class="hello">
{{ name }}
<input type="name" :value="age">
<!-- 点击事件 -->
<button @click="dianji()">点击</button>
<input type="text" v-model="name">
</div>
</template>
<script>
export default {
data() {
return {
name: 'I LOVE YOU',
age: 18
}
},
methods: {
dianji() {
alert('点击了')
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
2.5 条件渲染
<template>
<div class="hello">
<div v-if="sex==1">
男
</div>
<div v-else-if="sex==2">
女
</div>
<div v-else>
未知
</div>
</div>
</template>
<script>
export default {
data() {
return {
sex:2
}
},
methods: {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
2.6 axios
Axios是一个基于promise的网络请求库,作用于浏览器和node.js中,用于发ajax异步请求,实现前后端交互
2.6.1 下载axios
npm install axios
2.6.2 在项目中使用axios
2.6.3 axios的API
- url:请求路径
- data:请求体数据,最常见的是JSON格式
- config:配置对象,可以设置查询参数,请求头信息
2.6.4 axios请求的跨域问题
正常情况下,前端数据一个服务,后端属于一个服务,在不同的域里,所以会出现跨域问题,解决方法就是在vue.config.js文件中配置代理:
<template>
<button @click="request11()"></button>
</div>
</template>
<script>
// 导入axios
import axios from 'axios'
export default {
data() {
return {
sex:2
}
},
methods: {
request11(){
axios.post('http://localhost:8080/user/login',{
username:'admin', // json数据
password:'123456'
}).then(res=>{ // 成功回调
console.log(res.data)
}).catch(err=>{ // 错误回调
console.log(err)
})
}
}
}
</script>
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
port: 7070,
proxy:{
// 配置请求代理,用于将特定请求转发到后端服务器,解决跨域问题
'/api':{
// 将请求转发到的目标服务器地址
target:'http://localhost:8080',
changeOrigin:true, // 是否更改请求的来源,通常用于处理虚拟主机的情况
pathRewrite:{
'^/api':'' // 移除请求路径中的 '/api' 前缀,以便目标服务器正确处理请求
}
}
}
}
})
2.6.5 发送请求
- 方式一
<template>
<div class="hello">
<div v-if="sex==1">
男
</div>
<div v-else-if="sex==2">
女
</div>
<div v-else>
未知
</div>
<button @click="request11()">按钮</button>
</div>
</template>
<script>
// 导入axios
import axios from 'axios'
export default {
data() {
return {
sex:2
}
},
methods: {
request11(){
axios.post('http://localhost:8080/user/login',{
username:'admin', // json数据
password:'123456'
}).then(res=>{ // 成功回调
console.log(res.data)
}).catch(err=>{ // 错误回调
console.log(err)
})
},
req(){
axios.get('http://localhost:8080/user/getUserById',{
// 携带请求头
headers:{
token:'xxxxxxxxxxxxxxxxxxxxxxxxxx'
}
}).then(res=>{
console.log(res.data)
}).catch(err=>{
console.log(err)
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
- 方式二
<button @click="handleSend()"></button>
handleSend(){
axios({
url:'http://localhost:8080/user/login',
method:'post',
data:{
username:'admin',
password:'<PASSWORD>'
}
}).then(res=>{
console.log(res.data)
axios({
url:'http://localhost:8080/user/info',
method:'get',
headers:{
token:res.data.data.token
}
})
}).catch(err=>{
console.log(err)
})
}
3. 路由Vue-Router
vue属于单页面应用,路由的作用就是根据浏览器路径不同,用不同的视图组件替换掉这个页面内容。实现不同的路径,对应不同的页面展示
3.1 创建带有路由的项目
创建好之后,package.json中有vue-router的依赖
main.js中引入了router,说明这个项目具备了路由功能
3.2 路由配置
路由组成:
- VueRouter:路由器,根据路由请求在路由视图中动态渲染对应的视图组件
- <router-link>:路由链接组件,浏览器会解析成<a>
- <router-view>:路由视图组件,用来展示与路由路径匹配的视图组件
-
index.js中维护路由
- App.vue,<router-link>标签生成超链接
-
<router-view>:路由视图组件不能删掉!!!
路由视图组件起到的是占位符的作用
3.3 路由跳转
有两种方式:
- 标签式
- 编程式
标签式就是上面演示的那种,编程式如下:
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<input type="button" value="编程式跳转" @click="jump()">
</nav>
<!-- <router-view>:路由视图组件 -->
<router-view/>
</div>
</template>
<script>
export default {
methods: {
jump() {
this.$router.push('/about')
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
当用户输入的路径不对时,都会重定向到404页面,一张图告诉你如何重定向到不存在的页面:
4. 嵌套路由
嵌套路由是组件内要切换内容,就需要用到嵌套路由(子路由)
4.1 案例
以以下这个案例给大家分析:
实现步骤:
1)安装并导入elementui,实现页面布局(container布局容器)--ContainerView.vue
- 下载
使用命令下载elementui:npm i element-ui -S
- 在main.js中导入elementui
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 导入elementui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
// 全局使用elementui
Vue.use(ElementUI);
new Vue({
router,
render: h => h(App)
}).$mount('#app')
- 创建视图组件
<template>
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
}
</script>
<style>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
- 在main.js中配置路由跳转
2)提供子视图组件,用于效果展示
- P1View.vue
<template>
<div>
这是P1View
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
- P2View.vue
<template>
<div>
这是P2View
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
- P3View.vue
<template>
<div>
这是P3View
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
3)在src/router/index.js中配置路由映射规则(嵌套路由配置)
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [
// 维护路由表,某个路由路径对应哪个视图组件
{
path: '/',
name: 'home',
// 静态加载
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
// 懒加载
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
},
{
path: '/test',
component: () => import( '../views/404View.vue')
},
{
path: '/c',
component: () => import( '../views/container/containerView.vue'),
// 重定向到p1
redirect: '/c/p1',
children:[
{
path: '/c/p1',
component: () => import( '../views/container/P1View.vue')
},
{
path: '/c/p2',
component: () => import( '../views/container/P2View.vue')
},
{
path: '/c/p3',
component: () => import( '../views/container/P3View.vue')
}
]
},
{
path:"*",
redirect: '/404'
}
]
const router = new VueRouter({
routes
})
export default router
4)在布局容器视图中添加<router-view>,实现子视图组件展示
<template>
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>
<!-- 占位符 -->
<router-view/>
</el-main>
</el-container>
</el-container>
</template>
5)在布局容器视图中添加<router-link>,实现路由请求
<template>
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">
<!-- 路由请求:生成超链接 -->
<router-link to="/c/p1">P1</router-link><br>
<router-link to="/c/p2">P2</router-link><br>
<router-link to="/c/p3">P3</router-link>
</el-aside>
<el-main>
<!-- 占位符 -->
<router-view/>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
}
</script>
<style>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
5. 状态管理vuex
5.1 介绍
- vuex是一个专门为Vue.js应用程序开发的状态管理库
- vuex可以在多个组件之间共享数据,并且共享的数据是响应式的,即数据的变更能即使渲染到模板
- vuex采用集中式存储管理所有组件的状态
5.2 vuex核心概念
5.3 使用方式
5.3.1 创建携带vuex的项目
- index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 集中管理多个组件共享的数据
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
- main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store, // 创建vuex实例
render: h => h(App)
}).$mount('#app')
5.3.2 定义展示共享数据
5.3.3 在mutations中定义函数,修改共享数据
5.3.4 在actions中定义函数,用于调用mutation
// import { set } from 'core-js/core/dict'
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
// 集中管理多个组件共享的数据
export default new Vuex.Store({
// 共享数据
state: {
name:'未登录游客'
},
getters: {
},
// 修改共享数据只能通过mutation实现,必须是同步操作
mutations: {
setName(state, name){
state.name = name
}
},
// 封装异步操作,不能直接修改共享数据,只能通过mutation来实现
actions: {
setNameByAxios(context){
axios({
url:"/api/getUserInfo",
method:"POST",
data:{
username:"admin",
password:"123456"
}
}).then(res=>{
if(res.data.code == 1){
// 异步请求后,需要修改共享数据,只能通过mutation来实现
// 在action中调用mutation中定义的setName方法
context.commit("setName",res.data.username)
}
})
}
},
modules: {
}
})
在vue.config.js 配置重定向
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8081,
proxy: {
// 将含有/api的字段重定向成 http://localhost:8080
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
// 重写路径,将含有/api的字段替换成 (空)
'^/api': ''
}
}
}
}
})
6. TypeScript
6.1 TypeScript介绍
-
TypeScript是微软推出的开源语言
-
TypeScript是JavaScript的超集(js有的ts都有)
-
TypeScript在js基础上做了类型支持
-
TypeScript文件扩展名为ts
-
TypeScript可编译成标准的JavaScript,并且在编译时进行类型检查
6.2 TypeScript常用类型
6.2.1 创建携带TypeScripe的项目
类型标注的位置:
- 标注变量
- 标注参数
- 标注返回值
6.2.2 字符串类型、数据类型、布尔类型
// 字符串类型
let str: string = 'I LOVE YOU 1314';
// 数字类型
let num: number = 1314;
// 布尔类型
let bool: boolean = true;
console.log(str, num, bool);
console.log("-------------------------");
// 字面量类型
function printTest(s:string,alignment:'left'|'center'|'right'){
console.log(s,alignment)
}
// 调用函数
printTest('I LOVE YOU 1314','left');
console.log("-------------------------");
// interface 接口类型
interface Cat{
name:string;
// ? 可选属性
age?:number;
}
// 定义变量,并且指定为Cat类型
const c1:Cat = {name:'Tom',age:10}
const c2:Cat = {name:'Tom'}
console.log("-------------------------");
// class类 - 基本使用
class User{
name:string;
constructor(name:string){
this.name = name;
}
study(){
console.log('I am '+this.name);
}
}
// 使用User类型
const user = new User('Tom');
// 输出类中的属性
console.log(user.name);
// 调用类中的方法
user.study();
console.log("-------------------------");
// class类 - 实现接口
interface Animal{
name:string;
eat():void;
}
// 定义一个类实现Animal接口
class Bird implements Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat():void{
console.log('I am eating');
}
}
// 创建类型为Bird的对象
const bird = new Bird('小鸟');
console.log(bird.name);
bird.eat();
// class类 - 继承Bird类
class Parrot extends Bird{
say(){
console.log(this.name+' can say');
}
}
const parrot = new Parrot('鹦鹉');
console.log(parrot.name);
parrot.eat();
parrot.say();