Skip to content
索引

基础

transition

vue内置动画组件,会在组件创建销毁时添加class,从而可以触发动画效果

不指定name

css
.v-enter-active,
.v-leave-active {
  transition: all 0.4s ease-out;
}

.v-enter-from,
.v-leave-to {
  transform: translateY(-60px);
  opacity: 0;
}
.v-enter-active,
.v-leave-active {
  transition: all 0.4s ease-out;
}

.v-enter-from,
.v-leave-to {
  transform: translateY(-60px);
  opacity: 0;
}

指定name为slide-fade

css
.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.slide-fade-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}
.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.slide-fade-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}

为router-view设置切换动画

vue
<router-view v-slot="{ Component, route }">
    <transition name="slide">
      <component :is="Component" :key="route" />
    </transition>
  </router-view>
<router-view v-slot="{ Component, route }">
    <transition name="slide">
      <component :is="Component" :key="route" />
    </transition>
  </router-view>

按键修饰符

html
<input v-on:keyup.enter="confirmDelete" />  //注意小写
<input v-on:keyup.enter="confirmDelete" />  //注意小写

v-on

@click使用三元运算符

注意,函数后面必须加(),不然不生效

js
@click="allowNext?next():no()"
@click="allowNext?next():no()"

鼠标键盘事件

js
点击单击)@click="方法名" 
双击       @dblclick="方法名" 
鼠标按下    @mousedown="方法名" 
鼠标抬起    @mouseup="方法名" 
鼠标移动    @mousemove="方法名" 
鼠标离开    @mouseleave=方法名
鼠标离开    @mouseout=方法名
mouseleave和mouseout的区别在于mouseout有冒泡行为
鼠标进入    @mouseenter="方法名" 
鼠标进入    @mouseover="方法名" 
mouseenter和mouseover的区别在于mouseover有冒泡行为
鼠标滚轮滚动 @mousewheel="方法名" 
键盘按下     @keydown
键盘弹起     @keyup
键盘按住     @keypress
获取按键的键码 e.keyCode
方法后面跟keyCode值可以直接绑定键盘按键
@keyup.enter  回车
@keyup.up     上键
@keyup.down   下键
@keyup.left   左键
@keyup.right   右键
@keyup.delete  删除键
@keyup.space   空格
点击(单击)@click="方法名" 
双击       @dblclick="方法名" 
鼠标按下    @mousedown="方法名" 
鼠标抬起    @mouseup="方法名" 
鼠标移动    @mousemove="方法名" 
鼠标离开    @mouseleave=“方法名” 
鼠标离开    @mouseout=“方法名” 
mouseleave和mouseout的区别在于mouseout有冒泡行为
鼠标进入    @mouseenter="方法名" 
鼠标进入    @mouseover="方法名" 
mouseenter和mouseover的区别在于mouseover有冒泡行为
鼠标滚轮滚动 @mousewheel="方法名" 
键盘按下     @keydown
键盘弹起     @keyup
键盘按住     @keypress
获取按键的键码 e.keyCode
方法后面跟keyCode值可以直接绑定键盘按键
@keyup.enter  回车
@keyup.up     上键
@keyup.down   下键
@keyup.left   左键
@keyup.right   右键
@keyup.delete  删除键
@keyup.space   空格

v-bind 传入一个对象的所有属性

js
<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

ref

ref可以获取本页面的dom元素

使用ref获取单个dom

bash
<template>
    <div ref="setItemRef">1</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue"
  const setItemRef = ref('')
  onMounted(()=>{
    console.log(setItemRef.value)
  })
</script>
<template>
    <div ref="setItemRef">1</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue"
  const setItemRef = ref('')
  onMounted(()=>{
    console.log(setItemRef.value)
  })
</script>

v-for中使用ref获取多个dom

typescript
<template>
    <div v-for="item in list" :ref="setItemRef" >{{item}}</div>
</template>
<script lang="ts" setup>
type ref = (ref: any) => void
import { onMounted } from "vue"
  const list = [1,2,3,4]
  const itemrefs:Element[] = []
  const setItemRef:ref = el=>{
    if(el){
      itemrefs.push(el)
    }
  }
  onMounted(()=>{
    console.log(itemrefs);
  })
</script>
<template>
    <div v-for="item in list" :ref="setItemRef" >{{item}}</div>
</template>
<script lang="ts" setup>
type ref = (ref: any) => void
import { onMounted } from "vue"
  const list = [1,2,3,4]
  const itemrefs:Element[] = []
  const setItemRef:ref = el=>{
    if(el){
      itemrefs.push(el)
    }
  }
  onMounted(()=>{
    console.log(itemrefs);
  })
</script>

插槽

最简单的插槽

vue
// 子组件使用插槽
<button>
  <slot></slot>
</button>
// 父组件中使用子组件
<component-button>
  slot Content
</component-button>
// 子组件使用插槽
<button>
  <slot></slot>
</button>
// 父组件中使用子组件
<component-button>
  slot Content
</component-button>

具名插槽

vue
// 子组件使用具名插槽
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot> // 不指定name属性则默认为default,即<slot name="default"></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
// 父组件使用子组件
<base-layout>
  <template v-slot:header> // 可以缩写为<template #header>
    <h1>Here might be a page title</h1>
  </template>
  <template v-slot:default> // 可以缩写为<template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>
  <template v-slot:footer>  // 可以缩写为<template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>
// 子组件使用具名插槽
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot> // 不指定name属性则默认为default,即<slot name="default"></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
// 父组件使用子组件
<base-layout>
  <template v-slot:header> // 可以缩写为<template #header>
    <h1>Here might be a page title</h1>
  </template>
  <template v-slot:default> // 可以缩写为<template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>
  <template v-slot:footer>  // 可以缩写为<template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

自定义v-for中item

vue
// 子组件使用 <slot :item="item"></slot>
<template>
  <div v-for="item in list">
    <slot :item="item"></slot>
  </div>
</template>
<script lang="ts" setup>
const list = [1, 2, 3, 4]
</script>
// 父组件自定义item
<template>
    <HelloWorld #slotProps="{ item }">
        <span class="green" v-if="item != 1">{{ item }}年</span>
    </HelloWorld>
</template>
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
<style>
.green {
    color: pink;
}
</style>
// 子组件使用 <slot :item="item"></slot>
<template>
  <div v-for="item in list">
    <slot :item="item"></slot>
  </div>
</template>
<script lang="ts" setup>
const list = [1, 2, 3, 4]
</script>
// 父组件自定义item
<template>
    <HelloWorld #slotProps="{ item }">
        <span class="green" v-if="item != 1">{{ item }}年</span>
    </HelloWorld>
</template>
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
<style>
.green {
    color: pink;
}
</style>

父子通信

父传子

父页面中import导入子组件

js
import son from "../../components/son.vue"
import son from "../../components/son.vue"

components中注册子组件

js
export default {
		components:{
			son
		}
	}
export default {
		components:{
			son
		}
	}

此时子组件便可以在父页面中作为html标签使用,并可以传值至子组件

写法为 <子组件名 传递的变量名="传递的变量值"></子组件名>

js
<son title="有消息,注意查收"></son>
<son title="有消息,注意查收"></son>

子在props中接收(以下为所有类型的接收参数的写法)

js
 props: {
    a: {
      type: String,
      default: ""
    },
    b: {
      type: Number,
      default: 1
    },
    c: {
      type: Boolean,
      default: true
    },
    d: {
      type: Object,
      default: {}
    }
    e: {
      type: Array,
      default:[]
    },
    f: {
      type: Function,
      default: function() {}
    }
  }
 props: {
    a: {
      type: String,
      default: ""
    },
    b: {
      type: Number,
      default: 1
    },
    c: {
      type: Boolean,
      default: true
    },
    d: {
      type: Object,
      default: {}
    }
    e: {
      type: Array,
      default:[]
    },
    f: {
      type: Function,
      default: function() {}
    }
  }

简写:

js
 props: {
    a:  String,
    b:  Number,
    c:  Boolean,
    d:  Function,
    e:  Array,
    f:  Object,
  }
 props: {
    a:  String,
    b:  Number,
    c:  Boolean,
    d:  Function,
    e:  Array,
    f:  Object,
  }

子传父传单个参数

父组件传递给子组件一个方法

js
<son  @getTitle="showtitle"/>
<son  @getTitle="showtitle"/>

子组件通过emit方法,来调用父组件的这个方法,并且可以通过emit的第二个参数来传值给父组件

js
 this.$emit('getTitle', '一个标题名')
 this.$emit('getTitle', '一个标题名')

父组件直接在传递的方法中接收形参即可

js
 showtitle (e) {
            console.log(e)
        }
 showtitle (e) {
            console.log(e)
        }

子传父传多个参数(只支持vue2)

父组件传递给子组件一个方法里传参arguments

vue
  <son  @getTitle="showtitle(arguments)"/>
  <son  @getTitle="showtitle(arguments)"/>

子组件通过emit方法,来调用父组件的这个方法,并且可以通过emit传递多个参数来给父组件

vue
this.$emit('getTitle',1,2,3,4)
this.$emit('getTitle',1,2,3,4)

父组件直接在传递的方法中通过数组下标的形式接收形参即可

js
 showtitle (e) {
           console.log(e[0]);
            console.log(e[1]);
            console.log(e[2]);
            console.log(e[3]);
        }
 showtitle (e) {
           console.log(e[0]);
            console.log(e[1]);
            console.log(e[2]);
            console.log(e[3]);
        }

父传子并实现双向绑定

js
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event"/>
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event"/>

不难理解子组件中必须使用名为value的prop并通过this.$emit('input')来传值

js
export default {
  props: {
    value: String
  },
  methods(){
  	giveFather(){
  		this.$emit('input','title值')
  	}
  }
}
export default {
  props: {
    value: String
  },
  methods(){
  	giveFather(){
  		this.$emit('input','title值')
  	}
  }
}

如果想要更改prop或事件名称,则需要在ChildComponent组件中添加model选项:

js
export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    // 这将允许value属性用于其他用途
    value: String,
    // 使用title代替value作为model的prop
    title: {
      type: String,
      default: 'Default title'
    }
  }
}
export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    // 这将允许value属性用于其他用途
    value: String,
    // 使用title代替value作为model的prop
    title: {
      type: String,
      default: 'Default title'
    }
  }
}

这样设置完后,其实本质上就变成了如下传值

js
<ChildComponent :title="pageTitle" @change="pageTitle = $event"/>
<ChildComponent :title="pageTitle" @change="pageTitle = $event"/>

想要自定义传参则建议使用v-bind.sync="pageTitle"

js
<ChildComponent :title.sync="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<ChildComponent :title.sync="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
js
export default {
  props: {
    title: String
  },
  methods(){
  	giveFather(){
  		this.$emit('update:title','title值')
  	}
  }
}
export default {
  props: {
    title: String
  },
  methods(){
  	giveFather(){
  		this.$emit('update:title','title值')
  	}
  }
}

vue3使用v-model替代vue2的v-bind.sync

直接在组件上使用v-model

父使用v-model传值给子,这里的v-model本质上是:modelValue=“值”,@update:modelValue="值 = $event"这二者的缩写

vue
<template>
   <div>
	<child v-model="num"></child>
	// 上方代码是下方的简写
    <child :modelValue="num" @update:modelValue="num = $event"></child>
   </div>
</template>
<script>
import child from './test.vue'
export default {
    components: { child },
    data () {
        return {
            num: 0
        }
    }
}
 </script>
<template>
   <div>
	<child v-model="num"></child>
	// 上方代码是下方的简写
    <child :modelValue="num" @update:modelValue="num = $event"></child>
   </div>
</template>
<script>
import child from './test.vue'
export default {
    components: { child },
    data () {
        return {
            num: 0
        }
    }
}
 </script>

所以子中通过在props中接收modelValue再$emit(‘update:modelValue’) 来修改父传来的值

js
<template>
    <div>
        <input type="text" @input="a">
    </div>
</template>
<script>
export default {
    props: ["modelValue"],
    methods: {
        a (e) {
            this.$emit('update:modelValue', e.target.value)
        }
    }
}
</script>
<template>
    <div>
        <input type="text" @input="a">
    </div>
</template>
<script>
export default {
    props: ["modelValue"],
    methods: {
        a (e) {
            this.$emit('update:modelValue', e.target.value)
        }
    }
}
</script>

自定义v-model名,用于传递多个v-model

父使用v-model传值给子,这里的v-model本质上是:自定义名=“值”,@update:自定义名="值 = $event"这二者的缩写

js
<ChildComponent v-model:title="pageTitle" v-model:msg="pageTitle"/>
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<ChildComponent v-model:title="pageTitle" v-model:msg="pageTitle"/>
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

所以子中通过在props中接收自定义名,再$emit(‘update:自定义名’) 来修改父传来的值

js
<template>
    <div>
        <input type="text" @input="a">
    </div>
</template>
<script>
export default {
    props: ["title","msg"],
    methods: {
        a (e) {
            this.$emit('update:title', e.target.value)
            this.$emit('update:msg', e.target.value)
        }
    }
}
</script>
<template>
    <div>
        <input type="text" @input="a">
    </div>
</template>
<script>
export default {
    props: ["title","msg"],
    methods: {
        a (e) {
            this.$emit('update:title', e.target.value)
            this.$emit('update:msg', e.target.value)
        }
    }
}
</script>

第三方插件

main.js是vue脚手架入口文件,进行vue根实例的new,并挂载到#app的dom元素上,一般在main.js中导入第三方库

viewerjs

viewer是可放大缩小旋转轮播图片的插件

下载

bash
npm i -s viewerjs
npm i -s viewerjs

demo1,点击按钮查看图片

vue
<template>
    <!-- 挂载viewer到元素上,设置一个id,因为需求不需要展示图片,点击按钮时直接查看图片即可,所以v-show="false"-->
    <div id="viewerjs" v-show="false">
        <img :src="item" v-for="(item,index) in imgarr" :key="index" />
    </div>
    <!-- 点击展示viewer -->
    <el-button type="primary" @click="lookimg">查看材料</el-button>
</template>

<script>
import Viewer from "viewerjs"
import "viewerjs/dist/viewer.css"
export default {
    data () {
        return {
            imgarr: ['https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg']
        }
    },
    methods: {
        lookimg () {
            this.imgarr.push('https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg')
            //显而易见,我们只需要修改imgarr中的图片列表,即可实现不同的图片查看,比如发起后端请求,赋值图片数组,就在这一步进行。
            //更改了图片数组imgarr,并且图片列表使用v-for渲染,需要使用this.$nextTick获取更新后的dom
            this.$nextTick(() => {
                const viewerdom = document.getElementById("viewerjs")
                const viewer = new Viewer(viewerdom, {
                    title: false,
                    hide: () => {
                        viewer.destroy()
                    }
                })
                viewer.show()
            })
        }
    }
}
</script>
<template>
    <!-- 挂载viewer到元素上,设置一个id,因为需求不需要展示图片,点击按钮时直接查看图片即可,所以v-show="false"-->
    <div id="viewerjs" v-show="false">
        <img :src="item" v-for="(item,index) in imgarr" :key="index" />
    </div>
    <!-- 点击展示viewer -->
    <el-button type="primary" @click="lookimg">查看材料</el-button>
</template>

<script>
import Viewer from "viewerjs"
import "viewerjs/dist/viewer.css"
export default {
    data () {
        return {
            imgarr: ['https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg']
        }
    },
    methods: {
        lookimg () {
            this.imgarr.push('https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg')
            //显而易见,我们只需要修改imgarr中的图片列表,即可实现不同的图片查看,比如发起后端请求,赋值图片数组,就在这一步进行。
            //更改了图片数组imgarr,并且图片列表使用v-for渲染,需要使用this.$nextTick获取更新后的dom
            this.$nextTick(() => {
                const viewerdom = document.getElementById("viewerjs")
                const viewer = new Viewer(viewerdom, {
                    title: false,
                    hide: () => {
                        viewer.destroy()
                    }
                })
                viewer.show()
            })
        }
    }
}
</script>

demo2,点击图片查看图片

vue
<template>
    <!-- 为图片设置不同的id -->
    <img :src="item" v-for="(item,index) in imgarr" :key="index" :id="'viewerjs'+index" @click="lookimg(index)" />
</template>

<script>
import Viewer from "viewerjs";
import "viewerjs/dist/viewer.css";
export default {
    data () {
        return {
            imgarr: ['https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg']
        }
    },
    methods: {
        lookimg (index) {
            const viewerdom = document.getElementById("viewerjs" + index);
            const viewer = new Viewer(viewerdom, {
                title: false,
                hide: () => {
                    viewer.destroy()
                }
            })
            viewer.show()
        }
    }
}
</script>

<template>
    <!-- 为图片设置不同的id -->
    <img :src="item" v-for="(item,index) in imgarr" :key="index" :id="'viewerjs'+index" @click="lookimg(index)" />
</template>

<script>
import Viewer from "viewerjs";
import "viewerjs/dist/viewer.css";
export default {
    data () {
        return {
            imgarr: ['https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg']
        }
    },
    methods: {
        lookimg (index) {
            const viewerdom = document.getElementById("viewerjs" + index);
            const viewer = new Viewer(viewerdom, {
                title: false,
                hide: () => {
                    viewer.destroy()
                }
            })
            viewer.show()
        }
    }
}
</script>

v-viewer

效果同上

vue
<template>
    <div class="home images" id="viewerjs">
        <img alt="Vue logo" src="./logo.png" />
        <img alt="Vue logo" src="../../assets/logo.png" />
    </div>
</template>

<script>
import Viewer from "viewerjs";
import "viewerjs/dist/viewer.css";
export default {
    mounted () {
        const aaaa = document.getElementById("viewerjs");
        const viewer = new Viewer(aaaa, {
            // navbar: true,
            title: false,
            // toolbar: {
            //     prev: true,
            //     next: true,
            // },
            // zoomable: true,
            // rotatable: true,
            ready: () => {
                console.log('S');
                viewer.show()
            },
        });
    },
};
</script>
<template>
    <div class="home images" id="viewerjs">
        <img alt="Vue logo" src="./logo.png" />
        <img alt="Vue logo" src="../../assets/logo.png" />
    </div>
</template>

<script>
import Viewer from "viewerjs";
import "viewerjs/dist/viewer.css";
export default {
    mounted () {
        const aaaa = document.getElementById("viewerjs");
        const viewer = new Viewer(aaaa, {
            // navbar: true,
            title: false,
            // toolbar: {
            //     prev: true,
            //     next: true,
            // },
            // zoomable: true,
            // rotatable: true,
            ready: () => {
                console.log('S');
                viewer.show()
            },
        });
    },
};
</script>

less

bash
npm install -D less less-loader@7.3.0 
npm install -D less less-loader@7.3.0 

lang="less"代表设置css为less写法;scoped代表不影响全局样式

css
<style lang="less" scoped> 
html {					 
    body {
        height: 100px;
    }
}
</style>
<style lang="less" scoped> 
html {					 
    body {
        height: 100px;
    }
}
</style>

使用animate.css

bash
npm i -s animate
npm i -s animate

使用

js
import animate from "animate.css";
createApp(App).use(animate).mount('#app')
import animate from "animate.css";
createApp(App).use(animate).mount('#app')

element-plus

element-plus是适用于vue3的组件库

bash
npm i element-plus -s  
npm i element-plus -s  

修改main.js如下

js
import { createApp } from 'vue'  // createApp 作为 vue 的启动函数,返回一个应用实例
import App from './App.vue'
import ElementPlus from 'element-plus'                   //导入ElementPlus
import 'element-plus/lib/theme-chalk/index.css'          //导入ElementPlus样式
import zhCn from 'element-plus/es/locale/lang/zh-cn'     //导入ElementPlus中文包    	     
const app = createApp(App)
app.use(ElementPlus, {locale: zhCn})                     //使用ElementPlus中文包	                 
app.mount('#app')
import { createApp } from 'vue'  // createApp 作为 vue 的启动函数,返回一个应用实例
import App from './App.vue'
import ElementPlus from 'element-plus'                   //导入ElementPlus
import 'element-plus/lib/theme-chalk/index.css'          //导入ElementPlus样式
import zhCn from 'element-plus/es/locale/lang/zh-cn'     //导入ElementPlus中文包    	     
const app = createApp(App)
app.use(ElementPlus, {locale: zhCn})                     //使用ElementPlus中文包	                 
app.mount('#app')
vue
<template>
    {{num}}
</template>

<script>
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
    setup () {
        const store = useStore()
        const addnum = () => {
            store.commit('ADD',data)
}
        return {
            num: computed(() => store.state.num),
            addnum
        }
    }
}
</script>
<template>
    {{num}}
</template>

<script>
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
    setup () {
        const store = useStore()
        const addnum = () => {
            store.commit('ADD',data)
}
        return {
            num: computed(() => store.state.num),
            addnum
        }
    }
}
</script>

vuex等待上一个接口请求完成再进行下一个接口请求

vue
<template>
</template>

<script>
import axios from 'axios'
import { onMounted, watch } from "vue"
import { useStore } from 'vuex'
export default {
    setup () {
        const store = useStore()
        onMounted(() => {
            axios.get('1.json').then(res => {
                store.commit('change', res.data.name)
                console.log(store.state)
            })
        })
        const watchVuex = watch(() => store.state.text, () => {
            console.log('监听到vuex中text数据改变了')
            axios.get('2.json').then(res => {
                console.log(res.data)
            })
        })
        return {
            watchVuex
        }
    }
}
</script>
<template>
</template>

<script>
import axios from 'axios'
import { onMounted, watch } from "vue"
import { useStore } from 'vuex'
export default {
    setup () {
        const store = useStore()
        onMounted(() => {
            axios.get('1.json').then(res => {
                store.commit('change', res.data.name)
                console.log(store.state)
            })
        })
        const watchVuex = watch(() => store.state.text, () => {
            console.log('监听到vuex中text数据改变了')
            axios.get('2.json').then(res => {
                console.log(res.data)
            })
        })
        return {
            watchVuex
        }
    }
}
</script>

axios

初级做法

js
import axios from 'axios'
import VueAxios from 'vue-axios'
app.use(VueAxios, axios)
import axios from 'axios'
import VueAxios from 'vue-axios'
app.use(VueAxios, axios)
js
this.axios.request({ url: '/a.json', method: 'get' })
    .then(res => {
    console.log(res);
})
this.axios.request({ url: '/a.json', method: 'get' })
    .then(res => {
    console.log(res);
})

高级做法

全部封装

统一管理

js
const service = axios.create({
    baseURL: process.env.VUE_APP_URL, // api的base_url
    timeout: 5000 // 请求超时时间
})
const service = axios.create({
    baseURL: process.env.VUE_APP_URL, // api的base_url
    timeout: 5000 // 请求超时时间
})

qs

简单来说,qs 是一个增加了一些安全性的查询字符串解析和序列化字符串的库。

qs.parse()是将URL解析成对象的形式

qs.stringify()是将对象 序列化成URL的形式,以&进行拼接

.env.development/process.env.

.env.development文件中定义url

bash
NODE_ENV = developmentVUE_APP_URL = http://100.100.2.143:8280/api/
NODE_ENV = developmentVUE_APP_URL = http://100.100.2.143:8280/api/

使用

bash
process.env.VUE_APP_URL
process.env.VUE_APP_URL

路由缓存

meta

js
 {
        path: 'demandsServiceTemplate',
        name: 'demandsServiceTemplate',
        component: () => import('.../...vue'),
        meta: {
          keepAlive: true,
          requireAuth: true
        }
}
 {
        path: 'demandsServiceTemplate',
        name: 'demandsServiceTemplate',
        component: () => import('.../...vue'),
        meta: {
          keepAlive: true,
          requireAuth: true
        }
}

vue.config.js

json
const Timestamp = new Date().getTime()
module.exports = {
    chainWebpack: config => {
        config.plugins.delete("html")
        config.plugins.delete("preload")
        config.plugins.delete("prefetch")
    },
    publicPath: "/", //默认的'/'是绝对路径,如果不确定在根路径,改成相对路径'./'
    outputDir: "dist", // 输出文件目录
    assetsDir: "./static",
    // indexPath:'index.html',
    lintOnSave: true, // eslint是否在保存的时候检查
    configureWebpack: {
         output: {
             filename: `[name].${process.env.VUE_APP_Version}.${Timestamp}.js`,
             chunkFilename: `[name].${process.env.VUE_APP_Version}.${Timestamp}.js`
         }
    },
    productionSourceMap: false, // 生产环境是否生成 sourceMap 文件
    // css相关配置
    css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract: true,
        // 开启 CSS source maps?
        sourceMap: false,
        loaderOptions: {
            postcss: {
                plugins: [
                    require("postcss-plugin-px2rem")({
                        // 128.4 63.9
                        // rootValue:128.4,      // 新版本的是这个值
                        // mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
                        // minPixelValue: 3 //设置要替换的最小像素值(3px会被转rem)。 默认 0
                    })
                ]
            }
        }
    },
    // webpack-dev-server 相关配置
    devServer: {
        open: false, //open 在devServer启动且第一次构建完成时,自动用我们的系统的默认浏览器去打开要开发的网页
        host: "192.168.1.85", //localhost 100.100.2.35默认是 192.168.1.27 192.168.31.211。如果你希望服务器外部可访问,指定如下 host: '0.0.0.0',设置之后之后可以访问ip地址
        port: 8080,
        hot: false, //hot配置是否启用模块的热替换功能,devServer的默认行为是在发现源代码被变更后,通过自动刷新整个页面来做到事实预览,开启hot后,将在不刷新整个页面的情况下通过新模块替换老模块来做到实时预览。
        https: false,
        hotOnly: false, // hot 和 hotOnly 的区别是在某些模块不支持热更新的情况下,前者会自动刷新页面,后者不会刷新页面,而是在控制台输出热更新失败
        proxy: {
            "/api": {
                //1
                target: "http://192.168.31.48:8080", //2
                secure: false, //false为http访问,true为https访问
                changeOrigin: true,
                pathRewrite: {
                    "^/": "/" //重写接口
                }
            }
        }, // 设置代理

        before: app => {}
    },
    // 第三方插件配置
    pluginOptions: {
        // ...
    }
}
const Timestamp = new Date().getTime()
module.exports = {
    chainWebpack: config => {
        config.plugins.delete("html")
        config.plugins.delete("preload")
        config.plugins.delete("prefetch")
    },
    publicPath: "/", //默认的'/'是绝对路径,如果不确定在根路径,改成相对路径'./'
    outputDir: "dist", // 输出文件目录
    assetsDir: "./static",
    // indexPath:'index.html',
    lintOnSave: true, // eslint是否在保存的时候检查
    configureWebpack: {
         output: {
             filename: `[name].${process.env.VUE_APP_Version}.${Timestamp}.js`,
             chunkFilename: `[name].${process.env.VUE_APP_Version}.${Timestamp}.js`
         }
    },
    productionSourceMap: false, // 生产环境是否生成 sourceMap 文件
    // css相关配置
    css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract: true,
        // 开启 CSS source maps?
        sourceMap: false,
        loaderOptions: {
            postcss: {
                plugins: [
                    require("postcss-plugin-px2rem")({
                        // 128.4 63.9
                        // rootValue:128.4,      // 新版本的是这个值
                        // mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
                        // minPixelValue: 3 //设置要替换的最小像素值(3px会被转rem)。 默认 0
                    })
                ]
            }
        }
    },
    // webpack-dev-server 相关配置
    devServer: {
        open: false, //open 在devServer启动且第一次构建完成时,自动用我们的系统的默认浏览器去打开要开发的网页
        host: "192.168.1.85", //localhost 100.100.2.35默认是 192.168.1.27 192.168.31.211。如果你希望服务器外部可访问,指定如下 host: '0.0.0.0',设置之后之后可以访问ip地址
        port: 8080,
        hot: false, //hot配置是否启用模块的热替换功能,devServer的默认行为是在发现源代码被变更后,通过自动刷新整个页面来做到事实预览,开启hot后,将在不刷新整个页面的情况下通过新模块替换老模块来做到实时预览。
        https: false,
        hotOnly: false, // hot 和 hotOnly 的区别是在某些模块不支持热更新的情况下,前者会自动刷新页面,后者不会刷新页面,而是在控制台输出热更新失败
        proxy: {
            "/api": {
                //1
                target: "http://192.168.31.48:8080", //2
                secure: false, //false为http访问,true为https访问
                changeOrigin: true,
                pathRewrite: {
                    "^/": "/" //重写接口
                }
            }
        }, // 设置代理

        before: app => {}
    },
    // 第三方插件配置
    pluginOptions: {
        // ...
    }
}

跨域配置proxy

跨域,就是指协议、域名、端口其中一个或多个不一致的情况下,从当前域去访问另一个域。 出于安全考虑(防止跨域攻击 XSS、CSRF),浏览器会禁止跨域的请求访问

使用proxy代理解决跨域,vue.config.js添加以下代码

js
module.exports = {  
    devServer: {      
      host: 'localhost', // 前端项目运行ip地址
      port: 5001, // 前端项目使用端口
      // 配置proxy代理
      proxy: {     
        '/api': {   // 匹配所有以/api开头的url          
            target: 'http://localhost:5000',   // 后端的ip地址和端口         
            changeOrigin: true,        
            ws: true,        // 消去路径中 /api         
            pathRewrite: {          
                '^/api': ''       
           }      
        }    
    }  
}}
module.exports = {  
    devServer: {      
      host: 'localhost', // 前端项目运行ip地址
      port: 5001, // 前端项目使用端口
      // 配置proxy代理
      proxy: {     
        '/api': {   // 匹配所有以/api开头的url          
            target: 'http://localhost:5000',   // 后端的ip地址和端口         
            changeOrigin: true,        
            ws: true,        // 消去路径中 /api         
            pathRewrite: {          
                '^/api': ''       
           }      
        }    
    }  
}}

注意!axios.defaults.baseURL属性不能设置,如果之前设置过,需要删除,否则proxy不能生效

vue.config.js发生更改,需要重新运行项目才能生效

配置prettier

package.json中配置rules

json
"rules": {
      "prettier/prettier": [
        "error",
        {
          "semi": false,
          "trailingComma": "none"
        }
      ]
    }
"rules": {
      "prettier/prettier": [
        "error",
        {
          "semi": false,
          "trailingComma": "none"
        }
      ]
    }

vuex

vue的状态管理工具

1.项目中下载vuex

bash
yarn add vuex
yarn add vuex

2.导入vuex并使用

通过全局方法 Vue.use() 使用插件,Vue.use 会自动阻止多次注册相同插件,它需要在你调用 new Vue() 启动应用之前完成。

Vue.use() 方法至少传入一个参数,该参数类型必须是 Object 或 Function

js
import Vue from 'vue'import App from './App.vue'import Vuex from 'vuex'Vue.use(Vuex)
import Vue from 'vue'import App from './App.vue'import Vuex from 'vuex'Vue.use(Vuex)

3.定义store对象挂载到vue实例中

js
const store = new Vuex.Store({  state: { count: 0 },  mutations: {},  actions: {}})
new Vue({  router,  store,  render: h => h(App)}).$mount('#app')
const store = new Vuex.Store({  state: { count: 0 },  mutations: {},  actions: {}})
new Vue({  router,  store,  render: h => h(App)}).$mount('#app')

4.State

提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储

获取store中数据两种方法,自选其一

方法一.通过this.$store.state.数据名来获取

js
 <h3>count值为:{{this.$store.state.count}}</h3>
 <h3>count值为:{{this.$store.state.count}}</h3>

方法二.导入mapState辅助函数,然后再computed中调用mapstate

js
import { mapState } from 'vuex'
computed: {
    ...mapState(['count'])  //指定state中count属性,映射为computed中属性
}
import { mapState } from 'vuex'
computed: {
    ...mapState(['count'])  //指定state中count属性,映射为computed中属性
}
html
<h3>count值为:{{count}}</h3>
<h3>count值为:{{count}}</h3>

5.Mutation

Mutation用于变更Store中的数据

只能通过mutation变更Store数据,不可以直接赋值Store中的数据

通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化

在mutations中定义方法进行state的数据处理

js
const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    plus (state) {
      state.count++
    },
    minus (state) {
      state.count--
    }
  },
})
const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    plus (state) {
      state.count++
    },
    minus (state) {
      state.count--
    }
  },
})

方法一.在组件中通过this.$store.commit('mutation中方法名')调用mutation中方法进行state数据处理

js
    click () {
      this.$store.commit('plus')
    }
    click () {
      this.$store.commit('plus')
    }

传两个参数

js
const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    add (state, step) {
      state.count += step
    }
  }
})
const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    add (state, step) {
      state.count += step
    }
  }
})
js
click () {  this.$store.commit('add', 3) //提交第二个参数}
click () {  this.$store.commit('add', 3) //提交第二个参数}

方法二.导入mapMutations辅助函数,然后在methods中调用mapMutations

js
import { mapMutations } from 'vuex'
import { mapMutations } from 'vuex'
js
 methods: {    ...mapMutations(['minus']),  //指定mutations中minus方法,映射为methods中方法    clickminus () {      this.minus()    }  }
 methods: {    ...mapMutations(['minus']),  //指定mutations中minus方法,映射为methods中方法    clickminus () {      this.minus()    }  }

6.action

使用action进行异步操作

通过action调用mutation中方法的方式进行state数据更改

在store里定义actions

js
actions: {    addPlus (context) {      setTimeout(() => {        context.commit('plus')  //调用mutation中方法7      }, 1000);    }  }
actions: {    addPlus (context) {      setTimeout(() => {        context.commit('plus')  //调用mutation中方法7      }, 1000);    }  }

触发actiton

方法一 dispatch

需要调用的页面中通过this.$store.dispatch调用actions中的方法

js
 this.$store.dispatch('addPlus')
 this.$store.dispatch('addPlus')

vue与安卓端通信

vue调用安卓端方法

js
   if (window.nativeClient && window.nativeClient.androidFc) {
          window.nativeClient.androidFc()
        }
   if (window.nativeClient && window.nativeClient.androidFc) {
          window.nativeClient.androidFc()
        }

安卓端调用vue方法

挂在vue中方法至window对象上,供安卓端调用

js
window.jsFunction = this.jsFunction
window.jsFunction = this.jsFunction

表单提交

可用于提交formdata格式的数据至后端

html
<form @submit.prevent="submit($event)">
    <input type="text" class="form-control" placeholder="请输入姓名" name="username">
    <input type="submit" value="登陆" class="login" />
</form>
<form @submit.prevent="submit($event)">
    <input type="text" class="form-control" placeholder="请输入姓名" name="username">
    <input type="submit" value="登陆" class="login" />
</form>
js

methods:{
    submit(event) {
        const formData = new FormData(event.target)
        //axios
        axios.post('/user',obj).then(res => {
            // success callback
        }).catch(err => {
            // error callback
        })
    }
}

methods:{
    submit(event) {
        const formData = new FormData(event.target)
        //axios
        axios.post('/user',obj).then(res => {
            // success callback
        }).catch(err => {
            // error callback
        })
    }
}

vue与iframe通信

vue父页面调用iframe子页面的方法

调用iframe子页面方法的关键在于,获取到iframe元素对象中的contentWindow属性

这里使用了$refs ,如果使用dom方法获取也是可以的

js
<template>
  <div class="pjpccx">
    <h1 class="title_color">{{ title }}</h1>
    <button @click="reportPrint">点击调用iframe中的方法</button>
    <iframe ref="iframe" :src="urlPath" class="iframe" frameborder="0" scrolling="yes" name="iframe" seamless>您的浏览器版本过低,无法显示报表内容!建议升级浏览器或更换浏览器!</iframe>
  </div>
</template>

export default {
	methods: {
    reportPrint() {
      this.$refs.iframe.contentWindow.Print()
    }
  }
}
<template>
  <div class="pjpccx">
    <h1 class="title_color">{{ title }}</h1>
    <button @click="reportPrint">点击调用iframe中的方法</button>
    <iframe ref="iframe" :src="urlPath" class="iframe" frameborder="0" scrolling="yes" name="iframe" seamless>您的浏览器版本过低,无法显示报表内容!建议升级浏览器或更换浏览器!</iframe>
  </div>
</template>

export default {
	methods: {
    reportPrint() {
      this.$refs.iframe.contentWindow.Print()
    }
  }
}

传值

js
// vue
<template lang="pug">
  .map-content
    iframe#mapIframe(:src="mapIframeUrl" ref="mapFrame" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling="no" allowtransparency="yes")
</template>
<script>
export default {
  methods: {
    getGeometry () {
      let params = 'xxx'
      let temp = this.$refs.mapFrame.contentWindow.getGeometry(params)
      temp.then(res => {
        resolve(res)
      })
    }
  }
}
</script>

// iframe
 function getGeometry (params) {
  return new Promise(function (resolve, reject) {
    ...
    resolve(result)
  });
}
// vue
<template lang="pug">
  .map-content
    iframe#mapIframe(:src="mapIframeUrl" ref="mapFrame" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling="no" allowtransparency="yes")
</template>
<script>
export default {
  methods: {
    getGeometry () {
      let params = 'xxx'
      let temp = this.$refs.mapFrame.contentWindow.getGeometry(params)
      temp.then(res => {
        resolve(res)
      })
    }
  }
}
</script>

// iframe
 function getGeometry (params) {
  return new Promise(function (resolve, reject) {
    ...
    resolve(result)
  });
}

iframe子页面中调用vue父页面的方法

vue父页面

js
 mounted() {
    // iframe页面会给vue页面发送消息
    window.addEventListener("message", () => {
      this.refreshTable()
    })
  },
 mounted() {
    // iframe页面会给vue页面发送消息
    window.addEventListener("message", () => {
      this.refreshTable()
    })
  },

frame子页面

js
window.parent.postMessage({
        data :"params"
      },'*');
window.parent.postMessage({
        data :"params"
      },'*');

keep-alive

  • keep-alive顾名思义,保持活跃。保持谁活跃呢?
  • 首先我们知道,因为vue就是组件化编程,一个.vue文件就是一个组件。就像万事万物一样,都有从出生到消亡的生命周期过程,vue的组件也是一样,也有自己的生命周期,比如create创建组件、mounted往组件上挂数据、update更新组件上挂的数据,destroy把组件实例销毁。
  • 所以使用keep-alive就是保持组件活跃,不会被destroy销毁掉,就一直还活着,组件没有被销毁掉的话,组件上挂载的数据就还存在,所以状态就可以保留,所以,keep-alive就可以保持组件的状态。
vue
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>

属性

  1. include 字符串或正则表达式,只有名称匹配的组件会被缓存
  2. exclude 字符串或正则表达式,任何名称匹配的组件都不会被缓存
  3. max 数字,最多可以缓存多少组件实例

生命周期

  1. activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated
  2. deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated

调用子孙级组件方法

每个子孙组件都ref指定名称

js
this.$refs.tree.$refs.tree.$refs.tree.setCurrentKey()
this.$refs.tree.$refs.tree.$refs.tree.setCurrentKey()

等待父页面请求结束,子页面再进行请求

给子组件绑定v-if,等待父页面接口请求结束,再设为true,这时子页面才会渲染,触发mounted等生命周期函数

Released under the MIT License.