Vue自定义组件 & 前端初学习心得
文章目录
小总结
最近在老板公司实习,第一周要搞后端,完成增删改查工作;待测试好所有后台接口之后,第二周又要在该项目上搞前端。后端框架Mybatis+springMVC我还比较熟悉,上手没什么难度,但是对于前端框架vue-admin-template + element-ui,虽然我之前也写过些前端,但都是一个人单干的那种(用bootstrap + vue源),没人教也没人能问,因此对于这个全新的框架,我还是花了不少心思去学习,去讨教。下面谈谈我的心得吧,也算是对自己这几周实习的总结。
如何快速上手干一个页面
- 前端要上手快,不单单要掌握必备知识,还要对页面的结构,要实现的业务逻辑要了如指掌(学前端一定要把知识和业务逻辑建立关系,不是单纯停留在框架的使用上!!!),这个按键对应了自己定义的哪个函数要记住。而且定义的方法一定要有注释,功能,逻辑要写清楚,对应的是哪个控件的,也要写清楚。
为什么要封装组件
- 虽然页面看上去是一个整体,但是其中是有层级结构的,是由各个具有嵌套关系的组件(即函数)构成的。
- 要封装成组件,即需要向外暴露参数接口,便于动态调整组件里的值
- 设计前端组件其实是一种模块化思维,将复杂的东西集成到一个小组件中,既方便修改,也方便复用。在实际开发时,常常对原生组件进行二次封装,比如接下来要封装的组件是在element-ui中的
<el-tree>
上进行二次封装。- 组件其实就是一个函数,通过定义组件的形参,向父组件提供可供调用的接口。函数的返回值,可以通过父组件传递给子组件的函数,实现子组件的回调,给父组件赋值。( 回调函数(callback)是什么?)
- 对组件的学习,可以看看市面上常用组件的源码(element-ui),看它是怎么封装的,提供了哪些参数,暴露了哪些方法。
自定义组件核心
参考
1、封装组件的步骤
准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型(父组件可以使用 props 把数据传给子组件,props里定义的参数就是形参)
准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法,方便父组件去获取子组件的引用,得到子组件对象,调用子组件的方法。
子组件可以使用
$emit
, 给父组件的函数传递参数,实现父组件中值的修改 。(可以简单理解成组件的输出接口,实际上利用回调函数告知父组件已完成任务)
2、参数传递
在父组件中使用子组件
<TreeCheckbox>
(该名字与父组件注册的子组件名字一致)<!--父组件--> <TreeCheckbox style="width:270px;background-color: #272727;" :expanded_keys="expanded_keys" :checked_keys="checked_keys" :props="defaultProps" @checkboxSelected="checkboxSelected" > </TreeCheckbox>
其中
:expanded_keys
,:checked_keys
,:props
可以理解成子组件中的形参,这些形参是在props
中定义的,每个形参都有:<!--子组件--> <template> <el-tree ref="tree" :data="data" show-checkbox :node-key="defaultProps.value" :default-expanded-keys="expanded_keys" :default-checked-keys="checked_keys" :props="defaultProps" @check="handleCheckChange()"> </el-tree> </template> <script> export default { props:{ //组件参数配置项,用于封装前端组织树结构 defaultProps: { type: Object, default: () => { return { value: 'id', // ID字段名 label: 'label', // 显示名称 children: 'children', // 子级字段名 } }, }, //默认展开的节点的 key 的数组 expanded_keys: { type: Array, default: () => { // return ['1-1','1-2'] return [] }, }, //默认勾选的节点的 key 的数组 checked_keys: { type: Array, default: () => { // return ['1-1','1-2'] return [] }, }, }, } </script>
3、emit的使用(如何暴露组件方法)
参考vue2.0 如何自定义组件(vue组件的封装/组件的二次封装)
我们已经会使用 父组件向子组件传数据了,那如子组件如何来修改父组件的数据呢?这里提供 2 种实现方法,但是 第一种不推荐,强烈不推荐(引用修改)
方式一:
//父组件 expanded_keys : [] //子组件 this.expanded_keys = ['0-1','1-1'] //'我被修改了'
即,父组件将 对象 数据传递给子组件,子组件直接修改props过来的对象的值。这种方法仅适用于Array,Object等引用类型,但在实际开发中,强烈不建议这么写!!!这种方式写多了,容易出错,特别是多层组件嵌套的时候。这种修改对代码的迭代和错误的捕捉都不友好。
方式二:
通过 $emit 方法去调用父组件的方法,在父组件中修改data的数据。(根正苗红的方法,规范写法)
// 子组件 <script> this.$emit('checkboxSelected',this.selectOptions); //select事件触发后,自动触发父组件的checkboxSelected事件 </script> // 父组件 <el-form-item class="flex_row person_participate" label="参与人员" prop="ip"> <TreeCheckbox style="width:270px;background-color: #272727;" :expanded_keys="expanded_keys" :checked_keys="checked_keys" :props="defaultProps" @checkboxSelected="checkboxSelected" > </TreeCheckbox> </el-form-item> <script> //参与人员复选框选中事件(会激活子组件向该父组件传值,并修改父组件的值) checkboxSelected(value){ this.dialogForm.personOrgSelected = value alert(this.dialogForm.personOrgSelected) }, </script>
将父组件的方法注入子组件
@checkboxSelected="checkboxSelected"
,然后在子组件中通过$emit
调用他,并传递参数。达到修改的目的。
自定义组件注意点
1、组件声明 和 组件注册
父组件在使用子组件之前,
- 不仅需要单独测试子组件的内部功能是否完整**(比如在子组件页面中,测试后台数据传值到页面是否存在问题)**
- 还需要在父组件中导入子组件的包并对其重命名(组件声明),接着完成组件注册
<!--父组件--> <script> //导入方法或组件 import "@/utils/dateUtil"; import { organizationfindTreeList,organizationfindAll } from '@/api/organization/index' import Pagination from "@/components/PagiNation"; //分页组件 // import TreeSelect from "@/components/TreeSelect" //树形结构 import TreeCheckbox from "@/views/emergencyTrain/TreeCheckbox"; //参与人员树形结构(自定义的子组件) import { //真实接口 trainManageInsert, trainManageFileInsert, trainManageUpdate, trainManagefind, trainManageDel, trainManageInsertPersonal, trainManageFindPageList, personnelbyOrganization, trainPlanFindList, //培训计划全查(无分页) }from "@/api/tranplan/index" export default { //组件注册 components: { Pagination, // TreeSelect, TreeCheckbox //注册自定义的子组件 }, data() { return { } } } </script>
2、子组件监听父组件的值是否发生修改
这里使用
watch
监听没用,参考vue子组件监听父组件传值<script> export default{ ... methods : { watch: { expanded_keys: { handler(val) { console.log('expanded_keys deep change', val) this.expanded_keys = val }, deep: true, // 深度监听 immediate: true, // 初次监听即执行 }, //监听checked_keys checked_keys: { handler(val) { console.log('checked_keys deep change', val) this.checked_keys = val }, deep: true, // 深度监听 immediate: true, // 初次监听即执行 }, }, } } </script>
但是却存在bug:父组件值修改了,子组件也检测到父组件传进来参数(Array)的值发生修改,但是在回显的时候,即使expand_key为空,但是页面总是能回显出数据。这里使用vue生命周期中的
updated()
方法来检测当前的页面的值是否发生更新。updated() { console.log("updated") console.log(this.expanded_keys)//获取到父组件传过来的值 console.log(this.checked_keys)//获取到父组件传过来的值 },
从控制台可以看出,updated方法中打印出来的
checked-key
是个空数组,然而原生组件el-tree
在使用checked-key
回显复选框勾选状态时,却没有动态修改。主要原因是使用了
default-checked-key
,它只会初始化值,并不能在检测到值修改时,完成自动更新,因此需要获取原生组件(el-tree)对象,通过它提供的方法,手动修改checked_keysupdated() { console.log("updated") console.log(this.expanded_keys)//获取到父组件传过来的值 console.log(this.checked_keys)//获取到父组件传过来的值 this.$refs.tree.setCheckedKeys(this.checked_keys) //如果父组件的值发生更新(子组件也能监听到),但是仍然需要获取原生组件<el-tree>的引用,实现赋值 },
这里的
$refs.tree
是对子组件中原生组件<el-tree>
的引用