一、插槽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>
渲染结果: 父页面:
子页面:
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>
结果为:
子组件中:
<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,
})
]
}