vue基础
组件
组件制作
封装
// 定义Demo.vue ---------------------------------------------------------------
<template>
<div class="container" @click="e=>onClick(e,'我是点击事件')">
<div v-for="(item, index) in data" :key="index">
{{item}}
</div>
</div>
</template>
<script>
export default {
name:'Demo',
props: {
data: {
type: Array,
default: ()=>(['num1','num2','num3'])
},
},
methods:{
onClick(e,msg){
console.log('event',e)
console.log('msg',msg)
}
}
};
</script>
// 在home.vue中使用 ---------------------------------------------------------------
import Demo from '@/components/Demo.vue'
export default {
name: 'home',
components: {
Demo
}
}
// 非vue使用 ---------------------------------------------------------------
var vm = new Vue({
el: '#example', // 挂在的dom节点
template:'',
data: data // 绑定的数据
})
组件切换
// 情景1 ---------------------------------------------------------------
// html
<component :is="comp" :data="data" msg="Welcome to Your Vue.js App"/>
// Js
props:{
comp:{
default:Demo
}
},
// 情景2 注意是字符串名-----------------------------------------------------
// html <component :is="'Demo'" :data="data" msg="Welcome to Your Vue.js App"/>
// js
import Demo from "@/components/Demo.vue";
components: {
Demo:Demo
},
插槽
普通插槽
具名插槽
// 组件定义
<slot name="head"></slot>
// 使用
<comp>
<template slot="head">
<h3>window</h3>
</template>
</comp>
自定义指令
(主要用来重构组件属性特质)
// 全局定义
Vue.directive('color-block', function (el, binding) {
console.log('bind',binding)
switch (binding.value) {
case 'red':
{
el.style.backgroundColor = '#fddec4'
el.style.border = '1px solid #ff6131'
}
break;
case 'yellow':
{
el.style.backgroundColor = '#fbfdc4'
el.style.border = '1px solid #ffd831'
}
break;
}
})
// 使用
<div v-color-block="'green'" class="a">{{data | toFixed }}</div>
组件通信
父子通信
父向子
// 父发送
<Demo :data="data" msg="Welcome to Your Vue.js App"/>
// 子接收
props: {
data: {
type: Array,
default: ()=>(['num1','num2','num3'])
},
}
子向父
// 子发送
handleClick(){
this.$emit('childrenClick', this.emitData)
}
// 父接收
<Demo @childrenClick="onChildrenClick"/>
兄弟间通信
总线模式
(尽量不要用总线进行通信, 压平层级, 如果层级很深的话考虑provider和vuex )
建立总线
// 在main.js中
Vue.prototype.$bus = new Vue();
总线发射
this.$bus.$emit('addGoods', good);
总线接收
created(){
this.$bus.$on('addGoods',(good)=>{
this.addCart(good)
})
},
使用中介者模式
两个子组件绑定父组件的值, 修改时通过向父组件发送修改请求让父组件修改
多层级通信
(自己发送自己监听)
provider inject (祖 -> 孙)
// 父组件发送: provider可以给所有子组件提供数据
provide(){
return {
parentsKey:'祖先值'
}
}
// 后代组件接收
inject:['parentsKey']
跨级修改 (孙-> 祖)
先引入一个向上递归查找的工具类
// 说明: 一直向上查找, 让名为"componentName"的祖组件发送一个名为"eventName"的事件并绑定参数
$dispatch(componentName, eventName, params){
let parent = this.$parent || this.$root;
let name = parent.$options.name;
// 向上循环查找
while(parent && (!name || name !== componentName)){
parent = parent.$parent;
if(parent){
name = parent.$options.name;
}
}
if(parent){
parent.$emit.apply(parent,[eventName].concat(params));
}
}
/* 使用(需先附加到总线 Vue.prototype.$dispatch=xxxx):
后代元素: this.$dispatch.bind(this)('formItem','doValidate',this.inputValue);
祖先元素:
created(){
this.$on('doValidate',this.doValidate);
}
*/
数据过滤
计算: computed
适用场景: 根据一个数据生成另一个数据
// 普通模式
computed:{
reMsg(){
return this.msg.split('').reverse().join('')
}
}
// 高级模式 (先get值后set)
computed:{
reMsg:{
get:function(){
return this.msg.split('').reverse().join('')
},
set:function(value){
this.msg = value; //最后修改了msg
}
}
}
侦听:watch
适用于订阅性质的处理
data(){
return{
msg:''
}
},
watch:{
msg:{
handler(new,old){
// msg变量更改后触发
},
deep:true //深度观察, 对象里面的变动也会触发
},
$route(){
// 路由信息变化就会引发此函数
}
}
响应式赋值
(一般用于给对象对象内部元素赋值, 直接赋值对象一般不会触发响应式)
// $set是Vue.set的别名
this.$set(this.obj, k, v)
Vue.set(this.obj, k, v)
this.$delete(this.obj, k)
Vue.delete(this.obj, k)
// 全局使用
Vue.set(target, 'key', value)
Vue.set(_this.data[4].avatarAfter,'title',hiddenPhone(value))
大批量处理时候可以借助assign
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
this.userProfile = userProfile;
过滤器(针对template)
// 组件内定义
export default {
filters:{
toFix: function(value,places) {
if (!value) return "";
return (+value).toFixed(places);
}
}
}
// 全局定义, 在普通Js文件中
Vue.filter('toFixed', function (value,places=2) {
return (+value).toFixed(places);
})
// 使用方式相同
<div>{{data | toFix(2)}}</div>
<div :id="rawId | toFix(2)"></div>
状态管理
模块化构建
module/home.js
import router from '@/router';
const home = {
state: {
router: router,
selectedLabel:'进行中',
navOffset:150,
},
mutations: {
go(path) {
this.state.path = path;
},
changeLabel(state, newLabel){
state.selectedLabel = newLabel;
},
clearNavOffset(state){
state.navOffset = 0;
}
}
}
export default home;
index.js
// import Vue from 'vue';
// import Vuex from 'vuex';
import home from './modules/home';
import user from './modules/user';
// Vue.use(Vuex); // 使用cdn替换
const store = new Vuex.Store({
modules: {
home,
user
}
})
export default store;
state操作
// 取 ---------------------------------------------------------------
this.$store.state.user.authState
// 用map取
computed: {
...mapState({
authState: state => state.user.authState
})
}
// 存 (mutation合并到vuex的根下)
this.$store.commit("changeLabel", 'key_1');
// map操作
methods: {
...mapMutations(['increase'])
}
/* 技巧: data中直接用this.$store.user.authState更改值不触发响应式变更, 可以用watch配合更改data中的值
watch: {
authState(newValue) {
this.data[2].avatarAfter.title = newValue;
}
}
*/
action操作
// 定义
actions: {
increaseAsync({state, commit},asyncValue){
setTimeout(() => {
commit('increase',asyncValue)
}, 2000);
}
}
// 使用
this.$store.dispatch('increaseAsync',4);
// map操作
methods: {
...mapActions(['increaseAsync'])
}
Getters操作
(相当于vuex中的computed)
// 定义 ---------------------------------------------------------------
state:{
// 应用启动时, count置为0
count:0,
todos: [
{ id: 1, text: '水果类', done: true },
{ id: 2, text: '苹果', done: true }
]
},
getters: {
doneTodos: state => {//通过方法访问
return state.todos.filter(todo => todo.done)
}
}
// 使用, 直接当变量用即可
computed: {
...mapGetters(['doneTodos']) // 模块化时候不用像state那样分组
}
let tok =store.getters.getToken; // 模块化时候不分组
订阅, 同步localStorage
store.js文件中
store.subscribe((mutation, state)=>{
switch(mutation.type){
case 'setToken':
localStorage.setItem('token',Json.stringfy(state.token))
break;
}
})
路由
模块化构建
module/project.js
import ProjectLayout from 'views/layouts/ProjectLayout';
const routes = [
{
path: '/project',
name: 'project',
component:ProjectLayout,
children:[
{
path:'award',
name:'projectAward',
component:()=>import('views/ProjectAward')
},
{
path:'detail',
name:'projectDetail',
component:()=>import('views/ProjectDetail')
}
]
}
]
export default routes
routes.js
import root from './modules/root';
import user from './modules/user';
import order from './modules/order';
import project from './modules/project';
import system from './modules/system';
export default [
...root,
...user,
...order,
...project,
...system,
{
path:'/login/:id?',
name:'login',
component:()=>import(/* webpackChunkName:'login'*/'views/Login')
},
{
path: '*',
redirect: '/home'
}
]
index.js
import Vue from 'vue';
import Router from 'vue-router';
import routes from './routes';
// Vue.use(Router);
// https://router.vuejs.org/zh-cn/api/options.html
const router = new Router({
// mode: 'history',
routes
// base: '/',
// linkActiveClass: 'router-link-active',
// linkExactActiveClass: 'router-link-exact-active',
// fallback: true,
});
// 全局守卫
router.beforeEach((to, from, next) => {
// 登录鉴权
if (to.meta.auth) {
doLogin();
return;
}
next();
});
function doLogin() {
router.push('/login')
}
export default router;
参数处理
params参数
// 方案一 (推荐) ---------------------------------------------------------------
// 路由配置(加接参配置)
{
path: '/describe/:id?',
name: 'Describe',
component: Describe
}
// 传参
this.$router.push({
path: `/describe/${id}`,
})
// 提取参数
this.$route.params.xxx
// 方案二 ---------------------------------------------------------------
// 路由配置(不加接参配置)
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 传参
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
// 提取参数
this.$route.params.xxx
query参数
// 标签传参
<route-link :to=`/path/path?id=${id}` >我是跳转链接</router-link>
// 跳转传参
this.$router.push({
path: '/describe',
query: {
id: id
}
})
// 提取参数
this.$route.query.id
路由重定向
// 重定向
doLogin(){
store.state.isLogin=false;
router.replace({
path:'/login',
query:{
redirect:router.currentRoute.fullPath
}
})
}
// 跳转
{
path:'/lesson/2?redirect='+to.path
}
// 提取
const {redirect} = this.$route.query;
if(redirect){
this.$router.push(redirect);
}else{
this.$router.push('/')
}
子路由
children:[
{
path: routInfo.lesson3.routePath,
name: routInfo.lesson3.name,
component: Lesson3
}
]
// 注意 需要在此路由中放入
<router-view></router-view> // 路由视图
路由守卫
全局级守卫
const router = new Router({
// mode: 'history',
routes
});
router.beforeEach((to, from, next) => {
// 全局鉴权
});
// 其他
afterEach // 适合做历史记录自定义堆栈
beforeRouteUpdate // 仅路由参数变更时触发
路由级守卫
// 路由中放入钩子
{
path:'xxx',
name:'xxx',
component:()=>import('@/xxx') // 懒加载
beforeEnter(to, from, next){
}
}
组件级守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
export default {
name: "Admin",
data(){
return{
infor:'hw'
}
},
beforeRouteEnter:(to,from,next)=>{
//此时该组件还没被实例化
alert(this.infor); //弹出消息框信息为 undefined
next(vm =>{
//此时该组件被实例化了
alert(vm.infor); //弹出消息框信息为 hw
})
}
}
动画
基本形式
// 注意: 进行动画的组件或视图请使用position:absolute
<transition name="fade" mode="out-in" appear>
<component :is="view"></component>
</transition>
// 常用类名 ---------------------------------------------------------------
v-enter{}
v-leave-to{}
v-enter-active,v-leave-active{
}
// 常用api ---------------------------------------------------------------
mode="in-out"
appear // 是否页面一加载就执行
enter-active-class="animated bounceInUp" // 进入时的动画 常配合anmate.css
leave-active-class="animated swing" // 离开时的动画
appear-active-class="animated swing" // 刷新页面时的开始动画样式
// 常用过度动画
transform: translate3d(100%,0,0) // 配合position:absolute
// js动画钩子 ---------------------------------------------------------------
@before-enter="beforeEnter"
@enter="enter"
@after-enter-"afterEnter"
异步
async await
async getGoodsData(){
try {
const res = await axios.get('/api/goods');
this.goodsData = res.data.list;
} catch (error) {
console.log(error);
}
}
promistAll
Api
生命周期
// 创建
beforeCreate
created // 一般可以用来发送异步请求, 放置监听总线事件
// 挂载
beforeMount
mounted
// 刷新
beforeUpdate
updated
// 销毁
beforeDestroyed // 一般用来销毁计时器和echarts
destroyed
路由
router.go() // 历史记录上前进异步
router.replace() // 替换掉本条历史记录, 常用来做Login鉴权
原型链api
this.$parent
this.$router
this.$route
this.$emit()
this.$refs
this.$nextTick()
绑定类api
// 属性
:class="{ 'active': isActive, 'text-danger': hasError }"
// 事件
@click="e=>handleClick(e,params)"
v指令
v-for: <Comp v-for="(todo,index) in todos" :key="index"/>
v-model: <input v-model="message">
v-once: <span v-once>这个将不会改变: {{ msg }}</span>
v-show: 只是切换display 不删除节点
v-if="" && v-else-if && v-else // 如果要切换多个元素可以使用template
修饰符
// 按键修饰符 ---------------------------------------------------------------
// keycode数字
<input @keyup.13="submit">
// keycode别名
<input @keyup.enter="submit">
/* 常用
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
*/
// 事件修饰符 ---------------------------------------------------------------
<form @submit.prevent="onSubmit">...</form>
/* 常用
.stop 阻止事件冒泡
.prevent 阻止默认事件
.capture
.self 只有是自身元素时候才触发
.once 只触发一次
.passive
*/
// 命令键修饰符 ---------------------------------------------------------------
<input @keyup.alt.67="clear"> // alt+c
/* 常用
.ctrl
.alt
.shift
.meta
*/
// 鼠标键修饰符 ---------------------------------------------------------------
/* 常用
.left
.right
.middle
*/