1.computed做中间件 监听vuex的state变化

    可以采用以下方式

    watch: {
        xxx: function(newVal,oldVal) {},
    },
    computed: {
        xxx() {
            return this.$store.getters.xxx;
        }
    },

    或者直接使用(因为vuex本身就是响应式的,一旦vuex中的state发生变化,会自动广播到所有使用的组件中)

    this.$store.getters.xxx  (常用)

    注意:同样的道理,computed也可以用来监听某个对象更深层的属性,如下

    watch: {
        xxx: function(newVal,oldVal) {},
    },
    computed: {
        xxx() {
            return this.obj.a.b;
        }
    },

2.vue的混入功能:mixins

    主要用于不影响原文件的前提下,做一些新增的公用功能

    把一个js文件当子组件插入到另一个组件里(如xxx.vue),用mixins进行引入,然后可以直接使用mixins.js里的东西。

    冲突处理:其中created、mounted等是钩子函数;components、methods 、computed、data等为值对象;当遇到 值对象 冲突(对象键名相同)时,忽略mixins,只使用你组件中的 。当遇到 钩子函数 冲突时,mixins和你的组件都会被执行,且mixins中的会被优先执行。

     mixins.js:(该文件中只有js部分,结构和.vue文件的js部分是一样的)

    export const mixin = {
      data () {},
      methods: { fun(){}, ... }
    }

    xxx.vue:

    import {mixins} from './mixins.js';
    <template>
        ...
        {{fun()}}
        ...
    </template>
    export default {
        name: "xxx",
        mixins: [mixins],
        data() {...}
    }

3.vue的过滤功能:filter

    ...
    <el-table-column min-width='60' label="源文件">
        <template slot-scope="scope">
          <!-- 写法1,已经废弃 -->
          <a class="operate" :href="scope.row.sourceUrl" download>{{scope.row.sourceUrl | getFileName}}</a>
          <a class="operate" :href="scope.row.sourceUrl" download>{{scope.row.userName | makeUserName(prev)}}</a>
          <!-- 写法2 -->
          <p v-html="$options.filters.text()"></p>
        </template>
    </el-table-column>

    ...
    data() {
        return {
            prev: 'super'
        }
    },
    filters: {
        getFileName(val){              //val就是sourceUrl
            return val + '...';        //最后展示出来的结果
        },
        makeUserName(val, prev){       //在filters中无法直接获取this.prev,所以必须通过传参获取
            return prev + val;
        },
        text(){
            return 123;
        }
    }

4.vue中的键盘事件

    在element-ui中
    <el-input @keyup.native.enter="submit"></el-input>    //回车按钮keyup时触发

    在原生标签中
    <input @keyup.enter="submit"></input>

5.slot-scope/scope的作用

    在vue 2.5.0+ 中, slot-scope 替代了 scope

    组件test:

<template>    //template标签的作用就是包裹一段html代码,并在渲染页面后不体现自己
   <div>
      <div>下面是一个slot</div>
      <slot a="123" b="444" ></slot>
   </div>
</template>

    使用组件test

//slot-scope表示获取组件test中slot的所有属性,组成对象slotProps(该对象名可随意取)
<test>        
    <template slot-scope="slotProps">
        <div>下面是slot的props调用</div>
        <div>{{slotProps.a}}</div>
        <div>{{slotProps.b}}</div>
    </template>
</test>

6.vue3.0代理配置,解决跨域问题

    在根目录下创建vue.config.js文件,并写入以下代码:

    module.exports = {
      ...
      devServer: {
        port: 8088,                            //端口号
        host: '0.0.0.0',                    //匹配任意ip或域名
        https: false,                        //配置本地 模拟为https,端口号需配为443
        disableHostCheck: true,                //默认检查hostname,如果hostname不是配置内的,将中断访问,设为true则不检查,但易受到DNS重新绑定攻击
        open: true,                            //是否自动打开浏览器
        proxy: {                            // 配置多个代理
            '/api': {
                //用于代理转发请求后台接口,当调用 localhost:8088/api/xxx 接口时,localhost:8088/ 会被替换成 target
                target: 'https://mi-api-dev.nailtutu.com/',
                ws: true,                    //是否启用websocket
                changeOrigin: true,            //是否改写源,即是否将localhost:8088/ 替换成 target,如需跨域则设为 true
                pathRewrite: {
                    '^/api': ''                //重写路径
                }
            },
            '/pic': {
                //当图片与接口不同源时,用于代理转发请求图片地址
                target: 'https://cdn-dev.nailtutu.com',
                ws: true,
                changeOrigin: true
            }
        },
      }
    }

    注意:在页面中使用接口时,需去掉域名,否则不会走代理

7.解决 build之后的dist目录 部署到服务器路由访问失败(一片空白)

    原因:运行dev的话,可以用localhost:8080/xxx进行路由,但build之后的dist目录下,路由写法必须为localhost:8080/#/xxx

    解决方法:
    1.在vue.config.js中写入

        module.exports = {
            ...
            publicPath: './',
            outputDir: 'dist',
            lintOnSave: true,
            runtimeCompiler: true, //关键点在这,调整内部的 webpack 配置
            chainWebpack: () => {},
            configureWebpack: () => {},
            devServer:{...}
        }

    2.在router.js中

        export default new Router({
          // mode: 'history',                    //注释掉这2行 或 改为 hash 模式即可
          // base: process.env.BASE_URL,
          routes: [...]
        })

    3.重新build,或者重新运行 可生效

    注意:移动端的H5页面一般用history模式的兼容性会比较好

8.vue移动端左右滑屏第三方插件

    https://github.com/surmon-china/vue-awesome-swiper

9.vue实现i18n

  1) 安装
    npm install vue-i18n --save

  2) 引入

import VueI18n from 'vue-i18n';
Vue.use(VueI18n);                    //注册插件
const i18n = new VueI18n({            //实例化
    locale: 'en-US',                //初始语言标识,通过this.$i18n.locale来实现语言切换
    fallbackLocale: 'en-US',        //当匹配不到以下语言列表时,强制指定用哪种语言
    messages: {
        'zh-CN': require('./common/lang/zh'),   // 中文语言js静态文件
        'en-US': require('./common/lang/en'),   // 英文语言js静态文件
         ...
    }
})
new Vue({
    el: '#app',
    i18n,                //将实例化对象 注册到vue项目中
    store,
    router,
    template: '<App/>',
    components: { App }
})

  3) 语言js文件

module.exports = {
    userName: 'User Name:',
    warning: 'This is not allowed',
    ...
}

  4) 调用

//html中调用
<span>{{$t('userName')}}</span>
//js中调用
this.$t('warning')

10.Vue.nextTick的用法

    作用:vue的数据更新是异步的,想确保拿到的是更新且渲染后的数据,就要用nextTick    

    写法:

    <div ref="msgDiv">{{msg}}</div>
    <div>{{msg1}}</div>
    <div>{{msg2}}</div>

    data(){
        return {
            msg: 'Hi',
            msg1: '',
            msg2: '',
        }
    }
    methods: {
        change() {
          this.msg = "Hello";
          this.msg1 = this.$refs.msgDiv.innerHTML;        //msg1拿到的还是'Hi'
          this.$nextTick(() => {
            this.msg2 = this.$refs.msgDiv.innerHTML;    //msg2拿到的才是'Hello'
          })
        }
    }

11.vue样式模块化

    vue的css除了用scoped关键字做局部化之外,还可以通过module关键字进行模块化

    优点:比scoped有更多的控制权,可将该序列化的类名传给其他组件

    用法:通过this.$style['button']可以获取具体类名    

    例1:

<template>
    <button :class="$style.button" />        //最终渲染class名都会被序列化
</template>
<style module>
    .button { color: red }
</style>

    例2:

<template>
    <div>{{ $style.primaryColor }}</div>
</template>
<style module lang="scss">
    $primary-color: #B4DC47;
    :export {            //可用:export将scss变量导出,在this.$style中可获取到,也可传给其他组件
        primaryColor: $primary-color
    }
</style>

12.路由守卫

    作用:避免没有权限,但知道路由的人访问页面

    全局路由守卫:作用于所有路由,在main.js中写

router.beforeEach((to,from,next)=>{})
router.afterEach((to,from)=>{})

    单路由守卫:作用于单个路由

{ name: "login" path: "/login", component: Login, beforeEnter:(to,from,next)=>{} }    

    组件内守卫:写在组件内

data(){},
beforeRouteEnter:(to,from,next)=>{  //在beforeCreate之前执行
    next( vm => {  //参数vm就是当前组件的实例,相当于this
        ....
    })
},
beforeRouteLeave:(to,from,next)=>{  //在beforeDestroy之前执行
    next();
},
beforeRouteUpdate:(to,from,next)=>{  //当本组件中存在二级路由导航时,且二级路由发生变化才会触发
    next();
},

    举例(全局路由守卫):

    { name: "login" path: "/login", component: Login, meta: {title: 'login'} }
    { name: "home" path: "/home", component: HomePage, meta: {requireAuth: true, title: 'home'} }
    ...

    router.beforeEach((to,from,next)=>{
        let token = window.sessionStorage.getItem('access_token');
        window.document.title = to.meta.title;
        if(to.meta.requireAuth){                //需要登录成功才可进入的路由
            if(token){                          //取得到登录信息,进行下一步
                if(与权限配的菜单一致){
                    return next();
                }else{
                    return next({name: 'noAuth'});  //跳转到无权限访问的模板页
                }
            }else{                                  //token失效
                return next({path: '/login'});
                或者
                return next({name: 'login'});
            }
        }else{                       //不需要登录权限,例如登录页
            return next();
        }
    })

13.路由模式

    详细解释:https://www.jianshu.com/p/1e854973ab26

    hash模式(默认)

        使用URL的hash来模拟一个完整的URL,当#后面的hash发生变化,不会导致浏览器向服务器发出请求,触发onhashchange这个事件,通过监听hash值的变化来实现更新页面部分内容的操作

        注意:记得在vue.config.js中配置
            module.exports = {
                ...
                publicPath: './',
            }
    
    history模式

        使用HTML5的pushState()和replaceState()这两个api来实现,同样,可以改变url地址且不会发送请求

    区别

        1) hash模式只能改变#后面的url片段,而history模式的pushState设置的新URL可以是与当前URL同源的任意URL

        2) 在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,所以刷新时不会有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,则会刷新出来404页面。

14.路由跳转

    name只能搭配params,path只能搭配query

//参数不会体现在地址栏中,所以再次刷新的话,页面会拿不到参数userId
<router-link :to="{ name: 'news', params: { userId: 123}}">text</router-link>
this.$router.push({ name: 'news', params: { userId: 123 }});

//参数会体现在地址栏中,所以再次刷新仍可拿到参数(推荐)
<router-link :to="{ path: '/news', query: { userId: 123}}">text</router-link>
this.$router.push({ path: '/news', query: { userId: 123 }});

    push和replace的区别

this.$router.push        //可返回上一页
this.$router.replace     //替换了当前页,相当于只能返回上上页内容

    back和go的区别 (底层也是history)

this.$router.back(0)    //刷新
this.$router.back()     //后退,原页表表单中的内容会保留
this.$router.go(-1)     //后退+刷新,原页面表单中的内容会丢失

15.vue的深度监听

    data(){
        return {
            person: {
                firstname: 'Menghui',
                lastname: 'Jin',
                fullname: '',
                other: {
                    name: 'a'
                },
            }
        }
    },
    watch: {
        person: {
            handler(newValue,oldValue){
                    this.person.fullname = n.firstname + ' ' + this.person.lastname;
            },
            // immediate: true,   //第一次加载立即执行handler,默认watch第一次不会执行
            deep: true,           //深度检测 person 对象的属性值的变化
        }
    }

    注意1:deep为true只能正确检测到第一层,更深层次虽然可以进入handler,但newValue和oldValue都是一样的

    注意2:用this.$set(this.other, 'name', 'b')也可以触发渲染

    注意3:data中引用类型的数据,新增或删除是可以触发渲染的,只有修改(包括修改length)是不会触发的,所以才需要用this.$set

                 比如新增一个age属性,this.other['age'] = 10,这样是可以触发页面重新渲染的

16.vue父子组件中的方法互调

    父组件调用子组件中的方法:

        父组件:
            
            <Child ref="myChild"></Child>
            ...
            this.$refs.mychild.childMethod()  //childMethod是子组件中的方法

    子组件调用父组件中的方法:

        子组件:

            this.$parent.fatherMethod()        //fatherMethod是父组件中的方法

17.vue路由配置404页面自动调整

1)在路由守卫中配置

router.beforeEach((to, from, next) => {
  if (to.matched.length ===0) {  //如果未匹配到路由
    //如果上级未匹配到路由则跳转404页面,如果上级能匹配到则转上级路由
    from.name ? next({ name:from.name }) : next({ name: 'page404' });   
  } else {
    next();    //如果匹配到正确跳转
  }
});

2)在router中配置

let router = new VueRouter({
    routes:[
       {path:'/',redirect:{name:"home"}},  // 重定向到主页
       ...
       {path:'*',component: page404},      //*表示匹配全部;如果已有的路由全不匹配的情况下,返回404
    或者{path:'*',redirect: '/page404'},    //用重定向也可以
    ]
});

18.不用vuex的情况下,兄弟组件之间传值的方式

let eventBus = new Vue;  //事件车,用来连接两个兄弟

//组件A
methods:{
    change(){
       eventBus.$emit("changeGreen", 'green')
    }
},

//组件B
created(){
    eventBus.$on("changeGreen",(val)=>{this.color=val})
}

beforeDestroy(){
    eventBus.$off("changeGreen")
}

注意:实际应用中可能会有问题。在页面不刷新的情景中(即不会进入beforeDestroy,比如用了keep-alive),当注册多几个事件之后,在来回切换的过程中会不断触发导致卡顿。解决方法:网上推荐用插件 vue-happy-bus 来处理即可。

19.vue动态组件

    <component :is="xxx"></component>

<template>
  <p v-for="(item, i) in arr" :key="i">
   <component :is="item"></component>
  </p>
</template>

<script>
data: {
    arr: ['comA','comB'],
    currentView: 'comA'
},
components: {
  comA,
  comB
}
</script>

20.如何让keep-alive标签中的组件重新渲染

1)用exclude属性

<keep-alive :exclude="[ 'name1', 'name2' ]"> // 对应 router 配置中的 name
    <router-view :key="key"></router-view>
</keep-alive>

2)根据条件改变key值

3)在activated生命周期中刷新数据

activated() {
    this.refreshData();     //只刷新数据,不刷新整体的dom
}

21.Vuex中更新对象或数组的值,视图不更新的解决办法

    其实就是在mutation中使用全局的set方法去设置

import Vue from 'vue';
state: {
    testObj: {},
},
mutations: {
    setChatList(state, result){
        // state.testObj.name = '张三'; // 错误 - 只会更新数据,不会更新视图
        Vue.set(state.testObj,'name','张三') // 正确
    }
}

22.异步组件加载及生命周期的控制 

components: {
    MyDemo: () => import('./Demo').then(component => {
        component.default.mounted = ...
    })

    详情见:https://zhuanlan.zhihu.com/p/32015343

    组件同步引入时生命周期顺序为:父组件的beforeCreate、created、beforeMount --> 所有子组件的beforeCreate、created、beforeMount --> 所有子组件的mounted --> 父组件的mounted

    组件异步引入时生命周期顺序:父组件的beforeCreate、created、beforeMount、mounted --> 子组件的beforeCreate、created、beforeMount、mounted

23.按需引入echarts图表

    npm install echarts --save

  //加载echarts,引入主文件
  import echarts from 'echarts/lib/echarts' 
  // 再引入你需要使用的图表类型,标题,提示信息等
  import 'echarts/lib/chart/bar'
  import 'echarts/lib/component/legend'
  import 'echarts/lib/component/title'
  import 'echarts/lib/component/tooltip'

24.引入公共样式

    全局引入(在main.js中)

import '../static/css/global.css' /*引入公共样式*/

    局部引入

<style>
  @import './../static/css/global.css'; /*引入公共样式*/
</style>

25.v-for遍历的同时做v-if判断

    这种写法一般会报警告,官方不推荐 v-for 与 v-if 在同一个标签中出现。

    解决办法:套多一层标签,外面层用于 v-for,里面层用于 v-if

<div v-for="(item, index) in arr" :key="item.id">
    <div v-if="item.xxx > 2">
        ...
    </div>
</div>

26.手动销毁vue组件

this.$destroy('componentName'); //componentName就是组件的name

27.v-model如何与vuex结合使用

    通过computed的get和set

    注意:get和set不可用箭头函数的写法,否则获取不到this,因为隐式已经绑定了this

    详情见:https://blog.csdn.net/weixin_33824363/article/details/91434500

28.vue中全局和局部引入批量组件的方法

    用require.context实现

    详情见:https://blog.csdn.net/Dalin0929/article/details/104557131

29.watch和computed的区别

    详情见:https://www.cnblogs.com/jiajialove/p/11327945.html

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐