const vm = new Vue(
options
)
继续学习Vue构造选项options的进阶属性。
今天学习的进阶属性属于资源类和组合类
1、directives-指令
声明一个全局指令
全局指令就是在所有组件里都能用的指令。
Vue.directive( id, [definition] )
Vue.directive("x", directiveOptions)
{string} id{Function | Object} [definition]
代码示例
main.js
Vue.directive("x" , {//声明一个指令叫做x
inserted: function (el) {
//当元素被插入到页面中之后
//这里的元素指的是你的指令v-x放到的那个元素
//全局指令v-x可以在任何组件的任何标签/元素上使用
el.addEventListener("click",()=>{console.log("x")})
//我们就监听它的click事件,addEventListener的意思添加事件监听
},
})
练习 codesandbox.io/s/recursing…
声明一个局部指令
在组件HelloWorld.vue或App.vue里面
<script>
export default {
name: "HelloWorld",
directives: {
x: {
inserted(el) {
el.addEventListener("click", () => {
console.log("x");
});
}
}
},
props: {
msg: String
}
};
</script>
或者new Vue({})里面
new Vue({
...,
directives:{
"x": directiveOptions
}
})
局部指令v-x只能用在该实例中或该组件中。
directiveOptions里有哪些属性(钩子函数)?
- bind (可接受的参数有 el,info,vnode,oldVnode) - 类似created
- inserted(参数同上) -类似mounted
- update(参数同上) -类似update
- componentUpdated(参数同上) -用的不多
- unbind(参数同上) -类似destroyed
自制v-on2指令,模仿v-on
new Vue({
directives: {
on2: {
//生成一个名叫on2的指令,on2是简化版的v-on
// bind 可以改为 inserted
bind(el, info) {
//el:指令所绑定的元素,可以用来直接操作 DOM。
//info(binding):一个对象{},包含以下name,value,arg,modifiers等property
console.log("info")//打出info,看看里面的内容,找到arg和value就是我们需要监听和调用的
console.log("info.arg")//验证arg
console.log("info.value")//验证value
el.addEventListener(info.arg, info.value);
//对元素添加事件监听,监听'info.arg'事件,并调用'info.value',value就是下面methods中的hi函数
// Vue 自带的 v-on 并不是这样实现的,它更复杂,用了事件委托
},
unbind(el, info) {
el.removeEventListener(info.arg, info.value);
}//当元素从页面消失的时候,移除事件监听。
}
},
template: `
<button v-on2:click="hi">点我</button>
`,
methods: {
hi() {
console.log("hi");
}
}
}).$mount("#app");
指令的作用-减少重复的DOM操作。
vue指令主要用于DOM操作:vue把DOM操作封装成一个vue指令,只传一个函数给这个指令,我们就不用接触DOM操作了。(DOM操作就是:用DOM的API来操作页面。)
而vue实例/组件用于数据绑定、事件监听、DOM更新(不是用DOM的API更新,而是直接通过监听器更新)。
vue指令使得我们不用再在实例里面进行DOM操作,所有DOM操作都封装到了vue指令里面。
new Vue({
directives: {xxx:{封装指令}}}).$mount("#app");
如果某个DOM操作你经常使用,可以封装为指令。 如果某个DOM操作比较复杂,也可以封装为指令。
2、mixins-混入-把豆子(代码)倒进来
directives的作用是减少DOM操作的重复。
mixins的作用:
- 减少data、methods、钩子等options的重复。
- 将data()、created()等options导入xxx.vue文件,并自动与xxx.vue文件里面的data()、created()等options智能合并。
示例代码log.js -> Child1.vue-Child5.vue -> App.vue -> main.js
log.js
const log = {//声明一个叫做log的对象。
data() {
return {
name: undefined,
time: undefined//时间记录
};
},
created() {//当组件出生时
if (!this.name) {//也可写为'this.name === undefined'
//如果this.name不存在
throw new Error("need name");
//就报错"need name"
}
this.time = new Date();//记录created()出生时刻
console.log(`${this.name}出生了`);
},
beforeDestroy() {//当组件消亡前
const now = new Date();//记录beforeDestroy()死亡时刻
console.log(`${this.name}死亡了,共生存了 ${now - this.time} ms`);
}
};
export default log;//导出log对象
知识点回顾:JavaScript Date 对象
var myDate=new Date()//Date 对象会自动把当前日期和时间保存为其初始值。
详见 www.w3school.com.cn/jsref/jsref…
Child.vue
<template>
<div>Child1</div>
</template>
<script>
import log from "../mixins/log.js";//从上一层文件minxins里面的log.js里面接入log对象。
export default {
data() {
return {
name: "Child1"
};//给Child1一个name
},
created() {
console.log("Child 1 的 created");
},
mixins: [log]//把log对象里面的[data(),created(),beforeDestroy()]一个options组复制到这里
};
</script>
App.vue
知识点回顾:v-if条件渲染元素
根据表达式的值的 truthiness 来有条件地渲染元素。
<template>
<div id="app">
<Child1 v-if="child1Visible"/>
<!-- 当"child1Visible = true"时,让child1显示 -->
<button @click="child1Visible = false">x1</button>
<!-- 当点击button时,让child1隐藏 -->
...
<Child5 v-if="child5Visible"/>
<button @click="child5Visible = false">x5</button>
</div>
</template>
main.js
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)//渲染组件App
}).$mount("#app");
可以在main.js里面写Vue.mixin,但不推荐使用。
3、extends-继承
回顾1、directives和2、mixins的知识
- directives-vue指令封装DOM操作(v-xxx封装),减少DOM操作的重复。
- mixins-用xxx.js封装data、methods、钩子等options,再用
<script>
export default {
mixins: [log]
}
</script>
导入xxx.vue组件。减少了data、methods、钩子等options的重复。
-
extends是比mixins更抽象一点的封装。如果要写多次mixins,可以用extends来封装,实际工作用到很少。
-
可以使用Vue.extend/options.extends
示例代码log.js -> MyVue.js -> Child1.vue-Child5.vue -> App.vue -> main.js
log.js与2、mixins中示例一样
const log = {//声明一个叫做log的对象。
data() {},
created() {},
beforeDestroy() {};
export default log;//导出log对象
MyVue.js
import Vue from "vue";
import log from "./mixins/log.js";
const MyVue = Vue.extend({//使用"Vue.extend"得到一个新的vue类,这个类可以去new它
mixins: [log]
});
export default MyVue
Child.vue
<template>
<div>Child1</div>
</template>
<script>
import MyVue from "../MyVue.js";
export default {
data() {
return {
name: "Child1"
};//给Child1一个name
},
extends:MyVue//继承MyVue里面的data(),created(),beforeDestroy()
};
</script>
App.vue和main.js里面的代码与2、mixins中示例一样。
4、provide-提供;inject-注入
-
父组件App.vue通过
provide(){return{}}提供data(){}、methods:{}的属性给其他组件修改和调用。 -
子组件/孙组件xxx.vue通过
inject:["","",""]注入,来修改和调用这些由父组件提供的data(){}、methods:{}的属性。
示例代码ChangeThemeButton.vue -> Child1-Child5.vue -> App.vue ->main.js codesandbox.io/s/friendly-…
ChangeThemeButton.vue
<template>
<div>
<button @click="changeTheme">换肤</button>
<button @click="changeFontSize('big')">大字</button>
<!-- 当点击这个"大字"按钮的时候,通过inject和provide,调用函数changeFontSize,size的值为"big";
当size变化,this.fontSizeName = size = big ,
App.vue里面的<div :class="`app theme-${themeName} fontSize-${fontSizeName}`">就会变化为fontSize-big,
就会影响App.vue里面CSS部分的内容。 -->
<button @click="changeFontSize('small')">小字</button>
<button @click="changeFontSize('normal')">正常字</button>
</div>
</template>
<script>
export default {
inject: ["themeName", "changeTheme", "changeFontSize"]
//这些注入的属性可以用this. 调用
};
</script>
Child1.vue
<template>
<div>Child 1
<change-theme-button/>
<!-- 插入ChangeThemeButton.vue里面的XML -->
<!-- 也可以用<ChangeThemeButton/>,改成如上的小写形式也行,用中号线连接 -->
</div>
</template>
<script>
import ChangeThemeButton from "./ChangeThemeButton.vue";//引入ChangeThemeButton.vue的js代码
export default {
components: {
ChangeThemeButton
}
};
</script>
App.vue
//XML部分
<template>
<div :class="`app theme-${themeName} fontSize-${fontSizeName}`">
<!-- 双引号里面`app theme-${themeName} fontSize-${fontSizeName}`这一部分 是class的值,此双引号不是js的双引号,是XML的双引号。 -->
<!-- 反引号``代表的是js的引号,表明反引号里面的部分是js字符串 -->
<!-- js字符串里面的内容,'app'表示这个div的第一个class是app -->
<!-- 'theme-${themeName}'表示 div的第二个class是'theme-${themeName}',这个"themeName"存的是按钮组的颜色 -->
<!-- 'fontSize-${fontSizeName}'表示 div的第三个class是'fontSize-${fontSizeName}'这个"fontSizeName"存的是按钮组的字体大小 -->
<Child1/>
<button>x</button>
···
</div>
</template>
//JS部分
<script>
import Child1 from "./components/Child1.vue";
······
import Child5 from "./components/Child5.vue";
export default {
name: "App",
provide() {
//在提供data的地方,写一个'provide(){}'函数
//提供给其他组件比如ChangeThemeButton.vue来绑定事件修改或调用。
return {
themeName: this.themeName,
//把data里面的themeName提供给别人修改。
fontSizeNmae: this.fontSizeName,
//把data里面的fontSizeName提供给别人修改。
changeTheme: this.changeTheme,
// 把methods: {}里面的changeTheme函数提供给别人调用。
changeFontSize: this.changeFontSize
// 把methods: {}里面的changeFontSize函数提供给别人调用。
};
},
data() {
return {
themeName: "blue", // 'red'
fontSizeName: "normal" // 'big' | 'small'
};
},
methods: {
changeTheme() {
if (this.themeName === "blue") {
this.themeName = "red";
} else {
this.themeName = "blue";
}
},
changeFontSize(size) {
if (["normal", "big", "small"].indexOf(size) === -1) {
throw new Error(`wront size: ${size}`);
}//如果size的下标为-1,则报错`wront size: ${size}`
this.fontSizeName = size;
//如果size的下标不为-1,即为0或1或2,则size的值必然是["normal", "big", "small"]中的一个,则令this.fontSizeName = size
}
},
components: {
Child1,
···
}
};
</script>
//CSS部分
<style>
.app.theme-blue button {
/* 上述语句表示:如果.app这个类同时也属于.theme-blue这个类,那么button就用以下样式*/
/* 注意,这里的'.app'后面是没有空格的。如果".app" 和 ".theme-blue"之间有空格,就表示.app这个类里面,包含.theme-blue这个类。*/
background: blue;
color: white;
}
.app.fontSize-big {
font-size: 20px;
}
/* 如果一个元素div同时满足".app"和".fontSize-big"这两个选择器,那么就让它的font-size: 20px */
.app button {
font-size: inherit;
}
/* 按钮继承当前字体的大小 */
</style>
回顾知识点:CSS里面的fontSize-
provide/inject 总结
作用:大范围的data和method等的共用。
注意:不能只provide themeName不传 changeTheme,因为themeName的值是被复制给provide的字符串,不能达到效果。
那传引用可以吗?可以,但是不推荐,因为容易失控。
总结
1、directives指令
- 全局用
Vue.directive("x",{...}) - 局部用
options.directives - 作用是减少DOM操作相关重复代码
2、mixins混入
- 全局用
Vue.mixin({...}) - 局部用
options.mixins:[mixin1,mixin2] - 作用是减少options里的重复
3、extends继承/扩展
- 全局用
Vue.extend({...}) - 局部用
options.extends:{...} - 作用和mixins差不多,extends是比mixins更抽象一点的封装。如果要写多次mixins,可以用extends来封装,实际工作用到很少。
4、provide/inject 提供和注入
- 父组件通过
provide(){return{}}提供data(){}、methods:{}的属性给其他组件修改和调用。 - 子组件/孙组件xxx.vue通过
inject:["","",""]注入,来修改和调用这些由父组件提供的data(){}、methods:{}的属性。 - 作用的大范围、隔N代共享信息。