Vue开发中的实用技巧

动态导入本地图片 假设我们有这么一个功能,后台返回图片的名称,前端需要自己拼接路径获取本地图片,假定这些资源是存在我们前端的 assets/images,如果你采取传统的字符串拼接的方式: //template 发现图片无法显示,打开控制台审查元素,发现路径并没有正确解析,这个跟 webpack 编译打包有关系,在编译过程中目录结构改变导致的。 我们只需要一个 require 方法就可以完美解决这个问题: //template 刷新瞧瞧,是不是可以了~ 开发和生产的路由配置 配置路由的时候,开发环境下不需要使用 lazy-loading 加载 , 仅在生产环境使用即可,因为开发模式使用 lazy-loading 会导致 webpack 热更新比较慢。 可以创建2个 js ,分别为 _import_development.js 和 _import_production.js 用来加载我们的组件 ps : 我的页面是在 views 文件夹下 // _import_development.js module.exports = file => require('@/views' + file + '.vue').default; // _import_production.js module.exports = file => () => import('@/views/' + file + '.vue') 之后我们可以在我们的路由文件中通过当前运行环境( process.env.NODE_ENV )来加载不同的导入文件js,大概就变成下面的样子了。 // router.js const _import = require('./_import_' + process.env.NODE_ENV); import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Index', component: _import('/index') } ], mode: 'history' }) 带参数的自定义指令 我们平时书写自定义指令大部分都是以下的这种方式: Vue.directive('background', { inserted: function (el) { // 修改背景色 el.style.backgroundColor = 'red' } }) 这样可能在某些场景下显得不够灵活,其实我们是可以给指令传递参数的,我们可以将上面的代码改成下面这样: Vue.directive('background', { inserted: function (el,binding) { // 修改背景色 el.style.backgroundColor = binding.value } }) 其中第二个参数 binding 是一个对象,包含下面这些属性: name:指令名,不包括 v- 前缀。 value:指令的绑定值,例如:v-background="'red'" 中,绑定值为 red。 oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。 expression :字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。 arg :传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。 modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true } 使用时就可以 //固定值
Hello World
//动态传值
Hello World
其中 color 绑定的是 data 里面的 color 值。 如果你觉得这么还不够灵活,我想动态修改参数可以吗?当然没问题,请看下面: Vue.directive('style', { inserted: function (el,binding) { el.style[binding.arg] = binding.value; } }) 这么一来,你想修改啥直接写就是啦。 // 修改背景色
Hello World
如果我想批量修改,so easy 传个对象就好了,如下 Vue.directive('style', { inserted: function (el,binding) { for( let key in binding.value){ el.style[key] = binding.value[key] } } }) // 批量修改
Hello World
带参数过滤器 过滤器通常在** 双花括号插值**和 v-bind 表达式 中使用,经常是为了来格式化一些文本之类的。 它跟自定义指令一样,也是可以带参数的,不过过滤器比起指令要简单的多。 假设我们需要将后端传过来的时间戳格式化一下,一般的这么写就可以了: // ps: 这里引入了一个 moment 包 Vue.filter('formatDate',function (val) { return moment.unix(val).format('YYYY-MM-DD HH:mm:ss') }) 后来为了让用户可以自定义显示格式,后端增加了一个formate字段,我们不得不修改我们的过滤器,这时候就需要给过滤器加参数,来解决这个问题 Vue.filter('formatDate',function (val,format) { return moment.unix(val).format(format) }) 调用的时候只需要这么传入即可:
{{ timestamp | formatDate('YYYY/MM/DD HH:mm:ss') }}
过滤器第一个参数仍然是原始的值,YYYY/MM/DD HH:mm:ss 作为第二个参数传到了 format 中,这样的拓展性是不是更好了呢~ $attrs解决数据多级传递 $attrs 官方解释是包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。 理解起来一头雾水,其主要意思是父组件往子组件传没有在props里声明过的值时,子组件可以通过$attrs接受,且只包含父组件没有在props里声明的值。 通常我们如果需要从父组件接收传递很多个值,那么我们就需要在 props 里声明需要接受的值,如果孙子组件也需要,那么就又要重复在props中声明,显得非常繁琐。 例如我们现在有 A,B,C 三个组件,是父子孙的关系,如果 B、C 组件都要从 A 组件中继承一系列的属性: // com-a // com-b 这时候我们在 B 组件中通过 $attrs 就可以获取父组件传递的 title、desc和date,此外我们只要给 C 组件绑定 v-bind='$attrs' , 同理,C 组件内部也就可以通过 $attrs 获取到 A 里面的值了~ ps: B、C 组件的 DOM 上会绑定 A 传过来的属性,Vue 内部默认是这么处理的,要去掉的话给 B、C组件加上 inheritAttrs : false 属性即可。 $listeners 的用法也比较类似,不赘述了~ 跨组件通信的另一种方式 想到跨组件通信,可能会想到 eventBus , vuex 之类的方法,实际上我们可以借助 vue 本身的依赖注入这种方案优雅实现 我们首先需要在 main.js 中,定义一个 eventHub , 这是我们的关键点 // main.js new Vue({ el: '#app', router, store, components: { App }, data: { eventHub: new Vue() }, template: '' }) 之后,在我们在需要监控的组件的生命周期中绑定一下: // com-a mounted () { this.$root.eventHub.$on('update',(data)=>{ console.log(data); }) }, beforeDestroy () { this.$root.eventHub.$off('update') } 其他组件要触发改事件只需要一句话: emitEevent () { this.$root.eventHub.$emit('update',{ msg : 'hello world' }) } 值得注意的是,一定要在 beforeDestroy 生命周期中通过 $off 取消监听,不然会重复监听导致触发多次,如果只需要触发一次事件的话,$once 绑定会更加不错。 函数式组件 Vue 里的函数式组件和 React 中的无状态组件有些类似,如果说一个组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法,那么这时候我们可以考虑使用函数式组件。 函数式组件跟普通组件相比,因为没有状态管理,声明周期,只是函数,所以渲染开销低很多,以此可以优化我们的代码。 通常函数式组件的声明方式有2种(局部组件为例): 一种是模版渲染方式加上 functional 关键字创建 另一种是通过 render 渲染函数,并加上 functional 属性来标识创建,这种方式比模版更接近编译器,更加底层,渲染会更加迅速。 export default { functional: true, // Props 是可选的 props: { // ... }, render: function (createElement, context) { // ... } } 关于 render 函数,由于篇幅太长,这边不在赘述,想要了解更多细节和配置参数,可以参考官网的解释 由于函数式组件没有实例,为了弥补这个问题,组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象: props:提供所有 prop 的对象 children: VNode 子节点的数组 slots: 一个函数,返回了包含所有插槽的对象 scopedSlots: 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。 data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件 parent:对父组件的引用 listeners: 一个包含了所有父组件为当前组件注册的事件监听器的对象 injections: 如果使用了 inject 选项,则该对象包含了应当被注入的属性。 看起来雨里雾里,其实没那么高深! 我们来个例子更直观,我们现在需要渲染一个列表,没有具体的交互,仅做展示使用,为了优化代码,我们决定使用函数式组件来渲染。 列表数据格式如下: // list-data [{ id : 1 , title : '学习Vue' },{ id : 2 , title : '学习React' },{ id : 3 , title : '学习Angular' }] 如果我们通过模版方式来做的的话,我们可以定义如下: // todoList.vue 如果我们通过 render 函数来创建的话(.js文件,不是 .vue文件), 那么应该是这样: export default { functional: true, props: { todoList: { type: Array, default: () => [] } }, render: function (createElement, context) { let oLi = context.props.todoList.map(item => { return createElement('li', { domProps: { innerHTML: item.title } }) }) return createElement('ul', {}, oLi) } } 我们通过 import 在父组件导入一下,然后看看结果 效果一样,虽然 template 方式看起来简单的多,但是很多时候我还是比较倾向于 render 方式,因为它在修改或者条件判断的时候会比较方便,也省去了不少 v-if,v-show 这些指令,看起来更加优雅。 自动化导入component 如果你定义了一系列的牛X的公共组件,然而你每次需要频繁的去 import xxx from '../xxx',还要 components : { ComponentA ComponentB ... } 下面有种一劳永逸的方法,让你解放双手~ 需要借助 webpack 里面的 require.context 方法,简单了解一下,通过它获取一个特定的上下文,然后从中读取指定目录下的文件和文件内容。 该方法有三个参数 directory,useSubdirectories和useSubdirectories directory {String} -读取文件的路径 useSubdirectories {Boolean} -是否遍历文件的子目录 regExp {RegExp} -匹配文件的正则 如果我要遍历 components 目录下的所有公共组件,我就可以这么做: 首先在components目录下创建一个 baseComponent.js 文件, // baseComponent.js import Vue from 'vue' const autoRequireComponent = require.context('./', false, /.vue$/) autoRequireComponent.keys().forEach(file => { //获取组件配置信息 const componentConfig = autoRequireComponent(file).default //获取组件的名称 , 将 ./UButton.vue 名称替换成 UButton const componentName = file.replace(/^\.\//, '').replace(/\.\w+$/, '') //注册组件 Vue.component(componentName, componentConfig) }) 其中 autoRequireComponent.keys() 返回的就是一个文件名称的数组,类似于 ['UButton','UInput'],然后遍历并通过 Vue.component 方法注册到全局 最后在 main.js 中 import 一下就搞定了,现在你可以在任意组件中调用你的公共组件了! 该方法在批量处理一些文件的时候会有奇效! 以上就是我日常开发中遇到或者总结到的一些东西,如果你有更好更优雅的方式,记得分享哈~

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):