二、开发技巧推荐
2.1 vue开发技巧
2.1.1 使用v-bind="obj"将所有属性传给子组件
应用场景1:
将自身接收到的所有props传递给它的子组件,子组件需要在其props:{} 中定义要接受的参数名,常用于对组件的二次封装中,如:对el-table、el-form利用json配置项来控制渲染加载等。
1、v-bind=“$props”
p r o p s :当前组件接收到的 p r o p s 对象。可以通过 v − b i n d = " props:当前组件接收到的 props 对象。可以通过 v-bind="props:当前组件接收到的props对象。可以通过v−bind="props" 传 入 内 部 组 件 , 也 可 以 通 过 v m . props" 传入内部组件,还可以通过vm.props[name]的形式去获取。
应用场景2:
针对组件有过多的属性传参时,可以不用再一个一个的写,可以把较多的参数传递简化,减少组件本身的参数定义。
2、v-bind=“obj”
如:
<Children :name="name" :age="age" :sex="sex" :job="job"></Children>
改写:
<Children v-bind="{name,age,sex,job}"></Children>
2.1.2 使用$on(‘hook:’)
o n ( ‘ h o o k : ’ ) 能够注册 / 监听任意一个钩子函数。使用 on(‘hook:’)能够注册/监听任意一个钩子函数。使用on(‘hook:’)能够注册/监听任意一个钩子函数。使用on(‘hook:’)方法,可以仅使用一种生命周期方法(而不是两种)来定义/删除事件 。
实现一个定时器的调用与销毁大家很可能会用以下方式实现:
export default{ data(){ timer:null // 需要创建实例 }, mounted(){ this.timer = setInterval(()=>{ //具体执行内容 console.log('1'); },1000); }, beforeDestory(){ clearInterval(this.timer); this.timer = null ; } }
这种方法存在的问题是:
1、vue实例中需要有这个定时器的实例,感觉有点多余。
2、创建的定时器代码和销毁定时器的代码没有放在一起,不容易维护,通常很容易忘记去清理这个定时器。
使用o n ( ‘ h o o k : ’ ) 监听 b e f o r e D e s t o r y 生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用 on(‘hook:’)监听beforeDestory生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用on(‘hook:’)监听beforeDestory生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用once进行注册监听。
export default{ mounted(){ const timer = setInterval(()=>{ console.log('1'); },1000); this.$once('hook:beforeDestory',()=>{ // 监听一次即可 clearInterval(timer); timer = null; }) } }
2.1.3 如何为Vue组件添加非响应式数据
在vue组件中内data内函数返回的对象默认是响应式的,vue的observe函数会遍历此对象所有的属性和子孙属性并转化为getter/setter, 使Vue能够追踪依赖,在属性被访问和修改时通知变更。这种响应式被用在模板更新、watch变更、computed依赖中非常有用。
为什么要设置非响应式数据?
如果我们的数据不会改变,或者只会整体改变,或者本身就不需要响应式,那么为深度响应式做的转化、依赖以及产生的闭包、watcher空间其实是多余的,白白浪费了时间和性能。
平时我们自己写的对象不会太复杂这种性能消耗并不明显,但当在引用第三方工具库,比如图表、地图、模型等,如果把多个不需要深度响应式的第三方实例或数据直接挂载到data属性上,又或者遇到大数据量列表,性能的影响就会比较明显。
利用Vue无法检测到对象属性的添加来实现
官方文档中有介绍
受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:
let vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应式的 vm.b = 2 // `vm.b` 是非响应式的
因此,我们可以待实例完成初始化后,即created中加入
export default { data() { this.version = '' // 这样定义初始值也行 return { } }, created() { // 直接在created及后续的生命周期函数里定义赋值 this.bigData = { ··· } } ··· }
2.1.5 适时使用$options
vue实例属性$options是一个对象,可以调用vue的各个组件下的方法和数据 。
应用场景:
1、获取、调用data外定义的属性
<script> export default { data() { return { }; }, //在data外面定义的属性和方法通过$options可以获取和调用 name: "options_test", age: 18, testOptionsMethod() { console.log("hello options"); }, created() { console.log(this.$options.name); // options_test console.log(this.$options.age); // 18 this.$options.testOptionsMethod(); // hello options }, </script>
2、复用过滤器filters中的方法
假设在时间选择控件里可以获取到一个返回的时间戳,需要转换为日期时间来显示,这个时候就可以用到一个filterTime函数来格式化处理时间戳返回想要显示的时间格式。如果这个时候后台要求我们在离线场景提交时增加一个同样时间格式的提交时间字段,这个时候我们就可以利用this.$options.filters来拿到这个函数进行转换,而不是重新在定义一个方法来处理。
<template> <div>{{ planStartSjc | filterText }}</div> </template>
export default { data() { return { planStartSjc: 1681960059227 // 计划开始时间的时间戳 } }, filters: { // 时间戳转换 filterTime: function (sjc) { let date = new Date(sjc) const year = date.getFullYear() let month = date.getMonth() + 1 let day = date.getDate() let hour = date.getHours() let minute = date.getMinutes() let second = date.getSeconds() month = String(month).padStart(2, '0') day = String(day).padStart(2, '0') hour = String(hour).padStart(2, '0') minute = String(minute).padStart(2, '0') second = String(second).padStart(2, '0') let time = `${year}-${month}-${day} ${hour}:${minute}:${second}` return time } }, methods:{ // 计划提交 submitPlan(){ let filterTime = this.$options.filters.filterTime let nowSjc = + new Date() // 此时的时间戳 let planCreateTime = filterTime(nowSjc) // 转换后的时间 let planStartTime = filterTime(this.planStartSjc) // 转换后的时间 let params = { planCreateTime, // 计划创建时间 planStartTime, // 计划开始时间 } }, }, }
3、一键搞定之重置data中的某个数据
<script> export default { data() { return { // 表单 searchForm: { input: '', name: '', isSelected: false } } }, methods: { retset() { //重置某一个表单数据 this.searchForm = this.$options.data().searchForm; } } } </script>
4、重置整个data的数据
<script> export default { data() { return { // 表单 searchForm: { input: '', name: '', isSelected: false }, dataList: [], planId: '' } }, methods: { // 更新测试 testUpdata() { this.searchForm = { input: '输入', name: '张三', isSelected: true } this.dataList = [1,2] this.planId = '123456' }, retset() { // 重置前先更新一些数据 this.testUpdata() console.log('重置前的数据', this.$data) // 直接赋值重置即会导致报错产生(最新的Vue已经不允许这样直接对根实例$data进行赋值) // this.$data = this.$options.data(); // 改用以下写法重置 Object.assign(this.$data, this.$options.data()); console.log('重置后的数据', this.$data) } } } </script>
2.1.6 如何解决Vue在main.js全局引入scss文件,组件里使用scss变量报错问题
报错: Syntax Error: SassError: Undefined variable.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSFtggSk-1682384948777)(前端代码整洁与开发技巧.assets/20210909143917135-1681975189904.png)]
我一开始的引入方式是这样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5PhE89y-1682384948778)(前端代码整洁与开发技巧.assets/20210909145443693-1681975204155.png)]
以为在main.js全局引入了就一劳永逸,写着常规css样式时还好好的,用到scss变量时就报错了。
- 解决方法一
在需要用到的 .vue 文件里单独引用 variable.scss 变量文件,但是达不到我们想要的“一劳永逸“效果,可能还会有奇奇怪怪的潜在问题,建议使用方法二。
- 解决方法二
通过配置,使得全局都能使用scss变量,而不用每个文件单独引入 。
1、vue-cli2创建的项目:
修改build中的utils.js文件,将 scss: generateLoaders(‘sass’),修改为如下: scss: generateLoaders('sass').concat({ loader: 'sass-resources-loader', options: { //你自己的scss全局文件的路径 resources: path.resolve(__dirname, '../src/common/index.scss') } }),
2.vue-cli3创建的项目:
module.exports = { css: { loaderOptions: { // 不同 sass-loader 版本对应关键字, // v8-: data v8: prependData v10+: additionalData sass: { additionalData: `@import "@/assets/scss/index.scss";` }, scss: { additionalData: `@import "@/assets/scss/index.scss";` }, } } }
注意:scss 配置后面需要加 分号 ‘;’,否则会报错 Syntax Error: SassError: media query expression must begin with ‘(’
参考文档:https://cli.vuejs.org/zh/guide/css.html#css-modules