vue优化相关(0405)

449 阅读7分钟

一、插槽slot:

1.匿名插槽:

分别定义两个父子组件DashBoard和childOne:

父组件中:

<template>
 <child-one>
    <p>hello,world!</p>
  </child-one>
</template>

<script>
import childOne from './childOne/index'
export default {
  components:{
    childOne
  }

}
</script>

子组件:

<template>
    <div class="child-page">
        <h1>子页面</h1>
        <slot></slot>
    </div>
</template>

渲染结果: 父页面:

子页面:

可以看到子组件的slot位置被成功替换成了hello,world!。

2.具名插槽:

子组件中定义插槽:

<template>
    <div class="child-page">
        <h1>子页面</h1>
        <slot name="header"></slot>
        <slot></slot>  
        <slot name="footer"></slot>
    </div>
</template>

<script>

</script>
<style lang="less">

</style>

父组件:

<template>
 <child-one>
   <template v-slot:header>
      <p>我是头部</p>
    </template>
    <template v-slot:footer>
      <p>我是脚部</p>
    </template>
    <p>我是身体</p>
  </child-one>
</template>

<script>
import childOne from './childOne/index'
export default {
  components:{
    childOne
  }

}
</script>

子组件

<slot name="header"></slot>

的位置被替换成

<p>我是头部</p>

子组件

<slot name="footer"></slot>

的位置被替换成

<p>我是脚部</p>

结果为:

3.作用域插槽:

子组件中:

<template>
    <div class="child-page">
        <h1>子页面</h1>
         <slot name="fruits" :fruits="fruitsN">
            芒果
        </slot>
    </div>
</template>
<script>
     export default {
        data(){
            return {
                fruitsN:'草莓'
            }
        }
    }
</script>

父组件中:

<template>
 <child-one>
    <template v-slot:fruits="slotProps">
        {{slotProps.fruits}}
    </template>
  </child-one>
</template>

执行结果:

我们可以看到子组件中的芒果被替换成了草莓。 关于作用域插槽将其改写一下: 父组件:

<template>
 <child-one>
    <template v-slot:fruitsName="slotProps1">
        {{slotProps1.aa}}
    </template>
  </child-one>
</template>

<script>
import childOne from './childOne/index'
export default {
  components:{
    childOne
  }
}
</script>

子组件:

<template>
    <div class="child-page">
        <h1>子页面</h1>
         <slot name="fruitsName" :aa="fruitsN">
            芒果
        </slot>

    </div>
</template>

<script>
     export default {
        data(){
            return {
                fruitsN:'草莓'
            }
        }
    }
</script>

执行结果不变 子组件中slot后面是定义的插槽名称,需要与父组件中的v-slot名称一致,要改变的值即fruitsN用一个动态名称接收(如aa)父组件接收时候同样是在v-slot后面使用XXX.aa;

render函数

render函数有三个参数: 1.要渲染的元素或组件,可以是一个html标签、组件选项或一个函数(不常用),该参数为必填项。

    // 1. html 标签
    h('div');
    // 2. 组件选项
    import DatePicker from '../component/date-picker.vue';
    h(DatePicker);

2.对应属性的数据对象,比如组件的props、元素的class、绑定的事件、slot、自定义指令等,该参数是可选的, 3.子节点,可选,String或Array,它同样是一个h。

[
  '内容',
  h('p', '内容'),
  h(Component, {
    props: {
      someProp: 'foo'
    }
  })
]

1.如下代码:

<template>
    <div :class="{'blue':isBlue}">
        <span>render渲染</span>
    </div>
</template>

用render函数实现:


<script>
   export default{
       data(){
           return {
               isBlue:true
           }
       },
       render(h){
           return h('div',{
               'class':{
                   'isBlue':this.isBlue
               }
           },[
               h('span','render渲染')
           ])
       }
   }
</script>

带有v-for,v-if:

<template>
    <ul v-if="items.length">
        <li v-for="item in  items">{{ item.name }}</li>
    </ul>
    <p v-else>No items found.</p>
</template>

采用render渲染。

<script>
   export default{
       data(){
           return {
               items:[
                   {name:'11'},
                   {name:'22'}
               ],
               isBlue:true
           }
       },
       render(h){         
          if(this.items.length){
                return h('ul',
                    this.items.map(item => h('li',item.name))
                )
           }else{
               return h('p','No items found.')
           }
       }
   }
</script>

二、el-table的二次封装

我们在使用el-table的时候感觉很多时候都是在重复复制粘贴,我们想数据都是动态获取的,如果用v-for去动态循环出来的话,其阻碍无非是在于变化的表头,在这里我们想到一种思路就是将表头跟表格内容分开来渲染。

接下来用代码去验证实现一个简易的带选择框以及操作按钮的表格,代码如下:

 <el-row>
        <el-col :span="24">
            <el-table :data="list" border @selection-change="handleSelectionChange" :max-height="tableHeight" >
                <template v-for="(column,index) in columns">
                    <el-table-column :key="index" v-if="column.type === 'selection'" type="selection" width="55"> </el-table-column>
                    <el-table-column :key="index" v-else-if="column.type === 'index'"  type="index" width="50" label="序号"> </el-table-column>
                    <el-table-column :key="index" v-else align="left" :label="column.title" :width="column.width">
                        <template slot-scope="scope">
                            <label v-if="!column.hidden">
                                <label v-if="column.type === 'operate'">
                                    <a href="javascript:void(0)" class="operate-button" v-for="(operate, index) in column.operates" :key="index" @click="handleClick(operate, scope.row)">
                                        {{operate.name}}
                                    </a>
                                </label>
                                <span v-else>
                                    {{scope.row[column.key]}}
                                </span>
                            </label>
                            //此处的代码用作用域插槽依靠column.slot布尔值去动态的控制某一列的内容显示与否
                            <label v-if="column.slot">
                                <slot v-if="column.slot" :name="column.slot" :scope="scope">
                                    <!-- {{scope.row[column.key]}} -->
                                </slot>
                            </label>
                        </template>
                    </el-table-column>
                </template>
                <slot/>
            </el-table>
        </el-col>
    </el-row>

js部分代码:

<script>
export default {
    name: 'Base',
   props: {
    // 核心数据
    list: {
      type: Array,
      default: () => []
    },
    columns: {
      type: Array,
      required: true,
      default: () => []
    }
  },
    computed: {
        tableHeight() {
        // return this.$store.state.custom.maxTableHeight
        return 400
        }
    },
    methods:{
        handleClick(action,data){
            this.$emit(`${action.emitkey}`,data)
        },
        handleSelectionChange(val) {
            this.$emit('change-select', val)
        },
    }
}
</script>

接下来我们在包含组件的里面:

 <Base
      v-loading="loading"
      :columns="headers"
      :list="list"
      @reset="resetCb"
      @edit="editCb"
      @delete="deleteCb"
    >
      <!-- 选择自定义slot -->
      <!-- <template slot="roleSlot" slot-scope="{ scope }">
        {{ scope.row.roleName }}
      </template> -->
    </Base>

data中定义数据(主要关注表头部分headers):

  data() {
    return {
      loading: false,
      type: '',
      headers: [
        { type: 'selection' },
        { type: 'index' },
        { key: 'loginName', title: '登录名' },
        { key: 'userName', title: '用户名' },
        { key: 'roleName', title: '角色名称', hidden: true, slot: 'roleSlot' },
        { key: 'createDate', title: '创建时间' },
        // operate 这一行可以选择直接使用slot或者是使用配置项
        {
          type: 'operate',
          title: '操作',
          operates: [
            { name: '重置', emitKey: 'reset' },
            { name: '编辑', emitKey: 'edit' },
            { name: '删除', emitKey: 'delete' }
          ]
        }
      ]
    }
  },

对于表体内容list我们还是正常的发送后端请求获取

内容渲染即上述渲染代码(只要header中的key字段与后台相匹配):

<span v-else>
    {{scope.row[column.key]}}
</span>

三、合理使用mixins

我们页面大量用到分页组件此时我们考虑单独拿出一个js方法去定义: 如下mixins/list.js

const listMixins = {
  data() {
    return {
      loading: false, // 伴随loading状态
      pageNo: 1, // 页码
      pageSize: 15, // 页长
      totalCount: 0, // 总个数
      pageSizes: [15, 20, 25, 30], // 页长数
      pageLayout: 'total, sizes, prev, pager, next, jumper', // 分页布局
      list: []
    }
  },
  methods: {
    // 分页回掉事件
    handleSizeChange(val) {
      this.pageSize = val
      this.load()
    },
    handleCurrentChange(val) {
      this.pageNo = val
      this.load()
    },
    /**
     * 表格数据请求成功的回调
     * @param {*} apiResult
     * @returns {*} promise
     */
    listSuccessCb(apiResult = {}) {
      console.log(apiResult)
      return new Promise((resolve) => {
        let tempList = [{ name: 'demo', id: 'demo', age: 'demo' }] // 临时list
        this.loading = false
        // 直接抛出
        resolve(tempList)
      })
    },
    /**
     * 处理异常情况
     * ==> 简单处理  仅仅是对表格处理为空以及取消loading
     */
    listExceptionCb(error) {
      this.loading = false
      console.error(error)
    }
  },
  created() {
    console.log(`list mixin creatd`)
  }
}
export default listMixins

在对应组件我们只需引入:

import listMixins from '@/mixins/list'

便可以直接使用在mixins中定义的方法

<!-- 分页部分 使用mixin中的默认值-->
    <el-col :span="24" class="page">
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageNo" :page-sizes="pageSizes" :page-size="pageSize" :layout="pageLayout" :total="totalCount">
      </el-pagination>
    </el-col>
    
listSuccessCb().then(() => {
      //     // todo
      //     console.log('后续处理');
      //   })

四、webpack打包优化

使用Happypack插件 提示:由于HappyPack 对file-loader、url-loader 支持的不友好,所以不建议对该loader使用。 使用步骤: 1.安装:

npm i -D happypack

修改你的webpack.config.js 文件

const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        //把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行
        loader: 'happypack/loader?id=happyBabel',
        //排除node_modules 目录下的文件
        exclude: /node_modules/
      },
    ]
  },
plugins: [
    new HappyPack({
        //用id来标识 happypack处理那里类文件
      id: 'happyBabel',
      //如何处理  用法和loader 的配置一样
      loaders: [{
        loader: 'babel-loader?cacheDirectory=true',
      }],
      //共享进程池
      threadPool: happyThreadPool,
      //允许 HappyPack 输出日志
      verbose: true,
    })
  ]
}

参考文章:juejin.cn/post/684490…