DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<h1 style="text-align: center">{{ message }}h1>
<p>1.阻止默认事件p>
<a href="http://www.baidu.com" @click.prevent="showInfo">查看当前时间a>
<p>2.阻止冒泡事件p>
<div style="background: red;width: 200px;height: 200px" @click="showInfo">
<button @click.stop="showInfo">查看当前时间button>
div>
<p>3.事件只触发一次p>
<button @click.once="showInfo">查看当前时间button>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
var app = new Vue({
el:"#app",
data: {
message: "常用的事件修饰符"
},
methods: {
showInfo() {
alert("当前时间:"+new Date())
}
}
});
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<div id="root">
<button-counter>button-counter>
div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
// 定义一个名为 button-counter 的新组件
const buttonCounter = Vue.extend({
template:``,
data() {
return {
count:0
}
},
methods: {
addCount() {
this.count++
}
}
})
var app = new Vue({
el:"#app",
data:{
},
// 局部注册
components:{
buttonCounter
},
methods: {
},
});
script>
html>
因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。
组件主要是为了模块化和代码的复用,将一个大的页面划分为不同的模块,每个小模块就是一个组件,而每一个组件都可以复用在不同的页面当中。
在components目录下新建Student.vue
{{title}}
全局注册
在main.js下进行全局注册
import Student from "./components/Student"
Vue.component('Student',Student)
在App.vue中使用
首页
局部注册
在App.vue中进行局部注册
首页
Prop 是我们可以在组件上注册的一些自定义属性。当一个值传递给一个 prop 属性的时候,它就变成了那个组件实例的一个属性。
向Student组件传递学生列表信息
首页
Student组件接收数据
{{title}}
- {{student.name}}
**注意:**v-model绑定的值不能是props传过来的值,因为props是不可以修改的。
父组件需要先定义一个方法,通过props传递给子组件,然后子组件就可以直接调用这个传递过来的方法。通过该方法可以实现子组件向父组件传递传递数据。
以Student组件为例,在Student组件新增一个添加学生信息的功能并将学生信息传递给父组件的studentList
父组件传递方法给子组件
首页
子组件调用父组件的方法
{{title}}
- {{student.name}}
A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
第一种方式
或者
若想让自定义事件只能触发一次,可以使用once修饰符
或者
第二种方式
<Student ref="studentDemo" />
export default {
......
methods:{
addStudent(student) {
this.studentList.unshift(student)
}
},
mounted() {
this.$refs.studentDemo.$on("addStudent",this.addStudent)
},
......
}
若想让自定义事件只能触发一次,可以使用$once方法
this.$refs.studentDemo.$once("addStudent",this.addStudent)
子组件触发自定义事件
this.$emit("addStudent",数据)
// 解除一个
this.$off("addStudent")
// 解除多个
this.$off(["addStudent"])
// 解除所有的自定义事件
this.$off()
一种组件间通信的方式,适用于任意组件间通信。
在main.js中安装
new Vue({
render: h => h(App),
beforeCreate() {
// 安装总线
Vue.prototype.$bus = this
}
}).$mount('#app')
在需要接收数据的组件中绑定事件,当有组件触发事件时就会回调到这里
......
,
methods:{
addStudent(student) {
this.studentList.unshift(student)
}
},
mounted() {
// 绑定事件
this.$bus.$on("addStudent", this.addStudent)
},
beforeDestroy() {
// 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
this.$bus.$off("addStudent")
}
......
// 触发绑定的事件
this.$bus.$emit("addStudent",student)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。主要适用于多个组件需要共享数据的场景。
默认会安装最新的vuex,而vue2只能用vuex的3版本,所以安装时指定对应的版本号
# 指定版本号
cnpm i vuex@3 --save
# 不指定版本号
cnpm i vuex --save
在src/store目录里面新建index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备state对象:保存具体的数据
const state = {
}
// 准备mutations对象:修改state中的数据
const mutations = {
}
// 准备actions对象:响应组件中用户的动作
const actions = {
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
在main.js中引入
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)
// 准备state对象——保存具体的数据
const state = {
count:0
}
// 准备mutations对象——修改state中的数据
// 一条重要的原则就是要记住 mutation 必须是同步函数。
// https://vuex.vuejs.org/zh/guide/mutations.html
const mutations = {
// 对state中的count字段进行加法操作
addCount(state,value) {
console.log('mutations中的addCount被调用了', state, value)
state.count += value
}
}
// 准备actions对象——响应组件中用户的动作
const actions = {
// 对state中的count字段进行加法操作
actionAddCount(context,value) {
console.log('actions中的actionAddCount被调用了', context, value)
// actions 中的方法最终也是调用的mutations里面的方法
// 在调用mutations中的方法前可以做一些其它操作
context.commit('addCount',value)
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
this.$store.state.count
this.$store.dispatch('action中的方法名',参数) 或者 this.$store.commit('mutations中的方法名',参数)
当state中的数据需要经过加工后再使用时,可以使用getters加工。在store.js中追加getters配置
......
const getters = {
bigCount(state){
// 将count放大十倍
return state.count * 10
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
组件中读取$store.getters.bigCount
用于帮助我们映射state中的数据为计算属性。使得我们访问state中的数据就跟访问data中的数据一样
import {mapState} from 'vuex'
export default {
name: 'App',
data(){
return {
}
},
computed: {
// 借助mapState生成计算属性:count(对象写法)
// ...mapState({count:'count'}),
// 借助mapState生成计算属性:count(数组写法)
...mapState(['count']),
},
}
用于帮助我们映射getters中的数据为计算属性。使得我们访问getters中的数据就跟访问data中的数据一样。
import {mapGetters} from 'vuex'
export default {
name: 'App',
data(){
return {
}
},
computed: {
// 借助 mapGetters生成计算属性:bigCount(对象写法)
// ...mapGetters({bigCount:'bigCount'})
// 借助 mapGetters生成计算属性:bigCount(数组写法)
...mapGetters(['bigCount'])
}
}
用于帮助我们生成与actions对应的方法,使得我们访问actions中的方法就跟访问methods中的数据一样。
若需要传递参数,在模板中绑定事件时传递好参数,否则参数是事件对象,如actionAddCount(2)
import {mapActions} from 'vuex'
export default {
methods:{
// ...mapActions({actionAddCount:'actionAddCount'})
...mapActions(['actionAddCount'])
}
}
用于帮助我们生成与mapMutations对应的方法,使得我们访问mapMutations中的方法就跟访问methods中的数据一样。
若需要传递参数,在模板中绑定事件时传递好参数,否则参数是事件对象,如addCount(2)
import {mapMutations} from 'vuex'
export default {
methods:{
// 靠mapMutations生成:addCount(对象形式)
// ...mapMutations({addCount:'addCount'}),
// 靠mapMutations生成:addCount(数组形式)
...mapMutations(['addCount']),
}
}
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
修改src/store/index.js
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)
const moduleA = {
namespaced:true,// 开启命名空间
state:{},
mutations: {},
actions: {},
getters: {}
}
const moduleB = {
namespaced:true, // 开启命名空间
state:{},
mutations: {},
actions: {},
getters: {}
}
// 创建并暴露store
export default new Vuex.Store({
modules:{
moduleA:moduleA,
moduleB:moduleB
}
})
// 方式一:自己直接读取
$store.state.moduleA.count
// 方式二:借助mapState读取:
...mapState('moduleA',['count']),
// 方式一:自己直接读取
this.$store.getters['moduleA/bigCount']
// 方式二:借助mapGetters读取
...mapGetters('moduleA',['bigCount'])
//方式一:自己直接commit
this.$store.commit('moduleA/addCount',2)
//方式二:借助mapMutations:
...mapMutations('moduleA',['addCount']),
//方式一:自己直接dispatch
this.$store.dispatch('moduleA/actionAddCount',2)
//方式二:借助mapActions:
...mapActions('moduleA',['actionAddCount'])
# vue2只能安装vue-router的版本3
cnpm install vue-router@3 --save-dev
# 安装最新版本 vue-router
cnpm install vue-router --save-dev
在src/router下新建index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Vue from 'vue'
// 引入组件
import Home from '../components/Home'
import UserCenter from '../components/UserCenter'
Vue.use(VueRouter)
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/userCenter',
component:UserCenter
},
{
path:'/home',
component:Home
},
{
path: '/', // 这样就会重定向到/home
redirect: "/home"
},
]
})
在main.js中注册
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
在组件中使用
首页
home
userCenter
通过children配置子级路由
export default new VueRouter({
routes:[
{
path:'/home',
component:Home,
children:[ // 通过children配置子级路由
{
path:'news',
component:News
},
{
path:'message',
component:Message,
}
]
},
]
})
跳转要写完整路径
Home
news
message
参数传递
跳转
跳转
接收参数
$route.query.id
$route.query.title
配置路由,声明接收params参数
export default new VueRouter({
routes:[
{
path:'/home',
component:Home,
children:[ // 通过children配置子级路由
{
path:'news',
component:News
},
{
path:'message',
component:Message,
children:[
{
// 使用占位符声明接收params参数
path:'detail/:id/:title',
// 给地址取名 可以直接通过这个名字跳转页面 不需要写全地址
name:'xiangqing',
component: MessageDetail
}
]
}
]
}
]
})
参数传递
跳转
跳转
参数接收
$route.params.id
$route.params.title
让路由组件更方便的收到参数
export default new VueRouter({
routes:[
{
path:'/home',
component:Home,
children:[ // 通过children配置子级路由
{
path:'message',
component:Message,
children:[
{
path:'detail',
// 给地址取名 可以直接通过这个名字跳转页面 不需要写全地址
name:'xiangqing',
component: MessageDetail,
props($route){
return {
// 这里是query方式 params方式:id:$route.params.id,
id:$route.query.id,
title:$route.query.title
}
}
}
]
}
]
}
]
})
接收数据
MessageDetail
- 消息编号:{{id}}
- 消息标题:{{title}}
不借助实现路由跳转,让路由跳转更加灵活
// query方式
this.$router.push({
path:'/home/message/detail',
query:{
id:'123',
title:'哈哈哈'
}
})
// params 方式
this.$router.push({
name:'xiangqing',
params:{
id:'123',
title:'哈哈哈'
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
路由跳转不同组件时保持挂载,不被销毁。
keep-alive是 vue 中的内置组件,能够在组件切换过程中将状态保留在内存中,防止重复的渲染 DOM;
keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们;
keep-alive 可以设置一下 props 属性:
include字符串或者是正则表达式。只有名称匹配的组件才能被缓存
exclude字符串或者是正则表达式。
max数字。最多可以缓存多少组件实例。
匹配首先是检查自身的 name 选项,如果 name 属性不可用,则匹配它的局部注册名称,也就是父组件中 components 选项的键值,匿名组件不能被匹配。
设置了keep-alive缓存的组件,会多出两个生命周期钩子activated 和deactivated
<script>
export default {
name: "News",
activated() {
console.log('xxx组件被激活了')
},
deactivated() {
console.log('xxx组件失活了')
},
}
</script>
对路由进行权限控制,主要有三类:全局守卫、独享守卫、组件内守卫
const router = new VueRouter({
routes:[
{
path:'/userCenter',
component:UserCenter,
meta:{
isAuth:true
}
},
{
path:'/home',
component:Home,
},
{
path: '/', // 这样就会重定向到/home
redirect: "/home"
},
]
})
// 全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
console.log('前置路由守卫', to, from)
if(to.meta.isAuth){ // 判断是否需要鉴权
if(localStorage.getItem('user')==='lzc'){
// 继续往下走
next()
}else{
alert('未登录,无权限查看')
}
}else{
next()
}
})
// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫', to, from)
})
export default router
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Vue from 'vue'
// 引入组件
import Home from '../components/Home'
import UserCenter from '../components/UserCenter'
Vue.use(VueRouter)
const router = new VueRouter({
routes:[
{
path:'/home',
meta:{
isAuth:true
},
component:Home,
beforeEnter(to,from,next){
console.log('beforeEnter', to, from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('user') === 'lzc'){
next()
}else{
alert('暂无权限查看')
}
}else{
next()
}
},
}
]
})
export default router
新建http.js封装get、post等请求
import axios from 'axios'
import {Message} from 'element-ui'
/**
*
* 后端设置 Access-Control-Max-Age, 用来指定本次预检请求的有效期
* 单位为秒, 在此期间不用发出另一条预检请求
* httpResponse.addHeader("Access-Control-Max-Age", "86400");
*
* 使用自定义配置新建一个 axios 实例
* 文档地址:http://www.axios-js.com/zh-cn/docs/#axios-create-config
*
*/
const instance = axios.create({
baseURL: getBaseUrl(),
// 超时时间
timeout: 3000,
/**
* 为true时允许携带cookie
* 如果设置true: 后端设置 Access-Control-Allow-Origin不能为"*" 必须是源地址
* 如: httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("origin"));
*/
withCredentials:true,
headers: {'token':'xxxxxx'}
});
function getBaseUrl() {
// 根据环境返回不同的地址
console.log('getBaseUrl', process.env.NODE_ENV)
if (process.env.NODE_ENV === 'development') {
return 'http://localhost:8088'
} else if (process.env.NODE_ENV === 'production') {
return '生产地址'
}
}
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
console.log("interceptors.request",config)
// 在发送请求之前做些什么
return config;
}, function (error) {
console.log("interceptors.request.error",error)
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
console.log("interceptors.response",response)
// 对响应数据做点什么
return response;
}, function (error) {
console.log('interceptors.response.error',error)
Message.error(error.message)
// 对响应错误做点什么
return Promise.reject(error);
});
/**
* @param {Object} url 接口地址
* @param {Object} params 请求参数
* @param {Object} config 配置, 用来覆盖默认配置
*/
export function get(url, params, config){
return new Promise((resolve, reject) =>{
instance.get(url,{
params:params,
// baseURL: 'http://xxxxxx' 这里也可以指定来覆盖默认的
...config
})
.then(function (response) {
resolve(response.data)
})
.catch(function (error) {
reject(error.message)
});
})
}
/**
* @param {Object} url 接口地址
* @param {Object} params 请求参数
* @param {Object} config 配置, 用来覆盖默认配置
*/
export function post(url, params, config){
return new Promise((resolve, reject) =>{
instance.request({
url:url,
method:'post',
data:params,
...config
})
.then(function (response) {
resolve(response.data)
})
.catch(function (error) {
reject(error.message)
});
})
}
新建api.js用来管理接口
import {get,post} from './http.js'
export const apiTest1 = (params,config) => get("/test1",params,config);
export const apiTest2 = (params,config) => post("/test2",params,config);
使用
// 引入
import {apiTest1,apiTest2} from './utils/api.js'
// 调用 apiTest1
apiTest1({
q:"923226145"
}).then(data=>{
console.log("get data success:",data)
},error=>{
console.log("get data error:",error)
})
// 调用apiTest2
apiTest2({
username:"923226145",
age:20
}).then(data=>{
console.log("get data success:",data)
},error=>{
console.log("get data error:",error)
})
# vue2中less-loader安装7版本的
npm install less less-loader@7.0.0 --save
在style中加入lang="less"才会生效
npm i element-ui -S
按需引入
借助 babel-plugin-component,可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component
npm install babel-plugin-component -D
然后,将 babel.config.js修改为:
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["@babel/preset-env", { "modules": false }]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
接下来,如果你只希望引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
new Vue({
el: '#app',
render: h => h(App)
});
实现一个消息通知工具类
import {Message} from 'element-ui';
/**
* 定义消息提示工具类:这里使用的是 element-ui
*/
export function successMsg(msg, duration = 3000, showClose = false) {
Message({
message: msg,
type: 'success',
duration: duration,
showClose: showClose
});
}
export function warningMsg(msg, duration = 3000, showClose = false) {
Message({
message: msg,
type: 'warning',
duration: duration,
showClose: showClose
});
}
export function errorMsg(msg, duration = 3000, showClose = false) {
Message({
message: msg,
type: 'error',
duration: duration,
showClose: showClose
});
}
使用
import {warningMsg} from '@/common/MessageUtils.js';
warningMsg("警告信息");
新建MenuNode.vue
<template>
<el-submenu v-if="menuInfo.childrenList" :index="menuInfo.id">
<template slot="title">
<i class="el-icon-location"></i>
<span>{{menuInfo.title}}</span>
</template>
<MenuNode v-for="node in menuInfo.childrenList" :menuInfo = node :key="node.id" />
</el-submenu>
<!-- 没有子组件 -->
<el-menu-item v-else-if="!menuInfo.childrenList" :index="menuInfo.id">
<i class="el-icon-menu"></i>
<span slot="title">{{menuInfo.title}}</span>
</el-menu-item>
</template>
<script>
export default {
name:'MenuNode',
props:["menuInfo"]
}
</script>
<style>
</style>
新建MenuComponent.vue
<template>
<el-menu
default-active="2"
@select="handleSelect"
@open="handleOpen"
@close="handleClose">
<MenuNode v-for="menu in menuList" :menuInfo=menu :key="menu.id" />
</el-menu>
</template>
<script>
import MenuNode from './MenuNode.vue'
export default {
name:'MenuComponent',
components:{MenuNode},
props:["menuList"],
data(){
return {
}
},
methods: {
handleOpen(key, keyPath) {
console.log("handleOpen",key, keyPath);
},
handleClose(key, keyPath) {
console.log("handleClose",key, keyPath);
},
handleSelect(key, keyPath) {
console.log("handleSelect",key, keyPath);
}
},
}
</script>