Vue2
简介
Vue是一套用于构建用户界面的渐进式框架,与其它大型框架不同的是,Vue被设计为可以自底层向上逐层应用
Vue的核心库只关注视图,不仅易于上手,还便于与第三方库或既有项目整合
VUE是一款由
数据驱动视图的前端框架,它基于MVVM(Model View View Model)模式,实现数据与视图的双向绑定VUE是渐进式框架,VUE提供了一个核心库,用于渲染视图,它占空间小,能完成VUE中的常规基础操作

环境准备
启动powershell,安装vue脚手架
npm install -g @vue/cli
创建一个空文件夹作为项目工程目录
当前目录下启动cmd,打开vue工程的图形管理界面
vue ui
如果未打开浏览器,访问:http://localhost:8000/project/select 查看图形界面
创建项目
包管理器选用npm,选择手动配置项目,添加功能Router和Vuex
vue版本改成2.x,Lint错误集点选择第一项即可
使用此向导创建的vue工程自带git功能
安装vue开发者工具devtools
开发工具下载:https://devtools.vuejs.org/guide/installation.html
在项目目录下启动cmd,输入npm run serve,启动前端服务器
官网配置信息参考:https://webpack.js.org/configuration/dev-server/
修改服务端口
在vue.config.js文件下添加配置
/*引入vue配置*/
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 7070
}
})
跨域访问
在前端服务器中添加代理
防止跨域问题
devServer: {
proxy: {
'/api': {
//目标服务器
target: 'http://localhost:8080',
changeOrigin: true,
/*去除路径中的 /api */
pathRewrite: { '^/api': '' },
},
}
}
代理配置
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
'/api': {
//目标服务器
target: 'http://localhost:8080',
changeOrigin: true,
/*去除路径中的 /api */
pathRewrite: { '^/api': '' },
},
},
port: 7070
}
})
前端测试通过代理访问后端服务器:http://localhost:7070/api/demo
此时真实访问地址:localhost:8080/demo
原理:
浏览器端不允许跨域,但是服务器允许,在本地npm run serve等命令时实际上用了node运行了一个前端服务器,我们的请求先由浏览器发送给了前端服务器,然后通过前端服务器根据我们自己的proxy配置来将请求发送给后端服务器
Vue项目结构
基本模块有:
- assets 静态资源
- components 可重用组件
- router 路由
- store 数据共享
- views 视图组件
可能还会有的模块:
api 负责和后端api交互,发送fetch,xhr请求,接收响应等
plugins 插件
Vue组件
vue组件一般都以.vue结尾
每个组件一般由三个部分组成
<template></template>
<script></script>
<style></style>
- template 模板部分,由它生成html代码
- script 代码部分,控制模块的数据来源和行为
- style 样式部分
App.vue
vue的最顶层组件
app.vue是主组件,是页面入口文件,是vue页面资源的首加载项
所有的页面都是在app.vue中进行切换的
可以理解为所有的路由都是app.vue的子组件
app.vue中不但可以当做是网站首页,也可以写所有页面中公共需要的动画或者样式,不在上面写代码也可以
我们也可以改写此组件,可以发现实时更新到了浏览器中
<template>
<h1>Hello,world</h1>
</template>
<script></script>
我们可以通过JavaScript动态获取后端数据来渲染到模板上
Vue约定——options
在Vue框架中,所有的JavaScript代码都需要导出一个options对象
options对象内部可以带有data属性,用于返回数据
{{msg}}两个大括号嵌套用于获取data属性中的数据
<template>
<h1>{{msg}}</h1>
</template>
<script>
const options = {
data: function(){
return {msg:"Hello Vue!"};
}
};
export default options;
</script>
通过代码我们可以发现,vue通过main.js来调用app.vue这个组件
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
通过vue提供的
h函数来解析app.vue里面的内容,形成虚拟节点然后通过
$mount函数整合到html中,这里通过id选择器绑定到原始页面index.html中去
我们如果想替换原始App.vue组件,只需要修改import语句即可
例如:import App from './views/ExampleView1.vue'
Vue绑定
一般来说标签内容用 {{}}绑定
标签属性内容用:绑定
标签属性事件用@绑定
表单属性一般才会使用双向绑定
绑定文本
template内只能有一个顶层根元素
<!--这种写法是错误的-->
<template>
<h1>{{name}}{{age}}</h1>
</template>
<!--这种写法是正确的-->
<template>
<div>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
</div>
</template>
<script>
const options = {
data: function(){
return {name:"张三",age:20};
}
};
export default options;
</script>
==data属性必须是一个函数,函数返回的对象就是需要返回的数据==
★vue标签属性绑定
使用v-bind绑定标签属性
<template>
<div>
<div><input type="text" v-bind:value="msg"/></div>
<div><input type="date" v-bind:value="date"/></div>
</div>
</template>
<script>
const options = {
data: function(){
return {
msg:"Hello Vue!",
date:"2022-10-1"
};
}
};
export default options;
</script>
由于v-bind经常被使用到因此也可以省略只保留 :value=”xxx”
<div><input type="text" :value="age"/></div>
★vue事件绑定
提供一个methods属性,里面可以存放一些方法
<template>
<div>
<div>
<input type="button" value="点我" v-on:click="m1"/>
</div>
</div>
</template>
<script>
const options = {
data: function(){
return {};
},
methods : {
m1: function(){
alert("m1");
},
/* 可以省略function简写 */
m2() {
alert("m2");
}
}
};
export default options;
</script>
v-on也有简写模式 例如可以改成 @click=”m1”
this
数字加减操作
在函数中的this代表返回的data属性
如果要在methods中操作data中某个属性的值就需要用this来引用这个属性
<template>
<div>
<div>
<input type="button" value="加1" @click="m1"/>
</div>
<div>
<input type="button" value="减1" @click="m2"/>
</div>
<div>{{count}}</div>
</div>
</template>
<script>
const options = {
data: function(){
return {count: 0};
},
methods : {
m1(){
this.count ++;
},
m2() {
this.count --;
}
}
};
export default options;
</script>
★双向绑定
原来我们只是将js中的数据绑定到了标签属性上,但是用户输入的属性并不会绑定到js的属性上,因此我们利用双向绑定来实现用户输入数据绑定到js
<template>
<div>
<div>
<label for="">请输入姓名</label>
<input type="text" v-model="name"/>
</div>
<div>
<label for="">请输入年龄</label>
<input type="text" v-model="age"/>
</div>
</div>
</template>
<script>
const options = {
data: function(){
return {
name:"张三",
age:20
};
},
methods : {}
};
export default options;
</script>
在浏览器中修改数据,发现调试工具中js中的数据也改变了
利用vue调试工具来查看js中数据的变更
单选框
<template>
<div>
<div>
<label for="">请选择性别</label>
男 <input type="radio" value="男" v-model="sex"/>
女 <input type="radio" value="女" v-model="sex"/>
</div>
</div>
</template>
<script>
const options = {
data: function(){
return {
sex:'男'
};
},
methods : {}
};
export default options;
</script>
多选框
此时js中的数据用数组存储
<template>
<div>
<div>
<label for="">请选择爱好</label>
游泳 <input type="checkbox" value="游泳" v-model="hobby"/>
跳舞 <input type="checkbox" value="跳舞" v-model="hobby"/>
打篮球 <input type="checkbox" value="打篮球" v-model="hobby"/>
</div>
</div>
</template>
<script>
const options = {
data: function(){
return {
hobby: []
};
},
methods : {}
};
export default options;
</script>
下拉框
<template>
<div>
<div>
<label for="">请选择你的专业</label>
<select v-model="subject">
<option value="计科">计科</option>
<option value="软件">软件</option>
<option value="物联网">物联网</option>
<option value="信息安全">信息安全</option>
</select>
</div>
</div>
</template>
<script>
const options = {
data: function(){
return {
subject: []
};
},
methods : {}
};
export default options;
</script>
计算属性
文本拼值
可以使用 + 将两个字符串拼接,当然我们也可以直接调用方法
<template>
<div>
<h2>{{fullName()}}</h2>
</div>
</template>
<script>
const options = {
data: function(){
return {
firstName:"三",
lastName:"张"
};
},
methods : {
fullName(){
return this.lastName + this.firstName;
}
}
};
export default options;
</script>
这种方法的弊端是每次取值都要调用方法
这种方法不是最好的,我们可以使用计算属性来取值
- 写法和methods类似,当然这种方法就不用加括号了
- 计算属性是具有缓存的,如果第一次使用了此属性,那么之后再用到便不会重复进入方法加载属性
<template>
<div>
<h2>{{fullName}}</h2>
</div>
</template>
<script>
const options = {
data: function(){
return {
firstName:"三",
lastName:"张"
};
},
methods : {
},
computed: {
fullName() {
return this.lastName + this.firstName;
}
}
};
export default options;
</script>
v-for遍历集合
使用v-for属性来遍历集合,需要指定遍历
语法:(元素,索引) in 数组
需要指定key,一般绑定index
<div v-for="(x,index) in res.data" :key="index">
{{ x }}
<br>
</div>
接收后端传值并遍历数组
<template>
<div>
<div>
<input type="button" value="获取远程数据" @click="getRequest">
</div>
<div>
响应的数据:
<!--必须指定key绑定为index,即遍历的索引-->
<div v-for="(x,index) in res.data" :key="index">
<!--取到遍历的每个属性-->
{{x}}
<br>
</div>
</div>
</template>
<script>
import axios from 'axios'
const options = {
data: function (){
return {
res : ""
}
},
methods : {
async getRequest(){
let name = encodeURIComponent("os467");
let password = "os4670";
//发送请求头的数据
axios.post('/api/demo',{},
{
params:{
name: name,
password: password
}
}
).then(res => {
this.res = res;
});
}
}
};
export default options;
</script>
v-if v-else条件渲染
如果没有数据的时候显示暂无数据的字段
<div>
响应的数据:
<div v-if="res.length > 0">
<div v-for="(x,index) in res" :key="index">
{{ x }}
<br>
</div>
</div>
<div v-else>暂无学生数据</div>
</div>
v-else必须和v-if连接使用
组件加载完毕函数
在页面渲染完毕后执行的函数,如果需要用到options中的函数需要用到this
const options = {
mounted() {
this.getRequest();
}
}
组件重用
子组件
设计按钮子组件
<template>
<div class="button">
<button class="up" @click="buttonDown">
按钮
</button>
</div>
</template>
<script>
const options = {
methods:{
buttonDown(){
alert("触发了按钮");
}
}
};
export default options;
</script>
<style>
button,.up {
color: #42b983;
border-top-color: #cccccc;
border-left-color: #cccccc;
}
</style>
子组件插槽
<slot></slot>标签,在父组件使用子组件的时候可以在子组件的标签内部添加内容,slot仅起到占位作用,会被添加的内容替换掉
<template>
<div class="button">
<button class="up" @click="buttonDown">
<slot></slot>
</button>
</div>
</template>
父组件
在页面中使用子组件的标签
<template>
<div>
重用组件:
<my-components></my-components>
</div>
</template>
<script>
import MyComponents from "@/components/MyComponents";
const options = {
components:{
MyComponents
}
};
export default options;
</script>
vue组件自定义属性
在子组件的options中添加props属性
<template>
<div class="button" :class="[type,size]"><button class="up" @click="buttonDown">
按钮
</button></div>
</template>
<script>
const options = {
props:["type","size"],
methods:{
buttonDown(){
alert("触发了按钮");
}
}
};
export default options;
</script>
这样就可以在父模块使用该组件的时候添加标签,多个属性用
class="[field1,field2]"表示
★路由Router
vue属于单页面应用,所谓的路由,就是根据浏览器路径不同,用不同的视图组件替换这个页面内容展示
配置路由
path: 路由的路径
component: 用于替换的组件
这里我们使用静态import导入的方式导入vue组件
import Vue from 'vue'
import VueRouter from 'vue-router'
import TableComponent from "@/components/TableComponent";
Vue.use(VueRouter)
const routes = [
{
path:"/",
component:TableComponent
}
]
const router = new VueRouter({
routes
})
export default router
在main.js改为导入我们的路由文件,默认访问index.js
import router from './router/myRouter'
在主页面中引入路由组件
<router-view></router-view> : 需要被替换的路由组件
<template>
<div>
<el-container>
<el-aside width="200px">
<cascader-component></cascader-component>
</el-aside>
<el-container>
<el-header>标题</el-header>
<el-main>
<div>
<router-view></router-view>
</div>
</el-main>
<el-footer>页尾</el-footer>
</el-container>
</el-container>
</div>
</template>
动态导入组件
当打包构建应用时,JS包会变得非常大,影响页面加载
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效
结合Vue的异步组件和Webpack的代码分割功能,轻松实现路由组件的懒加载(按需加载)
- 只需要将component用一个动态import函数导入即可
- 此时只有当用到此组件时才会加载js代码,提高了主页面的加载速度
const routes = [
{
path:"/",
component:() => import('@/components/TableComponent.vue')
}
]
嵌套路由
定义子路由
子路由路径和父路由的路径合在一起才是最终访问子路由的路径
const routes = [
{
path:"/",
component:() => import('@/components/TableComponent.vue'),
children:[
{
path:'/text',
component:() => import('@/components/tableComponentChildren/TextComponent.vue')
},
{
path: '/button',
component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
}
]
}
]
在父路由组件中加入<router-view></router-view> 标签,用来显示子路由组件
通过重定向加载子组件
redirect:加载子组件的地址
const routes = [
{
path:"/",
component:() => import('@/components/TableComponent.vue'),
redirect:'/button',
children:[
{
path:'/text',
component:() => import('@/components/tableComponentChildren/TextComponent.vue')
},
{
path: '/button',
component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
}
]
}
]
匹配其它路由
例如输入未知的路由,我们可以设置一个通配的404NotFound组件路由
***** 号匹配所有无路由规则的路径
const routes = [
{
path:"/",
component:() => import('@/components/TableComponent.vue'),
redirect:'/button',
children:[
{
path:'/text',
component:() => import('@/components/tableComponentChildren/TextComponent.vue')
},
{
path: '/button',
component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
}
]
},
{
path: '/404',
component:() => import('@/components/notFoundComponent/NotFoundComponent.vue')
},
{
path:'*',
redirect: '/404'
}
]
路由跳转
使用<router-link></router-link> 可以实现路由地址跳转
下面实现了两个路由间的互相跳转
<template>
<div>
<h1>员工列表</h1>
<router-link to="/text">
<el-button type="primary" size="mini">跳转介绍文本</el-button>
</router-link>
</div>
</template>
<script>
export default {
name: "ToTextComponent.vue"
}
</script>
<style scoped>
</style>
<template>
<div>
<h3>子路由测试文本内容</h3>
<router-link to="/button">
<el-button type="primary" size="mini">点击返回</el-button>
</router-link>
</div>
</template>
<script>
export default {
name: "TextComponent",
}
</script>
<style scoped>
</style>
函数方式实现路由跳转
jump(url) {
this.$router.push(url);
}
动态路由
添加路由
从后端获取路由数据,动态的添加到路由列表中
- 这里直接用i循环取1到5拼接字符串来模拟后端传来的不同路由数据
<script>
export default {
mounted() {
//动态添加路由
for (let i = 1; i <= 5; i++) {
this.$router.addRoute('main',
{
path:`/p${i}`,
name:`p${i}`,
component: ()=> import(`@/components/main/children/childrenMenu${i}.vue`)
}
);
}
},
name: "mainPage"
}
</script>
第一个参数为父路由name值,第二个参数为子路由对象
重置路由
一般来说,不同用户有不同的路由,但是在注销后我们需要把路由重置为初始的状态,以提供下一个用户登录使用
(Vue2没有提供删除路由的方法,但是在Vue3中提供了)
这里直接初始化路由
export function resetRouter(){
router.matcher = new VueRouter({routes}).matcher
}
<template>
<div>
<el-button size="mini" @click="resetRouter">重置路由</el-button>
</div>
</template>
<script>
import {resetRouter} from "@/router/myRouter";
export default {
methods:{
resetRouter(){
resetRouter();
}
}
}
</script>
刷新页面
由于vue是单页面组件,如果用户刷新页面则相当于重置了vue组件,因此动态路由也会失效
解决方法
我们可以将我们的路由数据存入浏览器,以便于下次页面刷新的时候使用
浏览器为我们提供了两个对象来存放数据
localStorage
- 作用范围:浏览器关闭后依旧保存数据
sessionStorage
- 作用范围:以浏览器标签页为单位,关闭标签页时数据会被清除
例子:
//将数据保存到本地
sessionStorage.setItem("randomInitFerryRes",JSON.stringify(ferryRes));
mounted() {
//页面加载的时候重新从session域读取
let randomInitFerryRes = sessionStorage.getItem("randomInitFerryRes");
if (randomInitFerryRes){
this.ferryRes = JSON.parse(randomInitFerryRes);
}
}
导出动态路由js
将刚才的动态路由方法添加到我们的动态路由js文件中作为方法导出
export function addMenuRoutes(){
//动态添加路由
for (let i = 1; i <= 5; i++) {
router.addRoute('main',
{
path:`/p${i}`,
name:`p${i}`,
component: ()=> import(`@/components/main/children/childrenMenu${i}.vue`)
}
);
}
}
组件数据共享VueX
响应式数据展示技术
设置共享数据
state 用于存储组件之间需要共享的数据
mutations 用于定义方法来更新共享数据信息
- 方法第一个参数:传入state对象
- 方法第二个参数:传入更新的值
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
msg:''
},
getters: {
},
mutations: {
updateMsg(state,newMsg){
state.msg = newMsg;
}
},
actions: {
},
modules: {
}
})
修改共享数据
子组件中提供更新数据修改的方法
commit 更新共享数据值
<template>
<div>
子组件
<el-input v-model="msg" @change="updateMsg"></el-input>
</div>
</template>
<script>
export default {
name: "TestChildren",
methods:{
updateMsg(){
this.$store.commit('updateMsg',this.msg);
}
},
data(){
return {
msg:''
}
}
}
</script>
<style scoped>
</style>
使用VueX生成共享数据更新方法
import {mapMutations} from 'vuex'
methods:{
...mapMutations(['updateMsg'])
}
显示共享数据
其它组件获取共享数据
有以下三种方法
- 直接通过store获取即可
{{$store.state.msg}}
- 使用计算属性获取
<template>
<div>
父组件数据:
{{msg}}
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "TestComponent",
computed:{
msg(){
return this.$store.state.msg;
}
}
}
</script>
<style scoped>
</style>
- 使用VueX提供的计算属性函数
- 数组中提供需要自动获取共享数据的属性名
- 这种方法针对有大量需要获取的共享数据,可以高效的开发
import {mapState} from 'vuex'
computed: mapState(['msg'])
/*或者如果需要在对象中写入,则使用展开运算符*/
computed: {
...mapState(['msg'])
}
异步获取共享数据
一般我们都会从服务器获取数据存入store中以提供共享数据
而mutations中只能包含使得改动立刻生效的代码,不推荐使用await修饰的代码
因此我们使用actions来向服务器发起请求获取共享数据
store中 所有的异步操作 必须放在actions内完成,不能放在mutations中(因为devtools无法监测mutations中的异步)
用actions中的updateMsg2方法为mutations中的方法参数传值
mutations: {
updateMsg(state,msg){
state.msg = msg.msg;
state.msg2 = msg.msg2;
}
},
actions: {
updateMsgAction(context){
context.commit('updateMsg',{
msg:"异步数据1",
msg2:"异步数据2"
});
}
},
<template>
<div>
子组件
<el-button @click="updateMsgAction"></el-button>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name: "TestChildren",
methods:{
...mapActions(['updateMsgAction'])
},
data(){
return {
msg:'',
msg2:''
}
}
}
mapActions中其实就是生成了
this.$store.dispatch('updateMsg',args)方法,返回的是promise对象
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com