① vue cli 简介与安装html
vue cli 3.0以前安装的是vue-cli模块,vue cli 3.0以后安装的是 @vue/cli模块。
若是已经全局安装了旧版本的 vue-cli , 那么须要 先卸载vue-cli,再 全局安装 @vue/cli
虽然安装的是vue cli,可是 执行的命令仍然是vue
npm uninstall vue-cli -g // 卸载旧版本脚手架 npm install -g @vue/cli // 安装新版本脚手架 vue --version // 检测是否安装成功
② vue cli 的简单使用vue
vue create vue-test // 在当前目录下建立一个vue-test项目,经过问卷的方式选择好须要安装的模块后会自动安装并初始化vue项目 cd vue-test // 进入项目根目录下 npm run serve // 启动vue项目
若是入口文件是 .js文件,那么其中 必须建立一个Vue实例,而且 必须添加el属性,且其属性值 必须为"#app",而且 添加render属性渲染一个Vue组件以便可以显示组件内容。
若是入口文件是 .vue文件,那么能够直接渲染,.vue文件中的template里面 必需要有一个根元素标签,不限于<div>,可是不须要添加id为app的根元素,即 根元素内容能够任意书写
// main.jsvue-cli
import Vue from "vue"; import App from "./App"; const vm = new Vue({ el: "#app", // 固定为#app,不然没法显示App组件内容 render: h => h(App) }); export default vm; // 能够不对外暴露
若是父组件要向子组件传递数据,那么只须要在子组件中添加一个props属性,其属性值能够是一个数组,数组元素为一个字符串,即父组件给子组件传递数据时所使用的名称,直接在子组件标签中看成元素的一个属性名使用;props属性值也能够是一个对象,对象的属性名为父组件给子组件传递数据时所使用的名称,属性值为一个对象,用于控制父组件传递数据的类型、默认值等
// 父组件 Parent.vuenpm
<template> <div> Parent父组件: {{firstName}} <!--若是子组件的属性名前不带冒号,则传递是原字符串;若是带冒号则传递的是当前组件中该属性名对应的值,即this.firstName的值;--> <Son :value="firstName"></Son> <!-- 若是该属性名在该组件中不存在对应的值,那么虽然不会报错,可是子组件获取的值将为null,即获取不到值 --> <Son :value="firstName1"></Son> <!--父组件上不存在firstName1属性,子组件没法获取到value对应的值 --> </div> </template>
// 子组件 Son.vuejson
<template> <div> Son组件: {{value}} <button @click="change">修改子组件数据</button> </div> </template> <script> export default { mounted () { console.log(this.value); // props中定义的属性名也会添加到vue实例上,能够直接经过vue组件实例获取到 }, props: ["value"], // Son组件上定义了一个value属性,用于接收来自父组件传递过来的数据,能够子组件中能够直接使用 // props: { // 对象的形式 // value: { // type: String // 只能传递字符串类型,String是大写 // } // } methods: { change() { this.value = "zhang"; // 直接修改父组件的值是不容许的 } } } </script>
注意,上面子组件中添加了一个按钮用于修改value的属性值,因为Vue规定 子组件不能直接去修改父组件的值,因此会报错。即 父子组件中的数据是单向的。子组件中若是确实想要修改父组件传递过来的数据,那么能够在子组件中定义一个新的变量,将父组件传递过来的数据保存起来,这个时候子组件去修改这个新的变量对应的值就能够了,可是这样 子组件修改的值不会同步到父组件中,由于 子组件修改的是本身的数据了。
① 能够在父组件中使用子组件的时候,在子组件标签上监听一个事件,这个事件名能够任意,好比input事件,这样当子组件内部发射该事件后,子组件就能监听到该事件就能够直接调用父组件中的方法进行处理,如:
//父组件 Parent.vue数组
<!-- 在子组件上监听一个input事件,可是其事件处理函数是父组件中的函数 --> <Son :value="firstName" @input="change"></Son>
// 子组件,Son.vueapp
<button @click="change">修改父组件数据</button> methods: { change() { this.$emit("input", "zhang");// 子组件内部发射一个input事件 } }
在父组件使用子组件的时候,给子组件标签上添加了@input="change",子组件渲染的时候,就会在子组件上监听该事件,就至关于 子组件.$on("input", change),即 子组件监听到input事件后会触发父组件上change函数的执行,因此该种方式其实就是利用 父子组件单向数据流特性,由 子组件发起事件,经过 修改父组件中的数据来实现父子组件数据的同步, 本质就是修改父组件数据
② 父组件向子组件传递数据的时候,使用sync修饰符来修饰子组件上用于接收父组件数据的变量名,同时子组件内部发射一个update:value事件也能够实现父子组件数据的同步更新,如:
// 父组件 Parent.vueide
<Son :value.sync="firstName"></Son> <!-- <Son :value="firstName" @update:value="change"></Son> --><!--两者等价-->
// 子组件 Son.vue函数
methods: { change() { this.$emit("update:value", "zhang"); // 子组件发射update:value事件 } }
sync修饰符其实就是 在子组件上绑定值的同时监听了@update:value事件,是一种语法糖,可是 子组件上必须发射固定名称的update:value事件才会起做用。
须要注意的是,这里所谓的固定名称中 value是不固定的,value只是一个子组件用于接收父组件数据时所定义的变量(属性),好比子组件上定义的用于接收父组件的变量是 surname,那么子组件就要发射 "update:surname"事件了,父组件传递数据的时候就要使用: surname.sync="firstName"了
③ 若是子组件上定义的用于接收父组件数据的属性(变量)是value,而且在子组件中监听的是@input事件,那么咱们能够直接简写成v-model,由于v-model实际就是绑定value的值而且监听@input事件,如:
// 父组件 Parent.vueui
<Son v-model="firstName"></Son>
// 子组件 Son.vue
methods: { change() { this.$emit("input", "zhang");// 必须是发射input事件 } }
sync和v-model都能实现父子组件数据的同步,可是v-model相对比较局限,属性名必须是 value,事件名必须是 input,而sync修饰符属性名能够任意。
④ 若是是三级组件通讯,该如何处理?好比父组件与孙子组件通讯。
一样,咱们也能够利用单向数据流的原理,咱们只要可以改变父组件上的数据,那么儿子组件和孙子组件上的数据都会进行相应的修改了,而前面父子组件通讯的时候是经过子组件发射一个input事件来调用父组件的方法去改变父组件上的数据的,因为孙子组件直接发射一个input事件,父组件上是监听不到,由于父组件上监听的是子组件内部发射的input事件,可是咱们能够经过孙子组件的$parent属性获取到子组件,而后经过子组件去发射input事件,那么父组件就能监听到input事件了,从而改变父组件中的数据,实现三级组件通讯,如:
// 孙子组件 Grandson.vue
<div> Grandson组件: {{value}} <button @click="changeParent">修改个人父组件数据</button> </div> methods: { changeParent() { this.$parent.$emit("input", "Wang");// 经过孙子组件的父组件去发射input事件 } }
⑤ 若是是多级组件通讯呢?我也能够经过前面三级组件通讯原理,咱们只要遍历当前组件的全部祖先组件,而后让全部祖先组件都发射一个input事件,这样一层一层发射input事件,那么最终顶层的父组件确定可以收到一个input事件从而改变顶层父组件的数据,实现多级组件之间的通讯。如:
// main.js 在Vue原型对象上添加一个$dispatch方法,方便后辈组件调用
Vue.prototype.$dispatch = function (eventName, data) { // 切勿使用箭头函数 let parent = this.$parent; // 获取调用$dispatch方法的父组件 while(parent) { 遍历祖先组件,一层一层发射相应的事件 parent.$emit(eventName, data); parent = parent.$parent; } }
// 后辈组件
methods: { changeParent() { this.$dispatch("input", "Wang"); // 派发一个input事件,其祖先组件都会发生input事件 } }
⑥ 有后辈组件向祖先组件派发事件,天然有祖先组件向后辈组件广播事件,所谓广播就是祖先组件通知监听了某个事件的组件都执行一下对应的事件函数,如:
// main.js 在Vue原型对象上添加一个$broadcast方法,方便全部组件调用
Vue.prototype.$broadcast = function(eventName, data) { const broadcast = (children) => { // 递归调用broadcast方法 children.forEach((child) => { // 遍历子组件,每一个子组件都发射一个指定的事件 child.$emit(eventName, data); if (child.$children) { // 若是子组件上还有子组件 broadcast(child.$children) // 递归调用 } }); } broadcast(this.$children); }
⑦ v-bind和$attrs、v-on和$listenners的用法
当咱们在组件内部并无定义props属性来接收父组件传递过来的数据时,这些非prop声明的属性将会原封不动的添加到组件渲染后的html标签上,如:
// Parent.vue父组件
<!--父组件传递了 value、name、age三个属性给Son组件--> <Son :value="firstName" @input="change" :name="name" :age="age"></Son>
Son.vue 子组件
export default { props: ["value"] // 子组件上只定义了一个value属性用于接收父组件上的数据 }
子组件渲染完成后,除value属性外,name和age属性都原封不动添加到了html标签上,如:
<div name="Si" age="18"> Son组件: Li </div>
若是不想这些父组件传递过来的非prop属性出如今html标签上,那么能够 在子组件上添加一个 inheritAttrs属性,而且属性值设置为 false。
export default { inheritAttrs: false, // 不在html标签上继承非prop属性 props: ["value"] // 子组件上只定义了一个value属性用于接收父组件上的数据 }
须要注意的是虽然设置了inheritAttrs为false,可是子组件上仍是具备$attrs属性的,经过$attrs属性仍是能够获取到那些来自父组件传递过来的非prop属性的, $attrs为一个对象,对象属性名为非prop属性名,如上子组件上的$attrs值为{name: "Si", age: 18}
若是v-bind不绑定属性,直接赋值一个对象的时候,那么其会将对象的属性名看成组件的属性名,将对象的属性值传递给组件,如:
// Son.vue
<Grandson :value="value" v-bind="{name: 'Si', age: 18 }"></Grandson> <!--其等价于--> <Grandson :value="value" name='Si' age=18></Grandson>
因此若是父组件传递给子组件的数据,子组件不想用( 子组件并未定义相应的props属性进行接收),可是孙子组件想用,那么能够 经过v-bind直接传递给孙子组件,如:
// Son.vue
<Grandson :value="value" v-bind="$attrs"></Grandson>
一样的,还有$listeners,其包含的是
父做用域中不含.native修饰的事件监听器,如上面例子,在父做用域中的Son组件上监听了input事件,那么Son组件内就能够经过$listeners.input()执行input的事件函数。
固然也能够经过 v-on="$listeners" 传递给孙子组件使用。
其主要区别就是: $attrs是组件上属性的集合,$listeners是组件上方法(事件)的集合。
⑧ provide和inject,提供和注入实现祖先组件和后代组件之间通讯。
能够经过 provide()提供一个对象数据到父组件上,而后其后代组件就能够经过 inject将祖先组件上的数据注入到后代组件中
// Parent.vue
export default { provide() { // 提供一个数据到祖先组件上 return { money: 1000000 } } }
// Grandson.vue
export default { inject: ["money"] // 在后代组件中注入提供到祖先组件上的数据 } // Grandson组件就能够直接经过this.money获取到数据了
⑨ 获取子组件或者子标签的引用,父组件能够经过$refs属性获取到添加了ref属性的子组件或者子标签对象,而后进行相应的操做,如:
<Son :value="firstName" @input="change" ref="son"></Son> export default { mounted() { this.$refs.son.say(); // 经过$refs获取到<Son>组件,而后调用其say方法 } }
⑩ 经过eventBus进行通讯,所谓eventBus就是一个公共的Vue实例,全部组件都经过这个公共的Vue实例进行发射和监听事件,如:
Vue.prototype.$bus = new Vue(); // 将这个eventBus对象暴露到原型上方便调用
全部组件均可以获取到这个$bus对象并进行收发数据通讯了。