目录
1.父子组件通信-双向绑定
官方推荐:修改父组件的data,来达到修改子组件props的效果,而不是直接通过v-model来修改子组件的props中的继承数据;
解决方法如下:
在子组件的data选项中,新定义数据,然后把props中数据赋值给data中新定义的变量,然后再在子组件的template中通过v-model来绑定data中新定义的变量,而不是props中的变量;
data:{
return {新变量=this.props中的变量}
}
event.target.value
2.父组件访问子组件:children/refs
父组件访问子组件的实例对象:$children或者$refs;
this.$children返回的是一个VueComponent数组,然后通过索引去获取你想获取的哪个子组件实例对象,然后就可以访问子组件实例对象的属性、方法;
$children缺点在于:由于返回的是一个VueComponent数组,当开发者在HTML模板中添加/删除一个自定义组件的时候,就会导致开发者同时修改$children数组的索引下标,这就容易导致各种问题产生;
为了解决这个问题,可以使用$refs来取代$children;在自定义组件中添加ref=”key”的属性,然后就可以在父组件中通过this.$refs.key的形式来直接访问具体的某个VueComponent子组件,再也不用担心VueComponent数组下标的问题;
<body> <div id="app"> <cpn_father v-bind:cpn_movies="movies" @fatherclick="fatherclick2" ref="cpn0"></cpn_father> <button @click="getChildren">父组件访问子组件的实例对象</button> </div> <template id="cpn_template"> <div> {{title}} <ul> <li v-for="item in cpn_movies" @click="itemclick(item)">{{item}}</li> </ul> <cpn_child></cpn_child> </div> </template> <script src="js/vue.js"></script> <script> const cpn_father =Vue.component('test_cpn',{ template: '#cpn_template', data(){ return {title: '这是自定义的子组件'} }, props:{ cpn_movies:{ type:Array, required:true, default(){ return ['自定义组件的movies默认值']; } } }, methods: { itemclick(item){ console.log('这是自定义组件的单击事件',item); this.$emit('fatherclick',item); } } }); const root = new Vue({ el: '#app', data: { movies:['进击的巨人','海贼王','火影忍者','数码宝贝'] }, components: { cpn_father }, methods: { fatherclick2(item){ console.log('这是自定义组件发给父组件的事件',item); }, getChildren(){ console.log(this.$children); console.log(this.$refs.cpn0) } } }); </script> </body> |
3.子组件访问父组件:parent/root
子组件访问父组件的实例对象:$parent、$root;
$parent访问自己的直属上级组件,而$root则访问自己的根Vue实例;
<body> <div id="app"> <h1>这是Vue实例</h1> <cpn_father ref="cpn0"></cpn_father> </div> <template id="cpn_template"> <div> <h2>这是Vue实例的第1层子组件</h2> <cpn_child></cpn_child> </div> </template> <template id="cpn_template2"> <div> <h3>这是Vue实例的第1层子组件的第1层子组件,即Vue实例是该子组件的-爷爷级别</h3> <button @click="test">访问父组件,即Vue的第1层子组件</button> <button @click="test1">访问Vue组件</button> </div> </template> <script src="js/vue.js"></script> <script> const root = new Vue({ el: '#app', data: { movies:['进击的巨人','海贼王','火影忍者','数码宝贝'] }, components: { cpn_father:{ template: '#cpn_template', data(){ return {message:'Vue实例的第1层子组件'} }, components:{ cpn_child:{ template: '#cpn_template2', methods: { test(){ console.log(this.$parent);// 这里返回的是VueComponent实例 console.log(this.$parent.message);// 这里返回的是VueComponent实例 }, test1(){ console.log(this.$root);// 这里返回的是Vue实例 } } } } } } }); </script> </body> |
注意:
我们开发自定义组件,是为了在不同场景下进行复用,既然是复用,你就不能去严苛规定父组件必须有哪些属性、方法,这就决定了从子组件来访问父组件实例对方的使用场景很少;
4.slot插槽
组件的插槽是为了让封装的组件更加具有扩展性,让使用者来决定组件内部来展示哪些内容;
通俗来讲,就是使用者在使用某个组件时,需要对组件进行个性化修改,但开发者又不能去修改组件源代码,此时,则可以通过slot来进行这种个性化扩展;
<slot/>定义在子组件的template中,且<slot/>中可以定义默认值;
具名插槽:当template中存在多个<slot/>时,需要对多个<slot/>起唯一性的名字,这样当在Vue实例中,可以针对某一个<slot/>进行替换;
使用规则如下:
①<slot name=”xxxx”/>
②使用的时候slot=”xxxxx”
<body> <div id="app"> <h1>这是Vue实例</h1> <cpn_father ref="cpn0"> <p slot="middle">测试slot</p> </cpn_father> </div> <template id="cpn_template"> <div> <h3>自定义组件</h3> <slot name="left"><button>返回</button></slot> <slot name="middle"><button>查询</button></slot> <slot name="right"><button>删除</button></slot> </div> </template> <script src="js/vue.js"></script> <script> const root = new Vue({ el: '#app', components: { cpn_father:{ template: '#cpn_template' } } }); </script> </body> |
5.编译作用域
如果在template中使用变量,那么变量就会去该template所绑定的实例中查找同名变量,可以总结为:父组件template的所有东西都会在父级作用域内编译、子组件template的所有东西都会在子级作用域内编译;
作用域插槽:父组件替换子组件template中的<slot/>标签,但是内容还是由子组件提供,通俗来讲就是:由父组件提供展示样式,子组件提供展示内容;
使用步骤如下:
①在子组件template中,通过变量XXX指向data中的数据;
②在父组件template中来访问XXX来间接达到访问子组件data中的数据;
③然后可以按照想要的样式来输出子组件data中的数据;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>作用域插槽</title> </head> <body> <div id="app"> <h1>这是Vue实例</h1> <cpn_father ref="cpn0"> <!-- 2.5以下必须用template --> <!-- slot-scope='slottest',这里的slottest是随便起的名字,而slottest.test则是代表slottest去访问在子组件template中定义的变量test,而test又指向了子组件data中的message --> <template slot-scope="slottest"> <span v-for="item in slottest.test">{{item}} #-# </span> </template> </cpn_father> <cpn_father ref="cpn1"></cpn_father> </div> <template id="cpn_template"> <div> <!-- :test='message',这里的test是随便起的名字,而message则是子组件中data中的message,表明test指向了template对应实例对象data中的message --> <slot :test="message"> <ul> <li v-for="item in message">{{item}}</li> </ul> </slot> </div> </template> <script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data:{ }, components: { cpn_father:{ template: '#cpn_template', data(){ return {message: ['a','b','c','d','e','f']}; } } } }); </script> </body> </html> |
6.模块化开发
在ES6之前, JS通过闭包 + 匿名函数的方式来封装不同的模块,来实现模块化开发,此刻常见的模块化规范有:CommonJS/AMD/CMD;
CommonJS规范主要有2部分组成:导出、导入;具体流程如下:在一个名叫org.js文件中,该js文件中所有代码编写方式,与HTML中<script/>标签中的一致,CommonJS会把每个js文件当做一个独立的作用域,一个独立的模块,对于要导出的内容则通过如下代码方式来导出:module.exports = {导出内容1:导出内容1,导出内容2:导出内容2}
然后在想要使用该模块的js文件中,通过如下内容来导入:let {导出内容1,导出内容2} = require(‘org.js’);这样就可以在其他js文件中使用org.js已经定义好的变量与方法;
针对模块化开发,到了ES6才定义相关的语法:export导出、import导入;
7.ES6的export/import基本使用
<script src="xxx.js" type="module"/>,其中的type="module"代表的意思是被引入的xxx.js是一个独立的空间,与其他js文件是彼此独立的存在,如果其他js文件想要访问xxx.js中的内容,则需要在xxx.js中使用export导出,然后在使用的js文件import from导入;
export default:在某些情况下,一个模块中包含某个功能,但该功能的命名交给导入这来自己命名;
注意:一个js文件中,export default只能有一个;
import命令来加载对应的模块,注意<script src type=”module”/>,如果想一次性全部导入,则可以通过 import * as 变量名 from XXXX来实现;
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="js/vue.js"></script> <script src="js/export1.js" type="module"></script> <script src="js/export3.js" type="module"></script> </body> </html> |
export1.js
let name = 'export1.js'; let age = 1; let flag = true;
function sum(num1,num2){ return num1 + num2; }
class T{ run(){ console.log('----------------------run'); } }
// 导出变量、函数、类 export { name,age,flag,sum,T }
// export default export default function(argument){ console.log(argument); } // 导出变量 // export let name = 'export1.js'; // export let age = 1; // export let flag = true;
// 导出函数 // export function sum(num1,num2){ // return num1 + num2; // }
// 导出类 // export class T{ // run(){ // console.log('----------------------run'); // } // } |
export3.js
import {name,age,flag,sum,T} from './export1.js';
// 通过*来一次性全部导入 import * as OBJ from './export1.js';
console.log(OBJ.flag);
// export default import testdefault from './export1.js';
if(flag){ console.log(name,age,flag,sum(3,3)); const t = new T(); t.run(); testdefault('test export default'); } |